2 * Author: Steven Barth <steven at midlink.org>
4 * Copyright 2015 Deutsche Telekom AG
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
31 #include <netinet/ip.h>
32 #include <netinet/ip6.h>
34 #include <linux/mroute.h>
35 #include <linux/mroute6.h>
37 #include <libubox/uloop.h>
43 struct list_head head
;
44 struct in6_addr group
;
45 struct in6_addr source
;
46 omgp_time_t valid_until
;
51 struct list_head users
;
52 struct list_head routes
;
53 struct list_head queriers
;
54 struct uloop_timeout timer
;
57 static uint32_t ipv4_rtr_alert
= cpu_to_be32(0x94040000);
60 struct ip6_opt_router rt
;
64 .rt
= {IP6OPT_ROUTER_ALERT
, 2, {0, IP6_ALERT_MLD
}},
68 static struct mrib_iface mifs
[MAXMIFS
] = {};
69 static struct uloop_fd mrt_fd
= { .fd
= -1 };
70 static struct uloop_fd mrt6_fd
= { .fd
= -1 };
73 // Unmap IPv4 address from IPv6
74 static inline void mrib_unmap(struct in_addr
*addr4
, const struct in6_addr
*addr6
)
76 addr4
->s_addr
= addr6
->s6_addr32
[3];
79 // Add / delete multicast route
80 static int mrib_set(const struct in6_addr
*group
, const struct in6_addr
*source
,
81 struct mrib_iface
*iface
, mrib_filter dest
, bool del
)
84 size_t mifid
= iface
- mifs
;
85 if (IN6_IS_ADDR_V4MAPPED(group
)) {
86 struct mfcctl ctl
= { .mfcc_parent
= mifid
};
87 mrib_unmap(&ctl
.mfcc_origin
, source
);
88 mrib_unmap(&ctl
.mfcc_mcastgrp
, group
);
91 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
95 if (setsockopt(mrt_fd
.fd
, IPPROTO_IP
,
96 (del
) ? MRT_DEL_MFC
: MRT_ADD_MFC
,
100 struct mf6cctl ctl
= {
101 .mf6cc_origin
= {AF_INET6
, 0, 0, *source
, 0},
102 .mf6cc_mcastgrp
= {AF_INET6
, 0, 0, *group
, 0},
103 .mf6cc_parent
= mifid
,
107 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
109 IF_SET(i
, &ctl
.mf6cc_ifset
);
111 if (setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
,
112 (del
) ? MRT6_DEL_MFC
: MRT6_ADD_MFC
,
117 char groupbuf
[INET6_ADDRSTRLEN
], sourcebuf
[INET6_ADDRSTRLEN
];
118 inet_ntop(AF_INET6
, group
, groupbuf
, sizeof(groupbuf
));
119 inet_ntop(AF_INET6
, source
, sourcebuf
, sizeof(sourcebuf
));
121 L_DEBUG("%s: deleting MFC-entry for %s from %s%%%d: %s",
122 __FUNCTION__
, groupbuf
, sourcebuf
, iface
->ifindex
, strerror(-status
));
125 char ifbuf
[256] = {0};
126 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
128 ifbuf_len
+= snprintf(&ifbuf
[ifbuf_len
], sizeof(ifbuf
) - ifbuf_len
, " %d", mifs
[i
].ifindex
);
131 L_DEBUG("%s: setting MFC-entry for %s from %s%%%d to%s: %s",
132 __FUNCTION__
, groupbuf
, sourcebuf
, iface
->ifindex
, ifbuf
, strerror(-status
));
139 // We have no way of knowing when a source disappears, so we delete multicast routes from time to time
140 static void mrib_clean(struct uloop_timeout
*t
)
142 struct mrib_iface
*iface
= container_of(t
, struct mrib_iface
, timer
);
143 omgp_time_t now
= omgp_time();
144 uloop_timeout_cancel(t
);
146 struct mrib_route
*c
, *n
;
147 list_for_each_entry_safe(c
, n
, &iface
->routes
, head
) {
148 if (c
->valid_until
<= now
|| (list_empty(&iface
->users
) && list_empty(&iface
->queriers
))) {
149 mrib_set(&c
->group
, &c
->source
, iface
, 0, 1);
153 uloop_timeout_set(t
, c
->valid_until
- now
);
160 // Find MIFID by ifindex
161 static size_t mrib_find(int ifindex
)
164 while (i
< MAXMIFS
&& mifs
[i
].ifindex
!= ifindex
)
169 // Notify all users of a new multicast source
170 static void mrib_notify_newsource(struct mrib_iface
*iface
,
171 const struct in6_addr
*group
, const struct in6_addr
*source
)
173 mrib_filter filter
= 0;
174 struct mrib_user
*user
;
175 list_for_each_entry(user
, &iface
->users
, head
)
176 if (user
->cb_newsource
)
177 user
->cb_newsource(user
, group
, source
, &filter
);
179 char groupbuf
[INET6_ADDRSTRLEN
], sourcebuf
[INET6_ADDRSTRLEN
];
180 inet_ntop(AF_INET6
, group
, groupbuf
, sizeof(groupbuf
));
181 inet_ntop(AF_INET6
, source
, sourcebuf
, sizeof(sourcebuf
));
182 L_DEBUG("%s: detected new multicast source %s for %s on %d",
183 __FUNCTION__
, sourcebuf
, groupbuf
, iface
->ifindex
);
185 struct mrib_route
*route
= malloc(sizeof(*route
));
187 route
->group
= *group
;
188 route
->source
= *source
;
189 route
->valid_until
= omgp_time() + MRIB_DEFAULT_LIFETIME
* OMGP_TIME_PER_SECOND
;
191 if (list_empty(&iface
->routes
))
192 uloop_timeout_set(&iface
->timer
, MRIB_DEFAULT_LIFETIME
* OMGP_TIME_PER_SECOND
);
194 list_add_tail(&route
->head
, &iface
->routes
);
195 mrib_set(group
, source
, iface
, filter
, 0);
199 // Calculate IGMP-checksum
200 static uint16_t igmp_checksum(const uint16_t *buf
, size_t len
)
206 sum
= (sum
+ (sum
>> 16)) & 0xffff;
211 sum
+= *((uint8_t*)buf
);
212 sum
+= (sum
+ (sum
>> 16)) & 0xffff;
218 // Receive and handle MRT event
219 static void mrib_receive_mrt(struct uloop_fd
*fd
, __unused
unsigned flags
)
221 uint8_t buf
[9216], cbuf
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
222 char addrbuf
[INET_ADDRSTRLEN
];
223 struct sockaddr_in from
;
226 struct iovec iov
= {buf
, sizeof(buf
)};
227 struct msghdr hdr
= {
228 .msg_name
= (void*)&from
,
229 .msg_namelen
= sizeof(from
),
233 .msg_controllen
= sizeof(cbuf
)
236 ssize_t len
= recvmsg(fd
->fd
, &hdr
, MSG_DONTWAIT
);
237 if (len
< 0 && errno
== EAGAIN
)
240 struct iphdr
*iph
= iov
.iov_base
;
241 if (len
< (ssize_t
)sizeof(*iph
))
244 if (iph
->protocol
== 0) {
245 // Pseudo IP/IGMP-packet from kernel MC-API
246 struct igmpmsg
*msg
= iov
.iov_base
;
247 struct mrib_iface
*iface
= NULL
;
248 if (msg
->im_vif
< MAXMIFS
)
249 iface
= &mifs
[msg
->im_vif
];
252 L_WARN("MRT kernel-message for unknown MIF %i", msg
->im_vif
);
256 if (msg
->im_msgtype
!= IGMPMSG_NOCACHE
) {
257 L_WARN("Unknown MRT kernel-message %i on interface %d",
258 msg
->im_msgtype
, iface
->ifindex
);
262 struct in6_addr dst
= IN6ADDR_ANY_INIT
;
263 struct in6_addr src
= IN6ADDR_ANY_INIT
;
264 dst
.s6_addr32
[2] = cpu_to_be32(0xffff);
265 dst
.s6_addr32
[3] = msg
->im_dst
.s_addr
;
266 src
.s6_addr32
[2] = cpu_to_be32(0xffff);
267 src
.s6_addr32
[3] = msg
->im_src
.s_addr
;
269 mrib_notify_newsource(iface
, &dst
, &src
);
272 if ((len
-= iph
->ihl
* 4) < 0)
276 for (struct cmsghdr
*ch
= CMSG_FIRSTHDR(&hdr
); ch
!= NULL
; ch
= CMSG_NXTHDR(&hdr
, ch
)) {
277 if (ch
->cmsg_level
== IPPROTO_IP
&& ch
->cmsg_type
== IP_PKTINFO
) {
278 struct in_pktinfo
*info
= (struct in_pktinfo
*)CMSG_DATA(ch
);
279 ifindex
= info
->ipi_ifindex
;
286 inet_ntop(AF_INET
, &from
.sin_addr
, addrbuf
, sizeof(addrbuf
));
287 struct igmphdr
*igmp
= (struct igmphdr
*)&buf
[iph
->ihl
* 4];
289 uint16_t checksum
= igmp
->csum
;
292 if (iph
->ttl
!= 1 || len
< (ssize_t
)sizeof(*igmp
) ||
293 checksum
!= igmp_checksum((uint16_t*)igmp
, len
)) {
294 L_WARN("%s: ignoring invalid IGMP-message of type %x from %s on %d",
295 __FUNCTION__
, igmp
->type
, addrbuf
, ifindex
);
299 uint32_t *opts
= (uint32_t*)&iph
[1];
300 bool alert
= (void*)&opts
[1] <= (void*)igmp
&& *opts
== ipv4_rtr_alert
;
301 if (!alert
&& (igmp
->type
!= IGMP_HOST_MEMBERSHIP_QUERY
||
302 (size_t)len
> sizeof(*igmp
) || igmp
->code
> 0)) {
303 L_WARN("%s: ignoring invalid IGMP-message of type %x from %s on %d",
304 __FUNCTION__
, igmp
->type
, addrbuf
, ifindex
);
308 ssize_t mifid
= mrib_find(ifindex
);
309 if (mifid
< MAXMIFS
) {
310 struct mrib_querier
*q
;
311 list_for_each_entry(q
, &mifs
[mifid
].queriers
, head
)
313 q
->cb_igmp(q
, igmp
, len
, &from
);
319 // Receive and handle MRT6 event
320 static void mrib_receive_mrt6(struct uloop_fd
*fd
, __unused
unsigned flags
)
322 uint8_t buf
[9216], cbuf
[128];
323 char addrbuf
[INET6_ADDRSTRLEN
];
324 struct sockaddr_in6 from
;
327 struct iovec iov
= {buf
, sizeof(buf
)};
328 struct msghdr hdr
= {
329 .msg_name
= (void*)&from
,
330 .msg_namelen
= sizeof(from
),
334 .msg_controllen
= sizeof(cbuf
)
337 ssize_t len
= recvmsg(fd
->fd
, &hdr
, MSG_DONTWAIT
);
338 if (len
< 0 && errno
== EAGAIN
)
341 struct mld_hdr
*mld
= iov
.iov_base
;
342 if (len
< (ssize_t
)sizeof(*mld
))
345 if (mld
->mld_icmp6_hdr
.icmp6_type
== 0) {
346 // Pseudo ICMPv6/MLD-packet from kernel MC-API
347 struct mrt6msg
*msg
= iov
.iov_base
;
348 struct mrib_iface
*iface
= NULL
;
349 if (msg
->im6_mif
< MAXMIFS
)
350 iface
= &mifs
[msg
->im6_mif
];
353 L_WARN("MRT6 kernel-message for unknown MIF %i", msg
->im6_mif
);
357 if (msg
->im6_msgtype
!= MRT6MSG_NOCACHE
) {
358 L_WARN("Unknown MRT6 kernel-message %i on interface %d",
359 msg
->im6_msgtype
, iface
->ifindex
);
363 mrib_notify_newsource(iface
, &msg
->im6_dst
, &msg
->im6_src
);
365 int hlim
= 0, ifindex
= from
.sin6_scope_id
;
367 for (struct cmsghdr
*ch
= CMSG_FIRSTHDR(&hdr
); ch
!= NULL
; ch
= CMSG_NXTHDR(&hdr
, ch
)) {
368 if (ch
->cmsg_level
== IPPROTO_IPV6
&& ch
->cmsg_type
== IPV6_HOPLIMIT
)
369 memcpy(&hlim
, CMSG_DATA(ch
), sizeof(hlim
));
370 else if (ch
->cmsg_level
== IPPROTO_IPV6
&& ch
->cmsg_type
== IPV6_HOPOPTS
&&
371 ch
->cmsg_len
>= CMSG_LEN(sizeof(ipv6_rtr_alert
)) &&
372 memmem(CMSG_DATA(ch
), ch
->cmsg_len
- CMSG_LEN(0),
373 &ipv6_rtr_alert
.rt
, sizeof(ipv6_rtr_alert
.rt
)))
374 alert
= true; // FIXME: memmem is wrong
376 inet_ntop(AF_INET6
, &from
.sin6_addr
, addrbuf
, sizeof(addrbuf
));
378 if (!IN6_IS_ADDR_LINKLOCAL(&from
.sin6_addr
) || hlim
!= 1 || len
< 24 || !alert
) {
379 L_WARN("mld: ignoring invalid MLD-message of type %d from %s on %d",
380 mld
->mld_icmp6_hdr
.icmp6_type
, addrbuf
, ifindex
);
384 ssize_t mifid
= mrib_find(from
.sin6_scope_id
);
385 if (mifid
< MAXMIFS
) {
386 struct mrib_querier
*q
;
387 list_for_each_entry(q
, &mifs
[mifid
].queriers
, head
)
389 q
->cb_mld(q
, mld
, len
, &from
);
395 // Send an IGMP-packet
396 int mrib_send_igmp(struct mrib_querier
*q
, struct igmpv3_query
*igmp
, size_t len
,
397 const struct sockaddr_in
*dest
)
399 uint8_t cbuf
[CMSG_SPACE(sizeof(struct in_pktinfo
))] = {0};
400 struct iovec iov
= {igmp
, len
};
401 struct msghdr msg
= {
402 .msg_name
= (void*)dest
,
403 .msg_namelen
= sizeof(*dest
),
407 .msg_controllen
= sizeof(cbuf
)
411 igmp
->csum
= igmp_checksum((uint16_t*)igmp
, len
);
413 // Set control data (define destination interface)
414 struct cmsghdr
*chdr
= CMSG_FIRSTHDR(&msg
);
415 chdr
->cmsg_level
= IPPROTO_IP
;
416 chdr
->cmsg_type
= IP_PKTINFO
;
417 chdr
->cmsg_len
= CMSG_LEN(sizeof(struct in_pktinfo
));
419 struct in_pktinfo
*pktinfo
= (struct in_pktinfo
*)CMSG_DATA(chdr
);
420 pktinfo
->ipi_addr
.s_addr
= 0;
421 pktinfo
->ipi_ifindex
= q
->iface
->ifindex
;
422 if (mrib_igmp_source(q
, &pktinfo
->ipi_spec_dst
))
425 ssize_t s
= sendmsg(mrt_fd
.fd
, &msg
, MSG_DONTWAIT
);
426 return (s
< 0) ? -errno
: (s
< (ssize_t
)len
) ? -EMSGSIZE
: 0;
429 // Send an IGMP-packet
430 int mrib_send_mld(struct mrib_querier
*q
, struct mld_hdr
*mld
, size_t len
,
431 const struct sockaddr_in6
*dest
)
433 uint8_t cbuf
[CMSG_SPACE(sizeof(struct in6_pktinfo
))] = {0};
434 struct iovec iov
= {mld
, len
};
435 struct msghdr msg
= {
436 .msg_name
= (void*)dest
,
437 .msg_namelen
= sizeof(*dest
),
441 .msg_controllen
= sizeof(cbuf
)
444 // Set control data (define destination interface)
445 struct cmsghdr
*chdr
= CMSG_FIRSTHDR(&msg
);
446 chdr
->cmsg_level
= IPPROTO_IPV6
;
447 chdr
->cmsg_type
= IPV6_PKTINFO
;
448 chdr
->cmsg_len
= CMSG_LEN(sizeof(struct in6_pktinfo
));
450 struct in6_pktinfo
*pktinfo
= (struct in6_pktinfo
*)CMSG_DATA(chdr
);
451 pktinfo
->ipi6_ifindex
= q
->iface
->ifindex
;
452 if (mrib_mld_source(q
, &pktinfo
->ipi6_addr
))
455 ssize_t s
= sendmsg(mrt6_fd
.fd
, &msg
, MSG_DONTWAIT
);
456 return (s
< 0) ? -errno
: (s
< (ssize_t
)len
) ? -EMSGSIZE
: 0;
460 static int mrib_init(void)
465 if ((fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
)) < 0)
469 if (setsockopt(fd
, IPPROTO_IP
, MRT_INIT
, &val
, sizeof(val
)))
472 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof(val
)))
475 // Configure IP header fields
476 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &val
, sizeof(val
)))
480 if (setsockopt(fd
, IPPROTO_IP
, IP_TOS
, &val
, sizeof(val
)))
484 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &val
, sizeof(val
)))
487 // Set router-alert option
488 if (setsockopt(fd
, IPPROTO_IP
, IP_OPTIONS
, &ipv4_rtr_alert
, sizeof(ipv4_rtr_alert
)))
494 if ((fd
= socket(AF_INET6
, SOCK_RAW
, IPPROTO_ICMPV6
)) < 0)
497 // We need to know the source interface and hop-opts
499 if (setsockopt(fd
, IPPROTO_IPV6
, MRT6_INIT
, &val
, sizeof(val
)))
502 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_RECVHOPOPTS
, &val
, sizeof(val
)))
505 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
, sizeof(val
)))
508 // MLD has hoplimit 1
509 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
)))
513 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
, sizeof(val
)))
516 // Let the kernel compute our checksums
518 if (setsockopt(fd
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
)))
521 // Set hop-by-hop router alert on outgoing
522 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_HOPOPTS
, &ipv6_rtr_alert
, sizeof(ipv6_rtr_alert
)))
526 struct icmp6_filter flt
;
527 ICMP6_FILTER_SETBLOCKALL(&flt
);
528 ICMP6_FILTER_SETPASS(ICMPV6_MGM_QUERY
, &flt
);
529 ICMP6_FILTER_SETPASS(ICMPV6_MGM_REPORT
, &flt
);
530 ICMP6_FILTER_SETPASS(ICMPV6_MGM_REDUCTION
, &flt
);
531 ICMP6_FILTER_SETPASS(ICMPV6_MLD2_REPORT
, &flt
);
532 if (setsockopt(fd
, IPPROTO_ICMPV6
, ICMP6_FILTER
, &flt
, sizeof(flt
)))
537 mrt_fd
.cb
= mrib_receive_mrt
;
538 mrt6_fd
.cb
= mrib_receive_mrt6
;
540 uloop_fd_add(&mrt_fd
, ULOOP_READ
| ULOOP_EDGE_TRIGGER
);
541 uloop_fd_add(&mrt6_fd
, ULOOP_READ
| ULOOP_EDGE_TRIGGER
);
552 // Create new interface entry
553 static struct mrib_iface
* mrib_get_iface(int ifindex
)
555 if (mrt_fd
.fd
< 0 && mrib_init() < 0)
558 size_t mifid
= mrib_find(ifindex
);
563 if ((mifid
= mrib_find(0)) >= MAXMIFS
)
566 struct mrib_iface
*iface
= &mifs
[mifid
];
568 struct vifctl ctl
= {mifid
, VIFF_USE_IFINDEX
, 1, 0, { .vifc_lcl_ifindex
= ifindex
}, {INADDR_ANY
}};
569 if (setsockopt(mrt_fd
.fd
, IPPROTO_IP
, MRT_ADD_VIF
, &ctl
, sizeof(ctl
)))
572 struct mif6ctl ctl6
= {mifid
, 0, 1, ifindex
, 0};
573 if (setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, MRT6_ADD_MIF
, &ctl6
, sizeof(ctl6
)))
576 struct ip_mreqn mreq
= {{INADDR_ALLIGMPV3RTRS_GROUP
}, {INADDR_ANY
}, ifindex
};
577 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
));
579 mreq
.imr_multiaddr
.s_addr
= cpu_to_be32(INADDR_ALLRTRS_GROUP
);
580 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
));
582 struct ipv6_mreq mreq6
= {MLD2_ALL_MCR_INIT
, ifindex
};
583 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
585 mreq6
.ipv6mr_multiaddr
.s6_addr
[15] = 0x02;
586 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
588 iface
->timer
.cb
= mrib_clean
;
589 iface
->ifindex
= ifindex
;
590 INIT_LIST_HEAD(&iface
->routes
);
591 INIT_LIST_HEAD(&iface
->users
);
592 INIT_LIST_HEAD(&iface
->queriers
);
596 // Remove interfaces if it has no more users
597 static void mrib_clean_iface(struct mrib_iface
*iface
)
599 if (list_empty(&iface
->users
) && list_empty(&iface
->queriers
)) {
601 mrib_clean(&iface
->timer
);
603 size_t mifid
= iface
- mifs
;
604 struct vifctl ctl
= {mifid
, VIFF_USE_IFINDEX
, 1, 0,
605 { .vifc_lcl_ifindex
= iface
->ifindex
}, {INADDR_ANY
}};
606 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, MRT_DEL_VIF
, &ctl
, sizeof(ctl
));
608 struct mif6ctl ctl6
= {mifid
, 0, 1, iface
->ifindex
, 0};
609 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, MRT6_DEL_MIF
, &ctl6
, sizeof(ctl6
));
611 struct ip_mreqn mreq
= {{INADDR_ALLIGMPV3RTRS_GROUP
}, {INADDR_ANY
}, iface
->ifindex
};
612 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &mreq
, sizeof(mreq
));
614 mreq
.imr_multiaddr
.s_addr
= cpu_to_be32(INADDR_ALLRTRS_GROUP
);
615 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &mreq
, sizeof(mreq
));
617 struct ipv6_mreq mreq6
= {MLD2_ALL_MCR_INIT
, iface
->ifindex
};
618 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
620 mreq6
.ipv6mr_multiaddr
.s6_addr
[15] = 0x02;
621 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
625 // Register a new interface to mrib
626 int mrib_attach_user(struct mrib_user
*user
, int ifindex
, mrib_cb
*cb_newsource
)
628 struct mrib_iface
*iface
= mrib_get_iface(ifindex
);
632 if (user
->iface
== iface
)
635 list_add(&user
->head
, &iface
->users
);
637 user
->cb_newsource
= cb_newsource
;
641 // Deregister an interface from mrib
642 void mrib_detach_user(struct mrib_user
*user
)
644 struct mrib_iface
*iface
= user
->iface
;
649 list_del(&user
->head
);
650 mrib_clean_iface(iface
);
653 // Register a querier to mrib
654 int mrib_attach_querier(struct mrib_querier
*querier
, int ifindex
, mrib_igmp_cb
*cb_igmp
, mrib_mld_cb
*cb_mld
)
656 struct mrib_iface
*iface
= mrib_get_iface(ifindex
);
660 list_add(&querier
->head
, &iface
->queriers
);
661 querier
->iface
= iface
;
662 querier
->cb_igmp
= cb_igmp
;
663 querier
->cb_mld
= cb_mld
;
667 // Deregister a querier from mrib
668 void mrib_detach_querier(struct mrib_querier
*querier
)
670 struct mrib_iface
*iface
= querier
->iface
;
674 querier
->iface
= NULL
;
675 list_del(&querier
->head
);
676 mrib_clean_iface(iface
);
679 static uint8_t prefix_contains(const struct in6_addr
*p
, uint8_t plen
, const struct in6_addr
*addr
)
681 int blen
= plen
>> 3;
682 if(blen
&& memcmp(p
, addr
, blen
))
685 int rem
= plen
& 0x07;
686 if(rem
&& ((p
->s6_addr
[blen
] ^ addr
->s6_addr
[blen
]) >> (8 - rem
)))
692 // Flush state for a multicast route
693 int mrib_flush(struct mrib_user
*user
, const struct in6_addr
*group
, uint8_t group_plen
, const struct in6_addr
*source
)
695 struct mrib_iface
*iface
= user
->iface
;
700 struct mrib_route
*route
, *n
;
701 list_for_each_entry_safe(route
, n
, &iface
->routes
, head
) {
702 if (prefix_contains(group
, group_plen
, &route
->group
) &&
703 (!source
|| IN6_ARE_ADDR_EQUAL(&route
->source
, source
))) {
704 route
->valid_until
= 0;
705 list_del(&route
->head
);
706 list_add(&route
->head
, &iface
->routes
);
712 mrib_clean(&iface
->timer
);
714 return (found
) ? 0 : -ENOENT
;
717 // Add an interface to the filter
718 int mrib_filter_add(mrib_filter
*filter
, struct mrib_user
*user
)
720 struct mrib_iface
*iface
= user
->iface
;
724 *filter
|= 1 << (iface
- mifs
);
728 // Get MLD source address
729 int mrib_mld_source(struct mrib_querier
*q
, struct in6_addr
*source
)
731 struct sockaddr_in6 addr
= {AF_INET6
, 0, 0, MLD2_ALL_MCR_INIT
, q
->iface
->ifindex
};
732 socklen_t alen
= sizeof(addr
);
733 int sock
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
, IPPROTO_ICMPV6
);
736 if (sock
< 0 || connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)))
739 if (ret
|| getsockname(sock
, (struct sockaddr
*)&addr
, &alen
)) {
740 L_WARN("%s: failed to detect local source address on %d", __FUNCTION__
, q
->iface
->ifindex
);
747 *source
= addr
.sin6_addr
;
753 // Get IGMP source address
754 int mrib_igmp_source(struct mrib_querier
*q
, struct in_addr
*source
)
756 struct sockaddr_in addr
= {AF_INET
, 0, {cpu_to_be32(INADDR_ALLHOSTS_GROUP
)}, {0}};
757 socklen_t alen
= sizeof(addr
);
758 struct ifreq ifr
= {.ifr_name
= ""};
759 int sock
= socket(AF_INET
, SOCK_RAW
| SOCK_CLOEXEC
, IPPROTO_IGMP
);
762 ifr
.ifr_ifindex
= q
->iface
->ifindex
;
764 if (sock
< 0 || ioctl(sock
, SIOCGIFNAME
, &ifr
) ||
765 setsockopt(sock
, SOL_SOCKET
, SO_BINDTODEVICE
, ifr
.ifr_name
, strlen(ifr
.ifr_name
)))
769 if (ret
|| connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)))
772 if (ret
|| getsockname(sock
, (struct sockaddr
*)&addr
, &alen
)) {
773 L_WARN("%s: failed to detect local source address on %d", __FUNCTION__
, q
->iface
->ifindex
);
780 *source
= addr
.sin_addr
;