From 3ebbd0bf4f154ea9ea668d11b3a8d3a702552b6b Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 21 Mar 2024 05:47:49 +0100 Subject: [PATCH] confd: handle small changes to bridge port properties This patch adds support for small incremental changes to bridge ports. Changes where the libyang diff does not include a bridge reference so that it will have to be synthesized from the current configuration. For example, forgetting to set a port's PVID in a VLAN filtering bridge setup, changing a bridge port's multicast flooding properties, or any other per-bridge-port setting after the fact. Fixes #349. Signed-off-by: Joachim Wiberg --- src/confd/src/ietf-interfaces.c | 93 +++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/src/confd/src/ietf-interfaces.c b/src/confd/src/ietf-interfaces.c index c22ad4b8..b7ed76e6 100644 --- a/src/confd/src/ietf-interfaces.c +++ b/src/confd/src/ietf-interfaces.c @@ -745,6 +745,57 @@ static int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct l return 0; } +static void brport_pvid_adjust(FILE *br, struct lyd_node *vlan, int vid, const char *brport, + struct lydx_diff *pvidiff, int tagged) +{ + const char *type = tagged ? "tagged" : "untagged"; + struct lyd_node *port; + + LYX_LIST_FOR_EACH(lyd_child(vlan), port, type) { + if (strcmp(brport, lyd_get_value(port))) + continue; + + if (pvidiff->old && atoi(pvidiff->old) == vid) + fprintf(br, "vlan add vid %d dev %s %s\n", vid, brport, type); + if (pvidiff->new && atoi(pvidiff->new) == vid) + fprintf(br, "vlan add vid %d dev %s pvid %s\n", vid, brport, type); + } +} + +/* + * Called when only pvid is changed for a bridge-port. Then we use the + * cif data to iterate over all known VLANS for the given port. + */ +static int bridge_port_vlans(struct dagger *net, struct lyd_node *cif, const char *brname, + const char *brport, struct lydx_diff *pvidiff) +{ + struct lyd_node *bridge = lydx_find_by_name(lyd_parent(cif), "interface", brname); + struct lyd_node *vlan, *vlans; + int err = 0; + FILE *br; + + vlans = lydx_get_descendant(lyd_child(bridge), "bridge", "vlans", NULL); + if (!vlans) + goto done; + + br = dagger_fopen_next(net, "init", brname, 60, "init.bridge"); + if (!br) { + err = -EIO; + goto done; + } + + LYX_LIST_FOR_EACH(lyd_child(vlans), vlan, "vlan") { + int vid = atoi(lydx_get_cattr(vlan, "vid")); + + brport_pvid_adjust(br, vlan, vid, brport, pvidiff, 0); + brport_pvid_adjust(br, vlan, vid, brport, pvidiff, 1); + } + + fclose(br); +done: + return err; +} + static int bridge_diff_vlan_port(struct dagger *net, FILE *br, const char *brname, int vid, const char *brport, int tagged, enum lydx_op op) { @@ -877,20 +928,52 @@ static void bridge_port_settings(FILE *next, const char *ifname, struct lyd_node static int bridge_gen_ports(struct dagger *net, struct lyd_node *dif, struct lyd_node *cif, FILE *ip) { + const char *ifname = lydx_get_cattr(cif, "name"); struct lyd_node *node, *bridge; struct lydx_diff brdiff; - const char *ifname; int err = 0; if (!is_bridge_port(cif)) return 0; node = lydx_get_descendant(lyd_child(dif), "bridge-port", NULL); - bridge = lydx_get_child(node, "bridge"); - if (!node || !bridge) - return 0; /* bridge port but no diff, skip */ + if (!node) + goto fail; - ifname = lydx_get_cattr(cif, "name"); + /* + * If bridge is not in dif, then we only have bridge-port + * settings and can use cif instead for any new settings + * since we always set *all* port settings anyway. + */ + bridge = lydx_get_child(node, "bridge"); + if (!bridge) { + struct lyd_node *pvid = lydx_get_child(node, "pvid"); + struct lydx_diff pvidiff; + const char *brname; + FILE *next; + + node = lydx_get_descendant(lyd_child(cif), "bridge-port", NULL); + brname = lydx_get_cattr(node, "bridge"); + if (!node || !brname) + goto fail; + + next = dagger_fopen_next(net, "init", ifname, 56, "init.ip"); + if (!next) { + err = -EIO; + goto fail; + } + bridge_port_settings(next, ifname, cif); + fclose(next); + + /* Change in bridge port's PVID => change in VLAN port memberships */ + if (lydx_get_diff(pvid, &pvidiff)) + bridge_port_vlans(net, cif, brname, ifname, &pvidiff); + + err = dagger_add_dep(net, brname, ifname); + if (err) + return ERR_IFACE(cif, err, "Unable to add dep \"%s\" to %s", ifname, brdiff); + goto fail; + } if (lydx_get_diff(bridge, &brdiff) && brdiff.old) { FILE *prev;