1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2007-2009 Bruce Simpson.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "namespace.h"
33
34 #include <sys/param.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37
38 #include <net/if_dl.h>
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_var.h>
44
45 #include <assert.h>
46 #include <errno.h>
47 #include <ifaddrs.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include "un-namespace.h"
52
53 /*
54 * Advanced (Full-state) multicast group membership APIs [RFC3678]
55 * Currently this module assumes IPv4 support (INET) in the base system.
56 */
57 #ifndef INET
58 #define INET
59 #endif
60
61 union sockunion {
62 struct sockaddr_storage ss;
63 struct sockaddr sa;
64 struct sockaddr_dl sdl;
65 #ifdef INET
66 struct sockaddr_in sin;
67 #endif
68 #ifdef INET6
69 struct sockaddr_in6 sin6;
70 #endif
71 };
72 typedef union sockunion sockunion_t;
73
74 #ifndef MIN
75 #define MIN(a, b) ((a) < (b) ? (a) : (b))
76 #endif
77
78 /*
79 * Internal: Map an IPv4 unicast address to an interface index.
80 * This is quite inefficient so it is recommended applications use
81 * the newer, more portable, protocol independent API.
82 */
83 static uint32_t
__inaddr_to_index(in_addr_t ifaddr)84 __inaddr_to_index(in_addr_t ifaddr)
85 {
86 struct ifaddrs *ifa;
87 struct ifaddrs *ifaddrs;
88 char *ifname;
89 int ifindex;
90 sockunion_t *psu;
91
92 if (getifaddrs(&ifaddrs) < 0)
93 return (0);
94
95 ifindex = 0;
96 ifname = NULL;
97
98 /*
99 * Pass #1: Find the ifaddr entry corresponding to the
100 * supplied IPv4 address. We should really use the ifindex
101 * consistently for matches, however it is not available to
102 * us on this pass.
103 */
104 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
105 psu = (sockunion_t *)ifa->ifa_addr;
106 if (psu && psu->ss.ss_family == AF_INET &&
107 psu->sin.sin_addr.s_addr == ifaddr) {
108 ifname = ifa->ifa_name;
109 break;
110 }
111 }
112 if (ifname == NULL)
113 goto out;
114
115 /*
116 * Pass #2: Find the index of the interface matching the name
117 * we obtained from looking up the IPv4 ifaddr in pass #1.
118 * There must be a better way of doing this.
119 */
120 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
121 psu = (sockunion_t *)ifa->ifa_addr;
122 if (psu && psu->ss.ss_family == AF_LINK &&
123 strcmp(ifa->ifa_name, ifname) == 0) {
124 ifindex = LLINDEX(&psu->sdl);
125 break;
126 }
127 }
128 assert(ifindex != 0);
129
130 out:
131 freeifaddrs(ifaddrs);
132 return (ifindex);
133 }
134
135 /*
136 * Set IPv4 source filter list in use on socket.
137 *
138 * Stubbed to setsourcefilter(). Performs conversion of structures which
139 * may be inefficient; applications are encouraged to use the
140 * protocol-independent API.
141 */
142 int
setipv4sourcefilter(int s,struct in_addr interface,struct in_addr group,uint32_t fmode,uint32_t numsrc,struct in_addr * slist)143 setipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
144 uint32_t fmode, uint32_t numsrc, struct in_addr *slist)
145 {
146 #ifdef INET
147 sockunion_t tmpgroup;
148 struct in_addr *pina;
149 sockunion_t *psu, *tmpslist;
150 int err;
151 size_t i;
152 uint32_t ifindex;
153
154 assert(s != -1);
155
156 tmpslist = NULL;
157
158 if (!IN_MULTICAST(ntohl(group.s_addr)) ||
159 (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) {
160 errno = EINVAL;
161 return (-1);
162 }
163
164 ifindex = __inaddr_to_index(interface.s_addr);
165 if (ifindex == 0) {
166 errno = EADDRNOTAVAIL;
167 return (-1);
168 }
169
170 memset(&tmpgroup, 0, sizeof(sockunion_t));
171 tmpgroup.sin.sin_family = AF_INET;
172 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
173 tmpgroup.sin.sin_addr = group;
174
175 if (numsrc != 0 || slist != NULL) {
176 tmpslist = calloc(numsrc, sizeof(sockunion_t));
177 if (tmpslist == NULL) {
178 errno = ENOMEM;
179 return (-1);
180 }
181
182 pina = slist;
183 psu = tmpslist;
184 for (i = 0; i < numsrc; i++, pina++, psu++) {
185 psu->sin.sin_family = AF_INET;
186 psu->sin.sin_len = sizeof(struct sockaddr_in);
187 psu->sin.sin_addr = *pina;
188 }
189 }
190
191 err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
192 sizeof(struct sockaddr_in), fmode, numsrc,
193 (struct sockaddr_storage *)tmpslist);
194
195 if (tmpslist != NULL)
196 free(tmpslist);
197
198 return (err);
199 #else /* !INET */
200 return (EAFNOSUPPORT);
201 #endif /* INET */
202 }
203
204 /*
205 * Get IPv4 source filter list in use on socket.
206 *
207 * Stubbed to getsourcefilter(). Performs conversion of structures which
208 * may be inefficient; applications are encouraged to use the
209 * protocol-independent API.
210 * An slist of NULL may be used for guessing the required buffer size.
211 */
212 int
getipv4sourcefilter(int s,struct in_addr interface,struct in_addr group,uint32_t * fmode,uint32_t * numsrc,struct in_addr * slist)213 getipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
214 uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist)
215 {
216 sockunion_t *psu, *tmpslist;
217 sockunion_t tmpgroup;
218 struct in_addr *pina;
219 int err;
220 size_t i;
221 uint32_t ifindex, onumsrc;
222
223 assert(s != -1);
224 assert(fmode != NULL);
225 assert(numsrc != NULL);
226
227 onumsrc = *numsrc;
228 *numsrc = 0;
229 tmpslist = NULL;
230
231 if (!IN_MULTICAST(ntohl(group.s_addr)) ||
232 (onumsrc != 0 && slist == NULL)) {
233 errno = EINVAL;
234 return (-1);
235 }
236
237 ifindex = __inaddr_to_index(interface.s_addr);
238 if (ifindex == 0) {
239 errno = EADDRNOTAVAIL;
240 return (-1);
241 }
242
243 memset(&tmpgroup, 0, sizeof(sockunion_t));
244 tmpgroup.sin.sin_family = AF_INET;
245 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
246 tmpgroup.sin.sin_addr = group;
247
248 if (onumsrc != 0 || slist != NULL) {
249 tmpslist = calloc(onumsrc, sizeof(sockunion_t));
250 if (tmpslist == NULL) {
251 errno = ENOMEM;
252 return (-1);
253 }
254 }
255
256 err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
257 sizeof(struct sockaddr_in), fmode, numsrc,
258 (struct sockaddr_storage *)tmpslist);
259
260 if (tmpslist != NULL && *numsrc != 0) {
261 pina = slist;
262 psu = tmpslist;
263 for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) {
264 if (psu->ss.ss_family != AF_INET)
265 continue;
266 *pina++ = psu->sin.sin_addr;
267 }
268 free(tmpslist);
269 }
270
271 return (err);
272 }
273
274 /*
275 * Set protocol-independent source filter list in use on socket.
276 */
277 int
setsourcefilter(int s,uint32_t interface,struct sockaddr * group,socklen_t grouplen,uint32_t fmode,uint32_t numsrc,struct sockaddr_storage * slist)278 setsourcefilter(int s, uint32_t interface, struct sockaddr *group,
279 socklen_t grouplen, uint32_t fmode, uint32_t numsrc,
280 struct sockaddr_storage *slist)
281 {
282 struct __msfilterreq msfr;
283 sockunion_t *psu;
284 int level, optname;
285
286 if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) {
287 errno = EINVAL;
288 return (-1);
289 }
290
291 psu = (sockunion_t *)group;
292 switch (psu->ss.ss_family) {
293 #ifdef INET
294 case AF_INET:
295 if ((grouplen != sizeof(struct sockaddr_in) ||
296 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
297 errno = EINVAL;
298 return (-1);
299 }
300 level = IPPROTO_IP;
301 optname = IP_MSFILTER;
302 break;
303 #endif
304 #ifdef INET6
305 case AF_INET6:
306 if (grouplen != sizeof(struct sockaddr_in6) ||
307 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
308 errno = EINVAL;
309 return (-1);
310 }
311 level = IPPROTO_IPV6;
312 optname = IPV6_MSFILTER;
313 break;
314 #endif
315 default:
316 errno = EAFNOSUPPORT;
317 return (-1);
318 }
319
320 memset(&msfr, 0, sizeof(msfr));
321 msfr.msfr_ifindex = interface;
322 msfr.msfr_fmode = fmode;
323 msfr.msfr_nsrcs = numsrc;
324 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
325 msfr.msfr_srcs = slist; /* pointer */
326
327 return (_setsockopt(s, level, optname, &msfr, sizeof(msfr)));
328 }
329
330 /*
331 * Get protocol-independent source filter list in use on socket.
332 * An slist of NULL may be used for guessing the required buffer size.
333 */
334 int
getsourcefilter(int s,uint32_t interface,struct sockaddr * group,socklen_t grouplen,uint32_t * fmode,uint32_t * numsrc,struct sockaddr_storage * slist)335 getsourcefilter(int s, uint32_t interface, struct sockaddr *group,
336 socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc,
337 struct sockaddr_storage *slist)
338 {
339 struct __msfilterreq msfr;
340 sockunion_t *psu;
341 socklen_t optlen;
342 int err, level, nsrcs, optname;
343
344 if (interface == 0 || group == NULL || numsrc == NULL ||
345 fmode == NULL) {
346 errno = EINVAL;
347 return (-1);
348 }
349
350 nsrcs = *numsrc;
351 *numsrc = 0;
352 *fmode = 0;
353
354 psu = (sockunion_t *)group;
355 switch (psu->ss.ss_family) {
356 #ifdef INET
357 case AF_INET:
358 if ((grouplen != sizeof(struct sockaddr_in) ||
359 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
360 errno = EINVAL;
361 return (-1);
362 }
363 level = IPPROTO_IP;
364 optname = IP_MSFILTER;
365 break;
366 #endif
367 #ifdef INET6
368 case AF_INET6:
369 if (grouplen != sizeof(struct sockaddr_in6) ||
370 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
371 errno = EINVAL;
372 return (-1);
373 }
374 level = IPPROTO_IPV6;
375 optname = IPV6_MSFILTER;
376 break;
377 #endif
378 default:
379 errno = EAFNOSUPPORT;
380 return (-1);
381 break;
382 }
383
384 optlen = sizeof(struct __msfilterreq);
385 memset(&msfr, 0, optlen);
386 msfr.msfr_ifindex = interface;
387 msfr.msfr_fmode = 0;
388 msfr.msfr_nsrcs = nsrcs;
389 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
390
391 /*
392 * msfr_srcs is a pointer to a vector of sockaddr_storage. It
393 * may be NULL. The kernel will always return the total number
394 * of filter entries for the group in msfr.msfr_nsrcs.
395 */
396 msfr.msfr_srcs = slist;
397 err = _getsockopt(s, level, optname, &msfr, &optlen);
398 if (err == 0) {
399 *numsrc = msfr.msfr_nsrcs;
400 *fmode = msfr.msfr_fmode;
401 }
402
403 return (err);
404 }
405