1/*
2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * ifutil.c
26 * - network interface utility routines
27 */
28
29/*
30 * Modification History
31 *
32 * June 23, 2009	Dieter Siegmund (dieter@apple.com)
33 * - split out from ipconfigd.c
34 */
35
36
37#include <stdlib.h>
38#include <unistd.h>
39#include <syslog.h>
40#include <string.h>
41#include <sys/socket.h>
42#include <net/if.h>
43#include <netinet/in.h>
44#include <netinet/in_var.h>
45#include <netinet/icmp6.h>
46#include <net/if_media.h>
47#include <net/if_dl.h>
48#include <sys/types.h>
49#include <arpa/inet.h>
50#include <sys/param.h>
51#include <sys/sysctl.h>
52#include <sys/errno.h>
53#include <sys/ioctl.h>
54#include "ifutil.h"
55#include "rtutil.h"
56#include "symbol_scope.h"
57#include "mylog.h"
58#include "CGA.h"
59
60PRIVATE_EXTERN int
61inet_dgram_socket()
62{
63    return (socket(AF_INET, SOCK_DGRAM, 0));
64}
65
66STATIC int
67siocsifflags(int s, const char * name, short flags)
68{
69    struct ifreq	ifr;
70    int 		ret;
71
72    bzero(&ifr, sizeof(ifr));
73    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
74    ret = ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr);
75    if (ret < 0) {
76	return (ret);
77    }
78    ifr.ifr_flags |= flags;
79    return (ioctl(s, SIOCSIFFLAGS, &ifr));
80}
81
82STATIC int
83siocsifmtu(int s, const char * name, int mtu)
84{
85    struct ifreq	ifr;
86
87    bzero(&ifr, sizeof(ifr));
88    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
89    ifr.ifr_mtu = mtu;
90    return (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr));
91}
92
93STATIC int
94siocprotoattach(int s, const char * name)
95{
96    struct ifreq	ifr;
97
98    bzero(&ifr, sizeof(ifr));
99    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
100    return (ioctl(s, SIOCPROTOATTACH, &ifr));
101}
102
103PRIVATE_EXTERN int
104interface_set_mtu(const char * ifname, int mtu)
105{
106    int ret = 0;
107    int s = inet_dgram_socket();
108
109    if (s < 0) {
110	ret = errno;
111    }
112    else {
113	if (siocsifmtu(s, ifname, mtu) < 0) {
114	    ret = errno;
115	    my_log(LOG_NOTICE, "siocsifmtu(%s, %d) failed, %s (%d)",
116		   ifname, mtu, strerror(ret), ret);
117	}
118	close(s);
119    }
120    return (ret);
121
122}
123
124PRIVATE_EXTERN int
125inet_attach_interface(const char * ifname)
126{
127    int ret = 0;
128    int s = inet_dgram_socket();
129
130    if (s < 0) {
131	ret = errno;
132	goto done;
133    }
134
135    if (siocprotoattach(s, ifname) < 0) {
136	ret = errno;
137	if (ret != EEXIST && ret != ENXIO) {
138	    my_log(LOG_DEBUG, "siocprotoattach(%s) failed, %s (%d)",
139		   ifname, strerror(errno), errno);
140	}
141    }
142    (void)siocsifflags(s, ifname, IFF_UP);
143    close(s);
144
145 done:
146    return (ret);
147}
148
149STATIC int
150siocprotodetach(int s, const char * name)
151{
152    struct ifreq	ifr;
153
154    bzero(&ifr, sizeof(ifr));
155    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
156    return (ioctl(s, SIOCPROTODETACH, &ifr));
157}
158
159PRIVATE_EXTERN int
160inet_detach_interface(const char * ifname)
161{
162    int ret = 0;
163    int s = inet_dgram_socket();
164
165    if (s < 0) {
166	ret = errno;
167	goto done;
168    }
169    if (siocprotodetach(s, ifname) < 0) {
170	ret = errno;
171	if (ret != ENXIO) {
172	    my_log(LOG_ERR, "siocprotodetach(%s) failed, %s (%d)",
173		   ifname, strerror(errno), errno);
174	}
175    }
176    close(s);
177
178 done:
179    return (ret);
180}
181
182STATIC int
183siocautoaddr(int s, const char * name, int value)
184{
185    struct ifreq	ifr;
186
187    bzero(&ifr, sizeof(ifr));
188    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
189    ifr.ifr_data = (caddr_t)(intptr_t)value;
190    return (ioctl(s, SIOCAUTOADDR, &ifr));
191}
192
193PRIVATE_EXTERN int
194inet_set_autoaddr(const char * ifname, int val)
195{
196    int 		s = inet_dgram_socket();
197    int			ret = 0;
198
199    if (s < 0) {
200	ret = errno;
201	my_log(LOG_ERR,
202	       "inet_set_autoaddr(%s, %d): socket() failed, %s (%d)",
203	       ifname, val, strerror(errno), errno);
204    }
205    else {
206	if (siocautoaddr(s, ifname, val) < 0) {
207	    ret = errno;
208	    if (ret != ENXIO) {
209		my_log(LOG_ERR, "inet_set_autoaddr(%s, %d) failed, %s (%d)",
210		       ifname, val, strerror(errno), errno);
211	    }
212	}
213	close(s);
214    }
215    return (ret);
216}
217
218STATIC void
219set_sockaddr_in(struct sockaddr_in * sin_p, struct in_addr addr)
220{
221    sin_p->sin_len = sizeof(struct sockaddr_in);
222    sin_p->sin_family = AF_INET;
223    sin_p->sin_addr = addr;
224    return;
225}
226
227PRIVATE_EXTERN int
228inet_difaddr(int s, const char * name, const struct in_addr addr)
229{
230    struct ifreq	ifr;
231
232    bzero(&ifr, sizeof(ifr));
233    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
234    /* ALIGN: ifr.ifr_addr is aligned (in union), cast okay. */
235    set_sockaddr_in((struct sockaddr_in *)(void *)&ifr.ifr_addr, addr);
236    return (ioctl(s, SIOCDIFADDR, &ifr));
237}
238
239PRIVATE_EXTERN int
240inet_aifaddr(int s, const char * name, struct in_addr addr,
241	     const struct in_addr * mask,
242	     const struct in_addr * broadaddr)
243{
244    struct in_aliasreq	ifra;
245
246    bzero(&ifra, sizeof(ifra));
247    strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
248    set_sockaddr_in(&ifra.ifra_addr, addr);
249    if (mask != NULL) {
250	set_sockaddr_in(&ifra.ifra_mask, *mask);
251    }
252    if (broadaddr != NULL) {
253	set_sockaddr_in(&ifra.ifra_broadaddr, *broadaddr);
254    }
255    return (ioctl(s, SIOCAIFADDR, &ifra));
256}
257
258#include <netinet6/in6_var.h>
259#include <netinet6/nd6.h>
260
261STATIC int
262count_prefix_bits(void * val, int size)
263{
264    int		bit;
265    int 	byte;
266    uint8_t *	name = (uint8_t *)val;
267    int		plen = 0;
268
269    /* look for prefix bytes that have all bits set */
270    for (byte = 0; byte < size; byte++, plen += 8) {
271	if (name[byte] != 0xff) {
272	    break;
273	}
274    }
275
276    /* all of the bits were set */
277    if (byte == size) {
278	return (plen);
279    }
280
281    /* we have the prefix length when we seee the first bit that isn't set */
282    for (bit = 7; bit != 0; bit--, plen++) {
283	if (!(name[byte] & (1 << bit))) {
284	    break;
285	}
286    }
287
288    /* valididate that no bits are set after the last bit */
289    for (; bit != 0; bit--) {
290	if (name[byte] & (1 << bit)) {
291	    /* not a simple prefix */
292	    return (0);
293	}
294    }
295    byte++;
296    for (; byte < size; byte++) {
297	if (name[byte]) {
298	    /* not a simple prefix */
299	    return (0);
300	}
301    }
302    return (plen);
303}
304
305PRIVATE_EXTERN int
306inet6_dgram_socket()
307{
308    return (socket(AF_INET6, SOCK_DGRAM, 0));
309}
310
311STATIC int
312siocprotoattach_in6(int s, const char * name)
313{
314    struct in6_aliasreq		ifra;
315
316    bzero(&ifra, sizeof(ifra));
317    strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
318    return (ioctl(s, SIOCPROTOATTACH_IN6, &ifra));
319}
320
321STATIC int
322siocprotodetach_in6(int s, const char * name)
323{
324    struct in6_ifreq	ifr;
325
326    bzero(&ifr, sizeof(ifr));
327    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
328    return (ioctl(s, SIOCPROTODETACH_IN6, &ifr));
329}
330
331STATIC int
332siocll_start(int s, const char * name)
333{
334    struct in6_aliasreq		ifra_in6;
335
336    bzero(&ifra_in6, sizeof(ifra_in6));
337    strncpy(ifra_in6.ifra_name, name, sizeof(ifra_in6.ifra_name));
338    return (ioctl(s, SIOCLL_START, &ifra_in6));
339}
340
341STATIC int
342ll_start(int s, const char * name, boolean_t use_cga)
343{
344    struct in6_llstartreq	req;
345
346    if (use_cga == FALSE || !CGAIsEnabled()) {
347	return (siocll_start(s, name));
348    }
349    bzero(&req, sizeof(req));
350    strncpy(req.llsr_name, name, sizeof(req.llsr_name));
351    CGAPrepareSetForInterface(name, &req.llsr_cgaprep);
352    req.llsr_lifetime.ia6t_vltime = -1;
353    req.llsr_lifetime.ia6t_pltime = -1;
354    return (ioctl(s, SIOCLL_CGASTART, &req));
355}
356
357STATIC int
358siocll_stop(int s, const char * name)
359{
360    struct in6_ifreq		ifr;
361
362    bzero(&ifr, sizeof(ifr));
363    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
364    return (ioctl(s, SIOCLL_STOP, &ifr));
365}
366
367STATIC void
368set_sockaddr_in6(struct sockaddr_in6 * sin6_p, const struct in6_addr * addr)
369{
370    sin6_p->sin6_family = AF_INET6;
371    sin6_p->sin6_len = sizeof(struct sockaddr_in6);
372    sin6_p->sin6_addr = *addr;
373    return;
374}
375
376STATIC int
377siocgifaflag_in6(int s, const char * ifname, const struct in6_addr * in6_addr,
378		 uint16_t * ret_flags)
379{
380    struct in6_ifreq	ifr6;
381
382    bzero((char *)&ifr6, sizeof(ifr6));
383    strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
384    set_sockaddr_in6(&ifr6.ifr_addr, in6_addr);
385    if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
386	return (-1);
387    }
388    *ret_flags = ifr6.ifr_ifru.ifru_flags6;
389    return (0);
390}
391
392PRIVATE_EXTERN int
393inet6_attach_interface(const char * ifname)
394{
395    int	ret = 0;
396    int s = inet6_dgram_socket();
397
398    if (s < 0) {
399	ret = errno;
400	my_log(LOG_ERR,
401	       "inet6_attach_interface(%s): socket() failed, %s (%d)",
402	       ifname, strerror(ret), ret);
403	goto done;
404    }
405    if (siocprotoattach_in6(s, ifname) < 0) {
406	ret = errno;
407	if (ret != EEXIST && ret != ENXIO) {
408	    my_log(LOG_DEBUG, "siocprotoattach_in6(%s) failed, %s (%d)",
409		   ifname, strerror(errno), errno);
410	}
411    }
412    (void)siocsifflags(s, ifname, IFF_UP);
413    close(s);
414
415 done:
416    return (ret);
417}
418
419PRIVATE_EXTERN int
420inet6_detach_interface(const char * ifname)
421{
422    int ret = 0;
423    int s = inet6_dgram_socket();
424
425    if (s < 0) {
426	ret = errno;
427	my_log(LOG_ERR,
428	       "inet6_detach_interface(%s): socket() failed, %s (%d)",
429	       ifname, strerror(ret), ret);
430	goto done;
431    }
432    if (siocprotodetach_in6(s, ifname) < 0) {
433	ret = errno;
434	if (ret != ENXIO) {
435	    my_log(LOG_DEBUG, "siocprotodetach_in6(%s) failed, %s (%d)",
436		   ifname, strerror(errno), errno);
437	}
438    }
439    close(s);
440
441 done:
442    return (ret);
443}
444
445PRIVATE_EXTERN int
446inet6_linklocal_start(const char * ifname, boolean_t use_cga)
447{
448    int ret = 0;
449    int s = inet6_dgram_socket();
450
451    if (s < 0) {
452	ret = errno;
453	my_log(LOG_ERR,
454	       "inet6_linklocal_start(%s): socket() failed, %s (%d)",
455	       ifname, strerror(ret), ret);
456	goto done;
457    }
458    if (ll_start(s, ifname, use_cga) < 0) {
459	ret = errno;
460	if (errno != ENXIO) {
461	    my_log(LOG_ERR, "siocll_start(%s) failed, %s (%d)",
462		   ifname, strerror(errno), errno);
463	}
464    }
465    close(s);
466 done:
467    return (ret);
468}
469
470PRIVATE_EXTERN int
471inet6_linklocal_stop(const char * ifname)
472{
473    int ret = 0;
474    int s = inet6_dgram_socket();
475
476    if (s < 0) {
477	ret = errno;
478	my_log(LOG_ERR,
479	       "inet6_linklocal_stop(%s): socket() failed, %s (%d)",
480	       ifname, strerror(ret), ret);
481	goto done;
482    }
483    if (siocll_stop(s, ifname) < 0) {
484	ret = errno;
485	if (errno != ENXIO) {
486	    my_log(LOG_ERR, "siocll_stop(%s) failed, %s (%d)",
487		   ifname, strerror(errno), errno);
488	}
489    }
490    close(s);
491
492 done:
493    return (ret);
494}
495
496STATIC int
497siocautoconf_start(int s, const char * if_name)
498{
499    struct in6_ifreq	ifr;
500
501    bzero(&ifr, sizeof(ifr));
502    strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
503    return (ioctl(s, SIOCAUTOCONF_START, &ifr));
504}
505
506STATIC int
507siocautoconf_stop(int s, const char * if_name)
508{
509    struct in6_ifreq	ifr;
510
511    bzero(&ifr, sizeof(ifr));
512    strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
513    return (ioctl(s, SIOCAUTOCONF_STOP, &ifr));
514}
515
516PRIVATE_EXTERN int
517inet6_rtadv_enable(const char * if_name)
518{
519    int			ret = 0;
520    int			s = inet6_dgram_socket();
521
522    if (s < 0) {
523	ret = errno;
524	my_log(LOG_ERR,
525	       "inet6_rtadv_enable(%s): socket() failed, %s (%d)",
526	       if_name, strerror(ret), ret);
527	goto done;
528    }
529    if (siocautoconf_start(s, if_name) < 0) {
530	ret = errno;
531	if (errno != ENXIO) {
532	    my_log(LOG_ERR, "siocautoconf_start(%s) failed, %s (%d)",
533		   if_name, strerror(errno), errno);
534	}
535    }
536    close(s);
537 done:
538    return (ret);
539}
540
541PRIVATE_EXTERN int
542inet6_rtadv_disable(const char * if_name)
543{
544    int			ret = 0;
545    int			s = inet6_dgram_socket();
546
547    if (s < 0) {
548	ret = errno;
549	my_log(LOG_ERR,
550	       "inet6_rtadv_disable(%s): socket() failed, %s (%d)",
551	       if_name, strerror(ret), ret);
552	goto done;
553    }
554    if (siocautoconf_stop(s, if_name) < 0) {
555	ret = errno;
556	if (errno != ENXIO) {
557	    my_log(LOG_ERR, "siocautoconf_stop(%s) failed, %s (%d)",
558		   if_name, strerror(errno), errno);
559	}
560    }
561    close(s);
562 done:
563    return (ret);
564}
565
566PRIVATE_EXTERN int
567inet6_difaddr(int s, const char * name, const struct in6_addr * addr)
568{
569    struct in6_ifreq	ifr;
570
571    bzero(&ifr, sizeof(ifr));
572    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
573    if (addr != NULL) {
574	set_sockaddr_in6(&ifr.ifr_ifru.ifru_addr, addr);
575    }
576    return (ioctl(s, SIOCDIFADDR_IN6, &ifr));
577}
578
579/*
580 * from netinet6/in6.c
581 */
582STATIC void
583in6_len2mask(struct in6_addr * mask, int len)
584{
585    int i;
586
587    bzero(mask, sizeof(*mask));
588    for (i = 0; i < len / 8; i++)
589	mask->s6_addr[i] = 0xff;
590    if (len % 8)
591	mask->s6_addr[i] = (0xff00 >> (len % 8)) & 0xff;
592}
593
594STATIC void
595in6_maskaddr(struct in6_addr * addr, const struct in6_addr * mask)
596{
597    int i;
598
599    for (i = 0; i < sizeof(addr->s6_addr); i++) {
600	addr->s6_addr[i] &= mask->s6_addr[i];
601    }
602    return;
603}
604
605PRIVATE_EXTERN void
606in6_netaddr(struct in6_addr * addr, int len)
607{
608    struct in6_addr	mask;
609
610    in6_len2mask(&mask, len);
611    in6_maskaddr(addr, &mask);
612    return;
613}
614
615PRIVATE_EXTERN int
616inet6_get_prefix_length(const struct in6_addr * addr, int if_index)
617{
618    char *		buf = NULL;
619    size_t 		buf_len;
620    struct in6_prefix *	end;
621    int 		mib[] = {
622	CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST
623    };
624    struct in6_prefix *	next;
625    int			prefix_length = 0;
626    struct in6_prefix *	scan;
627
628    if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &buf_len, NULL, 0)
629	< 0) {
630	goto done;
631    }
632    buf_len += 1024;
633    buf = malloc(buf_len);
634    if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &buf_len, NULL, 0)
635	< 0) {
636	goto done;
637    }
638
639    /* ALIGN: buf is aligned (from malloc), cast ok. */
640    end = (struct in6_prefix *)(void *)(buf + buf_len);
641    for (scan = (struct in6_prefix *)(void *)buf; scan < end; scan = next) {
642	struct sockaddr_in6 *	advrtr;
643	struct in6_addr		netaddr;
644
645	advrtr = (struct sockaddr_in6 *)(scan + 1);
646	next = (struct in6_prefix *)&advrtr[scan->advrtrs];
647
648	if (if_index != 0 && if_index != scan->if_index) {
649	    continue;
650	}
651	netaddr = *addr;
652	in6_netaddr(&netaddr, scan->prefixlen);
653	if (IN6_ARE_ADDR_EQUAL(&netaddr, &scan->prefix.sin6_addr)) {
654	    prefix_length = scan->prefixlen;
655	    break;
656	}
657    }
658
659 done:
660    if (buf != NULL) {
661	free(buf);
662    }
663    return (prefix_length);
664}
665
666PRIVATE_EXTERN int
667inet6_aifaddr(int s, const char * name, const struct in6_addr * addr,
668	      const struct in6_addr * dstaddr, int prefix_length,
669	      u_int32_t valid_lifetime, u_int32_t preferred_lifetime)
670{
671    struct in6_aliasreq	ifra_in6;
672
673    bzero(&ifra_in6, sizeof(ifra_in6));
674    strncpy(ifra_in6.ifra_name, name, sizeof(ifra_in6.ifra_name));
675    ifra_in6.ifra_lifetime.ia6t_vltime = valid_lifetime;
676    ifra_in6.ifra_lifetime.ia6t_pltime = preferred_lifetime;
677    if (addr != NULL) {
678	set_sockaddr_in6(&ifra_in6.ifra_addr, addr);
679    }
680    if (dstaddr != NULL) {
681	set_sockaddr_in6(&ifra_in6.ifra_dstaddr, dstaddr);
682    }
683    if (prefix_length != 0) {
684	struct in6_addr		prefixmask;
685
686	in6_len2mask(&prefixmask, prefix_length);
687	set_sockaddr_in6(&ifra_in6.ifra_prefixmask, &prefixmask);
688    }
689    return (ioctl(s, SIOCAIFADDR_IN6, &ifra_in6));
690}
691
692STATIC int
693inet6_if_ioctl(const char * ifname, unsigned long request)
694{
695    struct in6_ifreq	ifr;
696    int 		ret = 0;
697    int			s;
698
699    s = inet6_dgram_socket();
700    if (s < 0) {
701	ret = errno;
702	goto done;
703    }
704    bzero(&ifr, sizeof(ifr));
705    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
706
707    if (ioctl(s, request, &ifr) < 0) {
708	ret = errno;
709    }
710 done:
711    if (s >=0) {
712	close(s);
713    }
714    return (ret);
715}
716
717PRIVATE_EXTERN int
718inet6_flush_prefixes(const char * ifname)
719{
720    return (inet6_if_ioctl(ifname, SIOCSPFXFLUSH_IN6));
721}
722
723PRIVATE_EXTERN int
724inet6_flush_routes(const char * ifname)
725{
726    return (inet6_if_ioctl(ifname, SIOCSRTRFLUSH_IN6));
727}
728
729STATIC boolean_t
730inet6_sysctl_get_int(int code, int * ret_value_p)
731{
732    int 	mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
733    size_t 	size;
734
735    mib[3] = code;
736    size = sizeof(*ret_value_p);
737    if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), ret_value_p, &size, NULL, 0)
738	< 0) {
739	my_log(LOG_ERR, "inet6_sysctl_get_int(%d) failed, %s", code,
740	       strerror(errno));
741	return (FALSE);
742    }
743    return (TRUE);
744}
745
746PRIVATE_EXTERN boolean_t
747inet6_forwarding_is_enabled(void)
748{
749    int		enabled = 0;
750
751    if (inet6_sysctl_get_int(IPV6CTL_FORWARDING, &enabled) == FALSE) {
752	return (FALSE);
753    }
754    return (enabled != 0);
755}
756
757PRIVATE_EXTERN boolean_t
758inet6_set_perform_nud(const char * if_name, boolean_t perform_nud)
759{
760    boolean_t		need_set = FALSE;
761    struct in6_ndireq 	nd;
762    int			s;
763    boolean_t		success = FALSE;
764
765    s = inet6_dgram_socket();
766    if (s < 0) {
767	my_log_fl(LOG_ERR, "socket failed, %s", strerror(errno));
768	goto done;
769    }
770    bzero(&nd, sizeof(nd));
771    strncpy(nd.ifname, if_name, sizeof(nd.ifname));
772    if (ioctl(s, SIOCGIFINFO_IN6, &nd)) {
773	my_log_fl(LOG_ERR, "SIOCGIFINFO_IN6(%s) failed, %s",
774		  if_name, strerror(errno));
775	goto done;
776    }
777    if (perform_nud) {
778	if ((nd.ndi.flags & ND6_IFF_PERFORMNUD) == 0) {
779	    nd.ndi.flags |= ND6_IFF_PERFORMNUD;
780	    need_set = TRUE;
781	}
782    }
783    else if ((nd.ndi.flags & ND6_IFF_PERFORMNUD) != 0) {
784	nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
785	need_set = TRUE;
786    }
787    if (need_set) {
788	if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd)) {
789	    my_log_fl(LOG_ERR, "SIOCSIFINFO_FLAGS(%s) failed, %s",
790		      if_name, strerror(errno));
791	    goto done;
792	}
793    }
794    success = TRUE;
795 done:
796    if (s >= 0) {
797	close(s);
798    }
799    return (success);
800}
801
802STATIC char *
803get_if_info(int if_index, int af, int * ret_len_p)
804{
805    char *			buf = NULL;
806    size_t			buf_len = 0;
807    int				mib[6];
808
809    mib[0] = CTL_NET;
810    mib[1] = PF_ROUTE;
811    mib[2] = 0;
812    mib[3] = af;
813    mib[4] = NET_RT_IFLIST;
814    mib[5] = if_index;
815
816    *ret_len_p = 0;
817    if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) {
818	fprintf(stderr, "sysctl() size failed: %s", strerror(errno));
819	goto failed;
820    }
821    buf_len *= 2; /* just in case something changes */
822    buf = malloc(buf_len);
823    if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) {
824	free(buf);
825	buf = NULL;
826	fprintf(stderr, "sysctl() failed: %s", strerror(errno));
827	goto failed;
828    }
829    *ret_len_p = buf_len;
830
831 failed:
832    return (buf);
833}
834
835PRIVATE_EXTERN void
836inet6_addrlist_copy(inet6_addrlist_t * addr_list_p, int if_index)
837{
838    int				addr_index = 0;
839    char *			buf = NULL;
840    char *			buf_end;
841    int				buf_len;
842    int				count;
843    int				error;
844    int				i;
845    char			ifname[IFNAMSIZ + 1];
846    inet6_addrinfo_t *		list = NULL;
847    char *			scan;
848    struct rt_msghdr *		rtm;
849    int				s = -1;
850
851    buf = get_if_info(if_index, AF_INET6, &buf_len);
852    if (buf == NULL) {
853	goto done;
854    }
855    buf_end = buf + buf_len;
856
857    addr_list_p->linklocal = NULL;
858
859    /* figure out how many IPv6 addresses there are */
860    count = 0;
861    ifname[0] = '\0';
862    for (scan = buf; scan < buf_end; scan += rtm->rtm_msglen) {
863	struct if_msghdr * 	ifm;
864
865	/* ALIGN: buf aligned (from calling get_if_info), scan aligned,
866	 * cast ok. */
867	rtm = (struct rt_msghdr *)(void *)scan;
868	if (rtm->rtm_version != RTM_VERSION) {
869	    continue;
870	}
871	switch (rtm->rtm_type) {
872	case RTM_IFINFO:
873	    ifm = (struct if_msghdr *)rtm;
874	    if (ifm->ifm_addrs & RTA_IFP) {
875		struct sockaddr_dl *	dl_p;
876
877		dl_p = (struct sockaddr_dl *)(ifm + 1);
878		if (dl_p->sdl_nlen == 0
879		    || dl_p->sdl_nlen >= sizeof(ifname)) {
880		    goto done;
881		}
882		bcopy(dl_p->sdl_data, ifname, dl_p->sdl_nlen);
883		ifname[dl_p->sdl_nlen] = '\0';
884	    }
885	    break;
886	case RTM_NEWADDR:
887	    count++;
888	    break;
889	default:
890	    break;
891	}
892    }
893    if (ifname[0] == '\0') {
894	goto done;
895    }
896    if (count == 0) {
897	goto done;
898    }
899    if (count > INET6_ADDRLIST_N_STATIC) {
900	list = (inet6_addrinfo_t *)malloc(sizeof(*list) * count);
901	if (list == NULL) {
902	    goto done;
903	}
904    }
905    else {
906	list = addr_list_p->list_static;
907    }
908    for (scan = buf; scan < buf_end; scan += rtm->rtm_msglen) {
909	boolean_t		got_address = FALSE;
910	struct ifa_msghdr *	ifam;
911	struct rt_addrinfo	info;
912
913	rtm = (struct rt_msghdr *)(void *)scan;
914	if (rtm->rtm_version != RTM_VERSION) {
915	    continue;
916	}
917	if (rtm->rtm_type == RTM_NEWADDR) {
918	    ifam = (struct ifa_msghdr *)rtm;
919	    info.rti_addrs = ifam->ifam_addrs;
920	    error = rt_xaddrs((char *)(ifam + 1),
921			      ((char *)ifam) + ifam->ifam_msglen,
922			      &info);
923	    if (error) {
924		fprintf(stderr, "couldn't extract rt_addrinfo %s (%d)\n",
925			strerror(error), error);
926		goto done;
927	    }
928	    for (i = 0; i < RTAX_MAX; i++) {
929		struct sockaddr_in6 *	sin6_p;
930
931		/* ALIGN: info.rti_info aligned (sockaddr), cast ok. */
932		sin6_p = (struct sockaddr_in6 *)(void *)info.rti_info[i];
933		if (sin6_p == NULL
934		    || sin6_p->sin6_len < sizeof(struct sockaddr_in6)) {
935		    continue;
936		}
937		switch (i) {
938		case RTAX_NETMASK:
939		    list[addr_index].prefix_length
940			= count_prefix_bits(&sin6_p->sin6_addr,
941					    sizeof(sin6_p->sin6_addr));
942		    break;
943		case RTAX_IFA:
944		    list[addr_index].addr = sin6_p->sin6_addr;
945		    got_address = TRUE;
946		    break;
947		default:
948		    break;
949		}
950	    }
951	    if (got_address) {
952		if (s < 0) {
953		    s = inet6_dgram_socket();
954		}
955		if (s >= 0) {
956		    siocgifaflag_in6(s, ifname,
957				     &list[addr_index].addr,
958				     &list[addr_index].addr_flags);
959		}
960		/* Mask the v6 LL scope id */
961		if (IN6_IS_ADDR_LINKLOCAL(&list[addr_index].addr)) {
962		    list[addr_index].addr.s6_addr16[1] = 0;
963		    if (addr_list_p->linklocal == NULL) {
964			addr_list_p->linklocal = &list[addr_index];
965		    }
966		}
967		addr_index++;
968	    }
969	}
970    }
971    if (addr_index == 0) {
972	if (list != addr_list_p->list_static) {
973	    free(list);
974	}
975	list = NULL;
976    }
977
978 done:
979    if (s >= 0) {
980	close(s);
981    }
982    if (buf != NULL) {
983	free(buf);
984    }
985    addr_list_p->list = list;
986    addr_list_p->count = addr_index;
987    return;
988}
989
990PRIVATE_EXTERN void
991inet6_addrlist_print(const inet6_addrlist_t * addr_list_p)
992{
993    int			i;
994    inet6_addrinfo_t *	scan;
995
996    for (i = 0, scan = addr_list_p->list; i < addr_list_p->count; i++, scan++) {
997	char 	ntopbuf[INET6_ADDRSTRLEN];
998
999	printf("%s/%d flags 0x%04x\n",
1000	       inet_ntop(AF_INET6, &scan->addr,
1001			 ntopbuf, sizeof(ntopbuf)),
1002	       scan->prefix_length,
1003	       scan->addr_flags);
1004    }
1005    return;
1006}
1007
1008PRIVATE_EXTERN void
1009inet6_addrlist_free(inet6_addrlist_t * addr_list_p)
1010{
1011    if (addr_list_p->list == NULL) {
1012	return;
1013    }
1014    if (addr_list_p->list != addr_list_p->list_static) {
1015	free(addr_list_p->list);
1016    }
1017    inet6_addrlist_init(addr_list_p);
1018    return;
1019}
1020
1021PRIVATE_EXTERN void
1022inet6_addrlist_init(inet6_addrlist_t * addr_list_p)
1023{
1024    addr_list_p->list = NULL;
1025    addr_list_p->count = 0;
1026    addr_list_p->linklocal = NULL;
1027    return;
1028}
1029
1030PRIVATE_EXTERN boolean_t
1031inet6_addrlist_contains_address(const inet6_addrlist_t * addr_list_p,
1032				const inet6_addrinfo_t * addr)
1033{
1034    int			i;
1035    inet6_addrinfo_t *	scan;
1036
1037    for (i = 0, scan = addr_list_p->list; i < addr_list_p->count; i++, scan++) {
1038	if (IN6_ARE_ADDR_EQUAL(&scan->addr, &addr->addr)
1039	    && (scan->prefix_length == addr->prefix_length)) {
1040	    return (TRUE);
1041	}
1042    }
1043    return (FALSE);
1044}
1045
1046PRIVATE_EXTERN inet6_addrinfo_t *
1047inet6_addrlist_get_linklocal(const inet6_addrlist_t * addr_list_p)
1048{
1049    if (addr_list_p != NULL) {
1050	return (addr_list_p->linklocal);
1051    }
1052    return (NULL);
1053}
1054
1055#if TEST_INET6_ADDRLIST || TEST_IPV6_LL
1056#include <stdio.h>
1057#include <stdlib.h>
1058#include "util.h"
1059
1060PRIVATE_EXTERN Boolean G_IPConfiguration_verbose = 1;
1061
1062STATIC bool S_cga_enabled;
1063
1064PRIVATE_EXTERN bool
1065CGAIsEnabled(void)
1066{
1067    return (S_cga_enabled);
1068}
1069
1070PRIVATE_EXTERN void
1071CGAPrepareSetForInterface(const char * name, struct in6_cga_prepare * cga_prep)
1072{
1073    if (S_cga_enabled == FALSE) {
1074	return;
1075    }
1076    cga_prep->cga_security_level = 0;
1077    fill_with_random(cga_prep->cga_modifier.octets,
1078		     sizeof(cga_prep->cga_modifier.octets));
1079    return;
1080}
1081
1082#endif /* TEST_INET6_ADDRLIST || TEST_IPV6_LL */
1083
1084#if TEST_INET6_ADDRLIST
1085int
1086main(int argc, char * argv[])
1087{
1088    inet6_addrlist_t 	addresses;
1089    int			if_index;
1090
1091    if (argc < 2) {
1092	fprintf(stderr, "you must specify the interface\n");
1093	exit(1);
1094    }
1095    if_index = if_nametoindex(argv[1]);
1096    if (if_index == 0) {
1097	fprintf(stderr, "No such interface '%s'\n", argv[1]);
1098	exit(2);
1099    }
1100    inet6_addrlist_copy(&addresses, if_index);
1101    inet6_addrlist_print(&addresses);
1102    inet6_addrlist_free(&addresses);
1103    exit(0);
1104    return(0);
1105}
1106#endif /* TEST_INET6_ADDRLIST */
1107
1108#if TEST_IPV6_LL
1109STATIC void
1110usage()
1111{
1112    fprintf(stderr, "usage: ipv6ll start | stop <ifname> [ <cga> ]\n");
1113    exit(1);
1114}
1115
1116int
1117main(int argc, char * argv[])
1118{
1119    int			is_start = 0;
1120
1121    if (argc < 3) {
1122	usage();
1123    }
1124    if (strcasecmp(argv[1], "start") == 0) {
1125	is_start = 1;
1126    }
1127    else if (strcasecmp(argv[1], "stop") == 0) {
1128    }
1129    else {
1130	usage();
1131    }
1132
1133    if (is_start) {
1134	S_cga_enabled = (argc > 3);
1135	if (inet6_linklocal_start(argv[2], TRUE) != 0) {
1136	    exit(1);
1137	}
1138	if (inet6_rtadv_enable(argv[2]) != 0) {
1139	    exit(1);
1140	}
1141    }
1142    else {
1143	inet6_rtadv_disable(argv[2]);
1144	if (inet6_linklocal_stop(argv[2]) != 0) {
1145	    exit(1);
1146	}
1147    }
1148    exit(0);
1149    return (0);
1150
1151}
1152
1153#endif /* TEST_IPV6_LL */
1154