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 <arpa/inet.h>
26 #include <netinet/in.h>
27 #include <linux/mroute6.h>
28 #include <libubox/list.h>
29 #include <libubox/avl.h>
34 // Add / update / remove a client entry for a multicast group
35 int client_set(struct client
*client
, const struct in6_addr
*group
,
36 bool include
, const struct in6_addr sources
[], size_t cnt
)
38 int family
= (IN6_IS_ADDR_V4MAPPED(group
)) ? AF_INET
: AF_INET6
;
39 int sol
= (family
== AF_INET
) ? SOL_IP
: SOL_IPV6
;
40 char addrbuf
[INET6_ADDRSTRLEN
];
41 size_t len
= sizeof(struct group_filter
) + cnt
* sizeof(struct sockaddr_storage
);
43 struct group_filter f
;
44 struct sockaddr_storage s
[];
45 } *filter
= alloca(len
);
46 struct sockaddr_in
*in_addr
= (struct sockaddr_in
*)&filter
->f
.gf_group
;
47 struct sockaddr_in6
*in6_addr
= (struct sockaddr_in6
*)&filter
->f
.gf_group
;
49 inet_ntop(AF_INET6
, group
, addrbuf
, sizeof(addrbuf
));
50 L_DEBUG("%s: %s on %d => %s (+%d sources)", __FUNCTION__
, addrbuf
,
51 client
->ifindex
, (include
) ? "include" : "exclude", (int)cnt
);
53 // Construct MSFILTER for outgoing IGMP / MLD
54 memset(filter
, 0, len
);
55 filter
->f
.gf_interface
= client
->ifindex
;
56 filter
->f
.gf_fmode
= include
? MCAST_INCLUDE
: MCAST_EXCLUDE
;
57 filter
->f
.gf_group
.ss_family
= family
;
58 filter
->f
.gf_numsrc
= cnt
;
60 if (family
== AF_INET
)
61 client_unmap(&in_addr
->sin_addr
, group
);
63 in6_addr
->sin6_addr
= *group
;
65 for (size_t i
= 0; i
< cnt
; ++i
) {
66 filter
->f
.gf_slist
[i
].ss_family
= family
;
68 in_addr
= (struct sockaddr_in
*)&filter
->f
.gf_slist
[i
];
69 in6_addr
= (struct sockaddr_in6
*)&filter
->f
.gf_slist
[i
];
71 if (family
== AF_INET
)
72 client_unmap(&in_addr
->sin_addr
, &sources
[i
]);
74 in6_addr
->sin6_addr
= sources
[i
];
77 int fd
= (family
== AF_INET
) ? client
->igmp_fd
: client
->mld_fd
;
78 setsockopt(fd
, sol
, MCAST_LEAVE_GROUP
, filter
, sizeof(struct group_req
));
79 if (!include
|| cnt
> 0) {
80 if (setsockopt(fd
, sol
, MCAST_JOIN_GROUP
, filter
, sizeof(struct group_req
))
81 && family
== AF_INET
&& errno
== ENOBUFS
) {
82 L_WARN("proxy: kernel denied joining multicast group. check igmp_max_memberships?");
86 if (setsockopt(fd
, sol
, MCAST_MSFILTER
, filter
, len
))
92 // Initialize client-instance
93 int client_init(struct client
*client
, int ifindex
)
95 client
->igmp_fd
= socket(AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
, 0);
96 if (client
->igmp_fd
< 0)
99 client
->mld_fd
= socket(AF_INET6
, SOCK_DGRAM
| SOCK_CLOEXEC
, 0);
100 if (client
->mld_fd
< 0)
103 client
->ifindex
= ifindex
;
107 // Cleanup client-instance
108 void client_deinit(struct client
*client
)
110 if (client
->ifindex
) {
111 close(client
->igmp_fd
);
112 close(client
->mld_fd
);
113 client
->igmp_fd
= -1;