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
TARGET=
DEBUG=
FORK=
+STATIC=
INCLUDEDIR=
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;;
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
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
#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
dhcp = ra = 0;
if (strcmp(reason, "TEST") == 0) {
- if (iface->ras)
+ if (ipv6rs_has_ra(iface))
ra = 1;
else
dhcp = 1;
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 {
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;
}
#define CONFIG_H
#define PACKAGE "dhcpcd"
-#define VERSION "5.5.6"
+#define VERSION "5.6.0"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
.\" 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
.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
.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
.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
#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"
#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;
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;
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;
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);
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)) {
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));
for (ifp = ifaces; ifp; ifp = ifp->next)
if (strcmp(argv[opt], ifp->name) == 0) {
len++;
- if (ifp->ras)
+ if (ipv6rs_has_ra(ifp))
len++;
}
}
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");
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) {
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;
.\" 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
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 .
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
#include <sys/socket.h>
#include <net/if.h>
-#include <netinet/in.h>
+//#include <netinet/in.h>
#include <limits.h>
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;
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;
#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__
#include "configure.h"
#include "dhcp.h"
#include "if-options.h"
+#include "ipv6.h"
#include "net.h"
#ifndef RT_ROUNDUP
const struct in_addr *netmask, const struct in_addr *broadcast,
int action)
{
- int retval;
struct ifaliasreq ifa;
union {
struct sockaddr *sa;
}
#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;
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));
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;
#include "common.h"
#include "configure.h"
#include "dhcp.h"
+#include "ipv6.h"
#include "net.h"
static int sock_fd;
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;
+}
#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'},
{"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'}
};
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;
}
#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[];
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 **);
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
* 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 */
} _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;
&filt, sizeof(filt)) == -1)
return -1;
+ set_cloexec(sock);
#if DEBUG_MEMORY
atexit(ipv6rs_cleanup);
#endif
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;
}
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)
{
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) {
}
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;
}
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;
}
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);
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;
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",
"%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;
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);
}
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)
}
}
- 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);
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();
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
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);
}
l++;
- for (rao = rap->options; rao; rao = rao->next) {
+ TAILQ_FOREACH(rao, &rap->options, next) {
if (rao->option == NULL)
continue;
if (env == NULL) {
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),
l += 3;
break;
}
-
+#endif
}
}
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;
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, <, &expire);
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, <);
if (!timerisset(&next) || timercmp(&next, <, >))
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, <);
if (timerisset(&next))
add_timeout_tv(&next, ipv6rs_expire, ifp);
- if (expired)
+ if (expired) {
+ ipv6_build_routes();
run_script_reason(ifp, "ROUTERADVERT");
+ }
}
int
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");
+ }
+}
#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
#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;
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]);
#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
# define ARPHRD_INFINIBAND 32
#endif
-
/* Work out if we have a private address or not
* 10/8
* 172.16/12
#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,
#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 */
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;
}
#include <syslog.h>
#include "common.h"
+#include "if-options.h"
#include "platform.h"
static const char *mproc =
#endif
;
+char **restore;
+ssize_t nrestore;
+
char *
hardware_platform(void)
{
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) {