Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/.custom_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ IPv
InfiniBand
InterVLAN
IoT
IPsec
KVM
LACPDUs
LAI
Expand Down Expand Up @@ -102,6 +103,7 @@ WakeOnWLan
Wi
Wi-Fi
WireGuard
XFRM
adapters
autostart
boolean
Expand Down
43 changes: 43 additions & 0 deletions doc/netplan-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ network:

> Configures Virtual Routing and Forwarding (VRF) devices.

- [**`xfrm-interfaces`**](#properties-for-device-type-xfrm-interfaces) (mapping)

> Creates and configures XFRM devices for offloaded IPsec.

- [**`wifis`**](#properties-for-device-type-wifis) (mapping)

> Configures physical Wi-Fi interfaces as `client`, `adhoc` or `access point`.
Expand Down Expand Up @@ -1956,6 +1960,45 @@ VXLAN specific keys:
> Allows setting the IPv4 Do not Fragment (DF) bit in outgoing packets.
> Takes a boolean value. When unset, the kernel default will be used.

(yaml-xfrm-interfaces)=
## Properties for device type `xfrm-interfaces`

**Status**: Optional.

**Purpose**: Use the `xfrm-interfaces` key to create virtual XFRM (IPsec) interfaces.

**Structure**: The key consists of a mapping of XFRM interface names. Each
`xfrm-interface` requires an `if_id`. The general configuration structure for
XFRM interfaces is shown below.

```yaml
network:
xfrm-interfaces:
xfrm0:
if_id: 10
link: eth0
...
```

When applied, a virtual interface called `xfrm0` will be created in the system.

XFRM interfaces provide the kernel interface for IPsec transform operations.

The specific settings for `xfrm-interfaces` are defined below.

- **`if_id`** (scalar)

> The XFRM interface ID (if_id). This is a required parameter.
> Takes a number in the range `1..4294967295`.

- **`link`** (scalar)

> The underlying physical interface for this XFRM interface.

- **`independent`** (boolean)

> If set to `true`, the XFRM interface is independent of the underlying interface (`link`). Defaults to `false`.

## Properties for device type `virtual-ethernets`

**Status**: Optional.
Expand Down
1 change: 1 addition & 0 deletions include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ typedef enum {
NETPLAN_DEF_TYPE_NM,
NETPLAN_DEF_TYPE_DUMMY, /* wokeignore:rule=dummy */
NETPLAN_DEF_TYPE_VETH,
NETPLAN_DEF_TYPE_XFRM,
/* Place holder type used to fill gaps when a netdef
* requires links to another netdef (such as vlan_link)
* but it's not strictly mandatory
Expand Down
7 changes: 7 additions & 0 deletions src/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,13 @@ struct netplan_net_definition {
guint port;
} tunnel;

/* XFRM interface properties */
struct {
guint interface_id;
gboolean independent;
NetplanNetDefinition* link;
} xfrm;

NetplanAuthenticationSettings auth;
gboolean has_auth;

Expand Down
1 change: 1 addition & 0 deletions src/names.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ netplan_def_type_to_str[NETPLAN_DEF_TYPE_MAX_] = {
[NETPLAN_DEF_TYPE_VLAN] = "vlans",
[NETPLAN_DEF_TYPE_VRF] = "vrfs",
[NETPLAN_DEF_TYPE_TUNNEL] = "tunnels",
[NETPLAN_DEF_TYPE_XFRM] = "xfrm-interfaces",
[NETPLAN_DEF_TYPE_DUMMY] = "dummy-devices", /* wokeignore:rule=dummy */
[NETPLAN_DEF_TYPE_VETH] = "virtual-ethernets",
[NETPLAN_DEF_TYPE_PORT] = "_ovs-ports",
Expand Down
9 changes: 9 additions & 0 deletions src/netplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,15 @@ _serialize_yaml(
if (def->type == NETPLAN_DEF_TYPE_VRF)
YAML_UINT_DEFAULT(def, event, emitter, "table", def->vrf_table, G_MAXUINT);

/* XFRM settings */
if (def->type == NETPLAN_DEF_TYPE_XFRM) {
YAML_UINT_DEFAULT(def, event, emitter, "if_id", def->xfrm.interface_id, 0);
if (def->xfrm.link)
YAML_STRING(def, event, emitter, "link", def->xfrm.link->id);
if (def->xfrm.independent)
YAML_BOOL_TRUE(def, event, emitter, "independent", def->xfrm.independent);
}

/* Tunnel settings */
if (def->type == NETPLAN_DEF_TYPE_TUNNEL) {
write_tunnel_settings(event, emitter, def);
Expand Down
63 changes: 53 additions & 10 deletions src/networkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,26 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
write_tunnel_params(s, def);
break;

/* Generate XFRM interface netdev file */
case NETPLAN_DEF_TYPE_XFRM:
g_string_append_printf(s, "Kind=xfrm\n\n[Xfrm]\nInterfaceId=%u\n", def->xfrm.interface_id);
if (!def->xfrm.independent && def->xfrm.link) {
const NetplanNetDefinition* parent = def->xfrm.link;
/* Determine the actual kernel interface name for the parent.
* This must match the name used in the parent's .network file [Match] section.
* Priority: set-name (if renamed) > match.original_name (if matched) > id (netplan ID)
*/
const char* parent_name = parent->set_name ? parent->set_name :
(parent->match.original_name ? parent->match.original_name : parent->id);

g_string_append_printf(s, "Parent=%s\n", parent_name);
}
/* Independent interfaces operate without link device, in reality it will show up as @lo. */
if (def->xfrm.independent) {
g_string_append(s, "Independent=true\n");
}
break;

default: g_assert_not_reached(); // LCOV_EXCL_LINE
}

Expand Down Expand Up @@ -896,15 +916,17 @@ _netplan_netdef_write_network_file(
/* Set link local addressing -- this does not apply to bond and bridge
* member interfaces, which always get it disabled.
*/
if (!def->bond && !def->bridge && (def->linklocal.ipv4 || def->linklocal.ipv6)) {
if (def->linklocal.ipv4 && def->linklocal.ipv6)
g_string_append(network, "LinkLocalAddressing=yes\n");
else if (def->linklocal.ipv4)
g_string_append(network, "LinkLocalAddressing=ipv4\n");
else if (def->linklocal.ipv6)
g_string_append(network, "LinkLocalAddressing=ipv6\n");
} else {
g_string_append(network, "LinkLocalAddressing=no\n");
if (def->type != NETPLAN_DEF_TYPE_XFRM) {
if (!def->bond && !def->bridge && (def->linklocal.ipv4 || def->linklocal.ipv6)) {
if (def->linklocal.ipv4 && def->linklocal.ipv6)
g_string_append(network, "LinkLocalAddressing=yes\n");
else if (def->linklocal.ipv4)
g_string_append(network, "LinkLocalAddressing=ipv4\n");
else if (def->linklocal.ipv6)
g_string_append(network, "LinkLocalAddressing=ipv6\n");
} else {
g_string_append(network, "LinkLocalAddressing=no\n");
}
}

if (def->ip4_addresses)
Expand Down Expand Up @@ -957,7 +979,7 @@ _netplan_netdef_write_network_file(
g_string_append_printf(network, "IPv6MTUBytes=%d\n", def->ipv6_mtubytes);
}

if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL || def->ignore_carrier)
if ((def->type >= NETPLAN_DEF_TYPE_VIRTUAL && def->type != NETPLAN_DEF_TYPE_XFRM) || def->ignore_carrier)
g_string_append(network, "ConfigureWithoutCarrier=yes\n");

if (def->critical)
Expand Down Expand Up @@ -1006,6 +1028,22 @@ _netplan_netdef_write_network_file(
if (def->vrf_link)
g_string_append_printf(network, "VRF=%s\n", def->vrf_link->id);

{
GList* l = np_state->netdefs_ordered;
for (; l != NULL; l = l->next) {
const NetplanNetDefinition* nd = l->data;

if (nd->type != NETPLAN_DEF_TYPE_XFRM)
continue;

if (nd->xfrm.independent)
continue;

if (nd->xfrm.link == def)
g_string_append_printf(network, "Xfrm=%s\n", nd->id);
}
}

/* VXLAN options */
if (def->has_vxlans) {
/* iterate over all netdefs to find VXLANs attached to us */
Expand Down Expand Up @@ -1110,6 +1148,11 @@ _netplan_netdef_write_network_file(
}
}

if (def->type == NETPLAN_DEF_TYPE_XFRM && !def->xfrm.independent) {
if (network->len > 0 && !g_str_has_prefix(network->str, "LinkLocalAddressing="))
g_string_prepend(network, "LinkLocalAddressing=no\n");
}

if (network->len > 0 || link->len > 0) {
s = g_string_sized_new(200);
append_match_section(def, s, TRUE);
Expand Down
5 changes: 5 additions & 0 deletions src/nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,11 @@ _netplan_netdef_write_nm(
return FALSE;
}

if (netdef->type == NETPLAN_DEF_TYPE_XFRM) {
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED, "ERROR: %s: XFRM interfaces are not supported by NetworkManager\n", netdef->id);
return FALSE;
}

if (netdef->type == NETPLAN_DEF_TYPE_VETH) {
/*
* Final validation of veths that can't be fully done during parsing due to the
Expand Down
56 changes: 56 additions & 0 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,36 @@ process_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, c
* Generic helper functions to extract data from scalar nodes.
*************************************************************/

/**
* Handler for setting a guint field from a scalar node, inside a given struct
* Supports hex (0x prefix) and decimal values
* @entryptr: pointer to the begining of the to-be-modified data structure
* @data: offset into entryptr struct where the guint field to write is located
*/
STATIC gboolean
handle_generic_guint_hex_dec(NetplanParser* npp, yaml_node_t* node, const void* entryptr, const void* data, GError** error)
{
g_assert(entryptr != NULL);
guint offset = GPOINTER_TO_UINT(data);
guint64 v;
gchar* endptr;

const char* s_node = scalar(node);

if (g_str_has_prefix(s_node, "0x") || g_str_has_prefix(s_node, "0X")) {
v = g_ascii_strtoull(s_node, &endptr, 16);
} else {
v = g_ascii_strtoull(s_node, &endptr, 10);
}

if (*endptr != '\0' || v > G_MAXUINT)
return yaml_error(npp, node, error, "invalid unsigned int value '%s'", s_node); // LCOV_EXCL_LINE

mark_data_as_dirty(npp, entryptr + offset);
*((guint*) ((void*) entryptr + offset)) = (guint) v;
return TRUE;
}

/**
* Handler for setting a guint field from a scalar node, inside a given struct
* @entryptr: pointer to the begining of the to-be-modified data structure
Expand Down Expand Up @@ -733,6 +763,12 @@ handle_netdef_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
return handle_generic_guint(npp, node, npp->current.netdef, data, error);
}

STATIC gboolean
handle_netdef_guint_hex_dec(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
return handle_generic_guint_hex_dec(npp, node, npp->current.netdef, data, error);
}

STATIC gboolean
handle_netdef_ip4(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
Expand Down Expand Up @@ -3141,6 +3177,15 @@ static const mapping_entry_handler tunnel_def_handlers[] = {
{NULL}
};

static const mapping_entry_handler xfrm_def_handlers[] = {
COMMON_LINK_HANDLERS,
COMMON_BACKEND_HANDLERS,
{"if_id", YAML_SCALAR_NODE, {.generic=handle_netdef_guint_hex_dec}, netdef_offset(xfrm.interface_id)}, /* hex/dec like iproute2 */
{"independent", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(xfrm.independent)},
{"link", YAML_SCALAR_NODE, {.generic=handle_netdef_id_ref}, netdef_offset(xfrm.link)},
{NULL}
};

/****************************************************
* Grammar and handlers for network node
****************************************************/
Expand Down Expand Up @@ -3375,6 +3420,7 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefi
case NETPLAN_DEF_TYPE_ETHERNET: handlers = ethernet_def_handlers; break;
case NETPLAN_DEF_TYPE_MODEM: handlers = modem_def_handlers; break;
case NETPLAN_DEF_TYPE_TUNNEL: handlers = tunnel_def_handlers; break;
case NETPLAN_DEF_TYPE_XFRM: handlers = xfrm_def_handlers; break;
case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break;
case NETPLAN_DEF_TYPE_VRF: handlers = vrf_def_handlers; break;
case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break;
Expand Down Expand Up @@ -3427,6 +3473,10 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefi
npp->current.netdef->vxlan->independent = TRUE;
}

if (!npp->xfrm_if_ids) {
npp->xfrm_if_ids = g_hash_table_new(g_direct_hash, g_direct_equal);
}

/* validate definition-level conditions */
int ret = validate_netdef_grammar(npp, npp->current.netdef, error);
if (!ret && (npp->flags & NETPLAN_PARSER_IGNORE_ERRORS) == 0)
Expand Down Expand Up @@ -3485,6 +3535,7 @@ static const mapping_entry_handler network_handlers[] = {
{"vlans", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VLAN)},
{"vrfs", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VRF)},
{"wifis", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_WIFI)},
{"xfrm-interfaces", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_XFRM)},
{"modems", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_MODEM)},
{"dummy-devices", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_DUMMY)}, /* wokeignore:rule=dummy */
{"virtual-ethernets", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VETH)},
Expand Down Expand Up @@ -3811,6 +3862,11 @@ netplan_parser_reset(NetplanParser* npp)
}
// LCOV_EXCL_STOP

if (npp->xfrm_if_ids) {
g_hash_table_destroy(npp->xfrm_if_ids);
npp->xfrm_if_ids = NULL;
}

if (npp->missing_id) {
g_hash_table_destroy(npp->missing_id);
npp->missing_id = NULL;
Expand Down
3 changes: 3 additions & 0 deletions src/types-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ struct netplan_parser {
* when the flag IGNORE_ERRORS is set
* */
unsigned int error_count;

/* Hash table to track XFRM interface IDs to ensure uniqueness */
GHashTable* xfrm_if_ids;
};

struct netplan_state_iterator {
Expand Down
5 changes: 5 additions & 0 deletions src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
memset(&netdef->tunnel, 0, sizeof(netdef->tunnel));
netdef->tunnel.mode = NETPLAN_TUNNEL_MODE_UNKNOWN;

/* Reset XFRM parameters */
memset(&netdef->xfrm, 0, sizeof(netdef->xfrm));
netdef->xfrm.independent = FALSE;
netdef->xfrm.link = NULL;

reset_auth_settings(&netdef->auth);
netdef->has_auth = FALSE;

Expand Down
Loading
Loading