/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * ipila.c ILA (Identifier Locator Addressing) support * * Authors: Tom Herbert */ #include #include #include #include #include #include #include #include #include #include "libgenl.h" #include "utils.h" #include "ip_common.h" #include "ila_common.h" #include "json_print.h" static void usage(void) { fprintf(stderr, "Usage: ip ila add loc_match LOCATOR_MATCH loc LOCATOR [ dev DEV ] OPTIONS\n" " ip ila del loc_match LOCATOR_MATCH [ loc LOCATOR ] [ dev DEV ]\n" " ip ila list\n" "OPTIONS := [ csum-mode { adj-transport | neutral-map |\n" " neutral-map-auto | no-action } ]\n" " [ ident-type { luid | use-format } ]\n"); exit(-1); } /* netlink socket */ static struct rtnl_handle genl_rth = { .fd = -1 }; static int genl_family = -1; #define ILA_REQUEST(_req, _bufsiz, _cmd, _flags) \ GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ ILA_GENL_VERSION, _cmd, _flags) #define ILA_RTA(g) ((struct rtattr *)(((char *)(g)) + \ NLMSG_ALIGN(sizeof(struct genlmsghdr)))) static void print_addr64(__u64 addr, char *buff, size_t len) { union { __u64 id64; __u16 words[4]; } id = { .id64 = addr }; __u16 v; int i, ret; size_t written = 0; char *sep = ":"; for (i = 0; i < 4; i++) { v = ntohs(id.words[i]); if (i == 3) sep = ""; ret = snprintf(&buff[written], len - written, "%x%s", v, sep); if (ret < 0 || ret >= len - written) break; written += ret; } } static void print_ila_locid(const char *tag, int attr, struct rtattr *tb[]) { char abuf[256]; if (tb[attr]) print_addr64(rta_getattr_u64(tb[attr]), abuf, sizeof(abuf)); else snprintf(abuf, sizeof(abuf), "-"); /* 20 = sizeof("xxxx:xxxx:xxxx:xxxx") */ print_string(PRINT_ANY, tag, "%-20s", abuf); } static int print_ila_mapping(struct nlmsghdr *n, void *arg) { struct genlmsghdr *ghdr; struct rtattr *tb[ILA_ATTR_MAX + 1]; int len = n->nlmsg_len; if (n->nlmsg_type != genl_family) return 0; len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < 0) return -1; ghdr = NLMSG_DATA(n); parse_rtattr(tb, ILA_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); open_json_object(NULL); print_ila_locid("locator_match", ILA_ATTR_LOCATOR_MATCH, tb); print_ila_locid("locator", ILA_ATTR_LOCATOR, tb); if (tb[ILA_ATTR_IFINDEX]) { __u32 ifindex = rta_getattr_u32(tb[ILA_ATTR_IFINDEX]); print_color_string(PRINT_ANY, COLOR_IFNAME, "interface", "%-16s", ll_index_to_name(ifindex)); } else { print_string(PRINT_FP, NULL, "%-10s ", "-"); } if (tb[ILA_ATTR_CSUM_MODE]) { __u8 csum = rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]); print_string(PRINT_ANY, "csum_mode", "%s", ila_csum_mode2name(csum)); } else print_string(PRINT_FP, NULL, "%-10s ", "-"); if (tb[ILA_ATTR_IDENT_TYPE]) print_string(PRINT_ANY, "ident_type", "%s", ila_ident_type2name(rta_getattr_u8( tb[ILA_ATTR_IDENT_TYPE]))); else print_string(PRINT_FP, NULL, "%s", "-"); print_nl(); close_json_object(); return 0; } #define NLMSG_BUF_SIZE 4096 static int do_list(int argc, char **argv) { ILA_REQUEST(req, 1024, ILA_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP); if (argc > 0) { fprintf(stderr, "\"ip ila show\" does not take " "any arguments.\n"); return -1; } if (rtnl_send(&genl_rth, (void *)&req, req.n.nlmsg_len) < 0) { perror("Cannot send dump request"); exit(1); } new_json_obj(json); if (rtnl_dump_filter(&genl_rth, print_ila_mapping, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); delete_json_obj(); return 1; } delete_json_obj(); fflush(stdout); return 0; } static int ila_parse_opt(int argc, char **argv, struct nlmsghdr *n, bool adding) { __u64 locator = 0; __u64 locator_match = 0; int ifindex = 0; int csum_mode = 0; int ident_type = 0; bool loc_set = false; bool loc_match_set = false; bool ifindex_set = false; bool csum_mode_set = false; bool ident_type_set = false; while (argc > 0) { if (!matches(*argv, "loc")) { NEXT_ARG(); if (get_addr64(&locator, *argv) < 0) { fprintf(stderr, "Bad locator: %s\n", *argv); return -1; } loc_set = true; } else if (!matches(*argv, "loc_match")) { NEXT_ARG(); if (get_addr64(&locator_match, *argv) < 0) { fprintf(stderr, "Bad locator to match: %s\n", *argv); return -1; } loc_match_set = true; } else if (!matches(*argv, "csum-mode")) { NEXT_ARG(); csum_mode = ila_csum_name2mode(*argv); if (csum_mode < 0) { fprintf(stderr, "Bad csum-mode: %s\n", *argv); return -1; } csum_mode_set = true; } else if (!matches(*argv, "ident-type")) { NEXT_ARG(); ident_type = ila_ident_name2type(*argv); if (ident_type < 0) { fprintf(stderr, "Bad ident-type: %s\n", *argv); return -1; } ident_type_set = true; } else if (!matches(*argv, "dev")) { NEXT_ARG(); ifindex = ll_name_to_index(*argv); if (ifindex == 0) { fprintf(stderr, "No such interface: %s\n", *argv); return -1; } ifindex_set = true; } else { usage(); return -1; } argc--, argv++; } if (adding) { if (!loc_set) { fprintf(stderr, "ila: missing locator\n"); return -1; } if (!loc_match_set) { fprintf(stderr, "ila: missing locator0match\n"); return -1; } } if (loc_match_set) addattr64(n, 1024, ILA_ATTR_LOCATOR_MATCH, locator_match); if (loc_set) addattr64(n, 1024, ILA_ATTR_LOCATOR, locator); if (ifindex_set) addattr32(n, 1024, ILA_ATTR_IFINDEX, ifindex); if (csum_mode_set) addattr8(n, 1024, ILA_ATTR_CSUM_MODE, csum_mode); if (ident_type_set) addattr8(n, 1024, ILA_ATTR_IDENT_TYPE, ident_type); return 0; } static int do_add(int argc, char **argv) { ILA_REQUEST(req, 1024, ILA_CMD_ADD, NLM_F_REQUEST); ila_parse_opt(argc, argv, &req.n, true); if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) return -2; return 0; } static int do_del(int argc, char **argv) { ILA_REQUEST(req, 1024, ILA_CMD_DEL, NLM_F_REQUEST); ila_parse_opt(argc, argv, &req.n, false); if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) return -2; return 0; } int do_ipila(int argc, char **argv) { if (argc < 1) usage(); if (matches(*argv, "help") == 0) usage(); if (genl_init_handle(&genl_rth, ILA_GENL_NAME, &genl_family)) exit(1); if (matches(*argv, "add") == 0) return do_add(argc-1, argv+1); if (matches(*argv, "delete") == 0) return do_del(argc-1, argv+1); if (matches(*argv, "list") == 0) return do_list(argc-1, argv+1); fprintf(stderr, "Command \"%s\" is unknown, try \"ip ila help\".\n", *argv); exit(-1); }