Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Get mdb table with netlink |
| 3 | */ |
| 4 | |
| 5 | #include <stdio.h> |
| 6 | #include <stdlib.h> |
| 7 | #include <unistd.h> |
| 8 | #include <fcntl.h> |
| 9 | #include <sys/socket.h> |
| 10 | #include <net/if.h> |
| 11 | #include <netinet/in.h> |
| 12 | #include <linux/if_bridge.h> |
| 13 | #include <linux/if_ether.h> |
| 14 | #include <string.h> |
| 15 | #include <arpa/inet.h> |
| 16 | |
| 17 | #include "libnetlink.h" |
| 18 | #include "br_common.h" |
| 19 | #include "rt_names.h" |
| 20 | #include "utils.h" |
| 21 | |
| 22 | #ifndef MDBA_RTA |
| 23 | #define MDBA_RTA(r) \ |
Stephen Hemminger | df4b043 | 2016-03-21 11:56:01 -0700 | [diff] [blame] | 24 | ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 25 | #endif |
| 26 | |
Nikolay Aleksandrov | 24687d6 | 2016-04-11 17:45:15 +0200 | [diff] [blame] | 27 | static unsigned int filter_index, filter_vlan; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 28 | |
| 29 | static void usage(void) |
| 30 | { |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 31 | fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n"); |
Nikolay Aleksandrov | 24687d6 | 2016-04-11 17:45:15 +0200 | [diff] [blame] | 32 | fprintf(stderr, " bridge mdb {show} [ dev DEV ] [ vid VID ]\n"); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 33 | exit(-1); |
| 34 | } |
| 35 | |
Nikolay Aleksandrov | ba03726 | 2016-03-14 11:04:46 +0100 | [diff] [blame] | 36 | static bool is_temp_mcast_rtr(__u8 type) |
| 37 | { |
| 38 | return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP; |
| 39 | } |
| 40 | |
| 41 | static void __print_router_port_stats(FILE *f, struct rtattr *pattr) |
| 42 | { |
| 43 | struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1]; |
| 44 | struct timeval tv; |
| 45 | __u8 type; |
| 46 | |
| 47 | parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)), |
| 48 | RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t))); |
| 49 | if (tb[MDBA_ROUTER_PATTR_TIMER]) { |
| 50 | __jiffies_to_tv(&tv, |
| 51 | rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER])); |
| 52 | fprintf(f, " %4i.%.2i", |
| 53 | (int)tv.tv_sec, (int)tv.tv_usec/10000); |
| 54 | } |
| 55 | if (tb[MDBA_ROUTER_PATTR_TYPE]) { |
| 56 | type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); |
| 57 | fprintf(f, " %s", |
| 58 | is_temp_mcast_rtr(type) ? "temp" : "permanent"); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx) |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 63 | { |
| 64 | uint32_t *port_ifindex; |
| 65 | struct rtattr *i; |
| 66 | int rem; |
| 67 | |
Nikolay Aleksandrov | ba03726 | 2016-03-14 11:04:46 +0100 | [diff] [blame] | 68 | if (!show_stats) |
| 69 | fprintf(f, "router ports on %s: ", ll_index_to_name(brifidx)); |
| 70 | |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 71 | rem = RTA_PAYLOAD(attr); |
| 72 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { |
| 73 | port_ifindex = RTA_DATA(i); |
Nikolay Aleksandrov | ba03726 | 2016-03-14 11:04:46 +0100 | [diff] [blame] | 74 | if (show_stats) { |
| 75 | fprintf(f, "router ports on %s: %s", |
| 76 | ll_index_to_name(brifidx), |
| 77 | ll_index_to_name(*port_ifindex)); |
| 78 | __print_router_port_stats(f, i); |
| 79 | fprintf(f, "\n"); |
| 80 | } else { |
| 81 | fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); |
| 82 | } |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 83 | } |
Nikolay Aleksandrov | ba03726 | 2016-03-14 11:04:46 +0100 | [diff] [blame] | 84 | if (!show_stats) |
| 85 | fprintf(f, "\n"); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 86 | } |
| 87 | |
Nikolay Aleksandrov | 90d7315 | 2015-07-30 11:30:32 +0200 | [diff] [blame] | 88 | static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, |
Nikolay Aleksandrov | 05d4f64 | 2016-02-22 15:16:13 +0100 | [diff] [blame] | 89 | struct nlmsghdr *n, struct rtattr **tb) |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 90 | { |
| 91 | SPRINT_BUF(abuf); |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 92 | const void *src; |
| 93 | int af; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 94 | |
Nikolay Aleksandrov | 24687d6 | 2016-04-11 17:45:15 +0200 | [diff] [blame] | 95 | if (filter_vlan && e->vid != filter_vlan) |
| 96 | return; |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 97 | af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; |
| 98 | src = af == AF_INET ? (const void *)&e->addr.u.ip4 : |
| 99 | (const void *)&e->addr.u.ip6; |
Nikolay Aleksandrov | 90d7315 | 2015-07-30 11:30:32 +0200 | [diff] [blame] | 100 | if (n->nlmsg_type == RTM_DELMDB) |
| 101 | fprintf(f, "Deleted "); |
Elad Raz | 29d61fb | 2016-03-06 12:46:04 -0800 | [diff] [blame] | 102 | fprintf(f, "dev %s port %s grp %s %s %s", ll_index_to_name(ifindex), |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 103 | ll_index_to_name(e->ifindex), |
| 104 | inet_ntop(af, src, abuf, sizeof(abuf)), |
Elad Raz | 29d61fb | 2016-03-06 12:46:04 -0800 | [diff] [blame] | 105 | (e->state & MDB_PERMANENT) ? "permanent" : "temp", |
| 106 | (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 107 | if (e->vid) |
| 108 | fprintf(f, " vid %hu", e->vid); |
Nikolay Aleksandrov | 05d4f64 | 2016-02-22 15:16:13 +0100 | [diff] [blame] | 109 | if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { |
| 110 | struct timeval tv; |
| 111 | |
| 112 | __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER])); |
| 113 | fprintf(f, "%4i.%.2i", (int)tv.tv_sec, (int)tv.tv_usec/10000); |
| 114 | } |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 115 | fprintf(f, "\n"); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 116 | } |
| 117 | |
Nikolay Aleksandrov | 90d7315 | 2015-07-30 11:30:32 +0200 | [diff] [blame] | 118 | static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, |
| 119 | struct nlmsghdr *n) |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 120 | { |
Nikolay Aleksandrov | 05d4f64 | 2016-02-22 15:16:13 +0100 | [diff] [blame] | 121 | struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1]; |
| 122 | struct br_mdb_entry *e; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 123 | struct rtattr *i; |
| 124 | int rem; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 125 | |
| 126 | rem = RTA_PAYLOAD(attr); |
| 127 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { |
| 128 | e = RTA_DATA(i); |
Nikolay Aleksandrov | 05d4f64 | 2016-02-22 15:16:13 +0100 | [diff] [blame] | 129 | parse_rtattr(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)), |
| 130 | RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e))); |
| 131 | print_mdb_entry(f, ifindex, e, n, etb); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 132 | } |
| 133 | } |
| 134 | |
| 135 | int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| 136 | { |
| 137 | FILE *fp = arg; |
| 138 | struct br_port_msg *r = NLMSG_DATA(n); |
| 139 | int len = n->nlmsg_len; |
Nikolay Aleksandrov | 6b4867e | 2015-07-27 13:44:05 +0200 | [diff] [blame] | 140 | struct rtattr *tb[MDBA_MAX+1], *i; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 141 | |
Cong Wang | 4a4ee61 | 2012-12-11 22:23:10 +0000 | [diff] [blame] | 142 | if (n->nlmsg_type != RTM_GETMDB && n->nlmsg_type != RTM_NEWMDB && n->nlmsg_type != RTM_DELMDB) { |
| 143 | fprintf(stderr, "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n", |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 144 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); |
| 145 | |
| 146 | return 0; |
| 147 | } |
| 148 | |
| 149 | len -= NLMSG_LENGTH(sizeof(*r)); |
| 150 | if (len < 0) { |
| 151 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); |
| 152 | return -1; |
| 153 | } |
| 154 | |
| 155 | if (filter_index && filter_index != r->ifindex) |
| 156 | return 0; |
| 157 | |
| 158 | parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); |
| 159 | |
| 160 | if (tb[MDBA_MDB]) { |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 161 | int rem = RTA_PAYLOAD(tb[MDBA_MDB]); |
| 162 | |
| 163 | for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) |
Nikolay Aleksandrov | 90d7315 | 2015-07-30 11:30:32 +0200 | [diff] [blame] | 164 | br_print_mdb_entry(fp, r->ifindex, i, n); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | if (tb[MDBA_ROUTER]) { |
Nikolay Aleksandrov | 6b4867e | 2015-07-27 13:44:05 +0200 | [diff] [blame] | 168 | if (n->nlmsg_type == RTM_GETMDB) { |
Nikolay Aleksandrov | ba03726 | 2016-03-14 11:04:46 +0100 | [diff] [blame] | 169 | if (show_details) |
| 170 | br_print_router_ports(fp, tb[MDBA_ROUTER], |
| 171 | r->ifindex); |
Nikolay Aleksandrov | 6b4867e | 2015-07-27 13:44:05 +0200 | [diff] [blame] | 172 | } else { |
| 173 | uint32_t *port_ifindex; |
| 174 | |
| 175 | i = RTA_DATA(tb[MDBA_ROUTER]); |
| 176 | port_ifindex = RTA_DATA(i); |
| 177 | if (n->nlmsg_type == RTM_DELMDB) |
| 178 | fprintf(fp, "Deleted "); |
| 179 | fprintf(fp, "router port dev %s master %s\n", |
| 180 | ll_index_to_name(*port_ifindex), |
| 181 | ll_index_to_name(r->ifindex)); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 182 | } |
| 183 | } |
| 184 | |
Wilson Kok | 4d45bf3 | 2015-10-15 14:53:17 -0700 | [diff] [blame] | 185 | fflush(fp); |
| 186 | |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 187 | return 0; |
| 188 | } |
| 189 | |
| 190 | static int mdb_show(int argc, char **argv) |
| 191 | { |
| 192 | char *filter_dev = NULL; |
| 193 | |
| 194 | while (argc > 0) { |
| 195 | if (strcmp(*argv, "dev") == 0) { |
| 196 | NEXT_ARG(); |
| 197 | if (filter_dev) |
| 198 | duparg("dev", *argv); |
| 199 | filter_dev = *argv; |
Nikolay Aleksandrov | 24687d6 | 2016-04-11 17:45:15 +0200 | [diff] [blame] | 200 | } else if (strcmp(*argv, "vid") == 0) { |
| 201 | NEXT_ARG(); |
| 202 | if (filter_vlan) |
| 203 | duparg("vid", *argv); |
| 204 | filter_vlan = atoi(*argv); |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 205 | } |
| 206 | argc--; argv++; |
| 207 | } |
| 208 | |
| 209 | if (filter_dev) { |
| 210 | filter_index = if_nametoindex(filter_dev); |
| 211 | if (filter_index == 0) { |
| 212 | fprintf(stderr, "Cannot find device \"%s\"\n", |
| 213 | filter_dev); |
| 214 | return -1; |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { |
| 219 | perror("Cannot send dump request"); |
Roopa Prabhu | 42ecedd | 2015-03-17 19:26:32 -0700 | [diff] [blame] | 220 | return -1; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 221 | } |
| 222 | |
| 223 | if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { |
| 224 | fprintf(stderr, "Dump terminated\n"); |
Roopa Prabhu | 42ecedd | 2015-03-17 19:26:32 -0700 | [diff] [blame] | 225 | return -1; |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 226 | } |
| 227 | |
| 228 | return 0; |
| 229 | } |
| 230 | |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 231 | static int mdb_modify(int cmd, int flags, int argc, char **argv) |
| 232 | { |
| 233 | struct { |
Stephen Hemminger | df4b043 | 2016-03-21 11:56:01 -0700 | [diff] [blame] | 234 | struct nlmsghdr n; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 235 | struct br_port_msg bpm; |
Stephen Hemminger | df4b043 | 2016-03-21 11:56:01 -0700 | [diff] [blame] | 236 | char buf[1024]; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 237 | } req; |
| 238 | struct br_mdb_entry entry; |
| 239 | char *d = NULL, *p = NULL, *grp = NULL; |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 240 | short vid = 0; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 241 | |
| 242 | memset(&req, 0, sizeof(req)); |
| 243 | memset(&entry, 0, sizeof(entry)); |
| 244 | |
| 245 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)); |
| 246 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; |
| 247 | req.n.nlmsg_type = cmd; |
| 248 | req.bpm.family = PF_BRIDGE; |
| 249 | |
| 250 | while (argc > 0) { |
| 251 | if (strcmp(*argv, "dev") == 0) { |
| 252 | NEXT_ARG(); |
| 253 | d = *argv; |
| 254 | } else if (strcmp(*argv, "grp") == 0) { |
| 255 | NEXT_ARG(); |
| 256 | grp = *argv; |
Cong Wang | d8b75d1 | 2012-12-20 10:54:19 -0800 | [diff] [blame] | 257 | } else if (strcmp(*argv, "port") == 0) { |
| 258 | NEXT_ARG(); |
| 259 | p = *argv; |
| 260 | } else if (strcmp(*argv, "permanent") == 0) { |
| 261 | if (cmd == RTM_NEWMDB) |
| 262 | entry.state |= MDB_PERMANENT; |
| 263 | } else if (strcmp(*argv, "temp") == 0) { |
| 264 | ;/* nothing */ |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 265 | } else if (strcmp(*argv, "vid") == 0) { |
| 266 | NEXT_ARG(); |
| 267 | vid = atoi(*argv); |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 268 | } else { |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 269 | if (matches(*argv, "help") == 0) |
| 270 | usage(); |
| 271 | } |
| 272 | argc--; argv++; |
| 273 | } |
| 274 | |
| 275 | if (d == NULL || grp == NULL || p == NULL) { |
| 276 | fprintf(stderr, "Device, group address and port name are required arguments.\n"); |
Roopa Prabhu | 42ecedd | 2015-03-17 19:26:32 -0700 | [diff] [blame] | 277 | return -1; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 278 | } |
| 279 | |
| 280 | req.bpm.ifindex = ll_name_to_index(d); |
| 281 | if (req.bpm.ifindex == 0) { |
| 282 | fprintf(stderr, "Cannot find device \"%s\"\n", d); |
| 283 | return -1; |
| 284 | } |
| 285 | |
| 286 | entry.ifindex = ll_name_to_index(p); |
| 287 | if (entry.ifindex == 0) { |
| 288 | fprintf(stderr, "Cannot find device \"%s\"\n", p); |
| 289 | return -1; |
| 290 | } |
| 291 | |
| 292 | if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) { |
| 293 | if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) { |
| 294 | fprintf(stderr, "Invalid address \"%s\"\n", grp); |
| 295 | return -1; |
| 296 | } else |
| 297 | entry.addr.proto = htons(ETH_P_IPV6); |
| 298 | } else |
| 299 | entry.addr.proto = htons(ETH_P_IP); |
| 300 | |
Nikolay Aleksandrov | 6aac861 | 2015-07-15 08:45:20 -0700 | [diff] [blame] | 301 | entry.vid = vid; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 302 | addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry)); |
| 303 | |
Stephen Hemminger | c079e12 | 2015-05-27 12:26:14 -0700 | [diff] [blame] | 304 | if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) |
Roopa Prabhu | 42ecedd | 2015-03-17 19:26:32 -0700 | [diff] [blame] | 305 | return -1; |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 306 | |
| 307 | return 0; |
| 308 | } |
| 309 | |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 310 | int do_mdb(int argc, char **argv) |
| 311 | { |
| 312 | ll_init_map(&rth); |
| 313 | |
| 314 | if (argc > 0) { |
Cong Wang | 9dca676 | 2012-12-11 22:23:09 +0000 | [diff] [blame] | 315 | if (matches(*argv, "add") == 0) |
| 316 | return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); |
| 317 | if (matches(*argv, "delete") == 0) |
| 318 | return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1); |
| 319 | |
Cong Wang | e06c7f7 | 2012-12-11 16:46:22 -0800 | [diff] [blame] | 320 | if (matches(*argv, "show") == 0 || |
| 321 | matches(*argv, "lst") == 0 || |
| 322 | matches(*argv, "list") == 0) |
| 323 | return mdb_show(argc-1, argv+1); |
| 324 | if (matches(*argv, "help") == 0) |
| 325 | usage(); |
| 326 | } else |
| 327 | return mdb_show(0, NULL); |
| 328 | |
| 329 | fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv); |
| 330 | exit(-1); |
| 331 | } |