if_arcsubr.c revision 184680
1/*	$NetBSD: if_arcsubr.c,v 1.36 2001/06/14 05:44:23 itojun Exp $	*/
2/*	$FreeBSD: head/sys/net/if_arcsubr.c 184680 2008-11-05 11:43:01Z bz $ */
3
4/*-
5 * Copyright (c) 1994, 1995 Ignatios Souvatzis
6 * Copyright (c) 1982, 1989, 1993
7 *	The Regents of the University of California.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the University of
20 *	California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp
38 *       @(#)if_ethersubr.c	8.1 (Berkeley) 6/10/93
39 *
40 */
41#include "opt_inet.h"
42#include "opt_inet6.h"
43#include "opt_ipx.h"
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/kernel.h>
48#include <sys/module.h>
49#include <sys/malloc.h>
50#include <sys/mbuf.h>
51#include <sys/protosw.h>
52#include <sys/socket.h>
53#include <sys/sockio.h>
54#include <sys/errno.h>
55#include <sys/syslog.h>
56
57#include <machine/cpu.h>
58
59#include <net/if.h>
60#include <net/netisr.h>
61#include <net/route.h>
62#include <net/if_dl.h>
63#include <net/if_types.h>
64#include <net/if_arc.h>
65#include <net/if_arp.h>
66#include <net/bpf.h>
67
68#if defined(INET) || defined(INET6)
69#include <netinet/in.h>
70#include <netinet/in_var.h>
71#include <netinet/if_ether.h>
72#endif
73
74#ifdef INET6
75#include <netinet6/nd6.h>
76#endif
77
78#ifdef IPX
79#include <netipx/ipx.h>
80#include <netipx/ipx_if.h>
81#endif
82
83#define ARCNET_ALLOW_BROKEN_ARP
84
85static struct mbuf *arc_defrag(struct ifnet *, struct mbuf *);
86static int arc_resolvemulti(struct ifnet *, struct sockaddr **,
87			    struct sockaddr *);
88
89u_int8_t  arcbroadcastaddr = 0;
90
91#define ARC_LLADDR(ifp)	(*(u_int8_t *)IF_LLADDR(ifp))
92
93#define senderr(e) { error = (e); goto bad;}
94#define SIN(s)	((struct sockaddr_in *)s)
95#define SIPX(s)	((struct sockaddr_ipx *)s)
96
97/*
98 * ARCnet output routine.
99 * Encapsulate a packet of type family for the local net.
100 * Assumes that ifp is actually pointer to arccom structure.
101 */
102int
103arc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
104    struct rtentry *rt0)
105{
106	struct arc_header	*ah;
107	int			error;
108	u_int8_t		atype, adst;
109	int			loop_copy = 0;
110	int			isphds;
111
112	if (!((ifp->if_flags & IFF_UP) &&
113	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
114		return(ENETDOWN); /* m, m1 aren't initialized yet */
115
116	error = 0;
117
118	switch (dst->sa_family) {
119#ifdef INET
120	case AF_INET:
121
122		/*
123		 * For now, use the simple IP addr -> ARCnet addr mapping
124		 */
125		if (m->m_flags & (M_BCAST|M_MCAST))
126			adst = arcbroadcastaddr; /* ARCnet broadcast address */
127		else if (ifp->if_flags & IFF_NOARP)
128			adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF;
129		else {
130			error = arpresolve(ifp, rt0, m, dst, &adst);
131			if (error)
132				return (error == EWOULDBLOCK ? 0 : error);
133		}
134
135		atype = (ifp->if_flags & IFF_LINK0) ?
136			ARCTYPE_IP_OLD : ARCTYPE_IP;
137		break;
138	case AF_ARP:
139	{
140		struct arphdr *ah;
141		ah = mtod(m, struct arphdr *);
142		ah->ar_hrd = htons(ARPHRD_ARCNET);
143
144		loop_copy = -1; /* if this is for us, don't do it */
145
146		switch(ntohs(ah->ar_op)) {
147		case ARPOP_REVREQUEST:
148		case ARPOP_REVREPLY:
149			atype = ARCTYPE_REVARP;
150			break;
151		case ARPOP_REQUEST:
152		case ARPOP_REPLY:
153		default:
154			atype = ARCTYPE_ARP;
155			break;
156		}
157
158		if (m->m_flags & M_BCAST)
159			bcopy(ifp->if_broadcastaddr, &adst, ARC_ADDR_LEN);
160		else
161			bcopy(ar_tha(ah), &adst, ARC_ADDR_LEN);
162
163	}
164	break;
165#endif
166#ifdef INET6
167	case AF_INET6:
168		error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)&adst);
169		if (error)
170			return (error);
171		atype = ARCTYPE_INET6;
172		break;
173#endif
174#ifdef IPX
175	case AF_IPX:
176		adst = SIPX(dst)->sipx_addr.x_host.c_host[5];
177		atype = ARCTYPE_IPX;
178		if (adst == 0xff)
179			adst = arcbroadcastaddr;
180		break;
181#endif
182
183	case AF_UNSPEC:
184		loop_copy = -1;
185		ah = (struct arc_header *)dst->sa_data;
186		adst = ah->arc_dhost;
187		atype = ah->arc_type;
188
189		if (atype == ARCTYPE_ARP) {
190			atype = (ifp->if_flags & IFF_LINK0) ?
191			    ARCTYPE_ARP_OLD: ARCTYPE_ARP;
192
193#ifdef ARCNET_ALLOW_BROKEN_ARP
194			/*
195			 * XXX It's not clear per RFC826 if this is needed, but
196			 * "assigned numbers" say this is wrong.
197			 * However, e.g., AmiTCP 3.0Beta used it... we make this
198			 * switchable for emergency cases. Not perfect, but...
199			 */
200			if (ifp->if_flags & IFF_LINK2)
201				mtod(m, struct arphdr *)->ar_pro = atype - 1;
202#endif
203		}
204		break;
205
206	default:
207		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
208		senderr(EAFNOSUPPORT);
209	}
210
211	isphds = arc_isphds(atype);
212	M_PREPEND(m, isphds ? ARC_HDRNEWLEN : ARC_HDRLEN, M_DONTWAIT);
213	if (m == 0)
214		senderr(ENOBUFS);
215	ah = mtod(m, struct arc_header *);
216	ah->arc_type = atype;
217	ah->arc_dhost = adst;
218	ah->arc_shost = ARC_LLADDR(ifp);
219	if (isphds) {
220		ah->arc_flag = 0;
221		ah->arc_seqid = 0;
222	}
223
224	if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
225		if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
226			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
227
228			(void) if_simloop(ifp, n, dst->sa_family, ARC_HDRLEN);
229		} else if (ah->arc_dhost == ah->arc_shost) {
230			(void) if_simloop(ifp, m, dst->sa_family, ARC_HDRLEN);
231			return (0);     /* XXX */
232		}
233	}
234
235	BPF_MTAP(ifp, m);
236
237	IFQ_HANDOFF(ifp, m, error);
238
239	return (error);
240
241bad:
242	if (m)
243		m_freem(m);
244	return (error);
245}
246
247void
248arc_frag_init(struct ifnet *ifp)
249{
250	struct arccom *ac;
251
252	ac = (struct arccom *)ifp->if_l2com;
253	ac->curr_frag = 0;
254}
255
256struct mbuf *
257arc_frag_next(struct ifnet *ifp)
258{
259	struct arccom *ac;
260	struct mbuf *m;
261	struct arc_header *ah;
262
263	ac = (struct arccom *)ifp->if_l2com;
264	if ((m = ac->curr_frag) == 0) {
265		int tfrags;
266
267		/* dequeue new packet */
268		IF_DEQUEUE(&ifp->if_snd, m);
269		if (m == 0)
270			return 0;
271
272		ah = mtod(m, struct arc_header *);
273		if (!arc_isphds(ah->arc_type))
274			return m;
275
276		++ac->ac_seqid;		/* make the seqid unique */
277		tfrags = (m->m_pkthdr.len + ARC_MAX_DATA - 1) / ARC_MAX_DATA;
278		ac->fsflag = 2 * tfrags - 3;
279		ac->sflag = 0;
280		ac->rsflag = ac->fsflag;
281		ac->arc_dhost = ah->arc_dhost;
282		ac->arc_shost = ah->arc_shost;
283		ac->arc_type = ah->arc_type;
284
285		m_adj(m, ARC_HDRNEWLEN);
286		ac->curr_frag = m;
287	}
288
289	/* split out next fragment and return it */
290	if (ac->sflag < ac->fsflag) {
291		/* we CAN'T have short packets here */
292		ac->curr_frag = m_split(m, ARC_MAX_DATA, M_DONTWAIT);
293		if (ac->curr_frag == 0) {
294			m_freem(m);
295			return 0;
296		}
297
298		M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT);
299		if (m == 0) {
300			m_freem(ac->curr_frag);
301			ac->curr_frag = 0;
302			return 0;
303		}
304
305		ah = mtod(m, struct arc_header *);
306		ah->arc_flag = ac->rsflag;
307		ah->arc_seqid = ac->ac_seqid;
308
309		ac->sflag += 2;
310		ac->rsflag = ac->sflag;
311	} else if ((m->m_pkthdr.len >=
312	    ARC_MIN_FORBID_LEN - ARC_HDRNEWLEN + 2) &&
313	    (m->m_pkthdr.len <=
314	    ARC_MAX_FORBID_LEN - ARC_HDRNEWLEN + 2)) {
315		ac->curr_frag = 0;
316
317		M_PREPEND(m, ARC_HDRNEWLEN_EXC, M_DONTWAIT);
318		if (m == 0)
319			return 0;
320
321		ah = mtod(m, struct arc_header *);
322		ah->arc_flag = 0xFF;
323		ah->arc_seqid = 0xFFFF;
324		ah->arc_type2 = ac->arc_type;
325		ah->arc_flag2 = ac->sflag;
326		ah->arc_seqid2 = ac->ac_seqid;
327	} else {
328		ac->curr_frag = 0;
329
330		M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT);
331		if (m == 0)
332			return 0;
333
334		ah = mtod(m, struct arc_header *);
335		ah->arc_flag = ac->sflag;
336		ah->arc_seqid = ac->ac_seqid;
337	}
338
339	ah->arc_dhost = ac->arc_dhost;
340	ah->arc_shost = ac->arc_shost;
341	ah->arc_type = ac->arc_type;
342
343	return m;
344}
345
346/*
347 * Defragmenter. Returns mbuf if last packet found, else
348 * NULL. frees imcoming mbuf as necessary.
349 */
350
351static __inline struct mbuf *
352arc_defrag(struct ifnet *ifp, struct mbuf *m)
353{
354	struct arc_header *ah, *ah1;
355	struct arccom *ac;
356	struct ac_frag *af;
357	struct mbuf *m1;
358	char *s;
359	int newflen;
360	u_char src,dst,typ;
361
362	ac = (struct arccom *)ifp->if_l2com;
363
364	if (m->m_len < ARC_HDRNEWLEN) {
365		m = m_pullup(m, ARC_HDRNEWLEN);
366		if (m == NULL) {
367			++ifp->if_ierrors;
368			return NULL;
369		}
370	}
371
372	ah = mtod(m, struct arc_header *);
373	typ = ah->arc_type;
374
375	if (!arc_isphds(typ))
376		return m;
377
378	src = ah->arc_shost;
379	dst = ah->arc_dhost;
380
381	if (ah->arc_flag == 0xff) {
382		m_adj(m, 4);
383
384		if (m->m_len < ARC_HDRNEWLEN) {
385			m = m_pullup(m, ARC_HDRNEWLEN);
386			if (m == NULL) {
387				++ifp->if_ierrors;
388				return NULL;
389			}
390		}
391
392		ah = mtod(m, struct arc_header *);
393	}
394
395	af = &ac->ac_fragtab[src];
396	m1 = af->af_packet;
397	s = "debug code error";
398
399	if (ah->arc_flag & 1) {
400		/*
401		 * first fragment. We always initialize, which is
402		 * about the right thing to do, as we only want to
403		 * accept one fragmented packet per src at a time.
404		 */
405		if (m1 != NULL)
406			m_freem(m1);
407
408		af->af_packet = m;
409		m1 = m;
410		af->af_maxflag = ah->arc_flag;
411		af->af_lastseen = 0;
412		af->af_seqid = ah->arc_seqid;
413
414		return NULL;
415		/* notreached */
416	} else {
417		/* check for unfragmented packet */
418		if (ah->arc_flag == 0)
419			return m;
420
421		/* do we have a first packet from that src? */
422		if (m1 == NULL) {
423			s = "no first frag";
424			goto outofseq;
425		}
426
427		ah1 = mtod(m1, struct arc_header *);
428
429		if (ah->arc_seqid != ah1->arc_seqid) {
430			s = "seqid differs";
431			goto outofseq;
432		}
433
434		if (typ != ah1->arc_type) {
435			s = "type differs";
436			goto outofseq;
437		}
438
439		if (dst != ah1->arc_dhost) {
440			s = "dest host differs";
441			goto outofseq;
442		}
443
444		/* typ, seqid and dst are ok here. */
445
446		if (ah->arc_flag == af->af_lastseen) {
447			m_freem(m);
448			return NULL;
449		}
450
451		if (ah->arc_flag == af->af_lastseen + 2) {
452			/* ok, this is next fragment */
453			af->af_lastseen = ah->arc_flag;
454			m_adj(m,ARC_HDRNEWLEN);
455
456			/*
457			 * m_cat might free the first mbuf (with pkthdr)
458			 * in 2nd chain; therefore:
459			 */
460
461			newflen = m->m_pkthdr.len;
462
463			m_cat(m1,m);
464
465			m1->m_pkthdr.len += newflen;
466
467			/* is it the last one? */
468			if (af->af_lastseen > af->af_maxflag) {
469				af->af_packet = NULL;
470				return(m1);
471			} else
472				return NULL;
473		}
474		s = "other reason";
475		/* if all else fails, it is out of sequence, too */
476	}
477outofseq:
478	if (m1) {
479		m_freem(m1);
480		af->af_packet = NULL;
481	}
482
483	if (m)
484		m_freem(m);
485
486	log(LOG_INFO,"%s: got out of seq. packet: %s\n",
487	    ifp->if_xname, s);
488
489	return NULL;
490}
491
492/*
493 * return 1 if Packet Header Definition Standard, else 0.
494 * For now: old IP, old ARP aren't obviously. Lacking correct information,
495 * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS.
496 * (Apple and Novell corporations were involved, among others, in PHDS work).
497 * Easiest is to assume that everybody else uses that, too.
498 */
499int
500arc_isphds(u_int8_t type)
501{
502	return (type != ARCTYPE_IP_OLD &&
503		type != ARCTYPE_ARP_OLD &&
504		type != ARCTYPE_DIAGNOSE);
505}
506
507/*
508 * Process a received Arcnet packet;
509 * the packet is in the mbuf chain m with
510 * the ARCnet header.
511 */
512void
513arc_input(struct ifnet *ifp, struct mbuf *m)
514{
515	struct arc_header *ah;
516	int isr;
517	u_int8_t atype;
518
519	if ((ifp->if_flags & IFF_UP) == 0) {
520		m_freem(m);
521		return;
522	}
523
524	/* possibly defragment: */
525	m = arc_defrag(ifp, m);
526	if (m == NULL)
527		return;
528
529	BPF_MTAP(ifp, m);
530
531	ah = mtod(m, struct arc_header *);
532	/* does this belong to us? */
533	if ((ifp->if_flags & IFF_PROMISC) == 0
534	    && ah->arc_dhost != arcbroadcastaddr
535	    && ah->arc_dhost != ARC_LLADDR(ifp)) {
536		m_freem(m);
537		return;
538	}
539
540	ifp->if_ibytes += m->m_pkthdr.len;
541
542	if (ah->arc_dhost == arcbroadcastaddr) {
543		m->m_flags |= M_BCAST|M_MCAST;
544		ifp->if_imcasts++;
545	}
546
547	atype = ah->arc_type;
548	switch (atype) {
549#ifdef INET
550	case ARCTYPE_IP:
551		m_adj(m, ARC_HDRNEWLEN);
552		if ((m = ip_fastforward(m)) == NULL)
553			return;
554		isr = NETISR_IP;
555		break;
556
557	case ARCTYPE_IP_OLD:
558		m_adj(m, ARC_HDRLEN);
559		if ((m = ip_fastforward(m)) == NULL)
560			return;
561		isr = NETISR_IP;
562		break;
563
564	case ARCTYPE_ARP:
565		if (ifp->if_flags & IFF_NOARP) {
566			/* Discard packet if ARP is disabled on interface */
567			m_freem(m);
568			return;
569		}
570		m_adj(m, ARC_HDRNEWLEN);
571		isr = NETISR_ARP;
572#ifdef ARCNET_ALLOW_BROKEN_ARP
573		mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
574#endif
575		break;
576
577	case ARCTYPE_ARP_OLD:
578		if (ifp->if_flags & IFF_NOARP) {
579			/* Discard packet if ARP is disabled on interface */
580			m_freem(m);
581			return;
582		}
583		m_adj(m, ARC_HDRLEN);
584		isr = NETISR_ARP;
585#ifdef ARCNET_ALLOW_BROKEN_ARP
586		mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP);
587#endif
588		break;
589#endif
590#ifdef INET6
591	case ARCTYPE_INET6:
592		m_adj(m, ARC_HDRNEWLEN);
593		isr = NETISR_IPV6;
594		break;
595#endif
596#ifdef IPX
597	case ARCTYPE_IPX:
598		m_adj(m, ARC_HDRNEWLEN);
599		isr = NETISR_IPX;
600		break;
601#endif
602	default:
603		m_freem(m);
604		return;
605	}
606	netisr_dispatch(isr, m);
607}
608
609/*
610 * Register (new) link level address.
611 */
612void
613arc_storelladdr(struct ifnet *ifp, u_int8_t lla)
614{
615	ARC_LLADDR(ifp) = lla;
616}
617
618/*
619 * Perform common duties while attaching to interface list
620 */
621void
622arc_ifattach(struct ifnet *ifp, u_int8_t lla)
623{
624	struct ifaddr *ifa;
625	struct sockaddr_dl *sdl;
626	struct arccom *ac;
627
628	if_attach(ifp);
629	ifp->if_addrlen = 1;
630	ifp->if_hdrlen = ARC_HDRLEN;
631	ifp->if_mtu = 1500;
632	ifp->if_resolvemulti = arc_resolvemulti;
633	if (ifp->if_baudrate == 0)
634		ifp->if_baudrate = 2500000;
635#if __FreeBSD_version < 500000
636	ifa = ifnet_addrs[ifp->if_index - 1];
637#else
638	ifa = ifp->if_addr;
639#endif
640	KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
641	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
642	sdl->sdl_type = IFT_ARCNET;
643	sdl->sdl_alen = ifp->if_addrlen;
644
645	if (ifp->if_flags & IFF_BROADCAST)
646		ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI;
647
648	ac = (struct arccom *)ifp->if_l2com;
649	ac->ac_seqid = (time_second) & 0xFFFF; /* try to make seqid unique */
650	if (lla == 0) {
651		/* XXX this message isn't entirely clear, to me -- cgd */
652		log(LOG_ERR,"%s: link address 0 reserved for broadcasts.  Please change it and ifconfig %s down up\n",
653		   ifp->if_xname, ifp->if_xname);
654	}
655	arc_storelladdr(ifp, lla);
656
657	ifp->if_broadcastaddr = &arcbroadcastaddr;
658
659	bpfattach(ifp, DLT_ARCNET, ARC_HDRLEN);
660}
661
662void
663arc_ifdetach(struct ifnet *ifp)
664{
665	bpfdetach(ifp);
666	if_detach(ifp);
667}
668
669int
670arc_ioctl(struct ifnet *ifp, int command, caddr_t data)
671{
672	struct ifaddr *ifa = (struct ifaddr *) data;
673	struct ifreq *ifr = (struct ifreq *) data;
674	int error = 0;
675
676	switch (command) {
677	case SIOCSIFADDR:
678		ifp->if_flags |= IFF_UP;
679		switch (ifa->ifa_addr->sa_family) {
680#ifdef INET
681		case AF_INET:
682			ifp->if_init(ifp->if_softc);	/* before arpwhohas */
683			arp_ifinit(ifp, ifa);
684			break;
685#endif
686#ifdef IPX
687		/*
688		 * XXX This code is probably wrong
689		 */
690		case AF_IPX:
691		{
692			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
693
694			if (ipx_nullhost(*ina))
695				ina->x_host.c_host[5] = ARC_LLADDR(ifp);
696			else
697				arc_storelladdr(ifp, ina->x_host.c_host[5]);
698
699			/*
700			 * Set new address
701			 */
702			ifp->if_init(ifp->if_softc);
703			break;
704		}
705#endif
706		default:
707			ifp->if_init(ifp->if_softc);
708			break;
709		}
710		break;
711
712	case SIOCGIFADDR:
713		{
714			struct sockaddr *sa;
715
716			sa = (struct sockaddr *) &ifr->ifr_data;
717			*(u_int8_t *)sa->sa_data = ARC_LLADDR(ifp);
718		}
719		break;
720
721	case SIOCADDMULTI:
722	case SIOCDELMULTI:
723		if (ifr == NULL)
724			error = EAFNOSUPPORT;
725		else {
726			switch (ifr->ifr_addr.sa_family) {
727			case AF_INET:
728			case AF_INET6:
729				error = 0;
730				break;
731			default:
732				error = EAFNOSUPPORT;
733				break;
734			}
735		}
736		break;
737
738	case SIOCSIFMTU:
739		/*
740		 * Set the interface MTU.
741		 * mtu can't be larger than ARCMTU for RFC1051
742		 * and can't be larger than ARC_PHDS_MTU
743		 */
744		if (((ifp->if_flags & IFF_LINK0) && ifr->ifr_mtu > ARCMTU) ||
745		    ifr->ifr_mtu > ARC_PHDS_MAXMTU)
746			error = EINVAL;
747		else
748			ifp->if_mtu = ifr->ifr_mtu;
749		break;
750	}
751
752	return (error);
753}
754
755/* based on ether_resolvemulti() */
756int
757arc_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa,
758    struct sockaddr *sa)
759{
760	struct sockaddr_dl *sdl;
761#ifdef INET
762	struct sockaddr_in *sin;
763#endif
764#ifdef INET6
765	struct sockaddr_in6 *sin6;
766#endif
767
768	switch(sa->sa_family) {
769	case AF_LINK:
770		/*
771		* No mapping needed. Just check that it's a valid MC address.
772		*/
773		sdl = (struct sockaddr_dl *)sa;
774		if (*LLADDR(sdl) != arcbroadcastaddr)
775			return EADDRNOTAVAIL;
776		*llsa = 0;
777		return 0;
778#ifdef INET
779	case AF_INET:
780		sin = (struct sockaddr_in *)sa;
781		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
782			return EADDRNOTAVAIL;
783		sdl = malloc(sizeof *sdl, M_IFMADDR,
784		       M_NOWAIT | M_ZERO);
785		if (sdl == NULL)
786			return ENOMEM;
787		sdl->sdl_len = sizeof *sdl;
788		sdl->sdl_family = AF_LINK;
789		sdl->sdl_index = ifp->if_index;
790		sdl->sdl_type = IFT_ARCNET;
791		sdl->sdl_alen = ARC_ADDR_LEN;
792		*LLADDR(sdl) = 0;
793		*llsa = (struct sockaddr *)sdl;
794		return 0;
795#endif
796#ifdef INET6
797	case AF_INET6:
798		sin6 = (struct sockaddr_in6 *)sa;
799		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
800			/*
801			 * An IP6 address of 0 means listen to all
802			 * of the Ethernet multicast address used for IP6.
803			 * (This is used for multicast routers.)
804			 */
805			ifp->if_flags |= IFF_ALLMULTI;
806			*llsa = 0;
807			return 0;
808		}
809		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
810			return EADDRNOTAVAIL;
811		sdl = malloc(sizeof *sdl, M_IFMADDR,
812		       M_NOWAIT | M_ZERO);
813		if (sdl == NULL)
814			return ENOMEM;
815		sdl->sdl_len = sizeof *sdl;
816		sdl->sdl_family = AF_LINK;
817		sdl->sdl_index = ifp->if_index;
818		sdl->sdl_type = IFT_ARCNET;
819		sdl->sdl_alen = ARC_ADDR_LEN;
820		*LLADDR(sdl) = 0;
821		*llsa = (struct sockaddr *)sdl;
822		return 0;
823#endif
824
825	default:
826		/*
827		 * Well, the text isn't quite right, but it's the name
828		 * that counts...
829		 */
830		return EAFNOSUPPORT;
831	}
832}
833
834MALLOC_DEFINE(M_ARCCOM, "arccom", "ARCNET interface internals");
835
836static void*
837arc_alloc(u_char type, struct ifnet *ifp)
838{
839	struct arccom	*ac;
840
841	ac = malloc(sizeof(struct arccom), M_ARCCOM, M_WAITOK | M_ZERO);
842	ac->ac_ifp = ifp;
843
844	return (ac);
845}
846
847static void
848arc_free(void *com, u_char type)
849{
850
851	free(com, M_ARCCOM);
852}
853
854static int
855arc_modevent(module_t mod, int type, void *data)
856{
857
858	switch (type) {
859	case MOD_LOAD:
860		if_register_com_alloc(IFT_ARCNET, arc_alloc, arc_free);
861		break;
862	case MOD_UNLOAD:
863		if_deregister_com_alloc(IFT_ARCNET);
864		break;
865	default:
866		return EOPNOTSUPP;
867	}
868
869	return (0);
870}
871
872static moduledata_t arc_mod = {
873	"arcnet",
874	arc_modevent,
875	0
876};
877
878DECLARE_MODULE(arcnet, arc_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
879MODULE_VERSION(arcnet, 1);
880