networkd: add support for address label

IPv6 address labels are used for address selection; they are described in RFC 3484.
Precedence is managed by userspace, and only the label itself is stored in the kernel.

enp0s25.network

[Match]
Name=enp0s25

[Network]
DHCP=yes
Address = 2001:db8:f00:baa::b

[AddressLabel]
Label=199
Prefix=2001:db8:41::/64

[AddressLabel]
Label=11
Prefix=2001:db8:31::/64

[AddressLabel]
Label=123
Prefix=2001:db8:21::/64

[AddressLabel]
Label=124
Prefix=2001:db8:11::/64
[sus@maximus label]$ ip addrlabel list

prefix ::1/128 label 0
prefix ::/96 label 3
prefix ::ffff:0.0.0.0/96 label 4
prefix 2001:db8:41::/64 dev enp0s25 label 199
prefix 2001:db8:31::/64 dev enp0s25 label 11
prefix 2001:db8:21::/64 dev enp0s25 label 123
prefix 2001:db8:11::/64 dev enp0s25 label 124
prefix 2001::/32 label 6
prefix 2001:10::/28 label 7
prefix 3ffe::/16 label 12
prefix 2002::/16 label 2
prefix fec0::/10 label 11
prefix fc00::/7 label 5
prefix ::/0 label 1
This commit is contained in:
Susant Sahani 2017-04-25 16:06:50 +05:30
parent 30746d6088
commit 95b74ef6ea
8 changed files with 403 additions and 0 deletions

View File

@ -5812,6 +5812,8 @@ libnetworkd_core_la_SOURCES = \
src/network/networkd-network-bus.c \
src/network/networkd-address.h \
src/network/networkd-address.c \
src/network/networkd-address-label.h \
src/network/networkd-address-label.c \
src/network/networkd-route.h \
src/network/networkd-route.c \
src/network/networkd-fdb.h \

View File

@ -796,6 +796,33 @@
</variablelist>
</refsect1>
<refsect1>
<title>[IPv6AddressLabel] Section Options</title>
<para>An <literal>[IPv6AddressLabel]</literal> section accepts the
following keys. Specify several <literal>[IPv6AddressLabel]</literal>
sections to configure several addresse labels. IPv6 address labels are
used for address selection. See <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>.
Precedence is managed by userspace, and only the label itself is stored in the kernel</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Label=</varname></term>
<listitem>
<para> The label for the prefix (an unsigned integer) ranges 0 to 4294967294.
0xffffffff is reserved. This key is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Prefix=</varname></term>
<listitem>
<para>IPv6 prefix is an address with a prefix length, separated by a slash <literal>/</literal> character.
This key is mandatory. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[Route] Section Options</title>
<para>The <literal>[Route]</literal> section accepts the

View File

@ -0,0 +1,257 @@
/***
This file is part of systemd.
Copyright 2017 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <net/if.h>
#include <linux/if_addrlabel.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "networkd-address-label.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "socket-util.h"
int address_label_new(AddressLabel **ret) {
_cleanup_address_label_free_ AddressLabel *addrlabel = NULL;
addrlabel = new0(AddressLabel, 1);
if (!addrlabel)
return -ENOMEM;
*ret = addrlabel;
addrlabel = NULL;
return 0;
}
void address_label_free(AddressLabel *label) {
if (!label)
return;
if (label->network) {
LIST_REMOVE(labels, label->network->address_labels, label);
assert(label->network->n_address_labels > 0);
label->network->n_address_labels--;
if (label->section) {
hashmap_remove(label->network->address_labels_by_section, label->section);
network_config_section_free(label->section);
}
}
free(label);
}
static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
_cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
_cleanup_address_label_free_ AddressLabel *label = NULL;
int r;
assert(network);
assert(ret);
assert(!!filename == (section_line > 0));
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
label = hashmap_get(network->address_labels_by_section, n);
if (label) {
*ret = label;
label = NULL;
return 0;
}
r = address_label_new(&label);
if (r < 0)
return r;
label->section = n;
n = NULL;
r = hashmap_put(network->address_labels_by_section, label->section, label);
if (r < 0)
return r;
label->network = network;
LIST_APPEND(labels, network->address_labels, label);
network->n_address_labels++;
*ret = label;
label = NULL;
return 0;
}
int address_label_configure(
AddressLabel *label,
Link *link,
sd_netlink_message_handler_t callback,
bool update) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(label);
assert(link);
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &req, RTM_NEWADDRLABEL,
link->ifindex, label->family);
if (r < 0)
return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
r = sd_rtnl_message_addrlabel_set_prefixlen(req, label->prefixlen);
if (r < 0)
return log_error_errno(r, "Could not set prefixlen: %m");
r = sd_netlink_message_append_u32(req, IFAL_LABEL, label->label);
if (r < 0)
return log_error_errno(r, "Could not append IFAL_LABEL attribute: %m");
r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &label->in_addr.in6);
if (r < 0)
return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m");
r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
link_ref(link);
return 0;
}
int config_parse_address_label_prefix(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_address_label_free_ AddressLabel *n = NULL;
Network *network = userdata;
const char *prefix, *e;
union in_addr_union buffer;
int r, f;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = address_label_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
/* AddressLabel=prefix/prefixlen */
/* prefixlen */
e = strchr(rvalue, '/');
if (e) {
unsigned i;
r = safe_atou(e + 1, &i);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
if (i > 128) {
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is out of range, ignoring assignment: %s", e + 1);
return 0;
}
n->prefixlen = (unsigned char) i;
prefix = strndupa(rvalue, e - rvalue);
} else
prefix = rvalue;
r = in_addr_from_string_auto(prefix, &f, &buffer);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring assignment: %s", prefix);
return 0;
}
if (f != AF_INET6) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Address label family is not IPv6, ignoring assignment: %s", prefix);
return 0;
}
n->family = f;
n->in_addr = buffer;
n = NULL;
return 0;
}
int config_parse_address_label(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_address_label_free_ AddressLabel *n = NULL;
Network *network = userdata;
uint32_t k;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = address_label_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
r = safe_atou32(rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue);
return 0;
}
if (k == 0xffffffffUL) {
log_syntax(unit, LOG_ERR, filename, line, r, "Adress label is invalid, ignoring: %s", rvalue);
return 0;
}
n->label = k;
n = NULL;
return 0;
}

View File

@ -0,0 +1,59 @@
#pragma once
/***
This file is part of systemd.
Copyright 2017 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <stdbool.h>
#include "in-addr-util.h"
typedef struct AddressLabel AddressLabel;
#include "networkd-link.h"
#include "networkd-network.h"
typedef struct Network Network;
typedef struct Link Link;
typedef struct NetworkConfigSection NetworkConfigSection;
struct AddressLabel {
Network *network;
Link *link;
NetworkConfigSection *section;
int family;
unsigned char prefixlen;
uint32_t label;
union in_addr_union in_addr;
LIST_FIELDS(AddressLabel, labels);
};
int address_label_new(AddressLabel **ret);
void address_label_free(AddressLabel *label);
DEFINE_TRIVIAL_CLEANUP_FUNC(AddressLabel*, address_label_free);
#define _cleanup_address_label_free_ _cleanup_(address_label_freep)
int address_label_configure(AddressLabel *address, Link *link, sd_netlink_message_handler_t callback, bool update);
int config_parse_address_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_label_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View File

@ -853,6 +853,35 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
return 1;
}
static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
assert(rtnl);
assert(m);
assert(link);
assert(link->ifname);
assert(link->link_messages > 0);
link->link_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
log_link_warning_errno(link, r, "could not set address label: %m");
else if (r >= 0)
manager_rtnl_process_address(rtnl, m, link->manager);
if (link->link_messages == 0) {
log_link_debug(link, "Addresses label set");
link_enter_set_routes(link);
}
return 1;
}
static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
_cleanup_free_ struct in_addr *addresses = NULL;
size_t n_addresses = 0, n_allocated = 0;
@ -965,6 +994,7 @@ static int link_set_bridge_fdb(Link *link) {
}
static int link_enter_set_addresses(Link *link) {
AddressLabel *label;
Address *ad;
int r;
@ -989,6 +1019,17 @@ static int link_enter_set_addresses(Link *link) {
link->link_messages++;
}
LIST_FOREACH(labels, label, link->network->address_labels) {
r = address_label_configure(label, link, address_label_handler, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set address label: %m");
link_enter_failed(link);
return r;
}
link->link_messages++;
}
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link)) {

View File

@ -79,6 +79,8 @@ Address.DuplicateAddressDetection, config_parse_address_flags,
Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0
Address.PrefixRoute, config_parse_address_flags, 0, 0
Address.AutoJoin, config_parse_address_flags, 0, 0
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0

View File

@ -114,6 +114,7 @@ static int network_load_one(Manager *manager, const char *filename) {
LIST_HEAD_INIT(network->static_routes);
LIST_HEAD_INIT(network->static_fdb_entries);
LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
LIST_HEAD_INIT(network->address_labels);
network->stacked_netdevs = hashmap_new(&string_hash_ops);
if (!network->stacked_netdevs)
@ -131,6 +132,10 @@ static int network_load_one(Manager *manager, const char *filename) {
if (!network->fdb_entries_by_section)
return log_oom();
network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
if (!network->address_labels_by_section)
return log_oom();
network->filename = strdup(filename);
if (!network->filename)
return log_oom();
@ -192,6 +197,7 @@ static int network_load_one(Manager *manager, const char *filename) {
"Link\0"
"Network\0"
"Address\0"
"IPv6AddressLabel\0"
"Route\0"
"DHCP\0"
"DHCPv4\0" /* compat */
@ -271,6 +277,7 @@ void network_free(Network *network) {
Address *address;
FdbEntry *fdb_entry;
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
AddressLabel *label;
Iterator i;
if (!network)
@ -318,9 +325,13 @@ void network_free(Network *network) {
while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
while ((label = network->address_labels))
address_label_free(label);
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);
hashmap_free(network->address_labels_by_section);
if (network->manager) {
if (network->manager->networks)

View File

@ -28,6 +28,7 @@
#include "resolve-util.h"
#include "networkd-address.h"
#include "networkd-address-label.h"
#include "networkd-brvlan.h"
#include "networkd-fdb.h"
#include "networkd-lldp-tx.h"
@ -202,15 +203,18 @@ struct Network {
LIST_HEAD(Route, static_routes);
LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
LIST_HEAD(AddressLabel, address_labels);
unsigned n_static_addresses;
unsigned n_static_routes;
unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses;
unsigned n_address_labels;
Hashmap *addresses_by_section;
Hashmap *routes_by_section;
Hashmap *fdb_entries_by_section;
Hashmap *address_labels_by_section;
struct in_addr_data *dns;
unsigned n_dns;