if_fddisubr.c revision 93369
119304Speter/*
219304Speter * Copyright (c) 1995, 1996
319304Speter *	Matt Thomas <matt@3am-software.com>.  All rights reserved.
419304Speter * Copyright (c) 1982, 1989, 1993
519304Speter *	The Regents of the University of California.  All rights reserved.
619304Speter *
719304Speter * Redistribution and use in source and binary forms, with or without
819304Speter * modification, are permitted provided that the following conditions
919304Speter * are met:
1019304Speter * 1. Redistributions of source code must retain the above copyright
1119304Speter *    notice, this list of conditions and the following disclaimer.
1219304Speter * 2. Redistributions in binary form must reproduce the above copyright
1319304Speter *    notice, this list of conditions and the following disclaimer in the
1419304Speter *    documentation and/or other materials provided with the distribution.
1519304Speter * 3. All advertising materials mentioning features or use of this software
1619304Speter *    must display the following acknowledgement:
1719304Speter *	This product includes software developed by the University of
1819304Speter *	California, Berkeley and its contributors.
1919304Speter * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *	from: if_ethersubr.c,v 1.5 1994/12/13 22:31:45 wollman Exp
36 * $FreeBSD: head/sys/net/if_fddisubr.c 93369 2002-03-29 08:04:36Z mdodd $
37 */
38
39#include "opt_atalk.h"
40#include "opt_inet.h"
41#include "opt_inet6.h"
42#include "opt_ipx.h"
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/mbuf.h>
47#include <sys/socket.h>
48#include <sys/malloc.h>
49
50#include <net/if.h>
51#include <net/netisr.h>
52#include <net/route.h>
53#include <net/if_llc.h>
54#include <net/if_dl.h>
55#include <net/if_types.h>
56
57#if defined(INET) || defined(INET6)
58#include <netinet/in.h>
59#include <netinet/in_var.h>
60#include <netinet/if_ether.h>
61#endif
62#ifdef INET6
63#include <netinet6/nd6.h>
64#endif
65#include <netinet/if_fddi.h>
66
67#ifdef IPX
68#include <netipx/ipx.h>
69#include <netipx/ipx_if.h>
70#endif
71
72#ifdef NS
73#include <netns/ns.h>
74#include <netns/ns_if.h>
75#endif
76
77#ifdef DECNET
78#include <netdnet/dn.h>
79#endif
80
81#ifdef NETATALK
82#include <netatalk/at.h>
83#include <netatalk/at_var.h>
84#include <netatalk/at_extern.h>
85
86#define llc_snap_org_code llc_un.type_snap.org_code
87#define llc_snap_ether_type llc_un.type_snap.ether_type
88
89extern u_char	at_org_code[ 3 ];
90extern u_char	aarp_org_code[ 3 ];
91#endif /* NETATALK */
92
93static	int fddi_resolvemulti(struct ifnet *, struct sockaddr **,
94			      struct sockaddr *);
95
96/*
97 * This really should be defined in if_llc.h but in case it isn't.
98 */
99#ifndef llc_snap
100#define	llc_snap	llc_un.type_snap
101#endif
102
103#define	IFP2AC(IFP)	((struct arpcom *)IFP)
104#define	senderr(e)	{ error = (e); goto bad; }
105
106/*
107 * FDDI output routine.
108 * Encapsulate a packet of type family for the local net.
109 * Use trailer local net encapsulation if enough data in first
110 * packet leaves a multiple of 512 bytes of data in remainder.
111 * Assumes that ifp is actually pointer to arpcom structure.
112 */
113int
114fddi_output(ifp, m, dst, rt0)
115	struct ifnet *ifp;
116	struct mbuf *m;
117	struct sockaddr *dst;
118	struct rtentry *rt0;
119{
120	u_int16_t type;
121	int loop_copy = 0, error = 0, hdrcmplt = 0;
122 	u_char esrc[6], edst[6];
123	struct rtentry *rt;
124	struct fddi_header *fh;
125	struct arpcom *ac = IFP2AC(ifp);
126
127	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
128		senderr(ENETDOWN);
129	getmicrotime(&ifp->if_lastchange);
130	if ((rt = rt0) != NULL) {
131		if ((rt->rt_flags & RTF_UP) == 0) {
132			if ((rt0 = rt = rtalloc1(dst, 1, 0UL)) != NULL)
133				rt->rt_refcnt--;
134			else
135				senderr(EHOSTUNREACH);
136		}
137		if (rt->rt_flags & RTF_GATEWAY) {
138			if (rt->rt_gwroute == 0)
139				goto lookup;
140			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
141				rtfree(rt); rt = rt0;
142			lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);
143				if ((rt = rt->rt_gwroute) == 0)
144					senderr(EHOSTUNREACH);
145			}
146		}
147		if (rt->rt_flags & RTF_REJECT)
148			if (rt->rt_rmx.rmx_expire == 0 ||
149			    time_second < rt->rt_rmx.rmx_expire)
150				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
151	}
152	switch (dst->sa_family) {
153
154#ifdef INET
155	case AF_INET: {
156		if (!arpresolve(ifp, rt, m, dst, edst, rt0))
157			return (0);	/* if not yet resolved */
158		type = htons(ETHERTYPE_IP);
159		break;
160	}
161#endif
162#ifdef INET6
163	case AF_INET6:
164		if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) {
165			/* Something bad happened */
166			return (0);
167		}
168		type = htons(ETHERTYPE_IPV6);
169		break;
170#endif
171#ifdef IPX
172	case AF_IPX:
173		type = htons(ETHERTYPE_IPX);
174 		bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
175		    (caddr_t)edst, sizeof (edst));
176		break;
177#endif
178#ifdef NETATALK
179	case AF_APPLETALK: {
180	    struct at_ifaddr *aa;
181            if (!aarpresolve(ac, m, (struct sockaddr_at *)dst, edst))
182                return (0);
183	    /*
184	     * ifaddr is the first thing in at_ifaddr
185	     */
186	    if ((aa = at_ifawithnet( (struct sockaddr_at *)dst)) == 0)
187		goto bad;
188
189	    /*
190	     * In the phase 2 case, we need to prepend an mbuf for the llc header.
191	     * Since we must preserve the value of m, which is passed to us by
192	     * value, we m_copy() the first mbuf, and use it for our llc header.
193	     */
194	    if (aa->aa_flags & AFA_PHASE2) {
195		struct llc llc;
196
197		M_PREPEND(m, sizeof(struct llc), M_TRYWAIT);
198		if (m == 0)
199			senderr(ENOBUFS);
200		llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
201		llc.llc_control = LLC_UI;
202		bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code));
203		llc.llc_snap_ether_type = htons(ETHERTYPE_AT);
204		bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc));
205		type = 0;
206	    } else {
207		type = htons(ETHERTYPE_AT);
208	    }
209	    break;
210	}
211#endif /* NETATALK */
212#ifdef NS
213	case AF_NS:
214		type = htons(ETHERTYPE_NS);
215 		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
216		    (caddr_t)edst, sizeof (edst));
217		break;
218#endif
219
220	case pseudo_AF_HDRCMPLT:
221	{
222		struct ether_header *eh;
223		hdrcmplt = 1;
224		eh = (struct ether_header *)dst->sa_data;
225 		(void)memcpy((caddr_t)esrc, (caddr_t)eh->ether_shost, sizeof (esrc));
226		/* FALLTHROUGH */
227	}
228
229	case AF_UNSPEC:
230	{
231		struct ether_header *eh;
232		loop_copy = -1;
233		eh = (struct ether_header *)dst->sa_data;
234 		(void)memcpy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
235		if (*edst & 1)
236			m->m_flags |= (M_BCAST|M_MCAST);
237		type = eh->ether_type;
238		break;
239	}
240
241	case AF_IMPLINK:
242	{
243		fh = mtod(m, struct fddi_header *);
244		error = EPROTONOSUPPORT;
245		switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) {
246			case FDDIFC_LLC_ASYNC: {
247				/* legal priorities are 0 through 7 */
248				if ((fh->fddi_fc & FDDIFC_Z) > 7)
249			        	goto bad;
250				break;
251			}
252			case FDDIFC_LLC_SYNC: {
253				/* FDDIFC_Z bits reserved, must be zero */
254				if (fh->fddi_fc & FDDIFC_Z)
255					goto bad;
256				break;
257			}
258			case FDDIFC_SMT: {
259				/* FDDIFC_Z bits must be non zero */
260				if ((fh->fddi_fc & FDDIFC_Z) == 0)
261					goto bad;
262				break;
263			}
264			default: {
265				/* anything else is too dangerous */
266               	 		goto bad;
267			}
268		}
269		error = 0;
270		if (fh->fddi_dhost[0] & 1)
271			m->m_flags |= (M_BCAST|M_MCAST);
272		goto queue_it;
273	}
274	default:
275		printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
276			dst->sa_family);
277		senderr(EAFNOSUPPORT);
278	}
279
280	if (type != 0) {
281		struct llc *l;
282		M_PREPEND(m, sizeof (struct llc), M_DONTWAIT);
283		if (m == 0)
284			senderr(ENOBUFS);
285		l = mtod(m, struct llc *);
286		l->llc_control = LLC_UI;
287		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
288		l->llc_snap.org_code[0] = l->llc_snap.org_code[1] = l->llc_snap.org_code[2] = 0;
289		(void)memcpy((caddr_t) &l->llc_snap.ether_type, (caddr_t) &type,
290			sizeof(u_int16_t));
291	}
292
293	/*
294	 * Add local net header.  If no space in first mbuf,
295	 * allocate another.
296	 */
297	M_PREPEND(m, sizeof (struct fddi_header), M_DONTWAIT);
298	if (m == 0)
299		senderr(ENOBUFS);
300	fh = mtod(m, struct fddi_header *);
301	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
302 	(void)memcpy((caddr_t)fh->fddi_dhost, (caddr_t)edst, sizeof (edst));
303  queue_it:
304	if (hdrcmplt)
305		(void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)esrc,
306			sizeof(fh->fddi_shost));
307	else
308		(void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)ac->ac_enaddr,
309			sizeof(fh->fddi_shost));
310	/*
311	 * If a simplex interface, and the packet is being sent to our
312	 * Ethernet address or a broadcast address, loopback a copy.
313	 * XXX To make a simplex device behave exactly like a duplex
314	 * device, we should copy in the case of sending to our own
315	 * ethernet address (thus letting the original actually appear
316	 * on the wire). However, we don't do that here for security
317	 * reasons and compatibility with the original behavior.
318	 */
319	if ((ifp->if_flags & IFF_SIMPLEX) &&
320	   (loop_copy != -1)) {
321		if ((m->m_flags & M_BCAST) || loop_copy) {
322			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
323
324			(void) if_simloop(ifp,
325				n, dst->sa_family, sizeof(struct fddi_header));
326	     	} else if (bcmp(fh->fddi_dhost,
327		    fh->fddi_shost, sizeof(fh->fddi_shost)) == 0) {
328			(void) if_simloop(ifp,
329				m, dst->sa_family, sizeof(struct fddi_header));
330			return (0);	/* XXX */
331		}
332	}
333
334	if (! IF_HANDOFF(&ifp->if_snd, m, ifp))
335		senderr(ENOBUFS);
336	return (error);
337
338bad:
339	if (m)
340		m_freem(m);
341	return (error);
342}
343
344/*
345 * Process a received FDDI packet;
346 * the packet is in the mbuf chain m without
347 * the fddi header, which is provided separately.
348 */
349void
350fddi_input(ifp, fh, m)
351	struct ifnet *ifp;
352	struct fddi_header *fh;
353	struct mbuf *m;
354{
355	struct ifqueue *inq;
356	struct llc *l;
357
358	if ((ifp->if_flags & IFF_UP) == 0) {
359		m_freem(m);
360		return;
361	}
362	getmicrotime(&ifp->if_lastchange);
363	ifp->if_ibytes += m->m_pkthdr.len + sizeof (*fh);
364	if (fh->fddi_dhost[0] & 1) {
365		if (bcmp((caddr_t)fddibroadcastaddr, (caddr_t)fh->fddi_dhost,
366		    sizeof(fddibroadcastaddr)) == 0)
367			m->m_flags |= M_BCAST;
368		else
369			m->m_flags |= M_MCAST;
370		ifp->if_imcasts++;
371	} else if ((ifp->if_flags & IFF_PROMISC)
372	    && bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t)fh->fddi_dhost,
373		    sizeof(fh->fddi_dhost)) != 0) {
374		m_freem(m);
375		return;
376	}
377
378#ifdef M_LINK0
379	/*
380	 * If this has a LLC priority of 0, then mark it so upper
381	 * layers have a hint that it really came via a FDDI/Ethernet
382	 * bridge.
383	 */
384	if ((fh->fddi_fc & FDDIFC_LLC_PRIO7) == FDDIFC_LLC_PRIO0)
385		m->m_flags |= M_LINK0;
386#endif
387
388	l = mtod(m, struct llc *);
389	switch (l->llc_dsap) {
390#if defined(INET) || defined(INET6) || defined(NS) || defined(DECNET) || defined(IPX) || defined(NETATALK)
391	case LLC_SNAP_LSAP:
392	{
393		u_int16_t type;
394		if (l->llc_control != LLC_UI || l->llc_ssap != LLC_SNAP_LSAP)
395			goto dropanyway;
396#ifdef NETATALK
397		if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code,
398			 sizeof(at_org_code)) == 0 &&
399		 	ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) {
400		    inq = &atintrq2;
401		    m_adj( m, sizeof( struct llc ));
402		    schednetisr(NETISR_ATALK);
403		    break;
404		}
405
406		if (Bcmp(&(l->llc_snap_org_code)[0], aarp_org_code,
407			 sizeof(aarp_org_code)) == 0 &&
408			ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) {
409		    m_adj( m, sizeof( struct llc ));
410		    aarpinput(IFP2AC(ifp), m); /* XXX */
411		    return;
412		}
413#endif /* NETATALK */
414		if (l->llc_snap.org_code[0] != 0 || l->llc_snap.org_code[1] != 0|| l->llc_snap.org_code[2] != 0)
415			goto dropanyway;
416		type = ntohs(l->llc_snap.ether_type);
417		m_adj(m, 8);
418		switch (type) {
419#ifdef INET
420		case ETHERTYPE_IP:
421			if (ipflow_fastforward(m))
422				return;
423			schednetisr(NETISR_IP);
424			inq = &ipintrq;
425			break;
426
427		case ETHERTYPE_ARP:
428			if (ifp->if_flags & IFF_NOARP)
429				goto dropanyway;
430			schednetisr(NETISR_ARP);
431			inq = &arpintrq;
432			break;
433#endif
434#ifdef INET6
435		case ETHERTYPE_IPV6:
436			schednetisr(NETISR_IPV6);
437			inq = &ip6intrq;
438			break;
439#endif
440#ifdef IPX
441		case ETHERTYPE_IPX:
442			schednetisr(NETISR_IPX);
443			inq = &ipxintrq;
444			break;
445#endif
446#ifdef NS
447		case ETHERTYPE_NS:
448			schednetisr(NETISR_NS);
449			inq = &nsintrq;
450			break;
451#endif
452#ifdef DECNET
453		case ETHERTYPE_DECNET:
454			schednetisr(NETISR_DECNET);
455			inq = &decnetintrq;
456			break;
457#endif
458#ifdef NETATALK
459		case ETHERTYPE_AT:
460	                schednetisr(NETISR_ATALK);
461			inq = &atintrq1;
462			break;
463	        case ETHERTYPE_AARP:
464			/* probably this should be done with a NETISR as well */
465			aarpinput(IFP2AC(ifp), m); /* XXX */
466			return;
467#endif /* NETATALK */
468		default:
469			/* printf("fddi_input: unknown protocol 0x%x\n", type); */
470			ifp->if_noproto++;
471			goto dropanyway;
472		}
473		break;
474	}
475#endif /* INET || NS */
476
477	default:
478		/* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */
479		ifp->if_noproto++;
480	dropanyway:
481		m_freem(m);
482		return;
483	}
484
485	(void) IF_HANDOFF(inq, m, NULL);
486}
487/*
488 * Perform common duties while attaching to interface list
489 */
490
491void
492fddi_ifattach(ifp)
493	struct ifnet *ifp;
494{
495	struct ifaddr *ifa;
496	struct sockaddr_dl *sdl;
497
498	ifp->if_type = IFT_FDDI;
499	ifp->if_addrlen = 6;
500	ifp->if_hdrlen = 21;
501	ifp->if_mtu = FDDIMTU;
502	ifp->if_resolvemulti = fddi_resolvemulti;
503	ifp->if_baudrate = 100000000;
504#ifdef IFF_NOTRAILERS
505	ifp->if_flags |= IFF_NOTRAILERS;
506#endif
507	ifp->if_broadcastaddr = fddibroadcastaddr;
508	ifa = ifaddr_byindex(ifp->if_index);
509	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
510	sdl->sdl_type = IFT_FDDI;
511	sdl->sdl_alen = ifp->if_addrlen;
512	bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
513}
514
515static int
516fddi_resolvemulti(ifp, llsa, sa)
517	struct ifnet *ifp;
518	struct sockaddr **llsa;
519	struct sockaddr *sa;
520{
521	struct sockaddr_dl *sdl;
522	struct sockaddr_in *sin;
523#ifdef INET6
524	struct sockaddr_in6 *sin6;
525#endif
526	u_char *e_addr;
527
528	switch(sa->sa_family) {
529	case AF_LINK:
530		/*
531		 * No mapping needed. Just check that it's a valid MC address.
532		 */
533		sdl = (struct sockaddr_dl *)sa;
534		e_addr = LLADDR(sdl);
535		if ((e_addr[0] & 1) != 1)
536			return (EADDRNOTAVAIL);
537		*llsa = 0;
538		return (0);
539
540#ifdef INET
541	case AF_INET:
542		sin = (struct sockaddr_in *)sa;
543		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
544			return (EADDRNOTAVAIL);
545		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
546		       M_WAITOK);
547		sdl->sdl_len = sizeof *sdl;
548		sdl->sdl_family = AF_LINK;
549		sdl->sdl_index = ifp->if_index;
550		sdl->sdl_type = IFT_FDDI;
551		sdl->sdl_nlen = 0;
552		sdl->sdl_alen = ETHER_ADDR_LEN;	/* XXX */
553		sdl->sdl_slen = 0;
554		e_addr = LLADDR(sdl);
555		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
556		*llsa = (struct sockaddr *)sdl;
557		return (0);
558#endif
559#ifdef INET6
560	case AF_INET6:
561		sin6 = (struct sockaddr_in6 *)sa;
562		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
563			/*
564			 * An IP6 address of 0 means listen to all
565			 * of the Ethernet multicast address used for IP6.
566			 * (This is used for multicast routers.)
567			 */
568			ifp->if_flags |= IFF_ALLMULTI;
569			*llsa = 0;
570			return (0);
571		}
572		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
573			return (EADDRNOTAVAIL);
574		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
575		       M_WAITOK);
576		sdl->sdl_len = sizeof *sdl;
577		sdl->sdl_family = AF_LINK;
578		sdl->sdl_index = ifp->if_index;
579		sdl->sdl_type = IFT_FDDI;
580		sdl->sdl_nlen = 0;
581		sdl->sdl_alen = ETHER_ADDR_LEN;	/* XXX */
582		sdl->sdl_slen = 0;
583		e_addr = LLADDR(sdl);
584		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
585		*llsa = (struct sockaddr *)sdl;
586		return (0);
587#endif
588
589	default:
590		/*
591		 * Well, the text isn't quite right, but it's the name
592		 * that counts...
593		 */
594		return (EAFNOSUPPORT);
595	}
596}
597