xref: /netbsd-src/sys/net/if_arcsubr.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: if_arcsubr.c,v 1.17 1998/07/05 00:51:26 jonathan Exp $	*/
2 
3 /*
4  * Copyright (c) 1994, 1995 Ignatios Souvatzis
5  * Copyright (c) 1982, 1989, 1993
6  *	The Regents of the University of California.  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 the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp
37  *       @(#)if_ethersubr.c	8.1 (Berkeley) 6/10/93
38  *
39  */
40 #include "opt_inet.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/ioctl.h>
50 #include <sys/errno.h>
51 #include <sys/syslog.h>
52 
53 #include <machine/cpu.h>
54 
55 #include <net/if.h>
56 #include <net/netisr.h>
57 #include <net/route.h>
58 #include <net/if_dl.h>
59 #include <net/if_types.h>
60 #include <net/if_arc.h>
61 #include <net/if_arp.h>
62 #include <net/if_ether.h>
63 
64 #ifdef INET
65 #include <netinet/in.h>
66 #include <netinet/in_var.h>
67 #include <netinet/if_inarp.h>
68 #endif
69 
70 #define ARCNET_ALLOW_BROKEN_ARP
71 
72 #ifndef	ARC_PHDSMTU
73 #define	ARC_PHDSMTU	1500
74 #endif
75 
76 static struct mbuf *arc_defrag __P((struct ifnet *, struct mbuf *));
77 
78 /*
79  * RC1201 requires us to have this configurable. We have it only per
80  * machine at the moment... there is no generic "set mtu" ioctl, AFAICS.
81  * Anyway, it is possible to binpatch this or set it per kernel config
82  * option.
83  */
84 #if ARC_PHDSMTU > 60480
85 ERROR: The arc_phdsmtu is ARC_PHDSMTU, but must not exceed 60480.
86 #endif
87 u_int16_t arc_phdsmtu = ARC_PHDSMTU;
88 u_int8_t  arcbroadcastaddr = 0;
89 
90 #define senderr(e) { error = (e); goto bad;}
91 #define SIN(s) ((struct sockaddr_in *)s)
92 
93 /*
94  * ARCnet output routine.
95  * Encapsulate a packet of type family for the local net.
96  * Assumes that ifp is actually pointer to arccom structure.
97  */
98 int
99 arc_output(ifp, m0, dst, rt0)
100 	register struct ifnet *ifp;
101 	struct mbuf *m0;
102 	struct sockaddr *dst;
103 	struct rtentry *rt0;
104 {
105 	struct mbuf		*m, *m1, *mcopy;
106 	struct rtentry		*rt;
107 	struct arccom		*ac;
108 	struct arc_header	*ah;
109 	struct arphdr		*arph;
110 	int			s, error, newencoding;
111 	u_int8_t		atype, adst, myself;
112 	int			tfrags, sflag, fsflag, rsflag;
113 
114 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
115 		return(ENETDOWN); /* m, m1 aren't initialized yet */
116 
117 	error = newencoding = 0;
118 	ac = (struct arccom *)ifp;
119 	m = m0;
120 	mcopy = m1 = NULL;
121 
122 	myself = *LLADDR(ifp->if_sadl);
123 
124 	ifp->if_lastchange = time;
125 	if ((rt = rt0)) {
126 		if ((rt->rt_flags & RTF_UP) == 0) {
127 			if ((rt0 = rt = rtalloc1(dst, 1)))
128 				rt->rt_refcnt--;
129 			else
130 				senderr(EHOSTUNREACH);
131 		}
132 		if (rt->rt_flags & RTF_GATEWAY) {
133 			if (rt->rt_gwroute == 0)
134 				goto lookup;
135 			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
136 				rtfree(rt); rt = rt0;
137 			lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
138 				if ((rt = rt->rt_gwroute) == 0)
139 					senderr(EHOSTUNREACH);
140 			}
141 		}
142 		if (rt->rt_flags & RTF_REJECT)
143 			if (rt->rt_rmx.rmx_expire == 0 ||
144 			    time.tv_sec < rt->rt_rmx.rmx_expire)
145 				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
146 	}
147 
148 	switch (dst->sa_family) {
149 #ifdef INET
150 	case AF_INET:
151 
152 		/*
153 		 * For now, use the simple IP addr -> ARCnet addr mapping
154 		 */
155 		if (m->m_flags & (M_BCAST|M_MCAST))
156 			adst = arcbroadcastaddr; /* ARCnet broadcast address */
157 		else if (ifp->if_flags & IFF_NOARP)
158 			adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF;
159 		else if (!arpresolve(ifp, rt, m, dst, &adst))
160 			return 0;	/* not resolved yet */
161 
162 		/* If broadcasting on a simplex interface, loopback a copy */
163 		if ((m->m_flags & (M_BCAST|M_MCAST)) &&
164 		    (ifp->if_flags & IFF_SIMPLEX))
165 			mcopy = m_copy(m, 0, (int)M_COPYALL);
166 		if (ifp->if_flags & IFF_LINK0) {
167 			atype = ARCTYPE_IP;
168 			newencoding = 1;
169 		} else {
170 			atype = ARCTYPE_IP_OLD;
171 			newencoding = 0;
172 		}
173 		break;
174 
175 	case AF_ARP:
176 		arph = mtod(m, struct arphdr *);
177 		if (m->m_flags & M_BCAST)
178 			adst = arcbroadcastaddr;
179 		else
180 			adst = *ar_tha(arph);
181 
182 		arph->ar_hrd = htons(ARPHRD_ARCNET);
183 
184 		switch(ntohs(arph->ar_op)) {
185 
186 		case ARPOP_REVREQUEST:
187 		case ARPOP_REVREPLY:
188 			if (!(ifp->if_flags & IFF_LINK0)) {
189 				printf("%s: can't handle af%d\n",
190 				    ifp->if_xname, dst->sa_family);
191 				senderr(EAFNOSUPPORT);
192 			}
193 
194 			atype = htons(ARCTYPE_REVARP);
195 			newencoding = 1;
196 			break;
197 
198 		case ARPOP_REQUEST:
199 		case ARPOP_REPLY:
200 		default:
201 			if (ifp->if_flags & IFF_LINK0) {
202 				atype = htons(ARCTYPE_ARP);
203 				newencoding = 1;
204 			} else {
205 				atype = htons(ARCTYPE_ARP_OLD);
206 				newencoding = 0;
207 			}
208 		}
209 #ifdef ARCNET_ALLOW_BROKEN_ARP
210 		/*
211 		 * XXX It's not clear per RFC826 if this is needed, but
212 		 * "assigned numbers" say this is wrong.
213 		 * However, e.g., AmiTCP 3.0Beta used it... we make this
214 		 * switchable for emergency cases. Not perfect, but...
215 		 */
216 		if (ifp->if_flags & IFF_LINK2)
217 			arph->ar_pro = atype - 1;
218 #endif
219 		break;
220 #endif
221 
222 	case AF_UNSPEC:
223 		ah = (struct arc_header *)dst->sa_data;
224  		adst = ah->arc_dhost;
225 		atype = ah->arc_type;
226 		break;
227 
228 	default:
229 		printf("%s: can't handle af%d\n", ifp->if_xname,
230 		    dst->sa_family);
231 		senderr(EAFNOSUPPORT);
232 	}
233 
234 	if (mcopy)
235 		(void) looutput(ifp, mcopy, dst, rt);
236 
237 	/*
238 	 * Add local net header.  If no space in first mbuf,
239 	 * allocate another.
240 	 *
241 	 * For ARCnet, this is just symbolic. The header changes
242 	 * form and position on its way into the hardware and out of
243 	 * the wire.  At this point, it contains source, destination and
244 	 * packet type.
245 	 */
246 	if (newencoding) {
247 		++ac->ac_seqid; /* make the seqid unique */
248 
249 		tfrags = (m->m_pkthdr.len + 503) / 504;
250 		fsflag = 2 * tfrags - 3;
251 		sflag = 0;
252 		rsflag = fsflag;
253 
254 		while (sflag < fsflag) {
255 			/* we CAN'T have short packets here */
256 			m1 = m_split(m, 504, M_DONTWAIT);
257 			if (m1 == 0)
258 				senderr(ENOBUFS);
259 
260 			M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT);
261 			if (m == 0)
262 				senderr(ENOBUFS);
263 			ah = mtod(m, struct arc_header *);
264 			ah->arc_type = atype;
265 			ah->arc_dhost = adst;
266 			ah->arc_shost = myself;
267 			ah->arc_flag = rsflag;
268 			ah->arc_seqid = ac->ac_seqid;
269 
270 			s = splimp();
271 			/*
272 			 * Queue message on interface, and start output if
273 			 * interface not yet active.
274 			 */
275 			if (IF_QFULL(&ifp->if_snd)) {
276 				IF_DROP(&ifp->if_snd);
277 				splx(s);
278 				senderr(ENOBUFS);
279 			}
280 			ifp->if_obytes += m->m_pkthdr.len;
281 			IF_ENQUEUE(&ifp->if_snd, m);
282 			if ((ifp->if_flags & IFF_OACTIVE) == 0)
283 				(*ifp->if_start)(ifp);
284 			splx(s);
285 
286 			m = m1;
287 			sflag += 2;
288 			rsflag = sflag;
289 		}
290 		m1 = NULL;
291 
292 		M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT);
293 		if (m == 0)
294 			senderr(ENOBUFS);
295 		ah = mtod(m, struct arc_header *);
296 		ah->arc_type = atype;
297 		ah->arc_flag = sflag;
298 		ah->arc_seqid = ac->ac_seqid;
299 
300 		/* here we can have small, especially forbidden packets */
301 
302 		if ((m->m_pkthdr.len >= ARC_MIN_FORBID_LEN + 2) &&
303 		    (m->m_pkthdr.len <= ARC_MAX_FORBID_LEN + 2)) {
304 			M_PREPEND(m, 4, M_DONTWAIT);
305 			if (m == 0)
306 				senderr(ENOBUFS);
307 			m = m_pullup(m, ARC_HDRNEWLEN);
308 			if (m == 0)
309 				senderr(ENOBUFS);
310 			ah = mtod(m, struct arc_header *);
311 			ah->arc_type = atype;
312 			ah->arc_flag = 0xFF;
313 			ah->arc_seqid = 0xFFFF;
314 		}
315 
316 		ah->arc_dhost = adst;
317 		ah->arc_shost = myself;
318 	} else {
319 		M_PREPEND(m, ARC_HDRLEN, M_DONTWAIT);
320 		if (m == 0)
321 			senderr(ENOBUFS);
322 		ah = mtod(m, struct arc_header *);
323 		ah->arc_type = atype;
324 		ah->arc_dhost = adst;
325 		ah->arc_shost = myself;
326 	}
327 
328 	s = splimp();
329 	/*
330 	 * Queue message on interface, and start output if interface
331 	 * not yet active.
332 	 */
333 	if (IF_QFULL(&ifp->if_snd)) {
334 		IF_DROP(&ifp->if_snd);
335 		splx(s);
336 		senderr(ENOBUFS);
337 	}
338 	ifp->if_obytes += m->m_pkthdr.len;
339 	IF_ENQUEUE(&ifp->if_snd, m);
340 	if ((ifp->if_flags & IFF_OACTIVE) == 0)
341 		(*ifp->if_start)(ifp);
342 	splx(s);
343 
344 	return (error);
345 
346 bad:
347 	if (m1)
348 		m_freem(m1);
349 	if (m)
350 		m_freem(m);
351 	return (error);
352 }
353 
354 /*
355  * Defragmenter. Returns mbuf if last packet found, else
356  * NULL. frees imcoming mbuf as necessary.
357  */
358 
359 __inline struct mbuf *
360 arc_defrag(ifp, m)
361 	struct ifnet *ifp;
362 	struct mbuf *m;
363 {
364 	struct arc_header *ah, *ah1;
365 	struct arccom *ac;
366 	struct ac_frag *af;
367 	struct mbuf *m1;
368 	char *s;
369 	int newflen;
370 	u_char src,dst,typ;
371 
372 	ac = (struct arccom *)ifp;
373 
374 	m = m_pullup(m, ARC_HDRNEWLEN);
375 	if (m == NULL) {
376 		++ifp->if_ierrors;
377 		return NULL;
378 	}
379 
380 	ah = mtod(m, struct arc_header *);
381 	typ = ah->arc_type;
382 
383 	if (!arc_isphds(typ))
384 		return m;
385 
386 	src = ah->arc_shost;
387 	dst = ah->arc_dhost;
388 
389 	if (ah->arc_flag == 0xff) {
390 		m_adj(m, 4);
391 
392 		m = m_pullup(m, ARC_HDRNEWLEN);
393 		if (m == NULL) {
394 			++ifp->if_ierrors;
395 			return NULL;
396 		}
397 
398 		ah = mtod(m, struct arc_header *);
399 		ah->arc_shost = src;
400 		ah->arc_dhost = dst;
401 		ah->arc_type = typ;
402 	}
403 
404 	af = &ac->ac_fragtab[src];
405 	m1 = af->af_packet;
406 	s = "debug code error";
407 
408 	if (ah->arc_flag & 1) {
409 		/*
410 		 * first fragment. We always initialize, which is
411 		 * about the right thing to do, as we only want to
412 		 * accept one fragmented packet per src at a time.
413 		 */
414 		if (m1 != NULL)
415 			m_freem(m1);
416 
417 		af->af_packet = m;
418 		m1 = m;
419 		af->af_maxflag = ah->arc_flag;
420 		af->af_lastseen = 0;
421 		af->af_seqid = ah->arc_seqid;
422 
423 		return NULL;
424 		/* notreached */
425 	} else {
426 		/* check for unfragmented packet */
427 		if (ah->arc_flag == 0)
428 			return m;
429 
430 		/* do we have a first packet from that src? */
431 		if (m1 == NULL) {
432 			s = "no first frag";
433 			goto outofseq;
434 		}
435 
436 		ah1 = mtod(m1, struct arc_header *);
437 
438 		if (ah->arc_seqid != ah1->arc_seqid) {
439 			s = "seqid differs";
440 			goto outofseq;
441 		}
442 
443 		if (ah->arc_type != ah1->arc_type) {
444 			s = "type differs";
445 			goto outofseq;
446 		}
447 
448 		if (ah->arc_dhost != ah1->arc_dhost) {
449 			s = "dest host differs";
450 			goto outofseq;
451 		}
452 
453 		/* typ, seqid and dst are ok here. */
454 
455 		if (ah->arc_flag == af->af_lastseen) {
456 			m_freem(m);
457 			return NULL;
458 		}
459 
460 		if (ah->arc_flag == af->af_lastseen + 2) {
461 			/* ok, this is next fragment */
462 			af->af_lastseen = ah->arc_flag;
463 			m_adj(m,ARC_HDRNEWLEN);
464 
465 			/*
466 			 * m_cat might free the first mbuf (with pkthdr)
467 			 * in 2nd chain; therefore:
468 			 */
469 
470 			newflen = m->m_pkthdr.len;
471 
472 			m_cat(m1,m);
473 
474 			m1->m_pkthdr.len += newflen;
475 
476 			/* is it the last one? */
477 			if (af->af_lastseen > af->af_maxflag) {
478 				af->af_packet = NULL;
479 				return(m1);
480 			} else
481 				return NULL;
482 		}
483 		s = "other reason";
484 		/* if all else fails, it is out of sequence, too */
485 	}
486 outofseq:
487 	if (m1) {
488 		m_freem(m1);
489 		af->af_packet = NULL;
490 	}
491 
492 	if (m)
493 		m_freem(m);
494 
495 	log(LOG_INFO,"%s: got out of seq. packet: %s\n",
496 	    ifp->if_xname, s);
497 
498 	return NULL;
499 }
500 
501 /*
502  * return 1 if Packet Header Definition Standard, else 0.
503  * For now: old IP, old ARP aren't obviously. Lacking correct information,
504  * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS.
505  * (Apple and Novell corporations were involved, among others, in PHDS work).
506  * Easiest is to assume that everybody else uses that, too.
507  */
508 int
509 arc_isphds(type)
510 	u_int8_t type;
511 {
512 	return ((type != ARCTYPE_IP_OLD &&
513 		 type != ARCTYPE_ARP_OLD));
514 }
515 
516 /*
517  * Process a received Arcnet packet;
518  * the packet is in the mbuf chain m with
519  * the ARCnet header.
520  */
521 void
522 arc_input(ifp, m)
523 	struct ifnet *ifp;
524 	struct mbuf *m;
525 {
526 	register struct arc_header *ah;
527 	register struct ifqueue *inq;
528 	u_int8_t atype;
529 	int s;
530 	struct arphdr *arph;
531 
532 	if ((ifp->if_flags & IFF_UP) == 0) {
533 		m_freem(m);
534 		return;
535 	}
536 
537 	/* possibly defragment: */
538 	m = arc_defrag(ifp, m);
539 	if (m == NULL)
540 		return;
541 
542 	ah = mtod(m, struct arc_header *);
543 
544 	ifp->if_lastchange = time;
545 	ifp->if_ibytes += m->m_pkthdr.len;
546 
547 	if (arcbroadcastaddr == ah->arc_dhost) {
548 		m->m_flags |= M_BCAST|M_MCAST;
549 		ifp->if_imcasts++;
550 	}
551 
552 	atype = ah->arc_type;
553 	switch (atype) {
554 #ifdef INET
555 	case ARCTYPE_IP:
556 		m_adj(m, ARC_HDRNEWLEN);
557 		schednetisr(NETISR_IP);
558 		inq = &ipintrq;
559 		break;
560 
561 	case ARCTYPE_IP_OLD:
562 		m_adj(m, ARC_HDRLEN);
563 		schednetisr(NETISR_IP);
564 		inq = &ipintrq;
565 		break;
566 
567 	case ARCTYPE_ARP:
568 		m_adj(m, ARC_HDRNEWLEN);
569 		schednetisr(NETISR_ARP);
570 		inq = &arpintrq;
571 #ifdef ARCNET_ALLOW_BROKEN_ARP
572 		mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
573 #endif
574 		break;
575 
576 	case ARCTYPE_ARP_OLD:
577 		m_adj(m, ARC_HDRLEN);
578 		schednetisr(NETISR_ARP);
579 		inq = &arpintrq;
580 		arph = mtod(m, struct arphdr *);
581 #ifdef ARCNET_ALLOW_BROKEN_ARP
582 		mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
583 #endif
584 		break;
585 #endif
586 	default:
587 		m_freem(m);
588 		return;
589 	}
590 
591 	s = splimp();
592 	if (IF_QFULL(inq)) {
593 		IF_DROP(inq);
594 		m_freem(m);
595 	} else
596 		IF_ENQUEUE(inq, m);
597 	splx(s);
598 }
599 
600 /*
601  * Convert Arcnet address to printable (loggable) representation.
602  */
603 static char digits[] = "0123456789abcdef";
604 char *
605 arc_sprintf(ap)
606 	register u_int8_t *ap;
607 {
608 	static char arcbuf[3];
609 	register char *cp = arcbuf;
610 
611 	*cp++ = digits[*ap >> 4];
612 	*cp++ = digits[*ap++ & 0xf];
613 	*cp   = 0;
614 	return (arcbuf);
615 }
616 
617 /*
618  * Perform common duties while attaching to interface list
619  */
620 void
621 arc_ifattach(ifp, lla)
622 	register struct ifnet *ifp;
623 	u_int8_t lla;
624 {
625 	register struct sockaddr_dl *sdl;
626 	register struct arccom *ac;
627 
628 	ifp->if_type = IFT_ARCNET;
629 	ifp->if_addrlen = 1;
630 	ifp->if_hdrlen = ARC_HDRLEN;
631 	if (ifp->if_flags & IFF_BROADCAST)
632 		ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI;
633 	if (ifp->if_flags & IFF_LINK0 && arc_phdsmtu > 60480)
634 		log(LOG_ERR,
635 		    "%s: arc_phdsmtu is %d, but must not exceed 60480",
636 		    ifp->if_xname, arc_phdsmtu);
637 
638 	ifp->if_mtu = (ifp->if_flags & IFF_LINK0 ? arc_phdsmtu : ARCMTU);
639 	ac = (struct arccom *)ifp;
640 	ac->ac_seqid = (time.tv_sec) & 0xFFFF; /* try to make seqid unique */
641 	if (lla == 0) {
642 		/* XXX this message isn't entirely clear, to me -- cgd */
643 		log(LOG_ERR,"%s: link address 0 reserved for broadcasts.  Please change it and ifconfig %s down up\n",
644 		   ifp->if_xname, ifp->if_xname);
645 	}
646 	if ((sdl = ifp->if_sadl) &&
647 	   sdl->sdl_family == AF_LINK) {
648 		sdl->sdl_type = IFT_ARCNET;
649 		sdl->sdl_alen = ifp->if_addrlen;
650 		bcopy((caddr_t)&lla, LLADDR(sdl), ifp->if_addrlen);
651 	}
652 	ifp->if_broadcastaddr = &arcbroadcastaddr;
653 }
654