mirror of
https://github.com/rsmarples/dhcpcd.git
synced 2024-11-23 18:14:09 +08:00
Add an implementation of an IPv6 Router Solicitor as specified in
RFC6016 with regards to RDNSS and DNSSL.
This commit is contained in:
parent
1011354f32
commit
91cd732493
2
Makefile
2
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
|
||||
|
21
common.c
21
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)
|
||||
{
|
||||
|
2
common.h
2
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);
|
||||
|
20
configure.c
20
configure.c
@ -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) {
|
||||
|
12
dhcp.c
12
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)
|
||||
|
1
dhcp.h
1
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 *);
|
||||
|
||||
|
@ -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
|
||||
|
60
dhcpcd.c
60
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;
|
||||
|
||||
|
@ -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.
|
||||
|
30
dhcpcd.h
30
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
|
||||
|
@ -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;
|
||||
|
@ -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[];
|
||||
|
||||
|
2
ipv4ll.c
2
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",
|
||||
|
704
ipv6rs.c
Normal file
704
ipv6rs.c
Normal file
@ -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, <, &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, <);
|
||||
if (!timerisset(&next) || timercmp(&next, <, >))
|
||||
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;
|
||||
}
|
37
ipv6rs.h
Normal file
37
ipv6rs.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user