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