blob: 83c40880f0425ac5407fa444bbcbbbb6b1849f56 [file] [log] [blame]
Vlad Yasevich9eff0e52013-02-28 10:04:05 +00001#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
16int filter_index;
17
18static 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
26static 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 struct rtattr *afspec;
36 struct bridge_vlan_info vinfo;
37 unsigned short flags = 0;
38
39 memset(&vinfo, 0, sizeof(vinfo));
40 memset(&req, 0, sizeof(req));
41
42 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
43 req.n.nlmsg_flags = NLM_F_REQUEST;
44 req.n.nlmsg_type = cmd;
45 req.ifm.ifi_family = PF_BRIDGE;
46
47 while (argc > 0) {
48 if (strcmp(*argv, "dev") == 0) {
49 NEXT_ARG();
50 d = *argv;
51 } else if (strcmp(*argv, "vid") == 0) {
52 NEXT_ARG();
53 vid = atoi(*argv);
54 } else if (strcmp(*argv, "self") == 0) {
55 flags |= BRIDGE_FLAGS_SELF;
56 } else if (strcmp(*argv, "master") == 0) {
57 flags |= BRIDGE_FLAGS_MASTER;
58 } else if (strcmp(*argv, "pvid") == 0) {
59 vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
60 } else if (strcmp(*argv, "untagged") == 0) {
61 vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
62 } else {
63 if (matches(*argv, "help") == 0) {
64 NEXT_ARG();
65 }
66 }
67 argc--; argv++;
68 }
69
70 if (d == NULL || vid == -1) {
71 fprintf(stderr, "Device and VLAN ID are required arguments.\n");
72 exit(-1);
73 }
74
75 req.ifm.ifi_index = ll_name_to_index(d);
76 if (req.ifm.ifi_index == 0) {
77 fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
78 return -1;
79 }
80
81 if (vid >= 4096) {
82 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
83 return -1;
84 }
85
86 vinfo.vid = vid;
87
88 afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
89
90 if (flags)
91 addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
92
93 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
94 sizeof(vinfo));
95
96 addattr_nest_end(&req.n, afspec);
97
98 if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
99 exit(2);
100
101 return 0;
102}
103
104static int print_vlan(const struct sockaddr_nl *who,
105 struct nlmsghdr *n,
106 void *arg)
107{
108 FILE *fp = arg;
109 struct ifinfomsg *ifm = NLMSG_DATA(n);
110 int len = n->nlmsg_len;
111 struct rtattr * tb[IFLA_MAX+1];
112
113 if (n->nlmsg_type != RTM_NEWLINK) {
114 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
115 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
116 return 0;
117 }
118
119 len -= NLMSG_LENGTH(sizeof(*ifm));
120 if (len < 0) {
121 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
122 return -1;
123 }
124
125 if (ifm->ifi_family != AF_BRIDGE)
126 return 0;
127
128 if (filter_index && filter_index != ifm->ifi_index)
129 return 0;
130
131 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
132
133 /* if AF_SPEC isn't there, vlan table is not preset for this port */
134 if (!tb[IFLA_AF_SPEC]) {
135 fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index));
136 return 0;
137 } else {
138 struct rtattr *i, *list = tb[IFLA_AF_SPEC];
139 int rem = RTA_PAYLOAD(list);
140
141 fprintf(fp, "%s", ll_index_to_name(ifm->ifi_index));
142 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
143 struct bridge_vlan_info *vinfo;
144
145 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
146 continue;
147
148 vinfo = RTA_DATA(i);
149 fprintf(fp, "\t %hu", vinfo->vid);
150 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID)
151 fprintf(fp, " PVID");
152 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED)
153 fprintf(fp, " Egress Untagged");
154 fprintf(fp, "\n");
155 }
156 }
157 fprintf(fp, "\n");
158 fflush(fp);
159 return 0;
160}
161
162static int vlan_show(int argc, char **argv)
163{
164 char *filter_dev = NULL;
165
166 while (argc > 0) {
167 if (strcmp(*argv, "dev") == 0) {
168 NEXT_ARG();
169 if (filter_dev)
170 duparg("dev", *argv);
171 filter_dev = *argv;
172 }
173 argc--; argv++;
174 }
175
176 if (filter_dev) {
177 if ((filter_index = if_nametoindex(filter_dev)) == 0) {
178 fprintf(stderr, "Cannot find device \"%s\"\n",
179 filter_dev);
180 return -1;
181 }
182 }
183
184 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
185 RTEXT_FILTER_BRVLAN) < 0) {
186 perror("Cannont send dump request");
187 exit(1);
188 }
189
190 printf("port\tvlan ids\n");
191 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
192 fprintf(stderr, "Dump ternminated\n");
193 exit(1);
194 }
195
196 return 0;
197}
198
199
200int do_vlan(int argc, char **argv)
201{
202 ll_init_map(&rth);
203
204 if (argc > 0) {
205 if (matches(*argv, "add") == 0)
206 return vlan_modify(RTM_SETLINK, argc-1, argv+1);
207 if (matches(*argv, "delete") == 0)
208 return vlan_modify(RTM_DELLINK, argc-1, argv+1);
209 if (matches(*argv, "show") == 0 ||
210 matches(*argv, "lst") == 0 ||
211 matches(*argv, "list") == 0)
212 return vlan_show(argc-1, argv+1);
213 if (matches(*argv, "help") == 0)
214 usage();
215 } else
216 return vlan_show(0, NULL);
217
218 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv);
219 exit(-1);
220}