Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

bridges: implement vlans, port-vlans, vlan-filtering and vlan-default-pvid options #540

Open
wants to merge 3 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
3 changes: 3 additions & 0 deletions doc/.custom_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ networkd
nm
passthrough
programmatically
pvid
renderer
reselection
runtime
Expand All @@ -145,6 +146,8 @@ udev
unconfigured
unencrypted
untagged
vid
vlans
vSwitch
wpa
wpasupplicant
Expand Down
22 changes: 22 additions & 0 deletions doc/netplan-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,13 @@ The specific settings for bridges are defined below.
eth1: 20
```

- **`port-vlans`** (sequence of scalars)

> Array of bridge VLAN objects. The VLAN list can be specified with the
> following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]..
> where $vid is either a single id between 1 and 4094 or a range,
> represented as a couple of ids separated by a dash.

- **`forward-delay`** (scalar)

> Specify the period of time the bridge will remain in Listening and
Expand All @@ -1345,6 +1352,21 @@ The specific settings for bridges are defined below.
> If no time suffix is specified, the value will be interpreted as
> seconds.

- **`vlans`** (sequence of scalars)
> Array of bridge VLAN objects. The VLAN list can be specified with the
> following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]..
> where $vid is either a single id between 1 and 4094 or a range,
> represented as a couple of ids separated by a dash.

- **`vlan-filtering`** (boolean)

> Enables VLAN filtering. Will be enabled by default if *vlans* are defined.

- **`vlan-default-pvid`** (scalar)

> Specifies the default port VLAN ID. Can be set to values between 1 and 4094,
> or to value `none` if `networkd` is used as a renderer. Defaults to `1`.

- **`hello-time`** (scalar)

> Specify the interval between two hello packets being sent out from
Expand Down
16 changes: 16 additions & 0 deletions examples/bridge_port_vlans.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
network:
version: 2
renderer: networkd
ethernets:
eno1: {}
switchport: {}
bridges:
br0:
interfaces: [eno1, eno2]
parameters:
vlan-filtering: true
vlan-default-pvid: 42
vlans: [10 pvid untagged, 20 untagged, 50]
port-vlans:
eno1: [2 pvid untagged, 4 untagged]
eno2: [4000-4094, 0 pvid, 30 untagged] # 0 pvid can only be used with networkd backend
11 changes: 11 additions & 0 deletions src/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ struct netplan_net_definition {
char* max_age;
guint path_cost;
gboolean stp;
GArray* vlans;
GArray* port_vlans;
gboolean vlan_filtering;
char* vlan_default_pvid;
} bridge_params;
gboolean custom_bridging;

Expand Down Expand Up @@ -432,3 +436,10 @@ struct netplan_net_definition {

NetplanRAOverrides ra_overrides;
};

typedef struct {
guint vid; //[1..4094]
guint vid_to; //set if vid range is defined
gboolean pvid;
gboolean untagged;
} NetplanBridgeVlan;
1 change: 1 addition & 0 deletions src/generate.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ find_interface(gchar* interface, GHashTable* netdefs)

int main(int argc, char** argv)
{
g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, g_log_default_handler, NULL);
NetplanError* error = NULL;
GOptionContext* opt_context;
/* are we being called as systemd generator? */
Expand Down
50 changes: 50 additions & 0 deletions src/netplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "yaml-helpers.h"
#include "util-internal.h"
#include "names.h"
#include "nm.h"

gchar *tmp = NULL;

Expand Down Expand Up @@ -310,6 +311,55 @@ write_bridge_params(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanN
YAML_MAPPING_CLOSE(event, emitter);
}

gboolean has_port_vlans = FALSE;
for (unsigned i = 0; i < interfaces->len; ++i) {
NetplanNetDefinition *nd = g_array_index(interfaces, NetplanNetDefinition*, i);
if (nd->bridge_params.port_vlans || DIRTY(nd, nd->bridge_params.port_vlans)) {
has_port_vlans = TRUE;
break;
}
}

if (has_port_vlans) {
YAML_SCALAR_PLAIN(event, emitter, "port-vlans");
YAML_MAPPING_OPEN(event, emitter);
for (unsigned i = 0; i < interfaces->len; ++i) {
NetplanNetDefinition *nd = g_array_index(interfaces, NetplanNetDefinition*, i);
if (nd->bridge_params.port_vlans || DIRTY(nd, nd->bridge_params.port_vlans)) {
YAML_SCALAR_PLAIN(event, emitter, nd->id);
YAML_SEQUENCE_OPEN(event, emitter);
for (unsigned i = 0; i < nd->bridge_params.port_vlans->len; ++i) {
NetplanBridgeVlan* vlan = g_array_index(nd->bridge_params.port_vlans, NetplanBridgeVlan*, i);
GString* v = bridge_vlan_str(vlan);
YAML_SCALAR_PLAIN(event, emitter, v->str);
g_string_free(v, TRUE);
}
YAML_SEQUENCE_CLOSE(event, emitter);
}

}
YAML_MAPPING_CLOSE(event, emitter);
}

if (def->bridge_params.vlan_filtering || def->bridge_params.vlans || DIRTY(def, def->bridge_params.vlans)) {
YAML_STRING(def, event, emitter, "vlan-filtering", "true");
if (def->bridge_params.vlans || DIRTY(def, def->bridge_params.vlans)) {
YAML_SCALAR_PLAIN(event, emitter, "vlans");
YAML_SEQUENCE_OPEN(event, emitter);
for (unsigned i = 0; i < def->bridge_params.vlans->len; ++i) {
NetplanBridgeVlan* vlan = g_array_index(def->bridge_params.vlans, NetplanBridgeVlan*, i);
GString* v = bridge_vlan_str(vlan);
YAML_SCALAR_PLAIN(event, emitter, v->str);
g_string_free(v, TRUE);
}
YAML_SEQUENCE_CLOSE(event, emitter);
}
}

if (def->bridge_params.vlan_default_pvid) {
YAML_STRING(def, event, emitter, "vlan-default-pvid", def->bridge_params.vlan_default_pvid);
}

YAML_MAPPING_CLOSE(event, emitter);
}
return TRUE;
Expand Down
54 changes: 54 additions & 0 deletions src/networkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ write_bridge_params_networkd(GString* s, const NetplanNetDefinition* def)
if (def->bridge_params.max_age)
g_string_append_printf(params, "MaxAgeSec=%s\n", def->bridge_params.max_age);
g_string_append_printf(params, "STP=%s\n", def->bridge_params.stp ? "true" : "false");
if (def->bridge_params.vlan_default_pvid)
g_string_append_printf(params, "DefaultPVID=%s\n", def->bridge_params.vlan_default_pvid);
if(def->bridge_params.vlan_filtering || def->bridge_params.vlans)
g_string_append_printf(params, "VLANFiltering=true\n");

g_string_append_printf(s, "\n[Bridge]\n%s", params->str);

Expand Down Expand Up @@ -831,6 +835,47 @@ combine_dhcp_overrides(const NetplanNetDefinition* def, NetplanDHCPOverrides* co
return TRUE;
}

/**
* Return networkd vlan string.
*/
GString*
bridge_vlan_networkd_str(const NetplanBridgeVlan* vlan)
{
GString *id = g_string_sized_new(9);
GString *def = g_string_sized_new(200);

g_string_append_printf(id, "%u", vlan->vid);
if (vlan->vid_to)
g_string_append_printf(id, "-%u", vlan->vid_to);


if (vlan->pvid)
g_string_append_printf(def, "PVID=%s\n", id->str);
else {
g_string_append_printf(def, "VLAN=%s\n", id->str);
}

if (vlan->untagged)
g_string_append_printf(def, "EgressUntagged=%s\n", id->str);

g_string_free(id, TRUE);

return def;
}

/**
* Write the needed networkd .network BridgeVLAN configuration for the selected vlan definition.
*/
STATIC void
write_vlans(GString_autoptr network, GArray* data) {
g_string_append(network, "\n[BridgeVLAN]\n");
for (unsigned i = 0; i < data->len; ++i) {
GString* v = bridge_vlan_networkd_str(g_array_index(data,NetplanBridgeVlan*, i));
g_string_append_printf(network, "%s", v->str);
g_string_free(v, TRUE);
}
}

/**
* Write the needed networkd .network configuration for the selected netplan definition.
*/
Expand Down Expand Up @@ -982,8 +1027,17 @@ _netplan_netdef_write_network_file(
g_string_append_printf(network, "Learning=%s\n", def->bridge_learning ? "true" : "false");
if (def->bridge_neigh_suppress != NETPLAN_TRISTATE_UNSET)
g_string_append_printf(network, "NeighborSuppression=%s\n", def->bridge_neigh_suppress ? "true" : "false");
if (def->bridge_params.port_vlans) {
// port's .network file
write_vlans(network, def->bridge_params.port_vlans);
}
}

if (!def->bridge && !def->bond && def->backend != NETPLAN_BACKEND_OVS && def->bridge_params.vlans) {
// bridge's .network file
write_vlans(network, def->bridge_params.vlans);
}

if (def->bond && def->backend != NETPLAN_BACKEND_OVS) {
g_string_append_printf(network, "Bond=%s\n", def->bond);

Expand Down
52 changes: 52 additions & 0 deletions src/nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,25 @@ wifi_band_str(const NetplanWifiBand band)
}
}

/**
* Return NM bridge vlan string.
*/
GString*
bridge_vlan_str(const NetplanBridgeVlan* vlan)
{
GString* s = NULL;
s = g_string_sized_new(24);

g_string_append_printf(s, "%u", vlan->vid);
if (vlan->vid_to)
g_string_append_printf(s, "-%u", vlan->vid_to);
if (vlan->pvid)
g_string_append(s, " pvid");
if (vlan->untagged)
g_string_append(s, " untagged");
return s;
}

/**
* Return NM addr-gen-mode string.
*/
Expand Down Expand Up @@ -376,7 +395,10 @@ write_nm_bond_parameters(const NetplanNetDefinition* def, GKeyFile *kf)
STATIC void
write_bridge_params_nm(const NetplanNetDefinition* def, GKeyFile *kf)
{
g_autoptr(GString) vlans = NULL;

if (def->custom_bridging) {
vlans = g_string_sized_new(200);
if (def->bridge_params.ageing_time)
g_key_file_set_string(kf, "bridge", "ageing-time", def->bridge_params.ageing_time);
if (def->bridge_params.priority)
Expand All @@ -388,6 +410,25 @@ write_bridge_params_nm(const NetplanNetDefinition* def, GKeyFile *kf)
if (def->bridge_params.max_age)
g_key_file_set_string(kf, "bridge", "max-age", def->bridge_params.max_age);
g_key_file_set_boolean(kf, "bridge", "stp", def->bridge_params.stp);
if(def->bridge_params.vlan_filtering || def->bridge_params.vlans)
g_key_file_set_string(kf, "bridge", "vlan-filtering", "true");
if (def->bridge_params.vlan_default_pvid) {
if (g_str_equal(def->bridge_params.vlan_default_pvid, "none")) {
g_fprintf(stderr, "ERROR: vlan-default-pvid cannot be set to 'none' if NetworkManager is used\n");
exit(1);
}
g_key_file_set_string(kf, "bridge", "vlan-default-pvid", def->bridge_params.vlan_default_pvid);
}
if (def->bridge_params.vlans) {
for (unsigned i = 0; i < def->bridge_params.vlans->len; ++i) {
if (i > 0)
g_string_append(vlans, ", ");
GString* v = bridge_vlan_str(g_array_index(def->bridge_params.vlans,NetplanBridgeVlan*, i));
g_string_append_printf(vlans, "%s", v->str);
g_string_free(v, TRUE);
}
g_key_file_set_string(kf, "bridge", "vlans", vlans->str);
}
}
}

Expand Down Expand Up @@ -824,6 +865,17 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
g_key_file_set_uint64(kf, "bridge-port", "priority", def->bridge_params.port_priority);
if (def->bridge_hairpin != NETPLAN_TRISTATE_UNSET)
g_key_file_set_boolean(kf, "bridge-port", "hairpin-mode", def->bridge_hairpin);
if (def->bridge_params.port_vlans) {
g_autoptr(GString) vlans = g_string_sized_new(200);
for (unsigned i = 0; i < def->bridge_params.port_vlans->len; ++i) {
if (i > 0)
g_string_append(vlans, ", ");
GString* v = bridge_vlan_str(g_array_index(def->bridge_params.port_vlans,NetplanBridgeVlan*, i));
g_string_append_printf(vlans, "%s", v->str);
g_string_free(v, TRUE);
}
g_key_file_set_string(kf, "bridge-port", "vlans", vlans->str);
}
}
if (def->bond) {
g_key_file_set_string(kf, "connection", "slave-type", "bond"); /* wokeignore:rule=slave */
Expand Down
3 changes: 3 additions & 0 deletions src/nm.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ _netplan_netdef_write_nm(

NETPLAN_INTERNAL gboolean
_netplan_nm_cleanup(const char* rootdir);

NETPLAN_INTERNAL GString*
bridge_vlan_str(const NetplanBridgeVlan* vlan);
Loading
Loading