if_arcsubr.c revision 1.15.4.1 1 /* $NetBSD: if_arcsubr.c,v 1.15.4.1 1997/10/14 10:29:07 thorpej 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