1df57947fSPedro F. Giffuni /*- 2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause 3df57947fSPedro F. Giffuni * 42ccbbd06SMarcelo Araujo * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu> 52ccbbd06SMarcelo Araujo * Copyright (c) 2012 ADARA Networks, Inc. 62ccbbd06SMarcelo Araujo * All rights reserved. 72ccbbd06SMarcelo Araujo * 82ccbbd06SMarcelo Araujo * Portions of this software were developed by Robert N. M. Watson under 92ccbbd06SMarcelo Araujo * contract to ADARA Networks, Inc. 1097ed1257SBill Paul * 1197ed1257SBill Paul * Redistribution and use in source and binary forms, with or without 1297ed1257SBill Paul * modification, are permitted provided that the following conditions 1397ed1257SBill Paul * are met: 1497ed1257SBill Paul * 1. Redistributions of source code must retain the above copyright 1597ed1257SBill Paul * notice, this list of conditions and the following disclaimer. 1697ed1257SBill Paul * 2. Redistributions in binary form must reproduce the above copyright 1797ed1257SBill Paul * notice, this list of conditions and the following disclaimer in the 1897ed1257SBill Paul * documentation and/or other materials provided with the distribution. 1997ed1257SBill Paul * 3. All advertising materials mentioning features or use of this software 2097ed1257SBill Paul * must display the following acknowledgement: 2197ed1257SBill Paul * This product includes software developed by Bill Paul. 2297ed1257SBill Paul * 4. Neither the name of the author nor the names of any co-contributors 2397ed1257SBill Paul * may be used to endorse or promote products derived from this software 2497ed1257SBill Paul * without specific prior written permission. 2597ed1257SBill Paul * 2697ed1257SBill Paul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2797ed1257SBill Paul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2897ed1257SBill Paul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2997ed1257SBill Paul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 3097ed1257SBill Paul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3197ed1257SBill Paul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3297ed1257SBill Paul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3397ed1257SBill Paul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3497ed1257SBill Paul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3597ed1257SBill Paul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3697ed1257SBill Paul * THE POSSIBILITY OF SUCH DAMAGE. 3797ed1257SBill Paul */ 3897ed1257SBill Paul 3997ed1257SBill Paul #include <sys/param.h> 4097ed1257SBill Paul #include <sys/ioctl.h> 4197ed1257SBill Paul #include <sys/socket.h> 4297ed1257SBill Paul #include <sys/sockio.h> 4397ed1257SBill Paul 4497ed1257SBill Paul #include <stdlib.h> 4597ed1257SBill Paul #include <unistd.h> 4697ed1257SBill Paul 4797ed1257SBill Paul #include <net/ethernet.h> 4897ed1257SBill Paul #include <net/if.h> 4997ed1257SBill Paul #include <net/if_vlan_var.h> 5097ed1257SBill Paul #include <net/route.h> 5197ed1257SBill Paul 5297ed1257SBill Paul #include <ctype.h> 5397ed1257SBill Paul #include <stdio.h> 5497ed1257SBill Paul #include <string.h> 5597ed1257SBill Paul #include <stdlib.h> 5697ed1257SBill Paul #include <unistd.h> 5797ed1257SBill Paul #include <err.h> 5897ed1257SBill Paul #include <errno.h> 5997ed1257SBill Paul 6097ed1257SBill Paul #include "ifconfig.h" 6197ed1257SBill Paul 6297ed1257SBill Paul #ifndef lint 6397ed1257SBill Paul static const char rcsid[] = 647f3dea24SPeter Wemm "$FreeBSD$"; 6597ed1257SBill Paul #endif 6697ed1257SBill Paul 67db82353dSSam Leffler #define NOTAG ((u_short) -1) 68db82353dSSam Leffler 69c7cffd65SAlexander V. Chernikov static const char proto_8021Q[] = "802.1q"; 70c7cffd65SAlexander V. Chernikov static const char proto_8021ad[] = "802.1ad"; 71b84d0aaaSAllan Jude static const char proto_qinq[] = "qinq"; 72c7cffd65SAlexander V. Chernikov 73db82353dSSam Leffler static struct vlanreq params = { 74db82353dSSam Leffler .vlr_tag = NOTAG, 75c7cffd65SAlexander V. Chernikov .vlr_proto = ETHERTYPE_VLAN, 76db82353dSSam Leffler }; 77db82353dSSam Leffler 78db82353dSSam Leffler static int 79db82353dSSam Leffler getvlan(int s, struct ifreq *ifr, struct vlanreq *vreq) 80db82353dSSam Leffler { 81db82353dSSam Leffler bzero((char *)vreq, sizeof(*vreq)); 82db82353dSSam Leffler ifr->ifr_data = (caddr_t)vreq; 83db82353dSSam Leffler 84db82353dSSam Leffler return ioctl(s, SIOCGETVLAN, (caddr_t)ifr); 85db82353dSSam Leffler } 862fe9aa26SYaroslav Tykhiy 875faf8dcbSSam Leffler static void 886e3a9d7fSAlexander V. Chernikov vlan_status(if_ctx *ctx) 8997ed1257SBill Paul { 9097ed1257SBill Paul struct vlanreq vreq; 9197ed1257SBill Paul 926e3a9d7fSAlexander V. Chernikov if (getvlan(ctx->io_s, &ifr, &vreq) == -1) 932ccbbd06SMarcelo Araujo return; 942ccbbd06SMarcelo Araujo printf("\tvlan: %d", vreq.vlr_tag); 95c7cffd65SAlexander V. Chernikov printf(" vlanproto: "); 96c7cffd65SAlexander V. Chernikov switch (vreq.vlr_proto) { 97c7cffd65SAlexander V. Chernikov case ETHERTYPE_VLAN: 98c7cffd65SAlexander V. Chernikov printf(proto_8021Q); 99c7cffd65SAlexander V. Chernikov break; 100c7cffd65SAlexander V. Chernikov case ETHERTYPE_QINQ: 101c7cffd65SAlexander V. Chernikov printf(proto_8021ad); 102c7cffd65SAlexander V. Chernikov break; 103c7cffd65SAlexander V. Chernikov default: 104c7cffd65SAlexander V. Chernikov printf("0x%04x", vreq.vlr_proto); 105c7cffd65SAlexander V. Chernikov } 1066e3a9d7fSAlexander V. Chernikov if (ioctl_ctx(ctx, SIOCGVLANPCP, (caddr_t)&ifr) != -1) 1072ccbbd06SMarcelo Araujo printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); 1082ccbbd06SMarcelo Araujo printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? 10997ed1257SBill Paul "<none>" : vreq.vlr_parent); 1102ccbbd06SMarcelo Araujo printf("\n"); 11197ed1257SBill Paul } 11297ed1257SBill Paul 113c7cffd65SAlexander V. Chernikov static int 114c7cffd65SAlexander V. Chernikov vlan_match_ethervid(const char *name) 115c7cffd65SAlexander V. Chernikov { 116c7cffd65SAlexander V. Chernikov return (strchr(name, '.') != NULL); 117c7cffd65SAlexander V. Chernikov } 118c7cffd65SAlexander V. Chernikov 119c7cffd65SAlexander V. Chernikov static void 120c7cffd65SAlexander V. Chernikov vlan_parse_ethervid(const char *name) 121c7cffd65SAlexander V. Chernikov { 122c7cffd65SAlexander V. Chernikov char ifname[IFNAMSIZ]; 123c7cffd65SAlexander V. Chernikov char *cp; 12428b498e6SZhenlei Huang unsigned int vid; 125c7cffd65SAlexander V. Chernikov 126c7cffd65SAlexander V. Chernikov strlcpy(ifname, name, IFNAMSIZ); 127c7cffd65SAlexander V. Chernikov if ((cp = strrchr(ifname, '.')) == NULL) 128c7cffd65SAlexander V. Chernikov return; 129c7cffd65SAlexander V. Chernikov /* 130c7cffd65SAlexander V. Chernikov * Derive params from interface name: "parent.vid". 131c7cffd65SAlexander V. Chernikov */ 132c7cffd65SAlexander V. Chernikov *cp++ = '\0'; 133c7cffd65SAlexander V. Chernikov if ((*cp < '1') || (*cp > '9')) 134c7cffd65SAlexander V. Chernikov errx(1, "invalid vlan tag"); 135c7cffd65SAlexander V. Chernikov 136c7cffd65SAlexander V. Chernikov vid = *cp++ - '0'; 13728b498e6SZhenlei Huang while ((*cp >= '0') && (*cp <= '9')) { 138c7cffd65SAlexander V. Chernikov vid = (vid * 10) + (*cp++ - '0'); 13928b498e6SZhenlei Huang if (vid >= 0xFFF) 14028b498e6SZhenlei Huang errx(1, "invalid vlan tag"); 14128b498e6SZhenlei Huang } 14228b498e6SZhenlei Huang if (*cp != '\0') 143c7cffd65SAlexander V. Chernikov errx(1, "invalid vlan tag"); 144c7cffd65SAlexander V. Chernikov 145779fd053SAlexander V. Chernikov /* 146779fd053SAlexander V. Chernikov * allow "devX.Y vlandev devX vlan Y" syntax 147779fd053SAlexander V. Chernikov */ 148779fd053SAlexander V. Chernikov if (params.vlr_tag == NOTAG || params.vlr_tag == vid) 149779fd053SAlexander V. Chernikov params.vlr_tag = vid; 150779fd053SAlexander V. Chernikov else 151779fd053SAlexander V. Chernikov errx(1, "ambiguous vlan specification"); 152779fd053SAlexander V. Chernikov 153779fd053SAlexander V. Chernikov /* Restrict overriding interface name */ 154779fd053SAlexander V. Chernikov if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname)) 155c7cffd65SAlexander V. Chernikov strlcpy(params.vlr_parent, ifname, IFNAMSIZ); 156779fd053SAlexander V. Chernikov else 157779fd053SAlexander V. Chernikov errx(1, "ambiguous vlan specification"); 158c7cffd65SAlexander V. Chernikov } 159c7cffd65SAlexander V. Chernikov 1605faf8dcbSSam Leffler static void 161*74b42611SAlexander V. Chernikov vlan_create(if_ctx *ctx, struct ifreq *ifr) 16297ed1257SBill Paul { 163c7cffd65SAlexander V. Chernikov vlan_parse_ethervid(ifr->ifr_name); 164c7cffd65SAlexander V. Chernikov 165db82353dSSam Leffler if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { 166db82353dSSam Leffler /* 167db82353dSSam Leffler * One or both parameters were specified, make sure both. 168db82353dSSam Leffler */ 169db82353dSSam Leffler if (params.vlr_tag == NOTAG) 170db82353dSSam Leffler errx(1, "must specify a tag for vlan create"); 171db82353dSSam Leffler if (params.vlr_parent[0] == '\0') 172db82353dSSam Leffler errx(1, "must specify a parent device for vlan create"); 173db82353dSSam Leffler ifr->ifr_data = (caddr_t) ¶ms; 17497ed1257SBill Paul } 175*74b42611SAlexander V. Chernikov ifcreate_ioctl(ctx, ifr); 1767fc2ce8aSYaroslav Tykhiy } 17797ed1257SBill Paul 1787fc2ce8aSYaroslav Tykhiy static void 179*74b42611SAlexander V. Chernikov vlan_cb(if_ctx *ctx __unused, void *arg __unused) 1807fc2ce8aSYaroslav Tykhiy { 181db82353dSSam Leffler if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) 1827fc2ce8aSYaroslav Tykhiy errx(1, "both vlan and vlandev must be specified"); 1832fe9aa26SYaroslav Tykhiy } 1842fe9aa26SYaroslav Tykhiy 1852fe9aa26SYaroslav Tykhiy static void 186db82353dSSam Leffler vlan_set(int s, struct ifreq *ifr) 1872fe9aa26SYaroslav Tykhiy { 188db82353dSSam Leffler if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { 189db82353dSSam Leffler ifr->ifr_data = (caddr_t) ¶ms; 190db82353dSSam Leffler if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) 1917fc2ce8aSYaroslav Tykhiy err(1, "SIOCSETVLAN"); 1927fc2ce8aSYaroslav Tykhiy } 19397ed1257SBill Paul } 1945faf8dcbSSam Leffler 1956e3a9d7fSAlexander V. Chernikov static void 1966e3a9d7fSAlexander V. Chernikov setvlantag(if_ctx *ctx, const char *val, int dummy __unused) 197db82353dSSam Leffler { 19867207b69SSam Leffler struct vlanreq vreq; 199db82353dSSam Leffler u_long ul; 200db82353dSSam Leffler char *endp; 201db82353dSSam Leffler 202db82353dSSam Leffler ul = strtoul(val, &endp, 0); 203db82353dSSam Leffler if (*endp != '\0') 204db82353dSSam Leffler errx(1, "invalid value for vlan"); 205db82353dSSam Leffler params.vlr_tag = ul; 206db82353dSSam Leffler /* check if the value can be represented in vlr_tag */ 207db82353dSSam Leffler if (params.vlr_tag != ul) 208db82353dSSam Leffler errx(1, "value for vlan out of range"); 209db82353dSSam Leffler 2106e3a9d7fSAlexander V. Chernikov if (getvlan(ctx->io_s, &ifr, &vreq) != -1) { 211b82b8055SKristof Provost vreq.vlr_tag = params.vlr_tag; 212b82b8055SKristof Provost memcpy(¶ms, &vreq, sizeof(params)); 2136e3a9d7fSAlexander V. Chernikov vlan_set(ctx->io_s, &ifr); 214db82353dSSam Leffler } 215b82b8055SKristof Provost } 216db82353dSSam Leffler 2176e3a9d7fSAlexander V. Chernikov static void 2186e3a9d7fSAlexander V. Chernikov setvlandev(if_ctx *ctx, const char *val, int dummy __unused) 219db82353dSSam Leffler { 220db82353dSSam Leffler struct vlanreq vreq; 221db82353dSSam Leffler 222db82353dSSam Leffler strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); 22367207b69SSam Leffler 2246e3a9d7fSAlexander V. Chernikov if (getvlan(ctx->io_s, &ifr, &vreq) != -1) 2256e3a9d7fSAlexander V. Chernikov vlan_set(ctx->io_s, &ifr); 226db82353dSSam Leffler } 227db82353dSSam Leffler 2286e3a9d7fSAlexander V. Chernikov static void 2296e3a9d7fSAlexander V. Chernikov setvlanproto(if_ctx *ctx, const char *val, int dummy __unused) 230c7cffd65SAlexander V. Chernikov { 231c7cffd65SAlexander V. Chernikov struct vlanreq vreq; 232c7cffd65SAlexander V. Chernikov 233c7cffd65SAlexander V. Chernikov if (strncasecmp(proto_8021Q, val, 234c7cffd65SAlexander V. Chernikov strlen(proto_8021Q)) == 0) { 235c7cffd65SAlexander V. Chernikov params.vlr_proto = ETHERTYPE_VLAN; 236b84d0aaaSAllan Jude } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0) 237b84d0aaaSAllan Jude || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) { 238c7cffd65SAlexander V. Chernikov params.vlr_proto = ETHERTYPE_QINQ; 239c7cffd65SAlexander V. Chernikov } else 240c7cffd65SAlexander V. Chernikov errx(1, "invalid value for vlanproto"); 241c7cffd65SAlexander V. Chernikov 2426e3a9d7fSAlexander V. Chernikov if (getvlan(ctx->io_s, &ifr, &vreq) != -1) { 243b82b8055SKristof Provost vreq.vlr_proto = params.vlr_proto; 244b82b8055SKristof Provost memcpy(¶ms, &vreq, sizeof(params)); 2456e3a9d7fSAlexander V. Chernikov vlan_set(ctx->io_s, &ifr); 246c7cffd65SAlexander V. Chernikov } 247b82b8055SKristof Provost } 248c7cffd65SAlexander V. Chernikov 2496e3a9d7fSAlexander V. Chernikov static void 2506e3a9d7fSAlexander V. Chernikov setvlanpcp(if_ctx *ctx, const char *val, int dummy __unused) 2512ccbbd06SMarcelo Araujo { 2522ccbbd06SMarcelo Araujo u_long ul; 2532ccbbd06SMarcelo Araujo char *endp; 2542ccbbd06SMarcelo Araujo 2552ccbbd06SMarcelo Araujo ul = strtoul(val, &endp, 0); 2562ccbbd06SMarcelo Araujo if (*endp != '\0') 2572ccbbd06SMarcelo Araujo errx(1, "invalid value for vlanpcp"); 2582ccbbd06SMarcelo Araujo if (ul > 7) 2592ccbbd06SMarcelo Araujo errx(1, "value for vlanpcp out of range"); 2602ccbbd06SMarcelo Araujo ifr.ifr_vlan_pcp = ul; 2616e3a9d7fSAlexander V. Chernikov if (ioctl(ctx->io_s, SIOCSVLANPCP, (caddr_t)&ifr) == -1) 2622ccbbd06SMarcelo Araujo err(1, "SIOCSVLANPCP"); 2632ccbbd06SMarcelo Araujo } 2642ccbbd06SMarcelo Araujo 2656e3a9d7fSAlexander V. Chernikov static void 2660c2beef7SAlexander V. Chernikov unsetvlandev(if_ctx *ctx, const char *val __unused, int dummy __unused) 267db82353dSSam Leffler { 268db82353dSSam Leffler struct vlanreq vreq; 269db82353dSSam Leffler 270db82353dSSam Leffler bzero((char *)&vreq, sizeof(struct vlanreq)); 271db82353dSSam Leffler ifr.ifr_data = (caddr_t)&vreq; 272db82353dSSam Leffler 2736e3a9d7fSAlexander V. Chernikov if (ioctl(ctx->io_s, SIOCGETVLAN, (caddr_t)&ifr) == -1) 274db82353dSSam Leffler err(1, "SIOCGETVLAN"); 275db82353dSSam Leffler 276db82353dSSam Leffler bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); 277db82353dSSam Leffler vreq.vlr_tag = 0; 278db82353dSSam Leffler 2796e3a9d7fSAlexander V. Chernikov if (ioctl(ctx->io_s, SIOCSETVLAN, (caddr_t)&ifr) == -1) 280db82353dSSam Leffler err(1, "SIOCSETVLAN"); 281db82353dSSam Leffler } 282db82353dSSam Leffler 2835faf8dcbSSam Leffler static struct cmd vlan_cmds[] = { 28467207b69SSam Leffler DEF_CLONE_CMD_ARG("vlan", setvlantag), 28567207b69SSam Leffler DEF_CLONE_CMD_ARG("vlandev", setvlandev), 286c7cffd65SAlexander V. Chernikov DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), 2872ccbbd06SMarcelo Araujo DEF_CMD_ARG("vlanpcp", setvlanpcp), 28824994b36SSam Leffler /* NB: non-clone cmds */ 28924994b36SSam Leffler DEF_CMD_ARG("vlan", setvlantag), 29024994b36SSam Leffler DEF_CMD_ARG("vlandev", setvlandev), 291c7cffd65SAlexander V. Chernikov DEF_CMD_ARG("vlanproto", setvlanproto), 292b0dcc11cSYaroslav Tykhiy /* XXX For compatibility. Should become DEF_CMD() some day. */ 293b0dcc11cSYaroslav Tykhiy DEF_CMD_OPTARG("-vlandev", unsetvlandev), 2945faf8dcbSSam Leffler DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), 2955faf8dcbSSam Leffler DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap), 2965faf8dcbSSam Leffler DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), 2975faf8dcbSSam Leffler DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), 298eafbb00dSJack F Vogel DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), 299eafbb00dSJack F Vogel DEF_CMD("-vlanhwfilter", -IFCAP_VLAN_HWFILTER, setifcap), 3000f686c0fSPyun YongHyeon DEF_CMD("-vlanhwtso", -IFCAP_VLAN_HWTSO, setifcap), 3010f686c0fSPyun YongHyeon DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), 3029b776960SBjoern A. Zeeb DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap), 3039b776960SBjoern A. Zeeb DEF_CMD("-vlanhwcsum", -IFCAP_VLAN_HWCSUM, setifcap), 3045faf8dcbSSam Leffler }; 3055faf8dcbSSam Leffler static struct afswtch af_vlan = { 3065faf8dcbSSam Leffler .af_name = "af_vlan", 3075faf8dcbSSam Leffler .af_af = AF_UNSPEC, 30890c4b74cSSam Leffler .af_other_status = vlan_status, 3095faf8dcbSSam Leffler }; 3105faf8dcbSSam Leffler 3115faf8dcbSSam Leffler static __constructor void 3125faf8dcbSSam Leffler vlan_ctor(void) 3135faf8dcbSSam Leffler { 314b59dcaeeSXin LI size_t i; 3155faf8dcbSSam Leffler 316abd71050SEnji Cooper for (i = 0; i < nitems(vlan_cmds); i++) 3175faf8dcbSSam Leffler cmd_register(&vlan_cmds[i]); 3185faf8dcbSSam Leffler af_register(&af_vlan); 3197fc2ce8aSYaroslav Tykhiy callback_register(vlan_cb, NULL); 320c7cffd65SAlexander V. Chernikov clone_setdefcallback_prefix("vlan", vlan_create); 321c7cffd65SAlexander V. Chernikov clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); 3225faf8dcbSSam Leffler } 323