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