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