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