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"; 71*b84d0aaaSAllan 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 8890c4b74cSSam Leffler vlan_status(int s) 8997ed1257SBill Paul { 9097ed1257SBill Paul struct vlanreq vreq; 9197ed1257SBill Paul 922ccbbd06SMarcelo Araujo if (getvlan(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 } 1062ccbbd06SMarcelo Araujo if (ioctl(s, 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; 124c7cffd65SAlexander V. Chernikov 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 * Don't mix vlan/vlandev parameters with dot notation. 131c7cffd65SAlexander V. Chernikov */ 132c7cffd65SAlexander V. Chernikov if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') 133c7cffd65SAlexander V. Chernikov errx(1, "ambiguous vlan specification"); 134c7cffd65SAlexander V. Chernikov /* 135c7cffd65SAlexander V. Chernikov * Derive params from interface name: "parent.vid". 136c7cffd65SAlexander V. Chernikov */ 137c7cffd65SAlexander V. Chernikov *cp++ = '\0'; 138c7cffd65SAlexander V. Chernikov if ((*cp < '1') || (*cp > '9')) 139c7cffd65SAlexander V. Chernikov errx(1, "invalid vlan tag"); 140c7cffd65SAlexander V. Chernikov 141c7cffd65SAlexander V. Chernikov vid = *cp++ - '0'; 142c7cffd65SAlexander V. Chernikov while ((*cp >= '0') && (*cp <= '9')) 143c7cffd65SAlexander V. Chernikov vid = (vid * 10) + (*cp++ - '0'); 144c7cffd65SAlexander V. Chernikov if ((*cp != '\0') || (vid & ~0xFFF)) 145c7cffd65SAlexander V. Chernikov errx(1, "invalid vlan tag"); 146c7cffd65SAlexander V. Chernikov 147c7cffd65SAlexander V. Chernikov strlcpy(params.vlr_parent, ifname, IFNAMSIZ); 148c7cffd65SAlexander V. Chernikov params.vlr_tag = (vid & 0xFFF); 149c7cffd65SAlexander V. Chernikov } 150c7cffd65SAlexander V. Chernikov 1515faf8dcbSSam Leffler static void 152db82353dSSam Leffler vlan_create(int s, struct ifreq *ifr) 15397ed1257SBill Paul { 154c7cffd65SAlexander V. Chernikov vlan_parse_ethervid(ifr->ifr_name); 155c7cffd65SAlexander V. Chernikov 156db82353dSSam Leffler if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { 157db82353dSSam Leffler /* 158db82353dSSam Leffler * One or both parameters were specified, make sure both. 159db82353dSSam Leffler */ 160db82353dSSam Leffler if (params.vlr_tag == NOTAG) 161db82353dSSam Leffler errx(1, "must specify a tag for vlan create"); 162db82353dSSam Leffler if (params.vlr_parent[0] == '\0') 163db82353dSSam Leffler errx(1, "must specify a parent device for vlan create"); 164db82353dSSam Leffler ifr->ifr_data = (caddr_t) ¶ms; 16597ed1257SBill Paul } 16605952067SHans Petter Selasky ioctl_ifcreate(s, ifr); 1677fc2ce8aSYaroslav Tykhiy } 16897ed1257SBill Paul 1697fc2ce8aSYaroslav Tykhiy static void 1707fc2ce8aSYaroslav Tykhiy vlan_cb(int s, void *arg) 1717fc2ce8aSYaroslav Tykhiy { 172db82353dSSam Leffler if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) 1737fc2ce8aSYaroslav Tykhiy errx(1, "both vlan and vlandev must be specified"); 1742fe9aa26SYaroslav Tykhiy } 1752fe9aa26SYaroslav Tykhiy 1762fe9aa26SYaroslav Tykhiy static void 177db82353dSSam Leffler vlan_set(int s, struct ifreq *ifr) 1782fe9aa26SYaroslav Tykhiy { 179db82353dSSam Leffler if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { 180db82353dSSam Leffler ifr->ifr_data = (caddr_t) ¶ms; 181db82353dSSam Leffler if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) 1827fc2ce8aSYaroslav Tykhiy err(1, "SIOCSETVLAN"); 1837fc2ce8aSYaroslav Tykhiy } 18497ed1257SBill Paul } 1855faf8dcbSSam Leffler 18667207b69SSam Leffler static 18767207b69SSam Leffler DECL_CMD_FUNC(setvlantag, val, d) 188db82353dSSam Leffler { 18967207b69SSam Leffler struct vlanreq vreq; 190db82353dSSam Leffler u_long ul; 191db82353dSSam Leffler char *endp; 192db82353dSSam Leffler 193db82353dSSam Leffler ul = strtoul(val, &endp, 0); 194db82353dSSam Leffler if (*endp != '\0') 195db82353dSSam Leffler errx(1, "invalid value for vlan"); 196db82353dSSam Leffler params.vlr_tag = ul; 197db82353dSSam Leffler /* check if the value can be represented in vlr_tag */ 198db82353dSSam Leffler if (params.vlr_tag != ul) 199db82353dSSam Leffler errx(1, "value for vlan out of range"); 200db82353dSSam Leffler 20167207b69SSam Leffler if (getvlan(s, &ifr, &vreq) != -1) 202db82353dSSam Leffler vlan_set(s, &ifr); 203db82353dSSam Leffler } 204db82353dSSam Leffler 205db82353dSSam Leffler static 206db82353dSSam Leffler DECL_CMD_FUNC(setvlandev, val, d) 207db82353dSSam Leffler { 208db82353dSSam Leffler struct vlanreq vreq; 209db82353dSSam Leffler 210db82353dSSam Leffler strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); 21167207b69SSam Leffler 212db82353dSSam Leffler if (getvlan(s, &ifr, &vreq) != -1) 213db82353dSSam Leffler vlan_set(s, &ifr); 214db82353dSSam Leffler } 215db82353dSSam Leffler 216db82353dSSam Leffler static 217c7cffd65SAlexander V. Chernikov DECL_CMD_FUNC(setvlanproto, val, d) 218c7cffd65SAlexander V. Chernikov { 219c7cffd65SAlexander V. Chernikov struct vlanreq vreq; 220c7cffd65SAlexander V. Chernikov 221c7cffd65SAlexander V. Chernikov if (strncasecmp(proto_8021Q, val, 222c7cffd65SAlexander V. Chernikov strlen(proto_8021Q)) == 0) { 223c7cffd65SAlexander V. Chernikov params.vlr_proto = ETHERTYPE_VLAN; 224*b84d0aaaSAllan Jude } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0) 225*b84d0aaaSAllan Jude || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) { 226c7cffd65SAlexander V. Chernikov params.vlr_proto = ETHERTYPE_QINQ; 227c7cffd65SAlexander V. Chernikov } else 228c7cffd65SAlexander V. Chernikov errx(1, "invalid value for vlanproto"); 229c7cffd65SAlexander V. Chernikov 230c7cffd65SAlexander V. Chernikov if (getvlan(s, &ifr, &vreq) != -1) 231c7cffd65SAlexander V. Chernikov vlan_set(s, &ifr); 232c7cffd65SAlexander V. Chernikov } 233c7cffd65SAlexander V. Chernikov 234c7cffd65SAlexander V. Chernikov static 2352ccbbd06SMarcelo Araujo DECL_CMD_FUNC(setvlanpcp, val, d) 2362ccbbd06SMarcelo Araujo { 2372ccbbd06SMarcelo Araujo u_long ul; 2382ccbbd06SMarcelo Araujo char *endp; 2392ccbbd06SMarcelo Araujo 2402ccbbd06SMarcelo Araujo ul = strtoul(val, &endp, 0); 2412ccbbd06SMarcelo Araujo if (*endp != '\0') 2422ccbbd06SMarcelo Araujo errx(1, "invalid value for vlanpcp"); 2432ccbbd06SMarcelo Araujo if (ul > 7) 2442ccbbd06SMarcelo Araujo errx(1, "value for vlanpcp out of range"); 2452ccbbd06SMarcelo Araujo ifr.ifr_vlan_pcp = ul; 2462ccbbd06SMarcelo Araujo if (ioctl(s, SIOCSVLANPCP, (caddr_t)&ifr) == -1) 2472ccbbd06SMarcelo Araujo err(1, "SIOCSVLANPCP"); 2482ccbbd06SMarcelo Araujo } 2492ccbbd06SMarcelo Araujo 2502ccbbd06SMarcelo Araujo static 251db82353dSSam Leffler DECL_CMD_FUNC(unsetvlandev, val, d) 252db82353dSSam Leffler { 253db82353dSSam Leffler struct vlanreq vreq; 254db82353dSSam Leffler 255db82353dSSam Leffler bzero((char *)&vreq, sizeof(struct vlanreq)); 256db82353dSSam Leffler ifr.ifr_data = (caddr_t)&vreq; 257db82353dSSam Leffler 258db82353dSSam Leffler if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1) 259db82353dSSam Leffler err(1, "SIOCGETVLAN"); 260db82353dSSam Leffler 261db82353dSSam Leffler bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); 262db82353dSSam Leffler vreq.vlr_tag = 0; 263db82353dSSam Leffler 264db82353dSSam Leffler if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1) 265db82353dSSam Leffler err(1, "SIOCSETVLAN"); 266db82353dSSam Leffler } 267db82353dSSam Leffler 2685faf8dcbSSam Leffler static struct cmd vlan_cmds[] = { 26967207b69SSam Leffler DEF_CLONE_CMD_ARG("vlan", setvlantag), 27067207b69SSam Leffler DEF_CLONE_CMD_ARG("vlandev", setvlandev), 271c7cffd65SAlexander V. Chernikov DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), 2722ccbbd06SMarcelo Araujo DEF_CMD_ARG("vlanpcp", setvlanpcp), 27324994b36SSam Leffler /* NB: non-clone cmds */ 27424994b36SSam Leffler DEF_CMD_ARG("vlan", setvlantag), 27524994b36SSam Leffler DEF_CMD_ARG("vlandev", setvlandev), 276c7cffd65SAlexander V. Chernikov DEF_CMD_ARG("vlanproto", setvlanproto), 277b0dcc11cSYaroslav Tykhiy /* XXX For compatibility. Should become DEF_CMD() some day. */ 278b0dcc11cSYaroslav Tykhiy DEF_CMD_OPTARG("-vlandev", unsetvlandev), 2795faf8dcbSSam Leffler DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), 2805faf8dcbSSam Leffler DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap), 2815faf8dcbSSam Leffler DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), 2825faf8dcbSSam Leffler DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), 283eafbb00dSJack F Vogel DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), 284eafbb00dSJack F Vogel DEF_CMD("-vlanhwfilter", -IFCAP_VLAN_HWFILTER, setifcap), 2850f686c0fSPyun YongHyeon DEF_CMD("-vlanhwtso", -IFCAP_VLAN_HWTSO, setifcap), 2860f686c0fSPyun YongHyeon DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), 2879b776960SBjoern A. Zeeb DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap), 2889b776960SBjoern A. Zeeb DEF_CMD("-vlanhwcsum", -IFCAP_VLAN_HWCSUM, setifcap), 2895faf8dcbSSam Leffler }; 2905faf8dcbSSam Leffler static struct afswtch af_vlan = { 2915faf8dcbSSam Leffler .af_name = "af_vlan", 2925faf8dcbSSam Leffler .af_af = AF_UNSPEC, 29390c4b74cSSam Leffler .af_other_status = vlan_status, 2945faf8dcbSSam Leffler }; 2955faf8dcbSSam Leffler 2965faf8dcbSSam Leffler static __constructor void 2975faf8dcbSSam Leffler vlan_ctor(void) 2985faf8dcbSSam Leffler { 299b59dcaeeSXin LI size_t i; 3005faf8dcbSSam Leffler 301abd71050SEnji Cooper for (i = 0; i < nitems(vlan_cmds); i++) 3025faf8dcbSSam Leffler cmd_register(&vlan_cmds[i]); 3035faf8dcbSSam Leffler af_register(&af_vlan); 3047fc2ce8aSYaroslav Tykhiy callback_register(vlan_cb, NULL); 305c7cffd65SAlexander V. Chernikov clone_setdefcallback_prefix("vlan", vlan_create); 306c7cffd65SAlexander V. Chernikov clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); 3075faf8dcbSSam Leffler } 308