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