1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-13 17:18:18 +03:00

Merge pull request #23888 from topimiettinen/networkd-netlabel-v2

network: NetLabel integration
This commit is contained in:
Yu Watanabe 2022-08-30 07:30:49 +09:00 committed by GitHub
commit 69a20cc3e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 488 additions and 8 deletions

View File

@ -1121,6 +1121,62 @@ Table=1234</programlisting></para>
Defaults to <literal>no</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetLabel=</varname><replaceable>label</replaceable></term>
<listitem>
<para>This setting provides a method for integrating static and dynamic network configuration into
Linux <ulink url="https://docs.kernel.org/netlabel/index.html">NetLabel</ulink> subsystem rules,
used by <ulink url="https://en.wikipedia.org/wiki/Linux_Security_Modules">Linux Security Modules
(LSMs)</ulink> for network access control. The label, with suitable LSM rules, can be used to
control connectivity of (for example) a service with peers in the local network. At least with
SELinux, only the ingress can be controlled but not egress. The benefit of using this setting is
that it may be possible to apply interface independent part of NetLabel configuration at very early
stage of system boot sequence, at the time when the network interfaces are not available yet, with
<citerefentry
project='man-pages'><refentrytitle>netlabelctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
and the per-interface configuration with <command>systemd-networkd</command> once the interfaces
appear later. Currently this feature is only implemented for SELinux.</para>
<para>The option expects a single NetLabel label. The label must conform to lexical restrictions of
LSM labels. When an interface is configured with IP addresses, the addresses and subnetwork masks
will be appended to the <ulink
url="https://github.com/SELinuxProject/selinux-notebook/blob/main/src/network_support.md">NetLabel
Fallback Peer Labeling</ulink> rules. They will be removed when the interface is
deconfigured. Failures to manage the labels will be ignored.</para>
<para>Warning: Once labeling is enabled for network traffic, a lot of LSM access control points in
Linux networking stack go from dormant to active. Care should be taken to avoid getting into a
situation where for example remote connectivity is broken, when the security policy hasn't been
updated to consider LSM per-packet access controls and no rules would allow any network
traffic. Also note that additional configuration with <citerefentry
project='man-pages'><refentrytitle>netlabelctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is needed.</para>
<para>Example:
<programlisting>[Address]
NetLabel=system_u:object_r:localnet_peer_t:s0</programlisting>
With the example rules applying for interface <literal>eth0</literal>, when the interface is
configured with an IPv4 address of 10.0.0.123/8, <command>systemd-networkd</command> performs the
equivalent of <command>netlabelctl</command> operation
<programlisting>netlabelctl unlbl add interface eth0 address:10.0.0.0/8 label:system_u:object_r:localnet_peer_t:s0</programlisting>
and the reverse operation when the IPv4 address is deconfigured. The configuration can be used with
LSM rules; in case of SELinux to allow a SELinux domain to receive data from objects of SELinux
<literal>peer</literal> class. For example:
<programlisting>type localnet_peer_t;
allow my_server_t localnet_peer_t:peer recv;</programlisting>
The effect of the above configuration and rules (in absence of other rules as may be the case) is
to only allow <literal>my_server_t</literal> (and nothing else) to receive data from local subnet
10.0.0.0/8 of interface <literal>eth0</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -2071,6 +2127,15 @@ Table=1234</programlisting></para>
<ulink url="https://tools.ietf.org/html/rfc5227">RFC 5227</ulink>. Defaults to false.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetLabel=</varname></term>
<listitem>
<para>This applies the NetLabel for the addresses received with DHCP, like
<varname>NetLabel=</varname> in [Address] section applies it to statically configured
addresses. See <varname>NetLabel=</varname> in [Address] section for more details.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -2197,6 +2262,7 @@ Table=1234</programlisting></para>
<term><varname>UseNTP=</varname></term>
<term><varname>UseHostname=</varname></term>
<term><varname>UseDomains=</varname></term>
<term><varname>NetLabel=</varname></term>
<listitem>
<para>As in the [DHCPv4] section.</para>
</listitem>
@ -2298,6 +2364,15 @@ Table=1234</programlisting></para>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetLabel=</varname></term>
<listitem>
<para>This applies the NetLabel for the addresses received with DHCP, like
<varname>NetLabel=</varname> in [Address] section applies it to statically configured
addresses. See <varname>NetLabel=</varname> in [Address] section for more details.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -2555,6 +2630,15 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
specified. Defaults to true.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetLabel=</varname></term>
<listitem>
<para>This applies the NetLabel for the addresses received with RA, like
<varname>NetLabel=</varname> in [Address] section applies it to statically configured
addresses. See <varname>NetLabel=</varname> in [Address] section for more details.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -586,6 +586,7 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
return 32U - u32ctz(be32toh(addr->s_addr));
}
/* Calculate an IPv4 netmask from prefix length, for example /8 -> 255.0.0.0. */
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
assert(addr);
assert(prefixlen <= 32);
@ -599,6 +600,47 @@ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned cha
return addr;
}
/* Calculate an IPv6 netmask from prefix length, for example /16 -> ffff::. */
struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen) {
assert(addr);
assert(prefixlen <= 128);
for (unsigned i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else if (prefixlen > 0) {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
} else {
assert(prefixlen == 0);
mask = 0;
}
addr->s6_addr[i] = mask;
}
return addr;
}
/* Calculate an IPv4 or IPv6 netmask from prefix length, for example /8 -> 255.0.0.0 or /16 -> ffff::. */
int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr);
switch (family) {
case AF_INET:
in4_addr_prefixlen_to_netmask(&addr->in, prefixlen);
return 0;
case AF_INET6:
in6_addr_prefixlen_to_netmask(&addr->in6, prefixlen);
return 0;
default:
return -EAFNOSUPPORT;
}
}
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
uint8_t msb_octet = *(uint8_t*) addr;

View File

@ -138,6 +138,8 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);

View File

@ -49,3 +49,35 @@
#ifndef IEEE80211_MAX_SSID_LEN
#define IEEE80211_MAX_SSID_LEN 32
#endif
/* Not exposed but defined in include/net/netlabel.h */
#ifndef NETLBL_NLTYPE_UNLABELED_NAME
#define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL"
#endif
/* Not exposed but defined in net/netlabel/netlabel_unlabeled.h */
enum {
NLBL_UNLABEL_C_UNSPEC,
NLBL_UNLABEL_C_ACCEPT,
NLBL_UNLABEL_C_LIST,
NLBL_UNLABEL_C_STATICADD,
NLBL_UNLABEL_C_STATICREMOVE,
NLBL_UNLABEL_C_STATICLIST,
NLBL_UNLABEL_C_STATICADDDEF,
NLBL_UNLABEL_C_STATICREMOVEDEF,
NLBL_UNLABEL_C_STATICLISTDEF,
__NLBL_UNLABEL_C_MAX,
};
/* Not exposed but defined in net/netlabel/netlabel_unlabeled.h */
enum {
NLBL_UNLABEL_A_UNSPEC,
NLBL_UNLABEL_A_ACPTFLG,
NLBL_UNLABEL_A_IPV6ADDR,
NLBL_UNLABEL_A_IPV6MASK,
NLBL_UNLABEL_A_IPV4ADDR,
NLBL_UNLABEL_A_IPV4MASK,
NLBL_UNLABEL_A_IFACE,
NLBL_UNLABEL_A_SECCTX,
__NLBL_UNLABEL_A_MAX,
};

View File

@ -178,6 +178,16 @@ static const NLAPolicy genl_macsec_policies[] = {
[MACSEC_ATTR_SA_CONFIG] = BUILD_POLICY_NESTED(genl_macsec_sa),
};
/***************** genl NetLabel type systems *****************/
static const NLAPolicy genl_netlabel_policies[] = {
[NLBL_UNLABEL_A_IPV4ADDR] = BUILD_POLICY(IN_ADDR),
[NLBL_UNLABEL_A_IPV4MASK] = BUILD_POLICY(IN_ADDR),
[NLBL_UNLABEL_A_IPV6ADDR] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
[NLBL_UNLABEL_A_IPV6MASK] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
[NLBL_UNLABEL_A_IFACE] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ-1),
[NLBL_UNLABEL_A_SECCTX] = BUILD_POLICY(STRING),
};
/***************** genl nl80211 type systems *****************/
static const NLAPolicy genl_nl80211_policies[] = {
[NL80211_ATTR_WIPHY] = BUILD_POLICY(U32),
@ -223,13 +233,14 @@ static const NLAPolicy genl_wireguard_policies[] = {
/***************** genl families *****************/
static const NLAPolicySetUnionElement genl_policy_set_union_elements[] = {
BUILD_UNION_ELEMENT_BY_STRING(CTRL_GENL_NAME, genl_ctrl),
BUILD_UNION_ELEMENT_BY_STRING(BATADV_NL_NAME, genl_batadv),
BUILD_UNION_ELEMENT_BY_STRING(FOU_GENL_NAME, genl_fou),
BUILD_UNION_ELEMENT_BY_STRING(L2TP_GENL_NAME, genl_l2tp),
BUILD_UNION_ELEMENT_BY_STRING(MACSEC_GENL_NAME, genl_macsec),
BUILD_UNION_ELEMENT_BY_STRING(NL80211_GENL_NAME, genl_nl80211),
BUILD_UNION_ELEMENT_BY_STRING(WG_GENL_NAME, genl_wireguard),
BUILD_UNION_ELEMENT_BY_STRING(CTRL_GENL_NAME, genl_ctrl),
BUILD_UNION_ELEMENT_BY_STRING(BATADV_NL_NAME, genl_batadv),
BUILD_UNION_ELEMENT_BY_STRING(FOU_GENL_NAME, genl_fou),
BUILD_UNION_ELEMENT_BY_STRING(L2TP_GENL_NAME, genl_l2tp),
BUILD_UNION_ELEMENT_BY_STRING(MACSEC_GENL_NAME, genl_macsec),
BUILD_UNION_ELEMENT_BY_STRING(NETLBL_NLTYPE_UNLABELED_NAME, genl_netlabel),
BUILD_UNION_ELEMENT_BY_STRING(NL80211_GENL_NAME, genl_nl80211),
BUILD_UNION_ELEMENT_BY_STRING(WG_GENL_NAME, genl_wireguard),
};
/* This is the root type system union, so match_attribute is not necessary. */

View File

@ -656,6 +656,8 @@ static void test_genl(void) {
(void) sd_genl_message_new(genl, MACSEC_GENL_NAME, 0, &m);
m = sd_netlink_message_unref(m);
(void) sd_genl_message_new(genl, NL80211_GENL_NAME, 0, &m);
m = sd_netlink_message_unref(m);
(void) sd_genl_message_new(genl, NETLBL_NLTYPE_UNLABELED_NAME, 0, &m);
for (;;) {
r = sd_event_run(event, 500 * USEC_PER_MSEC);

View File

@ -115,6 +115,8 @@ sources = files(
'networkd-ndisc.h',
'networkd-neighbor.c',
'networkd-neighbor.h',
'networkd-netlabel.c',
'networkd-netlabel.h',
'networkd-network-bus.c',
'networkd-network-bus.h',
'networkd-network.c',

View File

@ -12,6 +12,7 @@
#include "networkd-dhcp-server.h"
#include "networkd-ipv4acd.h"
#include "networkd-manager.h"
#include "networkd-netlabel.h"
#include "networkd-network.h"
#include "networkd-queue.h"
#include "networkd-route-util.h"
@ -137,6 +138,7 @@ Address *address_free(Address *address) {
config_section_free(address->section);
free(address->label);
free(address->netlabel);
return mfree(address);
}
@ -392,6 +394,7 @@ int address_dup(const Address *src, Address **ret) {
dest->link = NULL;
dest->label = NULL;
dest->acd = NULL;
dest->netlabel = NULL;
if (src->family == AF_INET) {
r = free_and_strdup(&dest->label, src->label);
@ -399,6 +402,10 @@ int address_dup(const Address *src, Address **ret) {
return r;
}
r = free_and_strdup(&dest->netlabel, src->netlabel);
if (r < 0)
return r;
*ret = TAKE_PTR(dest);
return 0;
}
@ -485,6 +492,8 @@ static int address_update(Address *address) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
address_add_netlabel(address);
if (address_is_ready(address) && address->callback) {
r = address->callback(address);
if (r < 0)
@ -511,6 +520,8 @@ static int address_drop(Address *address) {
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
address_del_netlabel(address);
if (address->state == 0)
address_free(address);
@ -1941,6 +1952,47 @@ int config_parse_duplicate_address_detection(
return 0;
}
int config_parse_address_netlabel(
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) {
Network *network = userdata;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
assert(network);
r = address_new_static(network, filename, section_line, &n);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to allocate new address, ignoring assignment: %m");
return 0;
}
r = config_parse_string(unit, filename, line, section, section_line,
lvalue, CONFIG_PARSE_STRING_SAFE, rvalue, &n->netlabel, network);
if (r < 0)
return r;
TAKE_PTR(n);
return 0;
}
static int address_section_verify(Address *address) {
if (section_is_invalid(address->section))
return -EINVAL;

View File

@ -38,7 +38,7 @@ struct Address {
unsigned char scope;
uint32_t flags;
uint32_t route_metric; /* route metric for prefix route */
char *label;
char *label, *netlabel;
int set_broadcast;
struct in_addr broadcast;
@ -130,3 +130,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
CONFIG_PARSER_PROTOTYPE(config_parse_address_netlabel);

View File

@ -412,6 +412,10 @@ static int dhcp_pd_request_address(
log_dhcp_pd_address(link, address);
r = free_and_strdup_warn(&address->netlabel, link->network->dhcp_pd_netlabel);
if (r < 0)
return r;
if (address_get(link, address, &existing) < 0)
link->dhcp_pd_configured = false;
else

View File

@ -877,6 +877,10 @@ static int dhcp4_request_address(Link *link, bool announce) {
if (r < 0)
return r;
r = free_and_strdup_warn(&addr->netlabel, link->network->dhcp_netlabel);
if (r < 0)
return r;
if (address_get(link, addr, &existing) < 0) /* The address is new. */
link->dhcp4_configured = false;
else

View File

@ -224,6 +224,10 @@ static int dhcp6_request_address(
if (verify_dhcp6_address(link, addr) < 0)
return 0;
r = free_and_strdup_warn(&addr->netlabel, link->network->dhcp6_netlabel);
if (r < 0)
return r;
if (address_get(link, addr, &existing) < 0)
link->dhcp6_configured = false;
else

View File

@ -278,6 +278,10 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
address->source = NETWORK_CONFIG_SOURCE_NDISC;
address->provider.in6 = router;
r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
if (r < 0)
return r;
if (address_get(link, address, &existing) < 0)
link->ndisc_configured = false;
else

View File

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "escape.h"
#include "netlink-util.h"
#include "networkd-address.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-netlabel.h"
#include "networkd-network.h"
static int netlabel_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert_se(rtnl);
assert_se(m);
assert_se(link);
r = sd_netlink_message_get_errno(m);
if (r < 0) {
log_link_message_warning_errno(link, m, r, "NetLabel operation failed, ignoring");
return 1;
}
log_link_debug(link, "NetLabel operation successful");
return 1;
}
static int netlabel_command(uint16_t command, const char *label, const Address *address) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(command != NLBL_UNLABEL_C_UNSPEC && command < __NLBL_UNLABEL_C_MAX);
assert(address);
assert(address->link);
assert(address->link->ifname);
assert(address->link->manager);
assert(address->link->manager->genl);
assert(IN_SET(address->family, AF_INET, AF_INET6));
r = sd_genl_message_new(address->link->manager->genl, NETLBL_NLTYPE_UNLABELED_NAME, command, &m);
if (r < 0)
return r;
r = sd_netlink_message_append_string(m, NLBL_UNLABEL_A_IFACE, address->link->ifname);
if (r < 0)
return r;
if (command == NLBL_UNLABEL_C_STATICADD) {
assert(label);
r = sd_netlink_message_append_string(m, NLBL_UNLABEL_A_SECCTX, label);
if (r < 0)
return r;
}
union in_addr_union netmask, masked_addr;
r = in_addr_prefixlen_to_netmask(address->family, &netmask, address->prefixlen);
if (r < 0)
return r;
/*
* When adding rules, kernel adds the address to its hash table _applying also the netmask_, but on
* removal, an exact match is required _without netmask applied_, so apply the mask on both
* operations.
*/
masked_addr = address->in_addr;
r = in_addr_mask(address->family, &masked_addr, address->prefixlen);
if (r < 0)
return r;
if (address->family == AF_INET) {
r = sd_netlink_message_append_in_addr(m, NLBL_UNLABEL_A_IPV4ADDR, &masked_addr.in);
if (r < 0)
return r;
r = sd_netlink_message_append_in_addr(m, NLBL_UNLABEL_A_IPV4MASK, &netmask.in);
} else if (address->family == AF_INET6) {
r = sd_netlink_message_append_in6_addr(m, NLBL_UNLABEL_A_IPV6ADDR, &masked_addr.in6);
if (r < 0)
return r;
r = sd_netlink_message_append_in6_addr(m, NLBL_UNLABEL_A_IPV6MASK, &netmask.in6);
}
if (r < 0)
return r;
r = netlink_call_async(address->link->manager->genl, NULL, m, netlabel_handler, link_netlink_destroy_callback,
address->link);
if (r < 0)
return r;
link_ref(address->link);
return 0;
}
void address_add_netlabel(const Address *address) {
int r;
assert(address);
if (!address->netlabel)
return;
r = netlabel_command(NLBL_UNLABEL_C_STATICADD, address->netlabel, address);
if (r < 0)
log_link_warning_errno(address->link, r, "Adding NetLabel %s for IP address %s failed, ignoring", address->netlabel,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
else
log_link_debug(address->link, "Adding NetLabel %s for IP address %s", address->netlabel,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
}
void address_del_netlabel(const Address *address) {
int r;
assert(address);
if (!address->netlabel)
return;
r = netlabel_command(NLBL_UNLABEL_C_STATICREMOVE, address->netlabel, address);
if (r < 0)
log_link_warning_errno(address->link, r, "Deleting NetLabel %s for IP address %s failed, ignoring", address->netlabel,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
else
log_link_debug(address->link, "Deleting NetLabel %s for IP address %s", address->netlabel,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
}

View File

@ -0,0 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
void address_add_netlabel(const Address *address);
void address_del_netlabel(const Address *address);

View File

@ -25,6 +25,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-ipv6ll.h"
#include "networkd-lldp-tx.h"
#include "networkd-ndisc.h"
#include "networkd-netlabel.h"
#include "networkd-network.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
@ -158,6 +159,7 @@ Address.AutoJoin, config_parse_address_flags,
Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0
Address.Scope, config_parse_address_scope, 0, 0
Address.RouteMetric, config_parse_address_route_metric, 0, 0
Address.NetLabel, config_parse_address_netlabel, 0, 0
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
Neighbor.Address, config_parse_neighbor_address, 0, 0
@ -246,6 +248,7 @@ DHCPv4.SendVendorOption, config_parse_dhcp_send_option,
DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
DHCPv4.Use6RD, config_parse_bool, 0, offsetof(Network, dhcp_use_6rd)
DHCPv4.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_netlabel)
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0
@ -264,6 +267,7 @@ DHCPv6.IAID, config_parse_iaid,
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit)
DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel)
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway)
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
@ -281,6 +285,7 @@ IPv6AcceptRA.PrefixDenyList, config_parse_in_addr_prefixes,
IPv6AcceptRA.RouteAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_route_prefix)
IPv6AcceptRA.RouteDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_route_prefix)
IPv6AcceptRA.Token, config_parse_address_generation_type, 0, offsetof(Network, ndisc_tokens)
IPv6AcceptRA.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, ndisc_netlabel)
DHCPServer.ServerAddress, config_parse_dhcp_server_address, 0, 0
DHCPServer.UplinkInterface, config_parse_uplink, 0, 0
DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_relay_target)
@ -347,6 +352,7 @@ DHCPPrefixDelegation.Assign, config_parse_bool,
DHCPPrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp_pd_manage_temporary_address)
DHCPPrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp_pd_tokens)
DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric)
DHCPPrefixDelegation.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_pd_netlabel)
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)

View File

@ -712,6 +712,7 @@ static Network *network_free(Network *network) {
set_free(network->dhcp_request_options);
ordered_hashmap_free(network->dhcp_client_send_options);
ordered_hashmap_free(network->dhcp_client_send_vendor_options);
free(network->dhcp_netlabel);
/* DHCPv6 client */
free(network->dhcp6_mudurl);
@ -720,10 +721,12 @@ static Network *network_free(Network *network) {
set_free(network->dhcp6_request_options);
ordered_hashmap_free(network->dhcp6_client_send_options);
ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
free(network->dhcp6_netlabel);
/* DHCP PD */
free(network->dhcp_pd_uplink_name);
set_free(network->dhcp_pd_tokens);
free(network->dhcp_pd_netlabel);
/* Router advertisement */
ordered_set_free(network->router_search_domains);
@ -738,6 +741,7 @@ static Network *network_free(Network *network) {
set_free(network->ndisc_deny_listed_route_prefix);
set_free(network->ndisc_allow_listed_route_prefix);
set_free(network->ndisc_tokens);
free(network->ndisc_netlabel);
/* LLDP */
free(network->lldp_mudurl);

View File

@ -155,6 +155,7 @@ struct Network {
Set *dhcp_request_options;
OrderedHashmap *dhcp_client_send_options;
OrderedHashmap *dhcp_client_send_vendor_options;
char *dhcp_netlabel;
/* DHCPv6 Client support */
bool dhcp6_use_address;
@ -180,6 +181,7 @@ struct Network {
OrderedHashmap *dhcp6_client_send_options;
OrderedHashmap *dhcp6_client_send_vendor_options;
Set *dhcp6_request_options;
char *dhcp6_netlabel;
/* DHCP Server Support */
bool dhcp_server;
@ -237,6 +239,7 @@ struct Network {
Set *dhcp_pd_tokens;
int dhcp_pd_uplink_index;
char *dhcp_pd_uplink_name;
char *dhcp_pd_netlabel;
/* Bridge Support */
int use_bpdu;
@ -321,6 +324,7 @@ struct Network {
Set *ndisc_deny_listed_route_prefix;
Set *ndisc_allow_listed_route_prefix;
Set *ndisc_tokens;
char *ndisc_netlabel;
/* LLDP support */
LLDPMode lldp_mode; /* LLDP reception */

View File

@ -364,4 +364,56 @@ TEST(in_addr_to_string) {
test_in_addr_to_string_one(AF_INET6, "fe80::");
}
TEST(in_addr_prefixlen_to_netmask) {
union in_addr_union addr;
static const char *const ipv4_netmasks[] = {
"0.0.0.0", "128.0.0.0", "192.0.0.0", "224.0.0.0", "240.0.0.0",
"248.0.0.0", "252.0.0.0", "254.0.0.0", "255.0.0.0",
"255.128.0.0", "255.192.0.0", "255.224.0.0", "255.240.0.0",
"255.248.0.0", "255.252.0.0", "255.254.0.0", "255.255.0.0",
"255.255.128.0", "255.255.192.0", "255.255.224.0", "255.255.240.0",
"255.255.248.0", "255.255.252.0", "255.255.254.0", "255.255.255.0",
"255.255.255.128", "255.255.255.192", "255.255.255.224", "255.255.255.240",
"255.255.255.248", "255.255.255.252", "255.255.255.254", "255.255.255.255",
};
static const char *const ipv6_netmasks[] = {
[0] = "::",
[1] = "8000::",
[2] = "c000::",
[7] = "fe00::",
[8] = "ff00::",
[9] = "ff80::",
[16] = "ffff::",
[17] = "ffff:8000::",
[32] = "ffff:ffff::",
[33] = "ffff:ffff:8000::",
[64] = "ffff:ffff:ffff:ffff::",
[65] = "ffff:ffff:ffff:ffff:8000::",
[96] = "ffff:ffff:ffff:ffff:ffff:ffff::",
[97] = "ffff:ffff:ffff:ffff:ffff:ffff:8000:0",
[127] = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
[128] = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
};
for (unsigned char prefixlen = 0; prefixlen <= 32; prefixlen++) {
_cleanup_free_ char *result = NULL;
assert_se(in_addr_prefixlen_to_netmask(AF_INET, &addr, prefixlen) >= 0);
assert_se(in_addr_to_string(AF_INET, &addr, &result) >= 0);
printf("test_in_addr_prefixlen_to_netmask: %s == %s\n", ipv4_netmasks[prefixlen], result);
assert_se(streq(ipv4_netmasks[prefixlen], result));
}
for (unsigned char prefixlen = 0; prefixlen <= 128; prefixlen++) {
_cleanup_free_ char *result = NULL;
assert_se(in_addr_prefixlen_to_netmask(AF_INET6, &addr, prefixlen) >= 0);
assert_se(in_addr_to_string(AF_INET6, &addr, &result) >= 0);
printf("test_in_addr_prefixlen_to_netmask: %s\n", result);
if (ipv6_netmasks[prefixlen])
assert_se(streq(ipv6_netmasks[prefixlen], result));
}
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -132,6 +132,7 @@ MUDURL=
RouteMTUBytes=
FallbackLeaseLifetimeSec=
Use6RD=
NetLabel=
[DHCPv6]
UseAddress=
UseDelegatedPrefix=
@ -153,6 +154,7 @@ RouteMetric=
IAID=
DUIDType=
DUIDRawData=
NetLabel=
[DHCPv6PrefixDelegation]
SubnetId=
Announce=
@ -160,6 +162,7 @@ Assign=
ManageTemporaryAddress=
Token=
RouteMetric=
NetLabel=
[DHCPPrefixDelegation]
UplinkInterface=
SubnetId=
@ -168,6 +171,7 @@ Assign=
ManageTemporaryAddress=
Token=
RouteMetric=
NetLabel=
[Route]
Destination=
Protocol=
@ -346,6 +350,7 @@ EmitDomains=
Managed=
OtherInformation=
UplinkInterface=
NetLabel=
[IPv6PrefixDelegation]
RouterPreference=
DNSLifetimeSec=

View File

@ -361,3 +361,8 @@ Address=10.3.3.251/16
Address=10.3.3.252/16
Address=10.3.3.253/16
Address=10.3.3.254/16
[Address]
Address=10.4.3.2/24
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0

View File

@ -26,6 +26,8 @@ SendDecline=yes
# DenyList= will be ignored
AllowList=192.168.5.0/24 192.168.6.0/24
DenyList=192.168.5.0/24
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0
[Route]
Destination=192.168.5.0/24

View File

@ -14,3 +14,5 @@ SubnetId=0
Announce=no
Token=eui64
Token=::1a:2b:3c:4d
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0

View File

@ -7,3 +7,5 @@ IPv6AcceptRA=true
[IPv6AcceptRA]
UseDomains=yes
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0

View File

@ -889,6 +889,18 @@ class Utilities():
self.assertNotRegex(output, address_regex)
def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
if not shutil.which('selinuxenabled'):
print(f'## Checking NetLabel skipped: selinuxenabled command not found.')
elif call_quiet('selinuxenabled') != 0:
print(f'## Checking NetLabel skipped: SELinux disabled.')
elif not shutil.which('netlabelctl'): # not packaged by all distros
print(f'## Checking NetLabel skipped: netlabelctl command not found.')
else:
output = check_output('netlabelctl unlbl list')
print(output)
self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
class NetworkctlTests(unittest.TestCase, Utilities):
def setUp(self):
@ -2209,6 +2221,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertIn('inet6 2001:db8:1:f101::1/64 scope global deprecated', output)
self.assertRegex(output, r'inet6 fd[0-9a-f:]*1/64 scope global')
self.check_netlabel('dummy98', '10\.4\.3\.0/24')
# Tests for #20891.
# 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
check_output('ip address change 10.7.8.9/16 dev dummy98 preferred_lft forever')
@ -4267,6 +4281,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, '2002:da8:1:0')
self.check_netlabel('veth99', '2002:da8:1::/64')
self.check_netlabel('veth99', '2002:da8:2::/64')
def test_ipv6_token_static(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
start_networkd()
@ -4560,6 +4577,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertIn('client provides name: test-hostname', output)
self.assertIn('26:mtu', output)
self.check_netlabel('veth99', '192\.168\.5\.0/24')
def test_dhcp_client_ipv4_use_routes_gateway(self):
first = True
for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
@ -5117,6 +5136,8 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
def verify_dhcp4_6rd(self, tunnel_name):
print('### ip -4 address show dev veth-peer scope global')
output = check_output('ip -4 address show dev veth-peer scope global')