if_vlan.c revision 71959
176195Sbrian/* 276195Sbrian * Copyright 1998 Massachusetts Institute of Technology 376195Sbrian * 476195Sbrian * Permission to use, copy, modify, and distribute this software and 576195Sbrian * its documentation for any purpose and without fee is hereby 676848Sbrian * granted, provided that both the above copyright notice and this 776195Sbrian * permission notice appear in all copies, that both the above 890706Sbde * copyright notice and this permission notice appear in all 976195Sbrian * supporting documentation, and that the name of M.I.T. not be used 10151350Syar * in advertising or publicity pertaining to distribution of the 1190706Sbde * software without specific, written prior permission. M.I.T. makes 12182668Simp * no representations about the suitability of this software for any 13182668Simp * purpose. It is provided "as is" without express or implied 14151350Syar * warranty. 1590706Sbde * 1676195Sbrian * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/sys/net/if_vlan.c 71959 2001-02-03 11:46:35Z phk $ 30 */ 31 32/* 33 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 34 * Might be extended some day to also handle IEEE 802.1p priority 35 * tagging. This is sort of sneaky in the implementation, since 36 * we need to pretend to be enough of an Ethernet implementation 37 * to make arp work. The way we do this is by telling everyone 38 * that we are an Ethernet, and then catch the packets that 39 * ether_output() left on our output queue when it calls 40 * if_start(), rewrite them for use by the real outgoing interface, 41 * and ask it to send them. 42 * 43 * 44 * XXX It's incorrect to assume that we must always kludge up 45 * headers on the physical device's behalf: some devices support 46 * VLAN tag insersion and extraction in firmware. For these cases, 47 * one can change the behavior of the vlan interface by setting 48 * the LINK0 flag on it (that is setting the vlan interface's LINK0 49 * flag, _not_ the parent's LINK0 flag; we try to leave the parent 50 * alone). If the interface as the LINK0 flag set, then it will 51 * not modify the ethernet header on output because the parent 52 * can do that for itself. On input, the parent can call vlan_input_tag() 53 * directly in order to supply us with an incoming mbuf and the vlan 54 * tag value that goes with it. 55 */ 56 57#include "vlan.h" 58#include "opt_inet.h" 59 60#include <sys/param.h> 61#include <sys/kernel.h> 62#include <sys/malloc.h> 63#include <sys/mbuf.h> 64#include <sys/module.h> 65#include <sys/queue.h> 66#include <sys/socket.h> 67#include <sys/sockio.h> 68#include <sys/sysctl.h> 69#include <sys/systm.h> 70 71#include <net/bpf.h> 72#include <net/ethernet.h> 73#include <net/if.h> 74#include <net/if_arp.h> 75#include <net/if_dl.h> 76#include <net/if_types.h> 77#include <net/if_vlan_var.h> 78 79#ifdef INET 80#include <netinet/in.h> 81#include <netinet/if_ether.h> 82#endif 83 84SYSCTL_DECL(_net_link); 85SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 86SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 87 88u_int vlan_proto = ETHERTYPE_VLAN; 89SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, 90 0, "Ethernet protocol used for VLAN encapsulation"); 91 92static struct ifvlan ifv_softc[NVLAN]; 93 94static void vlan_start(struct ifnet *ifp); 95static void vlan_ifinit(void *foo); 96static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 97static int vlan_setmulti(struct ifnet *ifp); 98static int vlan_unconfig(struct ifnet *ifp); 99static int vlan_config(struct ifvlan *ifv, struct ifnet *p); 100 101/* 102 * Program our multicast filter. What we're actually doing is 103 * programming the multicast filter of the parent. This has the 104 * side effect of causing the parent interface to receive multicast 105 * traffic that it doesn't really want, which ends up being discarded 106 * later by the upper protocol layers. Unfortunately, there's no way 107 * to avoid this: there really is only one physical interface. 108 */ 109static int 110vlan_setmulti(struct ifnet *ifp) 111{ 112 struct ifnet *ifp_p; 113 struct ifmultiaddr *ifma, *rifma = NULL; 114 struct ifvlan *sc; 115 struct vlan_mc_entry *mc = NULL; 116 struct sockaddr_dl sdl; 117 int error; 118 119 /* Find the parent. */ 120 sc = ifp->if_softc; 121 ifp_p = sc->ifv_p; 122 123 sdl.sdl_len = ETHER_ADDR_LEN; 124 sdl.sdl_family = AF_LINK; 125 126 /* First, remove any existing filter entries. */ 127 while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 128 mc = SLIST_FIRST(&sc->vlan_mc_listhead); 129 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 130 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 131 if (error) 132 return(error); 133 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 134 free(mc, M_DEVBUF); 135 } 136 137 /* Now program new ones. */ 138 for (ifma = LIST_FIRST(&ifp->if_multiaddrs); 139 ifma != NULL;ifma = LIST_NEXT(ifma, ifma_link)) { 140 if (ifma->ifma_addr->sa_family != AF_LINK) 141 continue; 142 mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT); 143 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 144 (char *)&mc->mc_addr, ETHER_ADDR_LEN); 145 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 146 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 147 if (error) 148 return(error); 149 } 150 151 return(0); 152} 153 154static void 155vlaninit(void) 156{ 157 int i; 158 159 for (i = 0; i < NVLAN; i++) { 160 struct ifnet *ifp = &ifv_softc[i].ifv_if; 161 162 ifp->if_softc = &ifv_softc[i]; 163 ifp->if_name = "vlan"; 164 ifp->if_unit = i; 165 /* NB: flags are not set here */ 166 ifp->if_linkmib = &ifv_softc[i].ifv_mib; 167 ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; 168 /* NB: mtu is not set here */ 169 170 ifp->if_init = vlan_ifinit; 171 ifp->if_start = vlan_start; 172 ifp->if_ioctl = vlan_ioctl; 173 ifp->if_output = ether_output; 174 ifp->if_snd.ifq_maxlen = ifqmaxlen; 175 ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 176 /* Now undo some of the damage... */ 177 ifp->if_data.ifi_type = IFT_8021_VLAN; 178 ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 179 ifp->if_resolvemulti = 0; 180 } 181} 182 183static int 184vlan_modevent(module_t mod, int type, void *data) 185{ 186 switch (type) { 187 case MOD_LOAD: 188 vlaninit(); 189 break; 190 case MOD_UNLOAD: 191 printf("if_vlan module unload - not possible for this module type\n"); 192 return EINVAL; 193 } 194 return 0; 195} 196 197static moduledata_t vlan_mod = { 198 "if_vlan", 199 vlan_modevent, 200 0 201}; 202 203DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 204 205static void 206vlan_ifinit(void *foo) 207{ 208 return; 209} 210 211static void 212vlan_start(struct ifnet *ifp) 213{ 214 struct ifvlan *ifv; 215 struct ifnet *p; 216 struct ether_vlan_header *evl; 217 struct mbuf *m; 218 219 ifv = ifp->if_softc; 220 p = ifv->ifv_p; 221 222 ifp->if_flags |= IFF_OACTIVE; 223 for (;;) { 224 IF_DEQUEUE(&ifp->if_snd, m); 225 if (m == 0) 226 break; 227 if (ifp->if_bpf) 228 bpf_mtap(ifp, m); 229 230 /* 231 * If the LINK0 flag is set, it means the underlying interface 232 * can do VLAN tag insertion itself and doesn't require us to 233 * create a special header for it. In this case, we just pass 234 * the packet along. However, we need some way to tell the 235 * interface where the packet came from so that it knows how 236 * to find the VLAN tag to use, so we set the rcvif in the 237 * mbuf header to our ifnet. 238 * 239 * Note: we also set the M_PROTO1 flag in the mbuf to let 240 * the parent driver know that the rcvif pointer is really 241 * valid. We need to do this because sometimes mbufs will 242 * be allocated by other parts of the system that contain 243 * garbage in the rcvif pointer. Using the M_PROTO1 flag 244 * lets the driver perform a proper sanity check and avoid 245 * following potentially bogus rcvif pointers off into 246 * never-never land. 247 */ 248 if (ifp->if_flags & IFF_LINK0) { 249 m->m_pkthdr.rcvif = ifp; 250 m->m_flags |= M_PROTO1; 251 } else { 252 M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 253 if (m == NULL) { 254 printf("vlan%d: M_PREPEND failed", ifp->if_unit); 255 ifp->if_ierrors++; 256 continue; 257 } 258 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 259 260 m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); 261 if (m == NULL) { 262 printf("vlan%d: m_pullup failed", ifp->if_unit); 263 ifp->if_ierrors++; 264 continue; 265 } 266 267 /* 268 * Transform the Ethernet header into an Ethernet header 269 * with 802.1Q encapsulation. 270 */ 271 bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 272 sizeof(struct ether_header)); 273 evl = mtod(m, struct ether_vlan_header *); 274 evl->evl_proto = evl->evl_encap_proto; 275 evl->evl_encap_proto = htons(vlan_proto); 276 evl->evl_tag = htons(ifv->ifv_tag); 277#ifdef DEBUG 278 printf("vlan_start: %*D\n", sizeof *evl, 279 (char *)evl, ":"); 280#endif 281 } 282 283 /* 284 * Send it, precisely as ether_output() would have. 285 * We are already running at splimp. 286 */ 287 if (IF_HANDOFF(&p->if_snd, m, p)) 288 ifp->if_opackets++; 289 else 290 ifp->if_oerrors++; 291 } 292 ifp->if_flags &= ~IFF_OACTIVE; 293 294 return; 295} 296 297int 298vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 299{ 300 int i; 301 struct ifvlan *ifv; 302 303 for (i = 0; i < NVLAN; i++) { 304 ifv = &ifv_softc[i]; 305 if (ifv->ifv_tag == t) 306 break; 307 } 308 309 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 310 m_free(m); 311 return -1; /* So the parent can take note */ 312 } 313 314 /* 315 * Having found a valid vlan interface corresponding to 316 * the given source interface and vlan tag, run the 317 * the real packet through ethert_input(). 318 */ 319 m->m_pkthdr.rcvif = &ifv->ifv_if; 320 321 ifv->ifv_if.if_ipackets++; 322 ether_input(&ifv->ifv_if, eh, m); 323 return 0; 324} 325 326int 327vlan_input(struct ether_header *eh, struct mbuf *m) 328{ 329 int i; 330 struct ifvlan *ifv; 331 332 for (i = 0; i < NVLAN; i++) { 333 ifv = &ifv_softc[i]; 334 if (m->m_pkthdr.rcvif == ifv->ifv_p 335 && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 336 == ifv->ifv_tag)) 337 break; 338 } 339 340 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 341 m_freem(m); 342 return -1; /* so ether_input can take note */ 343 } 344 345 /* 346 * Having found a valid vlan interface corresponding to 347 * the given source interface and vlan tag, remove the 348 * encapsulation, and run the real packet through 349 * ether_input() a second time (it had better be 350 * reentrant!). 351 */ 352 m->m_pkthdr.rcvif = &ifv->ifv_if; 353 eh->ether_type = mtod(m, u_int16_t *)[1]; 354 m->m_data += EVL_ENCAPLEN; 355 m->m_len -= EVL_ENCAPLEN; 356 m->m_pkthdr.len -= EVL_ENCAPLEN; 357 358 ifv->ifv_if.if_ipackets++; 359 ether_input(&ifv->ifv_if, eh, m); 360 return 0; 361} 362 363static int 364vlan_config(struct ifvlan *ifv, struct ifnet *p) 365{ 366 struct ifaddr *ifa1, *ifa2; 367 struct sockaddr_dl *sdl1, *sdl2; 368 369 if (p->if_data.ifi_type != IFT_ETHER) 370 return EPROTONOSUPPORT; 371 if (ifv->ifv_p) 372 return EBUSY; 373 ifv->ifv_p = p; 374 if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 375 ifv->ifv_if.if_mtu = p->if_mtu; 376 else 377 ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 378 379 /* 380 * Preserve the state of the LINK0 flag for ourselves. 381 */ 382 ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0)); 383 384 /* 385 * Set up our ``Ethernet address'' to reflect the underlying 386 * physical interface's. 387 */ 388 ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; 389 ifa2 = ifnet_addrs[p->if_index - 1]; 390 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 391 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 392 sdl1->sdl_type = IFT_ETHER; 393 sdl1->sdl_alen = ETHER_ADDR_LEN; 394 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 395 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 396 return 0; 397} 398 399static int 400vlan_unconfig(struct ifnet *ifp) 401{ 402 struct ifaddr *ifa; 403 struct sockaddr_dl *sdl; 404 struct vlan_mc_entry *mc; 405 struct ifvlan *ifv; 406 struct ifnet *p; 407 int error; 408 409 ifv = ifp->if_softc; 410 p = ifv->ifv_p; 411 412 /* 413 * Since the interface is being unconfigured, we need to 414 * empty the list of multicast groups that we may have joined 415 * while we were alive and remove them from the parent's list 416 * as well. 417 */ 418 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 419 struct sockaddr_dl sdl; 420 421 sdl.sdl_len = ETHER_ADDR_LEN; 422 sdl.sdl_family = AF_LINK; 423 mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 424 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 425 error = if_delmulti(p, (struct sockaddr *)&sdl); 426 error = if_delmulti(ifp, (struct sockaddr *)&sdl); 427 if (error) 428 return(error); 429 SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 430 free(mc, M_DEVBUF); 431 } 432 433 /* Disconnect from parent. */ 434 ifv->ifv_p = NULL; 435 ifv->ifv_if.if_mtu = ETHERMTU; 436 437 /* Clear our MAC address. */ 438 ifa = ifnet_addrs[ifv->ifv_if.if_index - 1]; 439 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 440 sdl->sdl_type = IFT_ETHER; 441 sdl->sdl_alen = ETHER_ADDR_LEN; 442 bzero(LLADDR(sdl), ETHER_ADDR_LEN); 443 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 444 445 return 0; 446} 447 448static int 449vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 450{ 451 struct ifaddr *ifa; 452 struct ifnet *p; 453 struct ifreq *ifr; 454 struct ifvlan *ifv; 455 struct vlanreq vlr; 456 int error = 0; 457 458 ifr = (struct ifreq *)data; 459 ifa = (struct ifaddr *)data; 460 ifv = ifp->if_softc; 461 462 switch (cmd) { 463 case SIOCSIFADDR: 464 ifp->if_flags |= IFF_UP; 465 466 switch (ifa->ifa_addr->sa_family) { 467#ifdef INET 468 case AF_INET: 469 arp_ifinit(&ifv->ifv_ac, ifa); 470 break; 471#endif 472 default: 473 break; 474 } 475 break; 476 477 case SIOCGIFADDR: 478 { 479 struct sockaddr *sa; 480 481 sa = (struct sockaddr *) &ifr->ifr_data; 482 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 483 (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 484 } 485 break; 486 487 case SIOCSIFMTU: 488 /* 489 * Set the interface MTU. 490 * This is bogus. The underlying interface might support 491 * jumbo frames. 492 */ 493 if (ifr->ifr_mtu > ETHERMTU) { 494 error = EINVAL; 495 } else { 496 ifp->if_mtu = ifr->ifr_mtu; 497 } 498 break; 499 500 case SIOCSETVLAN: 501 error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 502 if (error) 503 break; 504 if (vlr.vlr_parent[0] == '\0') { 505 vlan_unconfig(ifp); 506 if_down(ifp); 507 ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); 508 break; 509 } 510 p = ifunit(vlr.vlr_parent); 511 if (p == 0) { 512 error = ENOENT; 513 break; 514 } 515 error = vlan_config(ifv, p); 516 if (error) 517 break; 518 ifv->ifv_tag = vlr.vlr_tag; 519 ifp->if_flags |= IFF_RUNNING; 520 break; 521 522 case SIOCGETVLAN: 523 bzero(&vlr, sizeof vlr); 524 if (ifv->ifv_p) { 525 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 526 "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 527 vlr.vlr_tag = ifv->ifv_tag; 528 } 529 error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 530 break; 531 532 case SIOCSIFFLAGS: 533 /* 534 * We don't support promiscuous mode 535 * right now because it would require help from the 536 * underlying drivers, which hasn't been implemented. 537 */ 538 if (ifr->ifr_flags & (IFF_PROMISC)) { 539 ifp->if_flags &= ~(IFF_PROMISC); 540 error = EINVAL; 541 } 542 break; 543 case SIOCADDMULTI: 544 case SIOCDELMULTI: 545 error = vlan_setmulti(ifp); 546 break; 547 default: 548 error = EINVAL; 549 } 550 return error; 551} 552