mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
confd: ethtool: Condense logic around autoneg configuration
Rely on the interface diff to figure out if any autoneg related config has changed; if so, generate the resulting configuration based on the current interface config. - Remove loopback check, the model only allows ethernet config on interfaces of type "ethernet". - Split up the input validation of speed/duplex to give the user an error message with as much specificity as possible. - Validate that the configured speed/duplex is supported. - Move the generated script to execute earlier to avoid unnecessary link flapping. - Use negative POSIX error codes, these are (or at least should be) mapped to sysrepo errors in netdag_gen_iface.
This commit is contained in:
+49
-166
@@ -653,191 +653,74 @@ skip_mtu:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int netdag_write_ethtool(struct dagger *net, const char *ifname,
|
||||
const char *fmt, ...)
|
||||
static int netdag_gen_ethtool_autoneg(struct dagger *net, struct lyd_node *cif)
|
||||
{
|
||||
va_list ap;
|
||||
struct lyd_node *eth = lydx_get_child(cif, "ethernet");
|
||||
struct lyd_node *aneg = lydx_get_child(eth, "auto-negotiation");
|
||||
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, 65, "ethtool.sh");
|
||||
fp = dagger_fopen_next(net, "init", ifname, 10, "ethtool-aneg.sh");
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(fp, "ethtool --change %s autoneg ", ifname);
|
||||
|
||||
if (!aneg || lydx_is_enabled(aneg, "enable")) {
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *lyd_get_autoneg(struct lyd_node *cif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *node;
|
||||
|
||||
node = lydx_get_descendant(lyd_child(cif), "ethernet", "auto-negotiation", "enable", NULL);
|
||||
if (node && lydx_get_diff(node, &nd) && nd.new)
|
||||
return (strcmp(nd.val, "true") == 0) ? "on" : "off";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *lyd_get_duplex(struct lyd_node *cif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *node;
|
||||
|
||||
node = lydx_get_descendant(lyd_child(cif), "ethernet", "duplex", NULL);
|
||||
if (node && lydx_get_diff(node, &nd) && nd.new)
|
||||
return nd.val;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int lyd_get_speed(struct lyd_node *cif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *n;
|
||||
|
||||
n = lydx_get_descendant(lyd_child(cif), "ethernet", "speed", NULL);
|
||||
if (n && lydx_get_diff(n, &nd) && nd.new)
|
||||
return (int)(atof(nd.val) * 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lyd_speed_has_changed(struct lyd_node *dif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *n;
|
||||
|
||||
n = lydx_get_descendant(lyd_child(dif), "ethernet", "speed", NULL);
|
||||
return (n && lydx_get_diff(n, &nd));
|
||||
}
|
||||
|
||||
static int lyd_duplex_has_changed(struct lyd_node *dif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *n;
|
||||
|
||||
n = lydx_get_descendant(lyd_child(dif), "ethernet", "duplex", NULL);
|
||||
return (n && lydx_get_diff(n, &nd));
|
||||
}
|
||||
|
||||
static int lyd_autoneg_has_changed(struct lyd_node *dif)
|
||||
{
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *n;
|
||||
|
||||
n = lydx_get_descendant(lyd_child(dif), "ethernet", "auto-negotiation", "enable", NULL);
|
||||
return (n && lydx_get_diff(n, &nd));
|
||||
}
|
||||
|
||||
static int lyd_autoneg_is_on(struct lyd_node *cif)
|
||||
{
|
||||
const char *val;
|
||||
|
||||
val = lyd_get_autoneg(cif);
|
||||
if (!val) {
|
||||
/* TODO Change this to error when auto-neg is true in factory config */
|
||||
return 1;
|
||||
}
|
||||
return (strcmp(val, "on") == 0);
|
||||
}
|
||||
|
||||
static int netdag_set_autoneg_off(struct dagger *net, const char *ifname,
|
||||
const char *duplex, int speed)
|
||||
{
|
||||
if (!speed || !duplex) {
|
||||
sr_session_set_error_message(net->session,
|
||||
"Autoneg off requires explicit speed and duplex");
|
||||
return SR_ERR_INVAL_ARG;
|
||||
}
|
||||
|
||||
return netdag_write_ethtool(net, ifname,
|
||||
"ethtool -s %s autoneg off duplex %s speed %d\n",
|
||||
ifname, duplex, speed);
|
||||
}
|
||||
|
||||
static int netdag_set_autoneg_on(struct dagger *net, const char *ifname)
|
||||
{
|
||||
return netdag_write_ethtool(net, ifname, "ethtool -s %s autoneg on\n", ifname);
|
||||
}
|
||||
|
||||
static int netdag_set_autoneg(struct dagger *net, const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
const char *autoneg = lyd_get_autoneg(cif);
|
||||
const char *duplex = lyd_get_duplex(cif);
|
||||
int speed = lyd_get_speed(cif);
|
||||
|
||||
if (!autoneg)
|
||||
return SR_ERR_INVAL_ARG;
|
||||
|
||||
if (strcmp(autoneg, "off") == 0)
|
||||
return netdag_set_autoneg_off(net, ifname, duplex, speed);
|
||||
|
||||
return netdag_set_autoneg_on(net, ifname);
|
||||
}
|
||||
|
||||
static int netdag_set_speed(struct dagger *net, const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
int speed = lyd_get_speed(cif);
|
||||
|
||||
if (!speed && !lyd_autoneg_is_on(cif)) {
|
||||
sr_session_set_error_message(net->session,
|
||||
"Can't remove speed when autoneg is disabled");
|
||||
return SR_ERR_INVAL_ARG;
|
||||
}
|
||||
|
||||
return netdag_write_ethtool(net, ifname, "ethtool -s %s speed %d\n",
|
||||
ifname, speed);
|
||||
}
|
||||
|
||||
static int netdag_set_duplex(struct dagger *net, const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
const char *duplex = lyd_get_duplex(cif);
|
||||
|
||||
/**
|
||||
* Duplex has a default value, no need to check if autoneg is off and
|
||||
* the duplex is removed, which would be a problem otherwise.
|
||||
*/
|
||||
|
||||
return netdag_write_ethtool(net, ifname, "ethtool -s %s duplex %s\n",
|
||||
ifname, duplex);
|
||||
}
|
||||
|
||||
static int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
struct lyd_node *eth = lydx_get_child(dif, "ethernet");
|
||||
int err;
|
||||
|
||||
if (!strcmp(ifname, "lo"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
/**
|
||||
* Autoneg on means duplex and speed doesn't matter.
|
||||
* Autoneg off requires and set both speed and duplex.
|
||||
*
|
||||
* Either way, if autoneg has changed duplex and speed are handled.
|
||||
*/
|
||||
if (lyd_autoneg_has_changed(dif))
|
||||
return netdag_set_autoneg(net, ifname, cif);
|
||||
|
||||
if (lyd_duplex_has_changed(dif)) {
|
||||
err = netdag_set_duplex(net, ifname, cif);
|
||||
if (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;
|
||||
}
|
||||
|
||||
if (lyd_speed_has_changed(dif)) {
|
||||
err = netdag_set_speed(net, ifname, cif);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bridge_diff_vlan_port(struct dagger *net, FILE *br, const char *brname, int vid,
|
||||
|
||||
Reference in New Issue
Block a user