1 /* $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $ */
4 * SPDX-License-Identifier: BSD-3-Clause
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the project nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
38 #include <sys/ioctl.h>
40 #include <net/if_dl.h>
41 #include <net/if_types.h>
42 #include <net/ethernet.h>
43 #include <net/route.h>
44 #include <netinet/in.h>
45 #include <netinet/in_var.h>
46 #include <netinet/ip6.h>
47 #include <netinet/icmp6.h>
48 #include <netinet6/nd6.h>
56 #include "pathnames.h"
60 #define ROUNDUP(a, size) \
61 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
64 (ap) = (struct sockaddr *)((caddr_t)(ap) + \
65 ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) : \
68 struct sockaddr_in6 sin6_linklocal_allnodes
= {
69 .sin6_len
= sizeof(sin6_linklocal_allnodes
),
70 .sin6_family
= AF_INET6
,
71 .sin6_addr
= IN6ADDR_LINKLOCAL_ALLNODES_INIT
,
74 struct sockaddr_in6 sin6_linklocal_allrouters
= {
75 .sin6_len
= sizeof(sin6_linklocal_allrouters
),
76 .sin6_family
= AF_INET6
,
77 .sin6_addr
= IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
,
80 struct sockaddr_in6 sin6_sitelocal_allrouters
= {
81 .sin6_len
= sizeof(sin6_sitelocal_allrouters
),
82 .sin6_family
= AF_INET6
,
83 .sin6_addr
= IN6ADDR_SITELOCAL_ALLROUTERS_INIT
,
86 struct sockinfo sock
= { .si_fd
= -1, .si_name
= NULL
};
87 struct sockinfo rtsock
= { .si_fd
= -1, .si_name
= NULL
};
88 struct sockinfo ctrlsock
= { .si_fd
= -1, .si_name
= _PATH_CTRL_SOCK
};
92 static void get_rtaddrs(int, struct sockaddr
*,
94 static struct if_msghdr
*get_next_msghdr(struct if_msghdr
*,
98 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
102 for (i
= 0; i
< RTAX_MAX
; i
++) {
103 if (addrs
& (1 << i
)) {
112 #define ROUNDUP8(a) (1 + (((a) - 1) | 7))
114 lladdropt_length(struct sockaddr_dl
*sdl
)
116 switch (sdl
->sdl_type
) {
120 return (ROUNDUP8(ETHER_ADDR_LEN
+ 2));
127 lladdropt_fill(struct sockaddr_dl
*sdl
, struct nd_opt_hdr
*ndopt
)
131 ndopt
->nd_opt_type
= ND_OPT_SOURCE_LINKADDR
; /* fixed */
133 switch (sdl
->sdl_type
) {
137 ndopt
->nd_opt_len
= (ROUNDUP8(ETHER_ADDR_LEN
+ 2)) >> 3;
138 addr
= (char *)(ndopt
+ 1);
139 memcpy(addr
, LLADDR(sdl
), ETHER_ADDR_LEN
);
142 syslog(LOG_ERR
, "<%s> unsupported link type(%d)",
143 __func__
, sdl
->sdl_type
);
152 int mib
[6] = {CTL_NET
, AF_ROUTE
, 0, AF_INET6
, NET_RT_DUMP
, 0};
154 if (sysctl(mib
, 6, NULL
, &len
, NULL
, 0) < 0)
160 #define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
161 #define SIN6(s) ((struct sockaddr_in6 *)(s))
162 #define SDL(s) ((struct sockaddr_dl *)(s))
164 get_next_msg(char *buf
, char *lim
, int ifindex
, size_t *lenp
, int filter
)
166 struct rt_msghdr
*rtm
;
167 struct ifa_msghdr
*ifam
;
168 struct sockaddr
*sa
, *dst
, *gw
, *ifa
, *rti_info
[RTAX_MAX
];
171 for (rtm
= (struct rt_msghdr
*)buf
;
172 rtm
< (struct rt_msghdr
*)lim
;
173 rtm
= (struct rt_msghdr
*)(((char *)rtm
) + rtm
->rtm_msglen
)) {
174 /* just for safety */
175 if (!rtm
->rtm_msglen
) {
176 syslog(LOG_WARNING
, "<%s> rtm_msglen is 0 "
177 "(buf=%p lim=%p rtm=%p)", __func__
,
181 if (((struct rt_msghdr
*)buf
)->rtm_version
!= RTM_VERSION
) {
183 "<%s> routing message version mismatch "
184 "(buf=%p lim=%p rtm=%p)", __func__
,
189 if (FILTER_MATCH(rtm
->rtm_type
, filter
) == 0)
192 switch (rtm
->rtm_type
) {
196 /* address related checks */
197 sa
= (struct sockaddr
*)(rtm
+ 1);
198 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
199 if ((dst
= rti_info
[RTAX_DST
]) == NULL
||
200 dst
->sa_family
!= AF_INET6
)
203 if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst
)->sin6_addr
) ||
204 IN6_IS_ADDR_MULTICAST(&SIN6(dst
)->sin6_addr
))
207 if ((gw
= rti_info
[RTAX_GATEWAY
]) == NULL
||
208 gw
->sa_family
!= AF_LINK
)
210 if (ifindex
&& SDL(gw
)->sdl_index
!= ifindex
)
213 if (rti_info
[RTAX_NETMASK
] == NULL
)
217 *lenp
= rtm
->rtm_msglen
;
222 ifam
= (struct ifa_msghdr
*)rtm
;
224 /* address related checks */
225 sa
= (struct sockaddr
*)(ifam
+ 1);
226 get_rtaddrs(ifam
->ifam_addrs
, sa
, rti_info
);
227 if ((ifa
= rti_info
[RTAX_IFA
]) == NULL
||
228 (ifa
->sa_family
!= AF_INET
&&
229 ifa
->sa_family
!= AF_INET6
))
232 if (ifa
->sa_family
== AF_INET6
&&
233 (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa
)->sin6_addr
) ||
234 IN6_IS_ADDR_MULTICAST(&SIN6(ifa
)->sin6_addr
)))
237 if (ifindex
&& ifam
->ifam_index
!= ifindex
)
241 *lenp
= ifam
->ifam_msglen
;
247 *lenp
= rtm
->rtm_msglen
;
253 return ((char *)rtm
);
260 struct rt_msghdr
*rtm
= (struct rt_msghdr
*)buf
;
261 struct sockaddr
*sa
, *rti_info
[RTAX_MAX
];
263 sa
= (struct sockaddr
*)(rtm
+ 1);
264 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
266 return (&SIN6(rti_info
[RTAX_DST
])->sin6_addr
);
270 get_rtm_ifindex(char *buf
)
272 struct rt_msghdr
*rtm
= (struct rt_msghdr
*)buf
;
273 struct sockaddr
*sa
, *rti_info
[RTAX_MAX
];
275 sa
= (struct sockaddr
*)(rtm
+ 1);
276 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
278 return (((struct sockaddr_dl
*)rti_info
[RTAX_GATEWAY
])->sdl_index
);
282 get_prefixlen(char *buf
)
284 struct rt_msghdr
*rtm
= (struct rt_msghdr
*)buf
;
285 struct sockaddr
*sa
, *rti_info
[RTAX_MAX
];
288 sa
= (struct sockaddr
*)(rtm
+ 1);
289 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
290 sa
= rti_info
[RTAX_NETMASK
];
292 p
= (char *)(&SIN6(sa
)->sin6_addr
);
293 lim
= (char *)sa
+ sa
->sa_len
;
294 return prefixlen(p
, lim
);
298 prefixlen(unsigned char *p
, unsigned char *lim
)
302 for (masklen
= 0; p
< lim
; p
++) {
339 update_persist_ifinfo(struct ifilist_head_t
*ifi_head
, const char *ifname
)
345 ifindex
= if_nametoindex(ifname
);
346 TAILQ_FOREACH(ifi
, ifi_head
, ifi_next
) {
348 if (ifindex
== ifi
->ifi_ifindex
)
351 if (strncmp(ifname
, ifi
->ifi_ifname
,
352 sizeof(ifi
->ifi_ifname
)) == 0)
358 /* A new ifinfo element is needed. */
359 syslog(LOG_DEBUG
, "<%s> new entry: %s", __func__
,
362 ELM_MALLOC(ifi
, exit(1));
363 ifi
->ifi_ifindex
= 0;
364 strlcpy(ifi
->ifi_ifname
, ifname
, sizeof(ifi
->ifi_ifname
));
365 ifi
->ifi_rainfo
= NULL
;
366 ifi
->ifi_state
= IFI_STATE_UNCONFIGURED
;
367 TAILQ_INSERT_TAIL(ifi_head
, ifi
, ifi_next
);
370 ifi
->ifi_persist
= 1;
372 syslog(LOG_DEBUG
, "<%s> %s is marked PERSIST", __func__
,
374 syslog(LOG_DEBUG
, "<%s> %s is state = %d", __func__
,
375 ifi
->ifi_ifname
, ifi
->ifi_state
);
380 update_ifinfo_nd_flags(struct ifinfo
*ifi
)
382 struct in6_ndireq nd
;
386 if ((s
= socket(AF_INET6
, SOCK_DGRAM
, 0)) < 0) {
388 "<%s> socket() failed.", __func__
);
392 memset(&nd
, 0, sizeof(nd
));
393 strlcpy(nd
.ifname
, ifi
->ifi_ifname
,
395 error
= ioctl(s
, SIOCGIFINFO_IN6
, (caddr_t
)&nd
);
398 if (errno
!= EPFNOSUPPORT
)
399 syslog(LOG_ERR
, "<%s> ioctl() failed.", __func__
);
402 ifi
->ifi_nd_flags
= nd
.ndi
.flags
;
408 #define MAX_SYSCTL_TRY 5
411 update_ifinfo(struct ifilist_head_t
*ifi_head
, int ifindex
)
413 struct if_msghdr
*ifm
;
414 struct ifinfo
*ifi
= NULL
;
416 struct sockaddr
*rti_info
[RTAX_MAX
];
420 int mib
[] = { CTL_NET
, PF_ROUTE
, 0, AF_INET6
, NET_RT_IFLIST
, 0 };
423 syslog(LOG_DEBUG
, "<%s> enter", __func__
);
428 * We'll try to get addresses several times in case that
429 * the number of addresses is unexpectedly increased during
430 * the two sysctl calls. This should rarely happen.
431 * Portability note: since FreeBSD does not add margin of
432 * memory at the first sysctl, the possibility of failure on
433 * the second sysctl call is a bit higher.
436 if (sysctl(mib
, nitems(mib
), NULL
, &len
, NULL
, 0) < 0) {
438 "<%s> sysctl: NET_RT_IFLIST size get failed",
442 if ((msg
= malloc(len
)) == NULL
) {
443 syslog(LOG_ERR
, "<%s> malloc failed", __func__
);
446 if (sysctl(mib
, nitems(mib
), msg
, &len
, NULL
, 0) < 0) {
447 if (errno
!= ENOMEM
|| ++ntry
>= MAX_SYSCTL_TRY
) {
450 "<%s> sysctl: NET_RT_IFLIST get failed",
457 } while (msg
== NULL
);
460 for (ifm
= (struct if_msghdr
*)msg
;
461 ifm
!= NULL
&& ifm
< (struct if_msghdr
*)lim
;
462 ifm
= get_next_msghdr(ifm
,(struct if_msghdr
*)lim
)) {
465 syslog(LOG_DEBUG
, "<%s> ifm = %p, lim = %p, diff = %zu",
466 __func__
, ifm
, lim
, (char *)lim
- (char *)ifm
);
468 if (ifm
->ifm_version
!= RTM_VERSION
) {
470 "<%s> ifm_vesrion mismatch", __func__
);
473 if (ifm
->ifm_msglen
== 0) {
475 "<%s> ifm_msglen is 0", __func__
);
481 if (ifm
->ifm_type
== RTM_IFINFO
) {
484 char ifname
[IFNAMSIZ
];
486 syslog(LOG_DEBUG
, "<%s> RTM_IFINFO found. "
487 "ifm_index = %d, ifindex = %d",
488 __func__
, ifm
->ifm_index
, ifindex
);
490 /* when ifindex is specified */
491 if (ifindex
!= UPDATE_IFINFO_ALL
&&
492 ifindex
!= ifm
->ifm_index
)
496 if (if_indextoname(ifm
->ifm_index
, ifname
) == NULL
) {
498 "<%s> ifname not found (idx=%d)",
499 __func__
, ifm
->ifm_index
);
503 /* lookup an entry with the same ifindex */
504 TAILQ_FOREACH(ifi
, ifi_head
, ifi_next
) {
505 if (ifm
->ifm_index
== ifi
->ifi_ifindex
)
507 if (strncmp(ifname
, ifi
->ifi_ifname
,
508 sizeof(ifname
)) == 0)
513 "<%s> new entry for idx=%d",
514 __func__
, ifm
->ifm_index
);
515 ELM_MALLOC(ifi
, exit(1));
516 ifi
->ifi_rainfo
= NULL
;
517 ifi
->ifi_state
= IFI_STATE_UNCONFIGURED
;
518 ifi
->ifi_persist
= 0;
522 ifi
->ifi_ifindex
= ifm
->ifm_index
;
525 strlcpy(ifi
->ifi_ifname
, ifname
, IFNAMSIZ
);
527 if ((s
= socket(AF_INET6
, SOCK_DGRAM
, 0)) < 0) {
529 "<%s> socket() failed.", __func__
);
536 ifi
->ifi_phymtu
= ifm
->ifm_data
.ifi_mtu
;
537 if (ifi
->ifi_phymtu
== 0) {
538 memset(&ifr
, 0, sizeof(ifr
));
539 ifr
.ifr_addr
.sa_family
= AF_INET6
;
540 strlcpy(ifr
.ifr_name
, ifi
->ifi_ifname
,
541 sizeof(ifr
.ifr_name
));
542 error
= ioctl(s
, SIOCGIFMTU
, (caddr_t
)&ifr
);
546 "<%s> ioctl() failed.",
552 ifi
->ifi_phymtu
= ifr
.ifr_mtu
;
553 if (ifi
->ifi_phymtu
== 0) {
555 "<%s> no interface mtu info"
556 " on %s. %d will be used.",
557 __func__
, ifi
->ifi_ifname
,
559 ifi
->ifi_phymtu
= IPV6_MMTU
;
565 error
= update_ifinfo_nd_flags(ifi
);
573 sa
= (struct sockaddr
*)(ifm
+ 1);
574 get_rtaddrs(ifm
->ifm_addrs
, sa
, rti_info
);
575 if ((sa
= rti_info
[RTAX_IFP
]) != NULL
) {
576 if (sa
->sa_family
== AF_LINK
) {
577 memcpy(&ifi
->ifi_sdl
,
578 (struct sockaddr_dl
*)sa
,
579 sizeof(ifi
->ifi_sdl
));
582 memset(&ifi
->ifi_sdl
, 0,
583 sizeof(ifi
->ifi_sdl
));
586 ifi
->ifi_flags
= ifm
->ifm_flags
;
589 ifi
->ifi_type
= ifm
->ifm_type
;
592 "out of sync parsing NET_RT_IFLIST\n"
593 "expected %d, got %d\n msglen = %d\n",
594 RTM_IFINFO
, ifm
->ifm_type
, ifm
->ifm_msglen
);
600 "<%s> adding %s(idx=%d) to ifilist",
601 __func__
, ifi
->ifi_ifname
, ifi
->ifi_ifindex
);
602 TAILQ_INSERT_TAIL(ifi_head
, ifi
, ifi_next
);
607 if (mcastif
!= NULL
) {
608 error
= sock_mc_rr_update(&sock
, mcastif
);
616 static struct if_msghdr
*
617 get_next_msghdr(struct if_msghdr
*ifm
, struct if_msghdr
*lim
)
619 struct ifa_msghdr
*ifam
;
621 for (ifam
= (struct ifa_msghdr
*)((char *)ifm
+ ifm
->ifm_msglen
);
622 ifam
< (struct ifa_msghdr
*)lim
;
623 ifam
= (struct ifa_msghdr
*)((char *)ifam
+ ifam
->ifam_msglen
)) {
624 if (!ifam
->ifam_msglen
) {
626 "<%s> ifa_msglen is 0", __func__
);
629 if (ifam
->ifam_type
!= RTM_NEWADDR
)
633 return ((struct if_msghdr
*)ifam
);
637 getinet6sysctl(int code
)
639 int mib
[] = { CTL_NET
, PF_INET6
, IPPROTO_IPV6
, 0 };
644 size
= sizeof(value
);
645 if (sysctl(mib
, nitems(mib
), &value
, &size
, NULL
, 0)
647 syslog(LOG_ERR
, "<%s>: failed to get ip6 sysctl(%d): %s",
658 sock_mc_join(struct sockinfo
*s
, int ifindex
)
660 struct ipv6_mreq mreq
;
661 char ifname
[IFNAMSIZ
];
663 syslog(LOG_DEBUG
, "<%s> enter", __func__
);
669 * join all routers multicast address on each advertising
672 memset(&mreq
, 0, sizeof(mreq
));
674 memcpy(&mreq
.ipv6mr_multiaddr
.s6_addr
,
675 &sin6_linklocal_allrouters
.sin6_addr
,
676 sizeof(mreq
.ipv6mr_multiaddr
.s6_addr
));
678 mreq
.ipv6mr_interface
= ifindex
;
679 if (setsockopt(s
->si_fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &mreq
,
682 "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
683 __func__
, if_indextoname(ifindex
, ifname
),
688 "<%s> %s: join link-local all-routers MC group",
689 __func__
, if_indextoname(ifindex
, ifname
));
695 sock_mc_leave(struct sockinfo
*s
, int ifindex
)
697 struct ipv6_mreq mreq
;
698 char ifname
[IFNAMSIZ
];
700 syslog(LOG_DEBUG
, "<%s> enter", __func__
);
706 * join all routers multicast address on each advertising
710 memset(&mreq
, 0, sizeof(mreq
));
712 memcpy(&mreq
.ipv6mr_multiaddr
.s6_addr
,
713 &sin6_linklocal_allrouters
.sin6_addr
,
714 sizeof(mreq
.ipv6mr_multiaddr
.s6_addr
));
716 mreq
.ipv6mr_interface
= ifindex
;
717 if (setsockopt(s
->si_fd
, IPPROTO_IPV6
, IPV6_LEAVE_GROUP
, &mreq
,
720 "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
721 __func__
, if_indextoname(ifindex
, ifname
),
726 "<%s> %s: leave link-local all-routers MC group",
727 __func__
, if_indextoname(ifindex
, ifname
));
733 sock_mc_rr_update(struct sockinfo
*s
, char *mif
)
735 struct ipv6_mreq mreq
;
737 syslog(LOG_DEBUG
, "<%s> enter", __func__
);
742 * When attending router renumbering, join all-routers site-local
746 memcpy(&mreq
.ipv6mr_multiaddr
.s6_addr
,
747 &sin6_sitelocal_allrouters
.sin6_addr
,
748 sizeof(mreq
.ipv6mr_multiaddr
.s6_addr
));
749 if ((mreq
.ipv6mr_interface
= if_nametoindex(mif
)) == 0) {
751 "<%s> invalid interface: %s",
756 if (setsockopt(s
->si_fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
757 &mreq
, sizeof(mreq
)) < 0) {
759 "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
760 __func__
, mif
, strerror(errno
));
765 "<%s> %s: join site-local all-routers MC group",