• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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