]> git.proxmox.com Git - mirror_iproute2.git/blob - bridge/vlan.c
bridge: vlan: fix a few "fdb" typos in vlan doc
[mirror_iproute2.git] / bridge / vlan.c
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, filter_vlan;
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 ] [ vid VLAN_ID ]\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
55 NEXT_ARG();
56 p = strchr(*argv, '-');
57 if (p) {
58 *p = '\0';
59 p++;
60 vid = atoi(*argv);
61 vid_end = atoi(p);
62 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
63 } else {
64 vid = atoi(*argv);
65 }
66 } else if (strcmp(*argv, "self") == 0) {
67 flags |= BRIDGE_FLAGS_SELF;
68 } else if (strcmp(*argv, "master") == 0) {
69 flags |= BRIDGE_FLAGS_MASTER;
70 } else if (strcmp(*argv, "pvid") == 0) {
71 vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
72 } else if (strcmp(*argv, "untagged") == 0) {
73 vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
74 } else {
75 if (matches(*argv, "help") == 0) {
76 NEXT_ARG();
77 }
78 }
79 argc--; argv++;
80 }
81
82 if (d == NULL || vid == -1) {
83 fprintf(stderr, "Device and VLAN ID are required arguments.\n");
84 return -1;
85 }
86
87 req.ifm.ifi_index = ll_name_to_index(d);
88 if (req.ifm.ifi_index == 0) {
89 fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
90 return -1;
91 }
92
93 if (vid >= 4096) {
94 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
95 return -1;
96 }
97
98 if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
99 if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
100 fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
101 vid, vid_end);
102 return -1;
103 }
104 if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
105 fprintf(stderr,
106 "pvid cannot be configured for a vlan range\n");
107 return -1;
108 }
109 }
110
111 afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
112
113 if (flags)
114 addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
115
116 vinfo.vid = vid;
117 if (vid_end != -1) {
118 /* send vlan range start */
119 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
120 sizeof(vinfo));
121 vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
122
123 /* Now send the vlan range end */
124 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
125 vinfo.vid = vid_end;
126 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
127 sizeof(vinfo));
128 } else {
129 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
130 sizeof(vinfo));
131 }
132
133 addattr_nest_end(&req.n, afspec);
134
135 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
136 return -1;
137
138 return 0;
139 }
140
141 /* In order to use this function for both filtering and non-filtering cases
142 * we need to make it a tristate:
143 * return -1 - if filtering we've gone over so don't continue
144 * return 0 - skip entry and continue (applies to range start or to entries
145 * which are less than filter_vlan)
146 * return 1 - print the entry and continue
147 */
148 static int filter_vlan_check(struct bridge_vlan_info *vinfo)
149 {
150 /* if we're filtering we should stop on the first greater entry */
151 if (filter_vlan && vinfo->vid > filter_vlan &&
152 !(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
153 return -1;
154 if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
155 vinfo->vid < filter_vlan)
156 return 0;
157
158 return 1;
159 }
160
161 static int print_vlan(const struct sockaddr_nl *who,
162 struct nlmsghdr *n,
163 void *arg)
164 {
165 FILE *fp = arg;
166 struct ifinfomsg *ifm = NLMSG_DATA(n);
167 int len = n->nlmsg_len;
168 struct rtattr *tb[IFLA_MAX+1];
169
170 if (n->nlmsg_type != RTM_NEWLINK) {
171 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
172 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
173 return 0;
174 }
175
176 len -= NLMSG_LENGTH(sizeof(*ifm));
177 if (len < 0) {
178 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
179 return -1;
180 }
181
182 if (ifm->ifi_family != AF_BRIDGE)
183 return 0;
184
185 if (filter_index && filter_index != ifm->ifi_index)
186 return 0;
187
188 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
189
190 /* if AF_SPEC isn't there, vlan table is not preset for this port */
191 if (!tb[IFLA_AF_SPEC]) {
192 if (!filter_vlan)
193 fprintf(fp, "%s\tNone\n",
194 ll_index_to_name(ifm->ifi_index));
195 return 0;
196 } else {
197 struct rtattr *i, *list = tb[IFLA_AF_SPEC];
198 int rem = RTA_PAYLOAD(list);
199 __u16 last_vid_start = 0;
200
201 if (!filter_vlan)
202 fprintf(fp, "%s", ll_index_to_name(ifm->ifi_index));
203 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
204 struct bridge_vlan_info *vinfo;
205 int vcheck_ret;
206
207 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
208 continue;
209
210 vinfo = RTA_DATA(i);
211
212 if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
213 last_vid_start = vinfo->vid;
214 vcheck_ret = filter_vlan_check(vinfo);
215 if (vcheck_ret == -1)
216 break;
217 else if (vcheck_ret == 0)
218 continue;
219
220 if (filter_vlan)
221 fprintf(fp, "%s",
222 ll_index_to_name(ifm->ifi_index));
223 fprintf(fp, "\t %hu", last_vid_start);
224 if (last_vid_start != vinfo->vid)
225 fprintf(fp, "-%hu", vinfo->vid);
226 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID)
227 fprintf(fp, " PVID");
228 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED)
229 fprintf(fp, " Egress Untagged");
230 fprintf(fp, "\n");
231 }
232 }
233 if (!filter_vlan)
234 fprintf(fp, "\n");
235 fflush(fp);
236 return 0;
237 }
238
239 static int vlan_show(int argc, char **argv)
240 {
241 char *filter_dev = NULL;
242
243 while (argc > 0) {
244 if (strcmp(*argv, "dev") == 0) {
245 NEXT_ARG();
246 if (filter_dev)
247 duparg("dev", *argv);
248 filter_dev = *argv;
249 } else if (strcmp(*argv, "vid") == 0) {
250 NEXT_ARG();
251 if (filter_vlan)
252 duparg("vid", *argv);
253 filter_vlan = atoi(*argv);
254 }
255 argc--; argv++;
256 }
257
258 if (filter_dev) {
259 if ((filter_index = if_nametoindex(filter_dev)) == 0) {
260 fprintf(stderr, "Cannot find device \"%s\"\n",
261 filter_dev);
262 return -1;
263 }
264 }
265
266 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
267 (compress_vlans ?
268 RTEXT_FILTER_BRVLAN_COMPRESSED :
269 RTEXT_FILTER_BRVLAN)) < 0) {
270 perror("Cannont send dump request");
271 exit(1);
272 }
273
274 printf("port\tvlan ids\n");
275 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
276 fprintf(stderr, "Dump ternminated\n");
277 exit(1);
278 }
279
280 return 0;
281 }
282
283
284 int do_vlan(int argc, char **argv)
285 {
286 ll_init_map(&rth);
287
288 if (argc > 0) {
289 if (matches(*argv, "add") == 0)
290 return vlan_modify(RTM_SETLINK, argc-1, argv+1);
291 if (matches(*argv, "delete") == 0)
292 return vlan_modify(RTM_DELLINK, argc-1, argv+1);
293 if (matches(*argv, "show") == 0 ||
294 matches(*argv, "lst") == 0 ||
295 matches(*argv, "list") == 0)
296 return vlan_show(argc-1, argv+1);
297 if (matches(*argv, "help") == 0)
298 usage();
299 } else
300 return vlan_show(0, NULL);
301
302 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
303 exit(-1);
304 }