if_arcsubr.c revision 1.18 1 /* $NetBSD: if_arcsubr.c,v 1.18 1999/01/16 13:04:13 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 #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
293 /* here we can have small, especially forbidden packets */
294
295 if ((m->m_pkthdr.len >=
296 ARC_MIN_FORBID_LEN - ARC_HDRNEWLEN + 2) &&
297 (m->m_pkthdr.len <=
298 ARC_MAX_FORBID_LEN - ARC_HDRNEWLEN + 2)) {
299
300 M_PREPEND(m, ARC_HDRNEWLEN_EXC_1201, M_DONTWAIT);
301 if (m == 0)
302 senderr(ENOBUFS);
303 ah = mtod(m, struct arc_header *);
304 ah->arc_flag = 0xFF;
305 ah->arc_seqid = 0xFFFF;
306 ah->arc_type2 = atype;
307 ah->arc_flag2 = sflag;
308 ah->arc_seqid2 = ac->ac_seqid;
309 } else {
310 M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT);
311 if (m == 0)
312 senderr(ENOBUFS);
313 ah = mtod(m, struct arc_header *);
314 ah->arc_flag = sflag;
315 ah->arc_seqid = ac->ac_seqid;
316 }
317
318 ah->arc_dhost = adst;
319 ah->arc_shost = myself;
320 ah->arc_type = atype;
321 } else {
322 M_PREPEND(m, ARC_HDRLEN, M_DONTWAIT);
323 if (m == 0)
324 senderr(ENOBUFS);
325 ah = mtod(m, struct arc_header *);
326 ah->arc_type = atype;
327 ah->arc_dhost = adst;
328 ah->arc_shost = myself;
329 }
330
331 s = splimp();
332 /*
333 * Queue message on interface, and start output if interface
334 * not yet active.
335 */
336 if (IF_QFULL(&ifp->if_snd)) {
337 IF_DROP(&ifp->if_snd);
338 splx(s);
339 senderr(ENOBUFS);
340 }
341 ifp->if_obytes += m->m_pkthdr.len;
342 IF_ENQUEUE(&ifp->if_snd, m);
343 if ((ifp->if_flags & IFF_OACTIVE) == 0)
344 (*ifp->if_start)(ifp);
345 splx(s);
346
347 return (error);
348
349 bad:
350 if (m1)
351 m_freem(m1);
352 if (m)
353 m_freem(m);
354 return (error);
355 }
356
357 /*
358 * Defragmenter. Returns mbuf if last packet found, else
359 * NULL. frees imcoming mbuf as necessary.
360 */
361
362 __inline struct mbuf *
363 arc_defrag(ifp, m)
364 struct ifnet *ifp;
365 struct mbuf *m;
366 {
367 struct arc_header *ah, *ah1;
368 struct arccom *ac;
369 struct ac_frag *af;
370 struct mbuf *m1;
371 char *s;
372 int newflen;
373 u_char src,dst,typ;
374
375 ac = (struct arccom *)ifp;
376
377 m = m_pullup(m, ARC_HDRNEWLEN);
378 if (m == NULL) {
379 ++ifp->if_ierrors;
380 return NULL;
381 }
382
383 ah = mtod(m, struct arc_header *);
384 typ = ah->arc_type;
385
386 if (!arc_isphds(typ))
387 return m;
388
389 src = ah->arc_shost;
390 dst = ah->arc_dhost;
391
392 if (ah->arc_flag == 0xff) {
393 m_adj(m, 4);
394
395 m = m_pullup(m, ARC_HDRNEWLEN);
396 if (m == NULL) {
397 ++ifp->if_ierrors;
398 return NULL;
399 }
400
401 ah = mtod(m, struct arc_header *);
402 ah->arc_shost = src;
403 ah->arc_dhost = dst;
404 ah->arc_type = typ;
405 }
406
407 af = &ac->ac_fragtab[src];
408 m1 = af->af_packet;
409 s = "debug code error";
410
411 if (ah->arc_flag & 1) {
412 /*
413 * first fragment. We always initialize, which is
414 * about the right thing to do, as we only want to
415 * accept one fragmented packet per src at a time.
416 */
417 if (m1 != NULL)
418 m_freem(m1);
419
420 af->af_packet = m;
421 m1 = m;
422 af->af_maxflag = ah->arc_flag;
423 af->af_lastseen = 0;
424 af->af_seqid = ah->arc_seqid;
425
426 return NULL;
427 /* notreached */
428 } else {
429 /* check for unfragmented packet */
430 if (ah->arc_flag == 0)
431 return m;
432
433 /* do we have a first packet from that src? */
434 if (m1 == NULL) {
435 s = "no first frag";
436 goto outofseq;
437 }
438
439 ah1 = mtod(m1, struct arc_header *);
440
441 if (ah->arc_seqid != ah1->arc_seqid) {
442 s = "seqid differs";
443 goto outofseq;
444 }
445
446 if (ah->arc_type != ah1->arc_type) {
447 s = "type differs";
448 goto outofseq;
449 }
450
451 if (ah->arc_dhost != ah1->arc_dhost) {
452 s = "dest host differs";
453 goto outofseq;
454 }
455
456 /* typ, seqid and dst are ok here. */
457
458 if (ah->arc_flag == af->af_lastseen) {
459 m_freem(m);
460 return NULL;
461 }
462
463 if (ah->arc_flag == af->af_lastseen + 2) {
464 /* ok, this is next fragment */
465 af->af_lastseen = ah->arc_flag;
466 m_adj(m,ARC_HDRNEWLEN);
467
468 /*
469 * m_cat might free the first mbuf (with pkthdr)
470 * in 2nd chain; therefore:
471 */
472
473 newflen = m->m_pkthdr.len;
474
475 m_cat(m1,m);
476
477 m1->m_pkthdr.len += newflen;
478
479 /* is it the last one? */
480 if (af->af_lastseen > af->af_maxflag) {
481 af->af_packet = NULL;
482 return(m1);
483 } else
484 return NULL;
485 }
486 s = "other reason";
487 /* if all else fails, it is out of sequence, too */
488 }
489 outofseq:
490 if (m1) {
491 m_freem(m1);
492 af->af_packet = NULL;
493 }
494
495 if (m)
496 m_freem(m);
497
498 log(LOG_INFO,"%s: got out of seq. packet: %s\n",
499 ifp->if_xname, s);
500
501 return NULL;
502 }
503
504 /*
505 * return 1 if Packet Header Definition Standard, else 0.
506 * For now: old IP, old ARP aren't obviously. Lacking correct information,
507 * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS.
508 * (Apple and Novell corporations were involved, among others, in PHDS work).
509 * Easiest is to assume that everybody else uses that, too.
510 */
511 int
512 arc_isphds(type)
513 u_int8_t type;
514 {
515 return ((type != ARCTYPE_IP_OLD &&
516 type != ARCTYPE_ARP_OLD));
517 }
518
519 /*
520 * Process a received Arcnet packet;
521 * the packet is in the mbuf chain m with
522 * the ARCnet header.
523 */
524 void
525 arc_input(ifp, m)
526 struct ifnet *ifp;
527 struct mbuf *m;
528 {
529 register struct arc_header *ah;
530 register struct ifqueue *inq;
531 u_int8_t atype;
532 int s;
533 struct arphdr *arph;
534
535 if ((ifp->if_flags & IFF_UP) == 0) {
536 m_freem(m);
537 return;
538 }
539
540 /* possibly defragment: */
541 m = arc_defrag(ifp, m);
542 if (m == NULL)
543 return;
544
545 ah = mtod(m, struct arc_header *);
546
547 ifp->if_lastchange = time;
548 ifp->if_ibytes += m->m_pkthdr.len;
549
550 if (arcbroadcastaddr == ah->arc_dhost) {
551 m->m_flags |= M_BCAST|M_MCAST;
552 ifp->if_imcasts++;
553 }
554
555 atype = ah->arc_type;
556 switch (atype) {
557 #ifdef INET
558 case ARCTYPE_IP:
559 m_adj(m, ARC_HDRNEWLEN);
560 schednetisr(NETISR_IP);
561 inq = &ipintrq;
562 break;
563
564 case ARCTYPE_IP_OLD:
565 m_adj(m, ARC_HDRLEN);
566 schednetisr(NETISR_IP);
567 inq = &ipintrq;
568 break;
569
570 case ARCTYPE_ARP:
571 m_adj(m, ARC_HDRNEWLEN);
572 schednetisr(NETISR_ARP);
573 inq = &arpintrq;
574 #ifdef ARCNET_ALLOW_BROKEN_ARP
575 mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
576 #endif
577 break;
578
579 case ARCTYPE_ARP_OLD:
580 m_adj(m, ARC_HDRLEN);
581 schednetisr(NETISR_ARP);
582 inq = &arpintrq;
583 arph = mtod(m, struct arphdr *);
584 #ifdef ARCNET_ALLOW_BROKEN_ARP
585 mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
586 #endif
587 break;
588 #endif
589 default:
590 m_freem(m);
591 return;
592 }
593
594 s = splimp();
595 if (IF_QFULL(inq)) {
596 IF_DROP(inq);
597 m_freem(m);
598 } else
599 IF_ENQUEUE(inq, m);
600 splx(s);
601 }
602
603 /*
604 * Convert Arcnet address to printable (loggable) representation.
605 */
606 static char digits[] = "0123456789abcdef";
607 char *
608 arc_sprintf(ap)
609 register u_int8_t *ap;
610 {
611 static char arcbuf[3];
612 register char *cp = arcbuf;
613
614 *cp++ = digits[*ap >> 4];
615 *cp++ = digits[*ap++ & 0xf];
616 *cp = 0;
617 return (arcbuf);
618 }
619
620 /*
621 * Perform common duties while attaching to interface list
622 */
623 void
624 arc_ifattach(ifp, lla)
625 register struct ifnet *ifp;
626 u_int8_t lla;
627 {
628 register struct sockaddr_dl *sdl;
629 register struct arccom *ac;
630
631 ifp->if_type = IFT_ARCNET;
632 ifp->if_addrlen = 1;
633 ifp->if_hdrlen = ARC_HDRLEN;
634 if (ifp->if_flags & IFF_BROADCAST)
635 ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI;
636 if (ifp->if_flags & IFF_LINK0 && arc_phdsmtu > 60480)
637 log(LOG_ERR,
638 "%s: arc_phdsmtu is %d, but must not exceed 60480",
639 ifp->if_xname, arc_phdsmtu);
640
641 ifp->if_mtu = (ifp->if_flags & IFF_LINK0 ? arc_phdsmtu : ARCMTU);
642 ac = (struct arccom *)ifp;
643 ac->ac_seqid = (time.tv_sec) & 0xFFFF; /* try to make seqid unique */
644 if (lla == 0) {
645 /* XXX this message isn't entirely clear, to me -- cgd */
646 log(LOG_ERR,"%s: link address 0 reserved for broadcasts. Please change it and ifconfig %s down up\n",
647 ifp->if_xname, ifp->if_xname);
648 }
649 if ((sdl = ifp->if_sadl) &&
650 sdl->sdl_family == AF_LINK) {
651 sdl->sdl_type = IFT_ARCNET;
652 sdl->sdl_alen = ifp->if_addrlen;
653 bcopy((caddr_t)&lla, LLADDR(sdl), ifp->if_addrlen);
654 }
655 ifp->if_broadcastaddr = &arcbroadcastaddr;
656 }
657