Merge pull request #1843 from teg/ndisc

ndisc and dhcpv6
This commit is contained in:
Daniel Mack 2015-11-11 16:03:05 +01:00
commit 3116c225d2
20 changed files with 585 additions and 224 deletions

View File

@ -227,7 +227,14 @@
<literal>yes</literal>, <literal>no</literal>,
<literal>ipv4</literal>, or <literal>ipv6</literal>.</para>
<para>Please note that, by default, the domain name
<para>Note that DHCPv6 will by default be triggered by Router
Advertisment, if that is enabled, regardless of this parameter.
By enabling DHCPv6 support explicitly, the DHCPv6 client will
be started regardless of the presence of routers on the link,
or what flags the routers pass. See
<literal>IPv6AcceptRouterAdvertisements=</literal>.</para>
<para>Furthermore, note that by default the domain name
specified through DHCP is not used for name resolution.
See option <option>UseDomains=</option> below.</para>
</listitem>
@ -414,6 +421,9 @@
When unset, the kernel default is used, and router
advertisements are accepted only when local forwarding
is disabled for that interface.
When router advertisements are accepted, they will
trigger the start of the DHCPv6 client if the relevant
flags are passed, or if no routers are found on the link.
Takes a boolean. If true, router advertisements are
accepted, when false, router advertisements are ignored,
independently of the local forwarding state.</para>

View File

@ -23,19 +23,20 @@
/* Missing glibc definitions to access certain kernel APIs */
#include <sys/resource.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <linux/oom.h>
#include <linux/input.h>
#include <linux/if_link.h>
#include <linux/loop.h>
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/if_link.h>
#include <linux/input.h>
#include <linux/loop.h>
#include <linux/neighbour.h>
#include <linux/oom.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@ -900,6 +901,10 @@ static inline int setns(int fd, int nstype) {
#define NDA_MAX (__NDA_MAX - 1)
#endif
#ifndef RTA_PREF
#define RTA_PREF 20
#endif
#ifndef IPV6_UNICAST_IF
#define IPV6_UNICAST_IF 76
#endif

View File

@ -1115,11 +1115,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
return 0;
}
int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
return client->state != DHCP6_STATE_STOPPED;
}
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
int r = 0;
enum DHCP6State state = DHCP6_STATE_SOLICITATION;

View File

@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "async.h"
#include "icmp6-util.h"
#include "in-addr-util.h"
#include "list.h"
#include "socket-util.h"
@ -47,6 +48,12 @@ enum NDiscState {
#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
#define NDISC_OPT_LEN_UNITS 8
#define ND_RA_FLAG_PREF 0x18
#define ND_RA_FLAG_PREF_LOW 0x03
#define ND_RA_FLAG_PREF_MEDIUM 0x0
#define ND_RA_FLAG_PREF_HIGH 0x1
#define ND_RA_FLAG_PREF_INVALID 0x2
typedef struct NDiscPrefix NDiscPrefix;
struct NDiscPrefix {
@ -75,6 +82,9 @@ struct sd_ndisc {
sd_event_source *recv;
sd_event_source *timeout;
int nd_sent;
sd_ndisc_router_callback_t router_callback;
sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
sd_ndisc_callback_t callback;
void *userdata;
};
@ -119,15 +129,17 @@ static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
return 0;
}
static void ndisc_notify(sd_ndisc *nd, int event) {
if (nd->callback)
nd->callback(nd, event, nd->userdata);
}
int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t callback,
void *userdata) {
int sd_ndisc_set_callback(sd_ndisc *nd,
sd_ndisc_router_callback_t router_callback,
sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
sd_ndisc_callback_t callback,
void *userdata) {
assert(nd);
nd->router_callback = router_callback;
nd->prefix_onlink_callback = prefix_onlink_callback;
nd->prefix_autonomous_callback = prefix_autonomous_callback;
nd->callback = callback;
nd->userdata = userdata;
@ -320,7 +332,7 @@ static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
const struct nd_opt_prefix_info *prefix_opt) {
NDiscPrefix *prefix;
uint32_t lifetime;
uint32_t lifetime_valid, lifetime_preferred;
usec_t time_now;
char time_string[FORMAT_TIMESPAN_MAX];
int r;
@ -331,10 +343,17 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
if (len < prefix_opt->nd_opt_pi_len)
return -ENOMSG;
if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
return 0;
lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
return 0;
lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
if (lifetime_valid < lifetime_preferred)
return 0;
r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
prefix_opt->nd_opt_pi_prefix_len, &prefix);
@ -357,8 +376,8 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
prefix->len, lifetime,
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC));
prefix->len, lifetime_valid,
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
LIST_PREPEND(prefixes, nd->prefixes, prefix);
@ -378,17 +397,24 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
prefix->len, lifetime,
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC));
prefix->len, lifetime_valid,
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
}
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
prefix->valid_until = time_now + lifetime * USEC_PER_SEC;
prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
return r;
if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
nd->userdata);
return 0;
}
static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
@ -453,11 +479,13 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
}
static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_ndisc *nd = userdata;
int r, buflen = 0;
ssize_t len;
_cleanup_free_ struct nd_router_advert *ra = NULL;
int event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE;
sd_ndisc *nd = userdata;
int r, buflen = 0, pref, stateful;
union sockaddr_union router = {};
socklen_t router_len = sizeof(router);
unsigned lifetime;
ssize_t len;
assert(s);
assert(nd);
@ -471,9 +499,12 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
if (!ra)
return -ENOMEM;
len = read(fd, ra, buflen);
len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
if (len < 0) {
log_ndisc(nd, "Could not receive message from UDP socket: %m");
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
return 0;
} else if (router_len != sizeof(router.in6) && router_len != 0) {
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
return 0;
}
@ -487,26 +518,33 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER;
stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3;
if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED;
log_ndisc(nd, "Received Router Advertisement flags %s/%s",
ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
if (event != SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE) {
r = ndisc_ra_parse(nd, ra, len);
if (r < 0) {
log_ndisc(nd, "Could not parse Router Advertisement: %s",
strerror(-r));
return 0;
}
switch (pref) {
case ND_RA_FLAG_PREF_LOW:
case ND_RA_FLAG_PREF_HIGH:
break;
default:
pref = ND_RA_FLAG_PREF_MEDIUM;
break;
}
ndisc_notify(nd, event);
lifetime = be16toh(ra->nd_ra_router_lifetime);
log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
lifetime);
r = ndisc_ra_parse(nd, ra, len);
if (r < 0) {
log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r));
return 0;
}
if (nd->router_callback)
nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata);
return 0;
}
@ -525,7 +563,8 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
nd->timeout = sd_event_source_unref(nd->timeout);
if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
ndisc_notify(nd, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
if (nd->callback)
nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
} else {
if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
@ -549,7 +588,8 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
next_timeout, 0,
ndisc_router_solicitation_timeout, nd);
if (r < 0) {
ndisc_notify(nd, r);
/* we cannot continue if we are unable to rearm the timer */
sd_ndisc_stop(nd);
return 0;
}
@ -575,6 +615,9 @@ int sd_ndisc_stop(sd_ndisc *nd) {
nd->state = NDISC_STATE_IDLE;
if (nd->callback)
nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
return 0;
}

View File

@ -85,29 +85,28 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
return send_ra_function(0);
}
static void test_rs_done(sd_ndisc *nd, int event, void *userdata) {
static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
sd_event *e = userdata;
static int idx = 0;
struct {
uint8_t flag;
int event;
} flag_event[] = {
{ 0, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE },
{ ND_RA_FLAG_OTHER, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER },
{ ND_RA_FLAG_MANAGED, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED }
static unsigned idx = 0;
uint8_t flags_array[] = {
0,
0,
0,
ND_RA_FLAG_OTHER,
ND_RA_FLAG_MANAGED
};
uint32_t mtu;
assert_se(nd);
assert_se(event == flag_event[idx].event);
assert_se(flags == flags_array[idx]);
idx++;
if (verbose)
printf(" got event %d\n", event);
printf(" got event 0x%02x\n", flags);
if (idx < 3) {
send_ra(flag_event[idx].flag);
if (idx < ELEMENTSOF(flags_array)) {
send_ra(flags_array[idx]);
return;
}
@ -135,7 +134,7 @@ static void test_rs(void) {
assert_se(sd_ndisc_set_index(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_rs_done, e) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0);
assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
time_now + 2 *USEC_PER_SEC, 0,

View File

@ -83,20 +83,20 @@ static const NLTypeSystem empty_type_system = {
.types = empty_types,
};
static const NLType rtnl_link_info_data_veth_types[VETH_INFO_MAX + 1] = {
static const NLType rtnl_link_info_data_veth_types[] = {
[VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
};
static const NLType rtnl_link_info_data_ipvlan_types[IFLA_IPVLAN_MAX + 1] = {
static const NLType rtnl_link_info_data_ipvlan_types[] = {
[IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = {
static const NLType rtnl_link_info_data_macvlan_types[] = {
[IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 },
[IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = {
static const NLType rtnl_link_bridge_management_types[] = {
[IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
[IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 },
/*
@ -105,7 +105,7 @@ static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = {
*/
};
static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = {
static const NLType rtnl_link_info_data_bridge_types[] = {
[IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
[IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
[IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
@ -114,7 +114,7 @@ static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = {
[IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
static const NLType rtnl_link_info_data_vlan_types[] = {
[IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
/*
[IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) },
@ -124,7 +124,7 @@ static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
[IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
static const NLType rtnl_link_info_data_vxlan_types[] = {
[IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
[IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
@ -151,7 +151,7 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
[IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
};
static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = {
static const NLType rtnl_bond_arp_target_types[] = {
[BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 },
[BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 },
[BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 },
@ -175,7 +175,7 @@ static const NLTypeSystem rtnl_bond_arp_type_system = {
.types = rtnl_bond_arp_target_types,
};
static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
static const NLType rtnl_link_info_data_bond_types[] = {
[IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 },
[IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 },
[IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 },
@ -201,7 +201,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
[IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED },
};
static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = {
static const NLType rtnl_link_info_data_iptun_types[] = {
[IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
@ -220,7 +220,7 @@ static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = {
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = {
static const NLType rtnl_link_info_data_ipgre_types[] = {
[IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 },
[IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 },
@ -239,7 +239,7 @@ static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = {
[IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = {
static const NLType rtnl_link_info_data_ipvti_types[] = {
[IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 },
[IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 },
@ -247,7 +247,7 @@ static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = {
[IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
};
static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
static const NLType rtnl_link_info_data_ip6tnl_types[] = {
[IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
@ -259,7 +259,7 @@ static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
};
/* these strings must match the .kind entries in the kernel */
static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_MAX] = {
static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
[NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge",
[NL_UNION_LINK_INFO_DATA_VLAN] = "vlan",
@ -282,7 +282,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_DATA_MAX] = {
static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
.types = rtnl_link_info_data_bond_types },
[NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
@ -328,7 +328,7 @@ static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
.match = IFLA_INFO_KIND,
};
static const NLType rtnl_link_info_types[IFLA_INFO_MAX + 1] = {
static const NLType rtnl_link_info_types[] = {
[IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING },
[IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union},
/*
@ -343,7 +343,7 @@ static const NLTypeSystem rtnl_link_info_type_system = {
.types = rtnl_link_info_types,
};
static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] = {
static const struct NLType rtnl_prot_info_bridge_port_types[] = {
[IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
[IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
[IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
@ -357,7 +357,7 @@ static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1]
[IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
};
static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = {
static const NLTypeSystem rtnl_prot_info_type_systems[] = {
[AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types),
.types = rtnl_prot_info_bridge_port_types },
};
@ -368,7 +368,7 @@ static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
.match_type = NL_MATCH_PROTOCOL,
};
static const struct NLType rtnl_af_spec_inet6_types[IFLA_INET6_MAX + 1] = {
static const struct NLType rtnl_af_spec_inet6_types[] = {
[IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 },
/*
IFLA_INET6_CONF,
@ -386,7 +386,7 @@ static const NLTypeSystem rtnl_af_spec_inet6_type_system = {
.types = rtnl_af_spec_inet6_types,
};
static const NLType rtnl_af_spec_types[AF_MAX + 1] = {
static const NLType rtnl_af_spec_types[] = {
[AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
};
@ -395,7 +395,7 @@ static const NLTypeSystem rtnl_af_spec_type_system = {
.types = rtnl_af_spec_types,
};
static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
static const NLType rtnl_link_types[] = {
[IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
@ -454,7 +454,7 @@ static const NLTypeSystem rtnl_link_type_system = {
/* IFA_FLAGS was defined in kernel 3.14, but we still support older
* kernels where IFA_MAX is lower. */
static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = {
static const NLType rtnl_address_types[] = {
[IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
@ -472,7 +472,7 @@ static const NLTypeSystem rtnl_address_type_system = {
.types = rtnl_address_types,
};
static const NLType rtnl_route_types[RTA_MAX + 1] = {
static const NLType rtnl_route_types[] = {
[RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_IIF] = { .type = NETLINK_TYPE_U32 },
@ -490,7 +490,11 @@ static const NLType rtnl_route_types[RTA_MAX + 1] = {
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
RTA_VIA,
RTA_NEWDST,
*/
[RTA_PREF] = { .type = NETLINK_TYPE_U8 },
};
static const NLTypeSystem rtnl_route_type_system = {
@ -498,7 +502,7 @@ static const NLTypeSystem rtnl_route_type_system = {
.types = rtnl_route_types,
};
static const NLType rtnl_neigh_types[NDA_MAX + 1] = {
static const NLType rtnl_neigh_types[] = {
[NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
[NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
[NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
@ -514,7 +518,7 @@ static const NLTypeSystem rtnl_neigh_type_system = {
.types = rtnl_neigh_types,
};
static const NLType rtnl_types[RTM_MAX + 1] = {
static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
[RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },

View File

@ -84,6 +84,35 @@ int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope)
return 0;
}
int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) {
struct rtmsg *rtm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
rtm = NLMSG_DATA(m->hdr);
rtm->rtm_flags = flags;
return 0;
}
int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) {
struct rtmsg *rtm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
assert_return(flags, -EINVAL);
rtm = NLMSG_DATA(m->hdr);
*flags = rtm->rtm_flags;
return 0;
}
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
struct rtmsg *rtm;

View File

@ -248,6 +248,8 @@ static int address_add_internal(Link *link, Set **addresses,
address->family = family;
address->in_addr = *in_addr;
address->prefixlen = prefixlen;
/* Consider address tentative until we get the real flags from the kernel */
address->flags = IFA_F_TENTATIVE;
r = set_ensure_allocated(addresses, &address_hash_ops);
if (r < 0)
@ -327,13 +329,13 @@ static int address_release(Address *address) {
int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) {
bool ready;
int r;
assert(address);
assert(cinfo);
ready = address_is_ready(address);
address->added = true;
address->flags = flags;
address->scope = scope;
address->cinfo = *cinfo;
@ -341,8 +343,17 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s
if (address->link) {
link_update_operstate(address->link);
if (!ready && address_is_ready(address))
if (!ready && address_is_ready(address)) {
link_check_ready(address->link);
if (address->family == AF_INET6 &&
in_addr_is_link_local(AF_INET6, &address->in_addr) &&
!address->link->ipv6ll_address) {
r = link_ipv6ll_gained(address->link);
if (r < 0)
return r;
}
}
}
return 0;
@ -769,5 +780,5 @@ int config_parse_label(const char *unit,
bool address_is_ready(const Address *a) {
assert(a);
return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
}

View File

@ -52,7 +52,6 @@ struct Address {
union in_addr_union in_addr;
union in_addr_union in_addr_peer;
bool added:1;
bool ip_masquerade_done:1;
LIST_FIELDS(Address, addresses);

View File

@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
int r;
assert(link);
assert(link->dhcp4_messages);
assert(link->dhcp4_messages > 0);
link->dhcp4_messages --;
@ -44,7 +44,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
link_enter_failed(link);
}
if (!link->dhcp4_messages) {
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
link_check_ready(link);
}

View File

@ -165,83 +165,82 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
link_check_ready(link);
}
int dhcp6_configure(Link *link, bool inf_req) {
int r, information_request;
int dhcp6_request_address(Link *link) {
int r, inf_req;
bool running;
assert_return(link, -EINVAL);
link->dhcp6_configured = false;
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request);
if (r < 0) {
log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m");
goto error;
}
if (information_request && !inf_req) {
r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0) {
log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m");
goto error;
}
r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m");
goto error;
}
}
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0 && r != -EALREADY) {
log_link_warning_errno(link, r, "Could not restart DHCPv6: %m");
goto error;
}
if (r == -EALREADY)
link->dhcp6_configured = true;
assert(link);
assert(link->dhcp6_client);
r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
if (r < 0)
return r;
if (!inf_req)
return 0;
r = sd_dhcp6_client_is_running(link->dhcp6_client);
if (r < 0)
return r;
else
running = !!r;
if (running) {
r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0)
return r;
}
r = sd_dhcp6_client_new(&link->dhcp6_client);
r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false);
if (r < 0)
return r;
if (running) {
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
return r;
}
return 0;
}
int dhcp6_configure(Link *link) {
sd_dhcp6_client *client = NULL;
int r;
assert(link);
r = sd_dhcp6_client_new(&client);
if (r < 0)
return r;
r = sd_dhcp6_client_attach_event(client, NULL, 0);
if (r < 0)
goto error;
r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
r = sd_dhcp6_client_set_information_request(client, true);
if (r < 0)
goto error;
return r;
r = sd_dhcp6_client_set_mac(link->dhcp6_client,
r = sd_dhcp6_client_set_mac(client,
(const uint8_t *) &link->mac,
sizeof (link->mac), ARPHRD_ETHER);
if (r < 0)
goto error;
r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
r = sd_dhcp6_client_set_index(client, link->ifindex);
if (r < 0)
goto error;
r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
link);
r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
if (r < 0)
goto error;
if (inf_req) {
r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true);
if (r < 0)
goto error;
}
link->dhcp6_client = client;
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
goto error;
return 0;
return r;
error:
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
error:
sd_dhcp6_client_unref(client);
return r;
}

View File

@ -124,6 +124,28 @@ static bool link_ipv6_forward_enabled(Link *link) {
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
}
bool link_ipv6_accept_ra_enabled(Link *link) {
if (link->flags & IFF_LOOPBACK)
return false;
if (!link->network)
return false;
/* If unset use system default (enabled if local forwarding is disabled.
* disabled if local forwarding is enabled).
* If set, ignore or enforce RA independent of local forwarding state.
*/
if (link->network->ipv6_accept_ra < 0)
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
return !link_ipv6_forward_enabled(link);
else if (link->network->ipv6_accept_ra > 0)
/* accept RA even if ip_forward is enabled */
return true;
else
/* ignore RA */
return false;
}
static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
if (link->flags & IFF_LOOPBACK)
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
@ -485,13 +507,13 @@ static int link_stop_clients(Link *link) {
r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m");
}
if(link->ndisc_router_discovery) {
if (link->dhcp6_client) {
k = sd_dhcp6_client_stop(link->dhcp6_client);
if (k < 0)
r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
}
if (link->dhcp6_client) {
k = sd_dhcp6_client_stop(link->dhcp6_client);
if (k < 0)
r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
}
if (link->ndisc_router_discovery) {
k = sd_ndisc_stop(link->ndisc_router_discovery);
if (k < 0)
r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m");
@ -581,6 +603,10 @@ void link_check_ready(Link *link) {
!link->ipv4ll_route)
return;
if (link_ipv6ll_enabled(link))
if (!link->ipv6ll_address)
return;
if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
!link->dhcp4_configured) ||
(link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
@ -589,6 +615,9 @@ void link_check_ready(Link *link) {
!link->dhcp4_configured && !link->dhcp6_configured))
return;
if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
return;
SET_FOREACH(a, link->addresses, i)
if (!address_is_ready(a))
return;
@ -1213,6 +1242,34 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) {
}
}
static int link_acquire_ipv6_conf(Link *link) {
int r;
assert(link);
if (link_dhcp6_enabled(link)) {
assert(link->dhcp6_client);
log_link_debug(link, "Acquiring DHCPv6 lease");
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
}
if (link_ipv6_accept_ra_enabled(link)) {
assert(link->ndisc_router_discovery);
log_link_debug(link, "Discovering IPv6 routers");
r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
return 0;
}
static int link_acquire_conf(Link *link) {
int r;
@ -1241,16 +1298,6 @@ static int link_acquire_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
}
if (link_dhcp6_enabled(link)) {
assert(link->ndisc_router_discovery);
log_link_debug(link, "Discovering IPv6 routers");
r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
if (link_lldp_enabled(link)) {
assert(link->lldp);
@ -1883,7 +1930,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
}
static int link_set_ipv6_accept_ra(Link *link) {
const char *p = NULL, *v = NULL;
const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
@ -1893,29 +1940,16 @@ static int link_set_ipv6_accept_ra(Link *link) {
if (link->flags & IFF_LOOPBACK)
return 0;
/* If unset use system default (enabled if local forwarding is disabled.
* disabled if local forwarding is enabled).
* If set, ignore or enforce RA independent of local forwarding state.
*/
if (link->network->ipv6_accept_ra < 0)
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
v = "1";
else if (link->network->ipv6_accept_ra > 0)
/* "2" means accept RA even if ip_forward is enabled */
v = "2";
else
/* "0" means ignore RA */
v = "0";
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
r = write_string_file(p, v, 0);
/* we handle router advertisments ourselves, tell the kernel to GTFO */
r = write_string_file(p, "0", 0);
if (r < 0) {
/* If the right value is set anyway, don't complain */
if (verify_one_line_file(p, v) > 0)
if (verify_one_line_file(p, "0") > 0)
return 0;
log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m");
log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m");
}
return 0;
@ -2041,6 +2075,12 @@ static int link_configure(Link *link) {
}
if (link_dhcp6_enabled(link)) {
r = dhcp6_configure(link);
if (r < 0)
return r;
}
if (link_ipv6_accept_ra_enabled(link)) {
r = ndisc_configure(link);
if (r < 0)
return r;
@ -2065,6 +2105,12 @@ static int link_configure(Link *link) {
r = link_acquire_conf(link);
if (r < 0)
return r;
if (link->ipv6ll_address) {
r = link_acquire_ipv6_conf(link);
if (r < 0)
return r;
}
}
return link_enter_join_netdev(link);
@ -2411,6 +2457,27 @@ failed:
return r;
}
int link_ipv6ll_gained(Link *link) {
int r;
assert(link);
log_link_info(link, "Gained IPv6LL");
link->ipv6ll_address = true;
link_check_ready(link);
if (link->network) {
r = link_acquire_ipv6_conf(link);
if (r < 0) {
link_enter_failed(link);
return r;
}
}
return 0;
}
static int link_carrier_gained(Link *link) {
int r;
@ -2639,9 +2706,8 @@ int link_save(Link *link) {
sd_dhcp6_lease *dhcp6_lease = NULL;
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client,
&dhcp6_lease);
if (r < 0)
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
if (r < 0 && r != -ENOMSG)
log_link_debug(link, "No DHCPv6 lease");
}

View File

@ -95,10 +95,13 @@ struct Link {
unsigned dhcp4_messages;
bool dhcp4_configured;
bool dhcp6_configured;
unsigned ndisc_messages;
bool ndisc_configured;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address;
bool ipv4ll_route;
bool ipv4ll_address:1;
bool ipv4ll_route:1;
bool ipv6ll_address:1;
bool static_configured;
@ -141,13 +144,16 @@ int link_save(Link *link);
int link_carrier_reset(Link *link);
bool link_has_carrier(Link *link);
int link_ipv6ll_gained(Link *link);
int link_set_mtu(Link *link, uint32_t mtu);
int link_set_hostname(Link *link, const char *hostname);
int link_set_timezone(Link *link, const char *timezone);
int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
int dhcp6_configure(Link *link, bool information_request);
int dhcp6_configure(Link *link);
int dhcp6_request_address(Link *link);
int ndisc_configure(Link *link);
bool link_lldp_enabled(Link *link);
@ -156,6 +162,7 @@ bool link_ipv6ll_enabled(Link *link);
bool link_dhcp4_server_enabled(Link *link);
bool link_dhcp4_enabled(Link *link);
bool link_dhcp6_enabled(Link *link);
bool link_ipv6_accept_ra_enabled(Link *link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;

View File

@ -577,9 +577,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
if (r >= 0) {
if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = "ever";
else
if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
cinfo.ifa_valid * USEC_PER_SEC,
USEC_PER_SEC);
@ -590,7 +588,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
switch (type) {
case RTM_NEWADDR:
if (address)
log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", valid_str ?: "");
else {
/* An address appeared that we did not request */
r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
@ -598,7 +597,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen);
return 0;
} else
log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", valid_str ?: "");
}
address_update(address, flags, scope, &cinfo);
@ -608,10 +608,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
case RTM_DELADDR:
if (address) {
log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", valid_str ?: "");
address_drop(address);
} else
log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", valid_str ?: "");
break;
default:
@ -1091,7 +1093,8 @@ static bool manager_check_idle(void *userdata) {
link_ipv4ll_enabled(link) ||
link_dhcp4_server_enabled(link) ||
link_dhcp4_enabled(link) ||
link_dhcp6_enabled(link))
link_dhcp6_enabled(link) ||
link_ipv6_accept_ra_enabled(link))
return false;
}

View File

@ -20,14 +20,132 @@
***/
#include <netinet/ether.h>
#include <netinet/icmp6.h>
#include <linux/if.h>
#include "sd-ndisc.h"
#include "networkd-link.h"
static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) {
static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
assert(link);
assert(link->ndisc_messages > 0);
link->ndisc_messages --;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
link_enter_failed(link);
}
if (link->ndisc_messages == 0) {
link->ndisc_configured = true;
link_check_ready(link);
}
return 1;
}
static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) {
_cleanup_address_free_ Address *address = NULL;
Link *link = userdata;
usec_t time_now;
int r;
assert(nd);
assert(link);
assert(link->network);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
r = address_new(&address);
if (r < 0) {
log_link_error_errno(link, r, "Could not allocate address: %m");
return;
}
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
address->family = AF_INET6;
address->in_addr.in6 = *prefix;
if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8);
else {
/* see RFC4291 section 2.5.1 */
address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0];
address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1;
address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1];
address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2];
address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff;
address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe;
address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3];
address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4];
address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5];
}
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE;
address->cinfo.ifa_prefered = lifetime_preferred;
address->cinfo.ifa_valid = lifetime_valid;
r = address_configure(address, link, ndisc_netlink_handler, true);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
link_enter_failed(link);
return;
}
link->ndisc_messages ++;
}
static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) {
_cleanup_route_free_ Route *route = NULL;
Link *link = userdata;
usec_t time_now;
int r;
assert(nd);
assert(link);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
r = route_new(&route);
if (r < 0) {
log_link_error_errno(link, r, "Could not allocate route: %m");
return;
}
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
route->family = AF_INET6;
route->table = RT_TABLE_MAIN;
route->protocol = RTPROT_RA;
route->flags = RTM_F_PREFIX;
route->dst.in6 = *prefix;
route->dst_prefixlen = prefixlen;
route->lifetime = time_now + lifetime * USEC_PER_SEC;
r = route_configure(route, link, ndisc_netlink_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set prefix route: %m");
link_enter_failed(link);
return;
}
link->ndisc_messages ++;
}
static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
_cleanup_route_free_ Route *route = NULL;
Link *link = userdata;
usec_t time_now;
int r;
assert(link);
assert(link->network);
@ -36,27 +154,64 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) {
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
switch(event) {
case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE:
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
if (flags & ND_RA_FLAG_MANAGED)
dhcp6_request_address(link);
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0 && r != -EALREADY)
log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m");
}
if (!gateway)
return;
case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER:
dhcp6_configure(link, true);
r = route_new(&route);
if (r < 0) {
log_link_error_errno(link, r, "Could not allocate route: %m");
return;
}
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
route->family = AF_INET6;
route->table = RT_TABLE_MAIN;
route->protocol = RTPROT_RA;
route->pref = pref;
route->gw.in6 = *gateway;
route->lifetime = time_now + lifetime * USEC_PER_SEC;
r = route_configure(route, link, ndisc_netlink_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set default route: %m");
link_enter_failed(link);
return;
}
link->ndisc_messages ++;
}
static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
Link *link = userdata;
int r;
assert(link);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
switch (event) {
case SD_NDISC_EVENT_TIMEOUT:
dhcp6_request_address(link);
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0 && r != -EALREADY)
log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m");
break;
case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED:
dhcp6_configure(link, false);
case SD_NDISC_EVENT_STOP:
break;
default:
if (event < 0)
log_link_warning_errno(link, event, "IPv6 Neighbor Discover error: %m");
else
log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
break;
log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
}
}
@ -82,7 +237,11 @@ int ndisc_configure(Link *link) {
return r;
r = sd_ndisc_set_callback(link->ndisc_router_discovery,
ndisc_router_handler, link);
ndisc_router_handler,
ndisc_prefix_onlink_handler,
ndisc_prefix_autonomous_handler,
ndisc_handler,
link);
return r;
}

View File

@ -494,10 +494,18 @@ int route_configure(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
r = sd_rtnl_message_route_set_flags(req, route->flags);
if (r < 0)
return log_error_errno(r, "Colud not set flags: %m");
r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
if (r < 0)
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");

View File

@ -40,6 +40,8 @@ struct Route {
unsigned char tos;
uint32_t priority; /* note that ip(8) calls this 'metric' */
unsigned char table;
unsigned char pref;
unsigned flags;
union in_addr_union gw;
union in_addr_union dst;

View File

@ -62,6 +62,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
int sd_dhcp6_client_stop(sd_dhcp6_client *client);
int sd_dhcp6_client_start(sd_dhcp6_client *client);
int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
int priority);
int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);

View File

@ -31,19 +31,25 @@
_SD_BEGIN_DECLARATIONS;
enum {
SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
SD_NDISC_EVENT_STOP = 0,
SD_NDISC_EVENT_TIMEOUT = 1,
};
typedef struct sd_ndisc sd_ndisc;
typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event,
void *userdata);
typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata);
typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
unsigned lifetime, void *userdata);
typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata);
typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata);
int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb,
void *userdata);
int sd_ndisc_set_callback(sd_ndisc *nd,
sd_ndisc_router_callback_t rcb,
sd_ndisc_prefix_onlink_callback_t plcb,
sd_ndisc_prefix_autonomous_callback_t pacb,
sd_ndisc_callback_t cb,
void *userdata);
int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);

View File

@ -136,6 +136,8 @@ int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type);
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags);
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family);
int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol);
int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope);