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