Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

routing-policy: add 'iif' and 'oif' keys to routing-policy entry #543

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions doc/netplan-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,14 @@ network:

> Match on traffic going to the specified destination.

- **`iif`** (scalar)

> Set an incoming interface to match traffic for this policy rule.

- **`oif`** (scalar)

> Set an outgoing interface to match traffic for this policy rule.

- **`table`** (scalar)

> The table number to match for the route. In some scenarios, it may be
Expand Down
2 changes: 2 additions & 0 deletions src/netplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,8 @@ write_routes(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefin
YAML_UINT_DEFAULT(def, event, emitter, "mark", r->fwmark, NETPLAN_IP_RULE_FW_MARK_UNSPEC);
YAML_STRING(def, event, emitter, "from", r->from);
YAML_STRING(def, event, emitter, "to", r->to);
YAML_STRING(def, event, emitter, "iif", r->iif);
YAML_STRING(def, event, emitter, "oif", r->oif);
YAML_MAPPING_CLOSE(event, emitter);
}
YAML_SEQUENCE_CLOSE(event, emitter);
Expand Down
5 changes: 5 additions & 0 deletions src/networkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,11 @@ write_ip_rule(NetplanIPRule* r, GString* s)
g_string_append_printf(s, "From=%s\n", r->from);
if (r->to)
g_string_append_printf(s, "To=%s\n", r->to);
if (r->iif)
g_string_append_printf(s, "IncomingInterface=%s\n", r->iif);
if (r->oif)
g_string_append_printf(s, "OutgoingInterface=%s\n", r->oif);


if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
g_string_append_printf(s, "Table=%d\n", r->table);
Expand Down
4 changes: 4 additions & 0 deletions src/nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ write_ip_rules_nm(const NetplanNetDefinition* def, GKeyFile *kf, gint family, GE
g_string_append_printf(tmp_val, " from %s", cur_rule->from);
if (cur_rule->to)
g_string_append_printf(tmp_val, " to %s", cur_rule->to);
if (cur_rule->iif)
g_string_append_printf(tmp_val, " iif %s", cur_rule->iif);
if (cur_rule->oif)
g_string_append_printf(tmp_val, " oif %s", cur_rule->oif);
if (cur_rule->tos != NETPLAN_IP_RULE_TOS_UNSPEC)
g_string_append_printf(tmp_val, " tos %u", cur_rule->tos);
if (cur_rule->fwmark != NETPLAN_IP_RULE_FW_MARK_UNSPEC)
Expand Down
28 changes: 26 additions & 2 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,21 @@ handle_generic_str(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
return TRUE;
}

/**
* Handler for setting a string ID field from a scalar node, inside a given struct
* @entryptr: pointer to the beginning of the to-be-modified data structure
* @data: offset into entryptr struct where the const char* field to write is
* located
*/
STATIC gboolean
handle_generic_id(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
{
if (!assert_valid_id(npp, node, error))
return FALSE;

return handle_generic_str(npp, node, entryptr, data, error);
}

STATIC gboolean
handle_special_macaddress_option(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
{
Expand Down Expand Up @@ -2048,6 +2063,12 @@ handle_ip_rule_ip(NetplanParser* npp, yaml_node_t* node, const void* data, GErro
return TRUE;
}

STATIC gboolean
handle_ip_rule_if(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
return handle_generic_id(npp, node, npp->current.ip_rule, (void *) data, error);
}

STATIC gboolean
handle_ip_rule_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
Expand Down Expand Up @@ -2295,6 +2316,8 @@ static const mapping_entry_handler ip_rules_handlers[] = {
{"table", YAML_SCALAR_NODE, {.generic=handle_ip_rule_guint}, ip_rule_offset(table)},
{"to", YAML_SCALAR_NODE, {.generic=handle_ip_rule_ip}, ip_rule_offset(to)},
{"type-of-service", YAML_SCALAR_NODE, {.generic=handle_ip_rule_tos}, ip_rule_offset(tos)},
{"iif", YAML_SCALAR_NODE, {.generic=handle_ip_rule_if}, ip_rule_offset(iif)},
{"oif", YAML_SCALAR_NODE, {.generic=handle_ip_rule_if}, ip_rule_offset(oif)},
{NULL}
};

Expand All @@ -2309,11 +2332,12 @@ handle_ip_rules(NetplanParser* npp, yaml_node_t* node, __unused const void* _, G
reset_ip_rule(ip_rule);

npp->current.ip_rule = ip_rule;

ret = process_mapping(npp, entry, NULL, ip_rules_handlers, NULL, error);
npp->current.ip_rule = NULL;

if (ret && !ip_rule->from && !ip_rule->to)
ret = yaml_error(npp, node, error, "IP routing policy must include either a 'from' or 'to' IP");
if (ret && !ip_rule->from && !ip_rule->to && !ip_rule->iif && !ip_rule->oif)
ret = yaml_error(npp, node, error, "IP routing policy must include at least one of the following fields: 'from', 'to', 'iif', 'oif'");

if (!ret) {
ip_rule_clear(&ip_rule);
Expand Down
5 changes: 5 additions & 0 deletions src/types-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ typedef struct {
guint fwmark;
/* type-of-service: between 0 and 255 */
guint tos;

/* incoming interface */
char* iif;
/* outgoing interface */
char* oif;
} NetplanIPRule;

struct netplan_vxlan {
Expand Down
2 changes: 2 additions & 0 deletions src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ free_ip_rules(void* ptr)
NetplanIPRule* rule = ptr;
g_free(rule->to);
g_free(rule->from);
g_free(rule->iif);
g_free(rule->oif);
g_free(rule);
}

Expand Down
4 changes: 3 additions & 1 deletion src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,9 @@ is_route_rule_present(const NetplanNetDefinition* netdef, const NetplanIPRule* r
entry->table == rule->table &&
entry->priority == rule->priority &&
entry->fwmark == rule->fwmark &&
entry->tos == rule->tos
entry->tos == rule->tos &&
g_strcmp0(entry->iif, rule->iif) == 0 &&
g_strcmp0(entry->oif, rule->oif) == 0
)
return TRUE;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/generator/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,30 @@ def test_device_ip_rule_invalid_address(self):
- 192.168.14.2/24
- 2001:FFfe::1/64''', expect_fail=True)

def test_device_ip_rule_invalid_iif(self):
self.generate('''network:
version: 2
ethernets:
engreen:
routing-policy:
- from: 10.10.10.0/24
iif: not valid iface name
addresses:
- 192.168.14.2/24
- 2001:FFfe::1/64''', expect_fail=True)

def test_device_ip_rule_invalid_oif(self):
self.generate('''network:
version: 2
ethernets:
engreen:
routing-policy:
- from: 10.10.10.0/24
oif: not valid iface name
addresses:
- 192.168.14.2/24
- 2001:FFfe::1/64''', expect_fail=True)

def test_invalid_dhcp_identifier(self):
self.generate('''network:
version: 2
Expand Down
112 changes: 112 additions & 0 deletions tests/generator/test_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,56 @@ def test_ip_rule_tos(self):
[RoutingPolicyRule]
To=10.10.10.0/24
TypeOfService=250
'''})

def test_ip_rule_iif(self):
self.generate('''network:
version: 2
ethernets:
engreen:
addresses: ["192.168.14.2/24"]
routing-policy:
- to: 10.10.10.0/24
table: 100
iif: if0
''')

self.assert_networkd({'engreen.network': '''[Match]
Name=engreen

[Network]
LinkLocalAddressing=ipv6
Address=192.168.14.2/24

[RoutingPolicyRule]
To=10.10.10.0/24
IncomingInterface=if0
Table=100
'''})

def test_ip_rule_oif(self):
self.generate('''network:
version: 2
ethernets:
engreen:
addresses: ["192.168.14.2/24"]
routing-policy:
- to: 10.10.10.0/24
table: 100
oif: if0
''')

self.assert_networkd({'engreen.network': '''[Match]
Name=engreen

[Network]
LinkLocalAddressing=ipv6
Address=192.168.14.2/24

[RoutingPolicyRule]
To=10.10.10.0/24
OutgoingInterface=if0
Table=100
'''})

def test_use_routes(self):
Expand Down Expand Up @@ -1185,6 +1235,68 @@ def test_ip_rule_tos(self):
address1=192.168.14.2/24
routing-rule1=priority 99 to 10.10.10.0/24 tos 250

[ipv6]
method=ignore
'''})

def test_ip_rule_iif(self):
self.generate('''network:
version: 2
renderer: NetworkManager
ethernets:
engreen:
addresses: ["192.168.14.2/24"]
routing-policy:
- to: 10.10.10.0/24
table: 100
priority: 99
iif: if0
''')

self.assert_nm({'engreen': '''[connection]
id=netplan-engreen
type=ethernet
interface-name=engreen

[ethernet]
wake-on-lan=0

[ipv4]
method=manual
address1=192.168.14.2/24
routing-rule1=priority 99 to 10.10.10.0/24 iif if0 table 100

[ipv6]
method=ignore
'''})

def test_ip_rule_oif(self):
self.generate('''network:
version: 2
renderer: NetworkManager
ethernets:
engreen:
addresses: ["192.168.14.2/24"]
routing-policy:
- to: 10.10.10.0/24
table: 100
priority: 99
oif: if0
''')

self.assert_nm({'engreen': '''[connection]
id=netplan-engreen
type=ethernet
interface-name=engreen

[ethernet]
wake-on-lan=0

[ipv4]
method=manual
address1=192.168.14.2/24
routing-rule1=priority 99 to 10.10.10.0/24 oif if0 table 100

[ipv6]
method=ignore
'''})
Expand Down
Loading