diff --git a/external/bsd/dhcpcd/dist/dhcp.c b/external/bsd/dhcpcd/dist/dhcp.c index ce17887650cc..5fe977e44647 100644 --- a/external/bsd/dhcpcd/dist/dhcp.c +++ b/external/bsd/dhcpcd/dist/dhcp.c @@ -1,5 +1,5 @@ #include - __RCSID("$NetBSD: dhcp.c,v 1.6 2013/07/29 20:39:28 roy Exp $"); + __RCSID("$NetBSD: dhcp.c,v 1.7 2013/09/20 10:56:32 roy Exp $"); /* * dhcpcd - DHCP client daemon @@ -1702,7 +1702,6 @@ dhcp_rebind(void *arg) send_rebind(ifp); } - void dhcp_bind(void *arg) { @@ -1724,10 +1723,10 @@ dhcp_bind(void *arg) state->offer = NULL; get_lease(lease, state->new); if (ifo->options & DHCPCD_STATIC) { - syslog(LOG_INFO, "%s: using static address %s", - iface->name, inet_ntoa(lease->addr)); + syslog(LOG_INFO, "%s: using static address %s/%d", + iface->name, inet_ntoa(lease->addr), + inet_ntocidr(lease->net)); lease->leasetime = ~0U; - lease->net.s_addr = ifo->req_mask.s_addr; state->reason = "STATIC"; } else if (state->new->cookie != htonl(MAGIC_COOKIE)) { syslog(LOG_INFO, "%s: using IPv4LL address %s", @@ -1834,7 +1833,7 @@ dhcp_timeout(void *arg) } struct dhcp_message * -dhcp_message_new(struct in_addr *addr, struct in_addr *mask) +dhcp_message_new(const struct in_addr *addr, const struct in_addr *mask) { struct dhcp_message *dhcp; uint8_t *p; @@ -1854,60 +1853,65 @@ dhcp_message_new(struct in_addr *addr, struct in_addr *mask) return dhcp; } -static int -handle_3rdparty(struct interface *ifp) -{ - struct if_options *ifo; - struct dhcp_state *state; - struct in_addr addr, net, dst; - - ifo = ifp->options; - if (ifo->req_addr.s_addr != INADDR_ANY) - return 0; - - if (ipv4_getaddress(ifp->name, &addr, &net, &dst) == 1) - ipv4_handleifa(RTM_NEWADDR, ifp->name, &addr, &net, &dst); - else { - syslog(LOG_INFO, - "%s: waiting for 3rd party to configure IP address", - ifp->name); - state = D_STATE(ifp); - state->reason = "3RDPARTY"; - script_runreason(ifp, state->reason); - } - return 1; -} - static void dhcp_static(struct interface *ifp) { struct if_options *ifo; struct dhcp_state *state; - if (handle_3rdparty(ifp)) - return; - ifo = ifp->options; state = D_STATE(ifp); + ifo = ifp->options; + if (ifo->req_addr.s_addr == INADDR_ANY) { + syslog(LOG_INFO, + "%s: waiting for 3rd party to " + "configure IP address", + ifp->name); + state->reason = "3RDPARTY"; + script_runreason(ifp, state->reason); + return; + } state->offer = dhcp_message_new(&ifo->req_addr, &ifo->req_mask); - eloop_timeout_delete(NULL, ifp); - dhcp_bind(ifp); + if (state->offer) { + eloop_timeout_delete(NULL, ifp); + dhcp_bind(ifp); + } } void dhcp_inform(struct interface *ifp) { struct dhcp_state *state; - - if (handle_3rdparty(ifp)) - return; + struct if_options *ifo; + struct ipv4_addr *ap; state = D_STATE(ifp); + ifo = ifp->options; if (options & DHCPCD_TEST) { - state->addr.s_addr = ifp->options->req_addr.s_addr; - state->net.s_addr = ifp->options->req_mask.s_addr; + state->addr.s_addr = ifo->req_addr.s_addr; + state->net.s_addr = ifo->req_mask.s_addr; } else { - ifp->options->options |= DHCPCD_STATIC; - dhcp_static(ifp); + if (ifo->req_addr.s_addr == INADDR_ANY) { + state = D_STATE(ifp); + ap = ipv4_findaddr(ifp, NULL, NULL); + if (ap == NULL) { + syslog(LOG_INFO, + "%s: waiting for 3rd party to " + "configure IP address", + ifp->name); + state->reason = "3RDPARTY"; + script_runreason(ifp, state->reason); + return; + } + state->offer = + dhcp_message_new(&ap->addr, &ap->net); + } else + state->offer = + dhcp_message_new(&ifo->req_addr, &ifo->req_mask); + if (state->offer) { + ifo->options |= DHCPCD_STATIC; + dhcp_bind(ifp); + ifo->options &= ~DHCPCD_STATIC; + } } state->state = DHS_INFORM; @@ -2220,6 +2224,8 @@ dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp, if (!(ifo->options & DHCPCD_INFORM)) log_dhcp(LOG_DEBUG, "acknowledged", iface, dhcp, from); + else + ifo->options &= ~DHCPCD_STATIC; } /* BOOTP could have already assigned this above, so check we still @@ -2244,7 +2250,7 @@ dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp, /* If the interface already has the address configured * then we can't ARP for duplicate detection. */ addr.s_addr = state->offer->yiaddr; - if (ipv4_hasaddress(iface->name, &addr, NULL) != 1) { + if (!ipv4_findaddr(iface, &addr, NULL)) { state->claims = 0; state->probes = 0; state->conflicts = 0; @@ -2687,3 +2693,70 @@ dhcp_start(struct interface *ifp) else dhcp_reboot(ifp); } + +void +dhcp_handleifa(int type, struct interface *ifp, + const struct in_addr *addr, + const struct in_addr *net, + const struct in_addr *dst) +{ + struct dhcp_state *state; + struct if_options *ifo; + int i; + + state = D_STATE(ifp); + if (state == NULL) + return; + + if (type == RTM_DELADDR) { + if (state->new && + (state->new->yiaddr == addr->s_addr || + (state->new->yiaddr == INADDR_ANY && + state->new->ciaddr == addr->s_addr))) + { + syslog(LOG_INFO, "%s: removing IP address %s/%d", + ifp->name, inet_ntoa(state->lease.addr), + inet_ntocidr(state->lease.net)); + dhcp_drop(ifp, "EXPIRE"); + } + return; + } + + if (type != RTM_NEWADDR) + return; + + ifo = ifp->options; + if (ifo->options & DHCPCD_INFORM) { + if (state->state != DHS_INFORM) + dhcp_inform(ifp); + return; + } + + if (!(ifo->options & DHCPCD_STATIC)) + return; + if (ifo->req_addr.s_addr != INADDR_ANY) + return; + + free(state->old); + state->old = state->new; + state->new = dhcp_message_new(addr, net); + if (state->new == NULL) + return; + state->dst.s_addr = dst ? dst->s_addr : INADDR_ANY; + if (dst) { + for (i = 1; i < 255; i++) + if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i)) + dhcp_message_add_addr(state->new, i, *dst); + } + state->reason = "STATIC"; + ipv4_buildroutes(); + script_runreason(ifp, state->reason); + if (ifo->options & DHCPCD_INFORM) { + state->state = DHS_INFORM; + state->xid = dhcp_xid(ifp); + state->lease.server.s_addr = dst ? dst->s_addr : INADDR_ANY; + state->addr = *addr; + state->net = *net; + dhcp_inform(ifp); + } +} diff --git a/external/bsd/dhcpcd/dist/dhcpcd.8.in b/external/bsd/dhcpcd/dist/dhcpcd.8.in index 6a78f6c11930..29094f69b2ae 100644 --- a/external/bsd/dhcpcd/dist/dhcpcd.8.in +++ b/external/bsd/dhcpcd/dist/dhcpcd.8.in @@ -1,4 +1,4 @@ -.\" $NetBSD: dhcpcd.8.in,v 1.21 2013/07/29 20:39:28 roy Exp $ +.\" $NetBSD: dhcpcd.8.in,v 1.22 2013/09/20 10:56:32 roy Exp $ .\" Copyright (c) 2006-2013 Roy Marples .\" All rights reserved .\" @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 6, 2013 +.Dd September 12, 2013 .Dt DHCPCD 8 .Os .Sh NAME @@ -31,7 +31,7 @@ .Nd a DHCP client .Sh SYNOPSIS .Nm -.Op Fl 46ABbDdEGgHJKkLnpqTVw +.Op Fl 46ABbDdEGgHJKLpqTV .Op Fl C , Fl Fl nohook Ar hook .Op Fl c , Fl Fl script Ar script .Op Fl e , Fl Fl env Ar value @@ -52,6 +52,7 @@ .Op Fl u , Fl Fl userclass Ar class .Op Fl v , Fl Fl vendor Ar code , Ar value .Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr +.Op Fl w , Fl Fl waitip Op 4 | 6 .Op Fl y , Fl Fl reboot Ar seconds .Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr .Op Fl Z , Fl Fl denyinterfaces Ar pattern @@ -59,6 +60,9 @@ .Op interface .Op ... .Nm +.Fl n , Fl Fl rebind +.Op interface +.Nm .Fl k , Fl Fl release .Op interface .Nm @@ -300,16 +304,19 @@ For example If not set then none is sent. Some badly configured DHCP servers reject unknown vendorclassids. To work around it, try and impersonate Windows by using the MSFT vendorclassid. -.It Fl k , Fl Fl release +.It Fl k , Fl Fl release Op Ar interface This causes an existing .Nm process running on the .Ar interface -to release its lease, de-configure the +to release its lease and de-configure the +.Ar interface . +If no .Ar interface -and then exit. +is specified then this applies to all interfaces. +If no interfaces are left running, .Nm -then waits until this process has exited. +will exit. .It Fl l , Fl Fl leasetime Ar seconds Request a specific lease time in .Ar seconds . @@ -323,10 +330,12 @@ Metrics are used to prefer an interface over another one, lowest wins. will supply a default metic of 200 + .Xr if_nametoindex 3 . An extra 100 will be added for wireless interfaces. -.It Fl n , Fl Fl rebind +.It Fl n , Fl Fl rebind Op Ar interface Notifies .Nm -to reload its configuration and rebind its interfaces. +to reload its configuration and rebind the specified +.Ar interface . +If no interface is specified then this applies to all interfaces. If .Nm is not running, then it starts up as normal. @@ -344,7 +353,8 @@ normally de-configures the .Ar interface and configuration when it exits. Sometimes, this isn't desirable if, for example, you have root mounted over -NFS. +NFS or SSH clients connect to this host and they need to be notified of +the host shutting down. You can use this option to stop this from happening. .It Fl r , Fl Fl request Op Ar address Request the @@ -374,6 +384,21 @@ If .Nm fails to contact a DHCP server then it returns a failure instead of falling back on IPv4LL. +.It Fl S, Fl Fl static Ar value +Configures a static +.Ar value . +If you set +.Ic ip_address +then +.Nm +will not attempt to obtain a lease and just use the value for the address with +an infinite lease time. +.Pp +Here is an example which configures a static address, routes and dns. +.D1 dhcpcd -S ip_address=192.168.0.10/24 \e +.D1 -S routers=192.168.0.1 \e +.D1 -S domain_name_servers=192.168.0.1 \e +.D1 eth0 .It Fl t , Fl Fl timeout Ar seconds Timeout after .Ar seconds , @@ -409,9 +434,17 @@ Set un-encapsulated vendor option to hello world. Display both program version and copyright information. .Nm then exits before doing any configuration. -.It Fl w , Fl Fl waitip +.It Fl w , Fl Fl waitip Op 4 | 6 Wait for an address to be assigned before forking to the background. -.It Fl x , Fl Fl exit +4 means wait for an IPv4 address to be assigned. +6 means wait for an IPv6 address to be assigned. +If no argument is given, +.Nm +will wait for any address protocol to be assigned. +It is possible to wait for more than one address protocol and +.Nm +will only fork to the background when all waiting conditions are satisfied. +.It Fl x , Fl Fl exit Op Ar interface This will signal an existing .Nm process running on the @@ -419,6 +452,7 @@ process running on the to de-configure the .Ar interface and exit. +If no interface is specified, then the above is applied to all interfaces. .Nm then waits until this process has exited. .It Fl y , Fl Fl reboot Ar seconds @@ -494,21 +528,6 @@ Quiet .Nm on the command line, only warnings and errors will be displayed. The messages are still logged though. -.It Fl S, Fl Fl static Ar value -Configures a static -.Ar value . -If you set -.Ic ip_address -then -.Nm -will not attempt to obtain a lease and just use the value for the address with -an infinite lease time. -.Pp -Here is an example which configures a static address, routes and dns. -.D1 dhcpcd -S ip_address=192.168.0.10/24 \e -.D1 -S routers=192.168.0.1 \e -.D1 -S domain_name_servers=192.168.0.1 \e -.D1 eth0 .It Fl T, Fl Fl test On receipt of DHCP messages just call .Pa @SCRIPT@ @@ -554,6 +573,10 @@ which is a space or comma separated list of patterns passed to If the same interface is matched in .Fl Z , Fl Fl denyinterfaces then it is still denied. +.It Fl Fl nodev +Don't load any +.Pa /dev +management modules. .El .Sh 3RDPARTY LINK MANAGEMENT Some interfaces require configuration by 3rd parties, such as PPP or VPN. @@ -582,6 +605,9 @@ If you always use the same options, put them here. Text file that holds the DUID used to identify the host. .It Pa @SCRIPT@ Bourne shell script that is run to configure or de-configure an interface. +.It Pa @LIBDIR@/dhcpcd/dev +.Pa /dev +management modules. .It Pa @HOOKDIR@ A directory containing bourne shell scripts that are run by the above script. Each script can be disabled by using the @@ -612,7 +638,7 @@ running on the RFC\ 951 RFC\ 1534 RFC\ 2131, RFC\ 2132, RFC\ 2855, RFC\ 3004, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3927, RFC\ 4039 RFC\ 4075, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4704, RFC\ 4861, RFC\ 4833, -RFC\ 5227, RFC\ 5969, RFC\ 6106. +RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106. .Sh AUTHORS .An Roy Marples Aq Mt roy@marples.name .Sh BUGS diff --git a/external/bsd/dhcpcd/dist/dhcpcd.conf b/external/bsd/dhcpcd/dist/dhcpcd.conf index 9fb5ac1b9bba..d93b5d9c6e97 100644 --- a/external/bsd/dhcpcd/dist/dhcpcd.conf +++ b/external/bsd/dhcpcd/dist/dhcpcd.conf @@ -1,4 +1,4 @@ -# $NetBSD: dhcpcd.conf,v 1.7 2013/07/29 20:39:28 roy Exp $ +# $NetBSD: dhcpcd.conf,v 1.8 2013/09/20 10:56:32 roy Exp $ # A sample configuration for dhcpcd. # See dhcpcd.conf(5) for details. @@ -12,6 +12,9 @@ hostname # Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361. duid +# Persist interface configuration when dhcpcd exits. +persistent + # Rapid commit support. # Safe to enable by default because it requires the equivalent option set # on the server to actually work. diff --git a/external/bsd/dhcpcd/dist/dhcpcd.conf.5.in b/external/bsd/dhcpcd/dist/dhcpcd.conf.5.in index 20f261a8a4a2..d1ea75804ad7 100644 --- a/external/bsd/dhcpcd/dist/dhcpcd.conf.5.in +++ b/external/bsd/dhcpcd/dist/dhcpcd.conf.5.in @@ -1,4 +1,4 @@ -.\" $NetBSD: dhcpcd.conf.5.in,v 1.3 2013/07/29 20:39:28 roy Exp $ +.\" $NetBSD: dhcpcd.conf.5.in,v 1.4 2013/09/20 10:56:32 roy Exp $ .\" Copyright (c) 2006-2013 Roy Marples .\" All rights reserved .\" @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 25, 2013 +.Dd September 12, 2013 .Dt DHCPCD.CONF 5 SMM .Os .Sh NAME @@ -91,6 +91,13 @@ such as FireWire and InfiniBand. In most cases, .Nm dhcpcd will set this automatically. +.It Ic dev Ar value +Load the +.Ar value +.Pa /dev +management module. +.Nm dhcpcd +will load the first one found to work, if any. .It Ic env Ar value Push .Ar value @@ -135,6 +142,13 @@ is generated, otherwise DUID-LL is generated (link local address). The DUID-LLT generated will be held in .Pa @SYSCONFDIR@/dhcpcd.duid and should not be copied to other hosts. +.It Ic persistent +.Nm dhcpcd +normally de-configures the interface and configuration when it exits. +Sometimes, this isn't desirable if, for example, you have root mounted over +NFS or SSH clients connect to this host and they need to be notified of +the host shutting down. +You can use this option to stop this from happening. .It Ic fallback Ar profile Fallback to using this profile if DHCP fails. This allows you to configure a static profile instead of using ZeroConf. @@ -164,9 +178,11 @@ If none is specified, a default is used. If the interface name is 4 characters or less then that is used, otherwise the interface index is used. +You can request more than one ia_na by specifying a unique iaid for each one. .It Ic ia_ta Op Ar iaid Request a DHCPv6 Temporary Address for .Ar iaid . +You can request more than one ia_ta by specifying a unique iaid for each one. .It Ic ia_pd Op Ar iaid Op Ar interface Op / Ar sla_id Op / Ar prefix_len Request a DHCPv6 Delegated Prefix for .Ar iaid . @@ -255,6 +271,10 @@ alongside. .It Ic noarp Don't send any ARP requests. This also disables IPv4LL. +.It Ic nodev +Don't load +.Pa /dev +management modules. .It Ic nogateway Don't install any default routes. .It Ic nohook Ar script @@ -404,8 +424,16 @@ For example If not set then none is sent. Some badly configured DHCP servers reject unknown vendorclassids. To work around it, try and impersonate Windows by using the MSFT vendorclassid. -.It Ic waitip +.It Ic waitip Op 4 | 6 Wait for an address to be assigned before forking to the background. +4 means wait for an IPv4 address to be assigned. +6 means wait for an IPv6 address to be assigned. +If no argument is given, +.Nm +will wait for any address protocol to be assigned. +It is possible to wait for more than one address protocol and +.Nm +will only fork to the background when all waiting conditions are satisfied. .It Ic xidhwaddr Use the last four bytes of the hardware address as the DHCP xid instead of a randomly generated number. diff --git a/external/bsd/dhcpcd/dist/ipv6ns.c b/external/bsd/dhcpcd/dist/ipv6ns.c deleted file mode 100644 index 068bff1c416f..000000000000 --- a/external/bsd/dhcpcd/dist/ipv6ns.c +++ /dev/null @@ -1,690 +0,0 @@ -#include - __RCSID("$NetBSD: ipv6ns.c,v 1.1.1.3 2013/06/21 19:33:08 roy Exp $"); - -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2013 Roy Marples - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -# define _LINUX_IN6_H -# include -#endif - -#define ELOOP_QUEUE 2 -#include "common.h" -#include "dhcpcd.h" -#include "dhcp6.h" -#include "eloop.h" -#include "ipv6.h" -#include "ipv6ns.h" -#include "script.h" - -#define MIN_RANDOM_FACTOR 500 /* millisecs */ -#define MAX_RANDOM_FACTOR 1500 /* millisecs */ -#define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */ -#define MAX_RANDOM_FACTOR_U MAX_RANDOM_FACTOR * 1000 /* usecs */ - -#if BYTE_ORDER == BIG_ENDIAN -#define IPV6_ADDR_INT32_ONE 1 -#define IPV6_ADDR_INT16_MLL 0xff02 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define IPV6_ADDR_INT32_ONE 0x01000000 -#define IPV6_ADDR_INT16_MLL 0x02ff -#endif - -/* Debugging Neighbor Solicitations is a lot of spam, so disable it */ -//#define DEBUG_NS -// - -/* Currently, no known kernel allows us to send from the unspecified address - * which is required for DAD to work. This isn't that much of a problem as - * the kernel will do DAD for us correctly, however we don't know the exact - * randomness the kernel applies to the timeouts. So we just follow the same - * logic and have a little faith. - * This define is purely for completeness */ -// #define IPV6_SEND_DAD - -static int sock = -1; -#ifdef IPV6_SEND_DAD -static int unspec_sock = -1; -#endif -static struct sockaddr_in6 from; -static struct msghdr sndhdr; -static struct iovec sndiov[2]; -static unsigned char *sndbuf; -static struct msghdr rcvhdr; -static struct iovec rcviov[2]; -static unsigned char *rcvbuf; -static unsigned char ansbuf[1500]; -static char ntopbuf[INET6_ADDRSTRLEN]; - -static void ipv6ns_handledata(__unused void *arg); - -#if DEBUG_MEMORY -static void -ipv6ns_cleanup(void) -{ - - free(sndbuf); - free(rcvbuf); -} -#endif - -static int -ipv6ns_open(void) -{ - int on; - int len; - struct icmp6_filter filt; -#ifdef IPV6_SEND_DAD - union { - struct sockaddr sa; - struct sockaddr_in6 sin; - } su; -#endif - - if (sock != -1) - return sock; - - sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - if (sock == -1) - return -1; - on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, - &on, sizeof(on)) == -1) - goto eexit; - - on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, - &on, sizeof(on)) == -1) - goto eexit; - - ICMP6_FILTER_SETBLOCKALL(&filt); - -#ifdef IPV6_SEND_DAD - /* We send DAD requests from the unspecified address. */ - unspec_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - if (unspec_sock == -1) - goto eexit; - if (setsockopt(unspec_sock, IPPROTO_ICMPV6, ICMP6_FILTER, - &filt, sizeof(filt)) == -1) - goto eexit; - memset(&su, 0, sizeof(su)); - su.sin.sin6_family = AF_INET6; -#ifdef SIN6_LEN - su.sin.sin6_len = sizeof(su.sin); -#endif - if (bind(unspec_sock, &su.sa, sizeof(su.sin)) == -1) - goto eexit; -#endif - - ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt); - if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, - &filt, sizeof(filt)) == -1) - goto eexit; - - set_cloexec(sock); -#if DEBUG_MEMORY - atexit(ipv6ns_cleanup); -#endif - -#ifdef LISTEN_DAD - syslog(LOG_WARNING, "kernel does not report DAD results to userland"); - syslog(LOG_WARNING, - "warning listening to duplicated addresses on the wire"); -#endif - - len = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); - sndbuf = calloc(1, len); - if (sndbuf == NULL) - goto eexit; - sndhdr.msg_namelen = sizeof(struct sockaddr_in6); - sndhdr.msg_iov = sndiov; - sndhdr.msg_iovlen = 1; - sndhdr.msg_control = sndbuf; - sndhdr.msg_controllen = len; - rcvbuf = calloc(1, len); - if (rcvbuf == NULL) - goto eexit; - rcvhdr.msg_name = &from; - rcvhdr.msg_namelen = sizeof(from); - rcvhdr.msg_iov = rcviov; - rcvhdr.msg_iovlen = 1; - rcvhdr.msg_control = rcvbuf; - rcvhdr.msg_controllen = len; - rcviov[0].iov_base = ansbuf; - rcviov[0].iov_len = sizeof(ansbuf); - - eloop_event_add(sock, ipv6ns_handledata, NULL); - return sock; - -eexit: - syslog(LOG_ERR, "%s: %m", __func__); - close(sock); - sock = -1; - free(sndbuf); - sndbuf = NULL; - free(rcvbuf); - rcvbuf = NULL; - return -1; -} - -static void -ipv6ns_unreachable(void *arg) -{ - struct ra *rap = arg; - struct timeval tv; - - /* We could add an unreachable flag and persist the information, - * but that is more effort than it's probably worth. */ - syslog(LOG_WARNING, "%s: %s is unreachable, expiring it", - rap->iface->name, rap->sfrom); - rap->expired = 1; - ipv6_buildroutes(); - script_runreason(rap->iface, "ROUTERADVERT"); /* XXX not RA */ - - /* We should still test if it's reachable or not so - * incase it comes back to life and it's preferable. */ - if (rap->reachable) { - ms_to_tv(&tv, rap->reachable); - } else { - tv.tv_sec = REACHABLE_TIME; - tv.tv_usec = 0; - } - eloop_timeout_add_tv(&tv, ipv6ns_proberouter, rap); -} - -#ifdef LISTEN_DAD -void -ipv6ns_cancelprobeaddr(struct ipv6_addr *ap) -{ - - eloop_timeout_delete(ipv6ns_probeaddr, ap); - if (ap->dadcallback) - eloop_timeout_delete(ap->dadcallback, ap); -} -#endif - -void -ipv6ns_probeaddr(void *arg) -{ - struct ipv6_addr *ap = arg; -#ifdef IPV6_SEND_DAD - struct nd_neighbor_solicit *ns; - struct nd_opt_hdr *nd; - struct sockaddr_in6 dst; - struct cmsghdr *cm; - struct in6_pktinfo pi; - int hoplimit = HOPLIMIT; -#else -#ifdef LISTEN_DAD - struct timeval tv, rtv; - struct timeval mtv; - int i; -#endif -#endif - - if (ap->dadcallback && - ((ap->flags & IPV6_AF_NEW) == 0 || - ap->nsprobes >= ap->iface->options->dadtransmits)) - { -#ifdef IPV6_SEND_DAD - ap->dadcallback(ap); -#else - ipv6_addaddr(ap); -#endif - return; - } - - if (ipv6ns_open() == -1) - return; - - ap->flags &= ~IPV6_AF_DADCOMPLETED; - -#ifdef IPV6_SEND_DAD - if (!ap->ns) { - ap->nslen = sizeof(*ns) + ROUNDUP8(ap->iface->hwlen + 2); - ap->ns = calloc(1, ap->nslen); - if (ap->ns == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - return; - } - ns = (struct nd_neighbor_solicit *)(void *)ap->ns; - ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; - //ns->nd_ns_cksum = 0; - //ns->nd_ns_code = 0; - //ns->nd_ns_reserved = 0; - ns->nd_ns_target = ap->addr; - nd = (struct nd_opt_hdr *)(ap->ns + sizeof(*ns)); - nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd->nd_opt_len = (ROUNDUP8(ap->iface->hwlen + 2)) >> 3; - memcpy(nd + 1, ap->iface->hwaddr, ap->iface->hwlen); - } - - memset(&dst, 0, sizeof(dst)); - dst.sin6_family = AF_INET6; -#ifdef SIN6_LEN - dst.sin6_len = sizeof(dst); -#endif - dst.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL; - dst.sin6_addr.s6_addr16[1] = 0; - dst.sin6_addr.s6_addr32[1] = 0; - dst.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE; - dst.sin6_addr.s6_addr32[3] = ap->addr.s6_addr32[3]; - dst.sin6_addr.s6_addr[12] = 0xff; - - //memcpy(&dst.sin6_addr, &ap->addr, sizeof(dst.sin6_addr)); - dst.sin6_scope_id = ap->iface->index; - - sndhdr.msg_name = (caddr_t)&dst; - sndhdr.msg_iov[0].iov_base = ap->ns; - sndhdr.msg_iov[0].iov_len = ap->nslen; - - /* Set the outbound interface */ - cm = CMSG_FIRSTHDR(&sndhdr); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(pi)); - memset(&pi, 0, sizeof(pi)); - pi.ipi6_ifindex = ap->iface->index; - memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); - - /* Hop limit */ - cm = CMSG_NXTHDR(&sndhdr, cm); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_HOPLIMIT; - cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); - memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); - -#ifdef DEBUG_NS - syslog(LOG_INFO, "%s: sending IPv6 NS for %s", - ap->iface->name, ap->saddr); - if (ap->dadcallback == NULL) - syslog(LOG_WARNING, "%s: no callback!", ap->iface->name); -#endif - if (sendmsg(unspec_sock, &sndhdr, 0) == -1) { - syslog(LOG_ERR, "%s: %s: sendmsg: %m", - ap->iface->name, __func__); - return; - } - - if (ap->dadcallback) { - ms_to_tv(&tv, RETRANS_TIMER); - ms_to_tv(&rtv, MIN_RANDOM_FACTOR); - timeradd(&tv, &rtv, &tv); - rtv.tv_sec = 0; - rtv.tv_usec = arc4random() % - (MAX_RANDOM_FACTOR_U - MIN_RANDOM_FACTOR_U); - timeradd(&tv, &rtv, &tv); - - eloop_timeout_add_tv(&tv, - ++(ap->nsprobes) < ap->iface->options->dadtransmits ? - ipv6ns_probeaddr : ap->dadcallback, - ap); - } -#else /* IPV6_SEND_DAD */ - ipv6_addaddr(ap); -#ifdef LISTEN_DAD - /* Let the kernel handle DAD. - * We don't know the timings, so just wait for the max */ - if (ap->dadcallback) { - mtv.tv_sec = 0; - mtv.tv_usec = 0; - for (i = 0; i < ap->iface->options->dadtransmits; i++) { - ms_to_tv(&tv, RETRANS_TIMER); - ms_to_tv(&rtv, MAX_RANDOM_FACTOR); - timeradd(&tv, &rtv, &tv); - timeradd(&mtv, &tv, &mtv); - } - eloop_timeout_add_tv(&mtv, ap->dadcallback, ap); - } -#endif -#endif /* IPV6_SEND_DAD */ -} - -ssize_t -ipv6ns_probeaddrs(struct ipv6_addrhead *addrs) -{ - struct ipv6_addr *ap, *apn; - ssize_t i; - - i = 0; - TAILQ_FOREACH_SAFE(ap, addrs, next, apn) { - if (ap->prefix_vltime == 0) { - TAILQ_REMOVE(addrs, ap, next); - if (ap->flags & IPV6_AF_ADDED) { - syslog(LOG_INFO, "%s: deleting address %s", - ap->iface->name, ap->saddr); - i++; - } - if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) && - del_address6(ap) == -1 && - errno != EADDRNOTAVAIL && errno != ENXIO) - syslog(LOG_ERR, "del_address6 %m"); - if (ap->dadcallback) - eloop_q_timeout_delete(0, NULL, - ap->dadcallback); - free(ap); - } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) && - !(ap->flags & IPV6_AF_DELEGATED)) - { - ipv6ns_probeaddr(ap); - if (ap->flags & IPV6_AF_NEW) - i++; - } - } - - return i; -} - -void -ipv6ns_proberouter(void *arg) -{ - struct ra *rap = arg; - struct nd_neighbor_solicit *ns; - struct nd_opt_hdr *nd; - struct sockaddr_in6 dst; - struct cmsghdr *cm; - struct in6_pktinfo pi; - int hoplimit = HOPLIMIT; - struct timeval tv, rtv; - - if (ipv6ns_open() == -1) - return; - - if (!rap->ns) { - rap->nslen = sizeof(*ns) + ROUNDUP8(rap->iface->hwlen + 2); - rap->ns = calloc(1, rap->nslen); - if (rap->ns == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - return; - } - ns = (struct nd_neighbor_solicit *)(void *)rap->ns; - ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; - //ns->nd_ns_cksum = 0; - //ns->nd_ns_code = 0; - //ns->nd_ns_reserved = 0; - ns->nd_ns_target = rap->from; - nd = (struct nd_opt_hdr *)(rap->ns + sizeof(*ns)); - nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd->nd_opt_len = (ROUNDUP8(rap->iface->hwlen + 2)) >> 3; - memcpy(nd + 1, rap->iface->hwaddr, rap->iface->hwlen); - } - - memset(&dst, 0, sizeof(dst)); - dst.sin6_family = AF_INET6; -#ifdef SIN6_LEN - dst.sin6_len = sizeof(dst); -#endif - memcpy(&dst.sin6_addr, &rap->from, sizeof(dst.sin6_addr)); - dst.sin6_scope_id = rap->iface->index; - - sndhdr.msg_name = (caddr_t)&dst; - sndhdr.msg_iov[0].iov_base = rap->ns; - sndhdr.msg_iov[0].iov_len = rap->nslen; - - /* Set the outbound interface */ - cm = CMSG_FIRSTHDR(&sndhdr); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(pi)); - memset(&pi, 0, sizeof(pi)); - pi.ipi6_ifindex = rap->iface->index; - memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); - - /* Hop limit */ - cm = CMSG_NXTHDR(&sndhdr, cm); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_HOPLIMIT; - cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); - memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); - -#ifdef DEBUG_NS - syslog(LOG_INFO, "%s: sending IPv6 NS for %s", - rap->iface->name, rap->sfrom); -#endif - if (sendmsg(sock, &sndhdr, 0) == -1) { - syslog(LOG_ERR, "%s: %s: sendmsg: %m", - rap->iface->name, __func__); - return; - } - - ms_to_tv(&tv, rap->retrans == 0 ? RETRANS_TIMER : rap->retrans); - ms_to_tv(&rtv, MIN_RANDOM_FACTOR); - timeradd(&tv, &rtv, &tv); - rtv.tv_sec = 0; - rtv.tv_usec = arc4random() % (MAX_RANDOM_FACTOR_U -MIN_RANDOM_FACTOR_U); - timeradd(&tv, &rtv, &tv); - eloop_timeout_add_tv(&tv, ipv6ns_proberouter, rap); - - if (rap->nsprobes++ == 0) - eloop_timeout_add_sec(DELAY_FIRST_PROBE_TIME, - ipv6ns_unreachable, rap); -} - -void -ipv6ns_cancelproberouter(struct ra *rap) -{ - - eloop_timeout_delete(ipv6ns_proberouter, rap); - eloop_timeout_delete(ipv6ns_unreachable, rap); -} - -/* ARGSUSED */ -static void -ipv6ns_handledata(__unused void *arg) -{ - ssize_t len; - struct cmsghdr *cm; - int hoplimit; - struct in6_pktinfo pkt; - struct icmp6_hdr *icp; - struct interface *ifp; - const char *sfrom; - struct nd_neighbor_advert *nd_na; - struct ra *rap; - int is_router, is_solicited; -#ifdef DEBUG_NS - int found; -#endif - struct timeval tv; - -#ifdef LISTEN_DAD - struct dhcp6_state *d6state; - struct ipv6_addr *ap; -#endif - - len = recvmsg(sock, &rcvhdr, 0); - if (len == -1) { - syslog(LOG_ERR, "recvmsg: %m"); - return; - } - sfrom = inet_ntop(AF_INET6, &from.sin6_addr, - ntopbuf, INET6_ADDRSTRLEN); - if ((size_t)len < sizeof(struct nd_neighbor_advert)) { - syslog(LOG_ERR, "IPv6 NA packet too short from %s", sfrom); - return; - } - - pkt.ipi6_ifindex = hoplimit = 0; - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr); - cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm)) - { - if (cm->cmsg_level != IPPROTO_IPV6) - continue; - switch(cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len == CMSG_LEN(sizeof(pkt))) - memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt)); - break; - case IPV6_HOPLIMIT: - if (cm->cmsg_len == CMSG_LEN(sizeof(int))) - memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int)); - break; - } - } - - if (pkt.ipi6_ifindex == 0 || hoplimit != 255) { - syslog(LOG_ERR, - "IPv6 NA did not contain index or hop limit from %s", - sfrom); - return; - } - - icp = (struct icmp6_hdr *)rcvhdr.msg_iov[0].iov_base; - if (icp->icmp6_type != ND_NEIGHBOR_ADVERT || - icp->icmp6_code != 0) - { - syslog(LOG_ERR, "invalid IPv6 type or code from %s", sfrom); - return; - } - - TAILQ_FOREACH(ifp, ifaces, next) { - if (ifp->index == (unsigned int)pkt.ipi6_ifindex) - break; - } - if (ifp == NULL) { -#ifdef DEBUG_NS - syslog(LOG_DEBUG, "NA for unexpected interface from %s", sfrom); -#endif - return; - } - - nd_na = (struct nd_neighbor_advert *)icp; - is_router = nd_na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER; - is_solicited = nd_na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED; - - if (IN6_IS_ADDR_MULTICAST(&nd_na->nd_na_target)) { - syslog(LOG_ERR, "%s: NA for multicast address from %s", - ifp->name, sfrom); - return; - } - -#ifdef DEBUG_NS - found = 0; -#endif - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (rap->iface != ifp) - continue; - if (memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr, - sizeof(rap->from.s6_addr)) == 0) - break; -#ifdef LISTEN_DAD - TAILQ_FOREACH(ap, &rap->addrs, next) { - if (memcmp(ap->addr.s6_addr, - nd_na->nd_na_target.s6_addr, - sizeof(ap->addr.s6_addr)) == 0) - { - ap->flags |= IPV6_AF_DUPLICATED; - if (ap->dadcallback) - ap->dadcallback(ap); -#ifdef DEBUG_NS - found++; -#endif - } - } -#endif - } - if (rap == NULL) { -#ifdef LISTEN_DAD - d6state = D6_STATE(ifp); - if (d6state) { - TAILQ_FOREACH(ap, &d6state->addrs, next) { - if (memcmp(ap->addr.s6_addr, - nd_na->nd_na_target.s6_addr, - sizeof(ap->addr.s6_addr)) == 0) - { - ap->flags |= IPV6_AF_DUPLICATED; - if (ap->dadcallback) - ap->dadcallback(ap); -#ifdef DEBUG_NS - found++; -#endif - } - } - } -#endif - -#ifdef DEBUG_NS - if (found == 0) - syslog(LOG_DEBUG, "%s: unexpected NA from %s", - ifp->name, sfrom); -#endif - return; - } - -#ifdef DEBUG_NS - syslog(LOG_DEBUG, "%s: %sNA from %s", - ifp->name, is_solicited ? "solicited " : "", sfrom); -#endif - - /* Node is no longer a router, so remove it from consideration */ - if (!is_router && !rap->expired) { - syslog(LOG_INFO, "%s: %s is no longer a router", - ifp->name, sfrom); - rap->expired = 1; - ipv6ns_cancelproberouter(rap); - ipv6_buildroutes(); - script_runreason(ifp, "ROUTERADVERT"); - return; - } - - if (is_solicited && is_router && rap->lifetime) { - if (rap->expired) { - rap->expired = 0; - syslog(LOG_INFO, "%s: %s is reachable again", - ifp->name, sfrom); - ipv6_buildroutes(); - script_runreason(rap->iface, "ROUTERADVERT"); /* XXX */ - } - rap->nsprobes = 0; - if (rap->reachable) { - ms_to_tv(&tv, rap->reachable); - } else { - tv.tv_sec = REACHABLE_TIME; - tv.tv_usec = 0; - } - eloop_timeout_add_tv(&tv, ipv6ns_proberouter, rap); - eloop_timeout_delete(ipv6ns_unreachable, rap); - } -} diff --git a/external/bsd/dhcpcd/dist/ipv6ns.h b/external/bsd/dhcpcd/dist/ipv6ns.h deleted file mode 100644 index d22459fdfdf9..000000000000 --- a/external/bsd/dhcpcd/dist/ipv6ns.h +++ /dev/null @@ -1,51 +0,0 @@ -/* $NetBSD: ipv6ns.h,v 1.1.1.3 2013/06/21 19:33:08 roy Exp $ */ - -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2013 Roy Marples - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef IPV6NS_H -#define IPV6NS_H - -#include "dhcpcd.h" -#include "ipv6rs.h" - -#define MAX_REACHABLE_TIME 3600 /* seconds */ -#define REACHABLE_TIME 30 /* seconds */ -#define RETRANS_TIMER 1000 /* milliseconds */ -#define DELAY_FIRST_PROBE_TIME 5 /* seconds */ - -void ipv6ns_probeaddr(void *); -ssize_t ipv6ns_probeaddrs(struct ipv6_addrhead *); -void ipv6ns_proberouter(void *); -void ipv6ns_cancelproberouter(struct ra *); - -#ifdef LISTEN_DAD -void ipv6ns_cancelprobeaddr(struct ipv6_addr *); -#else -#define ipv6ns_cancelprobeaddr(a) -#endif -#endif diff --git a/external/bsd/dhcpcd/dist/ipv6rs.c b/external/bsd/dhcpcd/dist/ipv6rs.c deleted file mode 100644 index 851f42ad0a1a..000000000000 --- a/external/bsd/dhcpcd/dist/ipv6rs.c +++ /dev/null @@ -1,1277 +0,0 @@ -#include - __RCSID("$NetBSD: ipv6rs.c,v 1.1.1.13 2013/07/19 11:52:57 roy Exp $"); - -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2013 Roy Marples - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -# define _LINUX_IN6_H -# include -#endif - -#include -#include -#include -#include -#include -#include - -#define ELOOP_QUEUE 2 -#include "common.h" -#include "dhcpcd.h" -#include "dhcp6.h" -#include "eloop.h" -#include "ipv6.h" -#include "ipv6ns.h" -#include "ipv6rs.h" -#include "script.h" - -/* Debugging Router Solicitations is a lot of spam, so disable it */ -//#define DEBUG_RS - -#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ -#define MAX_RTR_SOLICITATIONS 3 /* times */ - -#ifndef ND_OPT_RDNSS -#define ND_OPT_RDNSS 25 -struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ - uint8_t nd_opt_rdnss_type; - uint8_t nd_opt_rdnss_len; - uint16_t nd_opt_rdnss_reserved; - uint32_t nd_opt_rdnss_lifetime; - /* followed by list of IP prefixes */ -} __packed; -#endif - -#ifndef ND_OPT_DNSSL -#define ND_OPT_DNSSL 31 -struct nd_opt_dnssl { /* DNSSL option RFC 6106 */ - uint8_t nd_opt_dnssl_type; - uint8_t nd_opt_dnssl_len; - uint16_t nd_opt_dnssl_reserved; - uint32_t nd_opt_dnssl_lifetime; - /* followed by list of DNS servers */ -} __packed; -#endif - -/* Minimal IPv6 MTU */ -#ifndef IPV6_MMTU -#define IPV6_MMTU 1280 -#endif - -#ifndef ND_RA_FLAG_RTPREF_HIGH -#define ND_RA_FLAG_RTPREF_MASK 0x18 -#define ND_RA_FLAG_RTPREF_HIGH 0x08 -#define ND_RA_FLAG_RTPREF_MEDIUM 0x00 -#define ND_RA_FLAG_RTPREF_LOW 0x18 -#define ND_RA_FLAG_RTPREF_RSV 0x10 -#endif - -/* RTPREF_MEDIUM has to be 0! */ -#define RTPREF_HIGH 1 -#define RTPREF_MEDIUM 0 -#define RTPREF_LOW (-1) -#define RTPREF_RESERVED (-2) -#define RTPREF_INVALID (-3) /* internal */ - -struct rahead ipv6_routers = TAILQ_HEAD_INITIALIZER(ipv6_routers); - -static int sock = -1; -static struct sockaddr_in6 allrouters, from; -static struct msghdr sndhdr; -static struct iovec sndiov[2]; -static unsigned char *sndbuf; -static struct msghdr rcvhdr; -static struct iovec rcviov[2]; -static unsigned char *rcvbuf; -static unsigned char ansbuf[1500]; -static char ntopbuf[INET6_ADDRSTRLEN]; - -#if DEBUG_MEMORY -static void -ipv6rs_cleanup(void) -{ - - free(sndbuf); - free(rcvbuf); -} -#endif - -static int -ipv6rs_open(void) -{ - int on; - int len; - struct icmp6_filter filt; - - sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - if (sock == -1) - return -1; - - memset(&allrouters, 0, sizeof(allrouters)); - allrouters.sin6_family = AF_INET6; -#ifdef SIN6_LEN - allrouters.sin6_len = sizeof(allrouters); -#endif - if (inet_pton(AF_INET6, ALLROUTERS, &allrouters.sin6_addr.s6_addr) != 1) - goto eexit; - on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, - &on, sizeof(on)) == -1) - goto eexit; - - on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, - &on, sizeof(on)) == -1) - goto eexit; - - ICMP6_FILTER_SETBLOCKALL(&filt); - ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); - if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, - &filt, sizeof(filt)) == -1) - goto eexit; - - set_cloexec(sock); -#if DEBUG_MEMORY - atexit(ipv6rs_cleanup); -#endif - - len = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); - sndbuf = calloc(1, len); - if (sndbuf == NULL) - goto eexit; - sndhdr.msg_namelen = sizeof(struct sockaddr_in6); - sndhdr.msg_iov = sndiov; - sndhdr.msg_iovlen = 1; - sndhdr.msg_control = sndbuf; - sndhdr.msg_controllen = len; - rcvbuf = calloc(1, len); - if (rcvbuf == NULL) - goto eexit; - rcvhdr.msg_name = &from; - rcvhdr.msg_namelen = sizeof(from); - rcvhdr.msg_iov = rcviov; - rcvhdr.msg_iovlen = 1; - rcvhdr.msg_control = rcvbuf; - rcvhdr.msg_controllen = len; - rcviov[0].iov_base = ansbuf; - rcviov[0].iov_len = sizeof(ansbuf); - return sock; - -eexit: - close(sock); - sock = -1; - free(sndbuf); - sndbuf = NULL; - free(rcvbuf); - rcvbuf = NULL; - return -1; -} - -static int -ipv6rs_makeprobe(struct interface *ifp) -{ - struct rs_state *state; - struct nd_router_solicit *rs; - struct nd_opt_hdr *nd; - - state = RS_STATE(ifp); - free(state->rs); - state->rslen = sizeof(*rs) + ROUNDUP8(ifp->hwlen + 2); - state->rs = calloc(1, state->rslen); - if (state->rs == NULL) - return -1; - rs = (struct nd_router_solicit *)(void *)state->rs; - rs->nd_rs_type = ND_ROUTER_SOLICIT; - rs->nd_rs_code = 0; - rs->nd_rs_cksum = 0; - rs->nd_rs_reserved = 0; - nd = (struct nd_opt_hdr *)(state->rs + sizeof(*rs)); - nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd->nd_opt_len = (ROUNDUP8(ifp->hwlen + 2)) >> 3; - memcpy(nd + 1, ifp->hwaddr, ifp->hwlen); - return 0; -} - -static void -ipv6rs_sendprobe(void *arg) -{ - struct interface *ifp = arg; - struct rs_state *state; - struct sockaddr_in6 dst; - struct cmsghdr *cm; - struct in6_pktinfo pi; - int hoplimit = HOPLIMIT; - - if (ipv6_linklocal(ifp) == NULL) { - syslog(LOG_DEBUG, - "%s: delaying Router Soliciation for LL address", - ifp->name); - ipv6_addlinklocalcallback(ifp, ipv6rs_sendprobe, ifp); - return; - } - - dst = allrouters; - dst.sin6_scope_id = ifp->index; - - state = RS_STATE(ifp); - sndhdr.msg_name = (caddr_t)&dst; - sndhdr.msg_iov[0].iov_base = state->rs; - sndhdr.msg_iov[0].iov_len = state->rslen; - - /* Set the outbound interface */ - cm = CMSG_FIRSTHDR(&sndhdr); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(pi)); - memset(&pi, 0, sizeof(pi)); - pi.ipi6_ifindex = ifp->index; - memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); - - /* Hop limit */ - cm = CMSG_NXTHDR(&sndhdr, cm); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_HOPLIMIT; - cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); - memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); - - syslog(LOG_DEBUG, "%s: sending Router Solicitation", ifp->name); - if (sendmsg(sock, &sndhdr, 0) == -1) { - syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__); - ipv6rs_drop(ifp); - ifp->options->options &= ~(DHCPCD_IPV6 | DHCPCD_IPV6RS); - return; - } - - if (state->rsprobes++ < MAX_RTR_SOLICITATIONS) - eloop_timeout_add_sec(RTR_SOLICITATION_INTERVAL, - ipv6rs_sendprobe, ifp); - else - syslog(LOG_WARNING, "%s: no IPv6 Routers available", ifp->name); -} - -static void -ipv6rs_free_opts(struct ra *rap) -{ - struct ra_opt *rao; - - while ((rao = TAILQ_FIRST(&rap->options))) { - TAILQ_REMOVE(&rap->options, rao, next); - free(rao->option); - free(rao); - } -} - -int -ipv6rs_addrexists(const struct ipv6_addr *a) -{ - struct ra *rap; - struct ipv6_addr *ap; - - TAILQ_FOREACH(rap, &ipv6_routers, next) { - TAILQ_FOREACH(ap, &rap->addrs, next) { - if (memcmp(&ap->addr, &a->addr, sizeof(a->addr)) == 0) - return 1; - } - } - return 0; -} - -void ipv6rs_freedrop_ra(struct ra *rap, int drop) -{ - - eloop_timeout_delete(NULL, rap->iface); - eloop_timeout_delete(NULL, rap); - if (!drop) - TAILQ_REMOVE(&ipv6_routers, rap, next); - ipv6_freedrop_addrs(&rap->addrs, drop, NULL); - ipv6rs_free_opts(rap); - free(rap->data); - free(rap->ns); - free(rap); -} - -ssize_t -ipv6rs_free(struct interface *ifp) -{ - struct rs_state *state; - struct ra *rap, *ran; - ssize_t n; - - state = RS_STATE(ifp); - if (state) { - free(state->rs); - free(state); - ifp->if_data[IF_DATA_IPV6RS] = NULL; - } - n = 0; - TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) { - if (rap->iface == ifp) { - ipv6rs_free_ra(rap); - n++; - } - } - return n; -} - -static int -rtpref(struct ra *rap) -{ - - switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) { - case ND_RA_FLAG_RTPREF_HIGH: - return (RTPREF_HIGH); - case ND_RA_FLAG_RTPREF_MEDIUM: - case ND_RA_FLAG_RTPREF_RSV: - return (RTPREF_MEDIUM); - case ND_RA_FLAG_RTPREF_LOW: - return (RTPREF_LOW); - default: - syslog(LOG_ERR, "rtpref: impossible RA flag %x", rap->flags); - return (RTPREF_INVALID); - } - /* NOTREACHED */ -} - -static void -add_router(struct ra *router) -{ - struct ra *rap; - - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (router->iface->metric < rap->iface->metric || - (router->iface->metric == rap->iface->metric && - rtpref(router) > rtpref(rap))) - { - TAILQ_INSERT_BEFORE(rap, router, next); - return; - } - } - TAILQ_INSERT_TAIL(&ipv6_routers, router, next); -} - -static void -ipv6rs_scriptrun(struct ra *rap) -{ - int hasdns; - struct ipv6_addr *ap; - const struct ra_opt *rao; - - /* If all addresses have completed DAD run the script */ - TAILQ_FOREACH(ap, &rap->addrs, next) { - if ((ap->flags & (IPV6_AF_ONLINK | IPV6_AF_AUTOCONF)) == - (IPV6_AF_ONLINK | IPV6_AF_AUTOCONF)) - { - if (!(ap->flags & IPV6_AF_DADCOMPLETED) && - ipv6_findaddr(ap->iface, &ap->addr)) - ap->flags |= IPV6_AF_DADCOMPLETED; - if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { - syslog(LOG_DEBUG, - "%s: waiting for Router Advertisement" - " DAD to complete", - rap->iface->name); - return; - } - } - } - - /* If we don't require RDNSS then set hasdns = 1 so we fork */ - if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS)) - hasdns = 1; - else { - hasdns = 0; - TAILQ_FOREACH(rao, &rap->options, next) { - if (rao->type == ND_OPT_RDNSS && - rao->option && - timerisset(&rao->expire)) - { - hasdns = 1; - break; - } - } - } - - script_runreason(rap->iface, "ROUTERADVERT"); - if (hasdns) - daemonise(); -#if 0 - else if (options & DHCPCD_DAEMONISE && - !(options & DHCPCD_DAEMONISED) && new_data) - syslog(LOG_WARNING, - "%s: did not fork due to an absent" - " RDNSS option in the RA", - ifp->name); -} -#endif -} - -static void -ipv6rs_dadcallback(void *arg) -{ - struct ipv6_addr *ap = arg, *rapap; - struct interface *ifp; - struct ra *rap; - int wascompleted, found; - - wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED); - ipv6ns_cancelprobeaddr(ap); - ap->flags |= IPV6_AF_DADCOMPLETED; - if (ap->flags & IPV6_AF_DUPLICATED) - /* No idea what how to try and make another address :( */ - syslog(LOG_WARNING, "%s: DAD detected %s", - ap->iface->name, ap->saddr); -#ifdef IPV6_SEND_DAD - else - ipv6_addaddr(ap); -#endif - - if (!wascompleted) { - ifp = ap->iface; - - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (rap->iface != ifp) - continue; - wascompleted = 1; - TAILQ_FOREACH(rapap, &rap->addrs, next) { - if (rapap->flags & IPV6_AF_AUTOCONF && - (rapap->flags & IPV6_AF_DADCOMPLETED) == 0) - { - wascompleted = 0; - break; - } - if (rapap == ap) - found = 1; - } - - if (wascompleted && found && rap->lifetime) { - syslog(LOG_DEBUG, - "%s: Router Advertisement DAD completed", - rap->iface->name); - ipv6rs_scriptrun(rap); - } - } - } -} - -/* ARGSUSED */ -static void -ipv6rs_handledata(__unused void *arg) -{ - ssize_t len, l, m, n, olen; - struct cmsghdr *cm; - int hoplimit; - struct in6_pktinfo pkt; - struct icmp6_hdr *icp; - struct interface *ifp; - const char *sfrom; - struct nd_router_advert *nd_ra; - struct nd_opt_prefix_info *pi; - struct nd_opt_mtu *mtu; - struct nd_opt_rdnss *rdnss; - struct nd_opt_dnssl *dnssl; - uint32_t lifetime, mtuv; - uint8_t *p, *op; - struct in6_addr addr; - char buf[INET6_ADDRSTRLEN]; - const char *cbp; - struct ra *rap; - struct nd_opt_hdr *ndo; - struct ra_opt *rao; - struct ipv6_addr *ap; - char *opt, *tmp; - struct timeval expire; - uint8_t new_rap, new_data; - - len = recvmsg(sock, &rcvhdr, 0); - if (len == -1) { - syslog(LOG_ERR, "recvmsg: %m"); - return; - } - sfrom = inet_ntop(AF_INET6, &from.sin6_addr, - ntopbuf, INET6_ADDRSTRLEN); - if ((size_t)len < sizeof(struct nd_router_advert)) { - syslog(LOG_ERR, "IPv6 RA packet too short from %s", sfrom); - return; - } - - pkt.ipi6_ifindex = hoplimit = 0; - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr); - cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm)) - { - if (cm->cmsg_level != IPPROTO_IPV6) - continue; - switch(cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len == CMSG_LEN(sizeof(pkt))) - memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt)); - break; - case IPV6_HOPLIMIT: - if (cm->cmsg_len == CMSG_LEN(sizeof(int))) - memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int)); - break; - } - } - - if (pkt.ipi6_ifindex == 0 || hoplimit == 0) { - syslog(LOG_ERR, - "IPv6 RA did not contain index or hop limit from %s", - sfrom); - return; - } - - icp = (struct icmp6_hdr *)rcvhdr.msg_iov[0].iov_base; - if (icp->icmp6_type != ND_ROUTER_ADVERT || - icp->icmp6_code != 0) - { - syslog(LOG_ERR, "invalid IPv6 type or code from %s", sfrom); - return; - } - - if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { - syslog(LOG_ERR, "RA from non local address %s", sfrom); - return; - } - - TAILQ_FOREACH(ifp, ifaces, next) { - if (ifp->index == (unsigned int)pkt.ipi6_ifindex) - break; - } - if (ifp == NULL) { -#ifdef DEBUG_RS - syslog(LOG_DEBUG, "RA for unexpected interface from %s", sfrom); -#endif - return; - } - if (!(ifp->options->options & DHCPCD_IPV6RS)) { -#ifdef DEBUG_RS - syslog(LOG_DEBUG, "%s: unexpected RA from %s", - ifp->name, sfrom); -#endif - return; - } - - /* We could recieve a RA before we sent a RS*/ - if (ipv6_linklocal(ifp) == NULL) { -#ifdef DEBUG_RS - syslog(LOG_DEBUG, "%s: received RA from %s (no link-local)", - ifp->name, sfrom); -#endif - return; - } - - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (ifp == rap->iface && - memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr, - sizeof(rap->from.s6_addr)) == 0) - break; - } - - nd_ra = (struct nd_router_advert *)icp; - /* Don't bother doing anything if we don't know about a router - * expiring */ - if ((rap == NULL || rap->lifetime == 0) - && nd_ra->nd_ra_router_lifetime == 0) - return; - - /* We don't want to spam the log with the fact we got an RA every - * 30 seconds or so, so only spam the log if it's different. */ - if (rap == NULL || (rap->data_len != len || - memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0)) - { - if (rap) { - free(rap->data); - rap->data_len = 0; - free(rap->ns); - rap->ns = NULL; - rap->nslen = 0; - } - new_data = 1; - } else - new_data = 0; - if (new_data || ifp->options->options & DHCPCD_DEBUG) - syslog(LOG_INFO, "%s: Router Advertisement from %s", - ifp->name, sfrom); - - if (rap == NULL) { - rap = calloc(1, sizeof(*rap)); - if (rap == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - return; - } - rap->iface = ifp; - memcpy(rap->from.s6_addr, from.sin6_addr.s6_addr, - sizeof(rap->from.s6_addr)); - strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom)); - TAILQ_INIT(&rap->addrs); - TAILQ_INIT(&rap->options); - new_rap = 1; - } else - new_rap = 0; - if (rap->data_len == 0) { - rap->data = malloc(len); - if (rap->data == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - if (new_rap) - free(rap); - return; - } - memcpy(rap->data, icp, len); - rap->data_len = len; - } - - get_monotonic(&rap->received); - rap->flags = nd_ra->nd_ra_flags_reserved; - if (new_rap == 0 && rap->lifetime == 0) - syslog(LOG_WARNING, "%s: %s router available", - ifp->name, rap->sfrom); - rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); - if (nd_ra->nd_ra_reachable) { - rap->reachable = ntohl(nd_ra->nd_ra_reachable); - if (rap->reachable > MAX_REACHABLE_TIME) - rap->reachable = 0; - } - if (nd_ra->nd_ra_retransmit) - rap->retrans = ntohl(nd_ra->nd_ra_retransmit); - if (rap->lifetime) - rap->expired = 0; - - len -= sizeof(struct nd_router_advert); - p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); - olen = 0; - lifetime = ~0U; - for (olen = 0; len > 0; p += olen, len -= olen) { - if ((size_t)len < sizeof(struct nd_opt_hdr)) { - syslog(LOG_ERR, "%s: Short option", ifp->name); - break; - } - ndo = (struct nd_opt_hdr *)p; - olen = ndo->nd_opt_len * 8 ; - if (olen == 0) { - syslog(LOG_ERR, "%s: zero length option", ifp->name); - break; - } - if (olen > len) { - syslog(LOG_ERR, - "%s: Option length exceeds message", ifp->name); - break; - } - - opt = NULL; - switch (ndo->nd_opt_type) { - case ND_OPT_PREFIX_INFORMATION: - pi = (struct nd_opt_prefix_info *)(void *)ndo; - if (pi->nd_opt_pi_len != 4) { - syslog(LOG_ERR, - "%s: invalid option len for prefix", - ifp->name); - break; - } - if (pi->nd_opt_pi_prefix_len > 128) { - syslog(LOG_ERR, "%s: invalid prefix len", - ifp->name); - break; - } - if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || - IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) - { - syslog(LOG_ERR, - "%s: invalid prefix in RA", ifp->name); - break; - } - if (ntohl(pi->nd_opt_pi_preferred_time) > - ntohl(pi->nd_opt_pi_valid_time)) - { - syslog(LOG_ERR, - "%s: pltime > vltime", ifp->name); - break; - } - TAILQ_FOREACH(ap, &rap->addrs, next) - if (ap->prefix_len ==pi->nd_opt_pi_prefix_len && - memcmp(ap->prefix.s6_addr, - pi->nd_opt_pi_prefix.s6_addr, - sizeof(ap->prefix.s6_addr)) == 0) - break; - if (ap == NULL) { - if (!(pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) && - !(pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK)) - break; - ap = calloc(1, sizeof(*ap)); - if (ap == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - break; - } - ap->iface = rap->iface; - ap->flags = IPV6_AF_NEW; - ap->prefix_len = pi->nd_opt_pi_prefix_len; - memcpy(ap->prefix.s6_addr, - pi->nd_opt_pi_prefix.s6_addr, - sizeof(ap->prefix.s6_addr)); - if (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) - { - ap->flags |= IPV6_AF_AUTOCONF; - ipv6_makeaddr(&ap->addr, ifp, - &ap->prefix, - pi->nd_opt_pi_prefix_len); - cbp = inet_ntop(AF_INET6, - ap->addr.s6_addr, - ntopbuf, INET6_ADDRSTRLEN); - if (cbp) - snprintf(ap->saddr, - sizeof(ap->saddr), - "%s/%d", - cbp, ap->prefix_len); - else - ap->saddr[0] = '\0'; - } else { - memset(&ap->addr, 0, sizeof(ap->addr)); - ap->saddr[0] = '\0'; - } - ap->dadcallback = ipv6rs_dadcallback; - TAILQ_INSERT_TAIL(&rap->addrs, ap, next); - } - if (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK) - ap->flags |= IPV6_AF_ONLINK; - ap->prefix_vltime = - ntohl(pi->nd_opt_pi_valid_time); - ap->prefix_pltime = - ntohl(pi->nd_opt_pi_preferred_time); - ap->nsprobes = 0; - if (opt) { - l = strlen(opt); - tmp = realloc(opt, - l + strlen(ap->saddr) + 2); - if (tmp) { - opt = tmp; - opt[l] = ' '; - strcpy(opt + l + 1, ap->saddr); - } - } else - opt = strdup(ap->saddr); - lifetime = ap->prefix_vltime; - break; - - case ND_OPT_MTU: - mtu = (struct nd_opt_mtu *)(void *)p; - mtuv = ntohl(mtu->nd_opt_mtu_mtu); - if (mtuv < IPV6_MMTU) { - syslog(LOG_ERR, "%s: invalid MTU %d", - ifp->name, mtuv); - break; - } - rap->mtu = mtuv; - snprintf(buf, sizeof(buf), "%d", mtuv); - opt = strdup(buf); - break; - - case ND_OPT_RDNSS: - rdnss = (struct nd_opt_rdnss *)p; - lifetime = ntohl(rdnss->nd_opt_rdnss_lifetime); - op = (uint8_t *)ndo; - op += offsetof(struct nd_opt_rdnss, - nd_opt_rdnss_lifetime); - op += sizeof(rdnss->nd_opt_rdnss_lifetime); - l = 0; - for (n = ndo->nd_opt_len - 1; n > 1; n -= 2, - op += sizeof(addr.s6_addr)) - { - m = ipv6_printaddr(NULL, 0, op, ifp->name); - if (m != -1) - l += m + 1; - } - op = (uint8_t *)ndo; - op += offsetof(struct nd_opt_rdnss, - nd_opt_rdnss_lifetime); - op += sizeof(rdnss->nd_opt_rdnss_lifetime); - tmp = opt = malloc(l); - if (opt) { - for (n = ndo->nd_opt_len - 1; n > 1; n -= 2, - op += sizeof(addr.s6_addr)) - { - m = ipv6_printaddr(tmp, l, op, - ifp->name); - if (m != -1) { - l -= (m + 1); - tmp += m; - *tmp++ = ' '; - } - } - if (tmp != opt) - (*--tmp) = '\0'; - else - *opt = '\0'; - } - break; - - case ND_OPT_DNSSL: - dnssl = (struct nd_opt_dnssl *)p; - lifetime = ntohl(dnssl->nd_opt_dnssl_lifetime); - op = p + offsetof(struct nd_opt_dnssl, - nd_opt_dnssl_lifetime); - op += sizeof(dnssl->nd_opt_dnssl_lifetime); - n = (dnssl->nd_opt_dnssl_len - 1) * 8; - l = decode_rfc3397(NULL, 0, n, op); - if (l < 1) { - syslog(LOG_ERR, "%s: invalid DNSSL option", - ifp->name); - } else { - tmp = malloc(l); - if (tmp) { - decode_rfc3397(tmp, l, n, op); - n = print_string(NULL, 0, - l - 1, (const uint8_t *)tmp); - opt = malloc(n); - if (opt) - print_string(opt, n, - l - 1, - (const uint8_t *)tmp); - free(tmp); - } - } - break; - - default: - continue; - } - - if (opt == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - continue; - } - TAILQ_FOREACH(rao, &rap->options, next) { - if (rao->type == ndo->nd_opt_type && - strcmp(rao->option, opt) == 0) - break; - } - if (lifetime == 0) { - if (rao) { - TAILQ_REMOVE(&rap->options, rao, next); - free(rao->option); - free(rao); - } - free(opt); - continue; - } - - if (rao == NULL) { - rao = malloc(sizeof(*rao)); - if (rao == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - continue; - } - rao->type = ndo->nd_opt_type; - rao->option = opt; - TAILQ_INSERT_TAIL(&rap->options, rao, next); - } else - free(opt); - if (lifetime == ~0U) - timerclear(&rao->expire); - else { - expire.tv_sec = lifetime; - expire.tv_usec = 0; - timeradd(&rap->received, &expire, &rao->expire); - } - } - - if (new_rap) - add_router(rap); - if (options & DHCPCD_TEST) { - script_runreason(ifp, "TEST"); - goto handle_flag; - } - if (ifp->options->options & DHCPCD_IPV6RA_OWN) - ipv6ns_probeaddrs(&rap->addrs); - ipv6_buildroutes(); - - /* We will get run by the expire function */ - if (rap->lifetime) - ipv6rs_scriptrun(rap); - - eloop_timeout_delete(NULL, ifp); - eloop_timeout_delete(NULL, rap); /* reachable timer */ - - /* If we're owning the RA then we need to try and ensure the - * router is actually reachable */ - if (ifp->options->options & DHCPCD_IPV6RA_OWN || - ifp->options->options & DHCPCD_IPV6RA_OWN_DEFAULT) - { - rap->nsprobes = 0; - if (rap->lifetime) - ipv6ns_proberouter(rap); - } - -handle_flag: - if (rap->flags & ND_RA_FLAG_MANAGED) { - if (rap->lifetime && new_data && - dhcp6_start(ifp, DH6S_INIT) == -1) - syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name); - } else if (rap->flags & ND_RA_FLAG_OTHER) { - if (rap->lifetime && new_data && - dhcp6_start(ifp, DH6S_INFORM) == -1) - syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name); - } else { - if (rap->lifetime && new_data) - syslog(LOG_DEBUG, "%s: No DHCPv6 instruction in RA", - ifp->name); - if (options & DHCPCD_TEST) - exit(EXIT_SUCCESS); - } - - /* Expire should be called last as the rap object could be destroyed */ - ipv6rs_expire(ifp); -} - -int -ipv6rs_has_ra(const struct interface *ifp) -{ - const struct ra *rap; - - TAILQ_FOREACH(rap, &ipv6_routers, next) - if (rap->iface == ifp) - return 1; - return 0; -} - -ssize_t -ipv6rs_env(char **env, const char *prefix, const struct interface *ifp) -{ - ssize_t l; - size_t len; - struct timeval now; - const struct ra *rap; - const struct ra_opt *rao; - int i; - char buffer[32]; - const char *optn; - char **pref, **mtu, **rdnss, **dnssl, ***var, *new; - - i = 0; - l = 0; - get_monotonic(&now); - TAILQ_FOREACH(rap, &ipv6_routers, next) { - i++; - if (rap->iface != ifp) - continue; - if (env) { - snprintf(buffer, sizeof(buffer), - "ra%d_from", i); - if (setvar(&env, prefix, buffer, rap->sfrom) == -1) - return -1; - } - l++; - - pref = mtu = rdnss = dnssl = NULL; - TAILQ_FOREACH(rao, &rap->options, next) { - if (rao->option == NULL) - continue; - var = NULL; - switch(rao->type) { - case ND_OPT_PREFIX_INFORMATION: - optn = "prefix"; - var = &pref; - break; - case ND_OPT_MTU: - optn = "mtu"; - var = &mtu; - break; - case ND_OPT_RDNSS: - optn = "rdnss"; - var = &rdnss; - break; - case ND_OPT_DNSSL: - optn = "dnssl"; - var = &dnssl; - break; - default: - continue; - } - if (*var == NULL) { - *var = env ? env : &new; - l++; - } else if (env) { - /* With single only options, last one takes - * precedence */ - if (rao->type == ND_OPT_MTU) { - new = strchr(**var, '='); - if (new == NULL) { - syslog(LOG_ERR, "new is null"); - continue; - } else - new++; - len = (new - **var) + - strlen(rao->option) + 1; - if (len > strlen(**var)) - new = realloc(**var, len); - else - new = **var; - if (new) { - **var = new; - new = strchr(**var, '='); - if (new) - strcpy(new + 1, - rao->option); - else - syslog(LOG_ERR, - "new is null"); - } - continue; - } - new = realloc(**var, - strlen(**var) + 1 + - strlen(rao->option) + 1); - if (new == NULL) - return -1; - **var = new; - new += strlen(new); - *new++ = ' '; - strcpy(new, rao->option); - continue; - } - if (env) { - snprintf(buffer, sizeof(buffer), - "ra%d_%s", i, optn); - if (setvar(&env, prefix, buffer, rao->option) - == -1) - return -1; - } - } - } - - if (env) { - if (setvard(&env, prefix, "ra_count", i) == -1) - return -1; - } - l++; - return l; -} - -const struct ipv6_addr * -ipv6rs_findprefix(const struct ipv6_addr *a) -{ - const struct ra *rap; - const struct ipv6_addr *ap; - const struct in6_addr *p1, *p2; - int bytelen, bitlen; - - p1 = &a->addr; - TAILQ_FOREACH(rap, &ipv6_routers, next) { - TAILQ_FOREACH(ap, &rap->addrs, next) { - p2 = & ap->prefix; - bytelen = ap->prefix_len / NBBY; - bitlen = ap->prefix_len % NBBY; - if (memcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) - continue; - if (bitlen != 0) { - bitlen = NBBY - bitlen; - if (p1->s6_addr[bytelen] >> bitlen != - p2->s6_addr[bytelen] >> bitlen) - continue; - } - return ap; - } - } - return NULL; -} - -void -ipv6rs_handleifa(int cmd, const char *ifname, - const struct in6_addr *addr, int flags) -{ - struct ra *rap; - - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (strcmp(rap->iface->name, ifname)) - continue; - ipv6_handleifa_addrs(cmd, &rap->addrs, addr, flags); - } -} - -void -ipv6rs_expire(void *arg) -{ - struct interface *ifp; - struct ra *rap, *ran; - struct ra_opt *rao, *raon; - struct timeval now, lt, expire, next; - int expired, valid; - - ifp = arg; - get_monotonic(&now); - expired = 0; - timerclear(&next); - - TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) { - if (rap->iface != ifp) - continue; - lt.tv_sec = rap->lifetime; - lt.tv_usec = 0; - timeradd(&rap->received, <, &expire); - if (rap->lifetime == 0 || timercmp(&now, &expire, >)) { - valid = 0; - if (!rap->expired) { - syslog(LOG_WARNING, - "%s: %s: router expired", - ifp->name, rap->sfrom); - rap->expired = expired = 1; - ipv6ns_cancelproberouter(rap); - } - } else { - valid = 1; - timersub(&expire, &now, <); - if (!timerisset(&next) || timercmp(&next, <, >)) - next = lt; - } - - /* Addresses are expired in ipv6ns_probeaddrs - * so that DHCPv6 addresses can be removed also. */ - - TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) { - if (rap->expired) { - switch(rao->type) { - case ND_OPT_RDNSS: /* FALLTHROUGH */ - case ND_OPT_DNSSL: - /* RFC6018 end of section 5.2 states - * that if tha RA has a lifetime of 0 - * then we should expire these - * options */ - TAILQ_REMOVE(&rap->options, rao, next); - expired = 1; - free(rao->option); - free(rao); - continue; - } - } - if (!timerisset(&rao->expire)) - continue; - if (timercmp(&now, &rao->expire, >)) { - /* Expired prefixes are logged above */ - if (rao->type != ND_OPT_PREFIX_INFORMATION) - syslog(LOG_WARNING, - "%s: %s: expired option %d", - ifp->name, rap->sfrom, rao->type); - TAILQ_REMOVE(&rap->options, rao, next); - expired = 1; - free(rao->option); - free(rao); - continue; - } - valid = 1; - timersub(&rao->expire, &now, <); - if (!timerisset(&next) || timercmp(&next, <, >)) - next = lt; - } - - /* No valid lifetimes are left on the RA, so we might - * as well punt it. */ - if (!valid && TAILQ_FIRST(&rap->addrs) == NULL) - ipv6rs_free_ra(rap); - } - - if (timerisset(&next)) - eloop_timeout_add_tv(&next, ipv6rs_expire, ifp); - if (expired) { - ipv6_buildroutes(); - script_runreason(ifp, "ROUTERADVERT"); - } -} - -int -ipv6rs_start(struct interface *ifp) -{ - struct rs_state *state; - - syslog(LOG_INFO, "%s: soliciting an IPv6 router", ifp->name); - if (sock == -1) { - if (ipv6rs_open() == -1) { - syslog(LOG_ERR, "%s: ipv6rs_open: %m", __func__); - return -1; - } - eloop_event_add(sock, ipv6rs_handledata, NULL); - } - - eloop_timeout_delete(NULL, ifp); - - state = RS_STATE(ifp); - if (state == NULL) { - ifp->if_data[IF_DATA_IPV6RS] = calloc(1, sizeof(*state)); - state = RS_STATE(ifp); - if (state == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - return -1; - } - } - - /* Always make a new probe as the underlying hardware - * address could have changed. */ - ipv6rs_makeprobe(ifp); - if (state->rs == NULL) { - syslog(LOG_ERR, "%s: ipv6rs_makeprobe: %m", __func__); - return -1; - } - - state->rsprobes = 0; - ipv6rs_sendprobe(ifp); - return 0; -} - -void -ipv6rs_drop(struct interface *ifp) -{ - struct ra *rap; - int expired = 0; - TAILQ_HEAD(rahead, ra) rtrs; - - eloop_timeout_delete(NULL, ifp); - TAILQ_INIT(&rtrs); - TAILQ_FOREACH(rap, &ipv6_routers, next) { - if (rap->iface == ifp) { - rap->expired = expired = 1; - TAILQ_REMOVE(&ipv6_routers, rap, next); - TAILQ_INSERT_TAIL(&rtrs, rap, next); - } - } - if (expired) { - while ((rap = TAILQ_FIRST(&rtrs))) { - TAILQ_REMOVE(&rtrs, rap, next); - ipv6rs_drop_ra(rap); - } - ipv6_buildroutes(); - script_runreason(ifp, "ROUTERADVERT"); - } -} diff --git a/external/bsd/dhcpcd/dist/ipv6rs.h b/external/bsd/dhcpcd/dist/ipv6rs.h deleted file mode 100644 index 15077d487ed5..000000000000 --- a/external/bsd/dhcpcd/dist/ipv6rs.h +++ /dev/null @@ -1,100 +0,0 @@ -/* $NetBSD: ipv6rs.h,v 1.1.1.5 2013/06/21 19:33:08 roy Exp $ */ - -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2013 Roy Marples - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef IPV6RS_H -#define IPV6RS_H - -#include - -#include - -#include "dhcpcd.h" -#include "ipv6.h" - -struct ra_opt { - TAILQ_ENTRY(ra_opt) next; - uint8_t type; - struct timeval expire; - char *option; -}; - -struct ra { - TAILQ_ENTRY(ra) next; - struct interface *iface; - struct in6_addr from; - char sfrom[INET6_ADDRSTRLEN]; - unsigned char *data; - ssize_t data_len; - struct timeval received; - unsigned char flags; - uint32_t lifetime; - uint32_t reachable; - uint32_t retrans; - uint32_t mtu; - struct ipv6_addrhead addrs; - TAILQ_HEAD(, ra_opt) options; - - unsigned char *ns; - size_t nslen; - int nsprobes; - - int expired; -}; - -extern TAILQ_HEAD(rahead, ra) ipv6_routers; - -struct rs_state { - unsigned char *rs; - size_t rslen; - int rsprobes; -}; - -#define RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6RS]) - -#ifdef INET6 -int ipv6rs_start(struct interface *); -ssize_t ipv6rs_env(char **, const char *, const struct interface *); -const struct ipv6_addr * ipv6rs_findprefix(const struct ipv6_addr *); -int ipv6rs_addrexists(const struct ipv6_addr *); -void ipv6rs_freedrop_ra(struct ra *, int); -#define ipv6rs_free_ra(ra) ipv6rs_freedrop_ra((ra), 0) -#define ipv6rs_drop_ra(ra) ipv6rs_freedrop_ra((ra), 1) -ssize_t ipv6rs_free(struct interface *); -void ipv6rs_expire(void *arg); -int ipv6rs_has_ra(const struct interface *); -void ipv6rs_handleifa(int, const char *, const struct in6_addr *, int); -void ipv6rs_drop(struct interface *); -#else -#define ipv6rs_start(a) {} -#define ipv6rs_free(a) -#define ipv6rs_has_ra(a) 0 -#define ipv6rs_drop(a) -#endif - -#endif diff --git a/external/bsd/dhcpcd/dist/net.c b/external/bsd/dhcpcd/dist/net.c index 4f212be00d32..0db72cbcec10 100644 --- a/external/bsd/dhcpcd/dist/net.c +++ b/external/bsd/dhcpcd/dist/net.c @@ -1,5 +1,5 @@ #include - __RCSID("$NetBSD: net.c,v 1.2 2013/09/11 18:50:00 drochner Exp $"); + __RCSID("$NetBSD: net.c,v 1.3 2013/09/20 10:56:32 roy Exp $"); /* * dhcpcd - DHCP client daemon @@ -70,10 +70,12 @@ #include "config.h" #include "common.h" +#include "dev.h" #include "dhcp.h" #include "dhcp6.h" #include "if-options.h" -#include "ipv6rs.h" +#include "ipv4.h" +#include "ipv6nd.h" #include "net.h" int socket_afnet = -1; @@ -141,10 +143,11 @@ free_interface(struct interface *ifp) if (ifp == NULL) return; + ipv4_free(ifp); dhcp_free(ifp); ipv6_free(ifp); dhcp6_free(ifp); - ipv6rs_free(ifp); + ipv6nd_free(ifp); free_options(ifp->options); free(ifp); } @@ -226,6 +229,10 @@ discover_interfaces(int argc, char * const *argv) #ifdef __linux__ char ifn[IF_NAMESIZE]; #endif +#ifdef INET + const struct sockaddr_in *addr; + const struct sockaddr_in *net; + const struct sockaddr_in *dst; #ifdef INET6 const struct sockaddr_in6 *sin6; int ifa_flags; @@ -264,6 +271,10 @@ discover_interfaces(int argc, char * const *argv) #endif } + /* Ensure that the interface name has settled */ + if (!dev_initialized(ifa->ifa_name)) + continue; + /* It's possible for an interface to have >1 AF_LINK. * For our purposes, we use the first one. */ TAILQ_FOREACH(ifp, ifs, next) { @@ -444,11 +455,29 @@ discover_interfaces(int argc, char * const *argv) TAILQ_INSERT_TAIL(ifs, ifp, next); } -#ifdef INET6 for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr != NULL && - ifa->ifa_addr->sa_family == AF_INET6) - { + if (ifa->ifa_addr == NULL) + continue; + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + addr = (const struct sockaddr_in *) + (void *)ifa->ifa_addr; + net = (const struct sockaddr_in *) + (void *)ifa->ifa_netmask; + if (ifa->ifa_flags & IFF_POINTOPOINT) + dst = (const struct sockaddr_in *) + (void *)ifa->ifa_dstaddr; + else + dst = NULL; + ipv4_handleifa(RTM_NEWADDR, ifs, ifa->ifa_name, + &addr->sin_addr, + &net->sin_addr, + dst ? &dst->sin_addr : NULL); + break; +#endif +#ifdef INET6 + case AF_INET6: sin6 = (const struct sockaddr_in6 *) (void *)ifa->ifa_addr; ifa_flags = in6_addr_flags(ifa->ifa_name, @@ -457,6 +486,8 @@ discover_interfaces(int argc, char * const *argv) ipv6_handleifa(RTM_NEWADDR, ifs, ifa->ifa_name, &sin6->sin6_addr, ifa_flags); + break; +#endif } } #endif