if_atmsubr.c revision 243882
1139804Simp/* $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $ */ 240711Swollman 340711Swollman/*- 440711Swollman * 540711Swollman * Copyright (c) 1996 Charles D. Cranor and Washington University. 640711Swollman * All rights reserved. 740711Swollman * 840711Swollman * Redistribution and use in source and binary forms, with or without 940711Swollman * modification, are permitted provided that the following conditions 1040711Swollman * are met: 1140711Swollman * 1. Redistributions of source code must retain the above copyright 1240711Swollman * notice, this list of conditions and the following disclaimer. 1340711Swollman * 2. Redistributions in binary form must reproduce the above copyright 1440711Swollman * notice, this list of conditions and the following disclaimer in the 15152543Syongari * documentation and/or other materials provided with the distribution. 1640711Swollman * 3. All advertising materials mentioning features or use of this software 1740711Swollman * must display the following acknowledgement: 1840711Swollman * This product includes software developed by Charles D. Cranor and 1940711Swollman * Washington University. 2040711Swollman * 4. The name of the author may not be used to endorse or promote products 2140711Swollman * derived from this software without specific prior written permission. 2240711Swollman * 2340711Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2440711Swollman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2540711Swollman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2640711Swollman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2740711Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2840711Swollman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2940711Swollman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3040711Swollman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3140711Swollman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3240711Swollman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3340711Swollman * 3440711Swollman * if_atmsubr.c 3540711Swollman */ 3640711Swollman 3740711Swollman#include <sys/cdefs.h> 3840711Swollman__FBSDID("$FreeBSD: head/sys/net/if_atmsubr.c 243882 2012-12-05 08:04:20Z glebius $"); 3940711Swollman 4040711Swollman#include "opt_inet.h" 4140711Swollman#include "opt_inet6.h" 4240711Swollman#include "opt_natm.h" 4340711Swollman 4440711Swollman#include <sys/param.h> 4540711Swollman#include <sys/systm.h> 4640711Swollman#include <sys/kernel.h> 4740711Swollman#include <sys/module.h> 4840711Swollman#include <sys/mbuf.h> 4940711Swollman#include <sys/socket.h> 5040711Swollman#include <sys/sockio.h> 5140711Swollman#include <sys/errno.h> 5240711Swollman#include <sys/sysctl.h> 5340711Swollman#include <sys/malloc.h> 5440711Swollman 5540711Swollman#include <net/if.h> 5640711Swollman#include <net/netisr.h> 5740711Swollman#include <net/route.h> 58168791Sjhb#include <net/if_dl.h> 59168791Sjhb#include <net/if_types.h> 60116182Sobrien#include <net/if_atm.h> 61116182Sobrien 62116182Sobrien#include <netinet/in.h> 6340711Swollman#include <netinet/if_atm.h> 6440711Swollman#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */ 6541304Sbde#if defined(INET) || defined(INET6) 66164881Sjhb#include <netinet/in_var.h> 6740711Swollman#endif 6840711Swollman#ifdef NATM 6971576Sjasone#include <netnatm/natm.h> 7045720Speter#endif 7145720Speter 7240711Swollman#include <security/mac/mac_framework.h> 73102962Siwasaki 7440711Swollman/* 75168791Sjhb * Netgraph interface functions. 76168791Sjhb * These need not be protected by a lock, because ng_atm nodes are persitent. 77168791Sjhb * The ng_atm module can be unloaded only if all ATM interfaces have been 78168791Sjhb * unloaded, so nobody should be in the code paths accessing these function 79151037Sphk * pointers. 80151037Sphk */ 81151037Sphkvoid (*ng_atm_attach_p)(struct ifnet *); 82151037Sphkvoid (*ng_atm_detach_p)(struct ifnet *); 83151037Sphkint (*ng_atm_output_p)(struct ifnet *, struct mbuf **); 84151037Sphkvoid (*ng_atm_input_p)(struct ifnet *, struct mbuf **, 85151037Sphk struct atm_pseudohdr *, void *); 86151037Sphkvoid (*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *, 87151037Sphk struct atm_pseudohdr *, void *); 88151037Sphkvoid (*ng_atm_event_p)(struct ifnet *, uint32_t, void *); 89151037Sphk 90151037Sphk/* 91151037Sphk * Harp pseudo interface hooks 92151037Sphk */ 93151037Sphkvoid (*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m, 94151037Sphk struct atm_pseudohdr *ah, void *rxhand); 95151037Sphkvoid (*atm_harp_attach_p)(struct ifnet *); 96151037Sphkvoid (*atm_harp_detach_p)(struct ifnet *); 97151037Sphkvoid (*atm_harp_event_p)(struct ifnet *, uint32_t, void *); 98151037Sphk 99151037SphkSYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware"); 100151037Sphk 101151037Sphkstatic MALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals"); 102188061Simp 103102962Siwasaki#ifndef ETHERTYPE_IPV6 104102962Siwasaki#define ETHERTYPE_IPV6 0x86dd 105102962Siwasaki#endif 10659910Spaul 107102962Siwasaki#define senderr(e) do { error = (e); goto bad; } while (0) 108102962Siwasaki 10945569Seivind/* 11040711Swollman * atm_output: ATM output routine 11140711Swollman * inputs: 11271576Sjasone * "ifp" = ATM interface to output to 113150523Sphk * "m0" = the packet to output 114150523Sphk * "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI) 115150523Sphk * "ro" = the route to use 116150523Sphk * returns: error code [0 == ok] 11740711Swollman * 118150523Sphk * note: special semantic: if (dst == NULL) then we assume "m" already 119150523Sphk * has an atm_pseudohdr on it and just send it directly. 120150523Sphk * [for native mode ATM output] if dst is null, then 121150523Sphk * ro->ro_rt must also be NULL. 122150523Sphk */ 123150523Sphkint 124150523Sphkatm_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, 125150523Sphk struct route *ro) 126150523Sphk{ 127150523Sphk u_int16_t etype = 0; /* if using LLC/SNAP */ 128150523Sphk int error = 0, sz; 129150523Sphk struct atm_pseudohdr atmdst, *ad; 13040711Swollman struct mbuf *m = m0; 13140711Swollman struct atmllc *atmllc; 13240711Swollman struct atmllc *llc_hdr = NULL; 133152543Syongari u_int32_t atm_flags; 13440711Swollman 13540711Swollman#ifdef MAC 13640711Swollman error = mac_ifnet_check_transmit(ifp, m); 13740711Swollman if (error) 13893818Sjhb senderr(error); 13940711Swollman#endif 14040711Swollman 141221218Sjhb if (!((ifp->if_flags & IFF_UP) && 142221218Sjhb (ifp->if_drv_flags & IFF_DRV_RUNNING))) 14340711Swollman senderr(ENETDOWN); 14440711Swollman 14540711Swollman /* 14640711Swollman * check for non-native ATM traffic (dst != NULL) 14740711Swollman */ 14868727Smckusick if (dst) { 14984781Sjhb switch (dst->sa_family) { 150152543Syongari 15140711Swollman#if defined(INET) || defined(INET6) 15293818Sjhb case AF_INET: 15340711Swollman case AF_INET6: 15472200Sbmilekic { 15540711Swollman if (dst->sa_family == AF_INET6) 15672200Sbmilekic etype = ETHERTYPE_IPV6; 15740711Swollman else 15840711Swollman etype = ETHERTYPE_IP; 15940711Swollman if (!atmresolve(ro->ro_rt, m, dst, &atmdst)) { 16040711Swollman m = NULL; 16140711Swollman /* XXX: atmresolve already free'd it */ 16240711Swollman senderr(EHOSTUNREACH); 163162224Sjhb /* XXX: put ATMARP stuff here */ 164236359Simp /* XXX: watch who frees m on failure */ 16540711Swollman } 166134040Snjl } 167134021Snjl break; 168221218Sjhb#endif /* INET || INET6 */ 169221218Sjhb 170150523Sphk case AF_UNSPEC: 171152543Syongari /* 17240711Swollman * XXX: bpfwrite. assuming dst contains 12 bytes 17340711Swollman * (atm pseudo header (4) + LLC/SNAP (8)) 17440711Swollman */ 17540711Swollman bcopy(dst->sa_data, &atmdst, sizeof(atmdst)); 17640711Swollman llc_hdr = (struct atmllc *)(dst->sa_data + 17772200Sbmilekic sizeof(atmdst)); 178162224Sjhb break; 179162224Sjhb 180164881Sjhb default: 181164881Sjhb printf("%s: can't handle af%d\n", ifp->if_xname, 182164881Sjhb dst->sa_family); 183164881Sjhb senderr(EAFNOSUPPORT); 184164881Sjhb } 185164881Sjhb 18640711Swollman /* 187162224Sjhb * must add atm_pseudohdr to data 18868727Smckusick */ 18968727Smckusick sz = sizeof(atmdst); 19040711Swollman atm_flags = ATM_PH_FLAGS(&atmdst); 191162224Sjhb if (atm_flags & ATM_PH_LLCSNAP) 192236359Simp sz += 8; /* sizeof snap == 8 */ 193236359Simp M_PREPEND(m, sz, M_NOWAIT); 194236359Simp if (m == 0) 195236359Simp senderr(ENOBUFS); 196162224Sjhb ad = mtod(m, struct atm_pseudohdr *); 197162224Sjhb *ad = atmdst; 198162224Sjhb if (atm_flags & ATM_PH_LLCSNAP) { 199236359Simp atmllc = (struct atmllc *)(ad + 1); 200236359Simp if (llc_hdr == NULL) { 201236359Simp bcopy(ATMLLC_HDR, atmllc->llchdr, 202236359Simp sizeof(atmllc->llchdr)); 203162224Sjhb /* note: in host order */ 204162224Sjhb ATM_LLC_SETTYPE(atmllc, etype); 205162224Sjhb } 206162224Sjhb else 207162224Sjhb bcopy(llc_hdr, atmllc, sizeof(struct atmllc)); 208162224Sjhb } 209162224Sjhb } 210162224Sjhb 211162224Sjhb if (ng_atm_output_p != NULL) { 212162224Sjhb if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) { 213162224Sjhb if (m != NULL) 214162224Sjhb m_freem(m); 215162224Sjhb return (error); 216162224Sjhb } 217162224Sjhb if (m == NULL) 218162224Sjhb return (0); 219162224Sjhb } 220162224Sjhb 221162224Sjhb /* 222162224Sjhb * Queue message on interface, and start output if interface 223166932Sscottl * not yet active. 224166932Sscottl */ 225166932Sscottl if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, 226166932Sscottl -(int)sizeof(struct atm_pseudohdr))) 227166932Sscottl return (ENOBUFS); 228166932Sscottl return (error); 229162224Sjhb 230166932Sscottlbad: 231162224Sjhb if (m) 23240711Swollman m_freem(m); 233236359Simp return (error); 23472200Sbmilekic} 235236359Simp 23640711Swollman/* 23740711Swollman * Process a received ATM packet; 23840711Swollman * the packet is in the mbuf chain m. 239159536Simp */ 240159536Simpvoid 241159536Simpatm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m, 242159536Simp void *rxhand) 243159536Simp{ 244159536Simp int isr; 245159536Simp u_int16_t etype = ETHERTYPE_IP; /* default */ 246159536Simp 247159536Simp if ((ifp->if_flags & IFF_UP) == 0) { 248159536Simp m_freem(m); 24940711Swollman return; 25040711Swollman } 251150523Sphk#ifdef MAC 25240711Swollman mac_ifnet_create_mbuf(ifp, m); 25372200Sbmilekic#endif 25468727Smckusick ifp->if_ibytes += m->m_pkthdr.len; 25545720Speter 25672200Sbmilekic if (ng_atm_input_p != NULL) { 25740711Swollman (*ng_atm_input_p)(ifp, &m, ah, rxhand); 25845720Speter if (m == NULL) 25940711Swollman return; 26040711Swollman } 26140711Swollman 26240711Swollman /* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */ 26340711Swollman if (atm_harp_input_p != NULL) { 26440711Swollman (*atm_harp_input_p)(ifp, &m, ah, rxhand); 26568727Smckusick if (m == NULL) 26668727Smckusick return; 26768727Smckusick } 26840711Swollman 26940711Swollman if (rxhand) { 27072200Sbmilekic#ifdef NATM 27172200Sbmilekic struct natmpcb *npcb; 27240711Swollman 27372200Sbmilekic /* 27471576Sjasone * XXXRW: this use of 'rxhand' is not a very good idea, and 27571576Sjasone * was subject to races even before SMPng due to the release 27640711Swollman * of spl here. 27740711Swollman */ 27840711Swollman NATM_LOCK(); 27940711Swollman npcb = rxhand; 280221220Sjhb npcb->npcb_inq++; /* count # in queue */ 281221220Sjhb isr = NETISR_NATM; 282221220Sjhb m->m_pkthdr.rcvif = rxhand; /* XXX: overload */ 283221220Sjhb NATM_UNLOCK(); 284221220Sjhb#else 285221220Sjhb printf("atm_input: NATM detected but not " 286221220Sjhb "configured in kernel\n"); 287221220Sjhb goto dropit; 288221220Sjhb#endif 289221220Sjhb } else { 290221220Sjhb /* 291221220Sjhb * handle LLC/SNAP header, if present 292221220Sjhb */ 293221220Sjhb if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) { 294221220Sjhb struct atmllc *alc; 295221220Sjhb 296221220Sjhb if (m->m_len < sizeof(*alc) && 297221220Sjhb (m = m_pullup(m, sizeof(*alc))) == 0) 298221220Sjhb return; /* failed */ 299221220Sjhb alc = mtod(m, struct atmllc *); 300221220Sjhb if (bcmp(alc, ATMLLC_HDR, 6)) { 301221220Sjhb printf("%s: recv'd invalid LLC/SNAP frame " 302221220Sjhb "[vp=%d,vc=%d]\n", ifp->if_xname, 303221220Sjhb ATM_PH_VPI(ah), ATM_PH_VCI(ah)); 304221220Sjhb m_freem(m); 305221220Sjhb return; 306221220Sjhb } 307221220Sjhb etype = ATM_LLC_TYPE(alc); 308221220Sjhb m_adj(m, sizeof(*alc)); 309221220Sjhb } 310221220Sjhb 311221220Sjhb switch (etype) { 312221220Sjhb 313221220Sjhb#ifdef INET 314221220Sjhb case ETHERTYPE_IP: 315221220Sjhb isr = NETISR_IP; 316221220Sjhb break; 317221220Sjhb#endif 318221220Sjhb 319221220Sjhb#ifdef INET6 320221220Sjhb case ETHERTYPE_IPV6: 321221220Sjhb isr = NETISR_IPV6; 322221220Sjhb break; 323221220Sjhb#endif 324221220Sjhb default: 325221220Sjhb#ifndef NATM 326221220Sjhb dropit: 327221220Sjhb#endif 328221220Sjhb if (ng_atm_input_orphan_p != NULL) 329221220Sjhb (*ng_atm_input_orphan_p)(ifp, m, ah, rxhand); 330221220Sjhb else 331221220Sjhb m_freem(m); 332221220Sjhb return; 333221220Sjhb } 334221220Sjhb } 335221220Sjhb M_SETFIB(m, ifp->if_fib); 336221220Sjhb netisr_dispatch(isr, m); 337221220Sjhb} 338221220Sjhb 339221220Sjhb/* 340221220Sjhb * Perform common duties while attaching to interface list. 341221220Sjhb */ 342221220Sjhbvoid 343221220Sjhbatm_ifattach(struct ifnet *ifp) 344221220Sjhb{ 345221220Sjhb struct ifaddr *ifa; 346221220Sjhb struct sockaddr_dl *sdl; 347221220Sjhb struct ifatm *ifatm = ifp->if_l2com; 348221220Sjhb 349221220Sjhb ifp->if_addrlen = 0; 350221220Sjhb ifp->if_hdrlen = 0; 351221220Sjhb if_attach(ifp); 352221220Sjhb ifp->if_mtu = ATMMTU; 353221220Sjhb ifp->if_output = atm_output; 354221220Sjhb#if 0 355221220Sjhb ifp->if_input = atm_input; 356221220Sjhb#endif 357221220Sjhb ifp->if_snd.ifq_maxlen = 50; /* dummy */ 358221220Sjhb 359221220Sjhb TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 360221220Sjhb if (ifa->ifa_addr->sa_family == AF_LINK) { 361221220Sjhb sdl = (struct sockaddr_dl *)ifa->ifa_addr; 362221220Sjhb sdl->sdl_type = IFT_ATM; 363221220Sjhb sdl->sdl_alen = ifp->if_addrlen; 364221220Sjhb#ifdef notyet /* if using ATMARP, store hardware address using the next line */ 365221220Sjhb bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen); 366221220Sjhb#endif 367221220Sjhb break; 368221220Sjhb } 369221220Sjhb 370221220Sjhb ifp->if_linkmib = &ifatm->mib; 371221220Sjhb ifp->if_linkmiblen = sizeof(ifatm->mib); 372221220Sjhb 373221220Sjhb if(ng_atm_attach_p) 374221220Sjhb (*ng_atm_attach_p)(ifp); 375221220Sjhb if (atm_harp_attach_p) 376221220Sjhb (*atm_harp_attach_p)(ifp); 377221220Sjhb} 378221220Sjhb 379221220Sjhb/* 380221220Sjhb * Common stuff for detaching an ATM interface 381221220Sjhb */ 382221220Sjhbvoid 383221220Sjhbatm_ifdetach(struct ifnet *ifp) 384221220Sjhb{ 385221220Sjhb if (atm_harp_detach_p) 386221220Sjhb (*atm_harp_detach_p)(ifp); 387221220Sjhb if(ng_atm_detach_p) 388221220Sjhb (*ng_atm_detach_p)(ifp); 389221220Sjhb if_detach(ifp); 390221220Sjhb} 391221220Sjhb 392221220Sjhb/* 393221220Sjhb * Support routine for the SIOCATMGVCCS ioctl(). 394221220Sjhb * 395221220Sjhb * This routine assumes, that the private VCC structures used by the driver 396221220Sjhb * begin with a struct atmio_vcc. 397221220Sjhb * 398221220Sjhb * Return a table of VCCs in a freshly allocated memory area. 399221220Sjhb * Here we have a problem: we first count, how many vccs we need 400221220Sjhb * to return. The we allocate the memory and finally fill it in. 401221220Sjhb * Because we cannot lock while calling malloc, the number of active 402221220Sjhb * vccs may change while we're in malloc. So we allocate a couple of 403221220Sjhb * vccs more and if space anyway is not enough re-iterate. 404221220Sjhb * 405221220Sjhb * We could use an sx lock for the vcc tables. 406221220Sjhb */ 407221220Sjhbstruct atmio_vcctable * 408221220Sjhbatm_getvccs(struct atmio_vcc **table, u_int size, u_int start, 409221220Sjhb struct mtx *lock, int waitok) 410221220Sjhb{ 411221220Sjhb u_int cid, alloc; 412221220Sjhb size_t len; 413221220Sjhb struct atmio_vcctable *vccs; 414221220Sjhb struct atmio_vcc *v; 415221220Sjhb 416221220Sjhb alloc = start + 10; 417221220Sjhb vccs = NULL; 418221220Sjhb 419221220Sjhb for (;;) { 420221220Sjhb len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]); 421221220Sjhb vccs = reallocf(vccs, len, M_TEMP, 422221220Sjhb waitok ? M_WAITOK : M_NOWAIT); 423221220Sjhb if (vccs == NULL) 424221220Sjhb return (NULL); 425221220Sjhb bzero(vccs, len); 426221220Sjhb 427221220Sjhb vccs->count = 0; 428221220Sjhb v = vccs->vccs; 429221220Sjhb 430221220Sjhb mtx_lock(lock); 431221220Sjhb for (cid = 0; cid < size; cid++) 432221220Sjhb if (table[cid] != NULL) { 433221220Sjhb if (++vccs->count == alloc) 434221220Sjhb /* too many - try again */ 435221220Sjhb break; 436221220Sjhb *v++ = *table[cid]; 437221220Sjhb } 43840711Swollman mtx_unlock(lock); 43988372Stmm 44088372Stmm if (cid == size) 44188372Stmm break; 44240711Swollman 44340711Swollman alloc *= 2; 444150523Sphk } 44588372Stmm return (vccs); 44640711Swollman} 447152543Syongari 44840711Swollman/* 449160958Sjb * Driver or channel state has changed. Inform whoever is interested 450160958Sjb * in these events. 451160958Sjb */ 452160958Sjbvoid 45340711Swollmanatm_event(struct ifnet *ifp, u_int event, void *arg) 45440711Swollman{ 45540711Swollman if (ng_atm_event_p != NULL) 45672200Sbmilekic (*ng_atm_event_p)(ifp, event, arg); 45740711Swollman if (atm_harp_event_p != NULL) 458152543Syongari (*atm_harp_event_p)(ifp, event, arg); 459265363Struckman} 46068727Smckusick 46140711Swollmanstatic void * 46240711Swollmanatm_alloc(u_char type, struct ifnet *ifp) 46368727Smckusick{ 46459910Spaul struct ifatm *ifatm; 46540711Swollman 46640711Swollman ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO); 46740711Swollman ifatm->ifp = ifp; 46888372Stmm 469265931Struckman return (ifatm); 470265931Struckman} 471265363Struckman 472265363Struckmanstatic void 473265363Struckmanatm_free(void *com, u_char type) 47488372Stmm{ 47588372Stmm 47640711Swollman free(com, M_IFATM); 47740711Swollman} 47840711Swollman 47968727Smckusickstatic int 48059910Spaulatm_modevent(module_t mod, int type, void *data) 481265363Struckman{ 482265363Struckman switch (type) { 483265363Struckman case MOD_LOAD: 484265363Struckman if_register_com_alloc(IFT_ATM, atm_alloc, atm_free); 485265363Struckman break; 486143665Simp case MOD_UNLOAD: 487143665Simp if_deregister_com_alloc(IFT_ATM); 48840711Swollman break; 48940711Swollman default: 490265931Struckman return (EOPNOTSUPP); 491265931Struckman } 492265363Struckman 493265363Struckman return (0); 494265363Struckman} 49540711Swollman 49659910Spaulstatic moduledata_t atm_mod = { 49740711Swollman "atm", 49840711Swollman atm_modevent, 49988372Stmm 0 50088372Stmm}; 50188372Stmm 50288372StmmDECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); 50388372StmmMODULE_VERSION(atm, 1); 50488372Stmm