Skip to content

Commit c5032c0

Browse files
author
max-foss
committed
#1905280 Proof of concept for xfrm_interfaces.
1 parent baba35f commit c5032c0

File tree

13 files changed

+437
-0
lines changed

13 files changed

+437
-0
lines changed

doc/netplan-yaml.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ network:
6565

6666
> Configures Virtual Routing and Forwarding (VRF) devices.
6767

68+
- [**`xfrm-interfaces`**](#properties-for-device-type-xfrm-interfaces) (mapping)
69+
70+
> Creates and configures XFRM devices for offloaded IPsec.
71+
6872
- [**`wifis`**](#properties-for-device-type-wifis) (mapping)
6973

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

1963+
(yaml-xfrm-interfaces)=
1964+
## Properties for device type `xfrm-interfaces`
1965+
1966+
**Status**: Optional.
1967+
1968+
**Purpose**: Use the `xfrm-interfaces` key to create virtual XFRM (IPsec) interfaces.
1969+
1970+
**Structure**: The key consists of a mapping of XFRM interface names. Each
1971+
`xfrm-interface` requires an `if_id`. The general configuration structure for
1972+
XFRM interfaces is shown below.
1973+
1974+
```yaml
1975+
network:
1976+
xfrm-interfaces:
1977+
xfrm0:
1978+
if_id: 10
1979+
link: eth0
1980+
...
1981+
```
1982+
1983+
When applied, a virtual interface called `xfrm0` will be created in the system.
1984+
1985+
XFRM interfaces provide the kernel interface for IPsec transform operations.
1986+
1987+
The specific settings for `xfrm-interfaces` are defined below.
1988+
1989+
- **`if_id`** (scalar)
1990+
1991+
> The XFRM interface ID (if_id). This is a required parameter.
1992+
> Takes a number in the range `1..4294967295`.
1993+
1994+
- **`link`** (scalar)
1995+
1996+
> The underlying physical interface for this XFRM interface.
1997+
1998+
- **`independent`** (boolean)
1999+
2000+
> If set to `true`, the XFRM interface is independent of the underlying interface (`link`). Defaults to `false`.
2001+
19592002
## Properties for device type `virtual-ethernets`
19602003

19612004
**Status**: Optional.

include/types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ typedef enum {
5757
NETPLAN_DEF_TYPE_TUNNEL,
5858
NETPLAN_DEF_TYPE_PORT,
5959
NETPLAN_DEF_TYPE_VRF,
60+
NETPLAN_DEF_TYPE_XFRM,
6061
/* Type fallback/passthrough */
6162
NETPLAN_DEF_TYPE_NM,
6263
NETPLAN_DEF_TYPE_DUMMY, /* wokeignore:rule=dummy */

src/abi.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,13 @@ struct netplan_net_definition {
356356
guint port;
357357
} tunnel;
358358

359+
/* XFRM interface properties */
360+
struct {
361+
guint interface_id;
362+
gboolean independent;
363+
NetplanNetDefinition* link;
364+
} xfrm;
365+
359366
NetplanAuthenticationSettings auth;
360367
gboolean has_auth;
361368

src/names.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ netplan_def_type_to_str[NETPLAN_DEF_TYPE_MAX_] = {
4949
[NETPLAN_DEF_TYPE_VLAN] = "vlans",
5050
[NETPLAN_DEF_TYPE_VRF] = "vrfs",
5151
[NETPLAN_DEF_TYPE_TUNNEL] = "tunnels",
52+
[NETPLAN_DEF_TYPE_XFRM] = "xfrm-interfaces",
5253
[NETPLAN_DEF_TYPE_DUMMY] = "dummy-devices", /* wokeignore:rule=dummy */
5354
[NETPLAN_DEF_TYPE_VETH] = "virtual-ethernets",
5455
[NETPLAN_DEF_TYPE_PORT] = "_ovs-ports",

src/networkd.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,15 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
697697
write_tunnel_params(s, def);
698698
break;
699699

700+
/* Generate XFRM interface netdev file */
701+
case NETPLAN_DEF_TYPE_XFRM:
702+
g_string_append_printf(s, "Kind=xfrm\n\n[Xfrm]\nInterfaceId=%u\n", def->xfrm.interface_id);
703+
/* Independent interfaces operate without link device, in reality it will show up as @lo. */
704+
if (def->xfrm.independent) {
705+
g_string_append(s, "Independent=true\n");
706+
}
707+
break;
708+
700709
default: g_assert_not_reached(); // LCOV_EXCL_LINE
701710
}
702711

src/parse.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,36 @@ process_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, c
346346
* Generic helper functions to extract data from scalar nodes.
347347
*************************************************************/
348348

349+
/**
350+
* Handler for setting a guint field from a scalar node, inside a given struct
351+
* Supports hex (0x prefix) and decimal values
352+
* @entryptr: pointer to the begining of the to-be-modified data structure
353+
* @data: offset into entryptr struct where the guint field to write is located
354+
*/
355+
STATIC gboolean
356+
handle_generic_guint_hex_dec(NetplanParser* npp, yaml_node_t* node, const void* entryptr, const void* data, GError** error)
357+
{
358+
g_assert(entryptr != NULL);
359+
guint offset = GPOINTER_TO_UINT(data);
360+
guint64 v;
361+
gchar* endptr;
362+
363+
const char* s_node = scalar(node);
364+
365+
if (g_str_has_prefix(s_node, "0x") || g_str_has_prefix(s_node, "0X")) {
366+
v = g_ascii_strtoull(s_node, &endptr, 16);
367+
} else {
368+
v = g_ascii_strtoull(s_node, &endptr, 10);
369+
}
370+
371+
if (*endptr != '\0' || v > G_MAXUINT)
372+
return yaml_error(npp, node, error, "invalid unsigned int value '%s'", s_node);
373+
374+
mark_data_as_dirty(npp, entryptr + offset);
375+
*((guint*) ((void*) entryptr + offset)) = (guint) v;
376+
return TRUE;
377+
}
378+
349379
/**
350380
* Handler for setting a guint field from a scalar node, inside a given struct
351381
* @entryptr: pointer to the begining of the to-be-modified data structure
@@ -733,6 +763,12 @@ handle_netdef_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
733763
return handle_generic_guint(npp, node, npp->current.netdef, data, error);
734764
}
735765

766+
STATIC gboolean
767+
handle_netdef_guint_hex_dec(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
768+
{
769+
return handle_generic_guint_hex_dec(npp, node, npp->current.netdef, data, error);
770+
}
771+
736772
STATIC gboolean
737773
handle_netdef_ip4(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
738774
{
@@ -3141,6 +3177,15 @@ static const mapping_entry_handler tunnel_def_handlers[] = {
31413177
{NULL}
31423178
};
31433179

3180+
static const mapping_entry_handler xfrm_def_handlers[] = {
3181+
COMMON_LINK_HANDLERS,
3182+
COMMON_BACKEND_HANDLERS,
3183+
{"if_id", YAML_SCALAR_NODE, {.generic=handle_netdef_guint_hex_dec}, netdef_offset(xfrm.interface_id)}, /* hex/dec like iproute2 */
3184+
{"independent", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(xfrm.independent)},
3185+
{"link", YAML_SCALAR_NODE, {.generic=handle_netdef_id_ref}, netdef_offset(xfrm.link)},
3186+
{NULL}
3187+
};
3188+
31443189
/****************************************************
31453190
* Grammar and handlers for network node
31463191
****************************************************/
@@ -3375,6 +3420,7 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefi
33753420
case NETPLAN_DEF_TYPE_ETHERNET: handlers = ethernet_def_handlers; break;
33763421
case NETPLAN_DEF_TYPE_MODEM: handlers = modem_def_handlers; break;
33773422
case NETPLAN_DEF_TYPE_TUNNEL: handlers = tunnel_def_handlers; break;
3423+
case NETPLAN_DEF_TYPE_XFRM: handlers = xfrm_def_handlers; break;
33783424
case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break;
33793425
case NETPLAN_DEF_TYPE_VRF: handlers = vrf_def_handlers; break;
33803426
case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break;
@@ -3427,6 +3473,10 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefi
34273473
npp->current.netdef->vxlan->independent = TRUE;
34283474
}
34293475

3476+
if (!npp->xfrm_if_ids) {
3477+
npp->xfrm_if_ids = g_hash_table_new(g_direct_hash, g_direct_equal);
3478+
}
3479+
34303480
/* validate definition-level conditions */
34313481
int ret = validate_netdef_grammar(npp, npp->current.netdef, error);
34323482
if (!ret && (npp->flags & NETPLAN_PARSER_IGNORE_ERRORS) == 0)
@@ -3485,6 +3535,7 @@ static const mapping_entry_handler network_handlers[] = {
34853535
{"vlans", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VLAN)},
34863536
{"vrfs", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VRF)},
34873537
{"wifis", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_WIFI)},
3538+
{"xfrm-interfaces", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_XFRM)},
34883539
{"modems", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_MODEM)},
34893540
{"dummy-devices", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_DUMMY)}, /* wokeignore:rule=dummy */
34903541
{"virtual-ethernets", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VETH)},
@@ -3811,6 +3862,11 @@ netplan_parser_reset(NetplanParser* npp)
38113862
}
38123863
// LCOV_EXCL_STOP
38133864

3865+
if (npp->xfrm_if_ids) {
3866+
g_hash_table_destroy(npp->xfrm_if_ids);
3867+
npp->xfrm_if_ids = NULL;
3868+
}
3869+
38143870
if (npp->missing_id) {
38153871
g_hash_table_destroy(npp->missing_id);
38163872
npp->missing_id = NULL;
@@ -3839,7 +3895,17 @@ netplan_parser_reset(NetplanParser* npp)
38393895
npp->global_renderer = NULL;
38403896
}
38413897

3898+
if (npp->xfrm_if_ids) {
3899+
g_hash_table_destroy(npp->xfrm_if_ids);
3900+
npp->xfrm_if_ids = NULL;
3901+
}
3902+
38423903
npp->flags = 0;
3904+
3905+
if (npp->xfrm_if_ids) {
3906+
g_hash_table_destroy(npp->xfrm_if_ids);
3907+
npp->xfrm_if_ids = NULL;
3908+
}
38433909
npp->error_count = 0;
38443910
}
38453911

src/types-internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ struct netplan_parser {
273273
* when the flag IGNORE_ERRORS is set
274274
* */
275275
unsigned int error_count;
276+
277+
/* Hash table to track XFRM interface IDs to ensure uniqueness */
278+
GHashTable* xfrm_if_ids;
276279
};
277280

278281
struct netplan_state_iterator {

src/types.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
350350
memset(&netdef->tunnel, 0, sizeof(netdef->tunnel));
351351
netdef->tunnel.mode = NETPLAN_TUNNEL_MODE_UNKNOWN;
352352

353+
/* Reset XFRM parameters */
354+
memset(&netdef->xfrm, 0, sizeof(netdef->xfrm));
355+
netdef->xfrm.independent = FALSE;
356+
netdef->xfrm.link = NULL;
357+
353358
reset_auth_settings(&netdef->auth);
354359
netdef->has_auth = FALSE;
355360

src/validation.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,27 @@ validate_netdef_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, GErr
417417
}
418418
}
419419

420+
/* Validate XFRM interface configuration */
421+
if (nd->type == NETPLAN_DEF_TYPE_XFRM) {
422+
if (nd->xfrm.interface_id == 0) {
423+
return yaml_error(npp, NULL, error, "%s: missing 'if_id'", nd->id);
424+
}
425+
if (nd->xfrm.interface_id < 1 || nd->xfrm.interface_id > 0xffffffff) {
426+
return yaml_error(npp, NULL, error, "%s: XFRM 'if_id' must be in range [1..0xffffffff]", nd->id);
427+
}
428+
if (!nd->xfrm.independent && nd->xfrm.link == NULL) {
429+
return yaml_error(npp, NULL, error, "%s: Non-independent XFRM interfaces require property 'link'", nd->id);
430+
}
431+
432+
/* Ensure no xfrm if_id is used more than once */
433+
NetplanNetDefinition* existing_def = g_hash_table_lookup(npp->xfrm_if_ids, GINT_TO_POINTER(nd->xfrm.interface_id));
434+
if (existing_def != NULL && existing_def != nd) {
435+
return yaml_error(npp, NULL, error, "%s: duplicate if_id '%u' (already used by %s)",
436+
nd->id, nd->xfrm.interface_id, existing_def->id);
437+
}
438+
g_hash_table_insert(npp->xfrm_if_ids, GINT_TO_POINTER(nd->xfrm.interface_id), nd);
439+
}
440+
420441
if (nd->type == NETPLAN_DEF_TYPE_VRF) {
421442
if (nd->vrf_table == G_MAXUINT) {
422443
return yaml_error(npp, NULL, error, "%s: missing 'table' property", nd->id);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as common from "./common.js";
2+
3+
const xfrm_interfaces_schema = {
4+
type: "object",
5+
additionalProperties: false,
6+
properties: {
7+
network: {
8+
type: "object",
9+
additionalProperties: false,
10+
properties: {
11+
renderer: {
12+
type: "string",
13+
enum: ["networkd"]
14+
},
15+
version: {
16+
type: "integer",
17+
minimum: 2,
18+
maximum: 2
19+
},
20+
ethernets: {
21+
type: "object",
22+
additionalProperties: false,
23+
properties: {
24+
"eth0": {
25+
type: "object",
26+
additionalProperties: false,
27+
properties: {
28+
"dhcp4": {
29+
type: "boolean"
30+
}
31+
}
32+
}
33+
}
34+
},
35+
"xfrm-interfaces": {
36+
type: "object",
37+
additionalProperties: false,
38+
properties: {
39+
"xfrm0": {
40+
type: "object",
41+
additionalProperties: false,
42+
properties: {
43+
"if_id": {
44+
type: "integer",
45+
minimum: 1,
46+
maximum: 4294967295
47+
},
48+
"independent": {
49+
type: "boolean"
50+
},
51+
"link": {
52+
type: "string",
53+
enum: ["eth0"]
54+
},
55+
...common.common_properties
56+
},
57+
required: ["if_id"]
58+
}
59+
}
60+
}
61+
},
62+
required: ["version", "xfrm-interfaces"]
63+
}
64+
},
65+
required: ["network"]
66+
};
67+
68+
export default xfrm_interfaces_schema;

0 commit comments

Comments
 (0)