void delete_json_obj(void) { if (_jw) { jsonw_end_array(_jw); jsonw_destroy(&_jw); } }
static int dump_btf_raw(const struct btf *btf, __u32 *root_type_ids, int root_type_cnt) { const struct btf_type *t; int i; if (json_output) { jsonw_start_object(json_wtr); jsonw_name(json_wtr, "types"); jsonw_start_array(json_wtr); } if (root_type_cnt) { for (i = 0; i < root_type_cnt; i++) { t = btf__type_by_id(btf, root_type_ids[i]); dump_btf_type(btf, root_type_ids[i], t); } } else { int cnt = btf__get_nr_types(btf); for (i = 1; i <= cnt; i++) { t = btf__type_by_id(btf, i); dump_btf_type(btf, i, t); } } if (json_output) { jsonw_end_array(json_wtr); jsonw_end_object(json_wtr); } return 0; }
int main(int argc, char **argv) { json_writer_t *wr = jsonw_new(stdout); jsonw_start_object(wr); jsonw_pretty(wr, true); jsonw_name(wr, "Vyatta"); jsonw_start_object(wr); jsonw_string_field(wr, "url", "http://vyatta.com"); jsonw_uint_field(wr, "downloads", 2000000ul); jsonw_float_field(wr, "stock", 8.16); jsonw_name(wr, "ARGV"); jsonw_start_array(wr); while (--argc) jsonw_string(wr, *++argv); jsonw_end_array(wr); jsonw_name(wr, "empty"); jsonw_start_array(wr); jsonw_end_array(wr); jsonw_name(wr, "NIL"); jsonw_start_object(wr); jsonw_end_object(wr); jsonw_null_field(wr, "my_null"); jsonw_name(wr, "special chars"); jsonw_start_array(wr); jsonw_string_field(wr, "slash", "/"); jsonw_string_field(wr, "newline", "\n"); jsonw_string_field(wr, "tab", "\t"); jsonw_string_field(wr, "ff", "\f"); jsonw_string_field(wr, "quote", "\""); jsonw_string_field(wr, "tick", "\'"); jsonw_string_field(wr, "backslash", "\\"); jsonw_end_array(wr); jsonw_end_object(wr); jsonw_end_object(wr); jsonw_destroy(&wr); return 0; }
/* * End json array or string array */ void close_json_array(enum output_type type, const char *str) { if (_IS_JSON_CONTEXT(type)) { jsonw_pretty(_jw, false); jsonw_end_array(_jw); jsonw_pretty(_jw, true); } else if (_IS_FP_CONTEXT(type)) { printf("%s", str); } }
static int do_show_tree(int argc, char **argv) { char *cgroup_root; int ret; switch (argc) { case 0: cgroup_root = find_cgroup_root(); if (!cgroup_root) { p_err("cgroup v2 isn't mounted"); return -1; } break; case 1: cgroup_root = argv[0]; break; default: p_err("too many parameters for cgroup tree"); return -1; } if (json_output) jsonw_start_array(json_wtr); else printf("%s\n" "%-8s %-15s %-15s %-15s\n", "CgroupPath", "ID", "AttachType", "AttachFlags", "Name"); switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) { case NFTW_ERR: p_err("can't iterate over %s: %s", cgroup_root, strerror(errno)); ret = -1; break; case SHOW_TREE_FN_ERR: ret = -1; break; default: ret = 0; } if (json_output) jsonw_end_array(json_wtr); if (argc == 0) free(cgroup_root); return ret; }
static int do_dump_btf(const struct btf_dumper *d, struct bpf_map_info *map_info, void *key, void *value) { int ret; /* start of key-value pair */ jsonw_start_object(d->jw); if (map_info->btf_key_type_id) { jsonw_name(d->jw, "key"); ret = btf_dumper_type(d, map_info->btf_key_type_id, key); if (ret) goto err_end_obj; } if (!map_is_per_cpu(map_info->type)) { jsonw_name(d->jw, "value"); ret = btf_dumper_type(d, map_info->btf_value_type_id, value); } else { unsigned int i, n, step; jsonw_name(d->jw, "values"); jsonw_start_array(d->jw); n = get_possible_cpus(); step = round_up(map_info->value_size, 8); for (i = 0; i < n; i++) { jsonw_start_object(d->jw); jsonw_int_field(d->jw, "cpu", i); jsonw_name(d->jw, "value"); ret = btf_dumper_type(d, map_info->btf_value_type_id, value + i * step); jsonw_end_object(d->jw); if (ret) break; } jsonw_end_array(d->jw); } err_end_obj: /* end of key-value pair */ jsonw_end_object(d->jw); return ret; }
static int do_show(int argc, char **argv) { enum bpf_attach_type type; int cgroup_fd; int ret = -1; if (argc < 1) { p_err("too few parameters for cgroup show"); goto exit; } else if (argc > 1) { p_err("too many parameters for cgroup show"); goto exit; } cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { p_err("can't open cgroup %s", argv[1]); goto exit; } if (json_output) jsonw_start_array(json_wtr); else printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType", "AttachFlags", "Name"); for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { /* * Not all attach types may be supported, so it's expected, * that some requests will fail. * If we were able to get the show for at least one * attach type, let's return 0. */ if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0) ret = 0; } if (json_output) jsonw_end_array(json_wtr); close(cgroup_fd); exit: return ret; }
static int do_show(int argc, char **argv) { int i, sock, ret, filter_idx = -1; struct bpf_netdev_t dev_array; unsigned int nl_pid; char err_buf[256]; if (argc == 2) { if (strcmp(argv[0], "dev") != 0) usage(); filter_idx = if_nametoindex(argv[1]); if (filter_idx == 0) { fprintf(stderr, "invalid dev name %s\n", argv[1]); return -1; } } else if (argc != 0) { usage(); } sock = libbpf_netlink_open(&nl_pid); if (sock < 0) { fprintf(stderr, "failed to open netlink sock\n"); return -1; } dev_array.devices = NULL; dev_array.used_len = 0; dev_array.array_len = 0; dev_array.filter_idx = filter_idx; if (json_output) jsonw_start_array(json_wtr); NET_START_OBJECT; NET_START_ARRAY("xdp", "%s:\n"); ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); NET_END_ARRAY("\n"); if (!ret) { NET_START_ARRAY("tc", "%s:\n"); for (i = 0; i < dev_array.used_len; i++) { ret = show_dev_tc_bpf(sock, nl_pid, &dev_array.devices[i]); if (ret) break; } NET_END_ARRAY("\n"); } NET_END_OBJECT; if (json_output) jsonw_end_array(json_wtr); if (ret) { if (json_output) jsonw_null(json_wtr); libbpf_strerror(ret, err_buf, sizeof(err_buf)); fprintf(stderr, "Error: %s\n", err_buf); } free(dev_array.devices); close(sock); return ret; }
static int do_show_tree_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftw) { enum bpf_attach_type type; bool skip = true; int cgroup_fd; if (typeflag != FTW_D) return 0; cgroup_fd = open(fpath, O_RDONLY); if (cgroup_fd < 0) { p_err("can't open cgroup %s: %s", fpath, strerror(errno)); return SHOW_TREE_FN_ERR; } for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { int count = count_attached_bpf_progs(cgroup_fd, type); if (count < 0 && errno != EINVAL) { p_err("can't query bpf programs attached to %s: %s", fpath, strerror(errno)); close(cgroup_fd); return SHOW_TREE_FN_ERR; } if (count > 0) { skip = false; break; } } if (skip) { close(cgroup_fd); return 0; } if (json_output) { jsonw_start_object(json_wtr); jsonw_string_field(json_wtr, "cgroup", fpath); jsonw_name(json_wtr, "programs"); jsonw_start_array(json_wtr); } else { printf("%s\n", fpath); } for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) show_attached_bpf_progs(cgroup_fd, type, ftw->level); if (errno == EINVAL) /* Last attach type does not support query. * Do not report an error for this, especially because batch * mode would stop processing commands. */ errno = 0; if (json_output) { jsonw_end_array(json_wtr); jsonw_end_object(json_wtr); } close(cgroup_fd); return 0; }
static int do_batch(int argc, char **argv) { char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; char *n_argv[BATCH_ARG_NB_MAX]; unsigned int lines = 0; int n_argc; FILE *fp; char *cp; int err; int i; if (argc < 2) { p_err("too few parameters for batch"); return -1; } else if (!is_prefix(*argv, "file")) { p_err("expected 'file', got: %s", *argv); return -1; } else if (argc > 2) { p_err("too many parameters for batch"); return -1; } NEXT_ARG(); if (!strcmp(*argv, "-")) fp = stdin; else fp = fopen(*argv, "r"); if (!fp) { p_err("Can't open file (%s): %s", *argv, strerror(errno)); return -1; } if (json_output) jsonw_start_array(json_wtr); while (fgets(buf, sizeof(buf), fp)) { cp = strchr(buf, '#'); if (cp) *cp = '\0'; if (strlen(buf) == sizeof(buf) - 1) { errno = E2BIG; break; } /* Append continuation lines if any (coming after a line ending * with '\' in the batch file). */ while ((cp = strstr(buf, "\\\n")) != NULL) { if (!fgets(contline, sizeof(contline), fp) || strlen(contline) == 0) { p_err("missing continuation line on command %d", lines); err = -1; goto err_close; } cp = strchr(contline, '#'); if (cp) *cp = '\0'; if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) { p_err("command %d is too long", lines); err = -1; goto err_close; } buf[strlen(buf) - 2] = '\0'; strcat(buf, contline); } n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines); if (!n_argc) continue; if (n_argc < 0) goto err_close; if (json_output) { jsonw_start_object(json_wtr); jsonw_name(json_wtr, "command"); jsonw_start_array(json_wtr); for (i = 0; i < n_argc; i++) jsonw_string(json_wtr, n_argv[i]); jsonw_end_array(json_wtr); jsonw_name(json_wtr, "output"); } err = cmd_select(cmds, n_argc, n_argv, do_help); if (json_output) jsonw_end_object(json_wtr); if (err) goto err_close; lines++; } if (errno && errno != ENOENT) { p_err("reading batch file failed: %s", strerror(errno)); err = -1; } else { if (!json_output) printf("processed %d commands\n", lines); err = 0; } err_close: if (fp != stdin) fclose(fp); if (json_output) jsonw_end_array(json_wtr); return err; }
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, bool opcodes, bool linum) { const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo; const struct bpf_insn_cbs cbs = { .cb_print = print_insn_json, .cb_call = print_call, .cb_imm = print_imm, .private_data = dd, }; struct bpf_func_info *record; struct bpf_insn *insn = buf; struct btf *btf = dd->btf; bool double_insn = false; unsigned int nr_skip = 0; char func_sig[1024]; unsigned int i; jsonw_start_array(json_wtr); record = dd->func_info; for (i = 0; i < len / sizeof(*insn); i++) { if (double_insn) { double_insn = false; continue; } double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); jsonw_start_object(json_wtr); if (btf && record) { if (record->insn_off == i) { btf_dumper_type_only(btf, record->type_id, func_sig, sizeof(func_sig)); if (func_sig[0] != '\0') { jsonw_name(json_wtr, "proto"); jsonw_string(json_wtr, func_sig); } record = (void *)record + dd->finfo_rec_size; } } if (prog_linfo) { const struct bpf_line_info *linfo; linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip); if (linfo) { btf_dump_linfo_json(btf, linfo, linum); nr_skip++; } } jsonw_name(json_wtr, "disasm"); print_bpf_insn(&cbs, insn + i, true); if (opcodes) { jsonw_name(json_wtr, "opcodes"); jsonw_start_object(json_wtr); jsonw_name(json_wtr, "code"); jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); jsonw_name(json_wtr, "src_reg"); jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); jsonw_name(json_wtr, "dst_reg"); jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); jsonw_name(json_wtr, "off"); print_hex_data_json((uint8_t *)(&insn[i].off), 2); jsonw_name(json_wtr, "imm"); if (double_insn && i < len - 1) print_hex_data_json((uint8_t *)(&insn[i].imm), 12); else print_hex_data_json((uint8_t *)(&insn[i].imm), 4); jsonw_end_object(json_wtr); } jsonw_end_object(json_wtr); } jsonw_end_array(json_wtr); }
static int dump_btf_type(const struct btf *btf, __u32 id, const struct btf_type *t) { json_writer_t *w = json_wtr; int kind, safe_kind; kind = BTF_INFO_KIND(t->info); safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN; if (json_output) { jsonw_start_object(w); jsonw_uint_field(w, "id", id); jsonw_string_field(w, "kind", btf_kind_str[safe_kind]); jsonw_string_field(w, "name", btf_str(btf, t->name_off)); } else { printf("[%u] %s '%s'", id, btf_kind_str[safe_kind], btf_str(btf, t->name_off)); } switch (BTF_INFO_KIND(t->info)) { case BTF_KIND_INT: { __u32 v = *(__u32 *)(t + 1); const char *enc; enc = btf_int_enc_str(BTF_INT_ENCODING(v)); if (json_output) { jsonw_uint_field(w, "size", t->size); jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v)); jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v)); jsonw_string_field(w, "encoding", enc); } else { printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s", t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v), enc); } break; } case BTF_KIND_PTR: case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_TYPEDEF: if (json_output) jsonw_uint_field(w, "type_id", t->type); else printf(" type_id=%u", t->type); break; case BTF_KIND_ARRAY: { const struct btf_array *arr = (const void *)(t + 1); if (json_output) { jsonw_uint_field(w, "type_id", arr->type); jsonw_uint_field(w, "index_type_id", arr->index_type); jsonw_uint_field(w, "nr_elems", arr->nelems); } else { printf(" type_id=%u index_type_id=%u nr_elems=%u", arr->type, arr->index_type, arr->nelems); } break; } case BTF_KIND_STRUCT: case BTF_KIND_UNION: { const struct btf_member *m = (const void *)(t + 1); __u16 vlen = BTF_INFO_VLEN(t->info); int i; if (json_output) { jsonw_uint_field(w, "size", t->size); jsonw_uint_field(w, "vlen", vlen); jsonw_name(w, "members"); jsonw_start_array(w); } else { printf(" size=%u vlen=%u", t->size, vlen); } for (i = 0; i < vlen; i++, m++) { const char *name = btf_str(btf, m->name_off); __u32 bit_off, bit_sz; if (BTF_INFO_KFLAG(t->info)) { bit_off = BTF_MEMBER_BIT_OFFSET(m->offset); bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset); } else { bit_off = m->offset; bit_sz = 0; } if (json_output) { jsonw_start_object(w); jsonw_string_field(w, "name", name); jsonw_uint_field(w, "type_id", m->type); jsonw_uint_field(w, "bits_offset", bit_off); if (bit_sz) { jsonw_uint_field(w, "bitfield_size", bit_sz); } jsonw_end_object(w); } else { printf("\n\t'%s' type_id=%u bits_offset=%u", name, m->type, bit_off); if (bit_sz) printf(" bitfield_size=%u", bit_sz); } } if (json_output) jsonw_end_array(w); break; } case BTF_KIND_ENUM: { const struct btf_enum *v = (const void *)(t + 1); __u16 vlen = BTF_INFO_VLEN(t->info); int i; if (json_output) { jsonw_uint_field(w, "size", t->size); jsonw_uint_field(w, "vlen", vlen); jsonw_name(w, "values"); jsonw_start_array(w); } else { printf(" size=%u vlen=%u", t->size, vlen); } for (i = 0; i < vlen; i++, v++) { const char *name = btf_str(btf, v->name_off); if (json_output) { jsonw_start_object(w); jsonw_string_field(w, "name", name); jsonw_uint_field(w, "val", v->val); jsonw_end_object(w); } else { printf("\n\t'%s' val=%u", name, v->val); } } if (json_output) jsonw_end_array(w); break; } case BTF_KIND_FWD: { const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union" : "struct"; if (json_output) jsonw_string_field(w, "fwd_kind", fwd_kind); else printf(" fwd_kind=%s", fwd_kind); break; } case BTF_KIND_FUNC: if (json_output) jsonw_uint_field(w, "type_id", t->type); else printf(" type_id=%u", t->type); break; case BTF_KIND_FUNC_PROTO: { const struct btf_param *p = (const void *)(t + 1); __u16 vlen = BTF_INFO_VLEN(t->info); int i; if (json_output) { jsonw_uint_field(w, "ret_type_id", t->type); jsonw_uint_field(w, "vlen", vlen); jsonw_name(w, "params"); jsonw_start_array(w); } else { printf(" ret_type_id=%u vlen=%u", t->type, vlen); } for (i = 0; i < vlen; i++, p++) { const char *name = btf_str(btf, p->name_off); if (json_output) { jsonw_start_object(w); jsonw_string_field(w, "name", name); jsonw_uint_field(w, "type_id", p->type); jsonw_end_object(w); } else { printf("\n\t'%s' type_id=%u", name, p->type); } } if (json_output) jsonw_end_array(w); break; } case BTF_KIND_VAR: { const struct btf_var *v = (const void *)(t + 1); const char *linkage; linkage = btf_var_linkage_str(v->linkage); if (json_output) { jsonw_uint_field(w, "type_id", t->type); jsonw_string_field(w, "linkage", linkage); } else { printf(" type_id=%u, linkage=%s", t->type, linkage); } break; } case BTF_KIND_DATASEC: { const struct btf_var_secinfo *v = (const void *)(t+1); __u16 vlen = BTF_INFO_VLEN(t->info); int i; if (json_output) { jsonw_uint_field(w, "size", t->size); jsonw_uint_field(w, "vlen", vlen); jsonw_name(w, "vars"); jsonw_start_array(w); } else { printf(" size=%u vlen=%u", t->size, vlen); } for (i = 0; i < vlen; i++, v++) { if (json_output) { jsonw_start_object(w); jsonw_uint_field(w, "type_id", v->type); jsonw_uint_field(w, "offset", v->offset); jsonw_uint_field(w, "size", v->size); jsonw_end_object(w); } else { printf("\n\ttype_id=%u offset=%u size=%u", v->type, v->offset, v->size); } } if (json_output) jsonw_end_array(w); break; } default: break; } if (json_output) jsonw_end_object(json_wtr); else printf("\n"); return 0; }
static int bpf_perf_event_open(int map_fd, int key, int cpu) { struct perf_event_attr attr = { .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_BPF_OUTPUT, }; int pmu_fd; pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0); if (pmu_fd < 0) { p_err("failed to open perf event %d for CPU %d", key, cpu); return -1; } if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) { p_err("failed to update map for event %d for CPU %d", key, cpu); goto err_close; } if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { p_err("failed to enable event %d for CPU %d", key, cpu); goto err_close; } return pmu_fd; err_close: close(pmu_fd); return -1; } int do_event_pipe(int argc, char **argv) { int i, nfds, map_fd, index = -1, cpu = -1; struct bpf_map_info map_info = {}; struct event_ring_info *rings; size_t tmp_buf_sz = 0; void *tmp_buf = NULL; struct pollfd *pfds; __u32 map_info_len; bool do_all = true; map_info_len = sizeof(map_info); map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); if (map_fd < 0) return -1; if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { p_err("map is not a perf event array"); goto err_close_map; } while (argc) { if (argc < 2) BAD_ARG(); if (is_prefix(*argv, "cpu")) { char *endptr; NEXT_ARG(); cpu = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as CPU ID", **argv); goto err_close_map; } NEXT_ARG(); } else if (is_prefix(*argv, "index")) { char *endptr; NEXT_ARG(); index = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as index", **argv); goto err_close_map; } NEXT_ARG(); } else { BAD_ARG(); } do_all = false; } if (!do_all) { if (index == -1 || cpu == -1) { p_err("cpu and index must be specified together"); goto err_close_map; } nfds = 1; } else { nfds = min(get_possible_cpus(), map_info.max_entries); cpu = 0; index = 0; } rings = calloc(nfds, sizeof(rings[0])); if (!rings) goto err_close_map; pfds = calloc(nfds, sizeof(pfds[0])); if (!pfds) goto err_free_rings; for (i = 0; i < nfds; i++) { rings[i].cpu = cpu + i; rings[i].key = index + i; rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key, rings[i].cpu); if (rings[i].fd < 0) goto err_close_fds_prev; rings[i].mem = perf_event_mmap(rings[i].fd); if (!rings[i].mem) goto err_close_fds_current; pfds[i].fd = rings[i].fd; pfds[i].events = POLLIN; } signal(SIGINT, int_exit); signal(SIGHUP, int_exit); signal(SIGTERM, int_exit); if (json_output) jsonw_start_array(json_wtr); while (!stop) { poll(pfds, nfds, 200); for (i = 0; i < nfds; i++) perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz); } free(tmp_buf); if (json_output) jsonw_end_array(json_wtr); for (i = 0; i < nfds; i++) { perf_event_unmap(rings[i].mem); close(rings[i].fd); } free(pfds); free(rings); close(map_fd); return 0; err_close_fds_prev: while (i--) { perf_event_unmap(rings[i].mem); err_close_fds_current: close(rings[i].fd); } free(pfds); err_free_rings: free(rings); err_close_map: close(map_fd); return -1; }
static int vlan_modify(int cmd, int argc, char **argv) { struct { struct nlmsghdr n; struct ifinfomsg ifm; char buf[1024]; } req = { .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .n.nlmsg_flags = NLM_F_REQUEST, .n.nlmsg_type = cmd, .ifm.ifi_family = PF_BRIDGE, }; char *d = NULL; short vid = -1; short vid_end = -1; struct rtattr *afspec; struct bridge_vlan_info vinfo = {}; unsigned short flags = 0; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); d = *argv; } else if (strcmp(*argv, "vid") == 0) { char *p; NEXT_ARG(); p = strchr(*argv, '-'); if (p) { *p = '\0'; p++; vid = atoi(*argv); vid_end = atoi(p); vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; } else { vid = atoi(*argv); } } else if (strcmp(*argv, "self") == 0) { flags |= BRIDGE_FLAGS_SELF; } else if (strcmp(*argv, "master") == 0) { flags |= BRIDGE_FLAGS_MASTER; } else if (strcmp(*argv, "pvid") == 0) { vinfo.flags |= BRIDGE_VLAN_INFO_PVID; } else if (strcmp(*argv, "untagged") == 0) { vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; } else { if (matches(*argv, "help") == 0) { NEXT_ARG(); } } argc--; argv++; } if (d == NULL || vid == -1) { fprintf(stderr, "Device and VLAN ID are required arguments.\n"); return -1; } req.ifm.ifi_index = ll_name_to_index(d); if (req.ifm.ifi_index == 0) { fprintf(stderr, "Cannot find bridge device \"%s\"\n", d); return -1; } if (vid >= 4096) { fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid); return -1; } if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) { fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n", vid, vid_end); return -1; } if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) { fprintf(stderr, "pvid cannot be configured for a vlan range\n"); return -1; } } afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); if (flags) addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags); vinfo.vid = vid; if (vid_end != -1) { /* send vlan range start */ addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, sizeof(vinfo)); vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; /* Now send the vlan range end */ vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; vinfo.vid = vid_end; addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, sizeof(vinfo)); } else { addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, sizeof(vinfo)); } addattr_nest_end(&req.n, afspec); if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) return -1; return 0; } /* In order to use this function for both filtering and non-filtering cases * we need to make it a tristate: * return -1 - if filtering we've gone over so don't continue * return 0 - skip entry and continue (applies to range start or to entries * which are less than filter_vlan) * return 1 - print the entry and continue */ static int filter_vlan_check(struct bridge_vlan_info *vinfo) { /* if we're filtering we should stop on the first greater entry */ if (filter_vlan && vinfo->vid > filter_vlan && !(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) return -1; if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) || vinfo->vid < filter_vlan) return 0; return 1; } static void print_vlan_port(FILE *fp, int ifi_index) { if (jw_global) { jsonw_pretty(jw_global, 1); jsonw_name(jw_global, ll_index_to_name(ifi_index)); jsonw_start_array(jw_global); } else { fprintf(fp, "%s", ll_index_to_name(ifi_index)); } } static void start_json_vlan_flags_array(bool *vlan_flags) { if (*vlan_flags) return; jsonw_name(jw_global, "flags"); jsonw_start_array(jw_global); *vlan_flags = true; } static int print_vlan(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = arg; struct ifinfomsg *ifm = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[IFLA_MAX+1]; bool vlan_flags; if (n->nlmsg_type != RTM_NEWLINK) { fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*ifm)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (ifm->ifi_family != AF_BRIDGE) return 0; if (filter_index && filter_index != ifm->ifi_index) return 0; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len); /* if AF_SPEC isn't there, vlan table is not preset for this port */ if (!tb[IFLA_AF_SPEC]) { if (!filter_vlan) fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index)); return 0; } else { struct rtattr *i, *list = tb[IFLA_AF_SPEC]; int rem = RTA_PAYLOAD(list); __u16 last_vid_start = 0; if (!filter_vlan) print_vlan_port(fp, ifm->ifi_index); for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { struct bridge_vlan_info *vinfo; int vcheck_ret; if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) continue; vinfo = RTA_DATA(i); if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) last_vid_start = vinfo->vid; vcheck_ret = filter_vlan_check(vinfo); if (vcheck_ret == -1) break; else if (vcheck_ret == 0) continue; if (filter_vlan) print_vlan_port(fp, ifm->ifi_index); if (jw_global) { jsonw_start_object(jw_global); jsonw_uint_field(jw_global, "vlan", last_vid_start); if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) continue; } else { fprintf(fp, "\t %hu", last_vid_start); } if (last_vid_start != vinfo->vid) { if (jw_global) jsonw_uint_field(jw_global, "vlanEnd", vinfo->vid); else fprintf(fp, "-%hu", vinfo->vid); } if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) { if (jw_global) { start_json_vlan_flags_array(&vlan_flags); jsonw_string(jw_global, "PVID"); } else { fprintf(fp, " PVID"); } } if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) { if (jw_global) { start_json_vlan_flags_array(&vlan_flags); jsonw_string(jw_global, "Egress Untagged"); } else { fprintf(fp, " Egress Untagged"); } } if (vlan_flags) { jsonw_end_array(jw_global); vlan_flags = false; } if (jw_global) jsonw_end_object(jw_global); else fprintf(fp, "\n"); } } if (!filter_vlan) { if (jw_global) jsonw_end_array(jw_global); else fprintf(fp, "\n"); } fflush(fp); return 0; }