@@ -33,6 +33,9 @@ int ll_proto_a2n(unsigned short *id, const char *buf);
const char *nl_proto_n2a(int id, char *buf, int len);
int nl_proto_a2n(__u32 *id, const char *arg);
+int protodown_reason_a2n(__u32 *id, const char *arg);
+int protodown_reason_n2a(int id, char *buf, int len);
+
extern int numeric;
#endif
@@ -874,6 +874,45 @@ static void print_link_event(FILE *f, __u32 event)
}
}
+static void print_proto_down(FILE *f, struct rtattr *tb[])
+{
+ struct rtattr *preason[IFLA_PROTO_DOWN_REASON_MAX+1];
+
+ if (tb[IFLA_PROTO_DOWN]) {
+ if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
+ print_bool(PRINT_ANY,
+ "proto_down", " protodown on ", true);
+ }
+
+ if (tb[IFLA_PROTO_DOWN_REASON]) {
+ char buf[255];
+ __u32 reason;
+ int i, start = 1;
+
+ parse_rtattr_nested(preason, IFLA_PROTO_DOWN_REASON_MAX,
+ tb[IFLA_PROTO_DOWN_REASON]);
+ if (!tb[IFLA_PROTO_DOWN_REASON_VALUE])
+ return;
+
+ reason = rta_getattr_u8(preason[IFLA_PROTO_DOWN_REASON_VALUE]);
+ if (!reason)
+ return;
+
+ open_json_array(PRINT_ANY,
+ is_json_context() ? "proto_down_reason" : "protodown_reason <");
+ for (i = 0; reason; i++, reason >>= 1) {
+ if (reason & 0x1) {
+ if (protodown_reason_n2a(i, buf, sizeof(buf)))
+ break;
+ print_string(PRINT_ANY, NULL,
+ start ? "%s" : ",%s", buf);
+ start = 0;
+ }
+ }
+ close_json_array(PRINT_ANY, ">");
+ }
+}
+
int print_linkinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
@@ -1066,11 +1105,8 @@ int print_linkinfo(struct nlmsghdr *n, void *arg)
print_int(PRINT_FP, NULL, " new-ifindex %d", id);
}
- if (tb[IFLA_PROTO_DOWN]) {
- if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
- print_bool(PRINT_ANY,
- "proto_down", " protodown on ", true);
- }
+ if (tb[IFLA_PROTO_DOWN])
+ print_proto_down(fp, tb);
if (show_details) {
if (tb[IFLA_PROMISCUITY])
@@ -105,6 +105,7 @@ void iplink_usage(void)
" [ nomaster ]\n"
" [ addrgenmode { eui64 | none | stable_secret | random } ]\n"
" [ protodown { on | off } ]\n"
+ " [ protodown_reason PREASON { on | off } ]\n"
" [ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n"
"\n"
" ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"
@@ -903,6 +904,28 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
return on_off("protodown", *argv);
addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
proto_down);
+ } else if (strcmp(*argv, "protodown_reason") == 0) {
+ struct rtattr *pr;
+ __u32 preason = 0, prvalue = 0, prmask = 0;
+
+ NEXT_ARG();
+ if (protodown_reason_a2n(&preason, *argv))
+ invarg("invalid protodown reason\n", *argv);
+ NEXT_ARG();
+ prmask = 1 << preason;
+ if (matches(*argv, "on") == 0)
+ prvalue |= prmask;
+ else if (matches(*argv, "off") == 0)
+ prvalue &= ~prmask;
+ else
+ return on_off("protodown_reason", *argv);
+ pr = addattr_nest(&req->n, sizeof(*req),
+ IFLA_PROTO_DOWN_REASON | NLA_F_NESTED);
+ addattr32(&req->n, sizeof(*req),
+ IFLA_PROTO_DOWN_REASON_MASK, prmask);
+ addattr32(&req->n, sizeof(*req),
+ IFLA_PROTO_DOWN_REASON_VALUE, prvalue);
+ addattr_nest_end(&req->n, pr);
} else if (strcmp(*argv, "gso_max_size") == 0) {
unsigned int max_size;
@@ -682,3 +682,95 @@ int nl_proto_a2n(__u32 *id, const char *arg)
*id = res;
return 0;
}
+
+#define PROTODOWN_REASON_NUM_BITS 32
+static char *protodown_reason_tab[PROTODOWN_REASON_NUM_BITS] = {
+};
+
+static int protodown_reason_init;
+
+static void protodown_reason_initialize(void)
+{
+ struct dirent *de;
+ DIR *d;
+
+ protodown_reason_init = 1;
+
+ d = opendir(CONFDIR "/protodown_reasons.d");
+ if (!d)
+ return;
+
+ while ((de = readdir(d)) != NULL) {
+ char path[PATH_MAX];
+ size_t len;
+
+ if (*de->d_name == '.')
+ continue;
+
+ /* only consider filenames ending in '.conf' */
+ len = strlen(de->d_name);
+ if (len <= 5)
+ continue;
+ if (strcmp(de->d_name + len - 5, ".conf"))
+ continue;
+
+ snprintf(path, sizeof(path), CONFDIR "/protodown_reasons.d/%s",
+ de->d_name);
+ rtnl_tab_initialize(path, protodown_reason_tab,
+ PROTODOWN_REASON_NUM_BITS);
+ }
+ closedir(d);
+}
+
+int protodown_reason_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= PROTODOWN_REASON_NUM_BITS)
+ return -1;
+
+ if (numeric) {
+ snprintf(buf, len, "%d", id);
+ return 0;
+ }
+
+ if (!protodown_reason_init)
+ protodown_reason_initialize();
+
+ if (protodown_reason_tab[id])
+ snprintf(buf, len, "%s", protodown_reason_tab[id]);
+ else
+ snprintf(buf, len, "%d", id);
+
+ return 0;
+}
+
+int protodown_reason_a2n(__u32 *id, const char *arg)
+{
+ static char *cache;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!protodown_reason_init)
+ protodown_reason_initialize();
+
+ for (i = 0; i < PROTODOWN_REASON_NUM_BITS; i++) {
+ if (protodown_reason_tab[i] &&
+ strcmp(protodown_reason_tab[i], arg) == 0) {
+ cache = protodown_reason_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res >= PROTODOWN_REASON_NUM_BITS)
+ return -1;
+ *id = res;
+ return 0;
+}
@@ -75,6 +75,9 @@ ip-link \- network device configuration
.br
.RB "[ " protodown " { " on " | " off " } ]"
.br
+.RB "[ " protodown_reason
+.IR PREASON " { " on " | " off " } ]"
+.br
.RB "[ " trailers " { " on " | " off " } ]"
.br
.RB "[ " txqueuelen
@@ -1917,6 +1920,13 @@ state on the device. Indicates that a protocol error has been detected
on the port. Switch drivers can react to this error by doing a phys
down on the switch port.
+.TP
+.BR "protodown_reason PREASON on " or " off"
+set
+.B PROTODOWN
+reasons on the device. protodown reason bit names can be enumerated under
+/etc/iproute2/protodown_reasons.d/. possible reasons bits 0-31
+
.TP
.BR "dynamic on " or " dynamic off"
change the