mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-02 13:53:01 +02:00
Merge pull request #870 from kernelkit/confd-ifaces-split
confd: Split up ietf-interfaces.c
This commit is contained in:
@@ -26,7 +26,12 @@ confd_plugin_la_SOURCES = \
|
||||
base64.c base64.h \
|
||||
core.c core.h \
|
||||
dagger.c dagger.h \
|
||||
ietf-interfaces.c \
|
||||
ietf-interfaces.c ietf-interfaces.h \
|
||||
ieee802-ethernet-interface.c \
|
||||
ietf-ip.c \
|
||||
infix-if-bridge.c \
|
||||
infix-if-veth.c \
|
||||
infix-if-vlan.c \
|
||||
ietf-keystore.c \
|
||||
ietf-system.c \
|
||||
ietf-syslog.c \
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdbool.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "ietf-interfaces.h"
|
||||
|
||||
static bool iface_uses_autoneg(struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *aneg = lydx_get_descendant(lyd_child(cif), "ethernet",
|
||||
"auto-negotiation", NULL);
|
||||
|
||||
/* Because `ieee802-ethernet-interface` declares
|
||||
* `auto-negotiation` as a presence container, the `enabled`
|
||||
* leaf, although `true` by default, is not set if the whole
|
||||
* container is absent. Since auto-negotiation is the expected
|
||||
* default behavior for most Ethernet links, we choose to
|
||||
* enable it in these situations.
|
||||
*/
|
||||
return !aneg || lydx_get_bool(aneg, "enable");
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: always disable flow control, for now, until we've added
|
||||
* configurable support for flow-control/pause/direction and
|
||||
* flow-control/force-flow-control
|
||||
*/
|
||||
static int netdag_gen_ethtool_flow_control(struct dagger *net, struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
FILE *fp;
|
||||
|
||||
fp = dagger_fopen_next(net, "init", ifname, 10, "ethtool-aneg.sh");
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "ethtool --pause %s autoneg %s rx off tx off\n",
|
||||
ifname, iface_uses_autoneg(cif) ? "on" : "off");
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_ethtool_autoneg(struct dagger *net, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *eth = lydx_get_child(cif, "ethernet");
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
const char *speed, *duplex;
|
||||
int mbps, err = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = dagger_fopen_next(net, "init", ifname, 10, "ethtool-aneg.sh");
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "ethtool --change %s autoneg ", ifname);
|
||||
|
||||
if (iface_uses_autoneg(cif)) {
|
||||
fputs("on\n", fp);
|
||||
} else {
|
||||
speed = lydx_get_cattr(eth, "speed");
|
||||
if (!speed) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"speed\" must be specified "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mbps = (int)(atof(speed) * 1000.);
|
||||
if (!((mbps == 10) || (mbps == 100))) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"speed\" must be either 0.01 or 0.1 "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
duplex = lydx_get_cattr(eth, "duplex");
|
||||
if (!duplex || (strcmp(duplex, "full") && strcmp(duplex, "half"))) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"duplex\" must be either "
|
||||
"\"full\" or \"half\" "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(fp,"off speed %d duplex %s\n", mbps, duplex);
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *eth = lydx_get_child(dif, "ethernet");
|
||||
const char *type = lydx_get_cattr(cif, "type");
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Story time: when assigning a physical interface to a container, and then
|
||||
* removing it, even though our type may be 'etherlike' we will
|
||||
* get the following from sysrepo:
|
||||
*
|
||||
* "ieee802-ethernet-interface:ethernet": {
|
||||
* "@": {
|
||||
* "yang:operation": "delete"
|
||||
* },
|
||||
* "duplex": "full"
|
||||
* },
|
||||
*
|
||||
* Hence this "redundant" check.
|
||||
*/
|
||||
if (strcmp(type, "infix-if-type:ethernet"))
|
||||
return 0;
|
||||
|
||||
if (!eth)
|
||||
return 0;
|
||||
|
||||
if (dagger_is_bootstrap(net) ||
|
||||
lydx_get_descendant(lyd_child(eth), "auto-negotiation", "enable", NULL)) {
|
||||
err = netdag_gen_ethtool_flow_control(net, cif);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dagger_is_bootstrap(net) ||
|
||||
lydx_get_descendant(lyd_child(eth), "auto-negotiation", "enable", NULL) ||
|
||||
lydx_get_child(eth, "speed") ||
|
||||
lydx_get_child(eth, "duplex")) {
|
||||
err = netdag_gen_ethtool_autoneg(net, cif);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+3
-1277
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#ifndef CONFD_IETF_INTERFACES_H_
|
||||
#define CONFD_IETF_INTERFACES_H_
|
||||
|
||||
#include "core.h"
|
||||
#include "cni.h"
|
||||
|
||||
#define IF_XPATH "/ietf-interfaces:interfaces/interface"
|
||||
#define IF_VLAN_XPATH "%s/infix-interfaces:vlan"
|
||||
|
||||
#define ERR_IFACE(_iface, _err, _fmt, ...) \
|
||||
({ \
|
||||
ERROR("%s: " _fmt, lydx_get_cattr(_iface, "name"), \
|
||||
##__VA_ARGS__); \
|
||||
_err; \
|
||||
})
|
||||
|
||||
#define DEBUG_IFACE(_iface, _fmt, ...) \
|
||||
DEBUG("%s: " _fmt, lydx_get_cattr(_iface, "name"), ##__VA_ARGS__)
|
||||
|
||||
#define ONOFF(boolean) boolean ? "on" : "off"
|
||||
|
||||
static inline const char *bridge_tagtype2str(const char *type)
|
||||
{
|
||||
if (!strcmp(type, "ieee802-dot1q-types:c-vlan"))
|
||||
return "802.1Q";
|
||||
else if (!strcmp(type, "ieee802-dot1q-types:s-vlan"))
|
||||
return "802.1ad";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool is_bridge_port(struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *node = lydx_get_descendant(lyd_child(cif), "bridge-port", NULL);
|
||||
|
||||
if (!node || !lydx_get_child(node, "bridge"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ieee802-ethernet-interface.c */
|
||||
int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif);
|
||||
|
||||
/* ietf-interfaces.c */
|
||||
char *get_phys_addr(struct lyd_node *parent, int *deleted);
|
||||
int netdag_exit_reload(struct dagger *net);
|
||||
|
||||
/* ietf-ip.c */
|
||||
int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif, FILE *ip);
|
||||
int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif);
|
||||
int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif);
|
||||
|
||||
/* infix-if-bridge.c */
|
||||
void mcast_querier(const char *ifname, int vid, int mode, int interval);
|
||||
int bridge_gen_ports(struct dagger *net, struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
|
||||
int netdag_gen_bridge(sr_session_ctx_t *session, struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip, int add);
|
||||
|
||||
/* infix-if-veth.c */
|
||||
int ifchange_cand_infer_veth(sr_session_ctx_t *session, const char *path);
|
||||
int netdag_gen_veth(struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip);
|
||||
|
||||
/* infix-if-vlan.c */
|
||||
int ifchange_cand_infer_vlan(sr_session_ctx_t *session, const char *path);
|
||||
int netdag_gen_vlan(struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip);
|
||||
|
||||
#endif /* CONFD_IETF_INTERFACES_H_ */
|
||||
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdbool.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "ietf-interfaces.h"
|
||||
|
||||
int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif, FILE *ip)
|
||||
{
|
||||
const char *preferred_lft = "86400", *valid_lft = "604800";
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, "ipv6");
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
int global = 0, random = 0;
|
||||
struct lyd_node *node;
|
||||
FILE *fp;
|
||||
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif)) {
|
||||
fputs(" addrgenmode none", ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = lydx_get_child(ipconf, "autoconf");
|
||||
if (node) {
|
||||
global = lydx_is_enabled(node, "create-global-addresses");
|
||||
random = lydx_is_enabled(node, "create-temporary-addresses");
|
||||
|
||||
preferred_lft = lydx_get_cattr(node, "temporary-preferred-lifetime");
|
||||
valid_lft = lydx_get_cattr(node, "temporary-valid-lifetime");
|
||||
}
|
||||
|
||||
/* 51: must run after interfaces have been created (think: bridge, veth) */
|
||||
fp = dagger_fopen_next(net, "init", ifname, 51, "init.sysctl");
|
||||
if (fp) {
|
||||
/* Autoconfigure addresses using Prefix Information in Router Advertisements */
|
||||
fprintf(fp, "net.ipv6.conf.%s.autoconf = %d\n", ifname, global);
|
||||
/* The amount of Duplicate Address Detection probes to send. */
|
||||
fprintf(fp, "net.ipv6.conf.%s.dad_transmits = %s\n", ifname,
|
||||
lydx_get_cattr(ipconf, "dup-addr-detect-transmits"));
|
||||
/* Preferred and valid lifetimes for temporary (random) addresses */
|
||||
fprintf(fp, "net.ipv6.conf.%s.temp_prefered_lft = %s\n", ifname, preferred_lft);
|
||||
fprintf(fp, "net.ipv6.conf.%s.temp_valid_lft = %s\n", ifname, valid_lft);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
fprintf(ip, " addrgenmode %s", random ? "random" : "eui64");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if ipv4 is enabled, only then can autoconf be enabled, in all
|
||||
* other cases it must be disabled. Since we have multiple settings in
|
||||
* autoconf, we check if either is modified (diff), in which case we not
|
||||
* only enable, but also "touch" the Finit service for avahi-autoipd to
|
||||
* ensure it is (re)started.
|
||||
*
|
||||
* Note: in Infix, regardless of the IPv4 configuration, any link-local
|
||||
* link-local address is disabled when the interface is being used
|
||||
* as a bridge port.
|
||||
*
|
||||
* Also, IPv4LL is not defined for loopback, so always skip there.
|
||||
*/
|
||||
int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, "ipv4");
|
||||
struct lyd_node *ipdiff = lydx_get_child(dif, "ipv4");
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
struct lyd_node *zcip;
|
||||
char defaults[64];
|
||||
FILE *initctl;
|
||||
int err = 0;
|
||||
|
||||
if (!strcmp(ifname, "lo"))
|
||||
return 0;
|
||||
|
||||
/* client defults for this interface, needed in both cases */
|
||||
snprintf(defaults, sizeof(defaults), "/etc/default/zeroconf-%s", ifname);
|
||||
|
||||
/* no ipv4 at all, ipv4 selectively disabled, or interface is a bridge port */
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif))
|
||||
goto disable;
|
||||
|
||||
/*
|
||||
* when enabled, we may have been enabled before, but skipped
|
||||
* for various reasons: was bridge port, ipv4 was disabled...
|
||||
*/
|
||||
zcip = lydx_get_child(ipconf, "autoconf");
|
||||
if (zcip && lydx_is_enabled(zcip, "enabled")) {
|
||||
struct lyd_node *node;
|
||||
const char *addr;
|
||||
int diff = 0;
|
||||
FILE *fp;
|
||||
|
||||
/* check for any changes in this container */
|
||||
node = lydx_get_child(ipdiff, "autoconf");
|
||||
if (node) {
|
||||
const struct lyd_node *tmp;
|
||||
|
||||
tmp = lydx_get_child(node, "enabled");
|
||||
if (tmp)
|
||||
diff++;
|
||||
tmp = lydx_get_child(node, "request-address");
|
||||
if (tmp)
|
||||
diff++;
|
||||
}
|
||||
|
||||
fp = fopen(defaults, "w");
|
||||
if (!fp) {
|
||||
ERRNO("Failed creating %s, cannot enable IPv4LL on %s", defaults, ifname);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(fp, "ZEROCONF_ARGS=\"--force-bind --syslog ");
|
||||
addr = lydx_get_cattr(zcip, "request-address");
|
||||
if (addr)
|
||||
fprintf(fp, "--start=%s", addr);
|
||||
fprintf(fp, "\"\n");
|
||||
fclose(fp);
|
||||
|
||||
initctl = dagger_fopen_next(net, "init", ifname, 60, "zeroconf-up.sh");
|
||||
if (!initctl)
|
||||
return -EIO;
|
||||
|
||||
/* on enable, or reactivation, it is enough to ensure the service is enabled */
|
||||
fprintf(initctl, "initctl -bnq enable zeroconf@%s.conf\n", ifname);
|
||||
/* on changes to autoconf we must ensure Finit restarts the service */
|
||||
if (diff)
|
||||
fprintf(initctl, "initctl -bnq touch zeroconf@%s.conf\n", ifname);
|
||||
} else {
|
||||
disable:
|
||||
initctl = dagger_fopen_current(net, "exit", ifname, 40, "zeroconf-down.sh");
|
||||
if (!initctl) {
|
||||
/* check if in bootstrap (pre gen 0) */
|
||||
if (errno == EUNATCH)
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(initctl, "initctl -bnq disable zeroconf@%s.conf\n", ifname);
|
||||
fprintf(initctl, "rm -f %s\n", defaults);
|
||||
err = netdag_exit_reload(net);
|
||||
}
|
||||
|
||||
fclose(initctl);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static bool is_std_lo_addr(const char *ifname, const char *ip, const char *pf)
|
||||
{
|
||||
struct in6_addr in6, lo6;
|
||||
struct in_addr in4;
|
||||
|
||||
if (strcmp(ifname, "lo"))
|
||||
return false;
|
||||
|
||||
if (inet_pton(AF_INET, ip, &in4) == 1)
|
||||
return (ntohl(in4.s_addr) == INADDR_LOOPBACK) && !strcmp(pf, "8");
|
||||
|
||||
if (inet_pton(AF_INET6, ip, &in6) == 1) {
|
||||
inet_pton(AF_INET6, "::1", &lo6);
|
||||
|
||||
return !memcmp(&in6, &lo6, sizeof(in6))
|
||||
&& !strcmp(pf, "128");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int netdag_gen_diff_addr(FILE *ip, const char *ifname,
|
||||
struct lyd_node *addr)
|
||||
{
|
||||
enum lydx_op op = lydx_get_op(addr);
|
||||
struct lyd_node *adr, *pfx;
|
||||
struct lydx_diff adrd, pfxd;
|
||||
const char *addcmd = "add";
|
||||
|
||||
adr = lydx_get_child(addr, "ip");
|
||||
pfx = lydx_get_child(addr, "prefix-length");
|
||||
if (!adr || !pfx)
|
||||
return -EINVAL;
|
||||
|
||||
lydx_get_diff(adr, &adrd);
|
||||
lydx_get_diff(pfx, &pfxd);
|
||||
|
||||
if (op != LYDX_OP_CREATE) {
|
||||
fprintf(ip, "address delete %s/%s dev %s\n",
|
||||
adrd.old, pfxd.old, ifname);
|
||||
|
||||
if (op == LYDX_OP_DELETE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* When bringing up loopback, the kernel will automatically
|
||||
* add the standard addresses, so don't treat the existance of
|
||||
* these as an error.
|
||||
*/
|
||||
if ((op == LYDX_OP_CREATE) &&
|
||||
is_std_lo_addr(ifname, adrd.new, pfxd.new))
|
||||
addcmd = "replace";
|
||||
|
||||
fprintf(ip, "address %s %s/%s dev %s proto 4\n", addcmd,
|
||||
adrd.new, pfxd.new, ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_diff_addrs(FILE *ip, const char *ifname,
|
||||
struct lyd_node *ipvx)
|
||||
{
|
||||
struct lyd_node *addr;
|
||||
int err = 0;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ipvx), addr, "address") {
|
||||
err = netdag_gen_diff_addr(ip, ifname, addr);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int netdag_set_conf_addrs(FILE *ip, const char *ifname,
|
||||
struct lyd_node *ipvx)
|
||||
{
|
||||
struct lyd_node *addr;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ipvx), addr, "address") {
|
||||
fprintf(ip, "address add %s/%s dev %s\n",
|
||||
lydx_get_cattr(addr, "ip"),
|
||||
lydx_get_cattr(addr, "prefix-length"),
|
||||
ifname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, proto);
|
||||
struct lyd_node *ipdiff = lydx_get_child(dif, proto);
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled")) {
|
||||
if (!cni_find(ifname) && if_nametoindex(ifname)) {
|
||||
FILE *fp;
|
||||
|
||||
fp = dagger_fopen_current(net, "exit", ifname, 49, "flush.sh");
|
||||
if (fp) {
|
||||
fprintf(fp, "ip -%c addr flush dev %s\n", proto[3], ifname);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lydx_get_op(lydx_get_child(ipdiff, "enabled")) == LYDX_OP_REPLACE)
|
||||
return netdag_set_conf_addrs(ip, ifname, ipconf);
|
||||
|
||||
return netdag_gen_diff_addrs(ip, ifname, ipdiff);
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdbool.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "ietf-interfaces.h"
|
||||
|
||||
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 void bridge_remove_vlan_ports(struct dagger *net, FILE *br, const char *brname,
|
||||
int vid, struct lyd_node *ports, int tagged)
|
||||
{
|
||||
struct lyd_node *port;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ports), port, tagged ? "tagged" : "untagged") {
|
||||
enum lydx_op op = lydx_get_op(port);
|
||||
const char *brport = lyd_get_value(port);
|
||||
|
||||
if (op != LYDX_OP_CREATE) {
|
||||
fprintf(br, "vlan del vid %d dev %s\n", vid, brport);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void bridge_add_vlan_ports(struct dagger *net, FILE *br, const char *brname,
|
||||
int vid, struct lyd_node *ports, int tagged)
|
||||
{
|
||||
struct lyd_node *port;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ports), port, tagged ? "tagged" : "untagged") {
|
||||
enum lydx_op op = lydx_get_op(port);
|
||||
const char *brport = lyd_get_value(port);
|
||||
|
||||
if (op != LYDX_OP_DELETE) {
|
||||
int pvid = 0;
|
||||
srx_get_int(net->session, &pvid, SR_UINT16_T, IF_XPATH "[name='%s']/bridge-port/pvid", brport);
|
||||
|
||||
fprintf(br, "vlan add vid %d dev %s %s %s %s\n", vid, brport, vid == pvid ? "pvid" : "",
|
||||
tagged ? "" : "untagged", strcmp(brname, brport) ? "" : "self");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int bridge_diff_vlan_ports(struct dagger *net, FILE *br, const char *brname,
|
||||
int vid, struct lyd_node *ports)
|
||||
{
|
||||
/* First remove all VLANs that should that should be removed, see #676 */
|
||||
bridge_remove_vlan_ports(net, br, brname, vid, ports, 0);
|
||||
bridge_remove_vlan_ports(net, br, brname, vid, ports, 1);
|
||||
|
||||
bridge_add_vlan_ports(net, br, brname, vid, ports, 0);
|
||||
bridge_add_vlan_ports(net, br, brname, vid, ports, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bridge_vlan_settings(struct lyd_node *cif, const char **proto, int *vlan_mcast)
|
||||
{
|
||||
struct lyd_node *vlans, *vlan;
|
||||
|
||||
vlans = lydx_get_descendant(lyd_child(cif), "bridge", "vlans", NULL);
|
||||
if (vlans) {
|
||||
const char *type = lydx_get_cattr(vlans, "proto");
|
||||
int num = 0;
|
||||
|
||||
*proto = bridge_tagtype2str(type);
|
||||
LYX_LIST_FOR_EACH(lyd_child(vlans), vlan, "vlan") {
|
||||
struct lyd_node *mcast;
|
||||
|
||||
mcast = lydx_get_descendant(lyd_child(vlan), "multicast", NULL);
|
||||
if (mcast)
|
||||
*vlan_mcast += lydx_is_enabled(mcast, "snooping");
|
||||
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bridge_port_settings(FILE *next, const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *bp, *flood, *mcast;
|
||||
int ucflood = 1; /* default: flood unknown unicast */
|
||||
|
||||
bp = lydx_get_descendant(lyd_child(cif), "bridge-port", NULL);
|
||||
if (!bp)
|
||||
return;
|
||||
|
||||
fprintf(next, "link set %s type bridge_slave", ifname);
|
||||
flood = lydx_get_child(bp, "flood");
|
||||
if (flood) {
|
||||
ucflood = lydx_is_enabled(flood, "unicast");
|
||||
|
||||
fprintf(next, " bcast_flood %s", ONOFF(lydx_is_enabled(flood, "broadcast")));
|
||||
fprintf(next, " flood %s", ONOFF(ucflood));
|
||||
fprintf(next, " mcast_flood %s", ONOFF(lydx_is_enabled(flood, "multicast")));
|
||||
}
|
||||
|
||||
if (ucflood) {
|
||||
/* proxy arp must be disabled while flood on, see man page */
|
||||
fprintf(next, " proxy_arp off");
|
||||
fprintf(next, " proxy_arp_wifi off");
|
||||
} else {
|
||||
/* XXX: proxy arp/wifi settings here */
|
||||
}
|
||||
|
||||
mcast = lydx_get_child(bp, "multicast");
|
||||
if (mcast) {
|
||||
const char *router = lydx_get_cattr(mcast, "router");
|
||||
struct { const char *str; int val; } xlate[] = {
|
||||
{ "off", 0 },
|
||||
{ "auto", 1 },
|
||||
{ "permanent", 2 },
|
||||
};
|
||||
int mrouter = 1;
|
||||
|
||||
for (size_t i = 0; i < NELEMS(xlate); i++) {
|
||||
if (strcmp(xlate[i].str, router))
|
||||
continue;
|
||||
|
||||
mrouter = xlate[i].val;
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(next, " mcast_fast_leave %s mcast_router %d",
|
||||
ONOFF(lydx_is_enabled(mcast, "fast-leave")),
|
||||
mrouter);
|
||||
}
|
||||
fprintf(next, "\n");
|
||||
}
|
||||
|
||||
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;
|
||||
int err = 0;
|
||||
|
||||
node = lydx_get_descendant(lyd_child(dif), "bridge-port", NULL);
|
||||
if (!node)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* 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, brname);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (lydx_get_diff(bridge, &brdiff) && brdiff.old) {
|
||||
FILE *prev;
|
||||
|
||||
prev = dagger_fopen_current(net, "exit", brdiff.old, 55, "exit.ip");
|
||||
if (!prev) {
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
fprintf(prev, "link set %s nomaster\n", ifname);
|
||||
fclose(prev);
|
||||
}
|
||||
|
||||
if (brdiff.new) {
|
||||
FILE *next;
|
||||
|
||||
next = dagger_fopen_next(net, "init", brdiff.new, 55, "init.ip");
|
||||
if (!next) {
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
fprintf(next, "link set %s master %s\n", ifname, brdiff.new);
|
||||
bridge_port_settings(next, ifname, cif);
|
||||
fclose(next);
|
||||
|
||||
err = dagger_add_dep(net, brdiff.new, ifname);
|
||||
if (err)
|
||||
return ERR_IFACE(cif, err, "Unable to add dep \"%s\" to %s", ifname, brdiff.new);
|
||||
}
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bridge_fwd_mask(struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *node, *proto;
|
||||
int fwd_mask = 0;
|
||||
|
||||
node = lydx_get_descendant(lyd_child(cif), "bridge", NULL);
|
||||
if (!node)
|
||||
goto fail;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(node), proto, "ieee-group-forward") {
|
||||
struct lyd_node_term *leaf = (struct lyd_node_term *)proto;
|
||||
struct lysc_node_leaf *sleaf = (struct lysc_node_leaf *)leaf->schema;
|
||||
|
||||
if ((sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (sleaf->type->basetype == LY_TYPE_UNION)) {
|
||||
struct lyd_value *actual = &leaf->value.subvalue->value;
|
||||
int val;
|
||||
|
||||
if (actual->realtype->basetype == LY_TYPE_UINT8)
|
||||
val = actual->uint8;
|
||||
else
|
||||
val = actual->enum_item->value;
|
||||
|
||||
fwd_mask |= 1 << val;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
return fwd_mask;
|
||||
}
|
||||
|
||||
static int querier_mode(const char *mode)
|
||||
{
|
||||
struct { const char *mode; int val; } table[] = {
|
||||
{ "off", 0 },
|
||||
{ "proxy", 1 },
|
||||
{ "auto", 2 },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < NELEMS(table); i++) {
|
||||
if (strcmp(table[i].mode, mode))
|
||||
continue;
|
||||
|
||||
return table[i].val;
|
||||
}
|
||||
|
||||
return 0; /* unknown: off */
|
||||
}
|
||||
|
||||
void mcast_querier(const char *ifname, int vid, int mode, int interval)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
DEBUG("mcast querier %s mode %d interval %d", ifname, mode, interval);
|
||||
if (!mode) {
|
||||
systemf("rm -f /etc/mc.d/%s-*.conf", ifname);
|
||||
systemf("initctl -bnq disable mcd");
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopenf("w", "/etc/mc.d/%s-%d.conf", ifname, vid);
|
||||
if (!fp) {
|
||||
ERRNO("Failed creating querier configuration for %s", ifname);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "iface %s", ifname);
|
||||
if (vid > 0)
|
||||
fprintf(fp, " vlan %d", vid);
|
||||
fprintf(fp, " enable %sigmpv3 query-interval %d\n",
|
||||
mode == 1 ? "proxy-queries " : "", interval);
|
||||
fclose(fp);
|
||||
|
||||
systemf("initctl -bnq enable mcd");
|
||||
systemf("initctl -bnq touch mcd");
|
||||
}
|
||||
|
||||
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;
|
||||
const char *ifname;
|
||||
|
||||
mcast = lydx_get_descendant(lyd_child(vlan), "multicast", NULL);
|
||||
if (!mcast)
|
||||
return 0;
|
||||
|
||||
snooping = lydx_is_enabled(mcast, "snooping");
|
||||
querier = querier_mode(lydx_get_cattr(mcast, "querier"));
|
||||
|
||||
fprintf(br, "vlan global set vid %d dev %s mcast_snooping %d",
|
||||
vid, brname, snooping);
|
||||
fprintf(br, " mcast_igmp_version 3 mcast_mld_version 2\n");
|
||||
|
||||
interval = atoi(lydx_get_cattr(mcast, "query-interval"));
|
||||
ifname = find_vlan_interface(session, brname, vid);
|
||||
if (ifname)
|
||||
mcast_querier(ifname, 0, querier, interval);
|
||||
else
|
||||
mcast_querier(brname, vid, querier, interval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bridge_mcast_settings(FILE *ip, const char *brname, struct lyd_node *cif, int vlan_mcast)
|
||||
{
|
||||
int interval, querier, snooping;
|
||||
struct lyd_node *mcast;
|
||||
|
||||
mcast = lydx_get_descendant(lyd_child(cif), "bridge", "multicast", NULL);
|
||||
if (!mcast) {
|
||||
mcast_querier(brname, 0, 0, 0);
|
||||
interval = snooping = querier = 0;
|
||||
} else {
|
||||
snooping = lydx_is_enabled(mcast, "snooping");
|
||||
querier = querier_mode(lydx_get_cattr(mcast, "querier"));
|
||||
interval = atoi(lydx_get_cattr(mcast, "query-interval"));
|
||||
}
|
||||
|
||||
fprintf(ip, " mcast_vlan_snooping %d", vlan_mcast ? 1 : 0);
|
||||
fprintf(ip, " mcast_snooping %d mcast_querier 0", vlan_mcast ? 1 : snooping);
|
||||
if (snooping)
|
||||
fprintf(ip, " mcast_igmp_version 3 mcast_mld_version 2");
|
||||
if (interval)
|
||||
fprintf(ip, " mcast_query_interval %d", interval * 100);
|
||||
|
||||
if (!vlan_mcast)
|
||||
mcast_querier(brname, 0, querier, interval);
|
||||
else
|
||||
mcast_querier(brname, 0, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_multicast_filter(FILE *current, FILE *prev, const char *brname,
|
||||
struct lyd_node *multicast_filter, int vid)
|
||||
{
|
||||
const char *group = lydx_get_cattr(multicast_filter, "group");
|
||||
enum lydx_op op = lydx_get_op(multicast_filter);
|
||||
struct lyd_node * port;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(multicast_filter), port, "ports") {
|
||||
enum lydx_op port_op = lydx_get_op(port);
|
||||
if (op == LYDX_OP_DELETE) {
|
||||
fprintf(prev, "mdb del dev %s port %s ", brname, lydx_get_cattr(port, "port"));
|
||||
fprintf(prev, " grp %s ", group);
|
||||
if (vid)
|
||||
fprintf(prev, " vid %d ", vid);
|
||||
fputs("\n", prev);
|
||||
} else {
|
||||
fprintf(current, "mdb replace dev %s ", brname);
|
||||
if (port_op != LYDX_OP_DELETE)
|
||||
fprintf(current, " port %s ", lydx_get_cattr(port, "port"));
|
||||
fprintf(current, " grp %s ", group);
|
||||
if (vid)
|
||||
fprintf(current, " vid %d ", vid);
|
||||
fprintf(current, " %s\n", lydx_get_cattr(port, "state"));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_multicast_filters(struct dagger *net, FILE *current, const char *brname,
|
||||
struct lyd_node *multicast_filters, int vid) {
|
||||
struct lyd_node *multicast_filter;
|
||||
FILE *prev = NULL;
|
||||
int err = 0;
|
||||
|
||||
prev = dagger_fopen_current(net, "exit", brname, 50, "exit.bridge");
|
||||
if (!prev) {
|
||||
/* check if in bootstrap (pre gen 0) */
|
||||
if (errno != EUNATCH) {
|
||||
err = -EIO;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(multicast_filters), multicast_filter, "multicast-filter") {
|
||||
netdag_gen_multicast_filter(current, prev, brname, multicast_filter, vid);
|
||||
}
|
||||
|
||||
if(prev)
|
||||
fclose(prev);
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
int netdag_gen_bridge(sr_session_ctx_t *session, struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip, int add)
|
||||
{
|
||||
struct lyd_node *vlans, *vlan, *multicast_filters;
|
||||
const char *brname = lydx_get_cattr(cif, "name");
|
||||
int vlan_filtering, fwd_mask, vlan_mcast = 0;
|
||||
const char *op = add ? "add" : "set";
|
||||
const char *proto;
|
||||
FILE *br = NULL;
|
||||
int err = 0;
|
||||
|
||||
vlan_filtering = bridge_vlan_settings(cif, &proto, &vlan_mcast);
|
||||
fwd_mask = bridge_fwd_mask(cif);
|
||||
|
||||
fprintf(ip, "link %s dev %s", op, brname);
|
||||
/*
|
||||
* Must set base mac on add to prevent kernel from seeding ipv6
|
||||
* addrgenmode eui64 with random mac, issue #357.
|
||||
*/
|
||||
if (add) {
|
||||
const char *mac = get_phys_addr(cif, NULL);
|
||||
|
||||
if (!mac) {
|
||||
struct json_t *j;
|
||||
|
||||
j = json_object_get(confd.root, "mac-address");
|
||||
if (j)
|
||||
mac = json_string_value(j);
|
||||
}
|
||||
if (mac)
|
||||
fprintf(ip, " address %s", mac);
|
||||
|
||||
/* on failure, fall back to kernel's random mac */
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue #198: we require explicit VLAN assignment for ports
|
||||
* when VLAN filtering is enabled. We strongly
|
||||
* believe this is the only sane way of doing it.
|
||||
* Issue #310: malplaced 'vlan_default_pvid 0'
|
||||
*/
|
||||
fprintf(ip, " type bridge group_fwd_mask %d mcast_flood_always 1"
|
||||
" vlan_filtering %d vlan_default_pvid 0",
|
||||
fwd_mask, vlan_filtering ? 1 : 0);
|
||||
|
||||
if ((err = bridge_mcast_settings(ip, brname, cif, vlan_mcast)))
|
||||
goto out;
|
||||
|
||||
br = dagger_fopen_next(net, "init", brname, 60, "init.bridge");
|
||||
if (!br) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!vlan_filtering) {
|
||||
fputc('\n', ip);
|
||||
|
||||
multicast_filters = lydx_get_descendant(lyd_child(dif), "bridge", "multicast-filters", NULL);
|
||||
if (multicast_filters)
|
||||
err = netdag_gen_multicast_filters(net, br, brname, multicast_filters, 0);
|
||||
goto out_close_br;
|
||||
} else if (!proto) {
|
||||
fputc('\n', ip);
|
||||
ERROR("%s: unsupported bridge proto", brname);
|
||||
err = -ENOSYS;
|
||||
goto out_close_br;
|
||||
}
|
||||
fprintf(ip, " vlan_protocol %s\n", proto);
|
||||
|
||||
vlans = lydx_get_descendant(lyd_child(dif), "bridge", "vlans", NULL);
|
||||
if (!vlans)
|
||||
goto out_close_br;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(vlans), vlan, "vlan") {
|
||||
int vid = atoi(lydx_get_cattr(vlan, "vid"));
|
||||
|
||||
err = bridge_diff_vlan_ports(net, br, brname, vid, vlan);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* MDB static groups */
|
||||
multicast_filters = lydx_get_child(vlan, "multicast-filters");
|
||||
if (multicast_filters) {
|
||||
if ((err = netdag_gen_multicast_filters(net, br, brname, multicast_filters, vid)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* need the vlans created before we can set features on them */
|
||||
vlans = lydx_get_descendant(lyd_child(cif), "bridge", "vlans", NULL);
|
||||
if (vlans) {
|
||||
LYX_LIST_FOR_EACH(lyd_child(vlans), vlan, "vlan") {
|
||||
int vid = atoi(lydx_get_cattr(vlan, "vid"));
|
||||
|
||||
err = vlan_mcast_settings(session, br, brname, vlan, vid);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_close_br:
|
||||
fclose(br);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdbool.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "ietf-interfaces.h"
|
||||
|
||||
int ifchange_cand_infer_veth(sr_session_ctx_t *session, const char *path)
|
||||
{
|
||||
char *ifname, *type, *peer, *xpath, *val;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
size_t cnt = 0;
|
||||
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
type = srx_get_str(session, "%s/type", xpath);
|
||||
if (!type)
|
||||
goto out;
|
||||
|
||||
if (strcmp(type, "infix-if-type:veth"))
|
||||
goto out_free_type;
|
||||
|
||||
ifname = srx_get_str(session, "%s/name", xpath);
|
||||
if (!ifname)
|
||||
goto out_free_type;
|
||||
|
||||
peer = srx_get_str(session, "%s/veth/peer", xpath);
|
||||
if (!peer)
|
||||
goto out_free_ifname;
|
||||
|
||||
err = srx_nitems(session, &cnt, "/interfaces/interface[name='%s']/name", peer);
|
||||
if (err || cnt)
|
||||
goto out_free_peer;
|
||||
|
||||
val = "infix-if-type:veth";
|
||||
err = srx_set_str(session, val, 0, IF_XPATH "[name='%s']/type", peer);
|
||||
if (err) {
|
||||
ERROR("failed setting iface %s type %s, err %d", peer, val, err);
|
||||
goto out_free_peer;
|
||||
}
|
||||
|
||||
err = srx_set_str(session, ifname, 0, IF_XPATH "[name='%s']/infix-interfaces:veth/peer", peer);
|
||||
if (err)
|
||||
ERROR("failed setting iface %s peer %s, err %d", peer, ifname, err);
|
||||
|
||||
out_free_peer:
|
||||
free(peer);
|
||||
out_free_ifname:
|
||||
free(ifname);
|
||||
out_free_type:
|
||||
free(type);
|
||||
out:
|
||||
free(xpath);
|
||||
return err;
|
||||
}
|
||||
|
||||
int netdag_gen_veth(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;
|
||||
const char *peer;
|
||||
int err;
|
||||
|
||||
node = lydx_get_descendant(lyd_child(cif), "veth", NULL);
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
peer = lydx_get_cattr(node, "peer");
|
||||
if (dagger_should_skip(net, ifname)) {
|
||||
err = dagger_add_dep(net, ifname, peer);
|
||||
if (err)
|
||||
return ERR_IFACE(cif, err, "Unable to add dep \"%s\" to %s", peer, ifname);
|
||||
} else {
|
||||
char ifname_args[64] = "", peer_args[64] = "";
|
||||
const char *mac;
|
||||
|
||||
dagger_skip_iface(net, peer);
|
||||
|
||||
mac = get_phys_addr(dif, NULL);
|
||||
if (mac)
|
||||
snprintf(ifname_args, sizeof(ifname_args), "address %s", mac);
|
||||
|
||||
node = lydx_find_by_name(lyd_parent(cif), "interface", peer);
|
||||
if (node && (mac = get_phys_addr(node, NULL)))
|
||||
snprintf(peer_args, sizeof(peer_args), "address %s", mac);
|
||||
|
||||
fprintf(ip, "link add dev %s %s type veth peer %s %s\n",
|
||||
ifname, ifname_args, peer, peer_args);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdbool.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "ietf-interfaces.h"
|
||||
|
||||
int ifchange_cand_infer_vlan(sr_session_ctx_t *session, const char *path)
|
||||
{
|
||||
sr_val_t inferred = { .type = SR_STRING_T };
|
||||
char *ifname, *type, *xpath, *lower;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
size_t cnt = 0;
|
||||
int vid;
|
||||
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
type = srx_get_str(session, "%s/type", xpath);
|
||||
if (!type)
|
||||
goto out;
|
||||
|
||||
if (strcmp(type, "infix-if-type:vlan"))
|
||||
goto out_free_type;
|
||||
|
||||
ifname = srx_get_str(session, "%s/name", xpath);
|
||||
if (!ifname)
|
||||
goto out_free_type;
|
||||
|
||||
if (!fnmatch("*.+([0-9])", ifname, FNM_EXTMATCH)) {
|
||||
char *ptr = rindex(ifname, '.');
|
||||
|
||||
if (!ptr)
|
||||
goto out_free_ifname;
|
||||
|
||||
*ptr++ = '\0';
|
||||
vid = strtol(ptr, NULL, 10);
|
||||
lower = ifname;
|
||||
} else if (!fnmatch("vlan+([0-9])", ifname, FNM_EXTMATCH)) {
|
||||
if (sscanf(ifname, "vlan%d", &vid) != 1)
|
||||
goto out_free_ifname;
|
||||
|
||||
/* Avoid setting lower-layer-if to vlanN */
|
||||
lower = NULL;
|
||||
} else {
|
||||
goto out_free_ifname;
|
||||
}
|
||||
|
||||
if (vid < 1 || vid > 4094)
|
||||
goto out_free_ifname;
|
||||
|
||||
if (lower) {
|
||||
err = srx_nitems(session, &cnt, "/interfaces/interface[name='%s']/name", lower);
|
||||
if (err || !cnt)
|
||||
goto out_free_ifname;
|
||||
|
||||
err = srx_nitems(session, &cnt, IF_VLAN_XPATH "/lower-layer-if", xpath);
|
||||
if (!err && !cnt) {
|
||||
inferred.data.string_val = lower;
|
||||
err = srx_set_item(session, &inferred, 0, IF_VLAN_XPATH "/lower-layer-if", xpath);
|
||||
if (err)
|
||||
goto out_free_ifname;
|
||||
}
|
||||
}
|
||||
|
||||
err = srx_nitems(session, &cnt, IF_VLAN_XPATH "/tag-type", xpath);
|
||||
if (!err && !cnt) {
|
||||
inferred.data.string_val = "ieee802-dot1q-types:c-vlan";
|
||||
err = srx_set_item(session, &inferred, 0, IF_VLAN_XPATH "/tag-type", xpath);
|
||||
if (err)
|
||||
goto out_free_ifname;
|
||||
}
|
||||
|
||||
err = srx_nitems(session, &cnt, IF_VLAN_XPATH "/id", xpath);
|
||||
if (!err && !cnt) {
|
||||
inferred.type = SR_INT32_T;
|
||||
inferred.data.int32_val = vid;
|
||||
err = srx_set_item(session, &inferred, 0, IF_VLAN_XPATH "/id", xpath);
|
||||
if (err)
|
||||
goto out_free_ifname;
|
||||
}
|
||||
|
||||
out_free_ifname:
|
||||
free(ifname);
|
||||
out_free_type:
|
||||
free(type);
|
||||
out:
|
||||
free(xpath);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int netdag_gen_vlan_ingress_qos(struct lyd_node *cif, FILE *ip)
|
||||
{
|
||||
const char *prio;
|
||||
|
||||
prio = lyd_get_value(lydx_get_descendant(lyd_child(cif),
|
||||
"vlan", "ingress-qos", "priority", NULL));
|
||||
|
||||
if (prio[0] >= '0' && prio[0] <= '7' && prio[1] == '\0') {
|
||||
fprintf(ip, " ingress-qos-map 0:%c 1:%c 2:%c 3:%c 4:%c 5:%c 6:%c 7:%c",
|
||||
prio[0], prio[0], prio[0], prio[0], prio[0], prio[0], prio[0], prio[0]);
|
||||
return 0;
|
||||
} else if (!strcmp(prio, "from-pcp")) {
|
||||
fputs(" ingress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7", ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ERR_IFACE(cif, -EINVAL, "Unsupported ingress priority mode \"%s\"", prio);
|
||||
}
|
||||
|
||||
static int netdag_gen_vlan_egress_qos(struct lyd_node *cif, FILE *ip)
|
||||
{
|
||||
const char *pcp;
|
||||
|
||||
pcp = lyd_get_value(lydx_get_descendant(lyd_child(cif),
|
||||
"vlan", "egress-qos", "pcp", NULL));
|
||||
|
||||
if (pcp[0] >= '0' && pcp[0] <= '7' && pcp[1] == '\0') {
|
||||
fprintf(ip, " egress-qos-map 0:%c 1:%c 2:%c 3:%c 4:%c 5:%c 6:%c 7:%c",
|
||||
pcp[0], pcp[0], pcp[0], pcp[0], pcp[0], pcp[0], pcp[0], pcp[0]);
|
||||
return 0;
|
||||
} else if (!strcmp(pcp, "from-priority")) {
|
||||
fputs(" egress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7", ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ERR_IFACE(cif, -EINVAL, "Unsupported egress priority mode \"%s\"", pcp);
|
||||
}
|
||||
|
||||
int netdag_gen_vlan(struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
struct lydx_diff typed, vidd;
|
||||
struct lyd_node *vlan;
|
||||
const char *lower_if;
|
||||
const char *proto;
|
||||
int err;
|
||||
|
||||
vlan = lydx_get_descendant(lyd_child(dif ? : cif), "vlan", NULL);
|
||||
if (!vlan) {
|
||||
/*
|
||||
* Note: this is only an error if vlan subcontext is missing
|
||||
* from cif, otherwise it just means the interface had a
|
||||
* a change that was not related to the VLAN config.
|
||||
*/
|
||||
if (!dif)
|
||||
ERROR("%s: missing mandatory vlan", ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lower_if = lydx_get_cattr(vlan, "lower-layer-if");
|
||||
DEBUG("ifname %s lower if %s\n", ifname, lower_if);
|
||||
|
||||
err = dagger_add_dep(net, ifname, lower_if);
|
||||
if (err)
|
||||
return ERR_IFACE(cif, err, "Unable to add dep \"%s\"", lower_if);
|
||||
|
||||
|
||||
fprintf(ip, "link add dev %s down link %s type vlan", ifname, lower_if);
|
||||
|
||||
if (lydx_get_diff(lydx_get_child(vlan, "tag-type"), &typed)) {
|
||||
proto = bridge_tagtype2str(typed.new);
|
||||
if (!proto)
|
||||
return ERR_IFACE(cif, -ENOSYS, "Unsupported tag type \"%s\"", typed.new);
|
||||
|
||||
fprintf(ip, " proto %s", proto);
|
||||
}
|
||||
|
||||
if (lydx_get_diff(lydx_get_child(vlan, "id"), &vidd))
|
||||
fprintf(ip, " id %s", vidd.new);
|
||||
|
||||
err = netdag_gen_vlan_ingress_qos(cif, ip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = netdag_gen_vlan_egress_qos(cif, ip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fputc('\n', ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user