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