iproute2/dcb/dcb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3#include <inttypes.h>
   4#include <stdio.h>
   5#include <linux/dcbnl.h>
   6#include <libmnl/libmnl.h>
   7#include <getopt.h>
   8
   9#include "dcb.h"
  10#include "mnl_utils.h"
  11#include "namespace.h"
  12#include "utils.h"
  13#include "version.h"
  14
  15static int dcb_init(struct dcb *dcb)
  16{
  17        dcb->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
  18        if (dcb->buf == NULL) {
  19                perror("Netlink buffer allocation");
  20                return -1;
  21        }
  22
  23        dcb->nl = mnlu_socket_open(NETLINK_ROUTE);
  24        if (dcb->nl == NULL) {
  25                perror("Open netlink socket");
  26                goto err_socket_open;
  27        }
  28
  29        new_json_obj_plain(dcb->json_output);
  30        return 0;
  31
  32err_socket_open:
  33        free(dcb->buf);
  34        return -1;
  35}
  36
  37static void dcb_fini(struct dcb *dcb)
  38{
  39        delete_json_obj_plain();
  40        mnl_socket_close(dcb->nl);
  41        free(dcb->buf);
  42}
  43
  44static struct dcb *dcb_alloc(void)
  45{
  46        struct dcb *dcb;
  47
  48        dcb = calloc(1, sizeof(*dcb));
  49        if (!dcb)
  50                return NULL;
  51        return dcb;
  52}
  53
  54static void dcb_free(struct dcb *dcb)
  55{
  56        free(dcb);
  57}
  58
  59struct dcb_get_attribute {
  60        struct dcb *dcb;
  61        int attr;
  62        void *payload;
  63        __u16 payload_len;
  64};
  65
  66static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data)
  67{
  68        struct dcb_get_attribute *ga = data;
  69
  70        if (mnl_attr_get_type(attr) != ga->attr)
  71                return MNL_CB_OK;
  72
  73        ga->payload = mnl_attr_get_payload(attr);
  74        ga->payload_len = mnl_attr_get_payload_len(attr);
  75        return MNL_CB_OK;
  76}
  77
  78static int dcb_get_attribute_attr_cb(const struct nlattr *attr, void *data)
  79{
  80        if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
  81                return MNL_CB_OK;
  82
  83        return mnl_attr_parse_nested(attr, dcb_get_attribute_attr_ieee_cb, data);
  84}
  85
  86static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data)
  87{
  88        return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data);
  89}
  90
  91static int dcb_get_attribute_bare_cb(const struct nlmsghdr *nlh, void *data)
  92{
  93        /* Bare attributes (e.g. DCB_ATTR_DCBX) are not wrapped inside an IEEE
  94         * container, so this does not have to go through unpacking in
  95         * dcb_get_attribute_attr_cb().
  96         */
  97        return mnl_attr_parse(nlh, sizeof(struct dcbmsg),
  98                              dcb_get_attribute_attr_ieee_cb, data);
  99}
 100
 101struct dcb_set_attribute_response {
 102        int response_attr;
 103};
 104
 105static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data)
 106{
 107        struct dcb_set_attribute_response *resp = data;
 108        uint16_t len;
 109        int8_t err;
 110
 111        if (mnl_attr_get_type(attr) != resp->response_attr)
 112                return MNL_CB_OK;
 113
 114        len = mnl_attr_get_payload_len(attr);
 115        if (len != 1) {
 116                fprintf(stderr, "Response attribute expected to have size 1, not %d\n", len);
 117                return MNL_CB_ERROR;
 118        }
 119
 120        /* The attribute is formally u8, but actually an i8 containing a
 121         * negative errno value.
 122         */
 123        err = mnl_attr_get_u8(attr);
 124        if (err) {
 125                errno = -err;
 126                return MNL_CB_ERROR;
 127        }
 128
 129        return MNL_CB_OK;
 130}
 131
 132static int dcb_set_attribute_cb(const struct nlmsghdr *nlh, void *data)
 133{
 134        return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_set_attribute_attr_cb, data);
 135}
 136
 137static int dcb_talk(struct dcb *dcb, struct nlmsghdr *nlh, mnl_cb_t cb, void *data)
 138{
 139        int ret;
 140
 141        ret = mnl_socket_sendto(dcb->nl, nlh, nlh->nlmsg_len);
 142        if (ret < 0) {
 143                perror("mnl_socket_sendto");
 144                return -1;
 145        }
 146
 147        return mnlu_socket_recv_run(dcb->nl, nlh->nlmsg_seq, dcb->buf, MNL_SOCKET_BUFFER_SIZE,
 148                                    cb, data);
 149}
 150
 151static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev,
 152                                    uint32_t nlmsg_type, uint8_t dcb_cmd)
 153{
 154        struct dcbmsg dcbm = {
 155                .cmd = dcb_cmd,
 156        };
 157        struct nlmsghdr *nlh;
 158
 159        nlh = mnlu_msg_prepare(dcb->buf, nlmsg_type, NLM_F_REQUEST | NLM_F_ACK,
 160                               &dcbm, sizeof(dcbm));
 161        mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev);
 162        return nlh;
 163}
 164
 165static int __dcb_get_attribute(struct dcb *dcb, int command,
 166                               const char *dev, int attr,
 167                               void **payload_p, __u16 *payload_len_p,
 168                               int (*get_attribute_cb)(const struct nlmsghdr *nlh,
 169                                                       void *data))
 170{
 171        struct dcb_get_attribute ga;
 172        struct nlmsghdr *nlh;
 173        int ret;
 174
 175        nlh = dcb_prepare(dcb, dev, RTM_GETDCB, command);
 176
 177        ga = (struct dcb_get_attribute) {
 178                .dcb = dcb,
 179                .attr = attr,
 180                .payload = NULL,
 181        };
 182        ret = dcb_talk(dcb, nlh, get_attribute_cb, &ga);
 183        if (ret) {
 184                perror("Attribute read");
 185                return ret;
 186        }
 187        if (ga.payload == NULL) {
 188                perror("Attribute not found");
 189                return -ENOENT;
 190        }
 191
 192        *payload_p = ga.payload;
 193        *payload_len_p = ga.payload_len;
 194        return 0;
 195}
 196
 197int dcb_get_attribute_va(struct dcb *dcb, const char *dev, int attr,
 198                         void **payload_p, __u16 *payload_len_p)
 199{
 200        return __dcb_get_attribute(dcb, DCB_CMD_IEEE_GET, dev, attr,
 201                                   payload_p, payload_len_p,
 202                                   dcb_get_attribute_cb);
 203}
 204
 205int dcb_get_attribute_bare(struct dcb *dcb, int cmd, const char *dev, int attr,
 206                           void **payload_p, __u16 *payload_len_p)
 207{
 208        return __dcb_get_attribute(dcb, cmd, dev, attr,
 209                                   payload_p, payload_len_p,
 210                                   dcb_get_attribute_bare_cb);
 211}
 212
 213int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
 214{
 215        __u16 payload_len;
 216        void *payload;
 217        int ret;
 218
 219        ret = dcb_get_attribute_va(dcb, dev, attr, &payload, &payload_len);
 220        if (ret)
 221                return ret;
 222
 223        if (payload_len != data_len) {
 224                fprintf(stderr, "Wrong len %d, expected %zd\n", payload_len, data_len);
 225                return -EINVAL;
 226        }
 227
 228        memcpy(data, payload, data_len);
 229        return 0;
 230}
 231
 232static int __dcb_set_attribute(struct dcb *dcb, int command, const char *dev,
 233                               int (*cb)(struct dcb *, struct nlmsghdr *, void *),
 234                               void *data, int response_attr)
 235{
 236        struct dcb_set_attribute_response resp = {
 237                .response_attr = response_attr,
 238        };
 239        struct nlmsghdr *nlh;
 240        int ret;
 241
 242        nlh = dcb_prepare(dcb, dev, RTM_SETDCB, command);
 243
 244        ret = cb(dcb, nlh, data);
 245        if (ret)
 246                return ret;
 247
 248        errno = 0;
 249        ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, &resp);
 250        if (ret) {
 251                if (errno)
 252                        perror("Attribute write");
 253                return ret;
 254        }
 255        return 0;
 256}
 257
 258struct dcb_set_attribute_ieee_cb {
 259        int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data);
 260        void *data;
 261};
 262
 263static int dcb_set_attribute_ieee_cb(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
 264{
 265        struct dcb_set_attribute_ieee_cb *ieee_data = data;
 266        struct nlattr *nest;
 267        int ret;
 268
 269        nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
 270        ret = ieee_data->cb(dcb, nlh, ieee_data->data);
 271        if (ret)
 272                return ret;
 273        mnl_attr_nest_end(nlh, nest);
 274
 275        return 0;
 276}
 277
 278int dcb_set_attribute_va(struct dcb *dcb, int command, const char *dev,
 279                         int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data),
 280                         void *data)
 281{
 282        struct dcb_set_attribute_ieee_cb ieee_data = {
 283                .cb = cb,
 284                .data = data,
 285        };
 286
 287        return __dcb_set_attribute(dcb, command, dev,
 288                                   &dcb_set_attribute_ieee_cb, &ieee_data,
 289                                   DCB_ATTR_IEEE);
 290}
 291
 292struct dcb_set_attribute {
 293        int attr;
 294        const void *data;
 295        size_t data_len;
 296};
 297
 298static int dcb_set_attribute_put(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
 299{
 300        struct dcb_set_attribute *dsa = data;
 301
 302        mnl_attr_put(nlh, dsa->attr, dsa->data_len, dsa->data);
 303        return 0;
 304}
 305
 306int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
 307{
 308        struct dcb_set_attribute dsa = {
 309                .attr = attr,
 310                .data = data,
 311                .data_len = data_len,
 312        };
 313
 314        return dcb_set_attribute_va(dcb, DCB_CMD_IEEE_SET, dev,
 315                                    &dcb_set_attribute_put, &dsa);
 316}
 317
 318int dcb_set_attribute_bare(struct dcb *dcb, int command, const char *dev,
 319                           int attr, const void *data, size_t data_len,
 320                           int response_attr)
 321{
 322        struct dcb_set_attribute dsa = {
 323                .attr = attr,
 324                .data = data,
 325                .data_len = data_len,
 326        };
 327
 328        return __dcb_set_attribute(dcb, command, dev,
 329                                   &dcb_set_attribute_put, &dsa, response_attr);
 330}
 331
 332void dcb_print_array_u8(const __u8 *array, size_t size)
 333{
 334        size_t i;
 335
 336        for (i = 0; i < size; i++) {
 337                print_uint(PRINT_JSON, NULL, NULL, array[i]);
 338                print_uint(PRINT_FP, NULL, "%zd:", i);
 339                print_uint(PRINT_FP, NULL, "%d ", array[i]);
 340        }
 341}
 342
 343void dcb_print_array_u64(const __u64 *array, size_t size)
 344{
 345        size_t i;
 346
 347        for (i = 0; i < size; i++) {
 348                print_u64(PRINT_JSON, NULL, NULL, array[i]);
 349                print_uint(PRINT_FP, NULL, "%zd:", i);
 350                print_u64(PRINT_FP, NULL, "%" PRIu64 " ", array[i]);
 351        }
 352}
 353
 354void dcb_print_array_on_off(const __u8 *array, size_t size)
 355{
 356        size_t i;
 357
 358        for (i = 0; i < size; i++) {
 359                print_on_off(PRINT_JSON, NULL, NULL, array[i]);
 360                print_uint(PRINT_FP, NULL, "%zd:", i);
 361                print_on_off(PRINT_FP, NULL, "%s ", array[i]);
 362        }
 363}
 364
 365void dcb_print_array_kw(const __u8 *array, size_t array_size,
 366                        const char *const kw[], size_t kw_size)
 367{
 368        size_t i;
 369
 370        for (i = 0; i < array_size; i++) {
 371                const char *str = "???";
 372                __u8 emt = array[i];
 373
 374                if (emt < kw_size && kw[emt])
 375                        str = kw[emt];
 376                print_string(PRINT_JSON, NULL, NULL, str);
 377                print_uint(PRINT_FP, NULL, "%zd:", i);
 378                print_string(PRINT_FP, NULL, "%s ", str);
 379        }
 380}
 381
 382void dcb_print_named_array(const char *json_name, const char *fp_name,
 383                           const __u8 *array, size_t size,
 384                           void (*print_array)(const __u8 *, size_t))
 385{
 386        open_json_array(PRINT_JSON, json_name);
 387        print_string(PRINT_FP, NULL, "%s ", fp_name);
 388        print_array(array, size);
 389        close_json_array(PRINT_JSON, json_name);
 390}
 391
 392int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
 393                      const char *what_value, __u64 value, __u64 max_value,
 394                      void (*set_array)(__u32 index, __u64 value, void *data),
 395                      void *set_array_data)
 396{
 397        bool is_all = key == (__u32) -1;
 398
 399        if (!is_all && key > max_key) {
 400                fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n",
 401                        what_key, what_value, what_key, max_key);
 402                return -EINVAL;
 403        }
 404
 405        if (value > max_value) {
 406                fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%llu\n",
 407                        what_key, what_value, what_value, max_value);
 408                return -EINVAL;
 409        }
 410
 411        if (is_all) {
 412                for (key = 0; key <= max_key; key++)
 413                        set_array(key, value, set_array_data);
 414        } else {
 415                set_array(key, value, set_array_data);
 416        }
 417
 418        return 0;
 419}
 420
 421void dcb_set_u8(__u32 key, __u64 value, void *data)
 422{
 423        __u8 *array = data;
 424
 425        array[key] = value;
 426}
 427
 428void dcb_set_u32(__u32 key, __u64 value, void *data)
 429{
 430        __u32 *array = data;
 431
 432        array[key] = value;
 433}
 434
 435void dcb_set_u64(__u32 key, __u64 value, void *data)
 436{
 437        __u64 *array = data;
 438
 439        array[key] = value;
 440}
 441
 442int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv,
 443                      int (*and_then)(struct dcb *dcb, const char *dev,
 444                                      int argc, char **argv),
 445                      void (*help)(void))
 446{
 447        const char *dev;
 448
 449        if (!argc || matches(*argv, "help") == 0) {
 450                help();
 451                return 0;
 452        } else if (matches(*argv, "dev") == 0) {
 453                NEXT_ARG();
 454                dev = *argv;
 455                if (check_ifname(dev)) {
 456                        invarg("not a valid ifname", *argv);
 457                        return -EINVAL;
 458                }
 459                NEXT_ARG_FWD();
 460                return and_then(dcb, dev, argc, argv);
 461        } else {
 462                fprintf(stderr, "Expected `dev DEV', not `%s'", *argv);
 463                help();
 464                return -EINVAL;
 465        }
 466}
 467
 468static void dcb_help(void)
 469{
 470        fprintf(stderr,
 471                "Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n"
 472                "       dcb [ -f | --force ] { -b | --batch } filename [ -n | --netns ] netnsname\n"
 473                "where  OBJECT := { app | apptrust | buffer | dcbx | ets | maxrate | pfc | rewr }\n"
 474                "       OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n"
 475                "                  | -N | --Numeric | -p | --pretty\n"
 476                "                  | -s | --statistics | -v | --verbose]\n");
 477}
 478
 479static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
 480{
 481        if (!argc || matches(*argv, "help") == 0) {
 482                dcb_help();
 483                return 0;
 484        } else if (matches(*argv, "app") == 0) {
 485                return dcb_cmd_app(dcb, argc - 1, argv + 1);
 486        } else if (strcmp(*argv, "apptrust") == 0) {
 487                return dcb_cmd_apptrust(dcb, argc - 1, argv + 1);
 488        } else if (strcmp(*argv, "rewr") == 0) {
 489                return dcb_cmd_rewr(dcb, argc - 1, argv + 1);
 490        } else if (matches(*argv, "buffer") == 0) {
 491                return dcb_cmd_buffer(dcb, argc - 1, argv + 1);
 492        } else if (matches(*argv, "dcbx") == 0) {
 493                return dcb_cmd_dcbx(dcb, argc - 1, argv + 1);
 494        } else if (matches(*argv, "ets") == 0) {
 495                return dcb_cmd_ets(dcb, argc - 1, argv + 1);
 496        } else if (matches(*argv, "maxrate") == 0) {
 497                return dcb_cmd_maxrate(dcb, argc - 1, argv + 1);
 498        } else if (matches(*argv, "pfc") == 0) {
 499                return dcb_cmd_pfc(dcb, argc - 1, argv + 1);
 500        }
 501
 502        fprintf(stderr, "Object \"%s\" is unknown\n", *argv);
 503        return -ENOENT;
 504}
 505
 506static int dcb_batch_cmd(int argc, char *argv[], void *data)
 507{
 508        struct dcb *dcb = data;
 509
 510        return dcb_cmd(dcb, argc, argv);
 511}
 512
 513static int dcb_batch(struct dcb *dcb, const char *name, bool force)
 514{
 515        return do_batch(name, force, dcb_batch_cmd, dcb);
 516}
 517
 518int main(int argc, char **argv)
 519{
 520        static const struct option long_options[] = {
 521                { "Version",            no_argument,            NULL, 'V' },
 522                { "force",              no_argument,            NULL, 'f' },
 523                { "batch",              required_argument,      NULL, 'b' },
 524                { "iec",                no_argument,            NULL, 'i' },
 525                { "json",               no_argument,            NULL, 'j' },
 526                { "Numeric",            no_argument,            NULL, 'N' },
 527                { "pretty",             no_argument,            NULL, 'p' },
 528                { "statistics",         no_argument,            NULL, 's' },
 529                { "netns",              required_argument,      NULL, 'n' },
 530                { "help",               no_argument,            NULL, 'h' },
 531                { NULL, 0, NULL, 0 }
 532        };
 533        const char *batch_file = NULL;
 534        bool force = false;
 535        struct dcb *dcb;
 536        int opt;
 537        int err;
 538        int ret;
 539
 540        dcb = dcb_alloc();
 541        if (!dcb) {
 542                fprintf(stderr, "Failed to allocate memory for dcb\n");
 543                return EXIT_FAILURE;
 544        }
 545
 546        while ((opt = getopt_long(argc, argv, "b:fhijn:psvNV",
 547                                  long_options, NULL)) >= 0) {
 548
 549                switch (opt) {
 550                case 'V':
 551                        printf("dcb utility, iproute2-%s\n", version);
 552                        ret = EXIT_SUCCESS;
 553                        goto dcb_free;
 554                case 'f':
 555                        force = true;
 556                        break;
 557                case 'b':
 558                        batch_file = optarg;
 559                        break;
 560                case 'j':
 561                        dcb->json_output = true;
 562                        break;
 563                case 'N':
 564                        dcb->numeric = true;
 565                        break;
 566                case 'p':
 567                        pretty = true;
 568                        break;
 569                case 's':
 570                        dcb->stats = true;
 571                        break;
 572                case 'n':
 573                        if (netns_switch(optarg)) {
 574                                ret = EXIT_FAILURE;
 575                                goto dcb_free;
 576                        }
 577                        break;
 578                case 'i':
 579                        dcb->use_iec = true;
 580                        break;
 581                case 'h':
 582                        dcb_help();
 583                        ret = EXIT_SUCCESS;
 584                        goto dcb_free;
 585                default:
 586                        fprintf(stderr, "Unknown option.\n");
 587                        dcb_help();
 588                        ret = EXIT_FAILURE;
 589                        goto dcb_free;
 590                }
 591        }
 592
 593        argc -= optind;
 594        argv += optind;
 595
 596        err = dcb_init(dcb);
 597        if (err) {
 598                ret = EXIT_FAILURE;
 599                goto dcb_free;
 600        }
 601
 602        if (batch_file)
 603                err = dcb_batch(dcb, batch_file, force);
 604        else
 605                err = dcb_cmd(dcb, argc, argv);
 606
 607        if (err) {
 608                ret = EXIT_FAILURE;
 609                goto dcb_fini;
 610        }
 611
 612        ret = EXIT_SUCCESS;
 613
 614dcb_fini:
 615        dcb_fini(dcb);
 616dcb_free:
 617        dcb_free(dcb);
 618
 619        return ret;
 620}
 621