1 /* $NetBSD: if_arcsubr.c,v 1.60 2008/11/07 00:20:13 dyoung Exp $ */
4 * Copyright (c) 1994, 1995 Ignatios Souvatzis
5 * Copyright (c) 1982, 1989, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp
33 * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: if_arcsubr.c,v 1.60 2008/11/07 00:20:13 dyoung Exp $");
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
49 #include <sys/protosw.h>
50 #include <sys/socket.h>
51 #include <sys/ioctl.h>
52 #include <sys/errno.h>
53 #include <sys/syslog.h>
58 #include <net/netisr.h>
59 #include <net/route.h>
60 #include <net/if_dl.h>
61 #include <net/if_types.h>
62 #include <net/if_arc.h>
63 #include <net/if_arp.h>
64 #include <net/if_ether.h>
71 #include <netinet/in.h>
72 #include <netinet/in_var.h>
73 #include <netinet/if_inarp.h>
78 #include <netinet/in.h>
80 #include <netinet6/in6_var.h>
81 #include <netinet6/nd6.h>
84 #define ARCNET_ALLOW_BROKEN_ARP
87 #define ARC_IPMTU 1500
90 static struct mbuf
*arc_defrag(struct ifnet
*, struct mbuf
*);
93 * RC1201 requires us to have this configurable. We have it only per
94 * machine at the moment... there is no generic "set mtu" ioctl, AFAICS.
95 * Anyway, it is possible to binpatch this or set it per kernel config
99 ERROR
: The arc_ipmtu is ARC_IPMTU
, but must
not exceed
60480.
101 int arc_ipmtu
= ARC_IPMTU
;
102 uint8_t arcbroadcastaddr
= 0;
104 #define senderr(e) { error = (e); goto bad;}
106 static int arc_output(struct ifnet
*, struct mbuf
*,
107 const struct sockaddr
*, struct rtentry
*);
108 static void arc_input(struct ifnet
*, struct mbuf
*);
111 * ARCnet output routine.
112 * Encapsulate a packet of type family for the local net.
113 * Assumes that ifp is actually pointer to arccom structure.
116 arc_output(struct ifnet
*ifp
, struct mbuf
*m0
, const struct sockaddr
*dst
,
119 struct mbuf
*m
, *m1
, *mcopy
;
122 const struct arc_header
*cah
;
123 struct arc_header
*ah
;
125 int error
, newencoding
;
126 uint8_t atype
, adst
, myself
;
127 int tfrags
, sflag
, fsflag
, rsflag
;
128 ALTQ_DECL(struct altq_pktattr pktattr
;)
130 if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
))
131 return (ENETDOWN
); /* m, m1 aren't initialized yet */
133 error
= newencoding
= 0;
134 ac
= (struct arccom
*)ifp
;
138 myself
= *CLLADDR(ifp
->if_sadl
);
141 if ((rt
->rt_flags
& RTF_UP
) == 0) {
142 if ((rt0
= rt
= rtalloc1(dst
, 1)))
145 senderr(EHOSTUNREACH
);
147 if (rt
->rt_flags
& RTF_GATEWAY
) {
148 if (rt
->rt_gwroute
== 0)
150 if (((rt
= rt
->rt_gwroute
)->rt_flags
& RTF_UP
) == 0) {
151 rtfree(rt
); rt
= rt0
;
152 lookup
: rt
->rt_gwroute
= rtalloc1(rt
->rt_gateway
, 1);
153 if ((rt
= rt
->rt_gwroute
) == 0)
154 senderr(EHOSTUNREACH
);
157 if (rt
->rt_flags
& RTF_REJECT
)
158 if (rt
->rt_rmx
.rmx_expire
== 0 ||
159 time_second
< rt
->rt_rmx
.rmx_expire
)
160 senderr(rt
== rt0
? EHOSTDOWN
: EHOSTUNREACH
);
164 * if the queueing discipline needs packet classification,
165 * do it before prepending link headers.
167 IFQ_CLASSIFY(&ifp
->if_snd
, m
, dst
->sa_family
, &pktattr
);
169 switch (dst
->sa_family
) {
174 * For now, use the simple IP addr -> ARCnet addr mapping
176 if (m
->m_flags
& (M_BCAST
|M_MCAST
))
177 adst
= arcbroadcastaddr
; /* ARCnet broadcast address */
178 else if (ifp
->if_flags
& IFF_NOARP
)
179 adst
= ntohl(satocsin(dst
)->sin_addr
.s_addr
) & 0xFF;
180 else if (!arpresolve(ifp
, rt
, m
, dst
, &adst
))
181 return 0; /* not resolved yet */
183 /* If broadcasting on a simplex interface, loopback a copy */
184 if ((m
->m_flags
& (M_BCAST
|M_MCAST
)) &&
185 (ifp
->if_flags
& IFF_SIMPLEX
))
186 mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
187 if (ifp
->if_flags
& IFF_LINK0
) {
191 atype
= ARCTYPE_IP_OLD
;
197 arph
= mtod(m
, struct arphdr
*);
198 if (m
->m_flags
& M_BCAST
)
199 adst
= arcbroadcastaddr
;
201 uint8_t *tha
= ar_tha(arph
);
207 arph
->ar_hrd
= htons(ARPHRD_ARCNET
);
209 switch (ntohs(arph
->ar_op
)) {
210 case ARPOP_REVREQUEST
:
212 if (!(ifp
->if_flags
& IFF_LINK0
)) {
213 printf("%s: can't handle af%d\n",
214 ifp
->if_xname
, dst
->sa_family
);
215 senderr(EAFNOSUPPORT
);
218 atype
= htons(ARCTYPE_REVARP
);
225 if (ifp
->if_flags
& IFF_LINK0
) {
226 atype
= htons(ARCTYPE_ARP
);
229 atype
= htons(ARCTYPE_ARP_OLD
);
233 #ifdef ARCNET_ALLOW_BROKEN_ARP
235 * XXX It's not clear per RFC826 if this is needed, but
236 * "assigned numbers" say this is wrong.
237 * However, e.g., AmiTCP 3.0Beta used it... we make this
238 * switchable for emergency cases. Not perfect, but...
240 if (ifp
->if_flags
& IFF_LINK2
)
241 arph
->ar_pro
= atype
- 1;
247 if (!nd6_storelladdr(ifp
, rt
, m
, dst
, &adst
, sizeof(adst
)))
248 return (0); /* it must be impossible, but... */
249 atype
= htons(ARCTYPE_INET6
);
255 cah
= (const struct arc_header
*)dst
->sa_data
;
256 adst
= cah
->arc_dhost
;
257 atype
= cah
->arc_type
;
261 printf("%s: can't handle af%d\n", ifp
->if_xname
,
263 senderr(EAFNOSUPPORT
);
267 (void) looutput(ifp
, mcopy
, dst
, rt
);
270 * Add local net header. If no space in first mbuf,
273 * For ARCnet, this is just symbolic. The header changes
274 * form and position on its way into the hardware and out of
275 * the wire. At this point, it contains source, destination and
279 ++ac
->ac_seqid
; /* make the seqid unique */
281 tfrags
= (m
->m_pkthdr
.len
+ 503) / 504;
282 fsflag
= 2 * tfrags
- 3;
286 while (sflag
< fsflag
) {
287 /* we CAN'T have short packets here */
288 m1
= m_split(m
, 504, M_DONTWAIT
);
292 M_PREPEND(m
, ARC_HDRNEWLEN
, M_DONTWAIT
);
295 ah
= mtod(m
, struct arc_header
*);
296 ah
->arc_type
= atype
;
297 ah
->arc_dhost
= adst
;
298 ah
->arc_shost
= myself
;
299 ah
->arc_flag
= rsflag
;
300 ah
->arc_seqid
= ac
->ac_seqid
;
302 if ((error
= ifq_enqueue(ifp
, m ALTQ_COMMA
303 ALTQ_DECL(&pktattr
))) != 0)
313 /* here we can have small, especially forbidden packets */
315 if ((m
->m_pkthdr
.len
>=
316 ARC_MIN_FORBID_LEN
- ARC_HDRNEWLEN
+ 2) &&
318 ARC_MAX_FORBID_LEN
- ARC_HDRNEWLEN
+ 2)) {
320 M_PREPEND(m
, ARC_HDRNEWLEN_EXC
, M_DONTWAIT
);
323 ah
= mtod(m
, struct arc_header
*);
325 ah
->arc_seqid
= 0xFFFF;
326 ah
->arc_type2
= atype
;
327 ah
->arc_flag2
= sflag
;
328 ah
->arc_seqid2
= ac
->ac_seqid
;
330 M_PREPEND(m
, ARC_HDRNEWLEN
, M_DONTWAIT
);
333 ah
= mtod(m
, struct arc_header
*);
334 ah
->arc_flag
= sflag
;
335 ah
->arc_seqid
= ac
->ac_seqid
;
338 ah
->arc_dhost
= adst
;
339 ah
->arc_shost
= myself
;
340 ah
->arc_type
= atype
;
342 M_PREPEND(m
, ARC_HDRLEN
, M_DONTWAIT
);
345 ah
= mtod(m
, struct arc_header
*);
346 ah
->arc_type
= atype
;
347 ah
->arc_dhost
= adst
;
348 ah
->arc_shost
= myself
;
351 return ifq_enqueue(ifp
, m ALTQ_COMMA
ALTQ_DECL(&pktattr
));
362 * Defragmenter. Returns mbuf if last packet found, else
363 * NULL. frees imcoming mbuf as necessary.
367 arc_defrag(struct ifnet
*ifp
, struct mbuf
*m
)
369 struct arc_header
*ah
, *ah1
;
375 u_char src
, dst
, typ
;
377 ac
= (struct arccom
*)ifp
;
379 if (m
->m_len
< ARC_HDRNEWLEN
) {
380 m
= m_pullup(m
, ARC_HDRNEWLEN
);
387 ah
= mtod(m
, struct arc_header
*);
390 if (!arc_isphds(typ
))
396 if (ah
->arc_flag
== 0xff) {
399 if (m
->m_len
< ARC_HDRNEWLEN
) {
400 m
= m_pullup(m
, ARC_HDRNEWLEN
);
407 ah
= mtod(m
, struct arc_header
*);
410 af
= &ac
->ac_fragtab
[src
];
412 s
= "debug code error";
414 if (ah
->arc_flag
& 1) {
416 * first fragment. We always initialize, which is
417 * about the right thing to do, as we only want to
418 * accept one fragmented packet per src at a time.
425 af
->af_maxflag
= ah
->arc_flag
;
427 af
->af_seqid
= ah
->arc_seqid
;
432 /* check for unfragmented packet */
433 if (ah
->arc_flag
== 0)
436 /* do we have a first packet from that src? */
442 ah1
= mtod(m1
, struct arc_header
*);
444 if (ah
->arc_seqid
!= ah1
->arc_seqid
) {
449 if (typ
!= ah1
->arc_type
) {
454 if (dst
!= ah1
->arc_dhost
) {
455 s
= "dest host differs";
459 /* typ, seqid and dst are ok here. */
461 if (ah
->arc_flag
== af
->af_lastseen
) {
466 if (ah
->arc_flag
== af
->af_lastseen
+ 2) {
467 /* ok, this is next fragment */
468 af
->af_lastseen
= ah
->arc_flag
;
469 m_adj(m
, ARC_HDRNEWLEN
);
472 * m_cat might free the first mbuf (with pkthdr)
473 * in 2nd chain; therefore:
476 newflen
= m
->m_pkthdr
.len
;
480 m1
->m_pkthdr
.len
+= newflen
;
482 /* is it the last one? */
483 if (af
->af_lastseen
> af
->af_maxflag
) {
484 af
->af_packet
= NULL
;
490 /* if all else fails, it is out of sequence, too */
495 af
->af_packet
= NULL
;
501 log(LOG_INFO
,"%s: got out of seq. packet: %s\n",
508 * return 1 if Packet Header Definition Standard, else 0.
509 * For now: old IP, old ARP aren't obviously. Lacking correct information,
510 * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS.
511 * (Apple and Novell corporations were involved, among others, in PHDS work).
512 * Easiest is to assume that everybody else uses that, too.
515 arc_isphds(uint8_t type
)
517 return (type
!= ARCTYPE_IP_OLD
&&
518 type
!= ARCTYPE_ARP_OLD
&&
519 type
!= ARCTYPE_DIAGNOSE
);
523 * Process a received Arcnet packet;
524 * the packet is in the mbuf chain m with
528 arc_input(struct ifnet
*ifp
, struct mbuf
*m
)
530 struct arc_header
*ah
;
535 if ((ifp
->if_flags
& IFF_UP
) == 0) {
540 /* possibly defragment: */
541 m
= arc_defrag(ifp
, m
);
545 ah
= mtod(m
, struct arc_header
*);
547 ifp
->if_ibytes
+= m
->m_pkthdr
.len
;
549 if (arcbroadcastaddr
== ah
->arc_dhost
) {
550 m
->m_flags
|= M_BCAST
|M_MCAST
;
554 atype
= ah
->arc_type
;
558 m_adj(m
, ARC_HDRNEWLEN
);
559 schednetisr(NETISR_IP
);
564 m_adj(m
, ARC_HDRLEN
);
565 schednetisr(NETISR_IP
);
570 m_adj(m
, ARC_HDRNEWLEN
);
571 schednetisr(NETISR_ARP
);
573 #ifdef ARCNET_ALLOW_BROKEN_ARP
574 mtod(m
, struct arphdr
*)->ar_pro
= htons(ETHERTYPE_IP
);
578 case ARCTYPE_ARP_OLD
:
579 m_adj(m
, ARC_HDRLEN
);
580 schednetisr(NETISR_ARP
);
582 #ifdef ARCNET_ALLOW_BROKEN_ARP
583 mtod(m
, struct arphdr
*)->ar_pro
= htons(ETHERTYPE_IP
);
589 m_adj(m
, ARC_HDRNEWLEN
);
590 schednetisr(NETISR_IPV6
);
609 * Convert Arcnet address to printable (loggable) representation.
612 arc_sprintf(uint8_t *ap
)
614 static char arcbuf
[3];
617 *cp
++ = hexdigits
[*ap
>> 4];
618 *cp
++ = hexdigits
[*ap
++ & 0xf];
624 * Perform common duties while attaching to interface list
627 arc_ifattach(struct ifnet
*ifp
, uint8_t lla
)
631 ifp
->if_type
= IFT_ARCNET
;
633 ifp
->if_hdrlen
= ARC_HDRLEN
;
634 ifp
->if_dlt
= DLT_ARCNET
;
635 if (ifp
->if_flags
& IFF_BROADCAST
)
636 ifp
->if_flags
|= IFF_MULTICAST
|IFF_ALLMULTI
;
637 if (ifp
->if_flags
& IFF_LINK0
&& arc_ipmtu
> ARC_PHDS_MAXMTU
)
639 "%s: arc_ipmtu is %d, but must not exceed %d",
640 ifp
->if_xname
, arc_ipmtu
, ARC_PHDS_MAXMTU
);
642 ifp
->if_output
= arc_output
;
643 ifp
->if_input
= arc_input
;
644 ac
= (struct arccom
*)ifp
;
645 ac
->ac_seqid
= (time_second
) & 0xFFFF; /* try to make seqid unique */
647 /* XXX this message isn't entirely clear, to me -- cgd */
648 log(LOG_ERR
,"%s: link address 0 reserved for broadcasts. Please change it and ifconfig %s down up\n",
649 ifp
->if_xname
, ifp
->if_xname
);
652 if_set_sadl(ifp
, &lla
, sizeof(lla
), true);
654 ifp
->if_broadcastaddr
= &arcbroadcastaddr
;
657 bpfattach(ifp
, DLT_ARCNET
, ARC_HDRLEN
);