]> git.ipfire.org Git - thirdparty/iproute2.git/blob - tc/m_vlan.c
tc: make action_util arg const
[thirdparty/iproute2.git] / tc / m_vlan.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * m_vlan.c vlan manipulation module
4 *
5 * Authors: Jiri Pirko <jiri@resnulli.us>
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <linux/if_ether.h>
13 #include "utils.h"
14 #include "rt_names.h"
15 #include "tc_util.h"
16 #include <linux/tc_act/tc_vlan.h>
17
18 static const char * const action_names[] = {
19 [TCA_VLAN_ACT_POP] = "pop",
20 [TCA_VLAN_ACT_PUSH] = "push",
21 [TCA_VLAN_ACT_MODIFY] = "modify",
22 [TCA_VLAN_ACT_POP_ETH] = "pop_eth",
23 [TCA_VLAN_ACT_PUSH_ETH] = "push_eth",
24 };
25
26 static void explain(void)
27 {
28 fprintf(stderr,
29 "Usage: vlan pop [CONTROL]\n"
30 " vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"
31 " vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"
32 " vlan pop_eth [CONTROL]\n"
33 " vlan push_eth dst_mac LLADDR src_mac LLADDR [CONTROL]\n"
34 " VLANPROTO is one of 802.1Q or 802.1AD\n"
35 " with default: 802.1Q\n"
36 " CONTROL := reclassify | pipe | drop | continue | pass |\n"
37 " goto chain <CHAIN_INDEX>\n");
38 }
39
40 static void usage(void)
41 {
42 explain();
43 exit(-1);
44 }
45
46 static bool has_push_attribs(int action)
47 {
48 return action == TCA_VLAN_ACT_PUSH || action == TCA_VLAN_ACT_MODIFY;
49 }
50
51 static void unexpected(const char *arg)
52 {
53 fprintf(stderr,
54 "unexpected \"%s\" - action already specified\n",
55 arg);
56 explain();
57 }
58
59 static int parse_vlan(const struct action_util *a, int *argc_p, char ***argv_p,
60 int tca_id, struct nlmsghdr *n)
61 {
62 int argc = *argc_p;
63 char **argv = *argv_p;
64 struct rtattr *tail;
65 int action = 0;
66 char dst_mac[ETH_ALEN] = {};
67 int dst_mac_set = 0;
68 char src_mac[ETH_ALEN] = {};
69 int src_mac_set = 0;
70 __u16 id;
71 int id_set = 0;
72 __u16 proto;
73 int proto_set = 0;
74 __u8 prio;
75 int prio_set = 0;
76 struct tc_vlan parm = {};
77
78 if (matches(*argv, "vlan") != 0)
79 return -1;
80
81 NEXT_ARG();
82
83 while (argc > 0) {
84 if (matches(*argv, "pop") == 0) {
85 if (action) {
86 unexpected(*argv);
87 return -1;
88 }
89 action = TCA_VLAN_ACT_POP;
90 } else if (matches(*argv, "push") == 0) {
91 if (action) {
92 unexpected(*argv);
93 return -1;
94 }
95 action = TCA_VLAN_ACT_PUSH;
96 } else if (matches(*argv, "modify") == 0) {
97 if (action) {
98 unexpected(*argv);
99 return -1;
100 }
101 action = TCA_VLAN_ACT_MODIFY;
102 } else if (matches(*argv, "pop_eth") == 0) {
103 if (action) {
104 unexpected(*argv);
105 return -1;
106 }
107 action = TCA_VLAN_ACT_POP_ETH;
108 } else if (matches(*argv, "push_eth") == 0) {
109 if (action) {
110 unexpected(*argv);
111 return -1;
112 }
113 action = TCA_VLAN_ACT_PUSH_ETH;
114 } else if (matches(*argv, "id") == 0) {
115 if (!has_push_attribs(action))
116 invarg("only valid for push/modify", *argv);
117
118 NEXT_ARG();
119 if (get_u16(&id, *argv, 0))
120 invarg("id is invalid", *argv);
121 id_set = 1;
122 } else if (matches(*argv, "protocol") == 0) {
123 if (!has_push_attribs(action))
124 invarg("only valid for push/modify", *argv);
125
126 NEXT_ARG();
127 if (ll_proto_a2n(&proto, *argv))
128 invarg("protocol is invalid", *argv);
129 proto_set = 1;
130 } else if (matches(*argv, "priority") == 0) {
131 if (!has_push_attribs(action))
132 invarg("only valid for push/modify", *argv);
133
134 NEXT_ARG();
135 if (get_u8(&prio, *argv, 0) || (prio & ~0x7))
136 invarg("prio is invalid", *argv);
137 prio_set = 1;
138 } else if (matches(*argv, "dst_mac") == 0) {
139 if (action != TCA_VLAN_ACT_PUSH_ETH)
140 invarg("only valid for push_eth", *argv);
141
142 NEXT_ARG();
143 if (ll_addr_a2n(dst_mac, sizeof(dst_mac), *argv) < 0)
144 invarg("dst_mac is invalid", *argv);
145 dst_mac_set = 1;
146 } else if (matches(*argv, "src_mac") == 0) {
147 if (action != TCA_VLAN_ACT_PUSH_ETH)
148 invarg("only valid for push_eth", *argv);
149
150 NEXT_ARG();
151 if (ll_addr_a2n(src_mac, sizeof(src_mac), *argv) < 0)
152 invarg("src_mac is invalid", *argv);
153 src_mac_set = 1;
154 } else if (matches(*argv, "help") == 0) {
155 usage();
156 } else {
157 break;
158 }
159 argc--;
160 argv++;
161 }
162
163 parse_action_control_dflt(&argc, &argv, &parm.action,
164 false, TC_ACT_PIPE);
165
166 if (argc) {
167 if (matches(*argv, "index") == 0) {
168 NEXT_ARG();
169 if (get_u32(&parm.index, *argv, 10)) {
170 fprintf(stderr, "vlan: Illegal \"index\"\n");
171 return -1;
172 }
173 argc--;
174 argv++;
175 }
176 }
177
178 if (has_push_attribs(action) && !id_set) {
179 fprintf(stderr, "id needs to be set for %s\n",
180 action_names[action]);
181 explain();
182 return -1;
183 }
184
185 if (action == TCA_VLAN_ACT_PUSH_ETH) {
186 if (!dst_mac_set) {
187 fprintf(stderr, "dst_mac needs to be set for %s\n",
188 action_names[action]);
189 explain();
190 return -1;
191 } else if (!src_mac_set) {
192 fprintf(stderr, "src_mac needs to be set for %s\n",
193 action_names[action]);
194 explain();
195 return -1;
196 }
197 }
198
199 parm.v_action = action;
200 tail = addattr_nest(n, MAX_MSG, tca_id);
201 addattr_l(n, MAX_MSG, TCA_VLAN_PARMS, &parm, sizeof(parm));
202 if (id_set)
203 addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_ID, &id, 2);
204 if (proto_set) {
205 if (proto != htons(ETH_P_8021Q) &&
206 proto != htons(ETH_P_8021AD)) {
207 fprintf(stderr, "protocol not supported\n");
208 explain();
209 return -1;
210 }
211
212 addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PROTOCOL, &proto, 2);
213 }
214 if (prio_set)
215 addattr8(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
216 if (dst_mac_set)
217 addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_ETH_DST, dst_mac,
218 sizeof(dst_mac));
219 if (src_mac_set)
220 addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_ETH_SRC, src_mac,
221 sizeof(src_mac));
222
223 addattr_nest_end(n, tail);
224
225 *argc_p = argc;
226 *argv_p = argv;
227 return 0;
228 }
229
230 static int print_vlan(const struct action_util *au, FILE *f, struct rtattr *arg)
231 {
232 SPRINT_BUF(b1);
233 struct rtattr *tb[TCA_VLAN_MAX + 1];
234 __u16 val;
235 struct tc_vlan *parm;
236
237 print_string(PRINT_ANY, "kind", "%s ", "vlan");
238 if (arg == NULL)
239 return 0;
240
241 parse_rtattr_nested(tb, TCA_VLAN_MAX, arg);
242
243 if (!tb[TCA_VLAN_PARMS]) {
244 fprintf(stderr, "Missing vlan parameters\n");
245 return -1;
246 }
247 parm = RTA_DATA(tb[TCA_VLAN_PARMS]);
248
249 print_string(PRINT_ANY, "vlan_action", " %s",
250 action_names[parm->v_action]);
251
252 switch (parm->v_action) {
253 case TCA_VLAN_ACT_PUSH:
254 case TCA_VLAN_ACT_MODIFY:
255 if (tb[TCA_VLAN_PUSH_VLAN_ID]) {
256 val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
257 print_uint(PRINT_ANY, "id", " id %u", val);
258 }
259 if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
260 __u16 proto;
261
262 proto = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
263 print_string(PRINT_ANY, "protocol", " protocol %s",
264 ll_proto_n2a(proto, b1, sizeof(b1)));
265 }
266 if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) {
267 val = rta_getattr_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
268 print_uint(PRINT_ANY, "priority", " priority %u", val);
269 }
270 break;
271 case TCA_VLAN_ACT_PUSH_ETH:
272 if (tb[TCA_VLAN_PUSH_ETH_DST] &&
273 RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_DST]) == ETH_ALEN) {
274 ll_addr_n2a(RTA_DATA(tb[TCA_VLAN_PUSH_ETH_DST]),
275 ETH_ALEN, 0, b1, sizeof(b1));
276 print_string(PRINT_ANY, "dst_mac", " dst_mac %s", b1);
277 }
278 if (tb[TCA_VLAN_PUSH_ETH_SRC] &&
279 RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_SRC]) == ETH_ALEN) {
280 ll_addr_n2a(RTA_DATA(tb[TCA_VLAN_PUSH_ETH_SRC]),
281 ETH_ALEN, 0, b1, sizeof(b1));
282 print_string(PRINT_ANY, "src_mac", " src_mac %s", b1);
283 }
284 }
285 print_action_control(f, " ", parm->action, "");
286
287 print_nl();
288 print_uint(PRINT_ANY, "index", "\t index %u", parm->index);
289 print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
290 print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
291
292 if (show_stats) {
293 if (tb[TCA_VLAN_TM]) {
294 struct tcf_t *tm = RTA_DATA(tb[TCA_VLAN_TM]);
295
296 print_tm(f, tm);
297 }
298 }
299
300 print_nl();
301
302 return 0;
303 }
304
305 struct action_util vlan_action_util = {
306 .id = "vlan",
307 .parse_aopt = parse_vlan,
308 .print_aopt = print_vlan,
309 };