1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu>
5 * Copyright (c) 2012 ADARA Networks, Inc.
6 * All rights reserved.
7  *
8 * Portions of this software were developed by Robert N. M. Watson under
9 * contract to ADARA Networks, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
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.
25 *
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.
37 */
38
39#include <sys/param.h>
40#include <sys/ioctl.h>
41#include <sys/socket.h>
42#include <sys/sockio.h>
43
44#include <stdlib.h>
45#include <unistd.h>
46
47#include <net/ethernet.h>
48#include <net/if.h>
49#include <net/if_vlan_var.h>
50#include <net/route.h>
51
52#include <ctype.h>
53#include <stdio.h>
54#include <string.h>
55#include <stdlib.h>
56#include <unistd.h>
57#include <err.h>
58#include <errno.h>
59
60#include "ifconfig.h"
61
62#ifndef lint
63static const char rcsid[] =
64  "$FreeBSD$";
65#endif
66
67#define	NOTAG	((u_short) -1)
68
69static const char proto_8021Q[]  = "802.1q";
70static const char proto_8021ad[] = "802.1ad";
71static const char proto_qinq[] = "qinq";
72
73static 	struct vlanreq params = {
74	.vlr_tag	= NOTAG,
75	.vlr_proto	= ETHERTYPE_VLAN,
76};
77
78static int
79getvlan(int s, struct ifreq *ifr, struct vlanreq *vreq)
80{
81	bzero((char *)vreq, sizeof(*vreq));
82	ifr->ifr_data = (caddr_t)vreq;
83
84	return ioctl(s, SIOCGETVLAN, (caddr_t)ifr);
85}
86
87static void
88vlan_status(int s)
89{
90	struct vlanreq		vreq;
91
92	if (getvlan(s, &ifr, &vreq) == -1)
93		return;
94	printf("\tvlan: %d", vreq.vlr_tag);
95	printf(" vlanproto: ");
96	switch (vreq.vlr_proto) {
97		case ETHERTYPE_VLAN:
98			printf(proto_8021Q);
99			break;
100		case ETHERTYPE_QINQ:
101			printf(proto_8021ad);
102			break;
103		default:
104			printf("0x%04x", vreq.vlr_proto);
105	}
106	if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1)
107		printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
108	printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
109	    "<none>" : vreq.vlr_parent);
110	printf("\n");
111}
112
113static int
114vlan_match_ethervid(const char *name)
115{
116	return (strchr(name, '.') != NULL);
117}
118
119static void
120vlan_parse_ethervid(const char *name)
121{
122	char ifname[IFNAMSIZ];
123	char *cp;
124	int vid;
125
126	strlcpy(ifname, name, IFNAMSIZ);
127	if ((cp = strrchr(ifname, '.')) == NULL)
128		return;
129	/*
130	 * Don't mix vlan/vlandev parameters with dot notation.
131	 */
132	if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0')
133		errx(1, "ambiguous vlan specification");
134	/*
135	 * Derive params from interface name: "parent.vid".
136	 */
137	*cp++ = '\0';
138	if ((*cp < '1') || (*cp > '9'))
139		errx(1, "invalid vlan tag");
140
141	vid = *cp++ - '0';
142	while ((*cp >= '0') && (*cp <= '9'))
143		vid = (vid * 10) + (*cp++ - '0');
144	if ((*cp != '\0') || (vid & ~0xFFF))
145		errx(1, "invalid vlan tag");
146
147	strlcpy(params.vlr_parent, ifname, IFNAMSIZ);
148	params.vlr_tag = (vid & 0xFFF);
149}
150
151static void
152vlan_create(int s, struct ifreq *ifr)
153{
154	vlan_parse_ethervid(ifr->ifr_name);
155
156	if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') {
157		/*
158		 * One or both parameters were specified, make sure both.
159		 */
160		if (params.vlr_tag == NOTAG)
161			errx(1, "must specify a tag for vlan create");
162		if (params.vlr_parent[0] == '\0')
163			errx(1, "must specify a parent device for vlan create");
164		ifr->ifr_data = (caddr_t) &params;
165	}
166	ioctl_ifcreate(s, ifr);
167}
168
169static void
170vlan_cb(int s, void *arg)
171{
172	if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0'))
173		errx(1, "both vlan and vlandev must be specified");
174}
175
176static void
177vlan_set(int s, struct ifreq *ifr)
178{
179	if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') {
180		ifr->ifr_data = (caddr_t) &params;
181		if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1)
182			err(1, "SIOCSETVLAN");
183	}
184}
185
186static
187DECL_CMD_FUNC(setvlantag, val, d)
188{
189	struct vlanreq vreq;
190	u_long ul;
191	char *endp;
192
193	ul = strtoul(val, &endp, 0);
194	if (*endp != '\0')
195		errx(1, "invalid value for vlan");
196	params.vlr_tag = ul;
197	/* check if the value can be represented in vlr_tag */
198	if (params.vlr_tag != ul)
199		errx(1, "value for vlan out of range");
200
201	if (getvlan(s, &ifr, &vreq) != -1)
202		vlan_set(s, &ifr);
203}
204
205static
206DECL_CMD_FUNC(setvlandev, val, d)
207{
208	struct vlanreq vreq;
209
210	strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent));
211
212	if (getvlan(s, &ifr, &vreq) != -1)
213		vlan_set(s, &ifr);
214}
215
216static
217DECL_CMD_FUNC(setvlanproto, val, d)
218{
219	struct vlanreq vreq;
220
221	if (strncasecmp(proto_8021Q, val,
222	    strlen(proto_8021Q)) == 0) {
223		params.vlr_proto = ETHERTYPE_VLAN;
224	} else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0)
225	    || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) {
226		params.vlr_proto = ETHERTYPE_QINQ;
227	} else
228		errx(1, "invalid value for vlanproto");
229
230	if (getvlan(s, &ifr, &vreq) != -1)
231		vlan_set(s, &ifr);
232}
233
234static
235DECL_CMD_FUNC(setvlanpcp, val, d)
236{
237	u_long ul;
238	char *endp;
239
240	ul = strtoul(val, &endp, 0);
241	if (*endp != '\0')
242		errx(1, "invalid value for vlanpcp");
243	if (ul > 7)
244		errx(1, "value for vlanpcp out of range");
245	ifr.ifr_vlan_pcp = ul;
246	if (ioctl(s, SIOCSVLANPCP, (caddr_t)&ifr) == -1)
247		err(1, "SIOCSVLANPCP");
248}
249
250static
251DECL_CMD_FUNC(unsetvlandev, val, d)
252{
253	struct vlanreq		vreq;
254
255	bzero((char *)&vreq, sizeof(struct vlanreq));
256	ifr.ifr_data = (caddr_t)&vreq;
257
258	if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
259		err(1, "SIOCGETVLAN");
260
261	bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent));
262	vreq.vlr_tag = 0;
263
264	if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
265		err(1, "SIOCSETVLAN");
266}
267
268static struct cmd vlan_cmds[] = {
269	DEF_CLONE_CMD_ARG("vlan",			setvlantag),
270	DEF_CLONE_CMD_ARG("vlandev",			setvlandev),
271	DEF_CLONE_CMD_ARG("vlanproto",			setvlanproto),
272	DEF_CMD_ARG("vlanpcp",				setvlanpcp),
273	/* NB: non-clone cmds */
274	DEF_CMD_ARG("vlan",				setvlantag),
275	DEF_CMD_ARG("vlandev",				setvlandev),
276	DEF_CMD_ARG("vlanproto",			setvlanproto),
277	/* XXX For compatibility.  Should become DEF_CMD() some day. */
278	DEF_CMD_OPTARG("-vlandev",			unsetvlandev),
279	DEF_CMD("vlanmtu",	IFCAP_VLAN_MTU,		setifcap),
280	DEF_CMD("-vlanmtu",	-IFCAP_VLAN_MTU,	setifcap),
281	DEF_CMD("vlanhwtag",	IFCAP_VLAN_HWTAGGING,	setifcap),
282	DEF_CMD("-vlanhwtag",	-IFCAP_VLAN_HWTAGGING,	setifcap),
283	DEF_CMD("vlanhwfilter",	IFCAP_VLAN_HWFILTER,	setifcap),
284	DEF_CMD("-vlanhwfilter", -IFCAP_VLAN_HWFILTER,	setifcap),
285	DEF_CMD("-vlanhwtso",	-IFCAP_VLAN_HWTSO,	setifcap),
286	DEF_CMD("vlanhwtso",	IFCAP_VLAN_HWTSO,	setifcap),
287	DEF_CMD("vlanhwcsum",	IFCAP_VLAN_HWCSUM,	setifcap),
288	DEF_CMD("-vlanhwcsum",	-IFCAP_VLAN_HWCSUM,	setifcap),
289};
290static struct afswtch af_vlan = {
291	.af_name	= "af_vlan",
292	.af_af		= AF_UNSPEC,
293	.af_other_status = vlan_status,
294};
295
296static __constructor void
297vlan_ctor(void)
298{
299	size_t i;
300
301	for (i = 0; i < nitems(vlan_cmds);  i++)
302		cmd_register(&vlan_cmds[i]);
303	af_register(&af_vlan);
304	callback_register(vlan_cb, NULL);
305	clone_setdefcallback_prefix("vlan", vlan_create);
306	clone_setdefcallback_filter(vlan_match_ethervid, vlan_create);
307}
308