if_atmsubr.c revision 148954
120253Sjoerg/*      $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $       */
220302Sjoerg
320302Sjoerg/*-
420253Sjoerg *
520253Sjoerg * Copyright (c) 1996 Charles D. Cranor and Washington University.
620253Sjoerg * All rights reserved.
720253Sjoerg *
820253Sjoerg * Redistribution and use in source and binary forms, with or without
920302Sjoerg * modification, are permitted provided that the following conditions
1020253Sjoerg * are met:
1120253Sjoerg * 1. Redistributions of source code must retain the above copyright
1220253Sjoerg *    notice, this list of conditions and the following disclaimer.
1320253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1420302Sjoerg *    notice, this list of conditions and the following disclaimer in the
1520253Sjoerg *    documentation and/or other materials provided with the distribution.
1620253Sjoerg * 3. All advertising materials mentioning features or use of this software
1720302Sjoerg *    must display the following acknowledgement:
1820253Sjoerg *      This product includes software developed by Charles D. Cranor and
1920253Sjoerg *	Washington University.
2020253Sjoerg * 4. The name of the author may not be used to endorse or promote products
2120253Sjoerg *    derived from this software without specific prior written permission.
2220253Sjoerg *
2320253Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2420253Sjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2520253Sjoerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2620253Sjoerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2730259Scharnier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2830259Scharnier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2950479Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3030259Scharnier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3130259Scharnier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3220253Sjoerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3320253Sjoerg *
3420253Sjoerg * if_atmsubr.c
3520253Sjoerg */
3620253Sjoerg
3720253Sjoerg#include <sys/cdefs.h>
3820253Sjoerg__FBSDID("$FreeBSD: head/sys/net/if_atmsubr.c 148954 2005-08-11 08:14:53Z glebius $");
3944229Sdavidn
4020253Sjoerg#include "opt_inet.h"
4120253Sjoerg#include "opt_inet6.h"
4220253Sjoerg#include "opt_mac.h"
4344229Sdavidn#include "opt_natm.h"
4444229Sdavidn
4520253Sjoerg#include <sys/param.h>
4644229Sdavidn#include <sys/systm.h>
4744229Sdavidn#include <sys/kernel.h>
4844229Sdavidn#include <sys/module.h>
4944229Sdavidn#include <sys/mac.h>
5044229Sdavidn#include <sys/mbuf.h>
5144229Sdavidn#include <sys/socket.h>
5244229Sdavidn#include <sys/sockio.h>
5344229Sdavidn#include <sys/errno.h>
5444229Sdavidn#include <sys/sysctl.h>
5544229Sdavidn#include <sys/malloc.h>
5644229Sdavidn
5744229Sdavidn#include <net/if.h>
5844229Sdavidn#include <net/netisr.h>
5944229Sdavidn#include <net/route.h>
6044229Sdavidn#include <net/if_dl.h>
6144229Sdavidn#include <net/if_types.h>
6244229Sdavidn#include <net/if_atm.h>
6344229Sdavidn
6444229Sdavidn#include <netinet/in.h>
6544229Sdavidn#include <netinet/if_atm.h>
6644229Sdavidn#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */
6744229Sdavidn#if defined(INET) || defined(INET6)
6844229Sdavidn#include <netinet/in_var.h>
6944229Sdavidn#endif
7044229Sdavidn#ifdef NATM
7144229Sdavidn#include <netnatm/natm.h>
7244229Sdavidn#endif
7344229Sdavidn
7444229Sdavidn/*
7544229Sdavidn * Netgraph interface functions.
7644229Sdavidn * These need not be protected by a lock, because ng_atm nodes are persitent.
7744229Sdavidn * The ng_atm module can be unloaded only if all ATM interfaces have been
7844229Sdavidn * unloaded, so nobody should be in the code paths accessing these function
7944229Sdavidn * pointers.
8020747Sdavidn */
8120253Sjoergvoid	(*ng_atm_attach_p)(struct ifnet *);
8220253Sjoergvoid	(*ng_atm_detach_p)(struct ifnet *);
8320253Sjoergint	(*ng_atm_output_p)(struct ifnet *, struct mbuf **);
8420253Sjoergvoid	(*ng_atm_input_p)(struct ifnet *, struct mbuf **,
8520747Sdavidn	    struct atm_pseudohdr *, void *);
8620747Sdavidnvoid	(*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
8720747Sdavidn	    struct atm_pseudohdr *, void *);
8820253Sjoergvoid	(*ng_atm_event_p)(struct ifnet *, uint32_t, void *);
8920747Sdavidn
9020747Sdavidn/*
9120747Sdavidn * Harp pseudo interface hooks
9220747Sdavidn */
9320747Sdavidnvoid	(*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m,
9420747Sdavidn	    struct atm_pseudohdr *ah, void *rxhand);
9520747Sdavidnvoid	(*atm_harp_attach_p)(struct ifnet *);
9620747Sdavidnvoid	(*atm_harp_detach_p)(struct ifnet *);
9720747Sdavidnvoid	(*atm_harp_event_p)(struct ifnet *, uint32_t, void *);
9820747Sdavidn
9920747SdavidnSYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware");
10020747Sdavidn
10120747SdavidnMALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals");
10220747Sdavidn
10320747Sdavidn#ifndef ETHERTYPE_IPV6
10420747Sdavidn#define	ETHERTYPE_IPV6	0x86dd
10520747Sdavidn#endif
10620747Sdavidn
10720747Sdavidn#define	senderr(e) do { error = (e); goto bad; } while (0)
10820747Sdavidn
10920747Sdavidn/*
11020747Sdavidn * atm_output: ATM output routine
11120747Sdavidn *   inputs:
11220747Sdavidn *     "ifp" = ATM interface to output to
11320747Sdavidn *     "m0" = the packet to output
11420747Sdavidn *     "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI)
11520747Sdavidn *     "rt0" = the route to use
11620747Sdavidn *   returns: error code   [0 == ok]
11720253Sjoerg *
11820253Sjoerg *   note: special semantic: if (dst == NULL) then we assume "m" already
11920253Sjoerg *		has an atm_pseudohdr on it and just send it directly.
12020253Sjoerg *		[for native mode ATM output]   if dst is null, then
12120253Sjoerg *		rt0 must also be NULL.
12220747Sdavidn */
12320253Sjoergint
12420747Sdavidnatm_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
12520253Sjoerg    struct rtentry *rt0)
12620253Sjoerg{
12720253Sjoerg	u_int16_t etype = 0;			/* if using LLC/SNAP */
12820253Sjoerg	int error = 0, sz;
12920253Sjoerg	struct atm_pseudohdr atmdst, *ad;
13020253Sjoerg	struct mbuf *m = m0;
13120253Sjoerg	struct atmllc *atmllc;
13220747Sdavidn	struct atmllc *llc_hdr = NULL;
13320747Sdavidn	u_int32_t atm_flags;
13420747Sdavidn
13520253Sjoerg#ifdef MAC
13644229Sdavidn	error = mac_check_ifnet_transmit(ifp, m);
13720747Sdavidn	if (error)
13820253Sjoerg		senderr(error);
13920253Sjoerg#endif
14020253Sjoerg
14120253Sjoerg	if (!((ifp->if_flags & IFF_UP) &&
14220747Sdavidn	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
14320747Sdavidn		senderr(ENETDOWN);
14444229Sdavidn
14544229Sdavidn	/*
14644229Sdavidn	 * check for non-native ATM traffic   (dst != NULL)
14744229Sdavidn	 */
14820747Sdavidn	if (dst) {
14920747Sdavidn		switch (dst->sa_family) {
15020747Sdavidn
15120253Sjoerg#if defined(INET) || defined(INET6)
15220253Sjoerg		case AF_INET:
15320253Sjoerg		case AF_INET6:
15420253Sjoerg		{
15520253Sjoerg			struct rtentry *rt = NULL;
15620253Sjoerg			/*
15720253Sjoerg			 * check route
15820253Sjoerg			 */
15920253Sjoerg			if (rt0 != NULL) {
16020253Sjoerg				error = rt_check(&rt, &rt0, dst);
16120253Sjoerg				if (error)
16220253Sjoerg					goto bad;
16320253Sjoerg				RT_UNLOCK(rt);
16420253Sjoerg			}
16520253Sjoerg
16620253Sjoerg			if (dst->sa_family == AF_INET6)
16720253Sjoerg			        etype = ETHERTYPE_IPV6;
16820253Sjoerg			else
16920253Sjoerg			        etype = ETHERTYPE_IP;
17020253Sjoerg			if (!atmresolve(rt, m, dst, &atmdst)) {
171				m = NULL;
172				/* XXX: atmresolve already free'd it */
173				senderr(EHOSTUNREACH);
174				/* XXX: put ATMARP stuff here */
175				/* XXX: watch who frees m on failure */
176			}
177		}
178			break;
179#endif /* INET || INET6 */
180
181		case AF_UNSPEC:
182			/*
183			 * XXX: bpfwrite. assuming dst contains 12 bytes
184			 * (atm pseudo header (4) + LLC/SNAP (8))
185			 */
186			bcopy(dst->sa_data, &atmdst, sizeof(atmdst));
187			llc_hdr = (struct atmllc *)(dst->sa_data +
188			    sizeof(atmdst));
189			break;
190
191		default:
192#if (defined(__FreeBSD__) && __FreeBSD_version >= 501113) || \
193    defined(__NetBSD__) || defined(__OpenBSD__)
194			printf("%s: can't handle af%d\n", ifp->if_xname,
195			    dst->sa_family);
196#elif defined(__FreeBSD__) || defined(__bsdi__)
197			printf("%s%d: can't handle af%d\n", ifp->if_name,
198			    ifp->if_unit, dst->sa_family);
199#endif
200			senderr(EAFNOSUPPORT);
201		}
202
203		/*
204		 * must add atm_pseudohdr to data
205		 */
206		sz = sizeof(atmdst);
207		atm_flags = ATM_PH_FLAGS(&atmdst);
208		if (atm_flags & ATM_PH_LLCSNAP)
209			sz += 8;	/* sizeof snap == 8 */
210		M_PREPEND(m, sz, M_DONTWAIT);
211		if (m == 0)
212			senderr(ENOBUFS);
213		ad = mtod(m, struct atm_pseudohdr *);
214		*ad = atmdst;
215		if (atm_flags & ATM_PH_LLCSNAP) {
216			atmllc = (struct atmllc *)(ad + 1);
217			if (llc_hdr == NULL) {
218			        bcopy(ATMLLC_HDR, atmllc->llchdr,
219				      sizeof(atmllc->llchdr));
220				/* note: in host order */
221				ATM_LLC_SETTYPE(atmllc, etype);
222			}
223			else
224			        bcopy(llc_hdr, atmllc, sizeof(struct atmllc));
225		}
226	}
227
228	if (ng_atm_output_p != NULL) {
229		if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) {
230			if (m != NULL)
231				m_freem(m);
232			return (error);
233		}
234		if (m == NULL)
235			return (0);
236	}
237
238	/*
239	 * Queue message on interface, and start output if interface
240	 * not yet active.
241	 */
242	if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp,
243	    -(int)sizeof(struct atm_pseudohdr)))
244		return (ENOBUFS);
245	return (error);
246
247bad:
248	if (m)
249		m_freem(m);
250	return (error);
251}
252
253/*
254 * Process a received ATM packet;
255 * the packet is in the mbuf chain m.
256 */
257void
258atm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m,
259    void *rxhand)
260{
261	int isr;
262	u_int16_t etype = ETHERTYPE_IP;		/* default */
263
264	if ((ifp->if_flags & IFF_UP) == 0) {
265		m_freem(m);
266		return;
267	}
268#ifdef MAC
269	mac_create_mbuf_from_ifnet(ifp, m);
270#endif
271	ifp->if_ibytes += m->m_pkthdr.len;
272
273	if (ng_atm_input_p != NULL) {
274		(*ng_atm_input_p)(ifp, &m, ah, rxhand);
275		if (m == NULL)
276			return;
277	}
278
279	/* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */
280	if (atm_harp_input_p != NULL) {
281		(*atm_harp_input_p)(ifp, &m, ah, rxhand);
282		if (m == NULL)
283			return;
284	}
285
286	if (rxhand) {
287#ifdef NATM
288		struct natmpcb *npcb;
289
290		/*
291		 * XXXRW: this use of 'rxhand' is not a very good idea, and
292		 * was subject to races even before SMPng due to the release
293		 * of spl here.
294		 */
295		NATM_LOCK();
296		npcb = rxhand;
297		npcb->npcb_inq++;	/* count # in queue */
298		isr = NETISR_NATM;
299		m->m_pkthdr.rcvif = rxhand; /* XXX: overload */
300		NATM_UNLOCK();
301#else
302		printf("atm_input: NATM detected but not "
303		    "configured in kernel\n");
304		goto dropit;
305#endif
306	} else {
307		/*
308		 * handle LLC/SNAP header, if present
309		 */
310		if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) {
311			struct atmllc *alc;
312
313			if (m->m_len < sizeof(*alc) &&
314			    (m = m_pullup(m, sizeof(*alc))) == 0)
315				return; /* failed */
316			alc = mtod(m, struct atmllc *);
317			if (bcmp(alc, ATMLLC_HDR, 6)) {
318#if (defined(__FreeBSD__) && __FreeBSD_version >= 501113) || \
319    defined(__NetBSD__) || defined(__OpenBSD__)
320				printf("%s: recv'd invalid LLC/SNAP frame "
321				    "[vp=%d,vc=%d]\n", ifp->if_xname,
322				    ATM_PH_VPI(ah), ATM_PH_VCI(ah));
323#elif defined(__FreeBSD__) || defined(__bsdi__)
324				printf("%s%d: recv'd invalid LLC/SNAP frame "
325				    "[vp=%d,vc=%d]\n", ifp->if_name,
326				    ifp->if_unit, ATM_PH_VPI(ah),
327				    ATM_PH_VCI(ah));
328#endif
329				m_freem(m);
330				return;
331			}
332			etype = ATM_LLC_TYPE(alc);
333			m_adj(m, sizeof(*alc));
334		}
335
336		switch (etype) {
337
338#ifdef INET
339		case ETHERTYPE_IP:
340			isr = NETISR_IP;
341			break;
342#endif
343
344#ifdef INET6
345		case ETHERTYPE_IPV6:
346			isr = NETISR_IPV6;
347			break;
348#endif
349		default:
350#ifndef NATM
351  dropit:
352#endif
353			if (ng_atm_input_orphan_p != NULL)
354				(*ng_atm_input_orphan_p)(ifp, m, ah, rxhand);
355			else
356				m_freem(m);
357			return;
358		}
359	}
360	netisr_dispatch(isr, m);
361}
362
363/*
364 * Perform common duties while attaching to interface list.
365 */
366void
367atm_ifattach(struct ifnet *ifp)
368{
369	struct ifaddr *ifa;
370	struct sockaddr_dl *sdl;
371	struct ifatm *ifatm = ifp->if_l2com;
372
373	ifp->if_addrlen = 0;
374	ifp->if_hdrlen = 0;
375	if_attach(ifp);
376	ifp->if_mtu = ATMMTU;
377	ifp->if_output = atm_output;
378#if 0
379	ifp->if_input = atm_input;
380#endif
381	ifp->if_snd.ifq_maxlen = 50;	/* dummy */
382
383#if defined(__NetBSD__) || defined(__OpenBSD__)
384	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
385#elif defined(__FreeBSD__) && (__FreeBSD__ > 2)
386	for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa;
387	    ifa = TAILQ_NEXT(ifa, ifa_link))
388#elif defined(__FreeBSD__) || defined(__bsdi__)
389	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
390#endif
391		if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
392		    sdl->sdl_family == AF_LINK) {
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