]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add an implementation of an IPv6 Router Solicitor as specified in
authorRoy Marples <roy@marples.name>
Thu, 15 Dec 2011 02:35:47 +0000 (02:35 +0000)
committerRoy Marples <roy@marples.name>
Thu, 15 Dec 2011 02:35:47 +0000 (02:35 +0000)
RFC6016 with regards to RDNSS and DNSSL.

15 files changed:
Makefile
common.c
common.h
configure.c
dhcp.c
dhcp.h
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
dhcpcd.h
if-options.c
if-options.h
ipv4ll.c
ipv6rs.c [new file with mode: 0644]
ipv6rs.h [new file with mode: 0644]

index ea6431f3f1db46072359462d8d5e51d4b47e02de..3b4dbfd689b0fe0fba60909fa37b2f19c935a013 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 
 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 net.c signals.c
+SRCS+=         if-options.c if-pref.c ipv4ll.c ipv6rs.c net.c signals.c
 SRCS+=         configure.c
 
 CFLAGS?=       -O2
index 06420558035a8acdcfa5452650f77085b0841359..95dc1405d0caa3ae3b7fd4bf06712e960aadfc45 100644 (file)
--- a/common.c
+++ b/common.c
@@ -200,6 +200,27 @@ get_monotonic(struct timeval *tp)
        return gettimeofday(tp, NULL);
 }
 
+ssize_t
+setvar(char ***e, const char *prefix, const char *var, const char *value)
+{
+       size_t len = strlen(prefix) + strlen(var) + strlen(value) + 4;
+
+       **e = xmalloc(len);
+       snprintf(**e, len, "%s_%s=%s", prefix, var, value);
+       (*e)++;
+       return len;
+}
+
+ssize_t
+setvard(char ***e, const char *prefix, const char *var, int value)
+{
+       char buffer[32];
+
+       snprintf(buffer, sizeof(buffer), "%d", value);
+       return setvar(e, prefix, var, buffer);
+}
+
+
 time_t
 uptime(void)
 {
index 90cd475a599ac3a23b26f82a7a66fc99848f90f9..877656b4025ae6f5657b833414dcf785d04b65f4 100644 (file)
--- a/common.h
+++ b/common.h
@@ -72,6 +72,8 @@ int set_nonblock(int);
 char *get_line(FILE * __restrict);
 extern int clock_monotonic;
 int get_monotonic(struct timeval *);
+ssize_t setvar(char ***, const char *, const char *, const char *);
+ssize_t setvard(char ***, const char *, const char *, int);
 time_t uptime(void);
 int writepid(int, pid_t);
 void *xrealloc(void *, size_t);
index f7e720260985287fd6579adc6a4d7ef7fa47d5a3..db5b0ec2e2387243b7b056c5974a661f71a97d1c 100644 (file)
@@ -46,6 +46,7 @@
 #include "dhcp.h"
 #include "if-options.h"
 #include "if-pref.h"
+#include "ipv6rs.h"
 #include "net.h"
 #include "signals.h"
 
@@ -168,6 +169,14 @@ make_env(const struct interface *iface, char ***argv)
        ssize_t e, elen, l;
        const struct if_options *ifo = iface->state->options;
        const struct interface *ifp;
+       int dhcp, ra;
+
+       dhcp = 0;
+       ra = 0;
+       if (strcmp(iface->state->reason, "ROUTERADVERT") == 0)
+               ra = 1;
+       else
+               dhcp = 1;
 
        /* When dumping the lease, we only want to report interface and
           reason - the other interface variables are meaningless */
@@ -235,7 +244,7 @@ make_env(const struct interface *iface, char ***argv)
                        snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
                }
        }
-       if (iface->state->old) {
+       if (dhcp && iface->state->old) {
                e = configure_env(NULL, NULL, iface->state->old, ifo);
                if (e > 0) {
                        env = xrealloc(env, sizeof(char *) * (elen + e + 1));
@@ -247,7 +256,7 @@ make_env(const struct interface *iface, char ***argv)
        }
 
 dumplease:
-       if (iface->state->new) {
+       if (dhcp && iface->state->new) {
                e = configure_env(NULL, NULL, iface->state->new, ifo);
                if (e > 0) {
                        env = xrealloc(env, sizeof(char *) * (elen + e + 1));
@@ -257,6 +266,13 @@ dumplease:
                append_config(&env, &elen, "new",
                    (const char *const *)ifo->config);
        }
+       if (ra) {
+               e = ipv6rs_env(NULL, NULL, iface);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += ipv6rs_env(env + elen, "new", iface);
+               }
+       }
 
        /* Add our base environment */
        if (ifo->environ) {
diff --git a/dhcp.c b/dhcp.c
index fa8b02d8f21ee54b2ffb027459ee5a73621775f4..a88cb20cd6265c07d828bcc9d7e69fbf5c824124 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -426,7 +426,7 @@ get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
  * separated string. Returns length of string (including
  * terminating zero) or zero on error. out may be NULL
  * to just determine output length. */
-static ssize_t
+ssize_t
 decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
 {
        const uint8_t *r, *q = p;
@@ -1357,16 +1357,6 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
        return bytes;
 }
 
-static void
-setvar(char ***e, const char *prefix, const char *var, const char *value)
-{
-       size_t len = strlen(prefix) + strlen(var) + strlen(value) + 4;
-
-       **e = xmalloc(len);
-       snprintf(**e, len, "%s_%s=%s", prefix, var, value);
-       (*e)++;
-}
-
 ssize_t
 configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
     const struct if_options *ifo)
diff --git a/dhcp.h b/dhcp.h
index 8e14f456d615e8158d93c623c7c17cd18e5256d4..1c2b81af5629cb01f50c09fecffa63ed446fbde0 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -187,6 +187,7 @@ int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
            !IN_LINKLOCAL(htonl((m)->yiaddr)) &&                        \
            get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
 struct rt *get_option_routes(const struct dhcp_message *, const char *, int *);
+ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
 ssize_t configure_env(char **, const char *, const struct dhcp_message *,
     const struct if_options *);
 
index 47944883d9ef962c81ee191c90e3d9a39224384d..b68d5dcd282fb5dae36b3bd428fa66b13e80e152 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 9, 2011
+.Dd December 15, 2011
 .Dt DHCPCD 8 SMM
 .Os
 .Sh NAME
@@ -99,6 +99,11 @@ changes.
 .Nm
 is also an implementation of the BOOTP client specified in
 .Li RFC 951 .
+.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.
 .Ss Local Link configuration
 If
 .Nm
@@ -571,7 +576,7 @@ running on the
 .Xr fnmatch 3
 .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 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 5969, RFC 6106.
 .Sh AUTHORS
 .An Roy Marples Aq roy@marples.name
 .Sh BUGS
index d9edab93ad9ad8c619a0fc8a8ff7caa349d6bb8a..75fd3c5bbea2db5ea816d9dc16a9f7ff85b7323f 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -67,6 +67,7 @@ const char copyright[] = "Copyright (c) 2006-2011 Roy Marples";
 #include "if-options.h"
 #include "if-pref.h"
 #include "ipv4ll.h"
+#include "ipv6rs.h"
 #include "net.h"
 #include "signals.h"
 
@@ -93,7 +94,7 @@ static char **ifv;
 static int ifc;
 static char *cffile;
 static char *pidfile;
-static int linkfd = -1;
+static int linkfd = -1, ipv6rsfd = -1;
 
 struct dhcp_op {
        uint8_t value;
@@ -214,7 +215,7 @@ handle_exit_timeout(_unused void *arg)
 }
 
 void
-drop_config(struct interface *iface, const char *reason)
+drop_dhcp(struct interface *iface, const char *reason)
 {
        free(iface->state->old);
        iface->state->old = iface->state->new;
@@ -244,7 +245,13 @@ stop_interface(struct interface *iface)
 
        syslog(LOG_INFO, "%s: removing interface", iface->name);
        if (strcmp(iface->state->reason, "RELEASE") != 0)
-               drop_config(iface, "STOP");
+               drop_dhcp(iface, "STOP");
+       if (iface->ras) {
+               ipv6rs_free(iface);
+               iface->ras = NULL;
+               iface->state->reason = "ROUTERADVERT";
+               run_script(iface);
+       }
        close_sockets(iface);
        delete_timeout(NULL, iface);
        for (ifp = ifaces; ifp; ifp = ifp->next) {
@@ -348,7 +355,7 @@ send_message(struct interface *iface, int type,
                if (r == -1) {
                        syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
                        if (!(options & DHCPCD_TEST))
-                               drop_config(iface, "FAIL");
+                               drop_dhcp(iface, "FAIL");
                        close_sockets(iface);
                        delete_timeout(NULL, iface);
                        callback = NULL;
@@ -407,7 +414,7 @@ start_expire(void *arg)
 
        syslog(LOG_ERR, "%s: lease expired", iface->name);
        delete_timeout(NULL, iface);
-       drop_config(iface, "EXPIRE");
+       drop_dhcp(iface, "EXPIRE");
        unlink(iface->leasefile);
        if (iface->carrier != LINK_DOWN)
                start_interface(iface);
@@ -504,7 +511,7 @@ handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp, const struct i
                /* We should restart on a NAK */
                log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
                if (!(options & DHCPCD_TEST)) {
-                       drop_config(iface, "NAK");
+                       drop_dhcp(iface, "NAK");
                        unlink(iface->leasefile);
                }
                close_sockets(iface);
@@ -734,7 +741,7 @@ send_release(struct interface *iface)
                ts.tv_sec = RELEASE_DELAY_S;
                ts.tv_nsec = RELEASE_DELAY_NS;
                nanosleep(&ts, NULL);
-               drop_config(iface, "RELEASE");
+               drop_dhcp(iface, "RELEASE");
        }
        unlink(iface->leasefile);
 }
@@ -903,7 +910,13 @@ 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);
-                       drop_config(iface, "NOCARRIER");
+                       drop_dhcp(iface, "NOCARRIER");
+                       if (iface->ras) {
+                               ipv6rs_free(iface);
+                               iface->ras = NULL;
+                               iface->state->reason = "ROUTERADVERT";
+                               run_script(iface);
+                       }
                }
        } else if (carrier == 1 && !(~iface->flags & (IFF_UP | IFF_RUNNING))) {
                if (iface->carrier != LINK_UP) {
@@ -1147,6 +1160,9 @@ start_interface(void *arg)
        free(iface->state->offer);
        iface->state->offer = NULL;
 
+       if (ifo->options & DHCPCD_IPV6RS)
+               ipv6rs_start(iface);
+
        if (iface->state->arping_index < ifo->arping_len) {
                start_arping(iface);
                return;
@@ -1162,7 +1178,7 @@ start_interface(void *arg)
        if (iface->hwlen == 0 && ifo->clientid[0] == '\0') {
                syslog(LOG_WARNING, "%s: needs a clientid to configure",
                    iface->name);
-               drop_config(iface, "FAIL");
+               drop_dhcp(iface, "FAIL");
                close_sockets(iface);
                delete_timeout(NULL, iface);
                return;
@@ -1321,7 +1337,7 @@ handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
                                syslog(LOG_INFO,
                                    "%s: expiring for new hardware address",
                                    ifp->name);
-                               drop_config(ifp, "EXPIRE");
+                               drop_dhcp(ifp, "EXPIRE");
                        }
                        memcpy(ifp->hwaddr, hwaddr, hwlen);
                        ifp->hwlen = hwlen;
@@ -1359,7 +1375,7 @@ handle_ifa(int type, const char *ifname,
        if (type == RTM_DELADDR) {
                if (ifp->state->new &&
                    ifp->state->new->yiaddr == addr->s_addr)
-                       drop_config(ifp, "EXPIRE");
+                       drop_dhcp(ifp, "EXPIRE");
                return;
        }
 
@@ -1418,7 +1434,7 @@ if_reboot(struct interface *iface, int argc, char **argv)
            (opt & (DHCPCD_INFORM | DHCPCD_STATIC) &&
                !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))))
        {
-               drop_config(iface, "EXPIRE");
+               drop_dhcp(iface, "EXPIRE");
        } else {
                free(iface->state->offer);
                iface->state->offer = NULL;
@@ -1525,7 +1541,7 @@ handle_signal(_unused void *arg)
        if (options & DHCPCD_TEST)
                exit(EXIT_FAILURE);
 
-       /* As drop_config could re-arrange the order, we do it like this. */
+       /* As drop_dhcp could re-arrange the order, we do it like this. */
        for (;;) {
                /* Be sane and drop the last config first */
                ifl = NULL;
@@ -1966,6 +1982,24 @@ main(int argc, char **argv)
                        add_event(linkfd, handle_link, NULL);
        }
 
+#if 0
+       if (options & DHCPCD_IPV6RS && disable_rtadv() == -1) {
+               syslog(LOG_ERR, "ipv6rs: %m");
+               options &= ~DHCPCD_IPV6RS;
+       }
+#endif
+
+       if (options & DHCPCD_IPV6RS) {
+               ipv6rsfd = ipv6rs_open();
+               if (ipv6rsfd == -1) {
+                       syslog(LOG_ERR, "ipv6rs: %m");
+                       options &= ~DHCPCD_IPV6RS;
+               } else {
+                       add_event(ipv6rsfd, ipv6rs_handledata, NULL);
+//                     atexit(restore_rtadv);
+               }
+       }
+
        ifc = argc - optind;
        ifv = argv + optind;
 
index 0c6ec6931bd20d4c24be9dd5657c2b09fd405f2f..aa94b8a69c69c3e331cb553f476ffd6cd9100c62 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd November 22, 2011
+.Dd December 15, 2011
 .Dt DHCPCD.CONF 5 SMM
 .Os
 .Sh NAME
@@ -173,6 +173,8 @@ See
 .Rs
 .%T "RFC 3927"
 .Re
+.It Ic noipv6rs
+Disable solicition of IPv6 Router Advertisements.
 .It Ic nolink
 Don't receive link messages about carrier status.
 You should only set this for buggy interface drivers.
index 1f6ae059988a64339985af81f087ccfe4b3be9a2..0ca4b68b1ca302d917a75a78ffb7faad70db1b04 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -30,6 +30,7 @@
 
 #include <sys/socket.h>
 #include <net/if.h>
+#include <netinet/in.h>
 
 #include <limits.h>
 
@@ -81,6 +82,27 @@ 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];
+       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;
+       struct ra *next;
+};
+
 struct interface {
        char name[IF_NAMESIZE];
        struct if_state *state;
@@ -109,6 +131,11 @@ struct interface {
 
        unsigned char *clientid;
 
+       unsigned char *rs;
+       size_t rslen;
+       int rsprobes;
+       struct ra *ras;
+
        struct interface *next;
 };
 
@@ -138,7 +165,8 @@ void start_expire(void *);
 void send_decline(struct interface *);
 void open_sockets(struct interface *);
 void close_sockets(struct interface *);
-void drop_config(struct interface *, const char *);
+void drop_dhcp(struct interface *, const char *);
+void drop_interface(struct interface *, const char *);
 int select_profile(struct interface *, const char *);
 
 #endif
index 8211e7896f9747a78bc5b8097af0fb05e4065da7..fbf8485c36d389d1c4f17d1b04c2dc2f874395d8 100644 (file)
@@ -53,6 +53,7 @@
 #define O_ARPING       O_BASE + 1
 #define O_FALLBACK     O_BASE + 2
 #define O_DESTINATION  O_BASE + 3
+#define O_NOIPV6RS     O_BASE + 4
 
 const struct option cf_options[] = {
        {"background",      no_argument,       NULL, 'b'},
@@ -103,6 +104,7 @@ const struct option cf_options[] = {
        {"arping",          required_argument, NULL, O_ARPING},
        {"destination",     required_argument, NULL, O_DESTINATION},
        {"fallback",        required_argument, NULL, O_FALLBACK},
+       {"noipv6rs",        no_argument,       NULL, O_NOIPV6RS},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -740,6 +742,9 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                free(ifo->fallback);
                ifo->fallback = xstrdup(arg);
                break;
+       case O_NOIPV6RS:
+               ifo->options &=~ DHCPCD_IPV6RS;
+               break;
        default:
                return 0;
        }
@@ -783,8 +788,8 @@ read_config(const char *file,
 
        /* Seed our default options */
        ifo = xzalloc(sizeof(*ifo));
-       ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
-       ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
+       ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE | DHCPCD_LINK;
+       ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_IPV6RS;
        ifo->timeout = DEFAULT_TIMEOUT;
        ifo->reboot = DEFAULT_REBOOT;
        ifo->metric = -1;
index 9fcb64bd4ca391969856a43ae28ed18548f7c3b1..85459d16054ecb6ae9b26a145454e45a309413c3 100644 (file)
@@ -77,6 +77,7 @@
 #define DHCPCD_XID_HWADDR      (1 << 28)
 #define DHCPCD_BROADCAST       (1 << 29)
 #define DHCPCD_DUMPLEASE       (1 << 30)
+#define DHCPCD_IPV6RS          (1 << 31)
 
 extern const struct option cf_options[];
 
index 7f4369ce531e039e409c4b3dd972d546dfb986e3..dfe692af9ba0b4131fb69de2b2ef39e9d0ed4c31 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -131,7 +131,7 @@ handle_ipv4ll_failure(void *arg)
                        syslog(LOG_DEBUG,
                            "%s: IPv4LL %d second defence failed",
                            iface->name, DEFEND_INTERVAL);
-                       drop_config(iface, "EXPIRE");
+                       drop_dhcp(iface, "EXPIRE");
                        iface->state->conflicts = -1;
                } else {
                        syslog(LOG_DEBUG, "%s: defended IPv4LL address",
diff --git a/ipv6rs.c b/ipv6rs.c
new file mode 100644 (file)
index 0000000..931c24c
--- /dev/null
+++ b/ipv6rs.c
@@ -0,0 +1,704 @@
+/* 
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.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 "bind.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcpcd.h"
+#include "eloop.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 */
+
+#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
+
+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];
+
+int
+ipv6rs_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_ROUTER_ADVERT, &filt);
+       if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER,
+               &filt, sizeof(filt)) == -1)
+               return -1;
+
+       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
+ipv6rs_makeprobe(struct interface *ifp)
+{
+       struct nd_router_solicit *rs;
+       struct nd_opt_hdr *nd;
+
+       ifp->rslen = sizeof(*rs) + ROUNDUP8(ifp->hwlen + 2);
+       ifp->rs = xzalloc(ifp->rslen);
+       if (ifp->rs == NULL)
+               return -1;
+       rs = (struct nd_router_solicit *)ifp->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 *)(ifp->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 sockaddr_in6 dst;
+       struct cmsghdr *cm;
+       struct in6_pktinfo pi;
+       int hoplimit = HOPLIMIT;
+
+       dst = allrouters;
+       //dst.sin6_scope_id = ifp->linkid;
+
+       ipv6rs_makeprobe(ifp);
+       sndhdr.msg_name = (caddr_t)&dst;
+       sndhdr.msg_iov[0].iov_base = ifp->rs;
+       sndhdr.msg_iov[0].iov_len = ifp->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 = if_nametoindex(ifp->name);
+       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_INFO, "%s: sending IPv6 Router Solicitation", ifp->name);
+       if (sendmsg(sock, &sndhdr, 0) == -1)
+               syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
+
+       if (++ifp->rsprobes < MAX_RTR_SOLICITATIONS)
+               add_timeout_sec(RTR_SOLICITATION_INTERVAL,
+                   ipv6rs_sendprobe, ifp);
+       else
+               syslog(LOG_INFO, "%s: no IPv6 Routers available", ifp->name);
+}
+
+static void
+ipv6rs_sort(struct interface *ifp)
+{
+       struct ra *rap, *sorted, *ran, *rat;
+
+       if (ifp->ras == NULL || ifp->ras->next == NULL)
+               return;
+
+       /* 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;
+               }
+               /* 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;
+                       }
+               }
+               /* We must be at the end */
+               if (!rat->next) {
+                       rat->next = rap;
+                       rap->next = NULL;
+               }
+       }
+}
+
+void
+ipv6rs_handledata(_unused void *arg)
+{
+       ssize_t len, l, 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 *radvert;
+       struct nd_opt_prefix_info *pi;
+       struct nd_opt_mtu *mtu;
+       struct nd_opt_rdnss *rdnss;
+       struct nd_opt_dnssl *dnssl;
+       uint32_t lifetime;
+       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;
+       char *opt;
+       struct timeval expire;
+
+       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 recieved from non local IPv6 address %s",
+                   sfrom);
+               return;
+       }
+
+       for (ifp = ifaces; ifp; ifp = ifp->next)
+               if (if_nametoindex(ifp->name) == (unsigned int)pkt.ipi6_ifindex)
+                       break;
+       if (ifp == NULL) {
+               syslog(LOG_ERR,"received RA for unexpected interface from %s",
+                   sfrom);
+               return;
+       }
+
+       syslog(LOG_INFO, "%s: Router Advertisement from %s", ifp->name, sfrom);
+       delete_timeouts(ifp, NULL);
+       radvert = (struct nd_router_advert *)icp;
+
+       for (rap = ifp->ras; rap; rap = rap->next) {
+               if (memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr,
+                   sizeof(rap->from.s6_addr)) == 0)
+                       break;
+       }
+       if (rap == NULL) {
+               rap = xmalloc(sizeof(*rap));
+               rap->next = ifp->ras;
+               rap->options = NULL;
+               ifp->ras = rap;
+               memcpy(rap->from.s6_addr, from.sin6_addr.s6_addr,
+                   sizeof(rap->from.s6_addr));
+               strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
+       }
+
+       get_monotonic(&rap->received);
+       rap->lifetime = ntohl(icp->icmp6_dataun.icmp6_un_data32);
+
+       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 *)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;
+                       }
+                       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);
+                       }
+                       break;
+
+               case ND_OPT_MTU:
+                       mtu = (struct nd_opt_mtu *)p;
+                       snprintf(buf, sizeof(buf), "%d",
+                           ntohl(mtu->nd_opt_mtu_mtu));
+                       opt = xstrdup(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) {
+                               memcpy(&addr.s6_addr, op, sizeof(addr.s6_addr));
+                               cbp = inet_ntop(AF_INET6, &addr,
+                                   ntopbuf, INET6_ADDRSTRLEN);
+                               if (cbp == NULL) {
+                                       syslog(LOG_ERR,
+                                           "%s: invalid RDNSS address",
+                                           ifp->name);
+                               } else {
+                                       if (opt) {
+                                               l = strlen(opt);
+                                               opt = xrealloc(opt,
+                                                       l + strlen(cbp) + 2);
+                                               opt[l] = ' ';
+                                               strcpy(opt + l + 1, cbp);
+                                               opt[l + strlen(cbp) + l + 1] =
+                                                   '\0';
+                                       } else
+                                               opt = xstrdup(cbp);
+                               }
+                               op += sizeof(addr.s6_addr);
+                       }
+                       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 {
+                               opt = xmalloc(l);
+                               decode_rfc3397(opt, l, n, op);
+                       }
+                       break;
+               }
+
+               if (opt == NULL)
+                       continue;
+               for (raol = NULL, rao = rap->options;
+                   rao;
+                   raol = rao, rao = rao->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;
+                               free(rao->option);
+                               free(rao);
+                       }
+                       continue;
+               }
+
+               if (rao == NULL) {
+                       rao = xmalloc(sizeof(*rao));
+                       rao->next = rap->options;
+                       rap->options = rao;
+                       rao->type = ndo->nd_opt_type;
+                       rao->option = opt;
+               }
+               if (lifetime == ~0U) {
+                       rao->expire.tv_sec = 0;
+                       rao->expire.tv_usec = 0;
+               } else {
+                       expire.tv_sec = lifetime;
+                       expire.tv_usec = 0;
+                       timeradd(&rap->received, &expire, &rao->expire);
+               }
+       }
+
+       ipv6rs_sort(ifp);
+
+       if (options & DHCPCD_TEST)
+               ifp->state->reason = "TEST";
+       else
+               ifp->state->reason = "ROUTERADVERT";
+       run_script(ifp);
+       if (options & DHCPCD_TEST)
+               exit(EXIT_SUCCESS);
+
+       delete_timeouts(ifp, NULL);
+       ipv6rs_expire(ifp);
+       daemonise();
+}
+
+ssize_t
+ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
+{
+       ssize_t l;
+       struct timeval now;
+       const struct ra *rap;
+       const struct ra_opt *rao;
+       int i;
+       char buffer[32], buffer2[32];
+       const char *optn;
+       
+       l = 0;
+       get_monotonic(&now);
+       for (rap = ifp->ras, i = 1; rap; rap = rap->next, i++) {
+               if (env) {
+                       snprintf(buffer, sizeof(buffer),
+                           "ra%d_from", i);
+                       setvar(&env, prefix, buffer, rap->sfrom);
+               }
+               l++;
+               for (rao = rap->options; rao; rao = rao->next) {
+                       if (rao->expire.tv_sec != 0 &&
+                           rao->expire.tv_usec != 0 &&
+                           timercmp(&now, &rao->expire, >))
+                       {
+                               syslog(LOG_INFO, "%s: %s: expired option %d",
+                                   ifp->name, rap->sfrom, rao->type);
+                               continue;
+                       }
+                       if (rao->option == NULL)
+                               continue;
+                       if (env == NULL) {
+                               switch (rao->type) {
+                               case ND_OPT_PREFIX_INFORMATION:
+                                       l += 4;
+                                       break;
+                               default:
+                                       l++;
+                               }
+                               continue;
+                       }
+                       switch (rao->type) {
+                       case ND_OPT_PREFIX_INFORMATION:
+                               optn = "prefix";
+                               break;
+                       case ND_OPT_MTU:
+                               optn = "mtu";
+                               break;
+                       case ND_OPT_RDNSS:
+                               optn = "rdnss";
+                               break;
+                       case ND_OPT_DNSSL:
+                               optn = "dnssl";
+                               break;
+                       default:
+                               continue;
+                       }
+                       snprintf(buffer, sizeof(buffer), "ra%d_%s", i, optn);
+                       setvar(&env, prefix, buffer, rao->option);
+                       l++;
+                       switch (rao->type) {
+                       case ND_OPT_PREFIX_INFORMATION:
+                               snprintf(buffer, sizeof(buffer),
+                                   "ra%d_prefix_len", i);
+                               snprintf(buffer2, sizeof(buffer2),
+                                   "%d", rap->prefix_len);
+                               setvar(&env, prefix, buffer, buffer2);
+
+                               snprintf(buffer, sizeof(buffer),
+                                   "ra%d_prefix_vltime", i);
+                               snprintf(buffer2, sizeof(buffer2),
+                                   "%d", rap->prefix_vltime);
+                               setvar(&env, prefix, buffer, buffer2);
+
+                               snprintf(buffer, sizeof(buffer),
+                                   "ra%d_prefix_pltime", i);
+                               snprintf(buffer2, sizeof(buffer2),
+                                   "%d", rap->prefix_pltime);
+                               setvar(&env, prefix, buffer, buffer2);
+                               l += 3;
+                               break;
+                       }
+               
+               }
+       }
+
+       if (env)
+               setvard(&env, prefix, "ra_count", i - 1);
+       l++;
+       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;
+
+       for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) {
+               ipv6rs_free_opts(rap);
+               free(rap);
+       }
+}
+
+void
+ipv6rs_expire(void *arg)
+{
+       struct interface *ifp;
+       struct ra *rap, *ran, *ral;
+       struct timeval now, lt, expire, next;
+       int expired;
+       uint32_t expire_secs;
+
+       ifp = arg;
+       get_monotonic(&now);
+       expired = 0;
+       expire_secs = ~0U;
+       timerclear(&next);
+
+       for (rap = ifp->ras, ral = NULL;
+           rap && (ran = rap->next, 1);
+           ral = rap, rap = ran)
+       {
+               lt.tv_sec = rap->lifetime;
+               lt.tv_usec = 0;
+               timeradd(&rap->received, &lt, &expire);
+               if (timercmp(&now, &expire, >)) {
+                       expired = 1;
+                       if (ral)
+                               ral->next = ran;
+                       else
+                               ifp->ras = ran;
+                       ipv6rs_free_opts(rap);
+                       free(rap);
+               } else {
+                       timersub(&now, &expire, &lt);
+                       if (!timerisset(&next) || timercmp(&next, &lt, >))
+                               next = lt;
+               }
+       }
+
+       if (timerisset(&next))
+               add_timeout_tv(&next, ipv6rs_expire, ifp);
+
+       if (expired) {
+               ifp->state->reason = "ROUTERADVERT";
+               run_script(ifp);
+       }
+
+}
+
+int
+ipv6rs_start(struct interface *ifp)
+{
+
+       delete_timeouts(ifp, NULL);
+
+       /* Always make a new probe as the underlying hardware
+        * address could have changed. */
+       free(ifp->rs);
+       ipv6rs_makeprobe(ifp);
+       if (ifp->rs == NULL)
+               return -1;
+
+       ifp->rsprobes = 0;
+       ipv6rs_sendprobe(ifp);
+       return 0;
+}
diff --git a/ipv6rs.h b/ipv6rs.h
new file mode 100644 (file)
index 0000000..0856acb
--- /dev/null
+++ b/ipv6rs.h
@@ -0,0 +1,37 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 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 IPV6RS_H
+#define IPV6RS_H
+
+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_expire(void *arg);
+#endif