libnm,nmcli: add ipvx.dhcp-dscp property

Currently the internal DHCP client sets traffic class "CS6" in the DS
field of the IP header for outgoing packets.

dhclient sets the field according to the definition of TOS (RFC 1349),
which was was deprecated in 1998 by RFC 2474 in favor of DSCP.

Introduce a new property IPvX.dhcp-dscp (currently valid only for
IPv4) to specify a custom DSCP value for DHCP backends that support it
(currently, only the internal one).

Define the default value to CS0, because:

 - section 4.9 of RFC 4594 specifies that DHCP should use the standard
   (CS0 = 0) service class;

 - section 3.2 says that class CS6 is for "transmitting packets
   between network devices (routers) that require control (routing)
   information to be exchanged between nodes", listing "OSPF, BGP,
   ISIS, RIP" as examples of such traffic. Furthermore, it says that:

     User traffic is not allowed to use this service class.  By user
     traffic, we mean packet flows that originate from user-controlled
     end points that are connected to the network.

- we got reports of some Cisco switches dropping DHCP packets because
  of the CS6 marking.
This commit is contained in:
Beniamino Galvani 2023-12-20 11:40:02 +01:00
parent 2f543f1154
commit fcd907e062
15 changed files with 1062 additions and 804 deletions

View file

@ -3360,6 +3360,8 @@ do_write_construct(NMConnection *connection,
GError **error)
{
NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingIPConfig *s_ip6;
nm_auto_shvar_file_close shvarFile *ifcfg = NULL;
const char *ifcfg_name;
gs_free char *ifcfg_name_free = NULL;
@ -3546,8 +3548,6 @@ do_write_construct(NMConnection *connection,
has_complex_routes_v6 = utils_has_complex_routes(ifcfg_name, AF_INET6);
if (has_complex_routes_v4 || has_complex_routes_v6) {
NMSettingIPConfig *s_ip4, *s_ip6;
s_ip4 = nm_connection_get_setting_ip4_config(connection);
s_ip6 = nm_connection_get_setting_ip6_config(connection);
if ((s_ip4 && nm_setting_ip_config_get_num_routes(s_ip4) > 0)
@ -3584,6 +3584,15 @@ do_write_construct(NMConnection *connection,
} else
route_ignore = FALSE;
if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))
&& nm_setting_ip_config_get_dhcp_dscp(s_ip4)) {
set_error_unsupported(error,
connection,
NM_SETTING_IP4_CONFIG_SETTING_NAME "." NM_SETTING_IP_CONFIG_DHCP_DSCP,
FALSE);
return FALSE;
}
write_ip4_setting(connection,
ifcfg,
!route_ignore && route_path_is_svformat ? &route_content_svformat : NULL,

View file

@ -1965,4 +1965,5 @@ global:
nm_setting_hsr_get_prp;
nm_setting_hsr_get_type;
nm_setting_hsr_new;
nm_setting_ip_config_get_dhcp_dscp;
} libnm_1_44_0;

View file

@ -461,6 +461,23 @@ nm_utils_validate_dhcp4_vendor_class_id(const char *vci, GError **error)
return TRUE;
}
gboolean
nm_utils_validate_dhcp_dscp(const char *dscp, GError **error)
{
g_return_val_if_fail(!error || !(*error), FALSE);
g_return_val_if_fail(dscp, FALSE);
if (!NM_IN_STRSET(dscp, "CS0", "CS4", "CS6")) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid DSCP value; allowed values are: 'CS0', 'CS4', 'CS6'"));
return FALSE;
}
return TRUE;
}
gboolean
nm_settings_connection_validate_permission_user(const char *item, gssize len)
{

View file

@ -273,6 +273,7 @@ NMClientPermissionResult nm_client_permission_result_from_string(const char *nm)
const char *nm_client_permission_result_to_string(NMClientPermissionResult permission);
gboolean nm_utils_validate_dhcp4_vendor_class_id(const char *vci, GError **error);
gboolean nm_utils_validate_dhcp_dscp(const char *dscp, GError **error);
/*****************************************************************************/

View file

@ -1564,6 +1564,10 @@
dbus-type="s"
gprop-type="gchararray"
/>
<property name="dhcp-dscp"
dbus-type="s"
gprop-type="gchararray"
/>
<property name="dhcp-fqdn"
dbus-type="s"
gprop-type="gchararray"
@ -1695,6 +1699,10 @@
dbus-type="i"
gprop-type="gint"
/>
<property name="dhcp-dscp"
dbus-type="s"
gprop-type="gchararray"
/>
<property name="dhcp-duid"
dbus-type="s"
gprop-type="gchararray"

View file

@ -3993,6 +3993,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig,
PROP_IGNORE_AUTO_ROUTES,
PROP_IGNORE_AUTO_DNS,
PROP_DHCP_HOSTNAME,
PROP_DHCP_DSCP,
PROP_DHCP_HOSTNAME_FLAGS,
PROP_DHCP_SEND_HOSTNAME,
PROP_NEVER_DEFAULT,
@ -5200,6 +5201,25 @@ nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting)
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_send_hostname;
}
/**
* nm_setting_ip_config_get_dhcp_dscp:
* @setting: the #NMSettingIPConfig
*
* Returns the value contained in the #NMSettingIPConfig:dhcp-dscp
* property.
*
* Returns: the value for the DSCP field for DHCP
*
* Since: 1.46
**/
const char *
nm_setting_ip_config_get_dhcp_dscp(NMSettingIPConfig *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_dscp;
}
/**
* nm_setting_ip_config_get_never_default:
* @setting: the #NMSettingIPConfig
@ -5731,6 +5751,14 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
}
}
if (priv->dhcp_dscp && !nm_utils_validate_dhcp_dscp(priv->dhcp_dscp, error)) {
g_prefix_error(error,
"%s.%s: ",
nm_setting_get_name(setting),
NM_SETTING_IP_CONFIG_DHCP_DSCP);
return FALSE;
}
/* Normalizable errors */
if (priv->gateway && priv->never_default) {
g_set_error(error,
@ -5983,6 +6011,12 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family)
.direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, dhcp_iaid),
.direct_string_allow_empty = TRUE);
_nm_properties_override_gobj(
properties_override,
obj_properties[PROP_DHCP_DSCP],
&nm_sett_info_propert_type_direct_string,
.direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, dhcp_dscp));
/* ---dbus---
* property: routing-rules
* format: array of 'a{sv}'
@ -6614,6 +6648,25 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:dhcp-dscp:
*
* Specifies the value for the DSCP field (traffic class) of the IP header. When
* empty, the global default value is used; if no global default is specified, it is
* assumed to be "CS0". Allowed values are: "CS0", "CS4" and "CS6".
*
* The property is currently valid only for IPv4, and it is supported only by the
* "internal" DHCP plugin.
*
* Since: 1.46
**/
obj_properties[PROP_DHCP_DSCP] =
g_param_spec_string(NM_SETTING_IP_CONFIG_DHCP_DSCP,
"",
"",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* NMSettingIPConfig:never-default:
*

View file

@ -386,6 +386,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
}
}
if (nm_setting_ip_config_get_dhcp_dscp(s_ip)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("DHCP DSCP is not supported for IPv6"));
g_prefix_error(error,
"%s.%s: ",
NM_SETTING_IP6_CONFIG_SETTING_NAME,
NM_SETTING_IP_CONFIG_DHCP_DSCP);
return FALSE;
}
/* Failures from here on, are NORMALIZABLE_ERROR... */
if (token_needs_normalization) {

View file

@ -185,6 +185,7 @@ typedef struct {
char *gateway;
char *dhcp_hostname;
char *dhcp_iaid;
char *dhcp_dscp;
gint64 route_metric;
int auto_route_ext_gw;
int replace_local_rule;

View file

@ -4082,6 +4082,7 @@ test_connection_diff_a_only(void)
{NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DNS_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DHCP_IAID, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DHCP_DSCP, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A},

View file

@ -332,6 +332,7 @@ char *nm_ip_routing_rule_to_string(const NMIPRoutingRule *self,
#define NM_SETTING_IP_CONFIG_DHCP_HOSTNAME "dhcp-hostname"
#define NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME "dhcp-send-hostname"
#define NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS "dhcp-hostname-flags"
#define NM_SETTING_IP_CONFIG_DHCP_DSCP "dhcp-dscp"
#define NM_SETTING_IP_CONFIG_NEVER_DEFAULT "never-default"
#define NM_SETTING_IP_CONFIG_MAY_FAIL "may-fail"
#define NM_SETTING_IP_CONFIG_DAD_TIMEOUT "dad-timeout"
@ -478,6 +479,8 @@ gboolean nm_setting_ip_config_get_ignore_auto_dns(NMSettingIPConfig *setting);
const char *nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting);
gboolean nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting);
NM_AVAILABLE_IN_1_46
const char *nm_setting_ip_config_get_dhcp_dscp(NMSettingIPConfig *setting);
gboolean nm_setting_ip_config_get_never_default(NMSettingIPConfig *setting);
gboolean nm_setting_ip_config_get_may_fail(NMSettingIPConfig *setting);

View file

@ -6368,6 +6368,12 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_IAID, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_IAID,
.property_type = &_pt_gobject_string,
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_DSCP, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_DSCP,
.property_type = &_pt_gobject_string,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
.values_static = NM_MAKE_STRV ("CS0", "CS4", "CS6"),
),
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT,
.property_type = &_pt_gobject_int,
.property_typ_data = &_ptd_gobject_int_timeout,

View file

@ -165,6 +165,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Maximum timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. The property is currently implemented only for IPv4. A zero value means that no duplicate address detection is performed, -1 means the default value (either the value configured globally in NetworkManger.conf or 200ms). A value greater than zero is a timeout in milliseconds. Note that the time intervals are subject to randomization as per RFC 5227 and so the actual duration can be between half and the full time specified in this property.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. The special value \"none\" prevents any client identifier from being sent. Note that this is normally not recommended. If unset, a globally configured default from NetworkManager.conf is used. If still unset, the default depends on the DHCP plugin. The internal dhcp client will default to \"mac\" and the dhclient plugin will try to use one from its config file if present, or won't sent any client-id otherwise.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_DSCP N_("Specifies the value for the DSCP field (traffic class) of the IP header. When empty, the global default value is used; if no global default is specified, it is assumed to be \"CS0\". Allowed values are: \"CS0\", \"CS4\" and \"CS6\". The property is currently valid only for IPv4, and it is supported only by the \"internal\" DHCP plugin.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) and \"fqdn-no-update\" (0x4). When no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is not set, the standard FQDN flags are set in the request: \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) for IPv4 and \"fqdn-serv-update\" (0x1) for IPv6. When this property is set to the default value \"none\" (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also \"none\" (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")
@ -194,6 +195,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Maximum timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. The property is currently implemented only for IPv4. A zero value means that no duplicate address detection is performed, -1 means the default value (either the value configured globally in NetworkManger.conf or 200ms). A value greater than zero is a timeout in milliseconds. Note that the time intervals are subject to randomization as per RFC 5227 and so the actual duration can be between half and the full time specified in this property.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DSCP N_("Specifies the value for the DSCP field (traffic class) of the IP header. When empty, the global default value is used; if no global default is specified, it is assumed to be \"CS0\". Allowed values are: \"CS0\", \"CS4\" and \"CS6\". The property is currently valid only for IPv4, and it is supported only by the \"internal\" DHCP plugin.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) and \"fqdn-no-update\" (0x4). When no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is not set, the standard FQDN flags are set in the request: \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) for IPv4 and \"fqdn-serv-update\" (0x1) for IPv6. When this property is set to the default value \"none\" (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also \"none\" (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")

View file

@ -1308,6 +1308,10 @@
<property name="dhcp-iaid"
nmcli-description="A string containing the &quot;Identity Association Identifier&quot; (IAID) used by the DHCP client. The string can be a 32-bit number (either decimal, hexadecimal or as colon separated hexadecimal numbers). Alternatively it can be set to the special values &quot;mac&quot;, &quot;perm-mac&quot;, &quot;ifname&quot; or &quot;stable&quot;. When set to &quot;mac&quot; (or &quot;perm-mac&quot;), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to &quot;ifname&quot;, the IAID is computed by hashing the interface name. The special value &quot;stable&quot; can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be &quot;ifname&quot;. For DHCPv4, the IAID is only used with &quot;ipv4.dhcp-client-id&quot; values &quot;duid&quot; and &quot;ipv6-duid&quot; to generate the client-id. For DHCPv6, note that at the moment this property is only supported by the &quot;internal&quot; DHCPv6 plugin. The &quot;dhclient&quot; DHCPv6 plugin always derives the IAID from the MAC address. The actually used DHCPv6 IAID for a currently activated interface is exposed in the lease information of the device."
format="string" />
<property name="dhcp-dscp"
nmcli-description="Specifies the value for the DSCP field (traffic class) of the IP header. When empty, the global default value is used; if no global default is specified, it is assumed to be &quot;CS0&quot;. Allowed values are: &quot;CS0&quot;, &quot;CS4&quot; and &quot;CS6&quot;. The property is currently valid only for IPv4, and it is supported only by the &quot;internal&quot; DHCP plugin."
format="string"
values="CS0, CS4, CS6" />
<property name="dhcp-timeout"
nmcli-description="A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity."
format="integer"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff