]>
Commit | Line | Data |
---|---|---|
6054c1eb | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
e06c7f7e CW |
2 | /* |
3 | * Get mdb table with netlink | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <fcntl.h> | |
10 | #include <sys/socket.h> | |
11 | #include <net/if.h> | |
12 | #include <netinet/in.h> | |
13 | #include <linux/if_bridge.h> | |
14 | #include <linux/if_ether.h> | |
15 | #include <string.h> | |
16 | #include <arpa/inet.h> | |
17 | ||
18 | #include "libnetlink.h" | |
19 | #include "br_common.h" | |
20 | #include "rt_names.h" | |
21 | #include "utils.h" | |
c7c1a1ef | 22 | #include "json_print.h" |
e06c7f7e CW |
23 | |
24 | #ifndef MDBA_RTA | |
25 | #define MDBA_RTA(r) \ | |
df4b043f | 26 | ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) |
e06c7f7e CW |
27 | #endif |
28 | ||
24687d67 | 29 | static unsigned int filter_index, filter_vlan; |
e06c7f7e CW |
30 | |
31 | static void usage(void) | |
32 | { | |
8589eb4e | 33 | fprintf(stderr, |
547b3197 | 34 | "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n" |
8589eb4e | 35 | " bridge mdb {show} [ dev DEV ] [ vid VID ]\n"); |
e06c7f7e CW |
36 | exit(-1); |
37 | } | |
38 | ||
ba037267 NA |
39 | static bool is_temp_mcast_rtr(__u8 type) |
40 | { | |
41 | return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP; | |
42 | } | |
43 | ||
2de81d1e | 44 | static const char *format_timer(__u32 ticks, int align) |
c7c1a1ef SH |
45 | { |
46 | struct timeval tv; | |
47 | static char tbuf[32]; | |
48 | ||
49 | __jiffies_to_tv(&tv, ticks); | |
2de81d1e NA |
50 | if (align) |
51 | snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu", | |
52 | (unsigned long)tv.tv_sec, | |
53 | (unsigned long)tv.tv_usec / 10000); | |
54 | else | |
55 | snprintf(tbuf, sizeof(tbuf), "%lu.%.2lu", | |
56 | (unsigned long)tv.tv_sec, | |
57 | (unsigned long)tv.tv_usec / 10000); | |
c7c1a1ef SH |
58 | |
59 | return tbuf; | |
60 | } | |
61 | ||
ba037267 NA |
62 | static void __print_router_port_stats(FILE *f, struct rtattr *pattr) |
63 | { | |
64 | struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1]; | |
ba037267 NA |
65 | |
66 | parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)), | |
67 | RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t))); | |
c7c1a1ef | 68 | |
ba037267 | 69 | if (tb[MDBA_ROUTER_PATTR_TIMER]) { |
c7c1a1ef SH |
70 | __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]); |
71 | ||
72 | print_string(PRINT_ANY, "timer", " %s", | |
2de81d1e | 73 | format_timer(timer, 1)); |
ba037267 | 74 | } |
c7c1a1ef | 75 | |
ba037267 | 76 | if (tb[MDBA_ROUTER_PATTR_TYPE]) { |
c7c1a1ef SH |
77 | __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); |
78 | ||
79 | print_string(PRINT_ANY, "type", " %s", | |
80 | is_temp_mcast_rtr(type) ? "temp" : "permanent"); | |
ba037267 NA |
81 | } |
82 | } | |
83 | ||
c7c1a1ef SH |
84 | static void br_print_router_ports(FILE *f, struct rtattr *attr, |
85 | const char *brifname) | |
e06c7f7e | 86 | { |
c7c1a1ef | 87 | int rem = RTA_PAYLOAD(attr); |
e06c7f7e | 88 | struct rtattr *i; |
e06c7f7e | 89 | |
c7c1a1ef SH |
90 | if (is_json_context()) |
91 | open_json_array(PRINT_JSON, brifname); | |
92 | else if (!show_stats) | |
93 | fprintf(f, "router ports on %s: ", brifname); | |
94 | ||
95 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
96 | uint32_t *port_ifindex = RTA_DATA(i); | |
97 | const char *port_ifname = ll_index_to_name(*port_ifindex); | |
98 | ||
99 | if (is_json_context()) { | |
100 | open_json_object(NULL); | |
101 | print_string(PRINT_JSON, "port", NULL, port_ifname); | |
102 | ||
44e0f6f3 NG |
103 | if (show_stats) |
104 | __print_router_port_stats(f, i); | |
c7c1a1ef SH |
105 | close_json_object(); |
106 | } else if (show_stats) { | |
107 | fprintf(f, "router ports on %s: %s", | |
108 | brifname, port_ifname); | |
109 | ||
110 | __print_router_port_stats(f, i); | |
44e0f6f3 | 111 | fprintf(f, "\n"); |
c7c1a1ef SH |
112 | } else { |
113 | fprintf(f, "%s ", port_ifname); | |
114 | } | |
e06c7f7e | 115 | } |
92bba4ed HL |
116 | |
117 | if (!show_stats) | |
118 | print_nl(); | |
119 | ||
c7c1a1ef | 120 | close_json_array(PRINT_JSON, NULL); |
44e0f6f3 NG |
121 | } |
122 | ||
2de81d1e NA |
123 | static void print_src_entry(struct rtattr *src_attr, int af, const char *sep) |
124 | { | |
125 | struct rtattr *stb[MDBA_MDB_SRCATTR_MAX + 1]; | |
126 | SPRINT_BUF(abuf); | |
127 | const char *addr; | |
128 | __u32 timer_val; | |
129 | ||
130 | parse_rtattr_nested(stb, MDBA_MDB_SRCATTR_MAX, src_attr); | |
131 | if (!stb[MDBA_MDB_SRCATTR_ADDRESS] || !stb[MDBA_MDB_SRCATTR_TIMER]) | |
132 | return; | |
133 | ||
134 | addr = inet_ntop(af, RTA_DATA(stb[MDBA_MDB_SRCATTR_ADDRESS]), abuf, | |
135 | sizeof(abuf)); | |
136 | if (!addr) | |
137 | return; | |
138 | timer_val = rta_getattr_u32(stb[MDBA_MDB_SRCATTR_TIMER]); | |
139 | ||
140 | open_json_object(NULL); | |
141 | print_string(PRINT_FP, NULL, "%s", sep); | |
142 | print_color_string(PRINT_ANY, ifa_family_color(af), | |
143 | "address", "%s", addr); | |
144 | print_string(PRINT_ANY, "timer", "/%s", format_timer(timer_val, 0)); | |
145 | close_json_object(); | |
146 | } | |
147 | ||
c7c1a1ef | 148 | static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e, |
05d4f64d | 149 | struct nlmsghdr *n, struct rtattr **tb) |
e06c7f7e | 150 | { |
547b3197 | 151 | const void *grp, *src; |
e06c7f7e | 152 | SPRINT_BUF(abuf); |
c7c1a1ef | 153 | const char *dev; |
6aac8617 NA |
154 | int af; |
155 | ||
24687d67 NA |
156 | if (filter_vlan && e->vid != filter_vlan) |
157 | return; | |
c7c1a1ef | 158 | |
6aac8617 | 159 | af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; |
547b3197 | 160 | grp = af == AF_INET ? (const void *)&e->addr.u.ip4 : |
6aac8617 | 161 | (const void *)&e->addr.u.ip6; |
c7c1a1ef SH |
162 | dev = ll_index_to_name(ifindex); |
163 | ||
164 | open_json_object(NULL); | |
165 | ||
a9661b8b NA |
166 | print_int(PRINT_JSON, "index", NULL, ifindex); |
167 | print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev); | |
168 | print_string(PRINT_ANY, "port", " port %s", | |
c7c1a1ef | 169 | ll_index_to_name(e->ifindex)); |
44e0f6f3 | 170 | |
c7c1a1ef | 171 | print_color_string(PRINT_ANY, ifa_family_color(af), |
a9661b8b | 172 | "grp", " grp %s", |
547b3197 NA |
173 | inet_ntop(af, grp, abuf, sizeof(abuf))); |
174 | if (tb && tb[MDBA_MDB_EATTR_SOURCE]) { | |
175 | src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]); | |
176 | print_color_string(PRINT_ANY, ifa_family_color(af), | |
177 | "src", " src %s", | |
178 | inet_ntop(af, src, abuf, sizeof(abuf))); | |
179 | } | |
a9661b8b | 180 | print_string(PRINT_ANY, "state", " %s", |
c7c1a1ef | 181 | (e->state & MDB_PERMANENT) ? "permanent" : "temp"); |
2de81d1e NA |
182 | if (show_details && tb) { |
183 | if (tb[MDBA_MDB_EATTR_GROUP_MODE]) { | |
184 | __u8 mode = rta_getattr_u8(tb[MDBA_MDB_EATTR_GROUP_MODE]); | |
c7c1a1ef | 185 | |
2de81d1e NA |
186 | print_string(PRINT_ANY, "filter_mode", " filter_mode %s", |
187 | mode == MCAST_INCLUDE ? "include" : | |
188 | "exclude"); | |
189 | } | |
190 | if (tb[MDBA_MDB_EATTR_SRC_LIST]) { | |
191 | struct rtattr *i, *attr = tb[MDBA_MDB_EATTR_SRC_LIST]; | |
192 | const char *sep = " "; | |
193 | int rem; | |
194 | ||
195 | open_json_array(PRINT_ANY, is_json_context() ? | |
196 | "source_list" : | |
197 | " source_list"); | |
198 | rem = RTA_PAYLOAD(attr); | |
199 | for (i = RTA_DATA(attr); RTA_OK(i, rem); | |
200 | i = RTA_NEXT(i, rem)) { | |
201 | print_src_entry(i, af, sep); | |
202 | sep = ","; | |
203 | } | |
204 | close_json_array(PRINT_JSON, NULL); | |
205 | } | |
86588450 NA |
206 | if (tb[MDBA_MDB_EATTR_RTPROT]) { |
207 | __u8 rtprot = rta_getattr_u8(tb[MDBA_MDB_EATTR_RTPROT]); | |
208 | SPRINT_BUF(rtb); | |
209 | ||
210 | print_string(PRINT_ANY, "protocol", " proto %s ", | |
211 | rtnl_rtprot_n2a(rtprot, rtb, sizeof(rtb))); | |
212 | } | |
1d28c480 NA |
213 | } |
214 | ||
c7c1a1ef SH |
215 | open_json_array(PRINT_JSON, "flags"); |
216 | if (e->flags & MDB_FLAGS_OFFLOAD) | |
a9661b8b | 217 | print_string(PRINT_ANY, NULL, " %s", "offload"); |
f94e8b07 NA |
218 | if (e->flags & MDB_FLAGS_FAST_LEAVE) |
219 | print_string(PRINT_ANY, NULL, " %s", "fast_leave"); | |
e331677e NA |
220 | if (e->flags & MDB_FLAGS_STAR_EXCL) |
221 | print_string(PRINT_ANY, NULL, " %s", "added_by_star_ex"); | |
222 | if (e->flags & MDB_FLAGS_BLOCKED) | |
223 | print_string(PRINT_ANY, NULL, " %s", "blocked"); | |
c7c1a1ef SH |
224 | close_json_array(PRINT_JSON, NULL); |
225 | ||
226 | if (e->vid) | |
227 | print_uint(PRINT_ANY, "vid", " vid %u", e->vid); | |
228 | ||
229 | if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { | |
230 | __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]); | |
231 | ||
232 | print_string(PRINT_ANY, "timer", " %s", | |
2de81d1e | 233 | format_timer(timer, 1)); |
05d4f64d | 234 | } |
92bba4ed HL |
235 | |
236 | print_nl(); | |
c7c1a1ef | 237 | close_json_object(); |
e06c7f7e CW |
238 | } |
239 | ||
90d73159 NA |
240 | static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, |
241 | struct nlmsghdr *n) | |
e06c7f7e | 242 | { |
05d4f64d NA |
243 | struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1]; |
244 | struct br_mdb_entry *e; | |
e06c7f7e CW |
245 | struct rtattr *i; |
246 | int rem; | |
e06c7f7e CW |
247 | |
248 | rem = RTA_PAYLOAD(attr); | |
249 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
250 | e = RTA_DATA(i); | |
2de81d1e NA |
251 | parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)), |
252 | RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)), | |
253 | NLA_F_NESTED); | |
05d4f64d | 254 | print_mdb_entry(f, ifindex, e, n, etb); |
e06c7f7e CW |
255 | } |
256 | } | |
257 | ||
c7c1a1ef SH |
258 | static void print_mdb_entries(FILE *fp, struct nlmsghdr *n, |
259 | int ifindex, struct rtattr *mdb) | |
260 | { | |
261 | int rem = RTA_PAYLOAD(mdb); | |
262 | struct rtattr *i; | |
263 | ||
c7c1a1ef SH |
264 | for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) |
265 | br_print_mdb_entry(fp, ifindex, i, n); | |
c7c1a1ef SH |
266 | } |
267 | ||
268 | static void print_router_entries(FILE *fp, struct nlmsghdr *n, | |
269 | int ifindex, struct rtattr *router) | |
270 | { | |
271 | const char *brifname = ll_index_to_name(ifindex); | |
272 | ||
c7c1a1ef SH |
273 | if (n->nlmsg_type == RTM_GETMDB) { |
274 | if (show_details) | |
275 | br_print_router_ports(fp, router, brifname); | |
276 | } else { | |
277 | struct rtattr *i = RTA_DATA(router); | |
278 | uint32_t *port_ifindex = RTA_DATA(i); | |
f5fc7387 | 279 | const char *port_name = ll_index_to_name(*port_ifindex); |
c7c1a1ef SH |
280 | |
281 | if (is_json_context()) { | |
282 | open_json_array(PRINT_JSON, brifname); | |
283 | open_json_object(NULL); | |
284 | ||
285 | print_string(PRINT_JSON, "port", NULL, | |
f5fc7387 | 286 | port_name); |
c7c1a1ef SH |
287 | close_json_object(); |
288 | close_json_array(PRINT_JSON, NULL); | |
289 | } else { | |
290 | fprintf(fp, "router port dev %s master %s\n", | |
f5fc7387 | 291 | port_name, brifname); |
c7c1a1ef SH |
292 | } |
293 | } | |
c7c1a1ef SH |
294 | } |
295 | ||
b11b495e | 296 | static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb) |
e06c7f7e | 297 | { |
e06c7f7e CW |
298 | struct br_port_msg *r = NLMSG_DATA(n); |
299 | int len = n->nlmsg_len; | |
e06c7f7e | 300 | |
c7c1a1ef SH |
301 | if (n->nlmsg_type != RTM_GETMDB && |
302 | n->nlmsg_type != RTM_NEWMDB && | |
303 | n->nlmsg_type != RTM_DELMDB) { | |
304 | fprintf(stderr, | |
305 | "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n", | |
e06c7f7e CW |
306 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | len -= NLMSG_LENGTH(sizeof(*r)); | |
312 | if (len < 0) { | |
313 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
314 | return -1; | |
315 | } | |
316 | ||
317 | if (filter_index && filter_index != r->ifindex) | |
318 | return 0; | |
319 | ||
320 | parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); | |
321 | ||
b11b495e NA |
322 | return 1; |
323 | } | |
324 | ||
325 | static int print_mdbs(struct nlmsghdr *n, void *arg) | |
326 | { | |
327 | struct br_port_msg *r = NLMSG_DATA(n); | |
328 | struct rtattr *tb[MDBA_MAX+1]; | |
329 | FILE *fp = arg; | |
330 | int ret; | |
331 | ||
332 | ret = __parse_mdb_nlmsg(n, tb); | |
333 | if (ret != 1) | |
334 | return ret; | |
335 | ||
336 | if (tb[MDBA_MDB]) | |
337 | print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]); | |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
342 | static int print_rtrs(struct nlmsghdr *n, void *arg) | |
343 | { | |
344 | struct br_port_msg *r = NLMSG_DATA(n); | |
345 | struct rtattr *tb[MDBA_MAX+1]; | |
346 | FILE *fp = arg; | |
347 | int ret; | |
348 | ||
349 | ret = __parse_mdb_nlmsg(n, tb); | |
350 | if (ret != 1) | |
351 | return ret; | |
352 | ||
353 | if (tb[MDBA_ROUTER]) | |
354 | print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]); | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | int print_mdb_mon(struct nlmsghdr *n, void *arg) | |
360 | { | |
361 | struct br_port_msg *r = NLMSG_DATA(n); | |
362 | struct rtattr *tb[MDBA_MAX+1]; | |
363 | FILE *fp = arg; | |
364 | int ret; | |
365 | ||
366 | ret = __parse_mdb_nlmsg(n, tb); | |
367 | if (ret != 1) | |
368 | return ret; | |
369 | ||
c7c1a1ef SH |
370 | if (n->nlmsg_type == RTM_DELMDB) |
371 | print_bool(PRINT_ANY, "deleted", "Deleted ", true); | |
e06c7f7e | 372 | |
c7c1a1ef SH |
373 | if (tb[MDBA_MDB]) |
374 | print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]); | |
e06c7f7e | 375 | |
c7c1a1ef SH |
376 | if (tb[MDBA_ROUTER]) |
377 | print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]); | |
4d45bf3b | 378 | |
e06c7f7e CW |
379 | return 0; |
380 | } | |
381 | ||
382 | static int mdb_show(int argc, char **argv) | |
383 | { | |
384 | char *filter_dev = NULL; | |
385 | ||
386 | while (argc > 0) { | |
387 | if (strcmp(*argv, "dev") == 0) { | |
388 | NEXT_ARG(); | |
389 | if (filter_dev) | |
390 | duparg("dev", *argv); | |
391 | filter_dev = *argv; | |
24687d67 NA |
392 | } else if (strcmp(*argv, "vid") == 0) { |
393 | NEXT_ARG(); | |
394 | if (filter_vlan) | |
395 | duparg("vid", *argv); | |
396 | filter_vlan = atoi(*argv); | |
e06c7f7e CW |
397 | } |
398 | argc--; argv++; | |
399 | } | |
400 | ||
401 | if (filter_dev) { | |
7a14358b | 402 | filter_index = ll_name_to_index(filter_dev); |
fe99adbc SP |
403 | if (!filter_index) |
404 | return nodev(filter_dev); | |
e06c7f7e CW |
405 | } |
406 | ||
c7c1a1ef | 407 | new_json_obj(json); |
b11b495e | 408 | open_json_object(NULL); |
c7c1a1ef | 409 | |
b11b495e | 410 | /* get mdb entries */ |
9dbe6df4 | 411 | if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) { |
44e0f6f3 NG |
412 | perror("Cannot send dump request"); |
413 | return -1; | |
414 | } | |
415 | ||
b11b495e NA |
416 | open_json_array(PRINT_JSON, "mdb"); |
417 | if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) { | |
44e0f6f3 NG |
418 | fprintf(stderr, "Dump terminated\n"); |
419 | return -1; | |
420 | } | |
b11b495e NA |
421 | close_json_array(PRINT_JSON, NULL); |
422 | ||
423 | /* get router ports */ | |
424 | if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) { | |
425 | perror("Cannot send dump request"); | |
426 | return -1; | |
427 | } | |
e06c7f7e | 428 | |
b11b495e NA |
429 | open_json_object("router"); |
430 | if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) { | |
431 | fprintf(stderr, "Dump terminated\n"); | |
432 | return -1; | |
433 | } | |
434 | close_json_object(); | |
435 | ||
436 | close_json_object(); | |
c7c1a1ef SH |
437 | delete_json_obj(); |
438 | fflush(stdout); | |
e06c7f7e CW |
439 | |
440 | return 0; | |
441 | } | |
442 | ||
9dca6767 CW |
443 | static int mdb_modify(int cmd, int flags, int argc, char **argv) |
444 | { | |
445 | struct { | |
df4b043f | 446 | struct nlmsghdr n; |
9dca6767 | 447 | struct br_port_msg bpm; |
df4b043f | 448 | char buf[1024]; |
d17b136f PS |
449 | } req = { |
450 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)), | |
451 | .n.nlmsg_flags = NLM_F_REQUEST | flags, | |
452 | .n.nlmsg_type = cmd, | |
453 | .bpm.family = PF_BRIDGE, | |
454 | }; | |
547b3197 | 455 | char *d = NULL, *p = NULL, *grp = NULL, *src = NULL; |
d17b136f | 456 | struct br_mdb_entry entry = {}; |
6aac8617 | 457 | short vid = 0; |
9dca6767 | 458 | |
9dca6767 CW |
459 | while (argc > 0) { |
460 | if (strcmp(*argv, "dev") == 0) { | |
461 | NEXT_ARG(); | |
462 | d = *argv; | |
463 | } else if (strcmp(*argv, "grp") == 0) { | |
464 | NEXT_ARG(); | |
465 | grp = *argv; | |
d8b75d1a CW |
466 | } else if (strcmp(*argv, "port") == 0) { |
467 | NEXT_ARG(); | |
468 | p = *argv; | |
469 | } else if (strcmp(*argv, "permanent") == 0) { | |
470 | if (cmd == RTM_NEWMDB) | |
471 | entry.state |= MDB_PERMANENT; | |
472 | } else if (strcmp(*argv, "temp") == 0) { | |
473 | ;/* nothing */ | |
6aac8617 NA |
474 | } else if (strcmp(*argv, "vid") == 0) { |
475 | NEXT_ARG(); | |
476 | vid = atoi(*argv); | |
547b3197 NA |
477 | } else if (strcmp(*argv, "src") == 0) { |
478 | NEXT_ARG(); | |
479 | src = *argv; | |
9dca6767 | 480 | } else { |
9dca6767 CW |
481 | if (matches(*argv, "help") == 0) |
482 | usage(); | |
483 | } | |
484 | argc--; argv++; | |
485 | } | |
486 | ||
487 | if (d == NULL || grp == NULL || p == NULL) { | |
488 | fprintf(stderr, "Device, group address and port name are required arguments.\n"); | |
42ecedd4 | 489 | return -1; |
9dca6767 CW |
490 | } |
491 | ||
492 | req.bpm.ifindex = ll_name_to_index(d); | |
fe99adbc SP |
493 | if (!req.bpm.ifindex) |
494 | return nodev(d); | |
9dca6767 CW |
495 | |
496 | entry.ifindex = ll_name_to_index(p); | |
fe99adbc SP |
497 | if (!entry.ifindex) |
498 | return nodev(p); | |
9dca6767 CW |
499 | |
500 | if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) { | |
501 | if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) { | |
502 | fprintf(stderr, "Invalid address \"%s\"\n", grp); | |
503 | return -1; | |
504 | } else | |
505 | entry.addr.proto = htons(ETH_P_IPV6); | |
506 | } else | |
507 | entry.addr.proto = htons(ETH_P_IP); | |
508 | ||
6aac8617 | 509 | entry.vid = vid; |
9dca6767 | 510 | addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry)); |
547b3197 NA |
511 | if (src) { |
512 | struct rtattr *nest = addattr_nest(&req.n, sizeof(req), | |
513 | MDBA_SET_ENTRY_ATTRS); | |
514 | struct in6_addr src_ip6; | |
515 | __be32 src_ip4; | |
516 | ||
517 | nest->rta_type |= NLA_F_NESTED; | |
518 | if (!inet_pton(AF_INET, src, &src_ip4)) { | |
519 | if (!inet_pton(AF_INET6, src, &src_ip6)) { | |
520 | fprintf(stderr, "Invalid source address \"%s\"\n", src); | |
521 | return -1; | |
522 | } | |
523 | addattr_l(&req.n, sizeof(req), MDBE_ATTR_SOURCE, &src_ip6, sizeof(src_ip6)); | |
524 | } else { | |
525 | addattr32(&req.n, sizeof(req), MDBE_ATTR_SOURCE, src_ip4); | |
526 | } | |
527 | addattr_nest_end(&req.n, nest); | |
528 | } | |
9dca6767 | 529 | |
86bf43c7 | 530 | if (rtnl_talk(&rth, &req.n, NULL) < 0) |
42ecedd4 | 531 | return -1; |
9dca6767 CW |
532 | |
533 | return 0; | |
534 | } | |
535 | ||
e06c7f7e CW |
536 | int do_mdb(int argc, char **argv) |
537 | { | |
538 | ll_init_map(&rth); | |
539 | ||
540 | if (argc > 0) { | |
9dca6767 CW |
541 | if (matches(*argv, "add") == 0) |
542 | return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); | |
543 | if (matches(*argv, "delete") == 0) | |
544 | return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1); | |
545 | ||
e06c7f7e CW |
546 | if (matches(*argv, "show") == 0 || |
547 | matches(*argv, "lst") == 0 || | |
548 | matches(*argv, "list") == 0) | |
549 | return mdb_show(argc-1, argv+1); | |
550 | if (matches(*argv, "help") == 0) | |
551 | usage(); | |
552 | } else | |
553 | return mdb_show(0, NULL); | |
554 | ||
555 | fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv); | |
556 | exit(-1); | |
557 | } |