]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Improve IPv6 RA support by allowing dhcpcd to manage the address and routes v5.6.0
authorRoy Marples <roy@marples.name>
Thu, 5 Jul 2012 16:37:41 +0000 (16:37 +0000)
committerRoy Marples <roy@marples.name>
Thu, 5 Jul 2012 16:37:41 +0000 (16:37 +0000)
instead of the kernel. dhcpcd will only do this if RA is disabled in the kernel
or dhcpcd has been instructed to do this via dhcpcd.conf(5) ipv6ra_own and
ipv6ra_own_default directives.

Send and process IPv6 Neighbor Solicitions and Adverts to prove router
reachability. If a router cannot be reached in this way then it is expired.

When debugging, all ND messages are displayed which will create a lot of log
spam.

To ease packaging, ./configure now accepts LDFLAGS and --enable-static.

22 files changed:
Makefile
configure
configure.c
defs.h
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
dhcpcd.h
if-bsd.c
if-linux.c
if-options.c
if-options.h
ipv6.c [new file with mode: 0644]
ipv6.h [new file with mode: 0644]
ipv6ns.c [new file with mode: 0644]
ipv6ns.h [new file with mode: 0644]
ipv6rs.c
ipv6rs.h
net.c
net.h
platform-bsd.c
platform-linux.c

index 3b4dbfd689b0fe0fba60909fa37b2f19c935a013..b5a7be7b2d457d94ae1034c1aaef380772e4be12 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,8 @@
 
 PROG=          dhcpcd
 SRCS=          arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
-SRCS+=         if-options.c if-pref.c ipv4ll.c ipv6rs.c net.c signals.c
-SRCS+=         configure.c
+SRCS+=         configure.c if-options.c if-pref.c ipv4ll.c net.c signals.c
+SRCS+=         ipv6.c ipv6rs.c ipv6ns.c
 
 CFLAGS?=       -O2
 CSTD?=         c99
index b3344970ebbf02c2cb6492d6e3e3a63bb386b889..7e3bfafe429bdbacaf43e5b00ac07bff6076f38e 100755 (executable)
--- a/configure
+++ b/configure
@@ -13,6 +13,7 @@ HOST=
 TARGET=
 DEBUG=
 FORK=
+STATIC=
 INCLUDEDIR=
 
 for x do
@@ -27,6 +28,8 @@ for x do
        --fork) FORK=$var;;
        --disable-fork) FORK=no;;
        --enable-fork) FORK=yes;;
+       --disable-static) STATIC=no;;
+       --enable-static) STATIC=yes;;
        --prefix) prefix=$var;;
        --sysconfdir) SYSCONFDIR=$var;;
        --bindir|--sbindir) SBINDIR=$var;;
@@ -194,6 +197,12 @@ fi
 if [ -n "$CPPLAGS" ]; then
        echo "CPPLAGS=          $CPPLAGS" >>$CONFIG_MK
 fi
+if [ -n "$LDFLAGS" ]; then
+       echo "LDFLAGS=          $LDFLAGS" >>$CONFIG_MK
+fi
+if [ "$STATIC" = yes ]; then
+       echo "LDFLAGS+= -static" >>$CONFIG_MK
+fi
 for x in $INCLUDEDIR; do
        echo "CPPFLAGS+=        -I$x" >>$CONFIG_MK
 done
@@ -370,6 +379,34 @@ if [ "$STRLCPY" = no ]; then
        echo "#include \"compat/strlcpy.h\"" >>$CONFIG_H
 fi
 
+if [ -z "$TAILQ_FOREACH_SAFE" ]; then
+       printf "Testing for TAILQ_FOREACH_SAFE ... "
+       cat <<EOF >_queue.c
+#include <sys/queue.h>
+int main(void) {
+#ifndef TAILQ_FOREACH_SAFE
+#error TAILQ_FOREACH_SAFE
+#endif
+       return 0;
+}
+EOF
+       if $XCC _queue.c -o _queue 2>/dev/null; then
+               TAILQ_FOREACH_SAFE=yes
+       else
+               TAILQ_FOREACH_SAFE=no
+       fi
+       echo "$TAILQ_FOREACH_SAFE"
+       rm -f _queue.c _queue
+fi
+if [ "$TAILQ_FOREACH_SAFE" = no ]; then
+       cat <<EOF >>$CONFIG_H
+#define        TAILQ_FOREACH_SAFE(var, head, field, next)                      \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var) && ((next) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (next))
+EOF
+fi
+
 if [ -z "$SERVICECMD" ]; then
        printf "Checking for OpenRC ... "
        if [ -x /sbin/rc-service ]; then
index a2d262f51f656d9553a2121b679b832ceb080f29..15747fdd1d7f8e6cfd54a8217d1b9589ef1ddaaf 100644 (file)
 
 #define DEFAULT_PATH   "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
 
-/* Some systems have route metrics */
-#ifndef HAVE_ROUTE_METRIC
-# ifdef __linux__
-#  define HAVE_ROUTE_METRIC 1
-# endif
-# ifndef HAVE_ROUTE_METRIC
-#  define HAVE_ROUTE_METRIC 0
-# endif
-#endif
-
 static struct rt *routes;
 
 static int
@@ -173,7 +163,7 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
 
        dhcp = ra = 0;
        if (strcmp(reason, "TEST") == 0) {
-               if (iface->ras)
+               if (ipv6rs_has_ra(iface))
                        ra = 1;
                else
                        dhcp = 1;
@@ -226,7 +216,7 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
                e--;
        }
        *--p = '\0';
-       if ((dhcp && iface->state->new) || (ra && iface->ras)) {
+       if ((dhcp && iface->state->new) || (ra && ipv6rs_has_ra(iface))) {
                env[8] = strdup("if_up=true");
                env[9] = strdup("if_down=false");
        } else {
@@ -333,7 +323,7 @@ send_interface(int fd, const struct interface *iface)
        int retval = 0;
        if (send_interface1(fd, iface, iface->state->reason) == -1)
                retval = -1;
-       if (iface->ras) {
+       if (ipv6rs_has_ra(iface)) {
                if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
                        retval = -1;
        }
diff --git a/defs.h b/defs.h
index b4f238097876b96635df788c8a078c61e1891a20..10de82d3da560a49f4c7781ed438c3fbae49b4a1 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "5.5.6"
+#define VERSION                        "5.6.0"
 
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
index fc3c04c685b00d6ce2020e868ef7d39aeda7b0c2..d41f6245e5b135eb14b283dfd25d020e9052a380 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 19, 2012
+.Dd June 7, 2012
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -102,8 +102,18 @@ is also an implementation of the BOOTP client specified in
 .Pp
 .Nm
 is also an implementation of an IPv6 Router Solicitor as specified in
-.Li RFC 6106
-with regard to the RDNSS and DNSSL options.
+.Li RFC 4861
+and
+.Li RFC 6106 .
+.Nm
+can optionally handle address and route management itself,
+and will do so by default if Router Solicitation is disabled in the kernel.
+If
+.Nm
+is managing routes,
+.Nm
+sends Neighbor Solicitions to each advertising router periodically and will
+expire the ones that do not respond.
 .Ss Local Link configuration
 If
 .Nm
@@ -274,8 +284,12 @@ Override the
 .Ar vendorclassid
 field sent.
 The default is
-dhcpcd <version>.
+dhcpcd-<version>:<os>:<machine>:<platform>.
+For example
+.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
 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
 This causes an existing
 .Nm
@@ -578,7 +592,8 @@ running on the
 .Xr resolvconf 8
 .Sh STANDARDS
 RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
-RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 5969, RFC 6106.
+RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861, RFC 5969,
+RFC 6106.
 .Sh AUTHORS
 .An Roy Marples Aq roy@marples.name
 .Sh BUGS
index b6595fead264266b2dea01fa27d0d17a4fc83afe..4b318caca80bf3cae9ca9e18c30584983cfe77c4 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -67,6 +67,8 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #include "if-options.h"
 #include "if-pref.h"
 #include "ipv4ll.h"
+#include "ipv6.h"
+#include "ipv6ns.h"
 #include "ipv6rs.h"
 #include "net.h"
 #include "platform.h"
@@ -80,7 +82,6 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #define RELEASE_DELAY_S                0
 #define RELEASE_DELAY_NS       10000000
 
-unsigned long long options = 0;
 int pidfd = -1;
 struct interface *ifaces = NULL;
 int ifac = 0;
@@ -95,7 +96,7 @@ static char **ifv;
 static int ifc;
 static char *cffile;
 static char *pidfile;
-static int linkfd = -1, ipv6rsfd = -1;
+static int linkfd = -1, ipv6rsfd = -1, ipv6nsfd = -1;
 
 struct dhcp_op {
        uint8_t value;
@@ -254,15 +255,8 @@ stop_interface(struct interface *iface)
        struct interface *ifp, *ifl = NULL;
 
        syslog(LOG_INFO, "%s: removing interface", iface->name);
-       if (iface->ras) {
-               ipv6rs_free(iface);
-               iface->ras = NULL;
-               run_script_reason(iface, "ROUTERADVERT");
-       }
-       if (strcmp(iface->state->reason, "RELEASE") != 0)
-               drop_dhcp(iface, "STOP");
-       close_sockets(iface);
-       delete_timeout(NULL, iface);
+
+       // Remove the interface from our list
        for (ifp = ifaces; ifp; ifp = ifp->next) {
                if (ifp == iface)
                        break;
@@ -272,6 +266,12 @@ stop_interface(struct interface *iface)
                ifl->next = ifp->next;
        else
                ifaces = ifp->next;
+
+       ipv6rs_drop(iface);
+       if (strcmp(iface->state->reason, "RELEASE") != 0)
+               drop_dhcp(iface, "STOP");
+       close_sockets(iface);
+       delete_timeout(NULL, iface);
        free_interface(ifp);
        if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
                exit(EXIT_FAILURE);
@@ -922,11 +922,7 @@ handle_carrier(int action, int flags, const char *ifname)
                        syslog(LOG_INFO, "%s: carrier lost", iface->name);
                        close_sockets(iface);
                        delete_timeouts(iface, start_expire, NULL);
-                       if (iface->ras) {
-                               ipv6rs_free(iface);
-                               iface->ras = NULL;
-                               run_script_reason(iface, "ROUTERADVERT");
-                       }
+                       ipv6rs_drop(iface);
                        drop_dhcp(iface, "NOCARRIER");
                }
        } else if (carrier == 1 && !(~iface->flags & IFF_UP)) {
@@ -1616,7 +1612,7 @@ handle_args(struct fd_list *fd, int argc, char **argv)
                        if (argc == 1) {
                                for (ifp = ifaces; ifp; ifp = ifp->next) {
                                        len++;
-                                       if (ifp->ras)
+                                       if (ipv6rs_has_ra(ifp))
                                                len++;
                                }
                                len = write(fd->fd, &len, sizeof(len));
@@ -1631,7 +1627,7 @@ handle_args(struct fd_list *fd, int argc, char **argv)
                                for (ifp = ifaces; ifp; ifp = ifp->next)
                                        if (strcmp(argv[opt], ifp->name) == 0) {
                                                len++;
-                                               if (ifp->ras)
+                                               if (ipv6rs_has_ra(ifp))
                                                        len++;
                                        }
                        }
@@ -1914,7 +1910,7 @@ main(int argc, char **argv)
                        syslog(LOG_INFO, "sending signal %d to pid %d",
                            sig, pid);
                if (pid == 0 || kill(pid, sig) != 0) {
-                       if (sig != SIGALRM)
+                       if (sig != SIGALRM && errno != EPERM)
                                syslog(LOG_ERR, ""PACKAGE" not running");
                        if (pid != 0 && errno != ESRCH) {
                                syslog(LOG_ERR, "kill: %m");
@@ -2006,6 +2002,10 @@ main(int argc, char **argv)
 
        if (options & DHCPCD_IPV6RS && !check_ipv6(NULL))
                options &= ~DHCPCD_IPV6RS;
+       if (options & DHCPCD_IPV6RS && ipv6_open() == -1) {
+               options &= ~DHCPCD_IPV6RS;
+               syslog(LOG_ERR, "ipv6_open: %m");
+       }
        if (options & DHCPCD_IPV6RS) {
                ipv6rsfd = ipv6rs_open();
                if (ipv6rsfd == -1) {
@@ -2015,6 +2015,15 @@ main(int argc, char **argv)
                        add_event(ipv6rsfd, ipv6rs_handledata, NULL);
 //                     atexit(restore_rtadv);
                }
+               if (options & DHCPCD_IPV6RA_OWN ||
+                   options & DHCPCD_IPV6RA_OWN_DEFAULT)
+               {
+                       ipv6nsfd = ipv6ns_open();
+                       if (ipv6nsfd == -1)
+                               syslog(LOG_ERR, "ipv6nd: %m");
+                       else
+                               add_event(ipv6nsfd, ipv6ns_handledata, NULL);
+               }
        }
 
        ifc = argc - optind;
index 9f0ef27bfcd6cbc32e0b397adfa629c99d7f622f..a1decb747223ee291208f7a0eb42eef39297ca5e 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 19, 2012
+.Dd May 21, 2012
 .Dt DHCPCD.CONF 5 SMM
 .Os
 .Sh NAME
@@ -154,6 +154,18 @@ RDNSS option.
 Set this option so to make
 .Nm dhcpcd
 always fork on an RA.
+.It ic ipv6ra_own
+Disables kernel IPv6 Router Advertisment processing so dhcpcd can manage
+addresses and routes.
+.It ic ipv6ra_own_default
+Each time dhcpcd receives an IPv6 Router Adveristment, dhcpcd will manage
+the default route only.
+This allows dhcpcd to prefer an interface for outbound traffic based on metric
+and/or user selection rather than the kernel.
+.It ic ipv6rs
+Enables IPv6 Router Advertisment solicitation.
+This is on by default, but is documented here in the case where it is disabled
+globally but needs to be enabled for one interface.
 .It Ic leasetime Ar seconds
 Request a leasetime of
 .Ar seconds .
@@ -289,8 +301,13 @@ Set the vendor option 03 with an IP address as a string.
 Set un-encapsulated vendor option to hello world.
 .D1 vendor ,"hello world"
 .It Ic vendorclassid Ar string
-Change the default vendorclassid sent from dhcpcd-version.
+The default is
+dhcpcd-<version>:<os>:<machine>:<platform>.
+For example
+.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
 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
 Wait for an address to be assigned before forking to the background.
 .It Ic xidhwaddr
index 642aed060110b6c918b4e04ad805b64a08853e77..fbee398893c93fc6b972a1e523acd3e3cdab1142 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -30,7 +30,7 @@
 
 #include <sys/socket.h>
 #include <net/if.h>
-#include <netinet/in.h>
+//#include <netinet/in.h>
 
 #include <limits.h>
 
@@ -82,30 +82,6 @@ struct if_state {
        size_t arping_index;
 };
 
-struct ra_opt {
-       uint8_t type;
-       struct timeval expire;
-       char *option;
-       struct ra_opt *next;
-};
-
-struct ra {
-       struct in6_addr from;
-       char sfrom[INET6_ADDRSTRLEN];
-       unsigned char *data;
-       ssize_t data_len;
-       struct timeval received;
-       uint32_t lifetime;
-       struct in6_addr prefix;
-       int prefix_len;
-       uint32_t prefix_vltime;
-       uint32_t prefix_pltime;
-       char sprefix[INET6_ADDRSTRLEN];
-       struct ra_opt *options;
-       int expired;
-       struct ra *next;
-};
-
 struct interface {
        char name[IF_NAMESIZE];
        struct if_state *state;
@@ -138,13 +114,11 @@ struct interface {
        unsigned char *rs;
        size_t rslen;
        int rsprobes;
-       struct ra *ras;
 
        struct interface *next;
 };
 
 extern int pidfd;
-extern unsigned long long options;
 extern int ifac;
 extern char **ifav;
 extern int ifdc;
index c3ffcace8df812fb4daa426747d826809d7e7c07..c4765385f81672bbbce2f0a6abf79438bfa483c4 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
 #include <arpa/inet.h>
 #include <net/if.h>
 #include <net/if_dl.h>
+#ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */
+#  include <net/if_var.h>
+#endif
 #include <net/route.h>
 #include <netinet/in.h>
+#include <netinet6/in6_var.h>
 #ifdef __DragonFly__
 #  include <netproto/802_11/ieee80211_ioctl.h>
 #elif __APPLE__
@@ -59,6 +63,7 @@
 #include "configure.h"
 #include "dhcp.h"
 #include "if-options.h"
+#include "ipv6.h"
 #include "net.h"
 
 #ifndef RT_ROUNDUP
@@ -154,7 +159,6 @@ if_address(const struct interface *iface, const struct in_addr *address,
     const struct in_addr *netmask, const struct in_addr *broadcast,
     int action)
 {
-       int retval;
        struct ifaliasreq ifa;
        union {
                struct sockaddr *sa;
@@ -178,23 +182,16 @@ if_address(const struct interface *iface, const struct in_addr *address,
        }
 #undef ADDADDR
 
-       if (action < 0)
-               retval = ioctl(socket_afnet, SIOCDIFADDR, &ifa);
-       else
-               retval = ioctl(socket_afnet, SIOCAIFADDR, &ifa);
-       return retval;
+       return ioctl(socket_afnet,
+           action < 0 ? SIOCDIFADDR : SIOCAIFADDR, &ifa);
 }
 
-/* ARGSUSED4 */
 int
 if_route(const struct rt *rt, int action)
 {
        union sockunion {
                struct sockaddr sa;
                struct sockaddr_in sin;
-#ifdef INET6
-               struct sockaddr_in6 sin6;
-#endif
                struct sockaddr_dl sdl;
                struct sockaddr_storage ss;
        } su;
@@ -207,17 +204,17 @@ if_route(const struct rt *rt, int action)
        size_t l;
        int retval = 0;
 
-#define ADDSU(_su) {                                                         \
-               l = RT_ROUNDUP(_su.sa.sa_len);                                \
-               memcpy(bp, &(_su), l);                                        \
+#define ADDSU {                                                                      \
+               l = RT_ROUNDUP(su.sa.sa_len);                                 \
+               memcpy(bp, &su, l);                                           \
                bp += l;                                                      \
        }
-#define ADDADDR(_a) {                                                        \
-               memset (&su, 0, sizeof(su));                                  \
+#define ADDADDR(addr) {                                                              \
+               memset(&su, 0, sizeof(su));                                   \
                su.sin.sin_family = AF_INET;                                  \
                su.sin.sin_len = sizeof(su.sin);                              \
-               memcpy (&su.sin.sin_addr, _a, sizeof(su.sin.sin_addr));       \
-               ADDSU(su);                                                    \
+               (&su.sin)->sin_addr = *addr;                                  \
+               ADDSU;                                                        \
        }
 
        memset(&rtm, 0, sizeof(rtm));
@@ -255,16 +252,154 @@ if_route(const struct rt *rt, int action)
                memset(&su, 0, sizeof(su));
                su.sdl.sdl_len = sizeof(struct sockaddr_dl);
                link_addr(rt->iface->name, &su.sdl);
-               ADDSU(su);
+               ADDSU;
        } else
                ADDADDR(&rt->gate);
 
        if (rtm.hdr.rtm_addrs & RTA_NETMASK)
                ADDADDR(&rt->net);
 
+       /* IFP here if we need it */
+
        if (rtm.hdr.rtm_addrs & RTA_IFA)
                ADDADDR(&rt->iface->addr);
 
+#undef ADDADDR
+#undef ADDSU
+
+       rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+       if (write(r_fd, &rtm, l) == -1)
+               retval = -1;
+       return retval;
+}
+
+int
+if_address6(const struct interface *ifp, const struct ipv6_addr *a, int action)
+{
+       struct in6_aliasreq ifa;
+       struct in6_addr mask;
+
+       memset(&ifa, 0, sizeof(ifa));
+       strlcpy(ifa.ifra_name, ifp->name, sizeof(ifa.ifra_name));
+
+#define ADDADDR(v, addr) {                                                   \
+               (v)->sin6_family = AF_INET6;                                  \
+               (v)->sin6_len = sizeof(*v);                                   \
+               (v)->sin6_addr = *addr;                                       \
+       }
+
+       ADDADDR(&ifa.ifra_addr, &a->addr);
+       ipv6_mask(&mask, a->prefix_len);
+       ADDADDR(&ifa.ifra_prefixmask, &mask);
+       ifa.ifra_lifetime.ia6t_vltime = a->prefix_vltime;
+       ifa.ifra_lifetime.ia6t_pltime = a->prefix_pltime;
+#undef ADDADDR
+
+       return ioctl(socket_afnet6,
+           action < 0 ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
+}
+
+int
+if_route6(const struct rt6 *rt, int action)
+{
+       union sockunion {
+               struct sockaddr sa;
+               struct sockaddr_in6 sin;
+               struct sockaddr_dl sdl;
+               struct sockaddr_storage ss;
+       } su;
+       struct rtm 
+       {
+               struct rt_msghdr hdr;
+               char buffer[sizeof(su) * 4];
+       } rtm;
+       char *bp = rtm.buffer;
+       size_t l;
+       int retval = 0;
+
+/* KAME based systems want to store the scope inside the sin6_addr
+ * for link local addreses */
+#ifdef __KAME__
+#define SCOPE {                                                                      \
+               if (IN6_IS_ADDR_LINKLOCAL(&su.sin.sin6_addr)) {               \
+                       *(uint16_t *)(void *)&su.sin.sin6_addr.s6_addr[2] =   \
+                           htons(su.sin.sin6_scope_id);                      \
+                       su.sin.sin6_scope_id = 0;                             \
+               }                                                             \
+       }
+#else
+#define SCOPE
+#endif
+
+#define ADDSU {                                                                      \
+               l = RT_ROUNDUP(su.sa.sa_len);                                 \
+               memcpy(bp, &su, l);                                           \
+               bp += l;                                                      \
+       }
+#define ADDADDRS(addr, scope) {                                                      \
+               memset(&su, 0, sizeof(su));                                   \
+               su.sin.sin6_family = AF_INET6;                                \
+               su.sin.sin6_len = sizeof(su.sin);                             \
+               (&su.sin)->sin6_addr = *addr;                                 \
+               su.sin.sin6_scope_id = scope;                                 \
+               SCOPE;                                                        \
+               ADDSU;                                                        \
+       }
+#define ADDADDR(addr) ADDADDRS(addr, 0)
+
+       memset(&rtm, 0, sizeof(rtm));
+       rtm.hdr.rtm_version = RTM_VERSION;
+       rtm.hdr.rtm_seq = 1;
+       if (action == 0)
+               rtm.hdr.rtm_type = RTM_CHANGE;
+       else if (action > 0)
+               rtm.hdr.rtm_type = RTM_ADD;
+       else
+               rtm.hdr.rtm_type = RTM_DELETE;
+
+       rtm.hdr.rtm_flags = RTF_UP;
+       /* None interface subnet routes are static. */
+       if (IN6_IS_ADDR_UNSPECIFIED(&rt->dest) &&
+           IN6_IS_ADDR_UNSPECIFIED(&rt->net))
+               rtm.hdr.rtm_flags |= RTF_GATEWAY;
+       else
+               rtm.hdr.rtm_flags |= RTF_CLONING;
+
+       rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+//     if (action >= 0)
+//             rtm.hdr.rtm_addrs |= RTA_IFA;
+
+       ADDADDR(&rt->dest);
+       if (rtm.hdr.rtm_flags & (RTF_HOST | RTF_CLONING)) {
+               /* Make us a link layer socket for the host gateway */
+               memset(&su, 0, sizeof(su));
+               su.sdl.sdl_len = sizeof(struct sockaddr_dl);
+               link_addr(rt->iface->name, &su.sdl);
+               ADDSU;
+       } else
+               ADDADDRS(&rt->gate, rt->iface->index);
+
+       if (rtm.hdr.rtm_addrs & RTA_NETMASK) {
+               if (rtm.hdr.rtm_flags & RTF_GATEWAY) {
+                       memset(&su, 0, sizeof(su));
+                       su.sin.sin6_family = AF_INET6;
+                       ADDSU;
+               } else
+                       ADDADDR(&rt->net);
+       }
+
+       /* IFP here if we need it */
+       /* IFA here if we need it */
+
+#undef ADDADDR
+#undef ADDSU
+#undef SCOPE
+
+       if (action >= 0 && rt->mtu) {
+               rtm.hdr.rtm_inits |= RTV_MTU;
+               rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
+       }
+
        rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
        if (write(r_fd, &rtm, l) == -1)
                retval = -1;
index 08acaef6fd40af79f4d602f26b007e5217e2c862..46b726cfe6c67828fda586ca1e480a0fa52ef2ac 100644 (file)
@@ -59,6 +59,7 @@
 #include "common.h"
 #include "configure.h"
 #include "dhcp.h"
+#include "ipv6.h"
 #include "net.h"
 
 static int sock_fd;
@@ -583,3 +584,85 @@ if_route(const struct rt *rt, int action)
        free(nlm);
        return retval;
 }
+
+int
+if_address6(const struct interface *ifp, const struct ipv6_addr *ap, int action)
+{
+       struct nlma *nlm;
+       int retval = 0;
+
+       nlm = xzalloc(sizeof(*nlm));
+       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+       if (action >= 0) {
+               nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+               nlm->hdr.nlmsg_type = RTM_NEWADDR;
+       } else
+               nlm->hdr.nlmsg_type = RTM_DELADDR;
+       nlm->ifa.ifa_index = ifp->index;
+       nlm->ifa.ifa_family = AF_INET6;
+       nlm->ifa.ifa_prefixlen = ap->prefix_len;
+       /* This creates the aliased interface */
+       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
+           ifp->name, strlen(ifp->name) + 1);
+       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
+           &ap->addr.s6_addr, sizeof(ap->addr.s6_addr));
+
+       if (send_netlink(&nlm->hdr) == -1)
+               retval = -1;
+       free(nlm);
+       return retval;
+}
+
+int
+if_route6(const struct rt6 *rt, int action)
+{
+       struct nlmr *nlm;
+       int retval = 0;
+
+       nlm = xzalloc(sizeof(*nlm));
+       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       nlm->hdr.nlmsg_type = RTM_NEWROUTE;
+       nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+       if (action == 0)
+               nlm->hdr.nlmsg_flags |= NLM_F_REPLACE;
+       else if (action == 1)
+               nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+       else
+               nlm->hdr.nlmsg_type = RTM_DELROUTE;
+       nlm->rt.rtm_family = AF_INET6;
+       nlm->rt.rtm_table = RT_TABLE_MAIN;
+
+       if (action == -1 || action == -2)
+               nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+       else {
+               nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+               /* None interface subnet routes are static. */
+               if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
+                       nlm->rt.rtm_protocol = RTPROT_KERNEL;
+                       nlm->rt.rtm_scope = RT_SCOPE_LINK;
+               } else
+                       nlm->rt.rtm_protocol = RTPROT_BOOT;
+               nlm->rt.rtm_type = RTN_UNICAST;
+       }
+
+       nlm->rt.rtm_dst_len = ipv6_prefixlen(&rt->net);
+       add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
+           &rt->dest.s6_addr, sizeof(rt->dest.s6_addr));
+
+       /* If destination == gateway then don't add the gateway */
+       if (!IN6_IS_ADDR_UNSPECIFIED(&rt->gate) &&
+           !IN6_ARE_ADDR_EQUAL(&rt->dest, &rt->gate))
+               add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+                   &rt->gate.s6_addr, sizeof(rt->gate.s6_addr));
+
+       add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, rt->iface->index);
+       add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, rt->metric);
+
+       if (send_netlink(&nlm->hdr) == -1)
+               retval = -1;
+       free(nlm);
+       return retval;
+       errno = ENOTSUP;
+       return -1;
+}
index 864dc85aa3cc863a1ff9c92170d3da46c942c263..36e996589806dfd592629d1414631aa3d3dd3984 100644 (file)
 #include "net.h"
 #include "platform.h"
 
+unsigned long long options = 0;
+
 /* These options only make sense in the config file, so don't use any
    valid short options for them */
-#define O_BASE         MAX('z', 'Z') + 1
-#define O_ARPING       O_BASE + 1
-#define O_FALLBACK     O_BASE + 2
-#define O_DESTINATION  O_BASE + 3
-#define O_NOIPV6RS     O_BASE + 4
-#define O_IPV6_RA_FORK O_BASE + 5
+#define O_BASE                 MAX('z', 'Z') + 1
+#define O_ARPING               O_BASE + 1
+#define O_FALLBACK             O_BASE + 2
+#define O_DESTINATION          O_BASE + 3
+#define O_IPV6RS               O_BASE + 4
+#define O_NOIPV6RS             O_BASE + 5 
+#define O_IPV6RA_FORK          O_BASE + 6 
+#define O_IPV6RA_OWN           O_BASE + 7
+#define O_IPV6RA_OWN_D         O_BASE + 8
 
 const struct option cf_options[] = {
        {"background",      no_argument,       NULL, 'b'},
@@ -105,8 +110,11 @@ const struct option cf_options[] = {
        {"arping",          required_argument, NULL, O_ARPING},
        {"destination",     required_argument, NULL, O_DESTINATION},
        {"fallback",        required_argument, NULL, O_FALLBACK},
+       {"ipv6rs",          no_argument,       NULL, O_IPV6RS},
        {"noipv6rs",        no_argument,       NULL, O_NOIPV6RS},
-       {"ipv6ra_fork",     no_argument,       NULL, O_IPV6_RA_FORK},
+       {"ipv6ra_fork",     no_argument,       NULL, O_IPV6RA_FORK},
+       {"ipv6ra_own",      no_argument,       NULL, O_IPV6RA_OWN},
+       {"ipv6ra_own_default", no_argument,    NULL, O_IPV6RA_OWN_D},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -744,12 +752,21 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                free(ifo->fallback);
                ifo->fallback = xstrdup(arg);
                break;
+       case O_IPV6RS:
+               ifo->options |= DHCPCD_IPV6RS;
+               break;
        case O_NOIPV6RS:
                ifo->options &= ~DHCPCD_IPV6RS;
                break;
-       case O_IPV6_RA_FORK:
+       case O_IPV6RA_FORK:
                ifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;
                break;
+       case O_IPV6RA_OWN:
+               ifo->options |= DHCPCD_IPV6RA_OWN;
+               break;
+       case O_IPV6RA_OWN_D:
+               ifo->options |= DHCPCD_IPV6RA_OWN_DEFAULT;
+               break;
        default:
                return 0;
        }
index 685de6e04a5071e64ec2219d51f31278c4a297a1..3e083d87b39dc0d5bec3b05ad148c82a9a3cdde6 100644 (file)
@@ -79,6 +79,9 @@
 #define DHCPCD_DUMPLEASE               (1ULL << 30)
 #define DHCPCD_IPV6RS                  (1ULL << 31)
 #define DHCPCD_IPV6RA_REQRDNSS         (1ULL << 32)
+#define DHCPCD_IPV6RA_OWN              (1ULL << 33)
+#define DHCPCD_IPV6RA_OWN_DEFAULT      (1ULL << 34)
+#define DHCPCD_IPV4                    (1ULL << 35)
 
 extern const struct option cf_options[];
 
@@ -117,6 +120,8 @@ struct if_options {
        char *fallback;
 };
 
+extern unsigned long long options;
+
 struct if_options *read_config(const char *,
     const char *, const char *, const char *);
 int add_options(struct if_options *, int, char **);
diff --git a/ipv6.c b/ipv6.c
new file mode 100644 (file)
index 0000000..e88035e
--- /dev/null
+++ b/ipv6.c
@@ -0,0 +1,438 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "configure.h"
+#include "dhcpcd.h"
+#include "ipv6.h"
+#include "ipv6rs.h"
+
+/* Hackery at it's finest. */
+#ifndef s6_addr32
+#  define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+int socket_afnet6;
+static struct rt6head *routes;
+
+#ifdef DEBUG_MEMORY
+static void
+ipv6_cleanup()
+{
+
+       free(routes);
+}
+#endif
+
+int
+ipv6_open(void)
+{
+       socket_afnet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+       if (socket_afnet6 == -1)
+               return -1;
+       set_cloexec(socket_afnet6);
+       routes = xmalloc(sizeof(*routes));
+       TAILQ_INIT(routes);
+#ifdef DEBUG_MEMORY
+       atexit(ipv6_cleanup);
+#endif
+       return socket_afnet6;
+}
+
+struct in6_addr *
+ipv6_linklocal(const char *ifname)
+{
+       struct ifaddrs *ifaddrs, *ifa;
+       struct sockaddr_in6 *sa6;
+       struct in6_addr *in6;
+
+       if (getifaddrs(&ifaddrs) == -1)
+               return NULL;
+
+       for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
+               if (ifa->ifa_addr == NULL ||
+                   ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+               if (strcmp(ifa->ifa_name, ifname))
+                       continue;
+               sa6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr;
+               if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
+                       break;
+       }
+
+       if (ifa) {
+               in6 = xmalloc(sizeof(*in6));
+               memcpy(in6, &sa6->sin6_addr, sizeof(*in6));
+       } else
+               in6 = NULL;
+
+       freeifaddrs(ifaddrs);
+       return in6;
+}
+
+int
+ipv6_makeaddr(struct in6_addr *addr, const char *ifname,
+    const struct in6_addr *prefix, int prefix_len)
+{
+       struct in6_addr *lla;
+
+       if (prefix_len > 64) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       lla = ipv6_linklocal(ifname);
+       if (lla == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       memcpy(addr, prefix, sizeof(*prefix));
+       addr->s6_addr32[2] = lla->s6_addr32[2];
+       addr->s6_addr32[3] = lla->s6_addr32[3];
+       free(lla);
+       return 0;
+}
+
+int
+ipv6_mask(struct in6_addr *mask, int len)
+{
+       static const unsigned char masks[NBBY] =
+           { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+       int bytes, bits, i;
+
+       if (len < 0 || len > 128) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       memset(mask, 0, sizeof(*mask));
+       bytes = len / NBBY;
+       bits = len % NBBY;
+       for (i = 0; i < bytes; i++)
+               mask->s6_addr[i] = 0xff;
+       if (bits)
+               mask->s6_addr[bytes] = masks[bits - 1]; 
+       return 0;
+}
+
+int
+ipv6_prefixlen(const struct in6_addr *mask)
+{
+       int x = 0, y;
+       const unsigned char *lim, *p;
+
+       lim = (const unsigned char *)mask + sizeof(*mask);
+       for (p = (const unsigned char *)mask; p < lim; x++, p++) {
+               if (*p != 0xff)
+                       break;
+       }
+       y = 0;
+       if (p < lim) {
+               for (y = 0; y < NBBY; y++) {
+                       if ((*p & (0x80 >> y)) == 0)
+                               break;
+               }
+       }
+
+       /*
+        * when the limit pointer is given, do a stricter check on the
+        * remaining bits.
+        */
+       if (p < lim) {
+               if (y != 0 && (*p & (0x00ff >> y)) != 0)
+                       return -1;
+               for (p = p + 1; p < lim; p++)
+                       if (*p != 0)
+                               return -1;
+       }
+
+       return x * NBBY + y;
+}
+
+static struct rt6 *
+find_route6(struct rt6head *rts, const struct rt6 *r)
+{
+       struct rt6 *rt;
+
+       TAILQ_FOREACH(rt, rts, next) {
+               if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
+#if HAVE_ROUTE_METRIC
+                   rt->iface->metric == r->iface->metric &&
+#endif
+                   IN6_ARE_ADDR_EQUAL(&rt->net, &r->net))
+                       return rt;
+       }
+       return NULL;
+}
+
+static void
+desc_route(const char *cmd, const struct rt6 *rt)
+{
+       char destbuf[INET6_ADDRSTRLEN];
+       char gatebuf[INET6_ADDRSTRLEN];
+       const char *ifname = rt->iface->name, *dest, *gate;
+
+       dest = inet_ntop(AF_INET6, &rt->dest.s6_addr,
+           destbuf, INET6_ADDRSTRLEN);
+       gate = inet_ntop(AF_INET6, &rt->gate.s6_addr,
+           gatebuf, INET6_ADDRSTRLEN);
+       if (IN6_ARE_ADDR_EQUAL(&rt->gate, &in6addr_any))
+               syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd,
+                   dest, ipv6_prefixlen(&rt->net));
+       else if (IN6_ARE_ADDR_EQUAL(&rt->dest, &in6addr_any) &&
+           IN6_ARE_ADDR_EQUAL(&rt->net, &in6addr_any))
+               syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd,
+                   gate);
+       else
+               syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd,
+                   dest, ipv6_prefixlen(&rt->net), gate);
+}
+
+static int
+n_route(struct rt6 *rt)
+{
+
+       /* Don't set default routes if not asked to */
+       if (IN6_IS_ADDR_UNSPECIFIED(&rt->dest) &&
+           IN6_IS_ADDR_UNSPECIFIED(&rt->net) &&
+           !(rt->iface->state->options->options & DHCPCD_GATEWAY))
+               return -1;
+
+       /* Delete the route first as it could exist prior to dhcpcd running
+        * and we need to ensure it leaves via our preffered interface */
+       del_route6(rt);
+       desc_route("adding", rt);
+       if (!add_route6(rt))
+               return 0;
+
+       syslog(LOG_ERR, "%s: add_route: %m", rt->iface->name);
+       return -1;
+}
+
+static int
+c_route(struct rt6 *ort, struct rt6 *nrt)
+{
+
+       /* Don't set default routes if not asked to */
+       if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) &&
+           IN6_IS_ADDR_UNSPECIFIED(&nrt->net) &&
+           !(nrt->iface->state->options->options & DHCPCD_GATEWAY))
+               return -1;
+
+       desc_route("changing", nrt);
+       /* We delete and add the route so that we can change metric.
+        * This also has the nice side effect of flushing ARP entries so
+        * we don't have to do that manually. */
+       del_route6(ort);
+       if (!add_route6(nrt))
+               return 0;
+       syslog(LOG_ERR, "%s: add_route: %m", nrt->iface->name);
+       return -1;
+}
+
+static int
+d_route(struct rt6 *rt)
+{
+       int retval;
+
+       desc_route("deleting", rt);
+       retval = del_route6(rt);
+       if (retval != 0 && errno != ENOENT && errno != ESRCH)
+               syslog(LOG_ERR,"%s: del_route: %m", rt->iface->name);
+       return retval;
+}
+
+static struct rt6 *
+make_route(struct ra *rap)
+{
+       struct rt6 *r;
+
+       r = xzalloc(sizeof(*r));
+       r->ra = rap;
+       r->iface = rap->iface;
+       r->metric = rap->iface->metric;
+       r->mtu = rap->mtu;
+       return r;
+}
+
+static struct rt6 *
+make_prefix(struct ra *rap, struct ipv6_addr *addr)
+{
+       struct rt6 *r;
+
+       if (addr == NULL || addr->prefix_len > 128)
+               return NULL;
+
+       r = make_route(rap);
+       r->dest = addr->prefix;
+       ipv6_mask(&r->net, addr->prefix_len);
+       r->gate = in6addr_any;
+       return r;
+}
+
+static struct rt6 *
+make_router(struct ra *rap)
+{
+       struct rt6 *r;
+
+       r = make_route(rap);
+       r->dest = in6addr_any;
+       r->net = in6addr_any;
+       r->gate = rap->from;
+       return r;
+}
+
+int
+ipv6_remove_subnet(struct ra *rap, struct ipv6_addr *addr)
+{
+       struct rt6 *rt;
+       int r;
+
+       /* We need to delete the subnet route to have our metric or
+        * prefer the interface. */
+       r = 0;
+       rt = make_prefix(rap, addr);
+       if (rt) {
+               rt->iface = rap->iface;
+#ifdef __linux__
+               rt->metric = 256;
+#else
+               rt->metric = 0;
+#endif
+               if (!find_route6(routes, rt))
+                       r = del_route6(rt);
+               free(rt);
+       }
+       return r;
+}
+
+#define RT_IS_DEFAULT(rtp) \
+       (IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) &&                  \
+           IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any))
+
+void
+ipv6_build_routes(void)
+{
+       struct rt6head dnr, *nrs;
+       struct rt6 *rt, *rtn, *or;
+       struct ra *rap, *ran;
+       struct ipv6_addr *addr;
+       int have_default;
+
+       if (!(options & (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT)))
+               return;
+
+       TAILQ_INIT(&dnr);
+       TAILQ_FOREACH(rap, &ipv6_routers, next) {
+               if (rap->expired)
+                       continue;
+               if (options & DHCPCD_IPV6RA_OWN)
+                       TAILQ_FOREACH(addr, &rap->addrs, next) {
+                               rt = make_prefix(rap, addr);
+                               if (rt)
+                                       TAILQ_INSERT_TAIL(&dnr, rt, next);
+                       }
+               rt = make_router(rap);
+               if (rt)
+                       TAILQ_INSERT_TAIL(&dnr, rt, next);
+       }
+
+       nrs = xmalloc(sizeof(*nrs));
+       TAILQ_INIT(nrs);
+       have_default = 0;
+       TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) {
+               /* Is this route already in our table? */
+               if (find_route6(nrs, rt) != NULL)
+                       continue;
+               //rt->src.s_addr = ifp->addr.s_addr;
+               /* Do we already manage it? */
+               if ((or = find_route6(routes, rt))) {
+                       if (or->iface != rt->iface ||
+               //          or->src.s_addr != ifp->addr.s_addr ||
+                           !IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate) ||
+                           rt->metric != or->metric)
+                       {
+                               if (c_route(or, rt) != 0)
+                                       continue;
+                       }
+                       TAILQ_REMOVE(routes, or, next);
+                       free(or);
+               } else {
+                       if (n_route(rt) != 0)
+                               continue;
+               }
+               if (RT_IS_DEFAULT(rt))
+                       have_default = 1;
+               TAILQ_REMOVE(&dnr, rt, next);
+               TAILQ_INSERT_TAIL(nrs, rt, next);
+       }
+
+       /* Free any routes we failed to add/change */
+       while ((rt = TAILQ_FIRST(&dnr))) {
+               TAILQ_REMOVE(&dnr, rt, next);
+               free(rt);
+       }
+
+       /* Remove old routes we used to manage
+        * If we own the default route, but not RA management itself
+        * then we need to preserve the last best default route we had */
+       TAILQ_FOREACH_SAFE(rt, routes, next, rtn) {
+               if (find_route6(nrs, rt) == NULL) {
+                       if (!have_default &&
+                           (options & DHCPCD_IPV6RA_OWN_DEFAULT) &&
+                           !(options & DHCPCD_IPV6RA_OWN) &&
+                           RT_IS_DEFAULT(rt))
+                               have_default = 1;
+                               /* no need to add it back to our routing table
+                                * as we delete an exiting route when we add
+                                * a new one */
+                       else
+                               d_route(rt);
+               }
+               free(rt);
+       }
+       free(routes);
+       routes = nrs;
+
+       /* Now drop expired routers */
+       TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) {
+               if (rap->expired)
+                       ipv6rs_drop_ra(rap);
+       }
+}
diff --git a/ipv6.h b/ipv6.h
new file mode 100644 (file)
index 0000000..8f6ef27
--- /dev/null
+++ b/ipv6.h
@@ -0,0 +1,76 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * 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 IPV6_H
+#define IPV6_H
+
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+
+#include "ipv6rs.h"
+
+#define ALLROUTERS "ff02::2"
+#define HOPLIMIT 255
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+
+struct ipv6_addr {
+       TAILQ_ENTRY(ipv6_addr) next;
+       struct in6_addr prefix;
+       int prefix_len;
+       uint32_t prefix_vltime;
+       uint32_t prefix_pltime;
+       struct in6_addr addr;
+       int new;
+       char saddr[INET6_ADDRSTRLEN];
+};
+
+struct rt6 {
+       TAILQ_ENTRY(rt6) next;
+       struct in6_addr dest;
+       struct in6_addr net;
+       struct in6_addr gate;
+       const struct interface *iface;
+       struct ra *ra;
+       int metric;
+       unsigned int mtu;
+};
+TAILQ_HEAD(rt6head, rt6);
+
+extern int socket_afnet6;
+
+int ipv6_open(void);
+struct in6_addr *ipv6_linklocal(const char *);
+int ipv6_makeaddr(struct in6_addr *, const char *, const struct in6_addr *, int);
+int ipv6_mask(struct in6_addr *, int);
+int ipv6_prefixlen(const struct in6_addr *);
+int ipv6_remove_subnet(struct ra *, struct ipv6_addr *);
+void ipv6_build_routes(void);
+void ipv6_drop(struct interface *);
+
+#endif
diff --git a/ipv6ns.c b/ipv6ns.c
new file mode 100644 (file)
index 0000000..3e48386
--- /dev/null
+++ b/ipv6ns.c
@@ -0,0 +1,349 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * 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 <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#ifdef __linux__
+#  define _LINUX_IN6_H
+#  include <linux/ipv6.h>
+#endif
+
+#define ELOOP_QUEUE 1
+#include "common.h"
+#include "configure.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "ipv6.h"
+#include "ipv6ns.h"
+
+#define MIN_RANDOM_FACTOR      (500 * 1000)    /* milliseconds in usecs */
+#define MAX_RANDOM_FACTOR      (1500 * 1000)   /* milliseconds in usecs */
+
+/* Debugging Neighbor Solicitations is a lot of spam, so disable it */
+//#define DEBUG_NS
+
+static int sock;
+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
+ipv6ns_cleanup(void)
+{
+
+       free(sndbuf);
+       free(rcvbuf);
+}
+#endif
+
+int
+ipv6ns_open(void)
+{
+       int on;
+       int len;
+       struct icmp6_filter filt;
+
+       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)
+               return -1;
+       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)
+               return -1;
+
+       on = 1;
+       if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+               &on, sizeof(on)) == -1)
+               return -1;
+
+       ICMP6_FILTER_SETBLOCKALL(&filt);
+       ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt);
+       if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER,
+               &filt, sizeof(filt)) == -1)
+               return -1;
+
+       set_cloexec(sock);
+#if DEBUG_MEMORY
+       atexit(ipv6ns_cleanup);
+#endif
+
+       len = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int));
+       sndbuf = xzalloc(len);
+       if (sndbuf == NULL)
+               return -1;
+       sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       sndhdr.msg_iov = sndiov;
+       sndhdr.msg_iovlen = 1;
+       sndhdr.msg_control = sndbuf;
+       sndhdr.msg_controllen = len;
+       rcvbuf = xzalloc(len);
+       if (rcvbuf == NULL)
+               return -1;
+       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;
+}
+
+static int
+ipv6ns_makeprobe(struct ra *rap)
+{
+       struct nd_neighbor_solicit *ns;
+       struct nd_opt_hdr *nd;
+
+       free(rap->ns);
+       rap->nslen = sizeof(*ns) + ROUNDUP8(rap->iface->hwlen + 2);
+       rap->ns = xzalloc(rap->nslen);
+       if (rap->ns == NULL)
+               return -1;
+       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);
+       return 0;
+}
+
+void
+ipv6ns_sendprobe(void *arg)
+{
+       struct ra *rap = arg;
+       struct sockaddr_in6 dst;
+       struct cmsghdr *cm;
+       struct in6_pktinfo pi;
+       int hoplimit = HOPLIMIT;
+       struct timeval tv, rtv;
+
+       if (!rap->nsprobes) {
+               if (ipv6ns_makeprobe(rap) == -1)
+                       return;
+       }
+
+       dst = allrouters;
+       //dst.sin6_scope_id = ifp->linkid;
+
+       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: sendmsg: %m", rap->iface->name);
+
+       tv.tv_sec = RETRANS_TIMER;
+       tv.tv_usec = MIN_RANDOM_FACTOR;
+       rtv.tv_sec = 0;
+       rtv.tv_usec = arc4random() % (MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR);
+       timeradd(&tv, &rtv, &tv);
+       add_timeout_tv(&tv, ipv6ns_sendprobe, rap);
+}
+
+void
+ipv6ns_unreachable(void *arg)
+{
+       struct ra *rap = arg;
+
+       /* 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_build_routes();
+       run_script_reason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
+}
+
+/* ARGSUSED */
+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;
+
+       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;
+       }
+
+       for (ifp = ifaces; ifp; ifp = ifp->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;
+       }
+
+       TAILQ_FOREACH(rap, &ipv6_routers, next) {
+               if (memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr,
+                   sizeof(rap->from.s6_addr)) == 0)
+                       break;
+       }
+       if (rap == NULL) {
+#ifdef DEBUG_NS
+               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;
+               ipv6_build_routes();
+               run_script_reason(ifp, "ROUTERADVERT");
+               return;
+       }
+
+       if (is_solicited) {
+               rap->nsprobes = 1;
+               add_timeout_sec(REACHABLE_TIME, ipv6ns_unreachable, rap);
+               add_timeout_sec(DELAY_FIRST_PROBE_TIME, ipv6ns_sendprobe, rap);
+       }
+}
diff --git a/ipv6ns.h b/ipv6ns.h
new file mode 100644 (file)
index 0000000..05636c9
--- /dev/null
+++ b/ipv6ns.h
@@ -0,0 +1,42 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
+ * 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 REACHABLE_TIME         30      /* seconds */
+#define RETRANS_TIMER          1       /* second */
+#define DELAY_FIRST_PROBE_TIME 5       /* seconds */
+
+int ipv6ns_open(void);
+void ipv6ns_unreachable(void *);
+void ipv6ns_sendprobe(void *);
+void ipv6ns_handledata(void *);
+#endif
index 31587264d1d22837a56bc240ddb82d81fd595dc3..242ff7ca61fad94f0cc2fe33309679a6d8fb5fb1 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
@@ -25,6 +25,7 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/ioctl.h>
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <net/if.h>
 #include "configure.h"
 #include "dhcpcd.h"
 #include "eloop.h"
+#include "ipv6.h"
+#include "ipv6ns.h"
 #include "ipv6rs.h"
 
-#define ALLROUTERS "ff02::2"
-#define HOPLIMIT 255
-
-#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
-
 #define RTR_SOLICITATION_INTERVAL       4 /* seconds */
 #define MAX_RTR_SOLICITATIONS           3 /* times */
 
@@ -81,6 +79,28 @@ struct nd_opt_dnssl {                /* DNSSL option RFC 6106 */
 } _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;
 static struct sockaddr_in6 allrouters, from;
 static struct msghdr sndhdr;
@@ -135,6 +155,7 @@ ipv6rs_open(void)
                &filt, sizeof(filt)) == -1)
                return -1;
 
+       set_cloexec(sock);
 #if DEBUG_MEMORY
        atexit(ipv6rs_cleanup);
 #endif
@@ -173,7 +194,7 @@ ipv6rs_makeprobe(struct interface *ifp)
        ifp->rs = xzalloc(ifp->rslen);
        if (ifp->rs == NULL)
                return -1;
-       rs = (struct nd_router_solicit *)ifp->rs;
+       rs = (struct nd_router_solicit *)(void *)ifp->rs;
        rs->nd_rs_type = ND_ROUTER_SOLICIT;
        rs->nd_rs_code = 0;
        rs->nd_rs_cksum = 0;
@@ -230,40 +251,101 @@ ipv6rs_sendprobe(void *arg)
 }
 
 static void
-ipv6rs_sort(struct interface *ifp)
+ipv6rs_free_opts(struct ra *rap)
 {
-       struct ra *rap, *sorted, *ran, *rat;
+       struct ra_opt *rao;
 
-       if (ifp->ras == NULL || ifp->ras->next == NULL)
-               return;
+       while ((rao = TAILQ_FIRST(&rap->options))) {
+               TAILQ_REMOVE(&rap->options, rao, next);
+               free(rao->option);
+               free(rao);
+       }
+}
 
-       /* Sort our RA's - most recent first */
-       sorted = ifp->ras;
-       ifp->ras = ifp->ras->next;
-       sorted->next = NULL;
-       for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) {
-               /* Are we the new head? */
-               if (timercmp(&rap->received, &sorted->received, <)) {
-                       rap->next = sorted;
-                       sorted = rap;
-                       continue;
+static void
+ipv6rs_drop_addrs(struct ra *rap)
+{
+       struct ipv6_addr *ap;
+
+       while ((ap = TAILQ_FIRST(&rap->addrs))) {
+               TAILQ_REMOVE(&rap->addrs, ap, next);
+               if ((options & DHCPCD_IPV6RA_OWN)) {
+               syslog(LOG_INFO, "%s: deleting address %s",
+                           rap->iface->name, ap->saddr);
+                       if (del_address6(rap->iface, ap) == -1)
+                               syslog(LOG_ERR, "del_address6 %m");
                }
-               /* Do we fit in the middle? */
-               for (rat = sorted; rat->next; rat = rat->next) {
-                       if (timercmp(&rap->received, &rat->next->received, <)) {
-                               rap->next = rat->next;
-                               rat->next = rap;
-                               break;
-                       }
+               free(ap);
+       }
+}
+
+void ipv6rs_drop_ra(struct ra *rap)
+{
+
+       delete_timeout(NULL, rap->iface);
+       delete_timeout(NULL, rap);
+       TAILQ_REMOVE(&ipv6_routers, rap, next);
+       ipv6rs_drop_addrs(rap);
+       ipv6rs_free_opts(rap);
+       free(rap->data);
+       free(rap->ns);
+       free(rap);
+}
+
+ssize_t
+ipv6rs_free(struct interface *ifp)
+{
+       struct ra *rap, *ran;
+       ssize_t n;
+
+       free(ifp->rs);
+       ifp->rs = NULL;
+       n = 0;
+       TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) {
+               if (rap->iface == ifp) {
+                       ipv6rs_drop_ra(rap);
+                       n++;
                }
-               /* We must be at the end */
-               if (!rat->next) {
-                       rat->next = rap;
-                       rap->next = NULL;
+       }
+       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_HEAD(&ipv6_routers, router, next);
 }
 
+/* ARGSUSED */
 void
 ipv6rs_handledata(_unused void *arg)
 {
@@ -279,17 +361,18 @@ ipv6rs_handledata(_unused void *arg)
        struct nd_opt_mtu *mtu;
        struct nd_opt_rdnss *rdnss;
        struct nd_opt_dnssl *dnssl;
-       uint32_t lifetime;
+       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, *raol;
+       struct ra_opt *rao;
+       struct ipv6_addr *ap;
        char *opt;
        struct timeval expire;
-       int has_dns;
+       int has_dns, new_rap;
 
        len = recvmsg(sock, &rcvhdr, 0);
        if (len == -1) {
@@ -338,8 +421,7 @@ ipv6rs_handledata(_unused void *arg)
        }
 
        if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
-               syslog(LOG_ERR, "RA recieved from non local IPv6 address %s",
-                   sfrom);
+               syslog(LOG_ERR, "RA from non local address %s", sfrom);
                return;
        }
 
@@ -347,11 +429,10 @@ ipv6rs_handledata(_unused void *arg)
                if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
                        break;
        if (ifp == NULL) {
-               syslog(LOG_ERR,"received RA for unexpected interface from %s",
-                   sfrom);
+               syslog(LOG_ERR, "RA for unexpected interface from %s", sfrom);
                return;
        }
-       for (rap = ifp->ras; rap; rap = rap->next) {
+       TAILQ_FOREACH(rap, &ipv6_routers, next) {
                if (memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr,
                    sizeof(rap->from.s6_addr)) == 0)
                        break;
@@ -372,15 +453,16 @@ ipv6rs_handledata(_unused void *arg)
        }
 
        if (rap == NULL) {
-               rap = xmalloc(sizeof(*rap));
-               rap->next = ifp->ras;
-               rap->options = NULL;
-               ifp->ras = rap;
+               rap = xzalloc(sizeof(*rap));
+               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));
-               rap->data_len = 0;
-       }
+               TAILQ_INIT(&rap->addrs);
+               TAILQ_INIT(&rap->options);
+               new_rap = 1;
+       } else
+               new_rap = 0;
        if (rap->data_len == 0) {
                rap->data = xmalloc(len);
                memcpy(rap->data, icp, len);
@@ -389,6 +471,7 @@ ipv6rs_handledata(_unused void *arg)
 
        get_monotonic(&rap->received);
        nd_ra = (struct nd_router_advert *)icp;
+       rap->flags = nd_ra->nd_ra_flags_reserved;
        rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
        rap->expired = 0;
 
@@ -417,7 +500,7 @@ ipv6rs_handledata(_unused void *arg)
                opt = NULL;
                switch (ndo->nd_opt_type) {
                case ND_OPT_PREFIX_INFORMATION:
-                       pi = (struct nd_opt_prefix_info *)ndo;
+                       pi = (struct nd_opt_prefix_info *)(void *)ndo;
                        if (pi->nd_opt_pi_len != 4) {
                                syslog(LOG_ERR,
                                    "%s: invalid option len for prefix",
@@ -436,22 +519,53 @@ ipv6rs_handledata(_unused void *arg)
                                    "%s: invalid prefix in RA", ifp->name);
                                break;
                        }
-                       opt = xstrdup(inet_ntop(AF_INET6,
-                           pi->nd_opt_pi_prefix.s6_addr,
-                           ntopbuf, INET6_ADDRSTRLEN));
-                       if (opt) {
-                               rap->prefix_len = pi->nd_opt_pi_prefix_len;
-                               rap->prefix_vltime =
-                                       ntohl(pi->nd_opt_pi_valid_time);
-                               rap->prefix_pltime =
-                                       ntohl(pi->nd_opt_pi_preferred_time);
-                       }
+                       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) {
+                               ap = xmalloc(sizeof(*ap));
+                               ap->new = 1;
+                               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));
+                               ipv6_makeaddr(&ap->addr, ifp->name,
+                                   &ap->prefix, pi->nd_opt_pi_prefix_len);
+                               cbp = inet_ntop(AF_INET6, ap->addr.s6_addr,
+                                   ntopbuf, INET6_ADDRSTRLEN);
+                               if (cbp)
+                                       memcpy(ap->saddr, cbp,
+                                           sizeof(ap->saddr));
+                               else
+                                       ap->saddr[0] = '\0';
+                               TAILQ_INSERT_TAIL(&rap->addrs, ap, next);
+                       } else if (ap->prefix_vltime !=
+                           ntohl(pi->nd_opt_pi_valid_time) ||
+                           ap->prefix_pltime !=
+                           ntohl(pi->nd_opt_pi_preferred_time))
+                               ap->new = 1;
+                       else
+                               ap->new = 0;
+                       ap->prefix_vltime =
+                           ntohl(pi->nd_opt_pi_valid_time);
+                       ap->prefix_pltime =
+                           ntohl(pi->nd_opt_pi_preferred_time);
                        break;
 
                case ND_OPT_MTU:
-                       mtu = (struct nd_opt_mtu *)p;
-                       snprintf(buf, sizeof(buf), "%d",
-                           ntohl(mtu->nd_opt_mtu_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;
+                       }
+                       if (rap->mtu == 0 || mtuv < rap->mtu)
+                               rap->mtu = mtuv;
+                       snprintf(buf, sizeof(buf), "%d", mtuv);
                        opt = xstrdup(buf);
                        break;
 
@@ -507,20 +621,14 @@ ipv6rs_handledata(_unused void *arg)
 
                if (opt == NULL)
                        continue;
-               for (raol = NULL, rao = rap->options;
-                   rao;
-                   raol = rao, rao = rao->next)
-               {
+               TAILQ_FOREACH(rao, &rap->options, next) {
                        if (rao->type == ndo->nd_opt_type &&
                            strcmp(rao->option, opt) == 0)
                                break;
                }
                if (lifetime == 0) {
                        if (rao) {
-                               if (raol)
-                                       raol->next = rao->next;
-                               else
-                                       rap->options = rao->next;
+                               TAILQ_REMOVE(&rap->options, rao, next);
                                free(rao->option);
                                free(rao);
                        }
@@ -529,10 +637,9 @@ ipv6rs_handledata(_unused void *arg)
 
                if (rao == NULL) {
                        rao = xmalloc(sizeof(*rao));
-                       rao->next = rap->options;
-                       rap->options = rao;
                        rao->type = ndo->nd_opt_type;
                        rao->option = opt;
+                       TAILQ_INSERT_TAIL(&rap->options, rao, next);
                } else
                        free(opt);
                if (lifetime == ~0U)
@@ -544,7 +651,25 @@ ipv6rs_handledata(_unused void *arg)
                }
        }
 
-       ipv6rs_sort(ifp);
+       if (new_rap)
+               add_router(rap);
+       if (options & DHCPCD_IPV6RA_OWN) {
+               TAILQ_FOREACH(ap, &rap->addrs, next) {
+                       syslog(ap->new ? LOG_INFO : LOG_DEBUG,
+                           "%s: adding address %s",
+                           ifp->name, ap->saddr);
+                       if (add_address6(ifp, ap) == -1)
+                               syslog(LOG_ERR, "add_address6 %m");
+                       else if (ipv6_remove_subnet(rap, ap) == -1)
+                               syslog(LOG_ERR, "ipv6_remove_subnet %m");
+                       else
+                               syslog(ap->new ? LOG_INFO : LOG_DEBUG,
+                                   "%s: vltime %d seconds, pltime %d seconds",
+                                   ifp->name, ap->prefix_vltime,
+                                   ap->prefix_pltime);
+               }
+       }
+       ipv6_build_routes();
        run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT");
        if (options & DHCPCD_TEST)
                exit(EXIT_SUCCESS);
@@ -556,6 +681,7 @@ ipv6rs_handledata(_unused void *arg)
        if (has_dns)
                delete_q_timeout(0, handle_exit_timeout, NULL);
        delete_timeout(NULL, ifp);
+       delete_timeout(NULL, rap); /* reachable timer */
        ipv6rs_expire(ifp);
        if (has_dns)
                daemonise();
@@ -563,6 +689,27 @@ ipv6rs_handledata(_unused void *arg)
                syslog(LOG_WARNING,
                    "%s: did not fork due to an absent RDNSS option in the RA",
                    ifp->name);
+
+       /* If we're owning the RA then we need to try and ensure the
+        * router is actually reachable */
+       if (options & DHCPCD_IPV6RA_OWN ||
+           options & DHCPCD_IPV6RA_OWN_DEFAULT)
+       {
+               rap->nsprobes = 0;
+               add_timeout_sec(REACHABLE_TIME, ipv6ns_unreachable, rap);
+               add_timeout_sec(DELAY_FIRST_PROBE_TIME, ipv6ns_sendprobe, rap);
+       }
+}
+
+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
@@ -573,12 +720,16 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
        const struct ra *rap;
        const struct ra_opt *rao;
        int i;
-       char buffer[32], buffer2[32];
+       char buffer[32];
        const char *optn;
-       
+
+       i = 1;
        l = 0;
        get_monotonic(&now);
-       for (rap = ifp->ras, i = 1; rap; rap = rap->next, i++) {
+       TAILQ_FOREACH(rap, &ipv6_routers, next) {
+               i++;
+               if (rap->iface != ifp)
+                       continue;
                if (env) {
                        snprintf(buffer, sizeof(buffer),
                            "ra%d_from", i);
@@ -586,7 +737,7 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
                }
                l++;
 
-               for (rao = rap->options; rao; rao = rao->next) {
+               TAILQ_FOREACH(rao, &rap->options, next) {
                        if (rao->option == NULL)
                                continue;
                        if (env == NULL) {
@@ -618,6 +769,7 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
                        snprintf(buffer, sizeof(buffer), "ra%d_%s", i, optn);
                        setvar(&env, prefix, buffer, rao->option);
                        l++;
+#if 0
                        switch (rao->type) {
                        case ND_OPT_PREFIX_INFORMATION:
                                snprintf(buffer, sizeof(buffer),
@@ -640,7 +792,7 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
                                l += 3;
                                break;
                        }
-               
+#endif         
                }
        }
 
@@ -650,38 +802,12 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
        return l;
 }
 
-static void
-ipv6rs_free_opts(struct ra *rap)
-{
-       struct ra_opt *rao, *raon;
-
-       for (rao = rap->options; rao && (raon = rao->next, 1); rao = raon) {
-               free(rao->option);
-               free(rao);
-       }
-}
-
-void
-ipv6rs_free(struct interface *ifp)
-{
-       struct ra *rap, *ran;
-
-       free(ifp->rs);
-       ifp->rs = NULL;
-       for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) {
-               ipv6rs_free_opts(rap);
-               free(rap->data);
-               free(rap);
-       }
-       ifp->ras = NULL;
-}
-
 void
 ipv6rs_expire(void *arg)
 {
        struct interface *ifp;
-       struct ra *rap, *ran, *ral;
-       struct ra_opt *rao, *raol, *raon;
+       struct ra *rap, *ran;
+       struct ra_opt *rao, *raon;
        struct timeval now, lt, expire, next;
        int expired;
 
@@ -690,10 +816,9 @@ ipv6rs_expire(void *arg)
        expired = 0;
        timerclear(&next);
 
-       for (rap = ifp->ras, ral = NULL;
-           rap && (ran = rap->next, 1);
-           ral = rap, rap = ran)
-       {
+       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, &lt, &expire);
@@ -701,33 +826,23 @@ ipv6rs_expire(void *arg)
                        syslog(LOG_INFO, "%s: %s: expired Router Advertisement",
                            ifp->name, rap->sfrom);
                        rap->expired = expired = 1;
-                       if (ral)
-                               ral->next = ran;
-                       else
-                               ifp->ras = ran;
-                       ipv6rs_free_opts(rap);
-                       free(rap);
                        continue;
                }
                timersub(&expire, &now, &lt);
                if (!timerisset(&next) || timercmp(&next, &lt, >))
                        next = lt;
-               
-               for (rao = rap->options, raol = NULL;
-                   rao && (raon = rao->next);
-                   raol = rao, rao = raon)
-               {
+       
+               TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) {
                        if (!timerisset(&rao->expire))
                                continue;
                        if (timercmp(&now, &rao->expire, >)) {
                                syslog(LOG_INFO,
                                    "%s: %s: expired option %d",
                                    ifp->name, rap->sfrom, rao->type);
-                               rap->expired = expired = 1;
-                               if (raol)
-                                       raol = raon;
-                               else
-                                       rap->options = raon;
+                               TAILQ_REMOVE(&rap->options, rao, next);
+                               expired = 1;
+                               free(rao->option);
+                               free(rao);
                                continue;
                        }
                        timersub(&rao->expire, &now, &lt);
@@ -738,8 +853,10 @@ ipv6rs_expire(void *arg)
 
        if (timerisset(&next))
                add_timeout_tv(&next, ipv6rs_expire, ifp);
-       if (expired)
+       if (expired) {
+               ipv6_build_routes();
                run_script_reason(ifp, "ROUTERADVERT");
+       }
 }
 
 int
@@ -758,3 +875,18 @@ ipv6rs_start(struct interface *ifp)
        ipv6rs_sendprobe(ifp);
        return 0;
 }
+
+void
+ipv6rs_drop(struct interface *ifp)
+{
+       struct ra *rap;
+       int expired = 0;
+
+       TAILQ_FOREACH(rap, &ipv6_routers, next)
+               if (rap->iface == ifp)
+                       rap->expired = expired = 1;
+       if (expired) {
+               ipv6_build_routes();
+               run_script_reason(ifp, "ROUTERADVERT");
+       }
+}
index 481aed3444d1dc0cf0a46d4ce5986c9078937bce..4af65450a1cefa53a161a45d95c41e1550592977 100644 (file)
--- a/ipv6rs.h
+++ b/ipv6rs.h
 #ifndef IPV6RS_H
 #define IPV6RS_H
 
+#include <sys/queue.h>
+
+#include <time.h>
+
+#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 mtu;
+       TAILQ_HEAD(, ipv6_addr) addrs;
+       TAILQ_HEAD(, ra_opt) options;
+       
+       unsigned char *ns;
+       size_t nslen;
+       int nsprobes;
+
+       int expired;
+};
+
+extern TAILQ_HEAD(rahead, ra) ipv6_routers;
+
 int ipv6rs_open(void);
 void ipv6rs_handledata(void *);
 int ipv6rs_start(struct interface *);
 ssize_t ipv6rs_env(char **, const char *, const struct interface *);
-void ipv6rs_free(struct interface *ifp);
+void ipv6rs_drop_ra(struct ra *);
+ssize_t ipv6rs_free(struct interface *);
 void ipv6rs_expire(void *arg);
+int ipv6rs_has_ra(const struct interface *);
+void ipv6rs_drop(struct interface *);
 #endif
diff --git a/net.c b/net.c
index 86a95e3331c76b206dff7e5f7d94f7827b2acb46..ae373c1687ee8772907bf65504ee36cb8ee5f651 100644 (file)
--- a/net.c
+++ b/net.c
@@ -70,7 +70,7 @@
 #include "net.h"
 #include "signals.h"
 
-static char hwaddr_buffer[(HWADDR_LEN * 3) + 1];
+static char hwaddr_buffer[(HWADDR_LEN * 3) + 1 + 1024];
 
 int socket_afnet = -1;
 
@@ -133,7 +133,7 @@ hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
        char *p = hwaddr_buffer;
        size_t i;
 
-       for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
+       for (i = 0; i < hwlen; i++) {
                if (i > 0)
                        *p ++= ':';
                p += snprintf(p, 3, "%.2x", hwaddr[i]);
diff --git a/net.h b/net.h
index 5fbab78b9f9d53af185d2aeeddb6cc0db79e74c9..0d1475a0d1017d4224f01ef74408b1a3df842542 100644 (file)
--- a/net.h
+++ b/net.h
 #include "config.h"
 #include "dhcp.h"
 #include "dhcpcd.h"
+#include "ipv6.h"
+
+/* Some systems have route metrics */
+#ifndef HAVE_ROUTE_METRIC
+# ifdef __linux__
+#  define HAVE_ROUTE_METRIC 1
+# endif
+# ifndef HAVE_ROUTE_METRIC
+#  define HAVE_ROUTE_METRIC 0
+# endif
+#endif
 
 #ifndef DUID_LEN
 #  define DUID_LEN                     128 + 2
@@ -59,7 +70,6 @@
 #  define ARPHRD_INFINIBAND            32
 #endif
 
-
 /* Work out if we have a private address or not
  * 10/8
  * 172.16/12
@@ -130,6 +140,16 @@ int if_route(const struct rt *rt, int);
 #define del_src_route(rt) if_route(rt, -2);
 void free_routes(struct rt *);
 
+int if_address6(const struct interface *, const struct ipv6_addr *, int);
+#define add_address6(ifp, a) if_address6(ifp, a, 1)
+#define del_address6(ifp, a) if_address6(ifp, a, -1)
+
+int if_route6(const struct rt6 *rt, int);
+#define add_route6(rt) if_route6(rt, 1)
+#define change_route6(rt) if_route6(rt, 0)
+#define del_route6(rt) if_route6(rt, -1)
+#define del_src_route6(rt) if_route6(rt, -2);
+
 int open_udp_socket(struct interface *);
 extern const size_t udp_dhcp_len;
 ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
index afa43845f65e824988633ccc2bc42982e8089824..fc6561307937f51e2b85e12ace3a02a0c11ce4ad 100644 (file)
 #include <sys/utsname.h>
 #include <netinet/in.h>
 
+#include <stdlib.h>
 #include <syslog.h>
 
+#include "if-options.h"
 #include "platform.h"
 
 #ifndef SYS_NMLN       /* OSX */
@@ -53,37 +55,62 @@ hardware_platform(void)
        return march;
 }
 
+#define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0)
+#define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1)
 static int
-inet6_sysctl(int code)
+inet6_sysctl(int code, int val, int action)
 {
        int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
-       int val;
        size_t size;
 
        mib[3] = code;
        size = sizeof(val);
+       if (action) {
+               if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
+                   NULL, 0, &val, size) == -1)
+                       return -1;
+               return 0;
+       }
        if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &val, &size, NULL, 0) == -1)
                return -1;
        return val;
 }
 
+static void
+restore_kernel_ra(void)
+{
+
+       syslog(LOG_INFO, "restoring Kernel IPv6 RA support");
+       if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 1) == -1)
+               syslog(LOG_ERR, "IPV6CTL_ACCEPT_RTADV: %m");
+}
+
 int
 check_ipv6(const char *ifname)
 {
+       int val;
 
-       /* BSD doesn't support these values per iface, so just reutrn 1 */
+       /* BSD doesn't support these values per iface, so just return 1 */
        if (ifname)
                return 1;
 
-       if (inet6_sysctl(IPV6CTL_ACCEPT_RTADV) != 1) {
-               syslog(LOG_WARNING,
-                   "Kernel is not configured to accept IPv6 RAs");
-               return 0;
+       val = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);
+       if (val == 0)
+               options |= DHCPCD_IPV6RA_OWN;
+       else if (options & DHCPCD_IPV6RA_OWN) {
+               syslog(LOG_INFO, "disabling Kernel IPv6 RA support");
+               if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1) {
+                       syslog(LOG_ERR, "IPV6CTL_ACCEPT_RTADV: %m");
+                       return 0;
+               }
+               atexit(restore_kernel_ra);
        }
-       if (inet6_sysctl(IPV6CTL_FORWARDING) != 0) {
+return 1;
+       if (get_inet6_sysctl(IPV6CTL_FORWARDING) != 0) {
                syslog(LOG_WARNING,
                    "Kernel is configured as a router, not a host");
                return 0;
        }
+
        return 1;
 }
index 119ec5012975fa85aa3eaf29f175bf32e8aa314b..64059175e323d91b76aef90fa8dc42affba36b01 100644 (file)
@@ -32,6 +32,7 @@
 #include <syslog.h>
 
 #include "common.h"
+#include "if-options.h"
 #include "platform.h"
 
 static const char *mproc = 
@@ -72,6 +73,9 @@ static const char *mproc =
 #endif
        ;
 
+char **restore;
+ssize_t nrestore;
+
 char *
 hardware_platform(void)
 {
@@ -121,23 +125,73 @@ check_proc_int(const char *path)
        return atoi(buf);
 }
 
+static ssize_t
+write_path(const char *path, const char *val)
+{
+       FILE *fp;
+       ssize_t r;
+
+       fp = fopen(path, "w");
+       if (fp == NULL)
+               return -1;
+       r = fprintf(fp, "%s\n", val);
+       fclose(fp);
+       return r;
+}
+
 static const char *prefix = "/proc/sys/net/ipv6/conf";
 
+static void
+restore_kernel_ra(void)
+{
+       char path[256];
+
+       for (nrestore--; nrestore >= 0; nrestore--) {
+               syslog(LOG_INFO, "%s: restoring Kernel IPv6 RA support",
+                      restore[nrestore]);
+               snprintf(path, sizeof(path), "%s/%s/accept_ra",
+                        prefix, restore[nrestore]);
+               if (write_path(path, "1") == -1)
+                       syslog(LOG_ERR, "write_path: %s: %m", path);
+#ifdef DEBUG_MEMORY
+               free(restore[nrestore]);
+#endif
+       }
+#ifdef DEBUG_MEMORY
+       free(restore);
+#endif
+}
+
 int
 check_ipv6(const char *ifname)
 {
-       int r;
+       int r, ex;
        char path[256];
 
-       if (ifname == NULL)
+       if (ifname == NULL) {
                ifname = "all";
+               ex = 1;
+       } else
+               ex = 0;
 
        snprintf(path, sizeof(path), "%s/%s/accept_ra", prefix, ifname);
        r = check_proc_int(path);
-       if (r != 1 && r != 2) {
-               syslog(LOG_WARNING,
-                   "%s: not configured to accept IPv6 RAs", ifname);
-               return 0;
+       if (r == 0)
+               options |= DHCPCD_IPV6RA_OWN;
+       else if (options & DHCPCD_IPV6RA_OWN) {
+               syslog(LOG_INFO, "disabling Kernel IPv6 RA support");
+               if (write_path(path, "0") == -1) {
+                       syslog(LOG_ERR, "write_path: %s: %m", path);
+                       return 0;
+               }
+               restore = realloc(restore, (nrestore + 1) * sizeof(char *));
+               if (restore == NULL) {
+                       syslog(LOG_ERR, "realloc: %m");
+                       exit(EXIT_FAILURE);
+               }
+               restore[nrestore++] = xstrdup(ifname);
+               if (ex)
+                       atexit(restore_kernel_ra);
        }
 
        if (r != 2) {