Home | History | Annotate | Download | only in bridge
      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