diff --git a/man/systemd.network.xml b/man/systemd.network.xml index c2ce1b1d69..da19d98c46 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1109,6 +1109,38 @@ Table=1234 Defaults to no. + + + NetLabel=label + + + 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. + + 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 netlabelctl8 + is needed. + + Example: + [Address] +NetLabel=system_u:object_r:localnet_peer_t:s0 + + With the example rules applying 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 + + netlabelctl unlbl add interface eth0 address:10.0.0.0/8 label:system_u:object_r:localnet_peer_t:s0 + + and the reverse operation when the IPv4 address is deconfigured. + + @@ -2050,6 +2082,13 @@ Table=1234 RFC 5227. Defaults to false. + + + NetLabel= + + As in [Address] section. + + @@ -2163,6 +2202,7 @@ Table=1234 UseNTP= UseHostname= UseDomains= + NetLabel= As in the [DHCPv4] section. @@ -2264,6 +2304,13 @@ Table=1234 + + + NetLabel= + + As in [Address] section. + + @@ -2521,6 +2568,13 @@ Token=prefixstable:2002:da8:1:: specified. Defaults to true. + + + NetLabel= + + As in [Address] section. + + diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 660c1f1824..148e11d12d 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -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; diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 5de87a9539..159337f2bd 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -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); diff --git a/src/basic/missing_network.h b/src/basic/missing_network.h index 6e71b26afd..776c7c8375 100644 --- a/src/basic/missing_network.h +++ b/src/basic/missing_network.h @@ -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, +}; diff --git a/src/libsystemd/sd-netlink/netlink-types-genl.c b/src/libsystemd/sd-netlink/netlink-types-genl.c index bdd5700c6e..149b4479e3 100644 --- a/src/libsystemd/sd-netlink/netlink-types-genl.c +++ b/src/libsystemd/sd-netlink/netlink-types-genl.c @@ -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. */ diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index fbc3ef0609..97085b84a7 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -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); diff --git a/src/network/meson.build b/src/network/meson.build index 2315b56a33..e4def6bc51 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -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', diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index afcd53cbc7..227216744b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.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); + 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; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 0237c1cb98..e5770155fa 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -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 index 0000000000..29eb1d867d --- /dev/null +++ b/src/network/networkd-netlabel.c @@ -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 index 0000000000..92f614fc73 --- /dev/null +++ b/src/network/networkd-netlabel.h @@ -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); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index ceaaa6a0f7..ef5cec1b52 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 39ea4eddd0..a6660d72b9 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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); } diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 98e6159040..96cd316e01 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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 */ diff --git a/src/test/test-in-addr-util.c b/src/test/test-in-addr-util.c index 636967c240..3ff2a7540e 100644 --- a/src/test/test-in-addr-util.c +++ b/src/test/test-in-addr-util.c @@ -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); diff --git a/test/fuzz/fuzz-network-parser/directives b/test/fuzz/fuzz-network-parser/directives index 276f3c9307..0b850cdfcf 100644 --- a/test/fuzz/fuzz-network-parser/directives +++ b/test/fuzz/fuzz-network-parser/directives @@ -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=