if_iso88025subr.c revision 112280
1/*
2 * Copyright (c) 1998, Larry Lile
3 * All rights reserved.
4 *
5 * For latest sources and information on this driver, please
6 * go to http://anarchy.stdio.com.
7 *
8 * Questions, comments or suggestions should be directed to
9 * Larry Lile <lile@stdio.com>.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice unmodified, this list of conditions, and the following
16 *    disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: head/sys/net/if_iso88025subr.c 112280 2003-03-15 20:33:30Z mdodd $
34 *
35 */
36
37/*
38 *
39 * General ISO 802.5 (Token Ring) support routines
40 *
41 */
42
43#include "opt_inet.h"
44#include "opt_inet6.h"
45#include "opt_ipx.h"
46
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/mbuf.h>
52#include <sys/module.h>
53#include <sys/socket.h>
54#include <sys/sockio.h>
55
56#include <net/if.h>
57#include <net/if_dl.h>
58#include <net/if_llc.h>
59#include <net/if_types.h>
60
61#include <net/netisr.h>
62#include <net/route.h>
63#include <net/bpf.h>
64#include <net/iso88025.h>
65
66#if defined(INET) || defined(INET6)
67#include <netinet/in.h>
68#include <netinet/in_var.h>
69#include <netinet/if_ether.h>
70#endif
71#ifdef INET6
72#include <netinet6/nd6.h>
73#endif
74
75#ifdef IPX
76#include <netipx/ipx.h>
77#include <netipx/ipx_if.h>
78#endif
79
80static u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
81			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
82
83static int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
84				  struct sockaddr *));
85
86#define	IFP2AC(IFP)	((struct arpcom *)IFP)
87#define	senderr(e)	do { error = (e); goto bad; } while (0)
88
89void
90iso88025_ifattach(struct ifnet *ifp)
91{
92    struct ifaddr *ifa = NULL;
93    struct sockaddr_dl *sdl;
94
95    ifp->if_type = IFT_ISO88025;
96    ifp->if_addrlen = ISO88025_ADDR_LEN;
97    ifp->if_hdrlen = ISO88025_HDR_LEN;
98    if (ifp->if_baudrate == 0)
99        ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
100    if (ifp->if_mtu == 0)
101        ifp->if_mtu = ISO88025_DEFAULT_MTU;
102    ifp->if_broadcastaddr = iso88025_broadcastaddr;
103
104    ifa = ifaddr_byindex(ifp->if_index);
105    if (ifa == 0) {
106            printf("iso88025_ifattach: no lladdr!\n");
107            return;
108    }
109    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
110    sdl->sdl_type = IFT_ISO88025;
111    sdl->sdl_alen = ifp->if_addrlen;
112    bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
113}
114
115/*
116 * Perform common duties while detaching a Token Ring interface
117 */
118void
119iso88025_ifdetach(ifp, bpf)
120        struct ifnet *ifp;
121        int bpf;
122{
123
124	if (bpf)
125                bpfdetach(ifp);
126
127	if_detach(ifp);
128
129	return;
130}
131
132int
133iso88025_ioctl(struct ifnet *ifp, int command, caddr_t data)
134{
135        struct ifaddr *ifa;
136        struct ifreq *ifr;
137        int error;
138
139	ifa = (struct ifaddr *) data;
140	ifr = (struct ifreq *) data;
141	error = 0;
142
143        switch (command) {
144        case SIOCSIFADDR:
145                ifp->if_flags |= IFF_UP;
146
147                switch (ifa->ifa_addr->sa_family) {
148#ifdef INET
149                case AF_INET:
150                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
151                        arp_ifinit(ifp, ifa);
152                        break;
153#endif	/* INET */
154#ifdef IPX
155                /*
156                 * XXX - This code is probably wrong
157                 */
158                case AF_IPX:
159                        {
160                        struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
161                        struct arpcom *ac = IFP2AC(ifp);
162
163                        if (ipx_nullhost(*ina))
164                                ina->x_host = *(union ipx_host *)ac->ac_enaddr;
165                        else {
166                                bcopy((caddr_t) ina->x_host.c_host,
167                                      (caddr_t) ac->ac_enaddr,
168                                      ISO88025_ADDR_LEN);
169                        }
170
171                        /*
172                         * Set new address
173                         */
174                        ifp->if_init(ifp->if_softc);
175                        break;
176                        }
177#endif	/* IPX */
178                default:
179                        ifp->if_init(ifp->if_softc);
180                        break;
181                }
182                break;
183
184        case SIOCGIFADDR:
185                {
186                        struct sockaddr *sa;
187
188                        sa = (struct sockaddr *) & ifr->ifr_data;
189                        bcopy(IFP2AC(ifp)->ac_enaddr,
190                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
191                }
192                break;
193
194        case SIOCSIFMTU:
195                /*
196                 * Set the interface MTU.
197                 */
198                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
199                        error = EINVAL;
200                } else {
201                        ifp->if_mtu = ifr->ifr_mtu;
202                }
203                break;
204	default:
205		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
206		break;
207        }
208
209        return (error);
210}
211
212/*
213 * ISO88025 encapsulation
214 */
215int
216iso88025_output(ifp, m, dst, rt0)
217	struct ifnet *ifp;
218	struct mbuf *m;
219	struct sockaddr *dst;
220	struct rtentry *rt0;
221{
222	u_int16_t snap_type = 0;
223	int loop_copy = 0, error = 0, rif_len = 0;
224	u_char edst[ISO88025_ADDR_LEN];
225	struct iso88025_header *th;
226	struct iso88025_header gen_th;
227	struct sockaddr_dl *sdl = NULL;
228	struct rtentry *rt;
229	struct arpcom *ac = (struct arpcom *)ifp;
230
231	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
232		senderr(ENETDOWN);
233	getmicrotime(&ifp->if_lastchange);
234
235	error = rt_check(&rt, &rt0, dst);
236	if (error)
237		goto bad;
238
239	/* Calculate routing info length based on arp table entry */
240	if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway))
241		if (SDL_ISO88025(sdl)->trld_rcf != 0)
242			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
243
244	/* Generate a generic 802.5 header for the packet */
245	gen_th.ac = TR_AC;
246	gen_th.fc = TR_LLC_FRAME;
247	(void)memcpy((caddr_t)gen_th.iso88025_shost, (caddr_t)ac->ac_enaddr,
248		     ISO88025_ADDR_LEN);
249	if (rif_len) {
250		gen_th.iso88025_shost[0] |= TR_RII;
251		if (rif_len > 2) {
252			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
253			(void)memcpy((caddr_t)gen_th.rd,
254				(caddr_t)SDL_ISO88025(sdl)->trld_route,
255				rif_len - 2);
256		}
257	}
258
259	switch (dst->sa_family) {
260#ifdef INET
261	case AF_INET:
262		if (!arpresolve(ifp, rt, m, dst, edst, rt0))
263			return (0);	/* if not yet resolved */
264		snap_type = ETHERTYPE_IP;
265		break;
266#endif	/* INET */
267#ifdef NOT_YET
268#ifdef INET6
269	case AF_INET6:
270		if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) {
271			/* Something bad happened */
272			return(0);
273		}
274		snap_type = ETHERTYPE_IPV6;
275		break;
276#endif	/* INET6 */
277#endif	/* NOT_YET */
278#ifdef IPX
279	case AF_IPX:
280	{
281		u_int8_t	*cp;
282
283		snap_type = 0;
284		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
285		      ISO88025_ADDR_LEN);
286
287		M_PREPEND(m, 3, M_TRYWAIT);
288		if (m == 0)
289			senderr(ENOBUFS);
290		m = m_pullup(m, 3);
291		if (m == 0)
292			senderr(ENOBUFS);
293		cp = mtod(m, u_int8_t *);
294		*cp++ = ETHERTYPE_IPX_8022;
295		*cp++ = ETHERTYPE_IPX_8022;
296		*cp++ = LLC_UI;
297	}
298	break;
299#endif	/* IPX */
300	case AF_UNSPEC:
301	{
302		struct iso88025_sockaddr_data *sd;
303		/*
304		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
305		 * mac information needed to send the packet.  This allows
306		 * full mac, llc, and source routing function to be controlled.
307		 * llc and source routing information must already be in the
308		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
309		 * should be an iso88025_sockaddr_data structure see iso88025.h
310		 */
311                loop_copy = -1;
312		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
313		gen_th.ac = sd->ac;
314		gen_th.fc = sd->fc;
315		(void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost,
316			     ISO88025_ADDR_LEN);
317		(void)memcpy((caddr_t)gen_th.iso88025_shost,
318			     (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN);
319		rif_len = 0;
320		break;
321	}
322	default:
323		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
324		senderr(EAFNOSUPPORT);
325		break;
326	}
327
328	/*
329	 * Add LLC header.
330	 */
331	if (snap_type != 0) {
332        	struct llc *l;
333		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
334		if (m == 0)
335			senderr(ENOBUFS);
336		l = mtod(m, struct llc *);
337		l->llc_snap.control = LLC_UI;
338		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
339		l->llc_snap.org_code[0] =
340			l->llc_snap.org_code[1] =
341			l->llc_snap.org_code[2] = 0;
342		l->llc_snap.ether_type = htons(snap_type);
343	}
344
345	(void)memcpy((caddr_t)&gen_th.iso88025_dhost, (caddr_t)edst,
346		     ISO88025_ADDR_LEN);
347
348	/*
349	 * Add local net header.  If no space in first mbuf,
350	 * allocate another.
351	 */
352	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
353	if (m == 0)
354		senderr(ENOBUFS);
355	th = mtod(m, struct iso88025_header *);
356
357	/* Copy as much of the generic header as is needed into the mbuf */
358	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
359
360        /*
361         * If a simplex interface, and the packet is being sent to our
362         * Ethernet address or a broadcast address, loopback a copy.
363         * XXX To make a simplex device behave exactly like a duplex
364         * device, we should copy in the case of sending to our own
365         * ethernet address (thus letting the original actually appear
366         * on the wire). However, we don't do that here for security
367         * reasons and compatibility with the original behavior.
368         */
369        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
370                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
371                        struct mbuf *n;
372			n = m_copy(m, 0, (int)M_COPYALL);
373                        (void) if_simloop(ifp, n, dst->sa_family,
374					  ISO88025_HDR_LEN);
375                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
376				 ETHER_ADDR_LEN) == 0) {
377			(void) if_simloop(ifp, m, dst->sa_family,
378					  ISO88025_HDR_LEN);
379                       	return(0);      /* XXX */
380		}
381        }
382
383	if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN) ) {
384		printf("iso88025_output: packet dropped QFULL.\n");
385		senderr(ENOBUFS);
386	}
387	return (error);
388
389bad:
390	if (m)
391		m_freem(m);
392	return (error);
393}
394
395/*
396 * ISO 88025 de-encapsulation
397 */
398void
399iso88025_input(ifp, th, m)
400	struct ifnet *ifp;
401	struct iso88025_header *th;
402	struct mbuf *m;
403{
404	int isr;
405	struct llc *l;
406
407	if ((ifp->if_flags & IFF_UP) == 0) {
408		m_freem(m);
409		return;
410	}
411
412	getmicrotime(&ifp->if_lastchange);
413	ifp->if_ibytes += m->m_pkthdr.len + sizeof(*th);
414
415	/*
416	 * Set mbuf flags for bcast/mcast.
417	 */
418	if (th->iso88025_dhost[0] & 1) {
419		if (bcmp((caddr_t)iso88025_broadcastaddr,
420			 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN) == 0)
421			m->m_flags |= M_BCAST;
422		else
423			m->m_flags |= M_MCAST;
424		ifp->if_imcasts++;
425	}
426
427	l = mtod(m, struct llc *);
428
429	switch (l->llc_dsap) {
430#ifdef IPX
431	case ETHERTYPE_IPX_8022:	/* Thanks a bunch Novell */
432		if ((l->llc_control != LLC_UI) ||
433		    (l->llc_ssap != ETHERTYPE_IPX_8022))
434			goto dropanyway;
435
436		th->iso88025_shost[0] &= ~(TR_RII);
437		m_adj(m, 3);
438		isr = NETISR_IPX;
439		break;
440#endif	/* IPX */
441	case LLC_SNAP_LSAP: {
442		u_int16_t type;
443		if ((l->llc_control != LLC_UI) ||
444		    (l->llc_ssap != LLC_SNAP_LSAP))
445			goto dropanyway;
446
447		if (l->llc_snap.org_code[0] != 0 ||
448		    l->llc_snap.org_code[1] != 0 ||
449		    l->llc_snap.org_code[2] != 0)
450			goto dropanyway;
451
452		type = ntohs(l->llc_snap.ether_type);
453		m_adj(m, LLC_SNAPFRAMELEN);
454		switch (type) {
455#ifdef INET
456		case ETHERTYPE_IP:
457			th->iso88025_shost[0] &= ~(TR_RII);
458			if (ipflow_fastforward(m))
459				return;
460			isr = NETISR_IP;
461			break;
462
463		case ETHERTYPE_ARP:
464			if (ifp->if_flags & IFF_NOARP)
465				goto dropanyway;
466			isr = NETISR_ARP;
467			break;
468#endif	/* INET */
469#ifdef IPX_SNAP	/* XXX: Not supported! */
470		case ETHERTYPE_IPX:
471			th->iso88025_shost[0] &= ~(TR_RII);
472			isr = NETISR_IPX;
473			break;
474#endif	/* IPX_SNAP */
475#ifdef NOT_YET
476#ifdef INET6
477		case ETHERTYPE_IPV6:
478			th->iso88025_shost[0] &= ~(TR_RII);
479			isr = NETISR_IPV6;
480			break;
481#endif	/* INET6 */
482#endif	/* NOT_YET */
483		default:
484			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
485			m_freem(m);
486			return;
487		}
488		break;
489	}
490	case LLC_ISO_LSAP:
491		switch (l->llc_control) {
492		case LLC_UI:
493			goto dropanyway;
494			break;
495                case LLC_XID:
496                case LLC_XID_P:
497			if(m->m_len < ISO88025_ADDR_LEN)
498				goto dropanyway;
499			l->llc_window = 0;
500			l->llc_fid = 9;
501			l->llc_class = 1;
502			l->llc_dsap = l->llc_ssap = 0;
503			/* Fall through to */
504		case LLC_TEST:
505		case LLC_TEST_P:
506		{
507			struct sockaddr sa;
508			struct arpcom *ac = (struct arpcom *)ifp;
509			struct iso88025_sockaddr_data *th2;
510			int i;
511			u_char c = l->llc_dsap;
512
513			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
514				printf("iso88025_input: dropping source routed LLC_TEST\n");
515				m_free(m);
516				return;
517			}
518			l->llc_dsap = l->llc_ssap;
519			l->llc_ssap = c;
520			if (m->m_flags & (M_BCAST | M_MCAST))
521				bcopy((caddr_t)ac->ac_enaddr,
522				      (caddr_t)th->iso88025_dhost,
523					ISO88025_ADDR_LEN);
524			sa.sa_family = AF_UNSPEC;
525			sa.sa_len = sizeof(sa);
526			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
527			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
528				th2->ether_shost[i] = c = th->iso88025_dhost[i];
529				th2->ether_dhost[i] = th->iso88025_dhost[i] =
530					th->iso88025_shost[i];
531				th->iso88025_shost[i] = c;
532			}
533			th2->ac = TR_AC;
534			th2->fc = TR_LLC_FRAME;
535			ifp->if_output(ifp, m, &sa, NULL);
536			return;
537		}
538		default:
539			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
540			m_freem(m);
541			return;
542		}
543		break;
544	default:
545		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
546		ifp->if_noproto++;
547	dropanyway:
548		m_freem(m);
549		return;
550	}
551
552	netisr_dispatch(isr, m);
553	return;
554}
555
556static int
557iso88025_resolvemulti (ifp, llsa, sa)
558	struct ifnet *ifp;
559	struct sockaddr **llsa;
560	struct sockaddr *sa;
561{
562	struct sockaddr_dl *sdl;
563	struct sockaddr_in *sin;
564#ifdef INET6
565	struct sockaddr_in6 *sin6;
566#endif
567	u_char *e_addr;
568
569	switch(sa->sa_family) {
570	case AF_LINK:
571		/*
572		 * No mapping needed. Just check that it's a valid MC address.
573		 */
574		sdl = (struct sockaddr_dl *)sa;
575		e_addr = LLADDR(sdl);
576		if ((e_addr[0] & 1) != 1) {
577			return (EADDRNOTAVAIL);
578		}
579		*llsa = 0;
580		return (0);
581
582#ifdef INET
583	case AF_INET:
584		sin = (struct sockaddr_in *)sa;
585		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
586			return (EADDRNOTAVAIL);
587		}
588		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
589		       M_WAITOK|M_ZERO);
590		sdl->sdl_len = sizeof *sdl;
591		sdl->sdl_family = AF_LINK;
592		sdl->sdl_index = ifp->if_index;
593		sdl->sdl_type = IFT_ISO88025;
594		sdl->sdl_alen = ISO88025_ADDR_LEN;
595		e_addr = LLADDR(sdl);
596		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
597		*llsa = (struct sockaddr *)sdl;
598		return (0);
599#endif
600#ifdef INET6
601	case AF_INET6:
602		sin6 = (struct sockaddr_in6 *)sa;
603		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
604			/*
605			 * An IP6 address of 0 means listen to all
606			 * of the Ethernet multicast address used for IP6.
607			 * (This is used for multicast routers.)
608			 */
609			ifp->if_flags |= IFF_ALLMULTI;
610			*llsa = 0;
611			return (0);
612		}
613		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
614			return (EADDRNOTAVAIL);
615		}
616		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
617		       M_WAITOK|M_ZERO);
618		sdl->sdl_len = sizeof *sdl;
619		sdl->sdl_family = AF_LINK;
620		sdl->sdl_index = ifp->if_index;
621		sdl->sdl_type = IFT_ISO88025;
622		sdl->sdl_alen = ISO88025_ADDR_LEN;
623		e_addr = LLADDR(sdl);
624		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
625		*llsa = (struct sockaddr *)sdl;
626		return (0);
627#endif
628
629	default:
630		/*
631		 * Well, the text isn't quite right, but it's the name
632		 * that counts...
633		 */
634		return (EAFNOSUPPORT);
635	}
636
637	return (0);
638}
639
640static moduledata_t iso88025_mod = {
641	"iso88025",
642	NULL,
643	0
644};
645
646DECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
647MODULE_VERSION(iso88025, 1);
648