if_atmsubr.c revision 183013
1/* $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $ */ 2 3/*- 4 * 5 * Copyright (c) 1996 Charles D. Cranor and Washington University. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Charles D. Cranor and 19 * Washington University. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 * 34 * if_atmsubr.c 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: head/sys/net/if_atmsubr.c 183013 2008-09-14 08:19:48Z julian $"); 39 40#include "opt_inet.h" 41#include "opt_inet6.h" 42#include "opt_mac.h" 43#include "opt_natm.h" 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/kernel.h> 48#include <sys/module.h> 49#include <sys/mbuf.h> 50#include <sys/socket.h> 51#include <sys/sockio.h> 52#include <sys/errno.h> 53#include <sys/sysctl.h> 54#include <sys/malloc.h> 55 56#include <net/if.h> 57#include <net/netisr.h> 58#include <net/route.h> 59#include <net/if_dl.h> 60#include <net/if_types.h> 61#include <net/if_atm.h> 62 63#include <netinet/in.h> 64#include <netinet/if_atm.h> 65#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */ 66#if defined(INET) || defined(INET6) 67#include <netinet/in_var.h> 68#endif 69#ifdef NATM 70#include <netnatm/natm.h> 71#endif 72 73#include <security/mac/mac_framework.h> 74 75/* 76 * Netgraph interface functions. 77 * These need not be protected by a lock, because ng_atm nodes are persitent. 78 * The ng_atm module can be unloaded only if all ATM interfaces have been 79 * unloaded, so nobody should be in the code paths accessing these function 80 * pointers. 81 */ 82void (*ng_atm_attach_p)(struct ifnet *); 83void (*ng_atm_detach_p)(struct ifnet *); 84int (*ng_atm_output_p)(struct ifnet *, struct mbuf **); 85void (*ng_atm_input_p)(struct ifnet *, struct mbuf **, 86 struct atm_pseudohdr *, void *); 87void (*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *, 88 struct atm_pseudohdr *, void *); 89void (*ng_atm_event_p)(struct ifnet *, uint32_t, void *); 90 91/* 92 * Harp pseudo interface hooks 93 */ 94void (*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m, 95 struct atm_pseudohdr *ah, void *rxhand); 96void (*atm_harp_attach_p)(struct ifnet *); 97void (*atm_harp_detach_p)(struct ifnet *); 98void (*atm_harp_event_p)(struct ifnet *, uint32_t, void *); 99 100SYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware"); 101 102MALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals"); 103 104#ifndef ETHERTYPE_IPV6 105#define ETHERTYPE_IPV6 0x86dd 106#endif 107 108#define senderr(e) do { error = (e); goto bad; } while (0) 109 110/* 111 * atm_output: ATM output routine 112 * inputs: 113 * "ifp" = ATM interface to output to 114 * "m0" = the packet to output 115 * "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI) 116 * "rt0" = the route to use 117 * returns: error code [0 == ok] 118 * 119 * note: special semantic: if (dst == NULL) then we assume "m" already 120 * has an atm_pseudohdr on it and just send it directly. 121 * [for native mode ATM output] if dst is null, then 122 * rt0 must also be NULL. 123 */ 124int 125atm_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, 126 struct rtentry *rt0) 127{ 128 u_int16_t etype = 0; /* if using LLC/SNAP */ 129 int error = 0, sz; 130 struct atm_pseudohdr atmdst, *ad; 131 struct mbuf *m = m0; 132 struct atmllc *atmllc; 133 struct atmllc *llc_hdr = NULL; 134 u_int32_t atm_flags; 135 136#ifdef MAC 137 error = mac_ifnet_check_transmit(ifp, m); 138 if (error) 139 senderr(error); 140#endif 141 142 if (!((ifp->if_flags & IFF_UP) && 143 (ifp->if_drv_flags & IFF_DRV_RUNNING))) 144 senderr(ENETDOWN); 145 146 /* 147 * check for non-native ATM traffic (dst != NULL) 148 */ 149 if (dst) { 150 switch (dst->sa_family) { 151 152#if defined(INET) || defined(INET6) 153 case AF_INET: 154 case AF_INET6: 155 { 156 struct rtentry *rt = NULL; 157 /* 158 * check route 159 */ 160 if (rt0 != NULL) { 161 error = rt_check(&rt, &rt0, dst); 162 if (error) 163 goto bad; 164 RT_UNLOCK(rt); 165 } 166 167 if (dst->sa_family == AF_INET6) 168 etype = ETHERTYPE_IPV6; 169 else 170 etype = ETHERTYPE_IP; 171 if (!atmresolve(rt, m, dst, &atmdst)) { 172 m = NULL; 173 /* XXX: atmresolve already free'd it */ 174 senderr(EHOSTUNREACH); 175 /* XXX: put ATMARP stuff here */ 176 /* XXX: watch who frees m on failure */ 177 } 178 } 179 break; 180#endif /* INET || INET6 */ 181 182 case AF_UNSPEC: 183 /* 184 * XXX: bpfwrite. assuming dst contains 12 bytes 185 * (atm pseudo header (4) + LLC/SNAP (8)) 186 */ 187 bcopy(dst->sa_data, &atmdst, sizeof(atmdst)); 188 llc_hdr = (struct atmllc *)(dst->sa_data + 189 sizeof(atmdst)); 190 break; 191 192 default: 193 printf("%s: can't handle af%d\n", ifp->if_xname, 194 dst->sa_family); 195 senderr(EAFNOSUPPORT); 196 } 197 198 /* 199 * must add atm_pseudohdr to data 200 */ 201 sz = sizeof(atmdst); 202 atm_flags = ATM_PH_FLAGS(&atmdst); 203 if (atm_flags & ATM_PH_LLCSNAP) 204 sz += 8; /* sizeof snap == 8 */ 205 M_PREPEND(m, sz, M_DONTWAIT); 206 if (m == 0) 207 senderr(ENOBUFS); 208 ad = mtod(m, struct atm_pseudohdr *); 209 *ad = atmdst; 210 if (atm_flags & ATM_PH_LLCSNAP) { 211 atmllc = (struct atmllc *)(ad + 1); 212 if (llc_hdr == NULL) { 213 bcopy(ATMLLC_HDR, atmllc->llchdr, 214 sizeof(atmllc->llchdr)); 215 /* note: in host order */ 216 ATM_LLC_SETTYPE(atmllc, etype); 217 } 218 else 219 bcopy(llc_hdr, atmllc, sizeof(struct atmllc)); 220 } 221 } 222 223 if (ng_atm_output_p != NULL) { 224 if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) { 225 if (m != NULL) 226 m_freem(m); 227 return (error); 228 } 229 if (m == NULL) 230 return (0); 231 } 232 233 /* 234 * Queue message on interface, and start output if interface 235 * not yet active. 236 */ 237 if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, 238 -(int)sizeof(struct atm_pseudohdr))) 239 return (ENOBUFS); 240 return (error); 241 242bad: 243 if (m) 244 m_freem(m); 245 return (error); 246} 247 248/* 249 * Process a received ATM packet; 250 * the packet is in the mbuf chain m. 251 */ 252void 253atm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m, 254 void *rxhand) 255{ 256 int isr; 257 u_int16_t etype = ETHERTYPE_IP; /* default */ 258 259 if ((ifp->if_flags & IFF_UP) == 0) { 260 m_freem(m); 261 return; 262 } 263#ifdef MAC 264 mac_ifnet_create_mbuf(ifp, m); 265#endif 266 ifp->if_ibytes += m->m_pkthdr.len; 267 268 if (ng_atm_input_p != NULL) { 269 (*ng_atm_input_p)(ifp, &m, ah, rxhand); 270 if (m == NULL) 271 return; 272 } 273 274 /* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */ 275 if (atm_harp_input_p != NULL) { 276 (*atm_harp_input_p)(ifp, &m, ah, rxhand); 277 if (m == NULL) 278 return; 279 } 280 281 if (rxhand) { 282#ifdef NATM 283 struct natmpcb *npcb; 284 285 /* 286 * XXXRW: this use of 'rxhand' is not a very good idea, and 287 * was subject to races even before SMPng due to the release 288 * of spl here. 289 */ 290 NATM_LOCK(); 291 npcb = rxhand; 292 npcb->npcb_inq++; /* count # in queue */ 293 isr = NETISR_NATM; 294 m->m_pkthdr.rcvif = rxhand; /* XXX: overload */ 295 NATM_UNLOCK(); 296#else 297 printf("atm_input: NATM detected but not " 298 "configured in kernel\n"); 299 goto dropit; 300#endif 301 } else { 302 /* 303 * handle LLC/SNAP header, if present 304 */ 305 if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) { 306 struct atmllc *alc; 307 308 if (m->m_len < sizeof(*alc) && 309 (m = m_pullup(m, sizeof(*alc))) == 0) 310 return; /* failed */ 311 alc = mtod(m, struct atmllc *); 312 if (bcmp(alc, ATMLLC_HDR, 6)) { 313 printf("%s: recv'd invalid LLC/SNAP frame " 314 "[vp=%d,vc=%d]\n", ifp->if_xname, 315 ATM_PH_VPI(ah), ATM_PH_VCI(ah)); 316 m_freem(m); 317 return; 318 } 319 etype = ATM_LLC_TYPE(alc); 320 m_adj(m, sizeof(*alc)); 321 } 322 323 switch (etype) { 324 325#ifdef INET 326 case ETHERTYPE_IP: 327 isr = NETISR_IP; 328 break; 329#endif 330 331#ifdef INET6 332 case ETHERTYPE_IPV6: 333 isr = NETISR_IPV6; 334 break; 335#endif 336 default: 337#ifndef NATM 338 dropit: 339#endif 340 if (ng_atm_input_orphan_p != NULL) 341 (*ng_atm_input_orphan_p)(ifp, m, ah, rxhand); 342 else 343 m_freem(m); 344 return; 345 } 346 } 347 netisr_dispatch(isr, m); 348} 349 350/* 351 * Perform common duties while attaching to interface list. 352 */ 353void 354atm_ifattach(struct ifnet *ifp) 355{ 356 struct ifaddr *ifa; 357 struct sockaddr_dl *sdl; 358 struct ifatm *ifatm = ifp->if_l2com; 359 360 ifp->if_addrlen = 0; 361 ifp->if_hdrlen = 0; 362 if_attach(ifp); 363 ifp->if_mtu = ATMMTU; 364 ifp->if_output = atm_output; 365#if 0 366 ifp->if_input = atm_input; 367#endif 368 ifp->if_snd.ifq_maxlen = 50; /* dummy */ 369 370 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 371 if (ifa->ifa_addr->sa_family == AF_LINK) { 372 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 373 sdl->sdl_type = IFT_ATM; 374 sdl->sdl_alen = ifp->if_addrlen; 375#ifdef notyet /* if using ATMARP, store hardware address using the next line */ 376 bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen); 377#endif 378 break; 379 } 380 381 ifp->if_linkmib = &ifatm->mib; 382 ifp->if_linkmiblen = sizeof(ifatm->mib); 383 384 if(ng_atm_attach_p) 385 (*ng_atm_attach_p)(ifp); 386 if (atm_harp_attach_p) 387 (*atm_harp_attach_p)(ifp); 388} 389 390/* 391 * Common stuff for detaching an ATM interface 392 */ 393void 394atm_ifdetach(struct ifnet *ifp) 395{ 396 if (atm_harp_detach_p) 397 (*atm_harp_detach_p)(ifp); 398 if(ng_atm_detach_p) 399 (*ng_atm_detach_p)(ifp); 400 if_detach(ifp); 401} 402 403/* 404 * Support routine for the SIOCATMGVCCS ioctl(). 405 * 406 * This routine assumes, that the private VCC structures used by the driver 407 * begin with a struct atmio_vcc. 408 * 409 * Return a table of VCCs in a freshly allocated memory area. 410 * Here we have a problem: we first count, how many vccs we need 411 * to return. The we allocate the memory and finally fill it in. 412 * Because we cannot lock while calling malloc, the number of active 413 * vccs may change while we're in malloc. So we allocate a couple of 414 * vccs more and if space anyway is not enough re-iterate. 415 * 416 * We could use an sx lock for the vcc tables. 417 */ 418struct atmio_vcctable * 419atm_getvccs(struct atmio_vcc **table, u_int size, u_int start, 420 struct mtx *lock, int waitok) 421{ 422 u_int cid, alloc; 423 size_t len; 424 struct atmio_vcctable *vccs; 425 struct atmio_vcc *v; 426 427 alloc = start + 10; 428 vccs = NULL; 429 430 for (;;) { 431 len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]); 432 vccs = reallocf(vccs, len, M_TEMP, 433 waitok ? M_WAITOK : M_NOWAIT); 434 if (vccs == NULL) 435 return (NULL); 436 bzero(vccs, len); 437 438 vccs->count = 0; 439 v = vccs->vccs; 440 441 mtx_lock(lock); 442 for (cid = 0; cid < size; cid++) 443 if (table[cid] != NULL) { 444 if (++vccs->count == alloc) 445 /* too many - try again */ 446 break; 447 *v++ = *table[cid]; 448 } 449 mtx_unlock(lock); 450 451 if (cid == size) 452 break; 453 454 alloc *= 2; 455 } 456 return (vccs); 457} 458 459/* 460 * Driver or channel state has changed. Inform whoever is interested 461 * in these events. 462 */ 463void 464atm_event(struct ifnet *ifp, u_int event, void *arg) 465{ 466 if (ng_atm_event_p != NULL) 467 (*ng_atm_event_p)(ifp, event, arg); 468 if (atm_harp_event_p != NULL) 469 (*atm_harp_event_p)(ifp, event, arg); 470} 471 472static void * 473atm_alloc(u_char type, struct ifnet *ifp) 474{ 475 struct ifatm *ifatm; 476 477 ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO); 478 ifatm->ifp = ifp; 479 480 return (ifatm); 481} 482 483static void 484atm_free(void *com, u_char type) 485{ 486 487 free(com, M_IFATM); 488} 489 490static int 491atm_modevent(module_t mod, int type, void *data) 492{ 493 switch (type) { 494 case MOD_LOAD: 495 if_register_com_alloc(IFT_ATM, atm_alloc, atm_free); 496 break; 497 case MOD_UNLOAD: 498 if_deregister_com_alloc(IFT_ATM); 499 break; 500 default: 501 return (EOPNOTSUPP); 502 } 503 504 return (0); 505} 506 507static moduledata_t atm_mod = { 508 "atm", 509 atm_modevent, 510 0 511}; 512 513DECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); 514MODULE_VERSION(atm, 1); 515