if_arcsubr.c revision 1.19 1 /* $NetBSD: if_arcsubr.c,v 1.19 1999/01/16 14:08:05 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 }
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 (typ != ah1->arc_type) {
444 s = "type differs";
445 goto outofseq;
446 }
447
448 if (dst != 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