// SPDX-License-Identifier: GPL-2.0+ #include #include #include #include #include "dcb.h" #include "mnl_utils.h" #include "namespace.h" #include "utils.h" #include "version.h" static int dcb_init(struct dcb *dcb) { dcb->buf = malloc(MNL_SOCKET_BUFFER_SIZE); if (dcb->buf == NULL) { perror("Netlink buffer allocation"); return -1; } dcb->nl = mnlu_socket_open(NETLINK_ROUTE); if (dcb->nl == NULL) { perror("Open netlink socket"); goto err_socket_open; } new_json_obj_plain(dcb->json_output); return 0; err_socket_open: free(dcb->buf); return -1; } static void dcb_fini(struct dcb *dcb) { delete_json_obj_plain(); mnl_socket_close(dcb->nl); } static struct dcb *dcb_alloc(void) { struct dcb *dcb; dcb = calloc(1, sizeof(*dcb)); if (!dcb) return NULL; return dcb; } static void dcb_free(struct dcb *dcb) { free(dcb); } struct dcb_get_attribute { struct dcb *dcb; int attr; void *data; size_t data_len; }; static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data) { struct dcb_get_attribute *ga = data; uint16_t len; if (mnl_attr_get_type(attr) != ga->attr) return MNL_CB_OK; len = mnl_attr_get_payload_len(attr); if (len != ga->data_len) { fprintf(stderr, "Wrong len %d, expected %zd\n", len, ga->data_len); return MNL_CB_ERROR; } memcpy(ga->data, mnl_attr_get_payload(attr), ga->data_len); return MNL_CB_STOP; } static int dcb_get_attribute_attr_cb(const struct nlattr *attr, void *data) { if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE) return MNL_CB_OK; return mnl_attr_parse_nested(attr, dcb_get_attribute_attr_ieee_cb, data); } static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data) { return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data); } static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data) { uint16_t len; uint8_t err; if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE) return MNL_CB_OK; len = mnl_attr_get_payload_len(attr); if (len != 1) { fprintf(stderr, "Response attribute expected to have size 1, not %d\n", len); return MNL_CB_ERROR; } err = mnl_attr_get_u8(attr); if (err) { fprintf(stderr, "Error when attempting to set attribute: %s\n", strerror(err)); return MNL_CB_ERROR; } return MNL_CB_STOP; } static int dcb_set_attribute_cb(const struct nlmsghdr *nlh, void *data) { return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_set_attribute_attr_cb, data); } static int dcb_talk(struct dcb *dcb, struct nlmsghdr *nlh, mnl_cb_t cb, void *data) { int ret; ret = mnl_socket_sendto(dcb->nl, nlh, nlh->nlmsg_len); if (ret < 0) { perror("mnl_socket_sendto"); return -1; } return mnlu_socket_recv_run(dcb->nl, nlh->nlmsg_seq, dcb->buf, MNL_SOCKET_BUFFER_SIZE, cb, data); } static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev, uint32_t nlmsg_type, uint8_t dcb_cmd) { struct dcbmsg dcbm = { .cmd = dcb_cmd, }; struct nlmsghdr *nlh; nlh = mnlu_msg_prepare(dcb->buf, nlmsg_type, NLM_F_REQUEST, &dcbm, sizeof(dcbm)); mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev); return nlh; } int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len) { struct dcb_get_attribute ga; struct nlmsghdr *nlh; int ret; nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_GET); ga = (struct dcb_get_attribute) { .dcb = dcb, .attr = attr, .data = data, .data_len = data_len, }; ret = dcb_talk(dcb, nlh, dcb_get_attribute_cb, &ga); if (ret) { perror("Attribute read"); return ret; } return 0; } int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len) { struct nlmsghdr *nlh; struct nlattr *nest; int ret; nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_SET); nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE); mnl_attr_put(nlh, attr, data_len, data); mnl_attr_nest_end(nlh, nest); ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, NULL); if (ret) { perror("Attribute write"); return ret; } return 0; } void dcb_print_array_u8(const __u8 *array, size_t size) { SPRINT_BUF(b); size_t i; for (i = 0; i < size; i++) { snprintf(b, sizeof(b), "%zd:%%d ", i); print_uint(PRINT_ANY, NULL, b, array[i]); } } void dcb_print_array_kw(const __u8 *array, size_t array_size, const char *const kw[], size_t kw_size) { SPRINT_BUF(b); size_t i; for (i = 0; i < array_size; i++) { __u8 emt = array[i]; snprintf(b, sizeof(b), "%zd:%%s ", i); if (emt < kw_size && kw[emt]) print_string(PRINT_ANY, NULL, b, kw[emt]); else print_string(PRINT_ANY, NULL, b, "???"); } } void dcb_print_named_array(const char *json_name, const char *fp_name, const __u8 *array, size_t size, void (*print_array)(const __u8 *, size_t)) { open_json_array(PRINT_JSON, json_name); print_string(PRINT_FP, NULL, "%s ", fp_name); print_array(array, size); close_json_array(PRINT_JSON, json_name); } int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key, const char *what_value, __u32 value, __u32 max_value, void (*set_array)(__u32 index, __u32 value, void *data), void *set_array_data) { bool is_all = key == (__u32) -1; if (!is_all && key > max_key) { fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n", what_key, what_value, what_key, max_key); return -EINVAL; } if (value > max_value) { fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n", what_key, what_value, what_value, max_value); return -EINVAL; } if (is_all) { for (key = 0; key <= max_key; key++) set_array(key, value, set_array_data); } else { set_array(key, value, set_array_data); } return 0; } void dcb_set_u8(__u32 key, __u32 value, void *data) { __u8 *array = data; array[key] = value; } int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv, int (*and_then)(struct dcb *dcb, const char *dev, int argc, char **argv), void (*help)(void)) { const char *dev; if (!argc || matches(*argv, "help") == 0) { help(); return 0; } else if (matches(*argv, "dev") == 0) { NEXT_ARG(); dev = *argv; if (check_ifname(dev)) { invarg("not a valid ifname", *argv); return -EINVAL; } NEXT_ARG_FWD(); return and_then(dcb, dev, argc, argv); } else { fprintf(stderr, "Expected `dev DEV', not `%s'", *argv); help(); return -EINVAL; } } static void dcb_help(void) { fprintf(stderr, "Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n" " dcb [ -f | --force ] { -b | --batch } filename [ -N | --Netns ] netnsname\n" "where OBJECT :=\n" " OPTIONS := [ -V | --Version | -j | --json | -p | --pretty | -v | --verbose ]\n"); } static int dcb_cmd(struct dcb *dcb, int argc, char **argv) { if (!argc || matches(*argv, "help") == 0) { dcb_help(); return 0; } fprintf(stderr, "Object \"%s\" is unknown\n", *argv); return -ENOENT; } static int dcb_batch_cmd(int argc, char *argv[], void *data) { struct dcb *dcb = data; return dcb_cmd(dcb, argc, argv); } static int dcb_batch(struct dcb *dcb, const char *name, bool force) { return do_batch(name, force, dcb_batch_cmd, dcb); } int main(int argc, char **argv) { static const struct option long_options[] = { { "Version", no_argument, NULL, 'V' }, { "force", no_argument, NULL, 'f' }, { "batch", required_argument, NULL, 'b' }, { "json", no_argument, NULL, 'j' }, { "pretty", no_argument, NULL, 'p' }, { "Netns", required_argument, NULL, 'N' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 } }; const char *batch_file = NULL; bool force = false; struct dcb *dcb; int opt; int err; int ret; dcb = dcb_alloc(); if (!dcb) { fprintf(stderr, "Failed to allocate memory for dcb\n"); return EXIT_FAILURE; } while ((opt = getopt_long(argc, argv, "b:c::fhjnpvN:V", long_options, NULL)) >= 0) { switch (opt) { case 'V': printf("dcb utility, iproute2-%s\n", version); ret = EXIT_SUCCESS; goto dcb_free; case 'f': force = true; break; case 'b': batch_file = optarg; break; case 'j': dcb->json_output = true; break; case 'p': pretty = true; break; case 'N': if (netns_switch(optarg)) { ret = EXIT_FAILURE; goto dcb_free; } break; case 'h': dcb_help(); return 0; default: fprintf(stderr, "Unknown option.\n"); dcb_help(); ret = EXIT_FAILURE; goto dcb_free; } } argc -= optind; argv += optind; err = dcb_init(dcb); if (err) { ret = EXIT_FAILURE; goto dcb_free; } if (batch_file) err = dcb_batch(dcb, batch_file, force); else err = dcb_cmd(dcb, argc, argv); if (err) { ret = EXIT_FAILURE; goto dcb_fini; } ret = EXIT_SUCCESS; dcb_fini: dcb_fini(dcb); dcb_free: dcb_free(dcb); return ret; }