125603Skjc/*      $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $       */
225603Skjc
3139823Simp/*-
425603Skjc *
525603Skjc * Copyright (c) 1996 Charles D. Cranor and Washington University.
625603Skjc * All rights reserved.
725603Skjc *
825603Skjc * Redistribution and use in source and binary forms, with or without
925603Skjc * modification, are permitted provided that the following conditions
1025603Skjc * are met:
1125603Skjc * 1. Redistributions of source code must retain the above copyright
1225603Skjc *    notice, this list of conditions and the following disclaimer.
1325603Skjc * 2. Redistributions in binary form must reproduce the above copyright
1425603Skjc *    notice, this list of conditions and the following disclaimer in the
1525603Skjc *    documentation and/or other materials provided with the distribution.
1625603Skjc * 3. All advertising materials mentioning features or use of this software
1725603Skjc *    must display the following acknowledgement:
1825603Skjc *      This product includes software developed by Charles D. Cranor and
1925603Skjc *	Washington University.
2025603Skjc * 4. The name of the author may not be used to endorse or promote products
2125603Skjc *    derived from this software without specific prior written permission.
2225603Skjc *
2325603Skjc * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2425603Skjc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2525603Skjc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2625603Skjc * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2725603Skjc * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2825603Skjc * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2925603Skjc * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3025603Skjc * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3125603Skjc * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3225603Skjc * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3354263Sshin *
3425603Skjc * if_atmsubr.c
3525603Skjc */
3625603Skjc
37116720Sharti#include <sys/cdefs.h>
38116720Sharti__FBSDID("$FreeBSD$");
39116720Sharti
4032350Seivind#include "opt_inet.h"
4154263Sshin#include "opt_inet6.h"
4232925Seivind#include "opt_natm.h"
4332350Seivind
4425603Skjc#include <sys/param.h>
4525603Skjc#include <sys/systm.h>
46114201Sharti#include <sys/kernel.h>
47114201Sharti#include <sys/module.h>
4825603Skjc#include <sys/mbuf.h>
4925603Skjc#include <sys/socket.h>
5037939Skjc#include <sys/sockio.h>
5137939Skjc#include <sys/errno.h>
52114201Sharti#include <sys/sysctl.h>
53117630Sharti#include <sys/malloc.h>
5425603Skjc
5525603Skjc#include <net/if.h>
56257176Sglebius#include <net/if_var.h>
5725603Skjc#include <net/netisr.h>
5825603Skjc#include <net/route.h>
5925603Skjc#include <net/if_dl.h>
6025603Skjc#include <net/if_types.h>
6125603Skjc#include <net/if_atm.h>
6225603Skjc
6325603Skjc#include <netinet/in.h>
6425603Skjc#include <netinet/if_atm.h>
6525603Skjc#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */
6637939Skjc#if defined(INET) || defined(INET6)
6725603Skjc#include <netinet/in_var.h>
6825603Skjc#endif
6925603Skjc#ifdef NATM
7025603Skjc#include <netnatm/natm.h>
7125603Skjc#endif
7225603Skjc
73163606Srwatson#include <security/mac/mac_framework.h>
74163606Srwatson
75116741Sharti/*
76116741Sharti * Netgraph interface functions.
77116741Sharti * These need not be protected by a lock, because ng_atm nodes are persitent.
78116741Sharti * The ng_atm module can be unloaded only if all ATM interfaces have been
79116741Sharti * unloaded, so nobody should be in the code paths accessing these function
80116741Sharti * pointers.
81116741Sharti */
82116741Shartivoid	(*ng_atm_attach_p)(struct ifnet *);
83116741Shartivoid	(*ng_atm_detach_p)(struct ifnet *);
84116741Shartiint	(*ng_atm_output_p)(struct ifnet *, struct mbuf **);
85116741Shartivoid	(*ng_atm_input_p)(struct ifnet *, struct mbuf **,
86116741Sharti	    struct atm_pseudohdr *, void *);
87116741Shartivoid	(*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
88116741Sharti	    struct atm_pseudohdr *, void *);
89118157Shartivoid	(*ng_atm_event_p)(struct ifnet *, uint32_t, void *);
90116741Sharti
91116741Sharti/*
92116741Sharti * Harp pseudo interface hooks
93116741Sharti */
94116741Shartivoid	(*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m,
95116741Sharti	    struct atm_pseudohdr *ah, void *rxhand);
96116741Shartivoid	(*atm_harp_attach_p)(struct ifnet *);
97116741Shartivoid	(*atm_harp_detach_p)(struct ifnet *);
98118157Shartivoid	(*atm_harp_event_p)(struct ifnet *, uint32_t, void *);
99116741Sharti
100114201ShartiSYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware");
101114201Sharti
102227293Sedstatic MALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals");
103147256Sbrooks
10437939Skjc#ifndef ETHERTYPE_IPV6
105116720Sharti#define	ETHERTYPE_IPV6	0x86dd
10637939Skjc#endif
10725603Skjc
108116720Sharti#define	senderr(e) do { error = (e); goto bad; } while (0)
10925603Skjc
11025603Skjc/*
11125603Skjc * atm_output: ATM output routine
11225603Skjc *   inputs:
11325603Skjc *     "ifp" = ATM interface to output to
11425603Skjc *     "m0" = the packet to output
11525603Skjc *     "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI)
116191148Skmacy *     "ro" = the route to use
11725603Skjc *   returns: error code   [0 == ok]
11825603Skjc *
11925603Skjc *   note: special semantic: if (dst == NULL) then we assume "m" already
12025603Skjc *		has an atm_pseudohdr on it and just send it directly.
12125603Skjc *		[for native mode ATM output]   if dst is null, then
122191148Skmacy *		ro->ro_rt must also be NULL.
12325603Skjc */
12425603Skjcint
125249925Sglebiusatm_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
126191148Skmacy    struct route *ro)
12725603Skjc{
12825603Skjc	u_int16_t etype = 0;			/* if using LLC/SNAP */
12978249Speter	int error = 0, sz;
13025603Skjc	struct atm_pseudohdr atmdst, *ad;
13159633Skjc	struct mbuf *m = m0;
13225603Skjc	struct atmllc *atmllc;
133249925Sglebius	const struct atmllc *llc_hdr = NULL;
13425603Skjc	u_int32_t atm_flags;
13525603Skjc
136105576Srwatson#ifdef MAC
137172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
138105576Srwatson	if (error)
139105576Srwatson		senderr(error);
140105576Srwatson#endif
141105576Srwatson
142148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
143148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
14425603Skjc		senderr(ENETDOWN);
14525603Skjc
14625603Skjc	/*
14725603Skjc	 * check for non-native ATM traffic   (dst != NULL)
14825603Skjc	 */
14925603Skjc	if (dst) {
15025603Skjc		switch (dst->sa_family) {
151116720Sharti
15237939Skjc#if defined(INET) || defined(INET6)
15325603Skjc		case AF_INET:
15437939Skjc		case AF_INET6:
155128636Sluigi		{
15646695Skjc			if (dst->sa_family == AF_INET6)
157112193Sharti			        etype = ETHERTYPE_IPV6;
15846695Skjc			else
159112193Sharti			        etype = ETHERTYPE_IP;
160191148Skmacy			if (!atmresolve(ro->ro_rt, m, dst, &atmdst)) {
16125603Skjc				m = NULL;
16225603Skjc				/* XXX: atmresolve already free'd it */
16325603Skjc				senderr(EHOSTUNREACH);
16425603Skjc				/* XXX: put ATMARP stuff here */
16525603Skjc				/* XXX: watch who frees m on failure */
16625603Skjc			}
167128636Sluigi		}
16825603Skjc			break;
16937939Skjc#endif /* INET || INET6 */
17025603Skjc
17137939Skjc		case AF_UNSPEC:
17237939Skjc			/*
17346695Skjc			 * XXX: bpfwrite. assuming dst contains 12 bytes
17446695Skjc			 * (atm pseudo header (4) + LLC/SNAP (8))
17537939Skjc			 */
17637939Skjc			bcopy(dst->sa_data, &atmdst, sizeof(atmdst));
177249925Sglebius			llc_hdr = (const struct atmllc *)(dst->sa_data +
178116720Sharti			    sizeof(atmdst));
17937939Skjc			break;
18037939Skjc
18125603Skjc		default:
18225603Skjc			printf("%s: can't handle af%d\n", ifp->if_xname,
18325603Skjc			    dst->sa_family);
18425603Skjc			senderr(EAFNOSUPPORT);
18525603Skjc		}
18625603Skjc
18725603Skjc		/*
18825603Skjc		 * must add atm_pseudohdr to data
18925603Skjc		 */
19025603Skjc		sz = sizeof(atmdst);
19125603Skjc		atm_flags = ATM_PH_FLAGS(&atmdst);
192116720Sharti		if (atm_flags & ATM_PH_LLCSNAP)
193116720Sharti			sz += 8;	/* sizeof snap == 8 */
194243882Sglebius		M_PREPEND(m, sz, M_NOWAIT);
195298075Spfg		if (m == NULL)
19625603Skjc			senderr(ENOBUFS);
19725603Skjc		ad = mtod(m, struct atm_pseudohdr *);
19825603Skjc		*ad = atmdst;
19925603Skjc		if (atm_flags & ATM_PH_LLCSNAP) {
20025603Skjc			atmllc = (struct atmllc *)(ad + 1);
20137939Skjc			if (llc_hdr == NULL) {
20237939Skjc			        bcopy(ATMLLC_HDR, atmllc->llchdr,
20337939Skjc				      sizeof(atmllc->llchdr));
204116720Sharti				/* note: in host order */
20537939Skjc				ATM_LLC_SETTYPE(atmllc, etype);
20637939Skjc			}
20737939Skjc			else
20837939Skjc			        bcopy(llc_hdr, atmllc, sizeof(struct atmllc));
20925603Skjc		}
21025603Skjc	}
21125603Skjc
212116741Sharti	if (ng_atm_output_p != NULL) {
213116741Sharti		if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) {
214116741Sharti			if (m != NULL)
215116741Sharti				m_freem(m);
216116741Sharti			return (error);
217116741Sharti		}
218116741Sharti		if (m == NULL)
219116741Sharti			return (0);
220116741Sharti	}
221116741Sharti
22225603Skjc	/*
22325603Skjc	 * Queue message on interface, and start output if interface
22425603Skjc	 * not yet active.
22525603Skjc	 */
226117629Sharti	if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp,
227117629Sharti	    -(int)sizeof(struct atm_pseudohdr)))
22869152Sjlemon		return (ENOBUFS);
22925603Skjc	return (error);
23025603Skjc
23125603Skjcbad:
23225603Skjc	if (m)
23325603Skjc		m_freem(m);
23425603Skjc	return (error);
23525603Skjc}
23625603Skjc
23725603Skjc/*
23825603Skjc * Process a received ATM packet;
23925603Skjc * the packet is in the mbuf chain m.
24025603Skjc */
24125603Skjcvoid
242116720Shartiatm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m,
243116720Sharti    void *rxhand)
24425603Skjc{
245111888Sjlemon	int isr;
246116720Sharti	u_int16_t etype = ETHERTYPE_IP;		/* default */
24725603Skjc
24825603Skjc	if ((ifp->if_flags & IFF_UP) == 0) {
24925603Skjc		m_freem(m);
25025603Skjc		return;
25125603Skjc	}
252105576Srwatson#ifdef MAC
253172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
254105576Srwatson#endif
255271867Sglebius	if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
25625603Skjc
257116741Sharti	if (ng_atm_input_p != NULL) {
258116741Sharti		(*ng_atm_input_p)(ifp, &m, ah, rxhand);
259116741Sharti		if (m == NULL)
260116741Sharti			return;
261116741Sharti	}
262116741Sharti
263116741Sharti	/* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */
264116741Sharti	if (atm_harp_input_p != NULL) {
265116741Sharti		(*atm_harp_input_p)(ifp, &m, ah, rxhand);
266116741Sharti		if (m == NULL)
267116741Sharti			return;
268116741Sharti	}
269116741Sharti
27025603Skjc	if (rxhand) {
27125603Skjc#ifdef NATM
272148125Srwatson		struct natmpcb *npcb;
273116720Sharti
274148125Srwatson		/*
275148125Srwatson		 * XXXRW: this use of 'rxhand' is not a very good idea, and
276148125Srwatson		 * was subject to races even before SMPng due to the release
277148125Srwatson		 * of spl here.
278148125Srwatson		 */
279148125Srwatson		NATM_LOCK();
280148125Srwatson		npcb = rxhand;
28137939Skjc		npcb->npcb_inq++;	/* count # in queue */
282111888Sjlemon		isr = NETISR_NATM;
28337939Skjc		m->m_pkthdr.rcvif = rxhand; /* XXX: overload */
284148125Srwatson		NATM_UNLOCK();
28525603Skjc#else
286116720Sharti		printf("atm_input: NATM detected but not "
287116720Sharti		    "configured in kernel\n");
288116741Sharti		goto dropit;
28925603Skjc#endif
29025603Skjc	} else {
29137939Skjc		/*
29237939Skjc		 * handle LLC/SNAP header, if present
29337939Skjc		 */
29437939Skjc		if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) {
29537939Skjc			struct atmllc *alc;
296116720Sharti
29737939Skjc			if (m->m_len < sizeof(*alc) &&
298298075Spfg			    (m = m_pullup(m, sizeof(*alc))) == NULL)
29937939Skjc				return; /* failed */
30037939Skjc			alc = mtod(m, struct atmllc *);
30137939Skjc			if (bcmp(alc, ATMLLC_HDR, 6)) {
302116720Sharti				printf("%s: recv'd invalid LLC/SNAP frame "
303116720Sharti				    "[vp=%d,vc=%d]\n", ifp->if_xname,
304116720Sharti				    ATM_PH_VPI(ah), ATM_PH_VCI(ah));
30537939Skjc				m_freem(m);
30637939Skjc				return;
30737939Skjc			}
30837939Skjc			etype = ATM_LLC_TYPE(alc);
30937939Skjc			m_adj(m, sizeof(*alc));
31037939Skjc		}
31125603Skjc
31237939Skjc		switch (etype) {
313116720Sharti
31425603Skjc#ifdef INET
31537939Skjc		case ETHERTYPE_IP:
316111888Sjlemon			isr = NETISR_IP;
31737939Skjc			break;
31825603Skjc#endif
319116720Sharti
32037939Skjc#ifdef INET6
32137939Skjc		case ETHERTYPE_IPV6:
322111888Sjlemon			isr = NETISR_IPV6;
32337939Skjc			break;
32437939Skjc#endif
32537939Skjc		default:
326116741Sharti#ifndef NATM
327116741Sharti  dropit:
328116741Sharti#endif
329116741Sharti			if (ng_atm_input_orphan_p != NULL)
330116741Sharti				(*ng_atm_input_orphan_p)(ifp, m, ah, rxhand);
331116741Sharti			else
332116741Sharti				m_freem(m);
33337939Skjc			return;
33437939Skjc		}
33525603Skjc	}
336223741Sbz	M_SETFIB(m, ifp->if_fib);
337111888Sjlemon	netisr_dispatch(isr, m);
33825603Skjc}
33925603Skjc
34025603Skjc/*
341114201Sharti * Perform common duties while attaching to interface list.
34225603Skjc */
34325603Skjcvoid
344116720Shartiatm_ifattach(struct ifnet *ifp)
34525603Skjc{
346111774Smdodd	struct ifaddr *ifa;
347111774Smdodd	struct sockaddr_dl *sdl;
348147256Sbrooks	struct ifatm *ifatm = ifp->if_l2com;
34925603Skjc
35025603Skjc	ifp->if_addrlen = 0;
35125603Skjc	ifp->if_hdrlen = 0;
352114201Sharti	if_attach(ifp);
35325603Skjc	ifp->if_mtu = ATMMTU;
35425603Skjc	ifp->if_output = atm_output;
355106939Ssam#if 0
356106939Ssam	ifp->if_input = atm_input;
357106939Ssam#endif
35846695Skjc	ifp->if_snd.ifq_maxlen = 50;	/* dummy */
35925603Skjc
360160035Syar	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
361160038Syar		if (ifa->ifa_addr->sa_family == AF_LINK) {
362160038Syar			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
36325603Skjc			sdl->sdl_type = IFT_ATM;
36425603Skjc			sdl->sdl_alen = ifp->if_addrlen;
36525603Skjc#ifdef notyet /* if using ATMARP, store hardware address using the next line */
36625603Skjc			bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen);
36725603Skjc#endif
36825603Skjc			break;
36925603Skjc		}
37037939Skjc
371114739Sharti	ifp->if_linkmib = &ifatm->mib;
372114739Sharti	ifp->if_linkmiblen = sizeof(ifatm->mib);
373116741Sharti
374116741Sharti	if(ng_atm_attach_p)
375116741Sharti		(*ng_atm_attach_p)(ifp);
376116741Sharti	if (atm_harp_attach_p)
377116741Sharti		(*atm_harp_attach_p)(ifp);
37837939Skjc}
379114201Sharti
380114201Sharti/*
381114201Sharti * Common stuff for detaching an ATM interface
382114201Sharti */
383114201Shartivoid
384114201Shartiatm_ifdetach(struct ifnet *ifp)
385114201Sharti{
386116741Sharti	if (atm_harp_detach_p)
387116741Sharti		(*atm_harp_detach_p)(ifp);
388116741Sharti	if(ng_atm_detach_p)
389116741Sharti		(*ng_atm_detach_p)(ifp);
390114201Sharti	if_detach(ifp);
391114201Sharti}
392114201Sharti
393117630Sharti/*
394117630Sharti * Support routine for the SIOCATMGVCCS ioctl().
395117630Sharti *
396117630Sharti * This routine assumes, that the private VCC structures used by the driver
397117630Sharti * begin with a struct atmio_vcc.
398117630Sharti *
399117630Sharti * Return a table of VCCs in a freshly allocated memory area.
400117630Sharti * Here we have a problem: we first count, how many vccs we need
401117630Sharti * to return. The we allocate the memory and finally fill it in.
402117630Sharti * Because we cannot lock while calling malloc, the number of active
403117630Sharti * vccs may change while we're in malloc. So we allocate a couple of
404117630Sharti * vccs more and if space anyway is not enough re-iterate.
405117630Sharti *
406117630Sharti * We could use an sx lock for the vcc tables.
407117630Sharti */
408117630Shartistruct atmio_vcctable *
409117630Shartiatm_getvccs(struct atmio_vcc **table, u_int size, u_int start,
410117630Sharti    struct mtx *lock, int waitok)
411117630Sharti{
412117630Sharti	u_int cid, alloc;
413117630Sharti	size_t len;
414117630Sharti	struct atmio_vcctable *vccs;
415117630Sharti	struct atmio_vcc *v;
416117630Sharti
417117630Sharti	alloc = start + 10;
418117630Sharti	vccs = NULL;
419117630Sharti
420117630Sharti	for (;;) {
421117630Sharti		len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]);
422117630Sharti		vccs = reallocf(vccs, len, M_TEMP,
423117630Sharti		    waitok ? M_WAITOK : M_NOWAIT);
424117630Sharti		if (vccs == NULL)
425117630Sharti			return (NULL);
426117630Sharti		bzero(vccs, len);
427117630Sharti
428117630Sharti		vccs->count = 0;
429117630Sharti		v = vccs->vccs;
430117630Sharti
431117630Sharti		mtx_lock(lock);
432117630Sharti		for (cid = 0; cid < size; cid++)
433117630Sharti			if (table[cid] != NULL) {
434117630Sharti				if (++vccs->count == alloc)
435117630Sharti					/* too many - try again */
436117630Sharti					break;
437117630Sharti				*v++ = *table[cid];
438117630Sharti			}
439117630Sharti		mtx_unlock(lock);
440117630Sharti
441117630Sharti		if (cid == size)
442117630Sharti			break;
443117630Sharti
444117630Sharti		alloc *= 2;
445117630Sharti	}
446117630Sharti	return (vccs);
447117630Sharti}
448117630Sharti
449118157Sharti/*
450118157Sharti * Driver or channel state has changed. Inform whoever is interested
451118157Sharti * in these events.
452118157Sharti */
453118157Shartivoid
454118157Shartiatm_event(struct ifnet *ifp, u_int event, void *arg)
455118157Sharti{
456118157Sharti	if (ng_atm_event_p != NULL)
457118157Sharti		(*ng_atm_event_p)(ifp, event, arg);
458118157Sharti	if (atm_harp_event_p != NULL)
459118157Sharti		(*atm_harp_event_p)(ifp, event, arg);
460118157Sharti}
461118157Sharti
462147256Sbrooksstatic void *
463147256Sbrooksatm_alloc(u_char type, struct ifnet *ifp)
464147256Sbrooks{
465147256Sbrooks	struct ifatm	*ifatm;
466147256Sbrooks
467147256Sbrooks	ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO);
468147256Sbrooks	ifatm->ifp = ifp;
469147256Sbrooks
470147256Sbrooks	return (ifatm);
471147256Sbrooks}
472147256Sbrooks
473147256Sbrooksstatic void
474147256Sbrooksatm_free(void *com, u_char type)
475147256Sbrooks{
476147256Sbrooks
477147256Sbrooks	free(com, M_IFATM);
478147256Sbrooks}
479147256Sbrooks
480147256Sbrooksstatic int
481147256Sbrooksatm_modevent(module_t mod, int type, void *data)
482147256Sbrooks{
483147256Sbrooks	switch (type) {
484147256Sbrooks	case MOD_LOAD:
485147256Sbrooks		if_register_com_alloc(IFT_ATM, atm_alloc, atm_free);
486147256Sbrooks		break;
487147256Sbrooks	case MOD_UNLOAD:
488147256Sbrooks		if_deregister_com_alloc(IFT_ATM);
489147256Sbrooks		break;
490147256Sbrooks	default:
491147256Sbrooks		return (EOPNOTSUPP);
492147256Sbrooks	}
493147256Sbrooks
494147256Sbrooks	return (0);
495147256Sbrooks}
496147256Sbrooks
497114201Shartistatic moduledata_t atm_mod = {
498114201Sharti        "atm",
499147256Sbrooks        atm_modevent,
500241394Skevlo        0
501114201Sharti};
502114201Sharti
503147256SbrooksDECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
504114201ShartiMODULE_VERSION(atm, 1);
505