iproute2/ip/ipmptcp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <stdio.h>
   4#include <string.h>
   5#include <rt_names.h>
   6#include <errno.h>
   7
   8#include <linux/genetlink.h>
   9#include <linux/mptcp.h>
  10
  11#include "utils.h"
  12#include "ip_common.h"
  13#include "libgenl.h"
  14#include "json_print.h"
  15
  16static void usage(void)
  17{
  18        fprintf(stderr,
  19                "Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
  20                "                                     [ FLAG-LIST ]\n"
  21                "       ip mptcp endpoint delete id ID\n"
  22                "       ip mptcp endpoint show [ id ID ]\n"
  23                "       ip mptcp endpoint flush\n"
  24                "       ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
  25                "       ip mptcp limits show\n"
  26                "FLAG-LIST := [ FLAG-LIST ] FLAG\n"
  27                "FLAG  := [ signal | subflow | backup ]\n");
  28
  29        exit(-1);
  30}
  31
  32/* netlink socket */
  33static struct rtnl_handle genl_rth = { .fd = -1 };
  34static int genl_family = -1;
  35
  36#define MPTCP_BUFLEN    4096
  37#define MPTCP_REQUEST(_req,  _cmd, _flags)      \
  38        GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0,        \
  39                     MPTCP_PM_VER, _cmd, _flags)
  40
  41/* Mapping from argument to address flag mask */
  42static const struct {
  43        const char *name;
  44        unsigned long value;
  45} mptcp_addr_flag_names[] = {
  46        { "signal",             MPTCP_PM_ADDR_FLAG_SIGNAL },
  47        { "subflow",            MPTCP_PM_ADDR_FLAG_SUBFLOW },
  48        { "backup",             MPTCP_PM_ADDR_FLAG_BACKUP },
  49};
  50
  51static void print_mptcp_addr_flags(unsigned int flags)
  52{
  53        unsigned int i;
  54
  55        for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
  56                unsigned long mask = mptcp_addr_flag_names[i].value;
  57
  58                if (flags & mask) {
  59                        print_string(PRINT_FP, NULL, "%s ",
  60                                     mptcp_addr_flag_names[i].name);
  61                        print_bool(PRINT_JSON,
  62                                   mptcp_addr_flag_names[i].name, NULL, true);
  63                }
  64
  65                flags &= ~mask;
  66        }
  67
  68        if (flags) {
  69                /* unknown flags */
  70                SPRINT_BUF(b1);
  71
  72                snprintf(b1, sizeof(b1), "%02x", flags);
  73                print_string(PRINT_ANY, "rawflags", "rawflags %s ", b1);
  74        }
  75}
  76
  77static int get_flags(const char *arg, __u32 *flags)
  78{
  79        unsigned int i;
  80
  81        for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
  82                if (strcmp(arg, mptcp_addr_flag_names[i].name))
  83                        continue;
  84
  85                *flags |= mptcp_addr_flag_names[i].value;
  86                return 0;
  87        }
  88        return -1;
  89}
  90
  91static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
  92                         bool adding)
  93{
  94        struct rtattr *attr_addr;
  95        bool addr_set = false;
  96        inet_prefix address;
  97        bool id_set = false;
  98        __u32 index = 0;
  99        __u32 flags = 0;
 100        __u8 id = 0;
 101
 102        ll_init_map(&rth);
 103        while (argc > 0) {
 104                if (get_flags(*argv, &flags) == 0) {
 105                } else if (matches(*argv, "id") == 0) {
 106                        NEXT_ARG();
 107
 108                        if (get_u8(&id, *argv, 0))
 109                                invarg("invalid ID\n", *argv);
 110                        id_set = true;
 111                } else if (matches(*argv, "dev") == 0) {
 112                        const char *ifname;
 113
 114                        NEXT_ARG();
 115
 116                        ifname = *argv;
 117
 118                        if (check_ifname(ifname))
 119                                invarg("invalid interface name\n", ifname);
 120
 121                        index = ll_name_to_index(ifname);
 122
 123                        if (!index)
 124                                invarg("device does not exist\n", ifname);
 125
 126                } else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
 127                        addr_set = true;
 128                } else {
 129                        invarg("unknown argument", *argv);
 130                }
 131                NEXT_ARG_FWD();
 132        }
 133
 134        if (!addr_set && adding)
 135                missarg("ADDRESS");
 136
 137        if (!id_set && !adding)
 138                missarg("ID");
 139
 140        attr_addr = addattr_nest(n, MPTCP_BUFLEN,
 141                                 MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
 142        if (id_set)
 143                addattr8(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_ID, id);
 144        if (flags)
 145                addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
 146        if (index)
 147                addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
 148        if (addr_set) {
 149                int type;
 150
 151                addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FAMILY,
 152                          address.family);
 153                type = address.family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
 154                                                   MPTCP_PM_ADDR_ATTR_ADDR6;
 155                addattr_l(n, MPTCP_BUFLEN, type, &address.data,
 156                          address.bytelen);
 157        }
 158
 159        addattr_nest_end(n, attr_addr);
 160        return 0;
 161}
 162
 163static int mptcp_addr_modify(int argc, char **argv, int cmd)
 164{
 165        MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
 166        int ret;
 167
 168        ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR);
 169        if (ret)
 170                return ret;
 171
 172        if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
 173                return -2;
 174
 175        return 0;
 176}
 177
 178static int print_mptcp_addrinfo(struct rtattr *addrinfo)
 179{
 180        struct rtattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
 181        __u8 family = AF_UNSPEC, addr_attr_type;
 182        const char *ifname;
 183        unsigned int flags;
 184        int index;
 185        __u16 id;
 186
 187        parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
 188
 189        open_json_object(NULL);
 190        if (tb[MPTCP_PM_ADDR_ATTR_FAMILY])
 191                family = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
 192
 193        addr_attr_type = family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
 194                                             MPTCP_PM_ADDR_ATTR_ADDR6;
 195        if (tb[addr_attr_type]) {
 196                print_string(PRINT_ANY, "address", "%s ",
 197                             format_host_rta(family, tb[addr_attr_type]));
 198        }
 199        if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
 200                id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
 201                print_uint(PRINT_ANY, "id", "id %u ", id);
 202        }
 203        if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) {
 204                flags = rta_getattr_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
 205                print_mptcp_addr_flags(flags);
 206        }
 207        if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) {
 208                index = rta_getattr_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
 209                ifname = index ? ll_index_to_name(index) : NULL;
 210
 211                if (ifname)
 212                        print_string(PRINT_ANY, "dev", "dev %s ", ifname);
 213        }
 214
 215        close_json_object();
 216        print_string(PRINT_FP, NULL, "\n", NULL);
 217        fflush(stdout);
 218
 219        return 0;
 220}
 221
 222static int print_mptcp_addr(struct nlmsghdr *n, void *arg)
 223{
 224        struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
 225        struct genlmsghdr *ghdr;
 226        struct rtattr *addrinfo;
 227        int len = n->nlmsg_len;
 228
 229        if (n->nlmsg_type != genl_family)
 230                return 0;
 231
 232        len -= NLMSG_LENGTH(GENL_HDRLEN);
 233        if (len < 0)
 234                return -1;
 235
 236        ghdr = NLMSG_DATA(n);
 237        parse_rtattr_flags(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN,
 238                           len, NLA_F_NESTED);
 239        addrinfo = tb[MPTCP_PM_ATTR_ADDR];
 240        if (!addrinfo)
 241                return -1;
 242
 243        ll_init_map(&rth);
 244        return print_mptcp_addrinfo(addrinfo);
 245}
 246
 247static int mptcp_addr_dump(void)
 248{
 249        MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST | NLM_F_DUMP);
 250
 251        if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
 252                perror("Cannot send show request");
 253                exit(1);
 254        }
 255
 256        new_json_obj(json);
 257
 258        if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) {
 259                fprintf(stderr, "Dump terminated\n");
 260                delete_json_obj();
 261                fflush(stdout);
 262                return -2;
 263        }
 264
 265        close_json_object();
 266        fflush(stdout);
 267        return 0;
 268}
 269
 270static int mptcp_addr_show(int argc, char **argv)
 271{
 272        MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST);
 273        struct nlmsghdr *answer;
 274        int ret;
 275
 276        if (argc <= 0)
 277                return mptcp_addr_dump();
 278
 279        ret = mptcp_parse_opt(argc, argv, &req.n, false);
 280        if (ret)
 281                return ret;
 282
 283        if (rtnl_talk(&genl_rth, &req.n, &answer) < 0)
 284                return -2;
 285
 286        return print_mptcp_addr(answer, stdout);
 287}
 288
 289static int mptcp_addr_flush(int argc, char **argv)
 290{
 291        MPTCP_REQUEST(req, MPTCP_PM_CMD_FLUSH_ADDRS, NLM_F_REQUEST);
 292
 293        if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
 294                return -2;
 295
 296        return 0;
 297}
 298
 299static int mptcp_parse_limit(int argc, char **argv, struct nlmsghdr *n)
 300{
 301        bool set_rcv_add_addrs = false;
 302        bool set_subflows = false;
 303        __u32 rcv_add_addrs = 0;
 304        __u32 subflows = 0;
 305
 306        while (argc > 0) {
 307                if (matches(*argv, "subflows") == 0) {
 308                        NEXT_ARG();
 309
 310                        if (get_u32(&subflows, *argv, 0))
 311                                invarg("invalid subflows\n", *argv);
 312                        set_subflows = true;
 313                } else if (matches(*argv, "add_addr_accepted") == 0) {
 314                        NEXT_ARG();
 315
 316                        if (get_u32(&rcv_add_addrs, *argv, 0))
 317                                invarg("invalid add_addr_accepted\n", *argv);
 318                        set_rcv_add_addrs = true;
 319                } else {
 320                        invarg("unknown limit", *argv);
 321                }
 322                NEXT_ARG_FWD();
 323        }
 324
 325        if (set_rcv_add_addrs)
 326                addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_RCV_ADD_ADDRS,
 327                          rcv_add_addrs);
 328        if (set_subflows)
 329                addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_SUBFLOWS, subflows);
 330        return set_rcv_add_addrs || set_subflows;
 331}
 332
 333static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
 334{
 335        struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
 336        struct genlmsghdr *ghdr;
 337        int len = n->nlmsg_len;
 338        __u32 val;
 339
 340        if (n->nlmsg_type != genl_family)
 341                return 0;
 342
 343        len -= NLMSG_LENGTH(GENL_HDRLEN);
 344        if (len < 0)
 345                return -1;
 346
 347        ghdr = NLMSG_DATA(n);
 348        parse_rtattr(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
 349
 350        open_json_object(NULL);
 351        if (tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]) {
 352                val = rta_getattr_u32(tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]);
 353
 354                print_uint(PRINT_ANY, "add_addr_accepted",
 355                           "add_addr_accepted %d ", val);
 356        }
 357
 358        if (tb[MPTCP_PM_ATTR_SUBFLOWS]) {
 359                val = rta_getattr_u32(tb[MPTCP_PM_ATTR_SUBFLOWS]);
 360
 361                print_uint(PRINT_ANY, "subflows", "subflows %d ", val);
 362        }
 363        print_string(PRINT_FP, NULL, "%s", "\n");
 364        fflush(stdout);
 365        close_json_object();
 366        return 0;
 367}
 368
 369static int mptcp_limit_get_set(int argc, char **argv, int cmd)
 370{
 371        bool do_get = cmd == MPTCP_PM_CMD_GET_LIMITS;
 372        MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
 373        struct nlmsghdr *answer;
 374        int ret;
 375
 376        ret = mptcp_parse_limit(argc, argv, &req.n);
 377        if (ret < 0)
 378                return -1;
 379
 380        if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0)
 381                return -2;
 382
 383        if (do_get)
 384                return print_mptcp_limit(answer, stdout);
 385        return 0;
 386}
 387
 388int do_mptcp(int argc, char **argv)
 389{
 390        if (argc == 0)
 391                usage();
 392
 393        if (matches(*argv, "help") == 0)
 394                usage();
 395
 396        if (genl_init_handle(&genl_rth, MPTCP_PM_NAME, &genl_family))
 397                exit(1);
 398
 399        if (matches(*argv, "endpoint") == 0) {
 400                NEXT_ARG_FWD();
 401                if (argc == 0)
 402                        return mptcp_addr_show(0, NULL);
 403
 404                if (matches(*argv, "add") == 0)
 405                        return mptcp_addr_modify(argc-1, argv+1,
 406                                                 MPTCP_PM_CMD_ADD_ADDR);
 407                if (matches(*argv, "delete") == 0)
 408                        return mptcp_addr_modify(argc-1, argv+1,
 409                                                 MPTCP_PM_CMD_DEL_ADDR);
 410                if (matches(*argv, "show") == 0)
 411                        return mptcp_addr_show(argc-1, argv+1);
 412                if (matches(*argv, "flush") == 0)
 413                        return mptcp_addr_flush(argc-1, argv+1);
 414
 415                goto unknown;
 416        }
 417
 418        if (matches(*argv, "limits") == 0) {
 419                NEXT_ARG_FWD();
 420                if (argc == 0)
 421                        return mptcp_limit_get_set(0, NULL,
 422                                                   MPTCP_PM_CMD_GET_LIMITS);
 423
 424                if (matches(*argv, "set") == 0)
 425                        return mptcp_limit_get_set(argc-1, argv+1,
 426                                                   MPTCP_PM_CMD_SET_LIMITS);
 427                if (matches(*argv, "show") == 0)
 428                        return mptcp_limit_get_set(argc-1, argv+1,
 429                                                   MPTCP_PM_CMD_GET_LIMITS);
 430        }
 431
 432unknown:
 433        fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
 434                *argv);
 435        exit(-1);
 436}
 437