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