]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: NetLabel integration
authorTopi Miettinen <toiwoton@gmail.com>
Tue, 3 May 2022 20:43:00 +0000 (23:43 +0300)
committerTopi Miettinen <topimiettinen@users.noreply.github.com>
Mon, 6 Jun 2022 18:24:10 +0000 (18:24 +0000)
New directive `NetLabel=` provides a method for integrating dynamic network
configuration into Linux NetLabel subsystem rules, used by Linux security
modules (LSMs) for network access control. The option expects a whitespace
separated list of NetLabel labels. The labels 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 NetLabel Fallback
Peer Labeling rules. They will be removed when the interface is
deconfigured. Failures to manage the labels will be ignored.

Example:
```
[DHCP]
NetLabel=system_u:object_r:localnet_peer_t:s0
```

With the above rules for interface `eth0`, when the interface is configured with
an IPv4 address of 10.0.0.0/8, `systemd-networkd` performs the equivalent of
`netlabelctl` operation

```
$ sudo netlabelctl unlbl add interface eth0 address:10.0.0.0/8 label:system_u:object_r:localnet_peer_t:s0
```

Result:
```
$ sudo netlabelctl -p unlbl list
...
 interface: eth0
   address: 10.0.0.0/8
    label: "system_u:object_r:localnet_peer_t:s0"
...
```

16 files changed:
man/systemd.network.xml
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/missing_network.h
src/libsystemd/sd-netlink/netlink-types-genl.c
src/libsystemd/sd-netlink/test-netlink.c
src/network/meson.build
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-netlabel.c [new file with mode: 0644]
src/network/networkd-netlabel.h [new file with mode: 0644]
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/test/test-in-addr-util.c
test/fuzz/fuzz-network-parser/directives

index c2ce1b1d6949075ee16cde387aea5b96b5a7784d..da19d98c462848b1725ea5dec1abf3c97875cd6d 100644 (file)
@@ -1109,6 +1109,38 @@ 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 dynamic network configuration into Linux
+          NetLabel subsystem rules, used by Linux security modules (LSMs) for network access control. The
+          option expects a whitespace separated list of NetLabel labels. The labels 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 NetLabel Fallback Peer Labeling 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. It is easy for someone not familiar with the LSM
+          per-packet access controls to get into a situation where for example remote connectivity is
+          broken. 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.0/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.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -2050,6 +2082,13 @@ 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>As in [Address] section.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -2163,6 +2202,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>
@@ -2264,6 +2304,13 @@ Table=1234</programlisting></para>
           </para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>NetLabel=</varname></term>
+        <listitem>
+          <para>As in [Address] section.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -2521,6 +2568,13 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
           specified. Defaults to true.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>NetLabel=</varname></term>
+        <listitem>
+          <para>As in [Address] section.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 660c1f18247c93417b523fac20437d1b0ad85718..148e11d12df7ee6fd1a6482b9991c077aa8d1474 100644 (file)
@@ -595,6 +595,45 @@ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned cha
         return addr;
 }
 
+struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen) {
+        assert(addr);
+        assert(prefixlen <= 128);
+
+        for (unsigned int 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;
+}
+
+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;
 
index 5de87a9539f472845a988d25a1137be0389494d0..159337f2bdb4bd31ccbeedc4a9510602bafe1f60 100644 (file)
@@ -88,6 +88,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);
index 6e71b26afd09eeb211578592d0f1288bd2c98415..776c7c837576f56b69e09bce253022d9228395b9 100644 (file)
 #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,
+};
index bdd5700c6e2c32a6c794673cea5631e6ea8e10de..149b4479e3f70e6d58ba7c774e5a6ed1cae0cae6 100644 (file)
@@ -221,15 +221,26 @@ static const NLType genl_wireguard_types[] = {
         [WGDEVICE_A_PEERS]       = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
 };
 
+/***************** genl NetLabel type systems *****************/
+static const NLType genl_netlabel_types[] = {
+        [NLBL_UNLABEL_A_IPV4ADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
+        [NLBL_UNLABEL_A_IPV4MASK] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
+        [NLBL_UNLABEL_A_IPV6ADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
+        [NLBL_UNLABEL_A_IPV6MASK] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
+        [NLBL_UNLABEL_A_IFACE]    = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
+        [NLBL_UNLABEL_A_SECCTX]   = { .type = NETLINK_TYPE_STRING },
+};
+
 /***************** genl families *****************/
 static const NLTypeSystemUnionElement genl_type_systems[] = {
-        { .name = CTRL_GENL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl),      },
-        { .name = BATADV_NL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv),    },
-        { .name = FOU_GENL_NAME,     .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou),       },
-        { .name = L2TP_GENL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp),      },
-        { .name = MACSEC_GENL_NAME,  .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec),    },
-        { .name = NL80211_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211),   },
-        { .name = WG_GENL_NAME,      .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
+        { .name = CTRL_GENL_NAME,               .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl),      },
+        { .name = BATADV_NL_NAME,               .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv),    },
+        { .name = FOU_GENL_NAME,                .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou),       },
+        { .name = L2TP_GENL_NAME,               .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp),      },
+        { .name = MACSEC_GENL_NAME,             .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec),    },
+        { .name = NL80211_GENL_NAME,            .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211),   },
+        { .name = WG_GENL_NAME,                 .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
+        { .name = NETLBL_NLTYPE_UNLABELED_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_netlabel),  },
 };
 
 /* This is the root type system union, so match_attribute is not necessary. */
index fbc3ef06094fb342198d3b0462add1ff2654e364..97085b84a77f5f1fcd31a2e2affa6a418fdd4f7c 100644 (file)
@@ -657,6 +657,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);
index 2315b56a3337b4187a71481263274aa638c227f3..e4def6bc51e2cdd61ec81d90f748362aad3cf75d 100644 (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',
index afcd53cbc780ea3a80ec74e3bd3d9a1f45d19f7f..227216744b79772dfb6bb17e8559988a3d33d078 100644 (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);
+        set_free(address->netlabels);
         return mfree(address);
 }
 
@@ -492,6 +494,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)
@@ -518,6 +522,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);
 
@@ -1939,6 +1945,41 @@ 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;
+        }
+
+        return config_parse_netlabel(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->netlabels, network);
+}
+
 static int address_section_verify(Address *address) {
         if (section_is_invalid(address->section))
                 return -EINVAL;
index 0237c1cb98c51d42cf8b1967d1d66d501f8ca444..e5770155fa37ff78e1ac41fa52097c1e1b04470c 100644 (file)
@@ -61,6 +61,9 @@ struct Address {
 
         /* Called when address become ready */
         address_ready_callback_t callback;
+
+        /* NetLabel */
+        Set *netlabels;
 };
 
 const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unused_result_;
@@ -135,3 +138,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);
diff --git a/src/network/networkd-netlabel.c b/src/network/networkd-netlabel.c
new file mode 100644 (file)
index 0000000..29eb1d8
--- /dev/null
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#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(address);
+        assert(address->link);
+        assert(address->link->manager);
+        assert(address->link->manager->genl);
+        assert(address->link->network);
+        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;
+
+        r = in_addr_prefixlen_to_netmask(address->family, &netmask, address->prefixlen);
+        if (r < 0)
+                return r;
+
+        if (address->family == AF_INET) {
+                r = sd_netlink_message_append_in_addr(m, NLBL_UNLABEL_A_IPV4ADDR, &address->in_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, &address->in_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;
+}
+
+static void address_add_netlabel_set(const Address *address, Set *labels) {
+        _cleanup_free_ char *addr_str = NULL;
+        int r;
+        const char *label;
+
+        (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
+
+        SET_FOREACH(label, labels) {
+                r = netlabel_command(NLBL_UNLABEL_C_STATICADD, label, address);
+                if (r < 0)
+                        log_link_warning_errno(address->link, r, "Adding NetLabel %s for IP address %s failed, ignoring",
+                                               label, strna(addr_str));
+                else
+                        log_link_debug(address->link, "Adding NetLabel %s for IP address %s", label, strna(addr_str));
+        }
+}
+
+void address_add_netlabel(const Address *address) {
+        assert(address);
+        assert(address->link);
+
+        if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
+                return;
+
+        switch (address->source) {
+        case NETWORK_CONFIG_SOURCE_DHCP4:
+                return address_add_netlabel_set(address, address->link->network->dhcp_netlabels);
+        case NETWORK_CONFIG_SOURCE_DHCP6:
+                return address_add_netlabel_set(address, address->link->network->dhcp6_netlabels);
+        case NETWORK_CONFIG_SOURCE_DHCP_PD:
+                return address_add_netlabel_set(address, address->link->network->dhcp_pd_netlabels);
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                return address_add_netlabel_set(address, address->link->network->ndisc_netlabels);
+        case NETWORK_CONFIG_SOURCE_STATIC:
+                return address_add_netlabel_set(address, address->netlabels);
+        default:
+                return;
+        }
+}
+
+void address_del_netlabel(const Address *address) {
+        int r;
+        _cleanup_free_ char *addr_str = NULL;
+
+        assert(address);
+        assert(address->link);
+
+        if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
+                return;
+
+        (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
+
+        r = netlabel_command(NLBL_UNLABEL_C_STATICREMOVE, NULL, address);
+        if (r < 0)
+                log_link_warning_errno(address->link, r, "Deleting NetLabels for IP address %s failed, ignoring",
+                                       strna(addr_str));
+        else
+                log_link_debug(address->link, "Deleting NetLabels for IP address %s", strna(addr_str));
+}
+
+int config_parse_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) {
+        int r;
+        Set **set = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(set);
+
+        if (isempty(rvalue)) {
+                *set = set_free(*set);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract NetLabel label, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                /* Label semantics depend on LSM but let's do basic checks */
+                if (!string_is_safe(w)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Bad NetLabel label, ignoring: %s", w);
+                        continue;
+                }
+
+                r = set_ensure_consume(set, &string_hash_ops_free, TAKE_PTR(w));
+                if (r < 0)
+                        return log_oom();
+        }
+}
diff --git a/src/network/networkd-netlabel.h b/src/network/networkd-netlabel.h
new file mode 100644 (file)
index 0000000..92f614f
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+void address_add_netlabel(const Address *address);
+void address_del_netlabel(const Address *address);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_netlabel);
index ceaaa6a0f7cdbabe2fa65ad3191cc2b4c93bf99b..ef5cec1b52d26202d3c2b46b4867e26fcce45460 100644 (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"
@@ -156,6 +157,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
@@ -243,6 +245,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_netlabel,                                    0,                             offsetof(Network, dhcp_netlabels)
 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
@@ -260,6 +263,7 @@ DHCPv6.SendOption,                           config_parse_dhcp_send_option,
 DHCPv6.IAID,                                 config_parse_iaid,                                        AF_INET6,                      0
 DHCPv6.DUIDType,                             config_parse_duid_type,                                   0,                             offsetof(Network, dhcp6_duid)
 DHCPv6.DUIDRawData,                          config_parse_duid_rawdata,                                0,                             offsetof(Network, dhcp6_duid)
+DHCPv6.NetLabel,                             config_parse_netlabel,                                    0,                             offsetof(Network, dhcp6_netlabels)
 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)
@@ -277,6 +281,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_netlabel,                                    0,                             offsetof(Network, ndisc_netlabels)
 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)
@@ -343,6 +348,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_netlabel,                                    0,                             offsetof(Network, dhcp_pd_netlabels)
 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)
index 39ea4eddd0893fd5e0e53a018912bd7c953fab3f..a6660d72b94e44b357a8ef77fce4b3854a518a66 100644 (file)
@@ -688,6 +688,8 @@ static Network *network_free(Network *network) {
         free(network->dhcp6_mudurl);
         strv_free(network->dhcp6_user_class);
         strv_free(network->dhcp6_vendor_class);
+        set_free(network->dhcp_netlabels);
+        set_free(network->dhcp6_netlabels);
 
         strv_free(network->ntp);
         for (unsigned i = 0; i < network->n_dns; i++)
@@ -754,6 +756,8 @@ static Network *network_free(Network *network) {
         ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
         set_free(network->dhcp_pd_tokens);
         set_free(network->ndisc_tokens);
+        set_free(network->dhcp_pd_netlabels);
+        set_free(network->ndisc_netlabels);
 
         return mfree(network);
 }
index 98e6159040ee302b8cbfa1064d7fb898a0a7505f..96cd316e0198adeea0cd088498b71180b9cd8abf 100644 (file)
@@ -155,6 +155,7 @@ struct Network {
         Set *dhcp_request_options;
         OrderedHashmap *dhcp_client_send_options;
         OrderedHashmap *dhcp_client_send_vendor_options;
+        Set *dhcp_netlabels;
 
         /* DHCPv6 Client support */
         bool dhcp6_use_address;
@@ -179,6 +180,7 @@ struct Network {
         OrderedHashmap *dhcp6_client_send_options;
         OrderedHashmap *dhcp6_client_send_vendor_options;
         Set *dhcp6_request_options;
+        Set *dhcp6_netlabels;
 
         /* DHCP Server Support */
         bool dhcp_server;
@@ -235,6 +237,7 @@ struct Network {
         Set *dhcp_pd_tokens;
         int dhcp_pd_uplink_index;
         char *dhcp_pd_uplink_name;
+        Set *dhcp_pd_netlabels;
 
         /* Bridge Support */
         int use_bpdu;
@@ -319,6 +322,7 @@ struct Network {
         Set *ndisc_deny_listed_route_prefix;
         Set *ndisc_allow_listed_route_prefix;
         Set *ndisc_tokens;
+        Set *ndisc_netlabels;
 
         /* LLDP support */
         LLDPMode lldp_mode; /* LLDP reception */
index 636967c240ea1b8361735e43a60670319ecd1f54..3ff2a7540e1d4bb771d6dc41c6b0ca7d2c71dd8f 100644 (file)
@@ -363,4 +363,35 @@ 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",
+        };
+
+        for (unsigned char prefixlen = 0; prefixlen <= 32; prefixlen++) {
+                _cleanup_free_ char *r = NULL;
+
+                assert_se(in_addr_prefixlen_to_netmask(AF_INET, &addr, prefixlen) >= 0);
+                assert_se(in_addr_to_string(AF_INET, &addr, &r) >= 0);
+                printf("test_in_addr_prefixlen_to_netmask: %s == %s\n", ipv4_netmasks[prefixlen], r);
+                assert_se(streq(ipv4_netmasks[prefixlen], r));
+        }
+
+        for (unsigned char prefixlen = 0; prefixlen <= 128; prefixlen++) {
+                _cleanup_free_ char *r = NULL;
+
+                assert_se(in_addr_prefixlen_to_netmask(AF_INET6, &addr, prefixlen) >= 0);
+                assert_se(in_addr_to_string(AF_INET6, &addr, &r) >= 0);
+                printf("test_in_addr_prefixlen_to_netmask: %s\n", r);
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 276f3c9307678a317ecc712d82cd313d7e2a0e19..0b850cdfcf0d7302aed3b0a10a6c2caf89f3e0a0 100644 (file)
@@ -131,6 +131,7 @@ MUDURL=
 RouteMTUBytes=
 FallbackLeaseLifetimeSec=
 Use6RD=
+NetLabel=
 [DHCPv6]
 UseAddress=
 UseDelegatedPrefix=
@@ -152,6 +153,7 @@ RouteMetric=
 IAID=
 DUIDType=
 DUIDRawData=
+NetLabel=
 [DHCPv6PrefixDelegation]
 SubnetId=
 Announce=
@@ -159,6 +161,7 @@ Assign=
 ManageTemporaryAddress=
 Token=
 RouteMetric=
+NetLabel=
 [DHCPPrefixDelegation]
 UplinkInterface=
 SubnetId=
@@ -167,6 +170,7 @@ Assign=
 ManageTemporaryAddress=
 Token=
 RouteMetric=
+NetLabel=
 [Route]
 Destination=
 Protocol=
@@ -343,6 +347,7 @@ EmitDomains=
 Managed=
 OtherInformation=
 UplinkInterface=
+NetLabel=
 [IPv6PrefixDelegation]
 RouterPreference=
 DNSLifetimeSec=