if_atmsubr.c revision 163606
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 163606 2006-10-22 11:52:19Z rwatson $"); 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_check_ifnet_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#if (defined(__FreeBSD__) && __FreeBSD_version >= 501113) || \ 194 defined(__NetBSD__) || defined(__OpenBSD__) 195 printf("%s: can't handle af%d\n", ifp->if_xname, 196 dst->sa_family); 197#elif defined(__FreeBSD__) || defined(__bsdi__) 198 printf("%s%d: can't handle af%d\n", ifp->if_name, 199 ifp->if_unit, dst->sa_family); 200#endif 201 senderr(EAFNOSUPPORT); 202 } 203 204 /* 205 * must add atm_pseudohdr to data 206 */ 207 sz = sizeof(atmdst); 208 atm_flags = ATM_PH_FLAGS(&atmdst); 209 if (atm_flags & ATM_PH_LLCSNAP) 210 sz += 8; /* sizeof snap == 8 */ 211 M_PREPEND(m, sz, M_DONTWAIT); 212 if (m == 0) 213 senderr(ENOBUFS); 214 ad = mtod(m, struct atm_pseudohdr *); 215 *ad = atmdst; 216 if (atm_flags & ATM_PH_LLCSNAP) { 217 atmllc = (struct atmllc *)(ad + 1); 218 if (llc_hdr == NULL) { 219 bcopy(ATMLLC_HDR, atmllc->llchdr, 220 sizeof(atmllc->llchdr)); 221 /* note: in host order */ 222 ATM_LLC_SETTYPE(atmllc, etype); 223 } 224 else 225 bcopy(llc_hdr, atmllc, sizeof(struct atmllc)); 226 } 227 } 228 229 if (ng_atm_output_p != NULL) { 230 if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) { 231 if (m != NULL) 232 m_freem(m); 233 return (error); 234 } 235 if (m == NULL) 236 return (0); 237 } 238 239 /* 240 * Queue message on interface, and start output if interface 241 * not yet active. 242 */ 243 if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, 244 -(int)sizeof(struct atm_pseudohdr))) 245 return (ENOBUFS); 246 return (error); 247 248bad: 249 if (m) 250 m_freem(m); 251 return (error); 252} 253 254/* 255 * Process a received ATM packet; 256 * the packet is in the mbuf chain m. 257 */ 258void 259atm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m, 260 void *rxhand) 261{ 262 int isr; 263 u_int16_t etype = ETHERTYPE_IP; /* default */ 264 265 if ((ifp->if_flags & IFF_UP) == 0) { 266 m_freem(m); 267 return; 268 } 269#ifdef MAC 270 mac_create_mbuf_from_ifnet(ifp, m); 271#endif 272 ifp->if_ibytes += m->m_pkthdr.len; 273 274 if (ng_atm_input_p != NULL) { 275 (*ng_atm_input_p)(ifp, &m, ah, rxhand); 276 if (m == NULL) 277 return; 278 } 279 280 /* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */ 281 if (atm_harp_input_p != NULL) { 282 (*atm_harp_input_p)(ifp, &m, ah, rxhand); 283 if (m == NULL) 284 return; 285 } 286 287 if (rxhand) { 288#ifdef NATM 289 struct natmpcb *npcb; 290 291 /* 292 * XXXRW: this use of 'rxhand' is not a very good idea, and 293 * was subject to races even before SMPng due to the release 294 * of spl here. 295 */ 296 NATM_LOCK(); 297 npcb = rxhand; 298 npcb->npcb_inq++; /* count # in queue */ 299 isr = NETISR_NATM; 300 m->m_pkthdr.rcvif = rxhand; /* XXX: overload */ 301 NATM_UNLOCK(); 302#else 303 printf("atm_input: NATM detected but not " 304 "configured in kernel\n"); 305 goto dropit; 306#endif 307 } else { 308 /* 309 * handle LLC/SNAP header, if present 310 */ 311 if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) { 312 struct atmllc *alc; 313 314 if (m->m_len < sizeof(*alc) && 315 (m = m_pullup(m, sizeof(*alc))) == 0) 316 return; /* failed */ 317 alc = mtod(m, struct atmllc *); 318 if (bcmp(alc, ATMLLC_HDR, 6)) { 319#if (defined(__FreeBSD__) && __FreeBSD_version >= 501113) || \ 320 defined(__NetBSD__) || defined(__OpenBSD__) 321 printf("%s: recv'd invalid LLC/SNAP frame " 322 "[vp=%d,vc=%d]\n", ifp->if_xname, 323 ATM_PH_VPI(ah), ATM_PH_VCI(ah)); 324#elif defined(__FreeBSD__) || defined(__bsdi__) 325 printf("%s%d: recv'd invalid LLC/SNAP frame " 326 "[vp=%d,vc=%d]\n", ifp->if_name, 327 ifp->if_unit, ATM_PH_VPI(ah), 328 ATM_PH_VCI(ah)); 329#endif 330 m_freem(m); 331 return; 332 } 333 etype = ATM_LLC_TYPE(alc); 334 m_adj(m, sizeof(*alc)); 335 } 336 337 switch (etype) { 338 339#ifdef INET 340 case ETHERTYPE_IP: 341 isr = NETISR_IP; 342 break; 343#endif 344 345#ifdef INET6 346 case ETHERTYPE_IPV6: 347 isr = NETISR_IPV6; 348 break; 349#endif 350 default: 351#ifndef NATM 352 dropit: 353#endif 354 if (ng_atm_input_orphan_p != NULL) 355 (*ng_atm_input_orphan_p)(ifp, m, ah, rxhand); 356 else 357 m_freem(m); 358 return; 359 } 360 } 361 netisr_dispatch(isr, m); 362} 363 364/* 365 * Perform common duties while attaching to interface list. 366 */ 367void 368atm_ifattach(struct ifnet *ifp) 369{ 370 struct ifaddr *ifa; 371 struct sockaddr_dl *sdl; 372 struct ifatm *ifatm = ifp->if_l2com; 373 374 ifp->if_addrlen = 0; 375 ifp->if_hdrlen = 0; 376 if_attach(ifp); 377 ifp->if_mtu = ATMMTU; 378 ifp->if_output = atm_output; 379#if 0 380 ifp->if_input = atm_input; 381#endif 382 ifp->if_snd.ifq_maxlen = 50; /* dummy */ 383 384#if defined(__NetBSD__) || defined(__OpenBSD__) 385 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) 386#elif defined(__FreeBSD__) && (__FreeBSD__ > 2) 387 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 388#elif defined(__FreeBSD__) || defined(__bsdi__) 389 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) 390#endif 391 if (ifa->ifa_addr->sa_family == AF_LINK) { 392 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 393 sdl->sdl_type = IFT_ATM; 394 sdl->sdl_alen = ifp->if_addrlen; 395#ifdef notyet /* if using ATMARP, store hardware address using the next line */ 396 bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen); 397#endif 398 break; 399 } 400 401 ifp->if_linkmib = &ifatm->mib; 402 ifp->if_linkmiblen = sizeof(ifatm->mib); 403 404 if(ng_atm_attach_p) 405 (*ng_atm_attach_p)(ifp); 406 if (atm_harp_attach_p) 407 (*atm_harp_attach_p)(ifp); 408} 409 410/* 411 * Common stuff for detaching an ATM interface 412 */ 413void 414atm_ifdetach(struct ifnet *ifp) 415{ 416 if (atm_harp_detach_p) 417 (*atm_harp_detach_p)(ifp); 418 if(ng_atm_detach_p) 419 (*ng_atm_detach_p)(ifp); 420 if_detach(ifp); 421} 422 423/* 424 * Support routine for the SIOCATMGVCCS ioctl(). 425 * 426 * This routine assumes, that the private VCC structures used by the driver 427 * begin with a struct atmio_vcc. 428 * 429 * Return a table of VCCs in a freshly allocated memory area. 430 * Here we have a problem: we first count, how many vccs we need 431 * to return. The we allocate the memory and finally fill it in. 432 * Because we cannot lock while calling malloc, the number of active 433 * vccs may change while we're in malloc. So we allocate a couple of 434 * vccs more and if space anyway is not enough re-iterate. 435 * 436 * We could use an sx lock for the vcc tables. 437 */ 438struct atmio_vcctable * 439atm_getvccs(struct atmio_vcc **table, u_int size, u_int start, 440 struct mtx *lock, int waitok) 441{ 442 u_int cid, alloc; 443 size_t len; 444 struct atmio_vcctable *vccs; 445 struct atmio_vcc *v; 446 447 alloc = start + 10; 448 vccs = NULL; 449 450 for (;;) { 451 len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]); 452 vccs = reallocf(vccs, len, M_TEMP, 453 waitok ? M_WAITOK : M_NOWAIT); 454 if (vccs == NULL) 455 return (NULL); 456 bzero(vccs, len); 457 458 vccs->count = 0; 459 v = vccs->vccs; 460 461 mtx_lock(lock); 462 for (cid = 0; cid < size; cid++) 463 if (table[cid] != NULL) { 464 if (++vccs->count == alloc) 465 /* too many - try again */ 466 break; 467 *v++ = *table[cid]; 468 } 469 mtx_unlock(lock); 470 471 if (cid == size) 472 break; 473 474 alloc *= 2; 475 } 476 return (vccs); 477} 478 479/* 480 * Driver or channel state has changed. Inform whoever is interested 481 * in these events. 482 */ 483void 484atm_event(struct ifnet *ifp, u_int event, void *arg) 485{ 486 if (ng_atm_event_p != NULL) 487 (*ng_atm_event_p)(ifp, event, arg); 488 if (atm_harp_event_p != NULL) 489 (*atm_harp_event_p)(ifp, event, arg); 490} 491 492static void * 493atm_alloc(u_char type, struct ifnet *ifp) 494{ 495 struct ifatm *ifatm; 496 497 ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO); 498 ifatm->ifp = ifp; 499 500 return (ifatm); 501} 502 503static void 504atm_free(void *com, u_char type) 505{ 506 507 free(com, M_IFATM); 508} 509 510static int 511atm_modevent(module_t mod, int type, void *data) 512{ 513 switch (type) { 514 case MOD_LOAD: 515 if_register_com_alloc(IFT_ATM, atm_alloc, atm_free); 516 break; 517 case MOD_UNLOAD: 518 if_deregister_com_alloc(IFT_ATM); 519 break; 520 default: 521 return (EOPNOTSUPP); 522 } 523 524 return (0); 525} 526 527static moduledata_t atm_mod = { 528 "atm", 529 atm_modevent, 530 0 531}; 532 533DECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); 534MODULE_VERSION(atm, 1); 535