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