1
2
3
4
5
6
7
8
9
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