From 20fbe9077166497ed90f9871d83b2156e156d58f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 24 Oct 2019 12:20:50 +0200 Subject: [PATCH 1/3] lib/ll_map: cache alternative names Alternative names are related to the "parent name". That means, whenever ll_remember_index() is called to add/delete/update and it founds the "parent name" im object by ifindex, processes related alternative name im objects too. Put them in a list which holds the relationship with the parent. Signed-off-by: Jiri Pirko Signed-off-by: David Ahern --- lib/ll_map.c | 190 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 152 insertions(+), 38 deletions(-) diff --git a/lib/ll_map.c b/lib/ll_map.c index e0ed54bf..9ec73d16 100644 --- a/lib/ll_map.c +++ b/lib/ll_map.c @@ -22,6 +22,7 @@ #include "libnetlink.h" #include "ll_map.h" #include "list.h" +#include "utils.h" struct ll_cache { struct hlist_node idx_hash; @@ -29,6 +30,7 @@ struct ll_cache { unsigned flags; unsigned index; unsigned short type; + struct list_head altnames_list; char name[]; }; @@ -77,10 +79,150 @@ static struct ll_cache *ll_get_by_name(const char *name) return NULL; } -int ll_remember_index(struct nlmsghdr *n, void *arg) +static struct ll_cache *ll_entry_create(struct ifinfomsg *ifi, + const char *ifname, + struct ll_cache *parent_im) +{ + struct ll_cache *im; + unsigned int h; + + im = malloc(sizeof(*im) + strlen(ifname) + 1); + if (!im) + return NULL; + im->index = ifi->ifi_index; + strcpy(im->name, ifname); + im->type = ifi->ifi_type; + im->flags = ifi->ifi_flags; + + if (parent_im) { + list_add_tail(&im->altnames_list, &parent_im->altnames_list); + } else { + /* This is parent, insert to index hash. */ + h = ifi->ifi_index & (IDXMAP_SIZE - 1); + hlist_add_head(&im->idx_hash, &idx_head[h]); + INIT_LIST_HEAD(&im->altnames_list); + } + + h = namehash(ifname) & (IDXMAP_SIZE - 1); + hlist_add_head(&im->name_hash, &name_head[h]); + return im; +} + +static void ll_entry_destroy(struct ll_cache *im, bool im_is_parent) +{ + hlist_del(&im->name_hash); + if (im_is_parent) + hlist_del(&im->idx_hash); + else + list_del(&im->altnames_list); + free(im); +} + +static void ll_entry_update(struct ll_cache *im, struct ifinfomsg *ifi, + const char *ifname) { unsigned int h; - const char *ifname; + + im->flags = ifi->ifi_flags; + if (!strcmp(im->name, ifname)) + return; + hlist_del(&im->name_hash); + h = namehash(ifname) & (IDXMAP_SIZE - 1); + hlist_add_head(&im->name_hash, &name_head[h]); +} + +static void ll_altname_entries_create(struct ll_cache *parent_im, + struct ifinfomsg *ifi, struct rtattr **tb) +{ + struct rtattr *i, *proplist = tb[IFLA_PROP_LIST]; + int rem; + + if (!proplist) + return; + rem = RTA_PAYLOAD(proplist); + for (i = RTA_DATA(proplist); RTA_OK(i, rem); + i = RTA_NEXT(i, rem)) { + if (i->rta_type != IFLA_ALT_IFNAME) + continue; + ll_entry_create(ifi, rta_getattr_str(i), parent_im); + } +} + +static void ll_altname_entries_destroy(struct ll_cache *parent_im) +{ + struct ll_cache *im, *tmp; + + list_for_each_entry_safe(im, tmp, &parent_im->altnames_list, + altnames_list) + ll_entry_destroy(im, false); +} + +static void ll_altname_entries_update(struct ll_cache *parent_im, + struct ifinfomsg *ifi, struct rtattr **tb) +{ + struct rtattr *i, *proplist = tb[IFLA_PROP_LIST]; + struct ll_cache *im; + int rem; + + if (!proplist) { + ll_altname_entries_destroy(parent_im); + return; + } + + /* Simply compare the altname list with the cached one + * and if it does not fit 1:1, recreate the cached list + * from scratch. + */ + im = list_first_entry(&parent_im->altnames_list, typeof(*im), + altnames_list); + rem = RTA_PAYLOAD(proplist); + for (i = RTA_DATA(proplist); RTA_OK(i, rem); + i = RTA_NEXT(i, rem)) { + if (i->rta_type != IFLA_ALT_IFNAME) + continue; + if (!im || strcmp(rta_getattr_str(i), im->name)) + goto recreate_altname_entries; + im = list_next_entry(im, altnames_list); + } + if (list_next_entry(im, altnames_list)) + goto recreate_altname_entries; + return; + +recreate_altname_entries: + ll_altname_entries_destroy(parent_im); + ll_altname_entries_create(parent_im, ifi, tb); +} + +static void ll_entries_create(struct ifinfomsg *ifi, struct rtattr **tb) +{ + struct ll_cache *parent_im; + + if (!tb[IFLA_IFNAME]) + return; + parent_im = ll_entry_create(ifi, rta_getattr_str(tb[IFLA_IFNAME]), + NULL); + if (!parent_im) + return; + ll_altname_entries_create(parent_im, ifi, tb); +} + +static void ll_entries_destroy(struct ll_cache *parent_im) +{ + ll_altname_entries_destroy(parent_im); + ll_entry_destroy(parent_im, true); +} + +static void ll_entries_update(struct ll_cache *parent_im, + struct ifinfomsg *ifi, struct rtattr **tb) +{ + if (tb[IFLA_IFNAME]) + ll_entry_update(parent_im, ifi, + rta_getattr_str(tb[IFLA_IFNAME])); + ll_altname_entries_update(parent_im, ifi, tb); +} + +int ll_remember_index(struct nlmsghdr *n, void *arg) +{ struct ifinfomsg *ifi = NLMSG_DATA(n); struct ll_cache *im; struct rtattr *tb[IFLA_MAX+1]; @@ -93,45 +235,17 @@ int ll_remember_index(struct nlmsghdr *n, void *arg) im = ll_get_by_index(ifi->ifi_index); if (n->nlmsg_type == RTM_DELLINK) { - if (im) { - hlist_del(&im->name_hash); - hlist_del(&im->idx_hash); - free(im); - } + if (im) + ll_entries_destroy(im); return 0; } - parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); - ifname = rta_getattr_str(tb[IFLA_IFNAME]); - if (ifname == NULL) - return 0; - - if (im) { - /* change to existing entry */ - if (strcmp(im->name, ifname) != 0) { - hlist_del(&im->name_hash); - h = namehash(ifname) & (IDXMAP_SIZE - 1); - hlist_add_head(&im->name_hash, &name_head[h]); - } - - im->flags = ifi->ifi_flags; - return 0; - } - - im = malloc(sizeof(*im) + strlen(ifname) + 1); - if (im == NULL) - return 0; - im->index = ifi->ifi_index; - strcpy(im->name, ifname); - im->type = ifi->ifi_type; - im->flags = ifi->ifi_flags; - - h = ifi->ifi_index & (IDXMAP_SIZE - 1); - hlist_add_head(&im->idx_hash, &idx_head[h]); - - h = namehash(ifname) & (IDXMAP_SIZE - 1); - hlist_add_head(&im->name_hash, &name_head[h]); - + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), + IFLA_PAYLOAD(n), NLA_F_NESTED); + if (im) + ll_entries_update(im, ifi, tb); + else + ll_entries_create(ifi, tb); return 0; } From 3aa0e51be64b47fed8fcf24c4edd1f81b276f5bc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 24 Oct 2019 12:20:51 +0200 Subject: [PATCH 2/3] ip: add support for alternative name addition/deletion/list Implement addition/deletion of lists of properties, currently alternative ifnames. Also extent the ip link show command to list them. Signed-off-by: Jiri Pirko Signed-off-by: David Ahern --- include/utils.h | 1 + ip/ipaddress.c | 20 +++++++++-- ip/iplink.c | 81 ++++++++++++++++++++++++++++++++++++++++++- lib/utils.c | 19 +++++++--- man/man8/ip-link.8.in | 11 ++++++ 5 files changed, 124 insertions(+), 8 deletions(-) diff --git a/include/utils.h b/include/utils.h index 794d3605..001491a1 100644 --- a/include/utils.h +++ b/include/utils.h @@ -196,6 +196,7 @@ void duparg(const char *, const char *) __attribute__((noreturn)); void duparg2(const char *, const char *) __attribute__((noreturn)); int nodev(const char *dev); int check_ifname(const char *); +int check_altifname(const char *name); int get_ifname(char *, const char *); const char *get_ifname_rta(int ifindex, const struct rtattr *rta); bool matches(const char *prefix, const char *string); diff --git a/ip/ipaddress.c b/ip/ipaddress.c index bc8f5ba1..b72eb7a1 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -879,7 +879,7 @@ int print_linkinfo(struct nlmsghdr *n, void *arg) if (filter.up && !(ifi->ifi_flags&IFF_UP)) return -1; - parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED); name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]); if (!name) @@ -1139,7 +1139,23 @@ int print_linkinfo(struct nlmsghdr *n, void *arg) close_json_array(PRINT_JSON, NULL); } - print_string(PRINT_FP, NULL, "%s", "\n"); + if (tb[IFLA_PROP_LIST]) { + struct rtattr *i, *proplist = tb[IFLA_PROP_LIST]; + int rem = RTA_PAYLOAD(proplist); + + open_json_array(PRINT_JSON, "altnames"); + for (i = RTA_DATA(proplist); RTA_OK(i, rem); + i = RTA_NEXT(i, rem)) { + if (i->rta_type != IFLA_ALT_IFNAME) + continue; + print_string(PRINT_FP, NULL, "%s altname ", _SL_); + print_string(PRINT_ANY, NULL, + "%s", rta_getattr_str(i)); + } + close_json_array(PRINT_JSON, NULL); + } + + print_string(PRINT_FP, NULL, "%s", _SL_); fflush(fp); return 1; } diff --git a/ip/iplink.c b/ip/iplink.c index 212a0885..bf90fad1 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -111,7 +111,9 @@ void iplink_usage(void) "\n" " ip link xstats type TYPE [ ARGS ]\n" "\n" - " ip link afstats [ dev DEVICE ]\n"); + " ip link afstats [ dev DEVICE ]\n" + " ip link property add dev DEVICE [ altname NAME .. ]\n" + " ip link property del dev DEVICE [ altname NAME .. ]\n"); if (iplink_have_newlink()) { fprintf(stderr, @@ -1617,6 +1619,80 @@ static int iplink_afstats(int argc, char **argv) return 0; } +static int iplink_prop_mod(int argc, char **argv, struct iplink_req *req) +{ + struct rtattr *proplist; + char *dev = NULL; + char *name; + + proplist = addattr_nest(&req->n, sizeof(*req), + IFLA_PROP_LIST | NLA_F_NESTED); + + while (argc > 0) { + if (matches(*argv, "altname") == 0) { + NEXT_ARG(); + if (check_altifname(*argv)) + invarg("not a valid altname", *argv); + name = *argv; + addattr_l(&req->n, sizeof(*req), IFLA_ALT_IFNAME, + name, strlen(name) + 1); + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + if (strcmp(*argv, "dev") == 0) + NEXT_ARG(); + if (dev) + duparg2("dev", *argv); + if (check_altifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + dev = *argv; + } + argv++; argc--; + } + addattr_nest_end(&req->n, proplist); + + if (!dev) { + fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); + exit(-1); + } + + req->i.ifi_index = ll_name_to_index(dev); + if (!req->i.ifi_index) + return nodev(dev); + + if (rtnl_talk(&rth, &req->n, NULL) < 0) + return -2; + + return 0; +} + +static int iplink_prop(int argc, char **argv) +{ + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = preferred_family, + }; + + if (argc <= 0) { + usage(); + exit(-1); + } + + if (matches(*argv, "add") == 0) { + req.n.nlmsg_flags |= NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND; + req.n.nlmsg_type = RTM_NEWLINKPROP; + } else if (matches(*argv, "del") == 0) { + req.n.nlmsg_type = RTM_DELLINKPROP; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Operator required\n"); + exit(-1); + } + return iplink_prop_mod(argc - 1, argv + 1, &req); +} + static void do_help(int argc, char **argv) { struct link_util *lu = NULL; @@ -1674,6 +1750,9 @@ int do_iplink(int argc, char **argv) return 0; } + if (matches(*argv, "property") == 0) + return iplink_prop(argc-1, argv+1); + if (matches(*argv, "help") == 0) { do_help(argc-1, argv+1); return 0; diff --git a/lib/utils.c b/lib/utils.c index 95d46ff2..bbb3bdcf 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -824,14 +824,10 @@ int nodev(const char *dev) return -1; } -int check_ifname(const char *name) +static int __check_ifname(const char *name) { - /* These checks mimic kernel checks in dev_valid_name */ if (*name == '\0') return -1; - if (strlen(name) >= IFNAMSIZ) - return -1; - while (*name) { if (*name == '/' || isspace(*name)) return -1; @@ -840,6 +836,19 @@ int check_ifname(const char *name) return 0; } +int check_ifname(const char *name) +{ + /* These checks mimic kernel checks in dev_valid_name */ + if (strlen(name) >= IFNAMSIZ) + return -1; + return __check_ifname(name); +} + +int check_altifname(const char *name) +{ + return __check_ifname(name); +} + /* buf is assumed to be IFNAMSIZ */ int get_ifname(char *buf, const char *name) { diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 31051c52..9629a649 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -244,6 +244,17 @@ ip-link \- network device configuration .IR VLAN-QOS " ] [" .B proto .IR VLAN-PROTO " ] ]" +.in -8 + +.ti -8 +.BI "ip link property add" +.RB "[ " altname +.IR NAME " .. ]" + +.ti -8 +.BI "ip link property del" +.RB "[ " altname +.IR NAME " .. ]" .SH "DESCRIPTION" .SS ip link add - add virtual link From afd67550c2c4efd428e445f5605785263bd0602d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 24 Oct 2019 12:20:52 +0200 Subject: [PATCH 3/3] ip: allow to use alternative names as handle Extend ll_name_to_index() to get the index of a netdevice using alternative interface name. Allow alternative long names to pass checks in couple of ip link/addr commands. Signed-off-by: Jiri Pirko Signed-off-by: David Ahern --- ip/iplink.c | 5 +++-- lib/ll_map.c | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ip/iplink.c b/ip/iplink.c index bf90fad1..47f73988 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -931,7 +931,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type) NEXT_ARG(); if (dev != name) duparg2("dev", *argv); - if (check_ifname(*argv)) + if (check_altifname(*argv)) invarg("\"dev\" not a valid ifname", *argv); dev = *argv; } @@ -1106,7 +1106,8 @@ int iplink_get(char *name, __u32 filt_mask) if (name) { addattr_l(&req.n, sizeof(req), - IFLA_IFNAME, name, strlen(name) + 1); + !check_ifname(name) ? IFLA_IFNAME : IFLA_ALT_IFNAME, + name, strlen(name) + 1); } addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); diff --git a/lib/ll_map.c b/lib/ll_map.c index 9ec73d16..70ea3d49 100644 --- a/lib/ll_map.c +++ b/lib/ll_map.c @@ -72,7 +72,7 @@ static struct ll_cache *ll_get_by_name(const char *name) struct ll_cache *im = container_of(n, struct ll_cache, name_hash); - if (strncmp(im->name, name, IFNAMSIZ) == 0) + if (strcmp(im->name, name) == 0) return im; } @@ -288,8 +288,9 @@ static int ll_link_get(const char *name, int index) addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); if (name) - addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, - strlen(name) + 1); + addattr_l(&req.n, sizeof(req), + !check_ifname(name) ? IFLA_IFNAME : IFLA_ALT_IFNAME, + name, strlen(name) + 1); if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) goto out;