1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 #include <sys/socket.h> 6 #include <net/if.h> 7 #include <netinet/in.h> 8 #include <linux/if_bridge.h> 9 #include <linux/if_ether.h> 10 #include <string.h> 11 12 #include "libnetlink.h" 13 #include "br_common.h" 14 #include "utils.h" 15 16 static unsigned int filter_index; 17 18 static void usage(void) 19 { 20 fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid] [ untagged ]\n"); 21 fprintf(stderr, " [ self ] [ master ]\n"); 22 fprintf(stderr, " bridge vlan { show } [ dev DEV ]\n"); 23 exit(-1); 24 } 25 26 static int vlan_modify(int cmd, int argc, char **argv) 27 { 28 struct { 29 struct nlmsghdr n; 30 struct ifinfomsg ifm; 31 char buf[1024]; 32 } req; 33 char *d = NULL; 34 short vid = -1; 35 short vid_end = -1; 36 struct rtattr *afspec; 37 struct bridge_vlan_info vinfo; 38 unsigned short flags = 0; 39 40 memset(&vinfo, 0, sizeof(vinfo)); 41 memset(&req, 0, sizeof(req)); 42 43 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 44 req.n.nlmsg_flags = NLM_F_REQUEST; 45 req.n.nlmsg_type = cmd; 46 req.ifm.ifi_family = PF_BRIDGE; 47 48 while (argc > 0) { 49 if (strcmp(*argv, "dev") == 0) { 50 NEXT_ARG(); 51 d = *argv; 52 } else if (strcmp(*argv, "vid") == 0) { 53 char *p; 54 NEXT_ARG(); 55 p = strchr(*argv, '-'); 56 if (p) { 57 *p = '\0'; 58 p++; 59 vid = atoi(*argv); 60 vid_end = atoi(p); 61 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; 62 } else { 63 vid = atoi(*argv); 64 } 65 } else if (strcmp(*argv, "self") == 0) { 66 flags |= BRIDGE_FLAGS_SELF; 67 } else if (strcmp(*argv, "master") == 0) { 68 flags |= BRIDGE_FLAGS_MASTER; 69 } else if (strcmp(*argv, "pvid") == 0) { 70 vinfo.flags |= BRIDGE_VLAN_INFO_PVID; 71 } else if (strcmp(*argv, "untagged") == 0) { 72 vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; 73 } else { 74 if (matches(*argv, "help") == 0) { 75 NEXT_ARG(); 76 } 77 } 78 argc--; argv++; 79 } 80 81 if (d == NULL || vid == -1) { 82 fprintf(stderr, "Device and VLAN ID are required arguments.\n"); 83 return -1; 84 } 85 86 req.ifm.ifi_index = ll_name_to_index(d); 87 if (req.ifm.ifi_index == 0) { 88 fprintf(stderr, "Cannot find bridge device \"%s\"\n", d); 89 return -1; 90 } 91 92 if (vid >= 4096) { 93 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid); 94 return -1; 95 } 96 97 if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { 98 if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) { 99 fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n", 100 vid, vid_end); 101 return -1; 102 } 103 if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) { 104 fprintf(stderr, 105 "pvid cannot be configured for a vlan range\n"); 106 return -1; 107 } 108 } 109 110 afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); 111 112 if (flags) 113 addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags); 114 115 vinfo.vid = vid; 116 if (vid_end != -1) { 117 /* send vlan range start */ 118 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, 119 sizeof(vinfo)); 120 vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; 121 122 /* Now send the vlan range end */ 123 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; 124 vinfo.vid = vid_end; 125 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, 126 sizeof(vinfo)); 127 } else { 128 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo, 129 sizeof(vinfo)); 130 } 131 132 addattr_nest_end(&req.n, afspec); 133 134 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) 135 return -1; 136 137 return 0; 138 } 139 140 static int print_vlan(const struct sockaddr_nl *who, 141 struct nlmsghdr *n, 142 void *arg) 143 { 144 FILE *fp = arg; 145 struct ifinfomsg *ifm = NLMSG_DATA(n); 146 int len = n->nlmsg_len; 147 struct rtattr * tb[IFLA_MAX+1]; 148 149 if (n->nlmsg_type != RTM_NEWLINK) { 150 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n", 151 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); 152 return 0; 153 } 154 155 len -= NLMSG_LENGTH(sizeof(*ifm)); 156 if (len < 0) { 157 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); 158 return -1; 159 } 160 161 if (ifm->ifi_family != AF_BRIDGE) 162 return 0; 163 164 if (filter_index && filter_index != ifm->ifi_index) 165 return 0; 166 167 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len); 168 169 /* if AF_SPEC isn't there, vlan table is not preset for this port */ 170 if (!tb[IFLA_AF_SPEC]) { 171 fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index)); 172 return 0; 173 } else { 174 struct rtattr *i, *list = tb[IFLA_AF_SPEC]; 175 int rem = RTA_PAYLOAD(list); 176 177 fprintf(fp, "%s", ll_index_to_name(ifm->ifi_index)); 178 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { 179 struct bridge_vlan_info *vinfo; 180 181 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) 182 continue; 183 184 vinfo = RTA_DATA(i); 185 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) 186 fprintf(fp, "-%hu", vinfo->vid); 187 else 188 fprintf(fp, "\t %hu", vinfo->vid); 189 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) 190 continue; 191 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) 192 fprintf(fp, " PVID"); 193 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) 194 fprintf(fp, " Egress Untagged"); 195 fprintf(fp, "\n"); 196 } 197 } 198 fprintf(fp, "\n"); 199 fflush(fp); 200 return 0; 201 } 202 203 static int vlan_show(int argc, char **argv) 204 { 205 char *filter_dev = NULL; 206 207 while (argc > 0) { 208 if (strcmp(*argv, "dev") == 0) { 209 NEXT_ARG(); 210 if (filter_dev) 211 duparg("dev", *argv); 212 filter_dev = *argv; 213 } 214 argc--; argv++; 215 } 216 217 if (filter_dev) { 218 if ((filter_index = if_nametoindex(filter_dev)) == 0) { 219 fprintf(stderr, "Cannot find device \"%s\"\n", 220 filter_dev); 221 return -1; 222 } 223 } 224 225 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK, 226 (compress_vlans ? 227 RTEXT_FILTER_BRVLAN_COMPRESSED : 228 RTEXT_FILTER_BRVLAN)) < 0) { 229 perror("Cannont send dump request"); 230 exit(1); 231 } 232 233 printf("port\tvlan ids\n"); 234 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) { 235 fprintf(stderr, "Dump ternminated\n"); 236 exit(1); 237 } 238 239 return 0; 240 } 241 242 243 int do_vlan(int argc, char **argv) 244 { 245 ll_init_map(&rth); 246 247 if (argc > 0) { 248 if (matches(*argv, "add") == 0) 249 return vlan_modify(RTM_SETLINK, argc-1, argv+1); 250 if (matches(*argv, "delete") == 0) 251 return vlan_modify(RTM_DELLINK, argc-1, argv+1); 252 if (matches(*argv, "show") == 0 || 253 matches(*argv, "lst") == 0 || 254 matches(*argv, "list") == 0) 255 return vlan_show(argc-1, argv+1); 256 if (matches(*argv, "help") == 0) 257 usage(); 258 } else 259 return vlan_show(0, NULL); 260 261 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv); 262 exit(-1); 263 } 264