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