if_atmsubr.c revision 37939
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
35/*
36 * if_atmsubr.c
37 */
38
39#include "opt_inet.h"
40#include "opt_natm.h"
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/mbuf.h>
45#include <sys/socket.h>
46#include <sys/sockio.h>
47#include <sys/malloc.h>
48#include <sys/errno.h>
49
50#include <net/if.h>
51#include <net/netisr.h>
52#include <net/route.h>
53#include <net/if_dl.h>
54#include <net/if_types.h>
55#include <net/if_atm.h>
56
57#include <netinet/in.h>
58#include <netinet/if_atm.h>
59#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */
60#if defined(INET) || defined(INET6)
61#include <netinet/in_var.h>
62#endif
63#ifdef NATM
64#include <netnatm/natm.h>
65#endif
66
67#ifndef ETHERTYPE_IPV6
68#define ETHERTYPE_IPV6	0x86dd
69#endif
70
71#define senderr(e) { error = (e); goto bad;}
72
73/*
74 * atm_output: ATM output routine
75 *   inputs:
76 *     "ifp" = ATM interface to output to
77 *     "m0" = the packet to output
78 *     "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI)
79 *     "rt0" = the route to use
80 *   returns: error code   [0 == ok]
81 *
82 *   note: special semantic: if (dst == NULL) then we assume "m" already
83 *		has an atm_pseudohdr on it and just send it directly.
84 *		[for native mode ATM output]   if dst is null, then
85 *		rt0 must also be NULL.
86 */
87
88int
89atm_output(ifp, m0, dst, rt0)
90	register struct ifnet *ifp;
91	struct mbuf *m0;
92	struct sockaddr *dst;
93	struct rtentry *rt0;
94{
95	u_int16_t etype = 0;			/* if using LLC/SNAP */
96	int s, error = 0, sz;
97	struct atm_pseudohdr atmdst, *ad;
98	register struct mbuf *m = m0;
99	register struct rtentry *rt;
100	struct atmllc *atmllc;
101	struct atmllc *llc_hdr = NULL;
102	u_int32_t atm_flags;
103
104	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
105		senderr(ENETDOWN);
106
107	/*
108	 * check route
109	 */
110	if ((rt = rt0) != NULL) {
111
112		if ((rt->rt_flags & RTF_UP) == 0) { /* route went down! */
113			if ((rt0 = rt = RTALLOC1(dst, 0)) != NULL)
114				rt->rt_refcnt--;
115			else
116				senderr(EHOSTUNREACH);
117		}
118
119		if (rt->rt_flags & RTF_GATEWAY) {
120			if (rt->rt_gwroute == 0)
121				goto lookup;
122			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
123				rtfree(rt); rt = rt0;
124			lookup: rt->rt_gwroute = RTALLOC1(rt->rt_gateway, 0);
125				if ((rt = rt->rt_gwroute) == 0)
126					senderr(EHOSTUNREACH);
127			}
128		}
129
130		/* XXX: put RTF_REJECT code here if doing ATMARP */
131
132	}
133
134	/*
135	 * check for non-native ATM traffic   (dst != NULL)
136	 */
137	if (dst) {
138		switch (dst->sa_family) {
139#if defined(INET) || defined(INET6)
140		case AF_INET:
141		case AF_INET6:
142			if (!atmresolve(rt, m, dst, &atmdst)) {
143				m = NULL;
144				/* XXX: atmresolve already free'd it */
145				senderr(EHOSTUNREACH);
146				/* XXX: put ATMARP stuff here */
147				/* XXX: watch who frees m on failure */
148			}
149			if (dst->sa_family == AF_INET6)
150			        etype = htons(ETHERTYPE_IPV6);
151			else
152			        etype = htons(ETHERTYPE_IP);
153			break;
154#endif /* INET || INET6 */
155
156		case AF_UNSPEC:
157			/*
158			 * XXX: bpfwrite or output from a pvc shadow if.
159			 * assuming dst contains 12 bytes (atm pseudo
160			 * header (4) + LLC/SNAP (8))
161			 */
162			bcopy(dst->sa_data, &atmdst, sizeof(atmdst));
163			llc_hdr = (struct atmllc *)(dst->sa_data + sizeof(atmdst));
164			break;
165
166		default:
167#if defined(__NetBSD__) || defined(__OpenBSD__)
168			printf("%s: can't handle af%d\n", ifp->if_xname,
169			    dst->sa_family);
170#elif defined(__FreeBSD__) || defined(__bsdi__)
171			printf("%s%d: can't handle af%d\n", ifp->if_name,
172			    ifp->if_unit, dst->sa_family);
173#endif
174			senderr(EAFNOSUPPORT);
175		}
176
177		/*
178		 * must add atm_pseudohdr to data
179		 */
180		sz = sizeof(atmdst);
181		atm_flags = ATM_PH_FLAGS(&atmdst);
182		if (atm_flags & ATM_PH_LLCSNAP) sz += 8; /* sizeof snap == 8 */
183		M_PREPEND(m, sz, M_DONTWAIT);
184		if (m == 0)
185			senderr(ENOBUFS);
186		ad = mtod(m, struct atm_pseudohdr *);
187		*ad = atmdst;
188		if (atm_flags & ATM_PH_LLCSNAP) {
189			atmllc = (struct atmllc *)(ad + 1);
190			if (llc_hdr == NULL) {
191			        bcopy(ATMLLC_HDR, atmllc->llchdr,
192				      sizeof(atmllc->llchdr));
193				ATM_LLC_SETTYPE(atmllc, etype);
194					/* note: already in network order */
195			}
196			else
197			        bcopy(llc_hdr, atmllc, sizeof(struct atmllc));
198		}
199	}
200
201	/*
202	 * Queue message on interface, and start output if interface
203	 * not yet active.
204	 */
205	s = splimp();
206	if (IF_QFULL(&ifp->if_snd)) {
207		IF_DROP(&ifp->if_snd);
208		splx(s);
209		senderr(ENOBUFS);
210	}
211	ifp->if_obytes += m->m_pkthdr.len;
212	IF_ENQUEUE(&ifp->if_snd, m);
213	if ((ifp->if_flags & IFF_OACTIVE) == 0)
214		(*ifp->if_start)(ifp);
215	splx(s);
216	return (error);
217
218bad:
219	if (m)
220		m_freem(m);
221	return (error);
222}
223
224/*
225 * Process a received ATM packet;
226 * the packet is in the mbuf chain m.
227 */
228void
229atm_input(ifp, ah, m, rxhand)
230	struct ifnet *ifp;
231	register struct atm_pseudohdr *ah;
232	struct mbuf *m;
233	void *rxhand;
234{
235	register struct ifqueue *inq;
236	u_int16_t etype = ETHERTYPE_IP; /* default */
237	int s;
238
239	if ((ifp->if_flags & IFF_UP) == 0) {
240		m_freem(m);
241		return;
242	}
243	ifp->if_ibytes += m->m_pkthdr.len;
244
245#ifdef ATM_PVCEXT
246	if (ATM_PH_FLAGS(ah) & ATM_PH_PVCSIF) {
247		/*
248		 * when PVC shadow interface is used, pointer to
249		 * the shadow interface is passed as rxhand.
250		 * override the receive interface of the packet.
251		 */
252		m->m_pkthdr.rcvif = (struct ifnet *)rxhand;
253		rxhand = NULL;
254	}
255#endif /*  ATM_PVCEXT */
256
257	if (rxhand) {
258#ifdef NATM
259		struct natmpcb *npcb = rxhand;
260		s = splimp();		/* in case 2 atm cards @ diff lvls */
261		npcb->npcb_inq++;	/* count # in queue */
262		splx(s);
263		schednetisr(NETISR_NATM);
264		inq = &natmintrq;
265		m->m_pkthdr.rcvif = rxhand; /* XXX: overload */
266#else
267		printf("atm_input: NATM detected but not configured in kernel\n");
268		m_freem(m);
269		return;
270#endif
271	} else {
272		/*
273		 * handle LLC/SNAP header, if present
274		 */
275		if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) {
276			struct atmllc *alc;
277			if (m->m_len < sizeof(*alc) &&
278			    (m = m_pullup(m, sizeof(*alc))) == 0)
279				return; /* failed */
280			alc = mtod(m, struct atmllc *);
281			if (bcmp(alc, ATMLLC_HDR, 6)) {
282#if defined(__NetBSD__) || defined(__OpenBSD__)
283				printf("%s: recv'd invalid LLC/SNAP frame [vp=%d,vc=%d]\n",
284				       ifp->if_xname, ATM_PH_VPI(ah), ATM_PH_VCI(ah));
285#elif defined(__FreeBSD__) || defined(__bsdi__)
286				printf("%s%d: recv'd invalid LLC/SNAP frame [vp=%d,vc=%d]\n",
287				       ifp->if_name, ifp->if_unit, ATM_PH_VPI(ah), ATM_PH_VCI(ah));
288#endif
289				m_freem(m);
290				return;
291			}
292			etype = ATM_LLC_TYPE(alc);
293			m_adj(m, sizeof(*alc));
294		}
295
296		switch (etype) {
297#ifdef INET
298		case ETHERTYPE_IP:
299			schednetisr(NETISR_IP);
300			inq = &ipintrq;
301			break;
302#endif
303#ifdef INET6
304		case ETHERTYPE_IPV6:
305			schednetisr(NETISR_IPV6);
306			inq = &ip6intrq;
307			break;
308#endif
309		default:
310			m_freem(m);
311			return;
312		}
313	}
314
315	s = splimp();
316	if (IF_QFULL(inq)) {
317		IF_DROP(inq);
318		m_freem(m);
319	} else
320		IF_ENQUEUE(inq, m);
321	splx(s);
322}
323
324/*
325 * Perform common duties while attaching to interface list
326 */
327void
328atm_ifattach(ifp)
329	register struct ifnet *ifp;
330{
331	register struct ifaddr *ifa;
332	register struct sockaddr_dl *sdl;
333
334	ifp->if_type = IFT_ATM;
335	ifp->if_addrlen = 0;
336	ifp->if_hdrlen = 0;
337	ifp->if_mtu = ATMMTU;
338	ifp->if_output = atm_output;
339
340#if defined(__NetBSD__) || defined(__OpenBSD__)
341	for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
342	    ifa = ifa->ifa_list.tqe_next)
343#elif defined(__FreeBSD__) && (__FreeBSD__ > 2)
344	for (ifa = ifp->if_addrhead.tqh_first; ifa;
345	    ifa = ifa->ifa_link.tqe_next)
346#elif defined(__FreeBSD__) || defined(__bsdi__)
347	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
348#endif
349		if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
350		    sdl->sdl_family == AF_LINK) {
351			sdl->sdl_type = IFT_ATM;
352			sdl->sdl_alen = ifp->if_addrlen;
353#ifdef notyet /* if using ATMARP, store hardware address using the next line */
354			bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen);
355#endif
356			break;
357		}
358
359}
360
361#ifdef ATM_PVCEXT
362/*
363 * ATM PVC shadow interface: a trick to assign a shadow interface
364 * to a PVC.
365 * with shadow interface, each PVC looks like an individual
366 * Point-to-Point interface.
367 * as oposed to the NBMA model, a shadow interface is inherently
368 * multicast capable (no LANE/MARS required).
369 */
370struct pvcsif {
371	struct ifnet sif_shadow;	/* shadow ifnet structure per pvc */
372	struct atm_pseudohdr sif_aph;	/* flags + vpi:vci */
373	struct ifnet *sif_ifp;		/* pointer to the genuine interface */
374};
375
376static int pvc_output __P((struct ifnet *, struct mbuf *,
377			   struct sockaddr *, struct rtentry *));
378static int pvc_ioctl __P((struct ifnet *, u_long, caddr_t));
379
380/*
381 * create and attach per pvc shadow interface
382 * (currently detach is not supported)
383 */
384static int pvc_number = 0;
385
386struct ifnet *
387pvc_attach(ifp)
388	struct ifnet *ifp;
389{
390	struct pvcsif *pvcsif;
391	struct ifnet *shadow;
392	struct ifaddr *ifa;
393	struct sockaddr_dl *sdl;
394	int s;
395
396	MALLOC(pvcsif, struct pvcsif *, sizeof(struct pvcsif),
397	       M_DEVBUF, M_WAITOK);
398	bzero(pvcsif, sizeof(struct pvcsif));
399
400	pvcsif->sif_ifp = ifp;
401	shadow = &pvcsif->sif_shadow;
402
403	shadow->if_name = "pvc";
404	shadow->if_unit = pvc_number++;
405	shadow->if_flags = ifp->if_flags | (IFF_POINTOPOINT | IFF_MULTICAST);
406	shadow->if_ioctl = pvc_ioctl;
407	shadow->if_output = pvc_output;
408	shadow->if_start = NULL;
409	shadow->if_mtu = ifp->if_mtu;
410	shadow->if_type = ifp->if_type;
411	shadow->if_addrlen = ifp->if_addrlen;
412	shadow->if_hdrlen = ifp->if_hdrlen;
413	shadow->if_softc = pvcsif;
414	shadow->if_snd.ifq_maxlen = 50;	/* dummy */
415
416	s = splimp();
417	if_attach(shadow);
418
419#if defined(__NetBSD__) || defined(__OpenBSD__)
420	for (ifa = shadow->if_addrlist.tqh_first; ifa != 0;
421	     ifa = ifa->ifa_list.tqe_next)
422#elif defined(__FreeBSD__) && (__FreeBSD__ > 2)
423	for (ifa = shadow->if_addrhead.tqh_first; ifa;
424	     ifa = ifa->ifa_link.tqe_next)
425#elif defined(__FreeBSD__) || defined(__bsdi__)
426	for (ifa = shadow->if_addrlist; ifa; ifa = ifa->ifa_next)
427#endif
428		if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
429		    sdl->sdl_family == AF_LINK) {
430			sdl->sdl_type = IFT_ATM;
431			sdl->sdl_alen = shadow->if_addrlen;
432			break;
433		}
434	splx(s);
435
436	return (shadow);
437}
438
439/*
440 * pvc_output relays the packet to atm_output along with vpi:vci info.
441 */
442static int
443pvc_output(shadow, m, dst, rt)
444	struct ifnet *shadow;
445	struct mbuf *m;
446	struct sockaddr *dst;
447	struct rtentry *rt;
448{
449	struct pvcsif *pvcsif;
450	struct sockaddr dst_addr;
451	struct atmllc *atmllc;
452	u_int16_t etype = 0;
453	int error = 0;
454
455	if ((shadow->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
456		senderr(ENETDOWN);
457
458	pvcsif = shadow->if_softc;
459	if (ATM_PH_VCI(&pvcsif->sif_aph) == 0)
460		senderr(ENETDOWN);
461
462	/*
463	 * create a dummy sockaddr: (using bpfwrite interface)
464	 * put atm pseudo header and llc/snap into sa_data (12 bytes)
465	 * and mark it as AF_UNSPEC.
466	 */
467	if (dst) {
468		switch (dst->sa_family) {
469#if defined(INET) || defined(INET6)
470		case AF_INET:
471		case AF_INET6:
472			if (dst->sa_family == AF_INET6)
473				etype = htons(ETHERTYPE_IPV6);
474			else
475				etype = htons(ETHERTYPE_IP);
476			break;
477#endif
478
479		default:
480			printf("%s%d: can't handle af%d\n", shadow->if_name,
481			       shadow->if_unit, dst->sa_family);
482			senderr(EAFNOSUPPORT);
483		}
484	}
485
486	dst_addr.sa_family = AF_UNSPEC;
487	bcopy(&pvcsif->sif_aph, dst_addr.sa_data,
488	      sizeof(struct atm_pseudohdr));
489	atmllc = (struct atmllc *)
490		(dst_addr.sa_data + sizeof(struct atm_pseudohdr));
491	bcopy(ATMLLC_HDR, atmllc->llchdr,  sizeof(atmllc->llchdr));
492	ATM_LLC_SETTYPE(atmllc, etype);  /* note: already in network order */
493
494	return atm_output(pvcsif->sif_ifp, m, &dst_addr, rt);
495
496bad:
497	if (m)
498		m_freem(m);
499	return (error);
500}
501
502static int
503pvc_ioctl(shadow, cmd, data)
504	struct ifnet *shadow;
505	u_long cmd;
506	caddr_t data;
507{
508	struct ifnet *ifp;
509	struct pvcsif *pvcsif;
510	struct ifreq *ifr = (struct ifreq *) data;
511	void (*ifa_rtrequest)(int, struct rtentry *, struct sockaddr *) = NULL;
512	int error = 0;
513
514	pvcsif = (struct pvcsif *)shadow->if_softc;
515	ifp = pvcsif->sif_ifp;
516	if (ifp == 0 || ifp->if_ioctl == 0)
517		return (EOPNOTSUPP);
518
519	/*
520	 * pre process
521	 */
522	switch (cmd) {
523	case SIOCGPVCSIF:
524		sprintf(ifr->ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
525		return (0);
526
527	case SIOCGPVCTX:
528		do {
529			struct pvctxreq *pvcreq = (struct pvctxreq *)data;
530
531			sprintf(pvcreq->pvc_ifname, "%s%d",
532				ifp->if_name, ifp->if_unit);
533			pvcreq->pvc_aph = pvcsif->sif_aph;
534		} while (0);
535		break;
536
537	case SIOCADDMULTI:
538	case SIOCDELMULTI:
539		if (ifr == 0)
540			return (EAFNOSUPPORT);	/* XXX */
541		switch (ifr->ifr_addr.sa_family) {
542#ifdef INET
543		case AF_INET:
544			return (0);
545#endif
546#ifdef INET6
547		case AF_INET6:
548			return (0);
549#endif
550		default:
551			return (EAFNOSUPPORT);
552		}
553		break;
554	case SIOCSIFADDR:
555		if (ifp->if_flags & IFF_UP) {
556			/* real if is already up */
557			shadow->if_flags = ifp->if_flags |
558				(IFF_POINTOPOINT|IFF_MULTICAST);
559			return (0);
560		}
561		/*
562		 * XXX: save the rtrequest field since the atm driver
563		 * overwrites this field.
564		 */
565		ifa_rtrequest = ((struct ifaddr *)data)->ifa_rtrequest;
566		break;
567
568	case SIOCSIFFLAGS:
569		if ((shadow->if_flags & IFF_UP) == 0) {
570			/*
571			 * interface down. don't pass this to
572			 * the real interface.
573			 */
574			return (0);
575		}
576		if (shadow->if_flags & IFF_UP) {
577			/*
578			 * interface up. if the real if is already up,
579			 * nothing to do.
580			 */
581			if (ifp->if_flags & IFF_UP) {
582				shadow->if_flags = ifp->if_flags |
583					(IFF_POINTOPOINT|IFF_MULTICAST);
584				return (0);
585			}
586		}
587		break;
588	}
589
590	/*
591	 * pass the ioctl to the genuine interface
592	 */
593	error = (*ifp->if_ioctl)(ifp, cmd, data);
594
595	/*
596	 * post process
597	 */
598	switch (cmd) {
599	case SIOCSIFMTU:
600		shadow->if_mtu = ifp->if_mtu;
601		break;
602	case SIOCSIFADDR:
603		/* restore rtrequest */
604		((struct ifaddr *)data)->ifa_rtrequest = ifa_rtrequest;
605		/* fall into... */
606	case SIOCSIFFLAGS:
607		/* update if_flags */
608		shadow->if_flags = ifp->if_flags
609			| (IFF_POINTOPOINT|IFF_MULTICAST);
610		break;
611	}
612
613	return (error);
614}
615
616int pvc_setaph(shadow, aph)
617	struct ifnet *shadow;
618	struct atm_pseudohdr *aph;
619{
620	struct pvcsif *pvcsif;
621
622	pvcsif = shadow->if_softc;
623	bcopy(aph, &pvcsif->sif_aph, sizeof(struct atm_pseudohdr));
624	return (0);
625}
626
627#endif /* ATM_PVCEXT */
628