confd: detect bridge per-VLAN interfaces and start querierd

In a VLAN filtering bridge setup we want to be able to support an
external IGMP/MLD querier running from userspace, because the bridge
multicast code can only generate proxy/NULL querys per VLAN.

This patch is a refactor to allow just that.  If a VLAN on the bridge
has an upper interface, matching the bridge name and VID, we generate
a profile for querierd and enable the service.

For all other cases we try to disable any running querierd.  It is up
to the daemon to figure out if it has a usable IP address to use as
the query source IP or use 0.0.0.0.

Since the logic for selecting a proper IP address must be handled by
the daemon in the per-VLAN setup, we revert back to also use it for
the stand-alone unfiltered bridge case as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-03-07 12:45:55 +01:00
committed by Mattias Walström
parent 944024f394
commit b19047e320
+105 -56
View File
@@ -954,39 +954,97 @@ fail:
return fwd_mask;
}
static int vlan_mcast_settings(FILE *br, const char *brname, struct lyd_node *vlan, int vid)
static int querier_mode(const char *mode)
{
struct { const char *str; int val; } mode[] = {
struct { const char *mode; int val; } table[] = {
{ "off", 0 },
{ "proxy", 1 },
{ "auto", 2 },
};
const char *querier, *interval;
for (size_t i = 0; i < NELEMS(table); i++) {
if (strcmp(table[i].mode, mode))
continue;
return table[i].val;
}
return 0; /* unknown: off */
}
static void mcast_querier(const char *ifname, int mode, int interval)
{
FILE *fp;
DEBUG("mcast querier %s mode %d interval %d", ifname, mode, interval);
if (!mode) {
erasef("/etc/querierd/%s.conf", ifname);
systemf("initctl -bnq disable querierd@%s", ifname);
return;
}
fp = fopenf("w", "/etc/querierd/%s.conf", ifname);
if (!fp) {
ERRNO("Failed creating querier configuration for %s", ifname);
return;
}
fprintf(fp, "query-interval %d\n", interval);
fprintf(fp, "iface %s enable %sigmpv3\n", ifname, mode == 1 ? "proxy-queries " : "");
fclose(fp);
systemf("initctl -bnq enable querierd@%s", ifname);
}
static char *find_vlan_interface(sr_session_ctx_t *session, const char *brname, int vid)
{
const char *fmt = "/interfaces/interface/vlan[id=%d and lower-layer-if='%s']";
static char xpath[128];
struct lyd_node *iface;
sr_data_t *data;
int rc;
snprintf(xpath, sizeof(xpath), fmt, vid, brname);
rc = sr_get_data(session, xpath, 0, 0, 0, &data);
if (rc || !data) {
DEBUG("Skpping VLAN %d interface for %s", vid, brname);
return NULL;
}
/* On match we should not need the if(iface) checks */
iface = lydx_get_descendant(data->tree, "interfaces", "interface", NULL);
if (iface)
strlcpy(xpath, lydx_get_cattr(iface, "name"), sizeof(xpath));
sr_release_data(data);
if (iface)
return xpath;
return NULL;
}
static int vlan_mcast_settings(sr_session_ctx_t *session, FILE *br, const char *brname, struct lyd_node *vlan, int vid)
{
int interval, querier, snooping;
struct lyd_node *mcast;
int snooping, period;
size_t i;
const char *ifname;
mcast = lydx_get_descendant(lyd_child(vlan), "multicast", NULL);
if (!mcast)
return 0;
snooping = lydx_is_enabled(mcast, "snooping");
querier = lydx_get_cattr(mcast, "querier");
for (i = 0; i < NELEMS(mode); i++) {
if (!strcmp(mode[i].str, querier))
break;
}
querier = querier_mode(lydx_get_cattr(mcast, "querier"));
fprintf(br, "vlan global set vid %d dev %s mcast_snooping %d mcast_querier %d",
vid, brname, snooping, mode[i].val);
fprintf(br, "vlan global set vid %d dev %s mcast_snooping %d",
vid, brname, snooping);
if (mode[i].val == 2) {
/* XXX: gen. querierd-br.vid.conf here, stop everywhere else */
}
interval = atoi(lydx_get_cattr(mcast, "query-interval"));
ifname = find_vlan_interface(session, brname, vid);
interval = lydx_get_cattr(mcast, "query-interval");
period = atoi(interval);
fprintf(br, " mcast_query_interval %d", period * 100);
mcast_querier(ifname, querier, interval);
fprintf(br, " mcast_querier %d mcast_query_interval %d",
ifname ? 0 : querier, interval * 100);
/* XXX: make configurable later */
fprintf(br, " mcast_igmp_version 3 mcast_mld_version 2\n");
@@ -994,46 +1052,36 @@ static int vlan_mcast_settings(FILE *br, const char *brname, struct lyd_node *vl
return 0;
}
static int bridge_mcast_settings(FILE *ip, struct lyd_node *cif, int vlan_filtering)
static int bridge_mcast_settings(FILE *ip, const char *brname, struct lyd_node *cif, int vlan_filtering)
{
struct { const char *mode; int querier; int ifaddr; } mode[] = {
{ "off", 0, 0 },
{ "proxy", 1, 0 },
{ "auto", 1, 1 },
};
const char *querier, *interval;
int interval, querier, snooping;
struct lyd_node *mcast;
int snooping, period;
size_t i;
mcast = lydx_get_descendant(lyd_child(cif), "bridge", "multicast", NULL);
if (!mcast)
if (!mcast) {
mcast_querier(brname, 0, 0);
return 0;
snooping = lydx_is_enabled(mcast, "snooping");
querier = lydx_get_cattr(mcast, "querier");
for (i = 0; i < NELEMS(mode); i++) {
if (!strcmp(mode[i].mode, querier))
break;
}
fprintf(ip, " mcast_vlan_snooping %d", vlan_filtering ? snooping : 0);
/*
* When snooping is enabled per VLAN we need to use an external
* querier, otherwise the bridge will go dumpster diving for an
* IP address on the base bridge interface.
*/
fprintf(ip, " mcast_snooping %d mcast_querier %d mcast_query_use_ifaddr %d",
snooping, mode[i].querier, vlan_filtering ? 0 : mode[i].ifaddr);
snooping = lydx_is_enabled(mcast, "snooping");
querier = querier_mode(lydx_get_cattr(mcast, "querier"));
interval = lydx_get_cattr(mcast, "query-interval");
period = atoi(interval);
fprintf(ip, " mcast_query_interval %d", period * 100);
fprintf(ip, " mcast_vlan_snooping %d", vlan_filtering ? snooping : 0);
fprintf(ip, " mcast_snooping %d mcast_querier 0", snooping);
interval = atoi(lydx_get_cattr(mcast, "query-interval"));
fprintf(ip, " mcast_query_interval %d", interval * 100);
DEBUG("%s: querier %d", brname, vlan_filtering ? -1 : querier);
if (!vlan_filtering)
mcast_querier(brname, querier, interval);
else
mcast_querier(brname, 0, 0);
return 0;
}
static int netdag_gen_bridge(struct dagger *net, struct lyd_node *dif,
static int netdag_gen_bridge(sr_session_ctx_t *session, struct dagger *net, struct lyd_node *dif,
struct lyd_node *cif, FILE *ip, int add)
{
const char *brname = lydx_get_cattr(cif, "name");
@@ -1057,7 +1105,7 @@ static int netdag_gen_bridge(struct dagger *net, struct lyd_node *dif,
" vlan_filtering %d vlan_default_pvid 0",
op, brname, fwd_mask, vlan_filtering ? 1 : 0);
if ((err = bridge_mcast_settings(ip, cif, vlan_mcast)))
if ((err = bridge_mcast_settings(ip, brname, cif, vlan_mcast)))
goto done;
if (!vlan_filtering) {
@@ -1096,7 +1144,7 @@ static int netdag_gen_bridge(struct dagger *net, struct lyd_node *dif,
break;
/* need the vlans created before we can set features on them */
err = vlan_mcast_settings(br, brname, vlan, vid);
err = vlan_mcast_settings(session, br, brname, vlan, vid);
if (err)
break;
}
@@ -1180,7 +1228,7 @@ static int netdag_gen_vlan(struct dagger *net, struct lyd_node *dif,
return 0;
}
static int netdag_gen_afspec_add(struct dagger *net, struct lyd_node *dif,
static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net, struct lyd_node *dif,
struct lyd_node *cif, FILE *ip)
{
const char *ifname = lydx_get_cattr(cif, "name");
@@ -1190,7 +1238,7 @@ static int netdag_gen_afspec_add(struct dagger *net, struct lyd_node *dif,
DEBUG_IFACE(dif, "");
if (!strcmp(iftype, "infix-if-type:bridge")) {
err = netdag_gen_bridge(net, dif, cif, ip, 1);
err = netdag_gen_bridge(session, net, dif, cif, ip, 1);
} else if (!strcmp(iftype, "infix-if-type:veth")) {
err = netdag_gen_veth(net, NULL, cif, ip);
} else if (!strcmp(iftype, "infix-if-type:vlan")) {
@@ -1210,7 +1258,7 @@ static int netdag_gen_afspec_add(struct dagger *net, struct lyd_node *dif,
return 0;
}
static int netdag_gen_afspec_set(struct dagger *net, struct lyd_node *dif,
static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net, struct lyd_node *dif,
struct lyd_node *cif, FILE *ip)
{
const char *ifname = lydx_get_cattr(cif, "name");
@@ -1219,7 +1267,7 @@ static int netdag_gen_afspec_set(struct dagger *net, struct lyd_node *dif,
DEBUG_IFACE(dif, "");
if (!strcmp(iftype, "infix-if-type:bridge"))
return netdag_gen_bridge(net, dif, cif, ip, 0);
return netdag_gen_bridge(session, net, dif, cif, ip, 0);
if (!strcmp(iftype, "infix-if-type:vlan"))
return netdag_gen_vlan(net, dif, cif, ip);
if (!strcmp(iftype, "infix-if-type:veth"))
@@ -1276,6 +1324,7 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
DEBUG_IFACE(dif, "");
mcast_querier(ifname, 0, 0);
if (dagger_should_skip_current(net, ifname))
return 0;
@@ -1294,7 +1343,7 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
return 0;
}
static sr_error_t netdag_gen_iface(struct dagger *net,
static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net,
struct lyd_node *dif, struct lyd_node *cif)
{
const char *ifname = lydx_get_cattr(dif, "name");
@@ -1351,7 +1400,7 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
}
if (!fixed && op == LYDX_OP_CREATE) {
err = netdag_gen_afspec_add(net, dif, cif, ip);
err = netdag_gen_afspec_add(session, net, dif, cif, ip);
if (err)
goto err_close_ip;
}
@@ -1372,7 +1421,7 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
/* Set type specific attributes */
if (!fixed && op != LYDX_OP_CREATE) {
err = netdag_gen_afspec_set(net, dif, cif, ip);
err = netdag_gen_afspec_set(session, net, dif, cif, ip);
if (err)
goto err_close_ip;
}
@@ -1464,7 +1513,7 @@ static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *modu
lydx_get_cattr(cif, "name")))
break;
err = netdag_gen_iface(&confd->netdag, dif, cif);
err = netdag_gen_iface(session, &confd->netdag, dif, cif);
if (err)
break;
}