Merge from vendor branch LIBSTDC++:
[dragonfly.git] / sys / net / ef / if_ef.c
1 /*-
2  * Copyright (c) 1999, 2000 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/net/if_ef.c,v 1.2.2.4 2001/02/22 09:27:04 bp Exp $
27  * $DragonFly: src/sys/net/ef/if_ef.c,v 1.7 2004/01/06 03:17:25 dillon Exp $
28  */
29
30 #include "opt_inet.h"
31 #include "opt_ipx.h"
32 #include "opt_ef.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/sockio.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40 #include <sys/syslog.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43
44 #include <net/ethernet.h>
45 #include <net/if_llc.h>
46 #include <net/if.h>
47 #include <net/if_arp.h>
48 #include <net/if_dl.h>
49 #include <net/if_types.h>
50 #include <net/netisr.h>
51 #include <net/route.h>
52 #include <net/bpf.h>
53
54 #ifdef INET
55 #include <netinet/in.h>
56 #include <netinet/in_var.h>
57 #include <netinet/if_ether.h>
58 #endif
59
60 #ifdef IPX
61 #include <netproto/ipx/ipx.h>
62 #include <netproto/ipx/ipx_if.h>
63 #endif
64
65 /* internal frame types */
66 #define ETHER_FT_EII            0       /* Ethernet_II - default */
67 #define ETHER_FT_8023           1       /* 802.3 (Novell) */
68 #define ETHER_FT_8022           2       /* 802.2 */
69 #define ETHER_FT_SNAP           3       /* SNAP */
70 #define EF_NFT                  4       /* total number of frame types */
71
72 #ifdef EF_DEBUG
73 #define EFDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
74 #else
75 #define EFDEBUG(format, args...)
76 #endif
77
78 #define EFERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
79
80 struct efnet {
81         struct arpcom   ef_ac;
82         struct ifnet *  ef_ifp;
83         int             ef_frametype;
84 };
85
86 struct ef_link {
87         SLIST_ENTRY(ef_link) el_next;
88         struct ifnet    *el_ifp;                /* raw device for this clones */
89         struct efnet    *el_units[EF_NFT];      /* our clones */
90 };
91
92 static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL};
93 static int efcount;
94
95 extern int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m);
96 extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp,
97                 struct sockaddr *dst, short *tp, int *hlen);
98
99 /*
100 static void ef_reset (struct ifnet *);
101 */
102 static int ef_attach(struct efnet *sc);
103 static int ef_detach(struct efnet *sc);
104 static void ef_init(void *);
105 static int ef_ioctl(struct ifnet *, u_long, caddr_t);
106 static void ef_start(struct ifnet *);
107 static int ef_input(struct ifnet*, struct ether_header *, struct mbuf *);
108 static int ef_output(struct ifnet *ifp, struct mbuf **mp,
109                 struct sockaddr *dst, short *tp, int *hlen);
110
111 static int ef_load(void);
112 static int ef_unload(void);
113
114 /*
115  * Install the interface, most of structure initialization done in ef_clone()
116  */
117 static int
118 ef_attach(struct efnet *sc)
119 {
120         struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
121         struct ifaddr *ifa1, *ifa2;
122         struct sockaddr_dl *sdl1, *sdl2;
123
124         ifp->if_output = ether_output;
125         ifp->if_start = ef_start;
126         ifp->if_watchdog = NULL;
127         ifp->if_init = ef_init;
128         ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
129         ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
130         /*
131          * Attach the interface
132          */
133         ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
134
135         ifp->if_resolvemulti = 0;
136         ifp->if_type = IFT_XETHER;
137         ifp->if_flags |= IFF_RUNNING;
138
139         ifa1 = ifnet_addrs[ifp->if_index - 1];
140         ifa2 = ifnet_addrs[sc->ef_ifp->if_index - 1];
141         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
142         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
143         sdl1->sdl_type = IFT_ETHER;
144         sdl1->sdl_alen = ETHER_ADDR_LEN;
145         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
146         bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN);
147
148         EFDEBUG("%s: attached\n", ifp->if_xname);
149         return 1;
150 }
151
152 /*
153  * This is for _testing_only_, just removes interface from interfaces list
154  */
155 static int
156 ef_detach(struct efnet *sc)
157 {
158         struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
159         int s;
160
161         s = splimp();
162
163         if (ifp->if_flags & IFF_UP) {
164                 if_down(ifp);
165                 if (ifp->if_flags & IFF_RUNNING) {
166                     /* find internet addresses and delete routes */
167                     struct ifaddr *ifa;
168                     TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
169                             rtinit(ifa, (int)RTM_DELETE, 0);
170                     }
171                 }
172         }
173
174         TAILQ_REMOVE(&ifnet, ifp, if_link);
175         splx(s);
176         return 0;
177 }
178
179 static void
180 ef_init(void *foo) {
181         return;
182 }
183
184 static int
185 ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
186 {
187         struct efnet *sc = ifp->if_softc;
188         struct ifaddr *ifa = (struct ifaddr*)data;
189         int s, error;
190
191         EFDEBUG("IOCTL %ld for %s\n", cmd, ifp->if_xname);
192         error = 0;
193         s = splimp();
194         switch (cmd) {
195             case SIOCSIFADDR:
196                 if (sc->ef_frametype == ETHER_FT_8023 && 
197                     ifa->ifa_addr->sa_family != AF_IPX) {
198                         error = EAFNOSUPPORT;
199                         break;
200                 }
201                 ifp->if_flags |= IFF_UP; 
202                 /* FALL THROUGH */
203             case SIOCGIFADDR:
204             case SIOCSIFMTU:
205                 error = ether_ioctl(ifp, cmd, data);
206                 break;
207             case SIOCSIFFLAGS:
208                 error = 0;
209                 break;
210             default:
211                 error = EINVAL;
212         }
213         splx(s);
214         return error;
215 }
216
217 /*
218  * Currently packet prepared in the ether_output(), but this can be a better
219  * place.
220  */
221 static void
222 ef_start(struct ifnet *ifp)
223 {
224         struct efnet *sc = (struct efnet*)ifp->if_softc;
225         struct ifnet *p;
226         struct mbuf *m;
227
228         ifp->if_flags |= IFF_OACTIVE;
229         p = sc->ef_ifp;
230
231         EFDEBUG("\n");
232         for (;;) {
233                 IF_DEQUEUE(&ifp->if_snd, m);
234                 if (m == 0)
235                         break;
236                 if (ifp->if_bpf)
237                         bpf_mtap(ifp, m);
238                 if (IF_QFULL(&p->if_snd)) {
239                         IF_DROP(&p->if_snd);
240                         ifp->if_oerrors++;
241                         m_freem(m);
242                         continue;
243                 }
244                 IF_ENQUEUE(&p->if_snd, m);
245                 if ((p->if_flags & IFF_OACTIVE) == 0) {
246                         p->if_start(p);
247                         ifp->if_opackets++;
248                 }
249         }
250         ifp->if_flags &= ~IFF_OACTIVE;
251         return;
252 }
253
254 /*
255  * Inline functions do not put additional overhead to procedure call or
256  * parameter passing but simplify the code
257  */
258 static int __inline
259 ef_inputEII(struct mbuf *m, struct ether_header *eh, struct llc* l,
260         u_short ether_type)
261 {
262         int isr;
263
264         switch(ether_type) {
265 #ifdef IPX
266         case ETHERTYPE_IPX:
267                 isr = NETISR_IPX;
268                 break;
269 #endif
270 #ifdef INET
271         case ETHERTYPE_IP:
272                 if (ipflow_fastforward(m))
273                         return (0);
274                 isr = NETISR_IP;
275                 break;
276         case ETHERTYPE_ARP:
277                 isr = NETISR_ARP;
278                 break;
279 #endif
280         default:
281                 return (EPROTONOSUPPORT);
282         }
283         netisr_dispatch(isr, m);
284         return (0);
285 }
286
287 static int __inline
288 ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l,
289         u_short ether_type)
290 {
291         int isr;
292
293         switch(ether_type) {
294 #ifdef IPX
295         case ETHERTYPE_IPX:
296                 m_adj(m, 8);
297                 isr = NETISR_IPX;
298                 break;
299 #endif
300         default:
301                 return (EPROTONOSUPPORT);
302         }
303         netisr_dispatch(isr, m);
304         return (0);
305 }
306
307 static int __inline
308 ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l,
309         u_short ether_type)
310 {
311         int isr;
312
313         switch(ether_type) {
314 #ifdef IPX
315         case 0xe0:
316                 m_adj(m, 3);
317                 isr = NETISR_IPX;
318                 break;
319 #endif
320         default:
321                 return (EPROTONOSUPPORT);
322         }
323         netisr_dispatch(isr, m);
324         return (0);
325 }
326
327 /*
328  * Called from ether_input()
329  */
330 static int
331 ef_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
332 {
333         u_short ether_type;
334         int ft = -1;
335         struct efnet *efp;
336         struct ifnet *eifp;
337         struct llc *l;
338         struct ef_link *efl;
339         int isr;
340
341         ether_type = ntohs(eh->ether_type);
342         if (ether_type < ETHERMTU) {
343                 l = mtod(m, struct llc*);
344                 if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) {
345                         /* 
346                          * Novell's "802.3" frame
347                          */
348                         ft = ETHER_FT_8023;
349                 } else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) {
350                         /*
351                          * 802.2/SNAP
352                          */
353                         ft = ETHER_FT_SNAP;
354                         ether_type = ntohs(l->llc_un.type_snap.ether_type);
355                 } else if (l->llc_dsap == l->llc_ssap) {
356                         /*
357                          * 802.3/802.2
358                          */
359                         ft = ETHER_FT_8022;
360                         ether_type = l->llc_ssap;
361                 }
362         } else
363                 ft = ETHER_FT_EII;
364
365         if (ft == -1) {
366                 EFDEBUG("Unrecognised ether_type %x\n", ether_type);
367                 return EPROTONOSUPPORT;
368         }
369
370         /*
371          * Check if interface configured for the given frame
372          */
373         efp = NULL;
374         SLIST_FOREACH(efl, &efdev, el_next) {
375                 if (efl->el_ifp == ifp) {
376                         efp = efl->el_units[ft];
377                         break;
378                 }
379         }
380         if (efp == NULL) {
381                 EFDEBUG("Can't find if for %d\n", ft);
382                 return EPROTONOSUPPORT;
383         }
384         eifp = &efp->ef_ac.ac_if;
385         if ((eifp->if_flags & IFF_UP) == 0)
386                 return EPROTONOSUPPORT;
387         eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
388         m->m_pkthdr.rcvif = eifp;
389
390         if (eifp->if_bpf) {
391                 struct mbuf m0;
392                 m0.m_next = m;
393                 m0.m_len = sizeof(struct ether_header);
394                 m0.m_data = (char *)eh;
395                 bpf_mtap(eifp, &m0);
396         }
397         /*
398          * Now we ready to adjust mbufs and pass them to protocol intr's
399          */
400         switch(ft) {
401         case ETHER_FT_EII:
402                 return (ef_inputEII(m, eh, l, ether_type));
403                 break;
404 #ifdef IPX
405         case ETHER_FT_8023:             /* only IPX can be here */
406                 isr = NETISR_IPX;
407                 break;
408 #endif
409         case ETHER_FT_SNAP:
410                 return (ef_inputSNAP(m, eh, l, ether_type));
411                 break;
412         case ETHER_FT_8022:
413                 return (ef_input8022(m, eh, l, ether_type));
414                 break;
415         default:
416                 EFDEBUG("No support for frame %d and proto %04x\n",
417                         ft, ether_type);
418                 return (EPROTONOSUPPORT);
419         }
420         netisr_dispatch(isr, m);
421         return (0);
422 }
423
424 static int
425 ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
426         int *hlen)
427 {
428         struct efnet *sc = (struct efnet*)ifp->if_softc;
429         struct mbuf *m = *mp;
430         u_char *cp;
431         short type;
432
433         if (ifp->if_type != IFT_XETHER)
434                 return ENETDOWN;
435         switch (sc->ef_frametype) {
436             case ETHER_FT_EII:
437 #ifdef IPX
438                 type = htons(ETHERTYPE_IPX);
439 #else
440                 return EPFNOSUPPORT;
441 #endif
442                 break;
443             case ETHER_FT_8023:
444                 type = htons(m->m_pkthdr.len);
445                 break;
446             case ETHER_FT_8022:
447                 M_PREPEND(m, ETHER_HDR_LEN + 3, M_WAIT);
448                 if (m == NULL) {
449                         *mp = NULL;
450                         return ENOBUFS;
451                 }
452                 /*
453                  * Ensure that ethernet header and next three bytes
454                  * will fit into single mbuf
455                  */
456                 m = m_pullup(m, ETHER_HDR_LEN + 3);
457                 if (m == NULL) {
458                         *mp = NULL;
459                         return ENOBUFS;
460                 }
461                 m_adj(m, ETHER_HDR_LEN);
462                 type = htons(m->m_pkthdr.len);
463                 cp = mtod(m, u_char *);
464                 *cp++ = 0xE0;
465                 *cp++ = 0xE0;
466                 *cp++ = 0x03;
467                 *hlen += 3;
468                 break;
469             case ETHER_FT_SNAP:
470                 M_PREPEND(m, 8, M_WAIT);
471                 if (m == NULL) {
472                         *mp = NULL;
473                         return ENOBUFS;
474                 }
475                 type = htons(m->m_pkthdr.len);
476                 cp = mtod(m, u_char *);
477                 bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8);
478                 *hlen += 8;
479                 break;
480             default:
481                 return EPFNOSUPPORT;
482         }
483         *mp = m;
484         *tp = type;
485         return 0;
486 }
487
488 /*
489  * Create clone from the given interface
490  */
491 static int
492 ef_clone(struct ef_link *efl, int ft)
493 {
494         struct efnet *efp;
495         struct ifnet *eifp;
496         struct ifnet *ifp = efl->el_ifp;
497
498         efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR,
499             M_WAITOK | M_ZERO);
500         if (efp == NULL)
501                 return ENOMEM;
502         efp->ef_ifp = ifp;
503         efp->ef_frametype = ft;
504         eifp = &efp->ef_ac.ac_if;
505         snprintf(eifp->if_xname, IFNAMSIZ,
506             "%sf%d", ifp->if_xname, efp->ef_frametype);
507         eifp->if_dname = "ef";
508         eifp->if_dunit = IF_DUNIT_NONE;
509         eifp->if_softc = efp;
510         if (ifp->if_ioctl)
511                 eifp->if_ioctl = ef_ioctl;
512         efl->el_units[ft] = efp;
513         return 0;
514 }
515
516 static int
517 ef_load(void)
518 {
519         struct ifnet *ifp;
520         struct efnet *efp;
521         struct ef_link *efl = NULL;
522         int error = 0, d;
523
524         TAILQ_FOREACH(ifp, &ifnet, if_link) {
525                 if (ifp->if_type != IFT_ETHER) continue;
526                 EFDEBUG("Found interface %s\n", ifp->if_xname);
527                 efl = (struct ef_link*)malloc(sizeof(struct ef_link), 
528                     M_IFADDR, M_WAITOK);
529                 if (efl == NULL) {
530                         error = ENOMEM;
531                         break;
532                 }
533                 bzero(efl, sizeof(*efl));
534
535                 efl->el_ifp = ifp;
536 #ifdef ETHER_II
537                 error = ef_clone(efl, ETHER_FT_EII);
538                 if (error) break;
539 #endif
540 #ifdef ETHER_8023
541                 error = ef_clone(efl, ETHER_FT_8023);
542                 if (error) break;
543 #endif
544 #ifdef ETHER_8022
545                 error = ef_clone(efl, ETHER_FT_8022);
546                 if (error) break;
547 #endif
548 #ifdef ETHER_SNAP
549                 error = ef_clone(efl, ETHER_FT_SNAP);
550                 if (error) break;
551 #endif
552                 efcount++;
553                 SLIST_INSERT_HEAD(&efdev, efl, el_next);
554         }
555         if (error) {
556                 if (efl)
557                         SLIST_INSERT_HEAD(&efdev, efl, el_next);
558                 SLIST_FOREACH(efl, &efdev, el_next) {
559                         for (d = 0; d < EF_NFT; d++)
560                                 if (efl->el_units[d])
561                                         free(efl->el_units[d], M_IFADDR);
562                         free(efl, M_IFADDR);
563                 }
564                 return error;
565         }
566         SLIST_FOREACH(efl, &efdev, el_next) {
567                 for (d = 0; d < EF_NFT; d++) {
568                         efp = efl->el_units[d];
569                         if (efp)
570                                 ef_attach(efp);
571                 }
572         }
573         ef_inputp = ef_input;
574         ef_outputp = ef_output;
575         EFDEBUG("Loaded\n");
576         return 0;
577 }
578
579 static int
580 ef_unload(void)
581 {
582         struct efnet *efp;
583         struct ef_link *efl;
584         int d;
585
586         ef_inputp = NULL;
587         ef_outputp = NULL;
588         SLIST_FOREACH(efl, &efdev, el_next) {
589                 for (d = 0; d < EF_NFT; d++) {
590                         efp = efl->el_units[d];
591                         if (efp) {
592                                 ef_detach(efp);
593                         }
594                 }
595         }
596         EFDEBUG("Unloaded\n");
597         return 0;
598 }
599
600 static int 
601 if_ef_modevent(module_t mod, int type, void *data)
602 {
603         switch ((modeventtype_t)type) {
604             case MOD_LOAD:
605                 return ef_load();
606             case MOD_UNLOAD:
607                 return ef_unload();
608             default:
609                 break;
610         }
611         return 0;
612 }
613
614 static moduledata_t if_ef_mod = {
615         "if_ef", if_ef_modevent, NULL
616 };
617
618 DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE);