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.
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32 #include <netinet/ip.h>
33 #include <netinet/ip6.h>
35 #include <linux/mroute.h>
36 #include <linux/mroute6.h>
38 #include <libubox/uloop.h>
44 struct list_head head
;
45 struct in6_addr group
;
46 struct in6_addr source
;
47 omgp_time_t valid_until
;
52 struct list_head users
;
53 struct list_head routes
;
54 struct list_head queriers
;
55 struct uloop_timeout timer
;
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;
62 static uint32_t ipv4_rtr_alert
= 0x00000494;
67 struct ip6_opt_router rt
;
71 .rt
= {IP6OPT_ROUTER_ALERT
, 2, {0, IP6_ALERT_MLD
}},
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 };
80 // Unmap IPv4 address from IPv6
81 static inline void mrib_unmap(struct in_addr
*addr4
, const struct in6_addr
*addr6
)
83 addr4
->s_addr
= addr6
->s6_addr32
[3];
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
)
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
);
98 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
100 ctl
.mfcc_ttls
[i
] = 1;
102 if (setsockopt(mrt_fd
.fd
, IPPROTO_IP
,
103 (del
) ? MRT_DEL_MFC
: MRT_ADD_MFC
,
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
,
114 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
116 IF_SET(i
, &ctl
.mf6cc_ifset
);
118 if (setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
,
119 (del
) ? MRT6_DEL_MFC
: MRT6_ADD_MFC
,
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
));
128 L_DEBUG("%s: deleting MFC-entry for %s from %s%%%d: %s",
129 __FUNCTION__
, groupbuf
, sourcebuf
, iface
->ifindex
, strerror(-status
));
132 char ifbuf
[256] = {0};
133 for (size_t i
= 0; i
< MAXMIFS
; ++i
)
135 ifbuf_len
+= snprintf(&ifbuf
[ifbuf_len
], sizeof(ifbuf
) - ifbuf_len
, " %d", mifs
[i
].ifindex
);
138 L_DEBUG("%s: setting MFC-entry for %s from %s%%%d to%s: %s",
139 __FUNCTION__
, groupbuf
, sourcebuf
, iface
->ifindex
, ifbuf
, strerror(-status
));
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
)
149 struct mrib_iface
*iface
= container_of(t
, struct mrib_iface
, timer
);
150 omgp_time_t now
= omgp_time();
151 uloop_timeout_cancel(t
);
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);
160 uloop_timeout_set(t
, c
->valid_until
- now
);
167 // Find MIFID by ifindex
168 static size_t mrib_find(int ifindex
)
171 while (i
< MAXMIFS
&& mifs
[i
].ifindex
!= ifindex
)
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
)
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
);
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
);
192 struct mrib_route
*route
= malloc(sizeof(*route
));
194 route
->group
= *group
;
195 route
->source
= *source
;
196 route
->valid_until
= omgp_time() + MRIB_DEFAULT_LIFETIME
* OMGP_TIME_PER_SECOND
;
198 if (list_empty(&iface
->routes
))
199 uloop_timeout_set(&iface
->timer
, MRIB_DEFAULT_LIFETIME
* OMGP_TIME_PER_SECOND
);
201 list_add_tail(&route
->head
, &iface
->routes
);
202 mrib_set(group
, source
, iface
, filter
, 0);
206 // Calculate IGMP-checksum
207 static uint16_t igmp_checksum(const uint16_t *buf
, size_t len
)
213 sum
= (sum
+ (sum
>> 16)) & 0xffff;
218 sum
+= *((uint8_t*)buf
);
219 sum
+= (sum
+ (sum
>> 16)) & 0xffff;
225 // Receive and handle MRT event
226 static void mrib_receive_mrt(struct uloop_fd
*fd
, __unused
unsigned flags
)
228 uint8_t buf
[9216], cbuf
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
229 char addrbuf
[INET_ADDRSTRLEN
];
230 struct sockaddr_in from
;
233 struct iovec iov
= {buf
, sizeof(buf
)};
234 struct msghdr hdr
= {
235 .msg_name
= (void*)&from
,
236 .msg_namelen
= sizeof(from
),
240 .msg_controllen
= sizeof(cbuf
)
243 ssize_t len
= recvmsg(fd
->fd
, &hdr
, MSG_DONTWAIT
);
244 if (len
< 0 && errno
== EAGAIN
)
247 struct iphdr
*iph
= iov
.iov_base
;
248 if (len
< (ssize_t
)sizeof(*iph
))
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
];
259 L_WARN("MRT kernel-message for unknown MIF %i", msg
->im_vif
);
263 if (msg
->im_msgtype
!= IGMPMSG_NOCACHE
) {
264 L_WARN("Unknown MRT kernel-message %i on interface %d",
265 msg
->im_msgtype
, iface
->ifindex
);
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
;
276 mrib_notify_newsource(iface
, &dst
, &src
);
279 if ((len
-= iph
->ihl
* 4) < 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
;
293 inet_ntop(AF_INET
, &from
.sin_addr
, addrbuf
, sizeof(addrbuf
));
294 struct igmphdr
*igmp
= (struct igmphdr
*)&buf
[iph
->ihl
* 4];
296 uint16_t checksum
= igmp
->csum
;
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
);
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
);
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
)
320 q
->cb_igmp(q
, igmp
, len
, &from
);
326 // Receive and handle MRT6 event
327 static void mrib_receive_mrt6(struct uloop_fd
*fd
, __unused
unsigned flags
)
329 uint8_t buf
[9216], cbuf
[128];
330 char addrbuf
[INET6_ADDRSTRLEN
];
331 struct sockaddr_in6 from
;
334 struct iovec iov
= {buf
, sizeof(buf
)};
335 struct msghdr hdr
= {
336 .msg_name
= (void*)&from
,
337 .msg_namelen
= sizeof(from
),
341 .msg_controllen
= sizeof(cbuf
)
344 ssize_t len
= recvmsg(fd
->fd
, &hdr
, MSG_DONTWAIT
);
345 if (len
< 0 && errno
== EAGAIN
)
348 struct mld_hdr
*mld
= iov
.iov_base
;
349 if (len
< (ssize_t
)sizeof(*mld
))
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
];
360 L_WARN("MRT6 kernel-message for unknown MIF %i", msg
->im6_mif
);
364 if (msg
->im6_msgtype
!= MRT6MSG_NOCACHE
) {
365 L_WARN("Unknown MRT6 kernel-message %i on interface %d",
366 msg
->im6_msgtype
, iface
->ifindex
);
370 mrib_notify_newsource(iface
, &msg
->im6_dst
, &msg
->im6_src
);
372 int hlim
= 0, ifindex
= from
.sin6_scope_id
;
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
383 inet_ntop(AF_INET6
, &from
.sin6_addr
, addrbuf
, sizeof(addrbuf
));
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
);
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
)
396 q
->cb_mld(q
, mld
, len
, &from
);
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
)
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
),
414 .msg_controllen
= sizeof(cbuf
)
418 igmp
->csum
= igmp_checksum((uint16_t*)igmp
, len
);
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
));
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
))
432 ssize_t s
= sendmsg(mrt_fd
.fd
, &msg
, MSG_DONTWAIT
);
433 return (s
< 0) ? -errno
: (s
< (ssize_t
)len
) ? -EMSGSIZE
: 0;
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
)
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
),
448 .msg_controllen
= sizeof(cbuf
)
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
));
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
))
462 ssize_t s
= sendmsg(mrt6_fd
.fd
, &msg
, MSG_DONTWAIT
);
463 return (s
< 0) ? -errno
: (s
< (ssize_t
)len
) ? -EMSGSIZE
: 0;
467 static int mrib_init(void)
472 if ((fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
)) < 0)
476 if (setsockopt(fd
, IPPROTO_IP
, MRT_INIT
, &val
, sizeof(val
)))
479 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof(val
)))
482 // Configure IP header fields
483 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &val
, sizeof(val
)))
487 if (setsockopt(fd
, IPPROTO_IP
, IP_TOS
, &val
, sizeof(val
)))
491 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &val
, sizeof(val
)))
494 // Set router-alert option
495 if (setsockopt(fd
, IPPROTO_IP
, IP_OPTIONS
, &ipv4_rtr_alert
, sizeof(ipv4_rtr_alert
)))
501 if ((fd
= socket(AF_INET6
, SOCK_RAW
, IPPROTO_ICMPV6
)) < 0)
504 // We need to know the source interface and hop-opts
506 if (setsockopt(fd
, IPPROTO_IPV6
, MRT6_INIT
, &val
, sizeof(val
)))
509 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_RECVHOPOPTS
, &val
, sizeof(val
)))
512 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
, sizeof(val
)))
515 // MLD has hoplimit 1
516 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
)))
520 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
, sizeof(val
)))
523 // Let the kernel compute our checksums
525 if (setsockopt(fd
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
)))
528 // Set hop-by-hop router alert on outgoing
529 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_HOPOPTS
, &ipv6_rtr_alert
, sizeof(ipv6_rtr_alert
)))
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
)))
544 mrt_fd
.cb
= mrib_receive_mrt
;
545 mrt6_fd
.cb
= mrib_receive_mrt6
;
547 uloop_fd_add(&mrt_fd
, ULOOP_READ
| ULOOP_EDGE_TRIGGER
);
548 uloop_fd_add(&mrt6_fd
, ULOOP_READ
| ULOOP_EDGE_TRIGGER
);
559 // Create new interface entry
560 static struct mrib_iface
* mrib_get_iface(int ifindex
)
562 if (mrt_fd
.fd
< 0 && mrib_init() < 0)
565 size_t mifid
= mrib_find(ifindex
);
570 if ((mifid
= mrib_find(0)) >= MAXMIFS
)
573 struct mrib_iface
*iface
= &mifs
[mifid
];
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
)))
579 struct mif6ctl ctl6
= {mifid
, 0, 1, ifindex
, 0};
580 if (setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, MRT6_ADD_MIF
, &ctl6
, sizeof(ctl6
)))
583 struct ip_mreqn mreq
= {{INADDR_ALLIGMPV3RTRS_GROUP
}, {INADDR_ANY
}, ifindex
};
584 setsockopt(mrt_fd
.fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
));
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
));
589 struct ipv6_mreq mreq6
= {MLD2_ALL_MCR_INIT
, ifindex
};
590 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
592 mreq6
.ipv6mr_multiaddr
.s6_addr
[15] = 0x02;
593 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
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
);
603 // Remove interfaces if it has no more users
604 static void mrib_clean_iface(struct mrib_iface
*iface
)
606 if (list_empty(&iface
->users
) && list_empty(&iface
->queriers
)) {
608 mrib_clean(&iface
->timer
);
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
));
615 struct mif6ctl ctl6
= {mifid
, 0, 1, iface
->ifindex
, 0};
616 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, MRT6_DEL_MIF
, &ctl6
, sizeof(ctl6
));
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
));
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
));
624 struct ipv6_mreq mreq6
= {MLD2_ALL_MCR_INIT
, iface
->ifindex
};
625 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
627 mreq6
.ipv6mr_multiaddr
.s6_addr
[15] = 0x02;
628 setsockopt(mrt6_fd
.fd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
));
632 // Register a new interface to mrib
633 int mrib_attach_user(struct mrib_user
*user
, int ifindex
, mrib_cb
*cb_newsource
)
635 struct mrib_iface
*iface
= mrib_get_iface(ifindex
);
639 if (user
->iface
== iface
)
642 list_add(&user
->head
, &iface
->users
);
644 user
->cb_newsource
= cb_newsource
;
648 // Deregister an interface from mrib
649 void mrib_detach_user(struct mrib_user
*user
)
651 struct mrib_iface
*iface
= user
->iface
;
656 list_del(&user
->head
);
657 mrib_clean_iface(iface
);
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
)
663 struct mrib_iface
*iface
= mrib_get_iface(ifindex
);
667 list_add(&querier
->head
, &iface
->queriers
);
668 querier
->iface
= iface
;
669 querier
->cb_igmp
= cb_igmp
;
670 querier
->cb_mld
= cb_mld
;
674 // Deregister a querier from mrib
675 void mrib_detach_querier(struct mrib_querier
*querier
)
677 struct mrib_iface
*iface
= querier
->iface
;
681 querier
->iface
= NULL
;
682 list_del(&querier
->head
);
683 mrib_clean_iface(iface
);
686 static uint8_t prefix_contains(const struct in6_addr
*p
, uint8_t plen
, const struct in6_addr
*addr
)
688 int blen
= plen
>> 3;
689 if(blen
&& memcmp(p
, addr
, blen
))
692 int rem
= plen
& 0x07;
693 if(rem
&& ((p
->s6_addr
[blen
] ^ addr
->s6_addr
[blen
]) >> (8 - rem
)))
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
)
702 struct mrib_iface
*iface
= user
->iface
;
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
);
719 mrib_clean(&iface
->timer
);
721 return (found
) ? 0 : -ENOENT
;
724 // Add an interface to the filter
725 int mrib_filter_add(mrib_filter
*filter
, struct mrib_user
*user
)
727 struct mrib_iface
*iface
= user
->iface
;
731 *filter
|= 1 << (iface
- mifs
);
735 // Get MLD source address
736 int mrib_mld_source(struct mrib_querier
*q
, struct in6_addr
*source
)
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
);
743 if (sock
< 0 || connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)))
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
);
754 *source
= addr
.sin6_addr
;
760 // Get IGMP source address
761 int mrib_igmp_source(struct mrib_querier
*q
, struct in_addr
*source
)
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
);
769 ifr
.ifr_ifindex
= q
->iface
->ifindex
;
771 if (sock
< 0 || ioctl(sock
, SIOCGIFNAME
, &ifr
) ||
772 setsockopt(sock
, SOL_SOCKET
, SO_BINDTODEVICE
, ifr
.ifr_name
, strlen(ifr
.ifr_name
)))
776 if (ret
|| connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)))
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
);
787 *source
= addr
.sin_addr
;