]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/iplink_bridge_slave.c
iplink: bridge: support bridge port vlan_tunnel attribute
[mirror_iproute2.git] / ip / iplink_bridge_slave.c
1 /*
2 * iplink_bridge_slave.c Bridge slave 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: Jiri Pirko <jiri@resnulli.us>
10 */
11
12 #include <stdio.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <linux/if_link.h>
16 #include <linux/if_bridge.h>
17
18 #include "rt_names.h"
19 #include "utils.h"
20 #include "ip_common.h"
21
22 static void print_explain(FILE *f)
23 {
24 fprintf(f,
25 "Usage: ... bridge_slave [ fdb_flush ]\n"
26 " [ state STATE ]\n"
27 " [ priority PRIO ]\n"
28 " [ cost COST ]\n"
29 " [ guard {on | off} ]\n"
30 " [ hairpin {on | off} ]\n"
31 " [ fastleave {on | off} ]\n"
32 " [ root_block {on | off} ]\n"
33 " [ learning {on | off} ]\n"
34 " [ flood {on | off} ]\n"
35 " [ proxy_arp {on | off} ]\n"
36 " [ proxy_arp_wifi {on | off} ]\n"
37 " [ mcast_router MULTICAST_ROUTER ]\n"
38 " [ mcast_fast_leave {on | off} ]\n"
39 " [ mcast_flood {on | off} ]\n"
40 " [ group_fwd_mask MASK ]\n"
41 " [ neigh_suppress {on | off} ]\n"
42 " [ vlan_tunnel {on | off} ]\n"
43 );
44 }
45
46 static void explain(void)
47 {
48 print_explain(stderr);
49 }
50
51 static const char *port_states[] = {
52 [BR_STATE_DISABLED] = "disabled",
53 [BR_STATE_LISTENING] = "listening",
54 [BR_STATE_LEARNING] = "learning",
55 [BR_STATE_FORWARDING] = "forwarding",
56 [BR_STATE_BLOCKING] = "blocking",
57 };
58
59 static const char *fwd_mask_tbl[16] = {
60 [0] = "stp",
61 [2] = "lacp",
62 [14] = "lldp"
63 };
64
65 static void print_portstate(FILE *f, __u8 state)
66 {
67 if (state <= BR_STATE_BLOCKING)
68 print_string(PRINT_ANY,
69 "state",
70 "state %s ",
71 port_states[state]);
72 else
73 print_int(PRINT_ANY, "state_index", "state (%d) ", state);
74 }
75
76 static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val)
77 {
78 if (is_json_context())
79 print_bool(PRINT_JSON, flag, NULL, val);
80 else
81 fprintf(f, "%s %s ", flag, val ? "on" : "off");
82 }
83
84 static void _print_hex(FILE *f,
85 const char *json_attr,
86 const char *attr,
87 __u16 val)
88 {
89 if (is_json_context()) {
90 SPRINT_BUF(b1);
91
92 snprintf(b1, sizeof(b1), "0x%x", val);
93 print_string(PRINT_JSON, json_attr, NULL, b1);
94 } else {
95 fprintf(f, "%s 0x%x ", attr, val);
96 }
97 }
98
99 static void _print_timer(FILE *f, const char *attr, struct rtattr *timer)
100 {
101 struct timeval tv;
102
103 __jiffies_to_tv(&tv, rta_getattr_u64(timer));
104 if (is_json_context()) {
105 json_writer_t *jw = get_json_writer();
106
107 jsonw_name(jw, attr);
108 jsonw_printf(jw, "%i.%.2i",
109 (int)tv.tv_sec, (int)tv.tv_usec / 10000);
110 } else {
111 fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec,
112 (int)tv.tv_usec / 10000);
113 }
114 }
115
116 static void _bitmask2str(__u16 bitmask, char *dst, size_t dst_size,
117 const char **tbl)
118 {
119 int len, i;
120
121 for (i = 0, len = 0; bitmask; i++, bitmask >>= 1) {
122 if (bitmask & 0x1) {
123 if (tbl[i])
124 len += snprintf(dst + len, dst_size - len, "%s,",
125 tbl[i]);
126 else
127 len += snprintf(dst + len, dst_size - len, "0x%x,",
128 (1 << i));
129 }
130 }
131
132 if (!len)
133 snprintf(dst, dst_size, "0x0");
134 else
135 dst[len - 1] = 0;
136 }
137
138 static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
139 struct rtattr *tb[])
140 {
141 if (!tb)
142 return;
143
144 if (tb[IFLA_BRPORT_STATE])
145 print_portstate(f, rta_getattr_u8(tb[IFLA_BRPORT_STATE]));
146
147 if (tb[IFLA_BRPORT_PRIORITY])
148 print_int(PRINT_ANY,
149 "priority",
150 "priority %d ",
151 rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY]));
152
153 if (tb[IFLA_BRPORT_COST])
154 print_int(PRINT_ANY,
155 "cost",
156 "cost %d ",
157 rta_getattr_u32(tb[IFLA_BRPORT_COST]));
158
159 if (tb[IFLA_BRPORT_MODE])
160 _print_onoff(f, "mode", "hairpin",
161 rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
162
163 if (tb[IFLA_BRPORT_GUARD])
164 _print_onoff(f, "guard", "guard",
165 rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
166
167 if (tb[IFLA_BRPORT_PROTECT])
168 _print_onoff(f, "protect", "root_block",
169 rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
170
171 if (tb[IFLA_BRPORT_FAST_LEAVE])
172 _print_onoff(f, "fast_leave", "fastleave",
173 rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
174
175 if (tb[IFLA_BRPORT_LEARNING])
176 _print_onoff(f, "learning", "learning",
177 rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
178
179 if (tb[IFLA_BRPORT_UNICAST_FLOOD])
180 _print_onoff(f, "unicast_flood", "flood",
181 rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
182
183 if (tb[IFLA_BRPORT_ID])
184 _print_hex(f, "id", "port_id",
185 rta_getattr_u16(tb[IFLA_BRPORT_ID]));
186
187 if (tb[IFLA_BRPORT_NO])
188 _print_hex(f, "no", "port_no",
189 rta_getattr_u16(tb[IFLA_BRPORT_NO]));
190
191 if (tb[IFLA_BRPORT_DESIGNATED_PORT])
192 print_uint(PRINT_ANY,
193 "designated_port",
194 "designated_port %u ",
195 rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT]));
196
197 if (tb[IFLA_BRPORT_DESIGNATED_COST])
198 print_uint(PRINT_ANY,
199 "designated_cost",
200 "designated_cost %u ",
201 rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST]));
202
203 if (tb[IFLA_BRPORT_BRIDGE_ID]) {
204 char bridge_id[32];
205
206 br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]),
207 bridge_id, sizeof(bridge_id));
208 print_string(PRINT_ANY,
209 "bridge_id",
210 "designated_bridge %s ",
211 bridge_id);
212 }
213
214 if (tb[IFLA_BRPORT_ROOT_ID]) {
215 char root_id[32];
216
217 br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]),
218 root_id, sizeof(root_id));
219 print_string(PRINT_ANY,
220 "root_id",
221 "designated_root %s ", root_id);
222 }
223
224 if (tb[IFLA_BRPORT_HOLD_TIMER])
225 _print_timer(f, "hold_timer", tb[IFLA_BRPORT_HOLD_TIMER]);
226
227 if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER])
228 _print_timer(f, "message_age_timer",
229 tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]);
230
231 if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER])
232 _print_timer(f, "forward_delay_timer",
233 tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]);
234
235 if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])
236 print_uint(PRINT_ANY,
237 "topology_change_ack",
238 "topology_change_ack %u ",
239 rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]));
240
241 if (tb[IFLA_BRPORT_CONFIG_PENDING])
242 print_uint(PRINT_ANY,
243 "config_pending",
244 "config_pending %u ",
245 rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
246
247 if (tb[IFLA_BRPORT_PROXYARP])
248 _print_onoff(f, "proxyarp", "proxy_arp",
249 rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
250
251 if (tb[IFLA_BRPORT_PROXYARP_WIFI])
252 _print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi",
253 rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
254
255 if (tb[IFLA_BRPORT_MULTICAST_ROUTER])
256 print_uint(PRINT_ANY,
257 "multicast_router",
258 "mcast_router %u ",
259 rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]));
260
261 if (tb[IFLA_BRPORT_FAST_LEAVE])
262 // not printing any json here because
263 // we already printed fast_leave before
264 print_string(PRINT_FP,
265 NULL,
266 "mcast_fast_leave %s ",
267 rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off");
268
269 if (tb[IFLA_BRPORT_MCAST_FLOOD])
270 _print_onoff(f, "mcast_flood", "mcast_flood",
271 rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
272
273 if (tb[IFLA_BRPORT_NEIGH_SUPPRESS])
274 _print_onoff(f, "neigh_suppress", "neigh_suppress",
275 rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS]));
276
277 if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
278 char convbuf[256];
279 __u16 fwd_mask;
280
281 fwd_mask = rta_getattr_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]);
282 _print_hex(f, "group_fwd_mask", "group_fwd_mask", fwd_mask);
283 _bitmask2str(fwd_mask, convbuf, sizeof(convbuf), fwd_mask_tbl);
284 print_string(PRINT_ANY, "group_fwd_mask_str",
285 "group_fwd_mask_str %s ", convbuf);
286 }
287
288 if (tb[IFLA_BRPORT_VLAN_TUNNEL])
289 _print_onoff(f, "vlan_tunnel", "vlan_tunnel",
290 rta_getattr_u8(tb[IFLA_BRPORT_VLAN_TUNNEL]));
291 }
292
293 static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
294 struct nlmsghdr *n, int type)
295 {
296 __u8 val;
297
298 if (strcmp(arg_val, "on") == 0)
299 val = 1;
300 else if (strcmp(arg_val, "off") == 0)
301 val = 0;
302 else
303 invarg("should be \"on\" or \"off\"", arg_name);
304
305 addattr8(n, 1024, type, val);
306 }
307
308 static int bridge_slave_parse_opt(struct link_util *lu, int argc, char **argv,
309 struct nlmsghdr *n)
310 {
311 __u8 state;
312 __u16 priority;
313 __u32 cost;
314
315 while (argc > 0) {
316 if (matches(*argv, "fdb_flush") == 0) {
317 addattr(n, 1024, IFLA_BRPORT_FLUSH);
318 } else if (matches(*argv, "state") == 0) {
319 NEXT_ARG();
320 if (get_u8(&state, *argv, 0))
321 invarg("state is invalid", *argv);
322 addattr8(n, 1024, IFLA_BRPORT_STATE, state);
323 } else if (matches(*argv, "priority") == 0) {
324 NEXT_ARG();
325 if (get_u16(&priority, *argv, 0))
326 invarg("priority is invalid", *argv);
327 addattr16(n, 1024, IFLA_BRPORT_PRIORITY, priority);
328 } else if (matches(*argv, "cost") == 0) {
329 NEXT_ARG();
330 if (get_u32(&cost, *argv, 0))
331 invarg("cost is invalid", *argv);
332 addattr32(n, 1024, IFLA_BRPORT_COST, cost);
333 } else if (matches(*argv, "hairpin") == 0) {
334 NEXT_ARG();
335 bridge_slave_parse_on_off("hairpin", *argv, n,
336 IFLA_BRPORT_MODE);
337 } else if (matches(*argv, "guard") == 0) {
338 NEXT_ARG();
339 bridge_slave_parse_on_off("guard", *argv, n,
340 IFLA_BRPORT_GUARD);
341 } else if (matches(*argv, "root_block") == 0) {
342 NEXT_ARG();
343 bridge_slave_parse_on_off("root_block", *argv, n,
344 IFLA_BRPORT_PROTECT);
345 } else if (matches(*argv, "fastleave") == 0) {
346 NEXT_ARG();
347 bridge_slave_parse_on_off("fastleave", *argv, n,
348 IFLA_BRPORT_FAST_LEAVE);
349 } else if (matches(*argv, "learning") == 0) {
350 NEXT_ARG();
351 bridge_slave_parse_on_off("learning", *argv, n,
352 IFLA_BRPORT_LEARNING);
353 } else if (matches(*argv, "flood") == 0) {
354 NEXT_ARG();
355 bridge_slave_parse_on_off("flood", *argv, n,
356 IFLA_BRPORT_UNICAST_FLOOD);
357 } else if (matches(*argv, "mcast_flood") == 0) {
358 NEXT_ARG();
359 bridge_slave_parse_on_off("mcast_flood", *argv, n,
360 IFLA_BRPORT_MCAST_FLOOD);
361 } else if (matches(*argv, "proxy_arp") == 0) {
362 NEXT_ARG();
363 bridge_slave_parse_on_off("proxy_arp", *argv, n,
364 IFLA_BRPORT_PROXYARP);
365 } else if (matches(*argv, "proxy_arp_wifi") == 0) {
366 NEXT_ARG();
367 bridge_slave_parse_on_off("proxy_arp_wifi", *argv, n,
368 IFLA_BRPORT_PROXYARP_WIFI);
369 } else if (matches(*argv, "mcast_router") == 0) {
370 __u8 mcast_router;
371
372 NEXT_ARG();
373 if (get_u8(&mcast_router, *argv, 0))
374 invarg("invalid mcast_router", *argv);
375 addattr8(n, 1024, IFLA_BRPORT_MULTICAST_ROUTER,
376 mcast_router);
377 } else if (matches(*argv, "mcast_fast_leave") == 0) {
378 NEXT_ARG();
379 bridge_slave_parse_on_off("mcast_fast_leave", *argv, n,
380 IFLA_BRPORT_FAST_LEAVE);
381 } else if (matches(*argv, "neigh_suppress") == 0) {
382 NEXT_ARG();
383 bridge_slave_parse_on_off("neigh_suppress", *argv, n,
384 IFLA_BRPORT_NEIGH_SUPPRESS);
385 } else if (matches(*argv, "group_fwd_mask") == 0) {
386 __u16 mask;
387
388 NEXT_ARG();
389 if (get_u16(&mask, *argv, 0))
390 invarg("invalid group_fwd_mask", *argv);
391 addattr16(n, 1024, IFLA_BRPORT_GROUP_FWD_MASK, mask);
392 } else if (matches(*argv, "vlan_tunnel") == 0) {
393 NEXT_ARG();
394 bridge_slave_parse_on_off("vlan_tunnel", *argv, n,
395 IFLA_BRPORT_VLAN_TUNNEL);
396 } else if (matches(*argv, "help") == 0) {
397 explain();
398 return -1;
399 } else {
400 fprintf(stderr, "bridge_slave: unknown option \"%s\"?\n",
401 *argv);
402 explain();
403 return -1;
404 }
405 argc--, argv++;
406 }
407
408 return 0;
409 }
410
411 static void bridge_slave_print_help(struct link_util *lu, int argc, char **argv,
412 FILE *f)
413 {
414 print_explain(f);
415 }
416
417 struct link_util bridge_slave_link_util = {
418 .id = "bridge_slave",
419 .maxattr = IFLA_BRPORT_MAX,
420 .print_opt = bridge_slave_print_opt,
421 .parse_opt = bridge_slave_parse_opt,
422 .print_help = bridge_slave_print_help,
423 .parse_ifla_xstats = bridge_parse_xstats,
424 .print_ifla_xstats = bridge_print_xstats,
425 };