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:
commit
69a20cc3e4
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
128
src/network/networkd-netlabel.c
Normal file
128
src/network/networkd-netlabel.c
Normal 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));
|
||||
}
|
5
src/network/networkd-netlabel.h
Normal file
5
src/network/networkd-netlabel.h
Normal 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);
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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=
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -7,3 +7,5 @@ IPv6AcceptRA=true
|
||||
|
||||
[IPv6AcceptRA]
|
||||
UseDomains=yes
|
||||
# just a random label which should exist
|
||||
NetLabel=system_u:object_r:root_t:s0
|
||||
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user