iproute2/ip/iplink_vlan.c
<<
>>
Prefs
   1/*
   2 * iplink_vlan.c        VLAN device support
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Patrick McHardy <kaber@trash.net>
  10 */
  11
  12#include <stdio.h>
  13#include <stdlib.h>
  14#include <string.h>
  15#include <linux/if_vlan.h>
  16
  17#include "rt_names.h"
  18#include "utils.h"
  19#include "ip_common.h"
  20
  21static void print_explain(FILE *f)
  22{
  23        fprintf(f,
  24                "Usage: ... vlan id VLANID\n"
  25                "               [ protocol VLANPROTO ]\n"
  26                "               [ reorder_hdr { on | off } ]\n"
  27                "               [ gvrp { on | off } ]\n"
  28                "               [ mvrp { on | off } ]\n"
  29                "               [ loose_binding { on | off } ]\n"
  30                "               [ bridge_binding { on | off } ]\n"
  31                "               [ ingress-qos-map QOS-MAP ]\n"
  32                "               [ egress-qos-map QOS-MAP ]\n"
  33                "\n"
  34                "VLANID := 0-4095\n"
  35                "VLANPROTO: [ 802.1Q | 802.1ad ]\n"
  36                "QOS-MAP := [ QOS-MAP ] QOS-MAPPING\n"
  37                "QOS-MAPPING := FROM:TO\n"
  38        );
  39}
  40
  41static void explain(void)
  42{
  43        print_explain(stderr);
  44}
  45
  46static int on_off(const char *msg, const char *arg)
  47{
  48        fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", msg, arg);
  49        return -1;
  50}
  51
  52static int parse_qos_mapping(__u32 key, char *value, void *data)
  53{
  54        struct nlmsghdr *n = data;
  55        struct ifla_vlan_qos_mapping m = {
  56                .from = key,
  57        };
  58
  59        if (get_u32(&m.to, value, 0))
  60                return 1;
  61
  62        return addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
  63}
  64
  65static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
  66                              int attrtype)
  67{
  68        struct rtattr *tail;
  69
  70        tail = addattr_nest(n, 1024, attrtype);
  71
  72        if (parse_mapping(argcp, argvp, false, &parse_qos_mapping, n))
  73                return 1;
  74
  75        addattr_nest_end(n, tail);
  76        return 0;
  77}
  78
  79static int vlan_parse_opt(struct link_util *lu, int argc, char **argv,
  80                          struct nlmsghdr *n)
  81{
  82        struct ifla_vlan_flags flags = { 0 };
  83        __u16 id, proto;
  84
  85        while (argc > 0) {
  86                if (matches(*argv, "protocol") == 0) {
  87                        NEXT_ARG();
  88                        if (ll_proto_a2n(&proto, *argv))
  89                                invarg("protocol is invalid", *argv);
  90                        addattr_l(n, 1024, IFLA_VLAN_PROTOCOL, &proto, 2);
  91                } else if (matches(*argv, "id") == 0) {
  92                        NEXT_ARG();
  93                        if (get_u16(&id, *argv, 0))
  94                                invarg("id is invalid", *argv);
  95                        addattr_l(n, 1024, IFLA_VLAN_ID, &id, 2);
  96                } else if (matches(*argv, "reorder_hdr") == 0) {
  97                        NEXT_ARG();
  98                        flags.mask |= VLAN_FLAG_REORDER_HDR;
  99                        if (strcmp(*argv, "on") == 0)
 100                                flags.flags |= VLAN_FLAG_REORDER_HDR;
 101                        else if (strcmp(*argv, "off") == 0)
 102                                flags.flags &= ~VLAN_FLAG_REORDER_HDR;
 103                        else
 104                                return on_off("reorder_hdr", *argv);
 105                } else if (matches(*argv, "gvrp") == 0) {
 106                        NEXT_ARG();
 107                        flags.mask |= VLAN_FLAG_GVRP;
 108                        if (strcmp(*argv, "on") == 0)
 109                                flags.flags |= VLAN_FLAG_GVRP;
 110                        else if (strcmp(*argv, "off") == 0)
 111                                flags.flags &= ~VLAN_FLAG_GVRP;
 112                        else
 113                                return on_off("gvrp", *argv);
 114                } else if (matches(*argv, "mvrp") == 0) {
 115                        NEXT_ARG();
 116                        flags.mask |= VLAN_FLAG_MVRP;
 117                        if (strcmp(*argv, "on") == 0)
 118                                flags.flags |= VLAN_FLAG_MVRP;
 119                        else if (strcmp(*argv, "off") == 0)
 120                                flags.flags &= ~VLAN_FLAG_MVRP;
 121                        else
 122                                return on_off("mvrp", *argv);
 123                } else if (matches(*argv, "loose_binding") == 0) {
 124                        NEXT_ARG();
 125                        flags.mask |= VLAN_FLAG_LOOSE_BINDING;
 126                        if (strcmp(*argv, "on") == 0)
 127                                flags.flags |= VLAN_FLAG_LOOSE_BINDING;
 128                        else if (strcmp(*argv, "off") == 0)
 129                                flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
 130                        else
 131                                return on_off("loose_binding", *argv);
 132                } else if (matches(*argv, "bridge_binding") == 0) {
 133                        NEXT_ARG();
 134                        flags.mask |= VLAN_FLAG_BRIDGE_BINDING;
 135                        if (strcmp(*argv, "on") == 0)
 136                                flags.flags |= VLAN_FLAG_BRIDGE_BINDING;
 137                        else if (strcmp(*argv, "off") == 0)
 138                                flags.flags &= ~VLAN_FLAG_BRIDGE_BINDING;
 139                        else
 140                                return on_off("bridge_binding", *argv);
 141                } else if (matches(*argv, "ingress-qos-map") == 0) {
 142                        NEXT_ARG();
 143                        if (vlan_parse_qos_map(&argc, &argv, n,
 144                                               IFLA_VLAN_INGRESS_QOS))
 145                                invarg("invalid ingress-qos-map", *argv);
 146                        continue;
 147                } else if (matches(*argv, "egress-qos-map") == 0) {
 148                        NEXT_ARG();
 149                        if (vlan_parse_qos_map(&argc, &argv, n,
 150                                               IFLA_VLAN_EGRESS_QOS))
 151                                invarg("invalid egress-qos-map", *argv);
 152                        continue;
 153                } else if (matches(*argv, "help") == 0) {
 154                        explain();
 155                        return -1;
 156                } else {
 157                        fprintf(stderr, "vlan: unknown command \"%s\"?\n", *argv);
 158                        explain();
 159                        return -1;
 160                }
 161                argc--, argv++;
 162        }
 163
 164        if (flags.mask)
 165                addattr_l(n, 1024, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
 166
 167        return 0;
 168}
 169
 170static void vlan_print_map(FILE *f,
 171                           const char *name_json,
 172                           const char *name_fp,
 173                           struct rtattr *attr)
 174{
 175        struct ifla_vlan_qos_mapping *m;
 176        struct rtattr *i;
 177        int rem;
 178
 179        open_json_array(PRINT_JSON, name_json);
 180        print_nl();
 181        print_string(PRINT_FP, NULL, "      %s { ", name_fp);
 182
 183        rem = RTA_PAYLOAD(attr);
 184        for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 185                m = RTA_DATA(i);
 186
 187                if (is_json_context()) {
 188                        open_json_object(NULL);
 189                        print_uint(PRINT_JSON, "from", NULL, m->from);
 190                        print_uint(PRINT_JSON, "to", NULL, m->to);
 191                        close_json_object();
 192                } else {
 193                        fprintf(f, "%u:%u ", m->from, m->to);
 194                }
 195        }
 196
 197        close_json_array(PRINT_JSON, NULL);
 198        print_string(PRINT_FP, NULL, "%s ", "}");
 199}
 200
 201static void vlan_print_flags(FILE *fp, __u32 flags)
 202{
 203        open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
 204#define _PF(f)  if (flags & VLAN_FLAG_##f) {                            \
 205                flags &= ~VLAN_FLAG_##f;                                \
 206                print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); \
 207        }
 208        _PF(REORDER_HDR);
 209        _PF(GVRP);
 210        _PF(MVRP);
 211        _PF(LOOSE_BINDING);
 212        _PF(BRIDGE_BINDING);
 213#undef _PF
 214        if (flags)
 215                print_hex(PRINT_ANY, NULL, "%x", flags);
 216        close_json_array(PRINT_ANY, "> ");
 217}
 218
 219static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 220{
 221        struct ifla_vlan_flags *flags;
 222
 223        SPRINT_BUF(b1);
 224
 225        if (!tb)
 226                return;
 227
 228        if (tb[IFLA_VLAN_PROTOCOL] &&
 229            RTA_PAYLOAD(tb[IFLA_VLAN_PROTOCOL]) < sizeof(__u16))
 230                return;
 231        if (!tb[IFLA_VLAN_ID] ||
 232            RTA_PAYLOAD(tb[IFLA_VLAN_ID]) < sizeof(__u16))
 233                return;
 234
 235        if (tb[IFLA_VLAN_PROTOCOL])
 236                print_string(PRINT_ANY,
 237                             "protocol",
 238                             "protocol %s ",
 239                             ll_proto_n2a(
 240                                     rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]),
 241                                     b1, sizeof(b1)));
 242        else
 243                print_string(PRINT_ANY, "protocol", "protocol %s ", "802.1q");
 244
 245        print_uint(PRINT_ANY,
 246                   "id",
 247                   "id %u ",
 248                   rta_getattr_u16(tb[IFLA_VLAN_ID]));
 249
 250        if (tb[IFLA_VLAN_FLAGS]) {
 251                if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags))
 252                        return;
 253                flags = RTA_DATA(tb[IFLA_VLAN_FLAGS]);
 254                vlan_print_flags(f, flags->flags);
 255        }
 256        if (tb[IFLA_VLAN_INGRESS_QOS])
 257                vlan_print_map(f,
 258                               "ingress_qos",
 259                               "ingress-qos-map",
 260                               tb[IFLA_VLAN_INGRESS_QOS]);
 261        if (tb[IFLA_VLAN_EGRESS_QOS])
 262                vlan_print_map(f,
 263                               "egress_qos",
 264                               "egress-qos-map",
 265                               tb[IFLA_VLAN_EGRESS_QOS]);
 266}
 267
 268static void vlan_print_help(struct link_util *lu, int argc, char **argv,
 269                            FILE *f)
 270{
 271        print_explain(f);
 272}
 273
 274struct link_util vlan_link_util = {
 275        .id             = "vlan",
 276        .maxattr        = IFLA_VLAN_MAX,
 277        .parse_opt      = vlan_parse_opt,
 278        .print_opt      = vlan_print_opt,
 279        .print_help     = vlan_print_help,
 280};
 281