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