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) \ 24 ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) 25 #endif 26 27 static unsigned int filter_index; 28 29 static void usage(void) 30 { 31 fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n"); 32 fprintf(stderr, " bridge mdb {show} [ dev DEV ]\n"); 33 exit(-1); 34 } 35 36 static void br_print_router_ports(FILE *f, struct rtattr *attr) 37 { 38 uint32_t *port_ifindex; 39 struct rtattr *i; 40 int rem; 41 42 rem = RTA_PAYLOAD(attr); 43 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { 44 port_ifindex = RTA_DATA(i); 45 fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); 46 } 47 48 fprintf(f, "\n"); 49 } 50 51 static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, 52 struct nlmsghdr *n) 53 { 54 SPRINT_BUF(abuf); 55 const void *src; 56 int af; 57 58 af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; 59 src = af == AF_INET ? (const void *)&e->addr.u.ip4 : 60 (const void *)&e->addr.u.ip6; 61 if (n->nlmsg_type == RTM_DELMDB) 62 fprintf(f, "Deleted "); 63 fprintf(f, "dev %s port %s grp %s %s", ll_index_to_name(ifindex), 64 ll_index_to_name(e->ifindex), 65 inet_ntop(af, src, abuf, sizeof(abuf)), 66 (e->state & MDB_PERMANENT) ? "permanent" : "temp"); 67 if (e->vid) 68 fprintf(f, " vid %hu", e->vid); 69 fprintf(f, "\n"); 70 } 71 72 static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, 73 struct nlmsghdr *n) 74 { 75 struct rtattr *i; 76 int rem; 77 struct br_mdb_entry *e; 78 79 rem = RTA_PAYLOAD(attr); 80 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { 81 e = RTA_DATA(i); 82 print_mdb_entry(f, ifindex, e, n); 83 } 84 } 85 86 int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 87 { 88 FILE *fp = arg; 89 struct br_port_msg *r = NLMSG_DATA(n); 90 int len = n->nlmsg_len; 91 struct rtattr *tb[MDBA_MAX+1], *i; 92 93 if (n->nlmsg_type != RTM_GETMDB && n->nlmsg_type != RTM_NEWMDB && n->nlmsg_type != RTM_DELMDB) { 94 fprintf(stderr, "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n", 95 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); 96 97 return 0; 98 } 99 100 len -= NLMSG_LENGTH(sizeof(*r)); 101 if (len < 0) { 102 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); 103 return -1; 104 } 105 106 if (filter_index && filter_index != r->ifindex) 107 return 0; 108 109 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); 110 111 if (tb[MDBA_MDB]) { 112 int rem = RTA_PAYLOAD(tb[MDBA_MDB]); 113 114 for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) 115 br_print_mdb_entry(fp, r->ifindex, i, n); 116 } 117 118 if (tb[MDBA_ROUTER]) { 119 if (n->nlmsg_type == RTM_GETMDB) { 120 if (show_details) { 121 fprintf(fp, "router ports on %s: ", 122 ll_index_to_name(r->ifindex)); 123 br_print_router_ports(fp, tb[MDBA_ROUTER]); 124 } 125 } else { 126 uint32_t *port_ifindex; 127 128 i = RTA_DATA(tb[MDBA_ROUTER]); 129 port_ifindex = RTA_DATA(i); 130 if (n->nlmsg_type == RTM_DELMDB) 131 fprintf(fp, "Deleted "); 132 fprintf(fp, "router port dev %s master %s\n", 133 ll_index_to_name(*port_ifindex), 134 ll_index_to_name(r->ifindex)); 135 } 136 } 137 138 fflush(fp); 139 140 return 0; 141 } 142 143 static int mdb_show(int argc, char **argv) 144 { 145 char *filter_dev = NULL; 146 147 while (argc > 0) { 148 if (strcmp(*argv, "dev") == 0) { 149 NEXT_ARG(); 150 if (filter_dev) 151 duparg("dev", *argv); 152 filter_dev = *argv; 153 } 154 argc--; argv++; 155 } 156 157 if (filter_dev) { 158 filter_index = if_nametoindex(filter_dev); 159 if (filter_index == 0) { 160 fprintf(stderr, "Cannot find device \"%s\"\n", 161 filter_dev); 162 return -1; 163 } 164 } 165 166 if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { 167 perror("Cannot send dump request"); 168 return -1; 169 } 170 171 if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { 172 fprintf(stderr, "Dump terminated\n"); 173 return -1; 174 } 175 176 return 0; 177 } 178 179 static int mdb_modify(int cmd, int flags, int argc, char **argv) 180 { 181 struct { 182 struct nlmsghdr n; 183 struct br_port_msg bpm; 184 char buf[1024]; 185 } req; 186 struct br_mdb_entry entry; 187 char *d = NULL, *p = NULL, *grp = NULL; 188 short vid = 0; 189 190 memset(&req, 0, sizeof(req)); 191 memset(&entry, 0, sizeof(entry)); 192 193 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)); 194 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 195 req.n.nlmsg_type = cmd; 196 req.bpm.family = PF_BRIDGE; 197 198 while (argc > 0) { 199 if (strcmp(*argv, "dev") == 0) { 200 NEXT_ARG(); 201 d = *argv; 202 } else if (strcmp(*argv, "grp") == 0) { 203 NEXT_ARG(); 204 grp = *argv; 205 } else if (strcmp(*argv, "port") == 0) { 206 NEXT_ARG(); 207 p = *argv; 208 } else if (strcmp(*argv, "permanent") == 0) { 209 if (cmd == RTM_NEWMDB) 210 entry.state |= MDB_PERMANENT; 211 } else if (strcmp(*argv, "temp") == 0) { 212 ;/* nothing */ 213 } else if (strcmp(*argv, "vid") == 0) { 214 NEXT_ARG(); 215 vid = atoi(*argv); 216 } else { 217 if (matches(*argv, "help") == 0) 218 usage(); 219 } 220 argc--; argv++; 221 } 222 223 if (d == NULL || grp == NULL || p == NULL) { 224 fprintf(stderr, "Device, group address and port name are required arguments.\n"); 225 return -1; 226 } 227 228 req.bpm.ifindex = ll_name_to_index(d); 229 if (req.bpm.ifindex == 0) { 230 fprintf(stderr, "Cannot find device \"%s\"\n", d); 231 return -1; 232 } 233 234 entry.ifindex = ll_name_to_index(p); 235 if (entry.ifindex == 0) { 236 fprintf(stderr, "Cannot find device \"%s\"\n", p); 237 return -1; 238 } 239 240 if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) { 241 if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) { 242 fprintf(stderr, "Invalid address \"%s\"\n", grp); 243 return -1; 244 } else 245 entry.addr.proto = htons(ETH_P_IPV6); 246 } else 247 entry.addr.proto = htons(ETH_P_IP); 248 249 entry.vid = vid; 250 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry)); 251 252 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) 253 return -1; 254 255 return 0; 256 } 257 258 int do_mdb(int argc, char **argv) 259 { 260 ll_init_map(&rth); 261 262 if (argc > 0) { 263 if (matches(*argv, "add") == 0) 264 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); 265 if (matches(*argv, "delete") == 0) 266 return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1); 267 268 if (matches(*argv, "show") == 0 || 269 matches(*argv, "lst") == 0 || 270 matches(*argv, "list") == 0) 271 return mdb_show(argc-1, argv+1); 272 if (matches(*argv, "help") == 0) 273 usage(); 274 } else 275 return mdb_show(0, NULL); 276 277 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv); 278 exit(-1); 279 } 280