2 * SPDX-License-Identifier: BSD-4-Clause
4 * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu>
5 * Copyright (c) 2012 ADARA Networks, Inc.
8 * Portions of this software were developed by Robert N. M. Watson under
9 * contract to ADARA Networks, Inc.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Bill Paul.
22 * 4. Neither the name of the author nor the names of any co-contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
36 * THE POSSIBILITY OF SUCH DAMAGE.
39 #include <sys/param.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
47 #include <net/ethernet.h>
49 #include <net/if_vlan_var.h>
50 #include <net/route.h>
63 static const char rcsid[] =
67 #define NOTAG ((u_short) -1)
69 static const char proto_8021Q[] = "802.1q";
70 static const char proto_8021ad[] = "802.1ad";
71 static const char proto_qinq[] = "qinq";
73 static struct vlanreq params = {
75 .vlr_proto = ETHERTYPE_VLAN,
79 vlan_status(if_ctx *ctx)
81 struct vlanreq vreq = {};
82 struct ifreq ifr = { .ifr_data = (caddr_t)&vreq };
84 if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1)
86 printf("\tvlan: %d", vreq.vlr_tag);
87 printf(" vlanproto: ");
88 switch (vreq.vlr_proto) {
96 printf("0x%04x", vreq.vlr_proto);
98 if (ioctl_ctx_ifr(ctx, SIOCGVLANPCP, &ifr) != -1)
99 printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
100 printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
101 "<none>" : vreq.vlr_parent);
106 vlan_match_ethervid(const char *name)
108 return (strchr(name, '.') != NULL);
112 vlan_parse_ethervid(const char *name)
114 char ifname[IFNAMSIZ];
118 strlcpy(ifname, name, IFNAMSIZ);
119 if ((cp = strrchr(ifname, '.')) == NULL)
122 * Derive params from interface name: "parent.vid".
125 if ((*cp < '1') || (*cp > '9'))
126 errx(1, "invalid vlan tag");
129 while ((*cp >= '0') && (*cp <= '9')) {
130 vid = (vid * 10) + (*cp++ - '0');
132 errx(1, "invalid vlan tag");
135 errx(1, "invalid vlan tag");
138 * allow "devX.Y vlandev devX vlan Y" syntax
140 if (params.vlr_tag == NOTAG || params.vlr_tag == vid)
141 params.vlr_tag = vid;
143 errx(1, "ambiguous vlan specification");
145 /* Restrict overriding interface name */
146 if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname))
147 strlcpy(params.vlr_parent, ifname, IFNAMSIZ);
149 errx(1, "ambiguous vlan specification");
153 vlan_create(if_ctx *ctx, struct ifreq *ifr)
155 vlan_parse_ethervid(ifr->ifr_name);
157 if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') {
159 * One or both parameters were specified, make sure both.
161 if (params.vlr_tag == NOTAG)
162 errx(1, "must specify a tag for vlan create");
163 if (params.vlr_parent[0] == '\0')
164 errx(1, "must specify a parent device for vlan create");
165 ifr->ifr_data = (caddr_t) ¶ms;
167 ifcreate_ioctl(ctx, ifr);
171 vlan_cb(if_ctx *ctx __unused, void *arg __unused)
173 if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0'))
174 errx(1, "both vlan and vlandev must be specified");
178 vlan_set(int s, struct ifreq *ifr)
180 if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') {
181 ifr->ifr_data = (caddr_t) ¶ms;
182 if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1)
183 err(1, "SIOCSETVLAN");
188 setvlantag(if_ctx *ctx, const char *val, int dummy __unused)
190 struct vlanreq vreq = {};
191 struct ifreq ifr = { .ifr_data = (caddr_t)&vreq };
195 ul = strtoul(val, &endp, 0);
197 errx(1, "invalid value for vlan");
199 /* check if the value can be represented in vlr_tag */
200 if (params.vlr_tag != ul)
201 errx(1, "value for vlan out of range");
203 if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) {
204 vreq.vlr_tag = params.vlr_tag;
205 memcpy(¶ms, &vreq, sizeof(params));
206 vlan_set(ctx->io_s, &ifr);
211 setvlandev(if_ctx *ctx, const char *val, int dummy __unused)
213 struct vlanreq vreq = {};
214 struct ifreq ifr = { .ifr_data = (caddr_t)&vreq };
216 strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent));
218 if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1)
219 vlan_set(ctx->io_s, &ifr);
223 setvlanproto(if_ctx *ctx, const char *val, int dummy __unused)
225 struct vlanreq vreq = {};
226 struct ifreq ifr = { .ifr_data = (caddr_t)&vreq };
228 if (strncasecmp(proto_8021Q, val,
229 strlen(proto_8021Q)) == 0) {
230 params.vlr_proto = ETHERTYPE_VLAN;
231 } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0)
232 || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) {
233 params.vlr_proto = ETHERTYPE_QINQ;
235 errx(1, "invalid value for vlanproto");
237 if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) {
238 vreq.vlr_proto = params.vlr_proto;
239 memcpy(¶ms, &vreq, sizeof(params));
240 vlan_set(ctx->io_s, &ifr);
245 setvlanpcp(if_ctx *ctx, const char *val, int dummy __unused)
249 struct ifreq ifr = {};
251 ul = strtoul(val, &endp, 0);
253 errx(1, "invalid value for vlanpcp");
255 errx(1, "value for vlanpcp out of range");
256 ifr.ifr_vlan_pcp = ul;
257 if (ioctl_ctx_ifr(ctx, SIOCSVLANPCP, &ifr) == -1)
258 err(1, "SIOCSVLANPCP");
262 unsetvlandev(if_ctx *ctx, const char *val __unused, int dummy __unused)
264 struct vlanreq vreq = {};
265 struct ifreq ifr = { .ifr_data = (caddr_t)&vreq };
267 if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1)
268 err(1, "SIOCGETVLAN");
270 bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent));
273 if (ioctl_ctx(ctx, SIOCSETVLAN, (caddr_t)&ifr) == -1)
274 err(1, "SIOCSETVLAN");
277 static struct cmd vlan_cmds[] = {
278 DEF_CLONE_CMD_ARG("vlan", setvlantag),
279 DEF_CLONE_CMD_ARG("vlandev", setvlandev),
280 DEF_CLONE_CMD_ARG("vlanproto", setvlanproto),
281 DEF_CMD_ARG("vlanpcp", setvlanpcp),
282 /* NB: non-clone cmds */
283 DEF_CMD_ARG("vlan", setvlantag),
284 DEF_CMD_ARG("vlandev", setvlandev),
285 DEF_CMD_ARG("vlanproto", setvlanproto),
286 /* XXX For compatibility. Should become DEF_CMD() some day. */
287 DEF_CMD_OPTARG("-vlandev", unsetvlandev),
288 DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap),
289 DEF_CMD("-vlanmtu", IFCAP_VLAN_MTU, clearifcap),
290 DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap),
291 DEF_CMD("-vlanhwtag", IFCAP_VLAN_HWTAGGING, clearifcap),
292 DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap),
293 DEF_CMD("-vlanhwfilter", IFCAP_VLAN_HWFILTER, clearifcap),
294 DEF_CMD("-vlanhwtso", IFCAP_VLAN_HWTSO, clearifcap),
295 DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap),
296 DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap),
297 DEF_CMD("-vlanhwcsum", IFCAP_VLAN_HWCSUM, clearifcap),
299 static struct afswtch af_vlan = {
300 .af_name = "af_vlan",
302 .af_other_status = vlan_status,
305 static __constructor void
310 for (i = 0; i < nitems(vlan_cmds); i++)
311 cmd_register(&vlan_cmds[i]);
312 af_register(&af_vlan);
313 callback_register(vlan_cb, NULL);
314 clone_setdefcallback_prefix("vlan", vlan_create);
315 clone_setdefcallback_filter(vlan_match_ethervid, vlan_create);