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
usage(void)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
vlan_modify(int cmd,int argc,char ** argv)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
print_vlan(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)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
vlan_show(int argc,char ** argv)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
do_vlan(int argc,char ** argv)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