mrib.c: don't use cpu_to_be32 outside of function
[project/omcproxy.git] / src / mrib.c
1 /*
2 * Author: Steven Barth <steven at midlink.org>
3 *
4 * Copyright 2015 Deutsche Telekom AG
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <endian.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28 #include <net/if.h>
29
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32 #include <netinet/ip.h>
33 #include <netinet/ip6.h>
34
35 #include <linux/mroute.h>
36 #include <linux/mroute6.h>
37
38 #include <libubox/uloop.h>
39
40 #include "omcproxy.h"
41 #include "mrib.h"
42
43 struct mrib_route {
44 struct list_head head;
45 struct in6_addr group;
46 struct in6_addr source;
47 omgp_time_t valid_until;
48 };
49
50 struct mrib_iface {
51 int ifindex;
52 struct list_head users;
53 struct list_head routes;
54 struct list_head queriers;
55 struct uloop_timeout timer;
56 };
57
58 /* we can't use cpu_to_be32 outside a function */
59 #if __BYTE_ORDER == __BIG_ENDIAN
60 static uint32_t ipv4_rtr_alert = 0x94040000;
61 #else
62 static uint32_t ipv4_rtr_alert = 0x00000494;
63 #endif
64
65 static struct {
66 struct ip6_hbh hdr;
67 struct ip6_opt_router rt;
68 uint8_t pad[2];
69 } ipv6_rtr_alert = {
70 .hdr = {0, 0},
71 .rt = {IP6OPT_ROUTER_ALERT, 2, {0, IP6_ALERT_MLD}},
72 .pad = {0, 0}
73 };
74
75 static struct mrib_iface mifs[MAXMIFS] = {};
76 static struct uloop_fd mrt_fd = { .fd = -1 };
77 static struct uloop_fd mrt6_fd = { .fd = -1 };
78
79
80 // Unmap IPv4 address from IPv6
81 static inline void mrib_unmap(struct in_addr *addr4, const struct in6_addr *addr6)
82 {
83 addr4->s_addr = addr6->s6_addr32[3];
84 }
85
86 // Add / delete multicast route
87 static int mrib_set(const struct in6_addr *group, const struct in6_addr *source,
88 struct mrib_iface *iface, mrib_filter dest, bool del)
89 {
90 int status = 0;
91 size_t mifid = iface - mifs;
92 if (IN6_IS_ADDR_V4MAPPED(group)) {
93 struct mfcctl ctl = { .mfcc_parent = mifid };
94 mrib_unmap(&ctl.mfcc_origin, source);
95 mrib_unmap(&ctl.mfcc_mcastgrp, group);
96
97 if(!del)
98 for (size_t i = 0; i < MAXMIFS; ++i)
99 if (dest & (1 << i))
100 ctl.mfcc_ttls[i] = 1;
101
102 if (setsockopt(mrt_fd.fd, IPPROTO_IP,
103 (del) ? MRT_DEL_MFC : MRT_ADD_MFC,
104 &ctl, sizeof(ctl)))
105 status = -errno;
106 } else {
107 struct mf6cctl ctl = {
108 .mf6cc_origin = {AF_INET6, 0, 0, *source, 0},
109 .mf6cc_mcastgrp = {AF_INET6, 0, 0, *group, 0},
110 .mf6cc_parent = mifid,
111 };
112
113 if(!del)
114 for (size_t i = 0; i < MAXMIFS; ++i)
115 if (dest & (1 << i))
116 IF_SET(i, &ctl.mf6cc_ifset);
117
118 if (setsockopt(mrt6_fd.fd, IPPROTO_IPV6,
119 (del) ? MRT6_DEL_MFC : MRT6_ADD_MFC,
120 &ctl, sizeof(ctl)))
121 status = -errno;
122 }
123
124 char groupbuf[INET6_ADDRSTRLEN], sourcebuf[INET6_ADDRSTRLEN];
125 inet_ntop(AF_INET6, group, groupbuf, sizeof(groupbuf));
126 inet_ntop(AF_INET6, source, sourcebuf, sizeof(sourcebuf));
127 if(del) {
128 L_DEBUG("%s: deleting MFC-entry for %s from %s%%%d: %s",
129 __FUNCTION__, groupbuf, sourcebuf, iface->ifindex, strerror(-status));
130 } else {
131 int ifbuf_len = 0;
132 char ifbuf[256] = {0};
133 for (size_t i = 0; i < MAXMIFS; ++i)
134 if (dest & (1 << i))
135 ifbuf_len += snprintf(&ifbuf[ifbuf_len], sizeof(ifbuf) - ifbuf_len, " %d", mifs[i].ifindex);
136
137
138 L_DEBUG("%s: setting MFC-entry for %s from %s%%%d to%s: %s",
139 __FUNCTION__, groupbuf, sourcebuf, iface->ifindex, ifbuf, strerror(-status));
140 }
141
142 return status;
143 }
144
145
146 // We have no way of knowing when a source disappears, so we delete multicast routes from time to time
147 static void mrib_clean(struct uloop_timeout *t)
148 {
149 struct mrib_iface *iface = container_of(t, struct mrib_iface, timer);
150 omgp_time_t now = omgp_time();
151 uloop_timeout_cancel(t);
152
153 struct mrib_route *c, *n;
154 list_for_each_entry_safe(c, n, &iface->routes, head) {
155 if (c->valid_until <= now || (list_empty(&iface->users) && list_empty(&iface->queriers))) {
156 mrib_set(&c->group, &c->source, iface, 0, 1);
157 list_del(&c->head);
158 free(c);
159 } else {
160 uloop_timeout_set(t, c->valid_until - now);
161 break;
162 }
163 }
164 }
165
166
167 // Find MIFID by ifindex
168 static size_t mrib_find(int ifindex)
169 {
170 size_t i = 0;
171 while (i < MAXMIFS && mifs[i].ifindex != ifindex)
172 ++i;
173 return i;
174 }
175
176 // Notify all users of a new multicast source
177 static void mrib_notify_newsource(struct mrib_iface *iface,
178 const struct in6_addr *group, const struct in6_addr *source)
179 {
180 mrib_filter filter = 0;
181 struct mrib_user *user;
182 list_for_each_entry(user, &iface->users, head)
183 if (user->cb_newsource)
184 user->cb_newsource(user, group, source, &filter);
185
186 char groupbuf[INET6_ADDRSTRLEN], sourcebuf[INET6_ADDRSTRLEN];
187 inet_ntop(AF_INET6, group, groupbuf, sizeof(groupbuf));
188 inet_ntop(AF_INET6, source, sourcebuf, sizeof(sourcebuf));
189 L_DEBUG("%s: detected new multicast source %s for %s on %d",
190 __FUNCTION__, sourcebuf, groupbuf, iface->ifindex);
191
192 struct mrib_route *route = malloc(sizeof(*route));
193 if (route) {
194 route->group = *group;
195 route->source = *source;
196 route->valid_until = omgp_time() + MRIB_DEFAULT_LIFETIME * OMGP_TIME_PER_SECOND;
197
198 if (list_empty(&iface->routes))
199 uloop_timeout_set(&iface->timer, MRIB_DEFAULT_LIFETIME * OMGP_TIME_PER_SECOND);
200
201 list_add_tail(&route->head, &iface->routes);
202 mrib_set(group, source, iface, filter, 0);
203 }
204 }
205
206 // Calculate IGMP-checksum
207 static uint16_t igmp_checksum(const uint16_t *buf, size_t len)
208 {
209 int32_t sum = 0;
210
211 while (len > 1) {
212 sum += *buf++;
213 sum = (sum + (sum >> 16)) & 0xffff;
214 len -= 2;
215 }
216
217 if (len == 1) {
218 sum += *((uint8_t*)buf);
219 sum += (sum + (sum >> 16)) & 0xffff;
220 }
221
222 return ~sum;
223 }
224
225 // Receive and handle MRT event
226 static void mrib_receive_mrt(struct uloop_fd *fd, __unused unsigned flags)
227 {
228 uint8_t buf[9216], cbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
229 char addrbuf[INET_ADDRSTRLEN];
230 struct sockaddr_in from;
231
232 while (true) {
233 struct iovec iov = {buf, sizeof(buf)};
234 struct msghdr hdr = {
235 .msg_name = (void*)&from,
236 .msg_namelen = sizeof(from),
237 .msg_iov = &iov,
238 .msg_iovlen = 1,
239 .msg_control = cbuf,
240 .msg_controllen = sizeof(cbuf)
241 };
242
243 ssize_t len = recvmsg(fd->fd, &hdr, MSG_DONTWAIT);
244 if (len < 0 && errno == EAGAIN)
245 break;
246
247 struct iphdr *iph = iov.iov_base;
248 if (len < (ssize_t)sizeof(*iph))
249 continue;
250
251 if (iph->protocol == 0) {
252 // Pseudo IP/IGMP-packet from kernel MC-API
253 struct igmpmsg *msg = iov.iov_base;
254 struct mrib_iface *iface = NULL;
255 if (msg->im_vif < MAXMIFS)
256 iface = &mifs[msg->im_vif];
257
258 if (!iface) {
259 L_WARN("MRT kernel-message for unknown MIF %i", msg->im_vif);
260 continue;
261 }
262
263 if (msg->im_msgtype != IGMPMSG_NOCACHE) {
264 L_WARN("Unknown MRT kernel-message %i on interface %d",
265 msg->im_msgtype, iface->ifindex);
266 continue;
267 }
268
269 struct in6_addr dst = IN6ADDR_ANY_INIT;
270 struct in6_addr src = IN6ADDR_ANY_INIT;
271 dst.s6_addr32[2] = cpu_to_be32(0xffff);
272 dst.s6_addr32[3] = msg->im_dst.s_addr;
273 src.s6_addr32[2] = cpu_to_be32(0xffff);
274 src.s6_addr32[3] = msg->im_src.s_addr;
275
276 mrib_notify_newsource(iface, &dst, &src);
277 } else {
278 // IGMP packet
279 if ((len -= iph->ihl * 4) < 0)
280 continue;
281
282 int ifindex = 0;
283 for (struct cmsghdr *ch = CMSG_FIRSTHDR(&hdr); ch != NULL; ch = CMSG_NXTHDR(&hdr, ch)) {
284 if (ch->cmsg_level == IPPROTO_IP && ch->cmsg_type == IP_PKTINFO) {
285 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(ch);
286 ifindex = info->ipi_ifindex;
287 }
288 }
289
290 if (ifindex == 0)
291 continue;
292
293 inet_ntop(AF_INET, &from.sin_addr, addrbuf, sizeof(addrbuf));
294 struct igmphdr *igmp = (struct igmphdr*)&buf[iph->ihl * 4];
295
296 uint16_t checksum = igmp->csum;
297 igmp->csum = 0;
298
299 if (iph->ttl != 1 || len < (ssize_t)sizeof(*igmp) ||
300 checksum != igmp_checksum((uint16_t*)igmp, len)) {
301 L_WARN("%s: ignoring invalid IGMP-message of type %x from %s on %d",
302 __FUNCTION__, igmp->type, addrbuf, ifindex);
303 continue;
304 }
305
306 uint32_t *opts = (uint32_t*)&iph[1];
307 bool alert = (void*)&opts[1] <= (void*)igmp && *opts == ipv4_rtr_alert;
308 if (!alert && (igmp->type != IGMP_HOST_MEMBERSHIP_QUERY ||
309 (size_t)len > sizeof(*igmp) || igmp->code > 0)) {
310 L_WARN("%s: ignoring invalid IGMP-message of type %x from %s on %d",
311 __FUNCTION__, igmp->type, addrbuf, ifindex);
312 continue;
313 }
314
315 ssize_t mifid = mrib_find(ifindex);
316 if (mifid < MAXMIFS) {
317 struct mrib_querier *q;
318 list_for_each_entry(q, &mifs[mifid].queriers, head)
319 if (q->cb_igmp)
320 q->cb_igmp(q, igmp, len, &from);
321 }
322 }
323 }
324 }
325
326 // Receive and handle MRT6 event
327 static void mrib_receive_mrt6(struct uloop_fd *fd, __unused unsigned flags)
328 {
329 uint8_t buf[9216], cbuf[128];
330 char addrbuf[INET6_ADDRSTRLEN];
331 struct sockaddr_in6 from;
332
333 while (true) {
334 struct iovec iov = {buf, sizeof(buf)};
335 struct msghdr hdr = {
336 .msg_name = (void*)&from,
337 .msg_namelen = sizeof(from),
338 .msg_iov = &iov,
339 .msg_iovlen = 1,
340 .msg_control = cbuf,
341 .msg_controllen = sizeof(cbuf)
342 };
343
344 ssize_t len = recvmsg(fd->fd, &hdr, MSG_DONTWAIT);
345 if (len < 0 && errno == EAGAIN)
346 break;
347
348 struct mld_hdr *mld = iov.iov_base;
349 if (len < (ssize_t)sizeof(*mld))
350 continue;
351
352 if (mld->mld_icmp6_hdr.icmp6_type == 0) {
353 // Pseudo ICMPv6/MLD-packet from kernel MC-API
354 struct mrt6msg *msg = iov.iov_base;
355 struct mrib_iface *iface = NULL;
356 if (msg->im6_mif < MAXMIFS)
357 iface = &mifs[msg->im6_mif];
358
359 if (!iface) {
360 L_WARN("MRT6 kernel-message for unknown MIF %i", msg->im6_mif);
361 continue;
362 }
363
364 if (msg->im6_msgtype != MRT6MSG_NOCACHE) {
365 L_WARN("Unknown MRT6 kernel-message %i on interface %d",
366 msg->im6_msgtype, iface->ifindex);
367 continue;
368 }
369
370 mrib_notify_newsource(iface, &msg->im6_dst, &msg->im6_src);
371 } else {
372 int hlim = 0, ifindex = from.sin6_scope_id;
373 bool alert = false;
374 for (struct cmsghdr *ch = CMSG_FIRSTHDR(&hdr); ch != NULL; ch = CMSG_NXTHDR(&hdr, ch)) {
375 if (ch->cmsg_level == IPPROTO_IPV6 && ch->cmsg_type == IPV6_HOPLIMIT)
376 memcpy(&hlim, CMSG_DATA(ch), sizeof(hlim));
377 else if (ch->cmsg_level == IPPROTO_IPV6 && ch->cmsg_type == IPV6_HOPOPTS &&
378 ch->cmsg_len >= CMSG_LEN(sizeof(ipv6_rtr_alert)) &&
379 memmem(CMSG_DATA(ch), ch->cmsg_len - CMSG_LEN(0),
380 &ipv6_rtr_alert.rt, sizeof(ipv6_rtr_alert.rt)))
381 alert = true; // FIXME: memmem is wrong
382 }
383 inet_ntop(AF_INET6, &from.sin6_addr, addrbuf, sizeof(addrbuf));
384
385 if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr) || hlim != 1 || len < 24 || !alert) {
386 L_WARN("mld: ignoring invalid MLD-message of type %d from %s on %d",
387 mld->mld_icmp6_hdr.icmp6_type, addrbuf, ifindex);
388 continue;
389 }
390
391 ssize_t mifid = mrib_find(from.sin6_scope_id);
392 if (mifid < MAXMIFS) {
393 struct mrib_querier *q;
394 list_for_each_entry(q, &mifs[mifid].queriers, head)
395 if (q->cb_mld)
396 q->cb_mld(q, mld, len, &from);
397 }
398 }
399 }
400 }
401
402 // Send an IGMP-packet
403 int mrib_send_igmp(struct mrib_querier *q, struct igmpv3_query *igmp, size_t len,
404 const struct sockaddr_in *dest)
405 {
406 uint8_t cbuf[CMSG_SPACE(sizeof(struct in_pktinfo))] = {0};
407 struct iovec iov = {igmp, len};
408 struct msghdr msg = {
409 .msg_name = (void*)dest,
410 .msg_namelen = sizeof(*dest),
411 .msg_iov = &iov,
412 .msg_iovlen = 1,
413 .msg_control = cbuf,
414 .msg_controllen = sizeof(cbuf)
415 };
416
417 igmp->csum = 0;
418 igmp->csum = igmp_checksum((uint16_t*)igmp, len);
419
420 // Set control data (define destination interface)
421 struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg);
422 chdr->cmsg_level = IPPROTO_IP;
423 chdr->cmsg_type = IP_PKTINFO;
424 chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
425
426 struct in_pktinfo *pktinfo = (struct in_pktinfo*)CMSG_DATA(chdr);
427 pktinfo->ipi_addr.s_addr = 0;
428 pktinfo->ipi_ifindex = q->iface->ifindex;
429 if (mrib_igmp_source(q, &pktinfo->ipi_spec_dst))
430 return -errno;
431
432 ssize_t s = sendmsg(mrt_fd.fd, &msg, MSG_DONTWAIT);
433 return (s < 0) ? -errno : (s < (ssize_t)len) ? -EMSGSIZE : 0;
434 }
435
436 // Send an IGMP-packet
437 int mrib_send_mld(struct mrib_querier *q, struct mld_hdr *mld, size_t len,
438 const struct sockaddr_in6 *dest)
439 {
440 uint8_t cbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))] = {0};
441 struct iovec iov = {mld, len};
442 struct msghdr msg = {
443 .msg_name = (void*)dest,
444 .msg_namelen = sizeof(*dest),
445 .msg_iov = &iov,
446 .msg_iovlen = 1,
447 .msg_control = cbuf,
448 .msg_controllen = sizeof(cbuf)
449 };
450
451 // Set control data (define destination interface)
452 struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg);
453 chdr->cmsg_level = IPPROTO_IPV6;
454 chdr->cmsg_type = IPV6_PKTINFO;
455 chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
456
457 struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(chdr);
458 pktinfo->ipi6_ifindex = q->iface->ifindex;
459 if (mrib_mld_source(q, &pktinfo->ipi6_addr))
460 return -errno;
461
462 ssize_t s = sendmsg(mrt6_fd.fd, &msg, MSG_DONTWAIT);
463 return (s < 0) ? -errno : (s < (ssize_t)len) ? -EMSGSIZE : 0;
464 }
465
466 // Initialize MRIB
467 static int mrib_init(void)
468 {
469 int fd;
470 int val;
471
472 if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
473 goto err;
474
475 val = 1;
476 if (setsockopt(fd, IPPROTO_IP, MRT_INIT, &val, sizeof(val)))
477 goto err;
478
479 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)))
480 goto err;
481
482 // Configure IP header fields
483 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)))
484 goto err;
485
486 val = 0xc0;
487 if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)))
488 goto err;
489
490 val = 0;
491 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)))
492 goto err;
493
494 // Set router-alert option
495 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, &ipv4_rtr_alert, sizeof(ipv4_rtr_alert)))
496 goto err;
497
498 mrt_fd.fd = fd;
499
500
501 if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
502 goto err;
503
504 // We need to know the source interface and hop-opts
505 val = 1;
506 if (setsockopt(fd, IPPROTO_IPV6, MRT6_INIT, &val, sizeof(val)))
507 goto err;
508
509 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &val, sizeof(val)))
510 goto err;
511
512 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)))
513 goto err;
514
515 // MLD has hoplimit 1
516 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)))
517 goto err;
518
519 val = 0;
520 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)))
521 goto err;
522
523 // Let the kernel compute our checksums
524 val = 2;
525 if (setsockopt(fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)))
526 goto err;
527
528 // Set hop-by-hop router alert on outgoing
529 if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPOPTS, &ipv6_rtr_alert, sizeof(ipv6_rtr_alert)))
530 goto err;
531
532 // Set ICMP6 filter
533 struct icmp6_filter flt;
534 ICMP6_FILTER_SETBLOCKALL(&flt);
535 ICMP6_FILTER_SETPASS(ICMPV6_MGM_QUERY, &flt);
536 ICMP6_FILTER_SETPASS(ICMPV6_MGM_REPORT, &flt);
537 ICMP6_FILTER_SETPASS(ICMPV6_MGM_REDUCTION, &flt);
538 ICMP6_FILTER_SETPASS(ICMPV6_MLD2_REPORT, &flt);
539 if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &flt, sizeof(flt)))
540 goto err;
541
542 mrt6_fd.fd = fd;
543
544 mrt_fd.cb = mrib_receive_mrt;
545 mrt6_fd.cb = mrib_receive_mrt6;
546
547 uloop_fd_add(&mrt_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
548 uloop_fd_add(&mrt6_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
549
550 fd = -1;
551 errno = 0;
552
553 err:
554 if (fd >= 0)
555 close(fd);
556 return -errno;
557 }
558
559 // Create new interface entry
560 static struct mrib_iface* mrib_get_iface(int ifindex)
561 {
562 if (mrt_fd.fd < 0 && mrib_init() < 0)
563 return NULL;
564
565 size_t mifid = mrib_find(ifindex);
566 if (mifid < MAXMIFS)
567 return &mifs[mifid];
568
569 errno = EBUSY;
570 if ((mifid = mrib_find(0)) >= MAXMIFS)
571 return NULL;
572
573 struct mrib_iface *iface = &mifs[mifid];
574
575 struct vifctl ctl = {mifid, VIFF_USE_IFINDEX, 1, 0, { .vifc_lcl_ifindex = ifindex }, {INADDR_ANY}};
576 if (setsockopt(mrt_fd.fd, IPPROTO_IP, MRT_ADD_VIF, &ctl, sizeof(ctl)))
577 return NULL;
578
579 struct mif6ctl ctl6 = {mifid, 0, 1, ifindex, 0};
580 if (setsockopt(mrt6_fd.fd, IPPROTO_IPV6, MRT6_ADD_MIF, &ctl6, sizeof(ctl6)))
581 return NULL;
582
583 struct ip_mreqn mreq = {{INADDR_ALLIGMPV3RTRS_GROUP}, {INADDR_ANY}, ifindex};
584 setsockopt(mrt_fd.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
585
586 mreq.imr_multiaddr.s_addr = cpu_to_be32(INADDR_ALLRTRS_GROUP);
587 setsockopt(mrt_fd.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
588
589 struct ipv6_mreq mreq6 = {MLD2_ALL_MCR_INIT, ifindex};
590 setsockopt(mrt6_fd.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
591
592 mreq6.ipv6mr_multiaddr.s6_addr[15] = 0x02;
593 setsockopt(mrt6_fd.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
594
595 iface->timer.cb = mrib_clean;
596 iface->ifindex = ifindex;
597 INIT_LIST_HEAD(&iface->routes);
598 INIT_LIST_HEAD(&iface->users);
599 INIT_LIST_HEAD(&iface->queriers);
600 return iface;
601 }
602
603 // Remove interfaces if it has no more users
604 static void mrib_clean_iface(struct mrib_iface *iface)
605 {
606 if (list_empty(&iface->users) && list_empty(&iface->queriers)) {
607 iface->ifindex = 0;
608 mrib_clean(&iface->timer);
609
610 size_t mifid = iface - mifs;
611 struct vifctl ctl = {mifid, VIFF_USE_IFINDEX, 1, 0,
612 { .vifc_lcl_ifindex = iface->ifindex }, {INADDR_ANY}};
613 setsockopt(mrt_fd.fd, IPPROTO_IP, MRT_DEL_VIF, &ctl, sizeof(ctl));
614
615 struct mif6ctl ctl6 = {mifid, 0, 1, iface->ifindex, 0};
616 setsockopt(mrt6_fd.fd, IPPROTO_IPV6, MRT6_DEL_MIF, &ctl6, sizeof(ctl6));
617
618 struct ip_mreqn mreq = {{INADDR_ALLIGMPV3RTRS_GROUP}, {INADDR_ANY}, iface->ifindex};
619 setsockopt(mrt_fd.fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
620
621 mreq.imr_multiaddr.s_addr = cpu_to_be32(INADDR_ALLRTRS_GROUP);
622 setsockopt(mrt_fd.fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
623
624 struct ipv6_mreq mreq6 = {MLD2_ALL_MCR_INIT, iface->ifindex};
625 setsockopt(mrt6_fd.fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
626
627 mreq6.ipv6mr_multiaddr.s6_addr[15] = 0x02;
628 setsockopt(mrt6_fd.fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
629 }
630 }
631
632 // Register a new interface to mrib
633 int mrib_attach_user(struct mrib_user *user, int ifindex, mrib_cb *cb_newsource)
634 {
635 struct mrib_iface *iface = mrib_get_iface(ifindex);
636 if (!iface)
637 return -errno;
638
639 if (user->iface == iface)
640 return -EALREADY;
641
642 list_add(&user->head, &iface->users);
643 user->iface = iface;
644 user->cb_newsource = cb_newsource;
645 return 0;
646 }
647
648 // Deregister an interface from mrib
649 void mrib_detach_user(struct mrib_user *user)
650 {
651 struct mrib_iface *iface = user->iface;
652 if (!iface)
653 return;
654
655 user->iface = NULL;
656 list_del(&user->head);
657 mrib_clean_iface(iface);
658 }
659
660 // Register a querier to mrib
661 int mrib_attach_querier(struct mrib_querier *querier, int ifindex, mrib_igmp_cb *cb_igmp, mrib_mld_cb *cb_mld)
662 {
663 struct mrib_iface *iface = mrib_get_iface(ifindex);
664 if (!iface)
665 return -errno;
666
667 list_add(&querier->head, &iface->queriers);
668 querier->iface = iface;
669 querier->cb_igmp = cb_igmp;
670 querier->cb_mld = cb_mld;
671 return 0;
672 }
673
674 // Deregister a querier from mrib
675 void mrib_detach_querier(struct mrib_querier *querier)
676 {
677 struct mrib_iface *iface = querier->iface;
678 if (!iface)
679 return;
680
681 querier->iface = NULL;
682 list_del(&querier->head);
683 mrib_clean_iface(iface);
684 }
685
686 static uint8_t prefix_contains(const struct in6_addr *p, uint8_t plen, const struct in6_addr *addr)
687 {
688 int blen = plen >> 3;
689 if(blen && memcmp(p, addr, blen))
690 return 0;
691
692 int rem = plen & 0x07;
693 if(rem && ((p->s6_addr[blen] ^ addr->s6_addr[blen]) >> (8 - rem)))
694 return 0;
695
696 return 1;
697 }
698
699 // Flush state for a multicast route
700 int mrib_flush(struct mrib_user *user, const struct in6_addr *group, uint8_t group_plen, const struct in6_addr *source)
701 {
702 struct mrib_iface *iface = user->iface;
703 if (!iface)
704 return -ENOENT;
705
706 bool found = false;
707 struct mrib_route *route, *n;
708 list_for_each_entry_safe(route, n, &iface->routes, head) {
709 if (prefix_contains(group, group_plen, &route->group) &&
710 (!source || IN6_ARE_ADDR_EQUAL(&route->source, source))) {
711 route->valid_until = 0;
712 list_del(&route->head);
713 list_add(&route->head, &iface->routes);
714 found = true;
715 }
716 }
717
718 if (found)
719 mrib_clean(&iface->timer);
720
721 return (found) ? 0 : -ENOENT;
722 }
723
724 // Add an interface to the filter
725 int mrib_filter_add(mrib_filter *filter, struct mrib_user *user)
726 {
727 struct mrib_iface *iface = user->iface;
728 if (!iface)
729 return -ENOENT;
730
731 *filter |= 1 << (iface - mifs);
732 return 0;
733 }
734
735 // Get MLD source address
736 int mrib_mld_source(struct mrib_querier *q, struct in6_addr *source)
737 {
738 struct sockaddr_in6 addr = {AF_INET6, 0, 0, MLD2_ALL_MCR_INIT, q->iface->ifindex};
739 socklen_t alen = sizeof(addr);
740 int sock = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
741 int ret = 0;
742
743 if (sock < 0 || connect(sock, (struct sockaddr*)&addr, sizeof(addr)))
744 ret = -errno;
745
746 if (ret || getsockname(sock, (struct sockaddr*)&addr, &alen)) {
747 L_WARN("%s: failed to detect local source address on %d", __FUNCTION__, q->iface->ifindex);
748 ret = -errno;
749 }
750
751 close(sock);
752
753 if (ret == 0)
754 *source = addr.sin6_addr;
755
756 return ret;
757 }
758
759
760 // Get IGMP source address
761 int mrib_igmp_source(struct mrib_querier *q, struct in_addr *source)
762 {
763 struct sockaddr_in addr = {AF_INET, 0, {cpu_to_be32(INADDR_ALLHOSTS_GROUP)}, {0}};
764 socklen_t alen = sizeof(addr);
765 struct ifreq ifr = {.ifr_name = ""};
766 int sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_IGMP);
767 int ret = 0;
768
769 ifr.ifr_ifindex = q->iface->ifindex;
770
771 if (sock < 0 || ioctl(sock, SIOCGIFNAME, &ifr) ||
772 setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifr.ifr_name, strlen(ifr.ifr_name)))
773 ret = -errno;
774
775
776 if (ret || connect(sock, (struct sockaddr*)&addr, sizeof(addr)))
777 ret = -errno;
778
779 if (ret || getsockname(sock, (struct sockaddr*)&addr, &alen)) {
780 L_WARN("%s: failed to detect local source address on %d", __FUNCTION__, q->iface->ifindex);
781 ret = -errno;
782 }
783
784 close(sock);
785
786 if (ret == 0)
787 *source = addr.sin_addr;
788
789 return ret;
790 }