diff options
Diffstat (limited to 'dcb/dcb.c')
-rw-r--r-- | dcb/dcb.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/dcb/dcb.c b/dcb/dcb.c new file mode 100644 index 0000000..fe0a0f0 --- /dev/null +++ b/dcb/dcb.c @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <inttypes.h> +#include <stdio.h> +#include <linux/dcbnl.h> +#include <libmnl/libmnl.h> +#include <getopt.h> + +#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); + free(dcb->buf); +} + +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 *payload; + __u16 payload_len; +}; + +static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data) +{ + struct dcb_get_attribute *ga = data; + + if (mnl_attr_get_type(attr) != ga->attr) + return MNL_CB_OK; + + ga->payload = mnl_attr_get_payload(attr); + ga->payload_len = mnl_attr_get_payload_len(attr); + return MNL_CB_OK; +} + +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_get_attribute_bare_cb(const struct nlmsghdr *nlh, void *data) +{ + /* Bare attributes (e.g. DCB_ATTR_DCBX) are not wrapped inside an IEEE + * container, so this does not have to go through unpacking in + * dcb_get_attribute_attr_cb(). + */ + return mnl_attr_parse(nlh, sizeof(struct dcbmsg), + dcb_get_attribute_attr_ieee_cb, data); +} + +struct dcb_set_attribute_response { + int response_attr; +}; + +static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data) +{ + struct dcb_set_attribute_response *resp = data; + uint16_t len; + int8_t err; + + if (mnl_attr_get_type(attr) != resp->response_attr) + 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; + } + + /* The attribute is formally u8, but actually an i8 containing a + * negative errno value. + */ + err = mnl_attr_get_u8(attr); + if (err) { + errno = -err; + return MNL_CB_ERROR; + } + + return MNL_CB_OK; +} + +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 | NLM_F_ACK, + &dcbm, sizeof(dcbm)); + mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev); + return nlh; +} + +static int __dcb_get_attribute(struct dcb *dcb, int command, + const char *dev, int attr, + void **payload_p, __u16 *payload_len_p, + int (*get_attribute_cb)(const struct nlmsghdr *nlh, + void *data)) +{ + struct dcb_get_attribute ga; + struct nlmsghdr *nlh; + int ret; + + nlh = dcb_prepare(dcb, dev, RTM_GETDCB, command); + + ga = (struct dcb_get_attribute) { + .dcb = dcb, + .attr = attr, + .payload = NULL, + }; + ret = dcb_talk(dcb, nlh, get_attribute_cb, &ga); + if (ret) { + perror("Attribute read"); + return ret; + } + if (ga.payload == NULL) { + perror("Attribute not found"); + return -ENOENT; + } + + *payload_p = ga.payload; + *payload_len_p = ga.payload_len; + return 0; +} + +int dcb_get_attribute_va(struct dcb *dcb, const char *dev, int attr, + void **payload_p, __u16 *payload_len_p) +{ + return __dcb_get_attribute(dcb, DCB_CMD_IEEE_GET, dev, attr, + payload_p, payload_len_p, + dcb_get_attribute_cb); +} + +int dcb_get_attribute_bare(struct dcb *dcb, int cmd, const char *dev, int attr, + void **payload_p, __u16 *payload_len_p) +{ + return __dcb_get_attribute(dcb, cmd, dev, attr, + payload_p, payload_len_p, + dcb_get_attribute_bare_cb); +} + +int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len) +{ + __u16 payload_len; + void *payload; + int ret; + + ret = dcb_get_attribute_va(dcb, dev, attr, &payload, &payload_len); + if (ret) + return ret; + + if (payload_len != data_len) { + fprintf(stderr, "Wrong len %d, expected %zd\n", payload_len, data_len); + return -EINVAL; + } + + memcpy(data, payload, data_len); + return 0; +} + +static int __dcb_set_attribute(struct dcb *dcb, int command, const char *dev, + int (*cb)(struct dcb *, struct nlmsghdr *, void *), + void *data, int response_attr) +{ + struct dcb_set_attribute_response resp = { + .response_attr = response_attr, + }; + struct nlmsghdr *nlh; + int ret; + + nlh = dcb_prepare(dcb, dev, RTM_SETDCB, command); + + ret = cb(dcb, nlh, data); + if (ret) + return ret; + + errno = 0; + ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, &resp); + if (ret) { + if (errno) + perror("Attribute write"); + return ret; + } + return 0; +} + +struct dcb_set_attribute_ieee_cb { + int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data); + void *data; +}; + +static int dcb_set_attribute_ieee_cb(struct dcb *dcb, struct nlmsghdr *nlh, void *data) +{ + struct dcb_set_attribute_ieee_cb *ieee_data = data; + struct nlattr *nest; + int ret; + + nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE); + ret = ieee_data->cb(dcb, nlh, ieee_data->data); + if (ret) + return ret; + mnl_attr_nest_end(nlh, nest); + + return 0; +} + +int dcb_set_attribute_va(struct dcb *dcb, int command, const char *dev, + int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data), + void *data) +{ + struct dcb_set_attribute_ieee_cb ieee_data = { + .cb = cb, + .data = data, + }; + + return __dcb_set_attribute(dcb, command, dev, + &dcb_set_attribute_ieee_cb, &ieee_data, + DCB_ATTR_IEEE); +} + +struct dcb_set_attribute { + int attr; + const void *data; + size_t data_len; +}; + +static int dcb_set_attribute_put(struct dcb *dcb, struct nlmsghdr *nlh, void *data) +{ + struct dcb_set_attribute *dsa = data; + + mnl_attr_put(nlh, dsa->attr, dsa->data_len, dsa->data); + return 0; +} + +int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len) +{ + struct dcb_set_attribute dsa = { + .attr = attr, + .data = data, + .data_len = data_len, + }; + + return dcb_set_attribute_va(dcb, DCB_CMD_IEEE_SET, dev, + &dcb_set_attribute_put, &dsa); +} + +int dcb_set_attribute_bare(struct dcb *dcb, int command, const char *dev, + int attr, const void *data, size_t data_len, + int response_attr) +{ + struct dcb_set_attribute dsa = { + .attr = attr, + .data = data, + .data_len = data_len, + }; + + return __dcb_set_attribute(dcb, command, dev, + &dcb_set_attribute_put, &dsa, response_attr); +} + +void dcb_print_array_u8(const __u8 *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + print_uint(PRINT_JSON, NULL, NULL, array[i]); + print_uint(PRINT_FP, NULL, "%zd:", i); + print_uint(PRINT_FP, NULL, "%d ", array[i]); + } +} + +void dcb_print_array_u64(const __u64 *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + print_u64(PRINT_JSON, NULL, NULL, array[i]); + print_uint(PRINT_FP, NULL, "%zd:", i); + print_u64(PRINT_FP, NULL, "%" PRIu64 " ", array[i]); + } +} + +void dcb_print_array_on_off(const __u8 *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + print_on_off(PRINT_JSON, NULL, NULL, array[i]); + print_uint(PRINT_FP, NULL, "%zd:", i); + print_on_off(PRINT_FP, NULL, "%s ", array[i]); + } +} + +void dcb_print_array_kw(const __u8 *array, size_t array_size, + const char *const kw[], size_t kw_size) +{ + size_t i; + + for (i = 0; i < array_size; i++) { + const char *str = "???"; + __u8 emt = array[i]; + + if (emt < kw_size && kw[emt]) + str = kw[emt]; + print_string(PRINT_JSON, NULL, NULL, str); + print_uint(PRINT_FP, NULL, "%zd:", i); + print_string(PRINT_FP, NULL, "%s ", str); + } +} + +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, __u64 value, __u64 max_value, + void (*set_array)(__u32 index, __u64 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..%llu\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, __u64 value, void *data) +{ + __u8 *array = data; + + array[key] = value; +} + +void dcb_set_u32(__u32 key, __u64 value, void *data) +{ + __u32 *array = data; + + array[key] = value; +} + +void dcb_set_u64(__u32 key, __u64 value, void *data) +{ + __u64 *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 := { app | apptrust | buffer | dcbx | ets | maxrate | pfc | rewr }\n" + " OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n" + " | -N | --Numeric | -p | --pretty\n" + " | -s | --statistics | -v | --verbose]\n"); +} + +static int dcb_cmd(struct dcb *dcb, int argc, char **argv) +{ + if (!argc || matches(*argv, "help") == 0) { + dcb_help(); + return 0; + } else if (matches(*argv, "app") == 0) { + return dcb_cmd_app(dcb, argc - 1, argv + 1); + } else if (strcmp(*argv, "apptrust") == 0) { + return dcb_cmd_apptrust(dcb, argc - 1, argv + 1); + } else if (strcmp(*argv, "rewr") == 0) { + return dcb_cmd_rewr(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "buffer") == 0) { + return dcb_cmd_buffer(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "dcbx") == 0) { + return dcb_cmd_dcbx(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "ets") == 0) { + return dcb_cmd_ets(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "maxrate") == 0) { + return dcb_cmd_maxrate(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "pfc") == 0) { + return dcb_cmd_pfc(dcb, argc - 1, argv + 1); + } + + 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' }, + { "iec", no_argument, NULL, 'i' }, + { "json", no_argument, NULL, 'j' }, + { "Numeric", no_argument, NULL, 'N' }, + { "pretty", no_argument, NULL, 'p' }, + { "statistics", no_argument, NULL, 's' }, + { "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:fhijn:psvNV", + 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 'N': + dcb->numeric = true; + break; + case 'p': + pretty = true; + break; + case 's': + dcb->stats = true; + break; + case 'n': + if (netns_switch(optarg)) { + ret = EXIT_FAILURE; + goto dcb_free; + } + break; + case 'i': + dcb->use_iec = true; + break; + case 'h': + dcb_help(); + ret = EXIT_SUCCESS; + goto dcb_free; + 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; +} |