1 /* 2 * Copyright 1998 Massachusetts Institute of Technology 3 * 4 * Permission to use, copy, modify, and distribute this software and 5 * its documentation for any purpose and without fee is hereby 6 * granted, provided that both the above copyright notice and this 7 * permission notice appear in all copies, that both the above 8 * copyright notice and this permission notice appear in all 9 * supporting documentation, and that the name of M.I.T. not be used 10 * in advertising or publicity pertaining to distribution of the 11 * software without specific, written prior permission. M.I.T. makes 12 * no representations about the suitability of this software for any 13 * purpose. It is provided "as is" without express or implied 14 * warranty. 15 * 16 * 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 * $Id: if_vlan.c,v 1.3 1998/08/23 03:07:10 wollman Exp $ 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 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 #include "vlan.h" 45 #if NVLAN > 0 46 #include "opt_inet.h" 47 #include "bpfilter.h" 48 49 #include <sys/param.h> 50 #include <sys/kernel.h> 51 #include <sys/mbuf.h> 52 #include <sys/socket.h> 53 #include <sys/sockio.h> 54 #include <sys/sysctl.h> 55 #include <sys/systm.h> 56 57 #if NBPFILTER > 0 58 #include <net/bpf.h> 59 #endif 60 #include <net/ethernet.h> 61 #include <net/if.h> 62 #include <net/if_arp.h> 63 #include <net/if_dl.h> 64 #include <net/if_types.h> 65 #include <net/if_vlan_var.h> 66 67 #ifdef INET 68 #include <netinet/in.h> 69 #include <netinet/if_ether.h> 70 #endif 71 72 SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 73 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 74 75 u_int vlan_proto = ETHERTYPE_VLAN; 76 SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, 77 0, "Ethernet protocol used for VLAN encapsulation"); 78 79 static struct ifvlan ifv_softc[NVLAN]; 80 81 static void vlan_start(struct ifnet *ifp); 82 static void vlan_ifinit(void *foo); 83 static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 84 85 static void 86 vlaninit(void *dummy) 87 { 88 int i; 89 90 for (i = 0; i < NVLAN; i++) { 91 struct ifnet *ifp = &ifv_softc[i].ifv_if; 92 93 ifp->if_softc = &ifv_softc[i]; 94 ifp->if_name = "vlan"; 95 ifp->if_unit = i; 96 /* NB: flags are not set here */ 97 ifp->if_linkmib = &ifv_softc[i].ifv_mib; 98 ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; 99 /* NB: mtu is not set here */ 100 101 ifp->if_init = vlan_ifinit; 102 ifp->if_start = vlan_start; 103 ifp->if_ioctl = vlan_ioctl; 104 ifp->if_output = ether_output; 105 if_attach(ifp); 106 ether_ifattach(ifp); 107 #if NBPFILTER > 0 108 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 109 #endif 110 /* Now undo some of the damage... */ 111 ifp->if_data.ifi_type = IFT_8021_VLAN; 112 ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 113 ifp->if_resolvemulti = 0; 114 } 115 } 116 PSEUDO_SET(vlaninit, if_vlan); 117 118 static void 119 vlan_ifinit(void *foo) 120 { 121 ; 122 } 123 124 static void 125 vlan_start(struct ifnet *ifp) 126 { 127 struct ifvlan *ifv; 128 struct ifnet *p; 129 struct ether_vlan_header *evl; 130 struct mbuf *m; 131 132 ifv = ifp->if_softc; 133 p = ifv->ifv_p; 134 135 ifp->if_flags |= IFF_OACTIVE; 136 for (;;) { 137 IF_DEQUEUE(&ifp->if_snd, m); 138 if (m == 0) 139 break; 140 #if NBPFILTER > 0 141 if (ifp->if_bpf) 142 bpf_mtap(ifp, m); 143 #endif /* NBPFILTER > 0 */ 144 145 M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 146 if (m == 0) 147 continue; 148 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 149 150 /* 151 * Transform the Ethernet header into an Ethernet header 152 * with 802.1Q encapsulation. 153 */ 154 bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 155 sizeof(struct ether_header)); 156 evl = mtod(m, struct ether_vlan_header *); 157 evl->evl_proto = evl->evl_encap_proto; 158 evl->evl_encap_proto = htons(vlan_proto); 159 evl->evl_tag = htons(ifv->ifv_tag); 160 printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":"); 161 162 /* 163 * Send it, precisely as ether_output() would have. 164 * We are already running at splimp. 165 */ 166 if (IF_QFULL(&p->if_snd)) { 167 IF_DROP(&p->if_snd); 168 /* XXX stats */ 169 } 170 IF_ENQUEUE(&p->if_snd, m); 171 if ((p->if_flags & IFF_OACTIVE) == 0) 172 p->if_start(p); 173 } 174 ifp->if_flags &= ~IFF_OACTIVE; 175 } 176 177 int 178 vlan_input(struct ether_header *eh, struct mbuf *m) 179 { 180 int i; 181 struct ifvlan *ifv; 182 183 for (i = 0; i < NVLAN; i++) { 184 ifv = &ifv_softc[i]; 185 if (m->m_pkthdr.rcvif == ifv->ifv_p 186 && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 187 == ifv->ifv_tag)) 188 break; 189 } 190 191 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 192 m_freem(m); 193 return -1; /* so ether_input can take note */ 194 } 195 196 /* 197 * Having found a valid vlan interface corresponding to 198 * the given source interface and vlan tag, remove the 199 * encapsulation, and run the real packet through 200 * ether_input() a second time (it had better be 201 * reentrant!). 202 */ 203 m->m_pkthdr.rcvif = &ifv->ifv_if; 204 eh->ether_type = mtod(m, u_int16_t *)[1]; 205 m->m_data += EVL_ENCAPLEN; 206 m->m_len -= EVL_ENCAPLEN; 207 m->m_pkthdr.len -= EVL_ENCAPLEN; 208 209 #if NBPFILTER > 0 210 if (ifv->ifv_if.if_bpf) { 211 /* 212 * Do the usual BPF fakery. Note that we don't support 213 * promiscuous mode here, since it would require the 214 * drivers to know about VLANs and we're not ready for 215 * that yet. 216 */ 217 struct mbuf m0; 218 m0.m_next = m; 219 m0.m_len = sizeof(struct ether_header); 220 m0.m_data = (char *)eh; 221 bpf_mtap(&ifv->ifv_if, &m0); 222 } 223 #endif 224 ether_input(&ifv->ifv_if, eh, m); 225 return 0; 226 } 227 228 static int 229 vlan_config(struct ifvlan *ifv, struct ifnet *p) 230 { 231 struct ifaddr *ifa1, *ifa2; 232 struct sockaddr_dl *sdl1, *sdl2; 233 234 if (p->if_data.ifi_type != IFT_ETHER) 235 return EPROTONOSUPPORT; 236 if (ifv->ifv_p) 237 return EBUSY; 238 ifv->ifv_p = p; 239 if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 240 ifv->ifv_if.if_mtu = p->if_mtu; 241 else 242 ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 243 244 /* 245 * NB: we don't support multicast at this point. 246 */ 247 ifv->ifv_if.if_flags = (p->if_flags & ~IFF_MULTICAST); /* XXX */ 248 249 /* 250 * Set up our ``Ethernet address'' to reflect the underlying 251 * physical interface's. 252 */ 253 ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; 254 ifa2 = ifnet_addrs[p->if_index - 1]; 255 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 256 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 257 sdl1->sdl_type = IFT_ETHER; 258 sdl1->sdl_alen = ETHER_ADDR_LEN; 259 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 260 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 261 return 0; 262 } 263 264 static int 265 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 266 { 267 struct ifaddr *ifa; 268 struct ifnet *p; 269 struct ifreq *ifr; 270 struct ifvlan *ifv; 271 struct vlanreq vlr; 272 int error = 0; 273 274 ifr = (struct ifreq *)data; 275 ifa = (struct ifaddr *)data; 276 ifv = ifp->if_softc; 277 278 switch (cmd) { 279 case SIOCSIFADDR: 280 ifp->if_flags |= IFF_UP; 281 282 switch (ifa->ifa_addr->sa_family) { 283 #ifdef INET 284 case AF_INET: 285 arp_ifinit(&ifv->ifv_ac, ifa); 286 break; 287 #endif 288 default: 289 break; 290 } 291 break; 292 293 case SIOCGIFADDR: 294 { 295 struct sockaddr *sa; 296 297 sa = (struct sockaddr *) &ifr->ifr_data; 298 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 299 (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 300 } 301 break; 302 303 case SIOCSIFMTU: 304 /* 305 * Set the interface MTU. 306 */ 307 if (ifr->ifr_mtu > ETHERMTU) { 308 error = EINVAL; 309 } else { 310 ifp->if_mtu = ifr->ifr_mtu; 311 } 312 break; 313 314 case SIOCSETVLAN: 315 error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 316 if (error) 317 break; 318 if (vlr.vlr_parent[0] == '\0') { 319 ifv->ifv_p = 0; 320 if_down(ifp); 321 break; 322 } 323 p = ifunit(vlr.vlr_parent); 324 if (p == 0) { 325 error = ENOENT; 326 break; 327 } 328 error = vlan_config(ifv, p); 329 if (error) 330 break; 331 ifv->ifv_tag = vlr.vlr_tag; 332 break; 333 334 case SIOCGETVLAN: 335 bzero(&vlr, sizeof vlr); 336 if (ifv->ifv_p) { 337 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 338 "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 339 vlr.vlr_tag = ifv->ifv_tag; 340 } 341 error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 342 break; 343 344 case SIOCSIFFLAGS: 345 /* 346 * We don't support all-multicast or promiscuous modes 347 * right now because it would require help from the 348 * underlying drivers, which hasn't been implemented. 349 */ 350 if (ifr->ifr_flags & (IFF_PROMISC|IFF_ALLMULTI)) { 351 ifp->if_flags &= ~(IFF_PROMISC|IFF_ALLMULTI); 352 error = EINVAL; 353 } 354 break; 355 356 /* NB: this will reject multicast state changes */ 357 default: 358 error = EINVAL; 359 } 360 return error; 361 } 362 363 #endif /* NVLAN > 0 */ 364