kernel tree reorganization stage 1: Major cvs repository work (not logged as
[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.4 2003/08/07 21:54:30 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 };
84
85 struct ef_link {
86         SLIST_ENTRY(ef_link) el_next;
87         struct ifnet    *el_ifp;                /* raw device for this clones */
88         struct efnet    *el_units[EF_NFT];      /* our clones */
89 };
90
91 static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL};
92 static int efcount;
93
94 extern int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m);
95 extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp,
96                 struct sockaddr *dst, short *tp, int *hlen);
97
98 /*
99 static void ef_reset (struct ifnet *);
100 */
101 static int ef_attach(struct efnet *sc);
102 static int ef_detach(struct efnet *sc);
103 static void ef_init(void *);
104 static int ef_ioctl(struct ifnet *, u_long, caddr_t);
105 static void ef_start(struct ifnet *);
106 static int ef_input(struct ifnet*, struct ether_header *, struct mbuf *);
107 static int ef_output(struct ifnet *ifp, struct mbuf **mp,
108                 struct sockaddr *dst, short *tp, int *hlen);
109
110 static int ef_load(void);
111 static int ef_unload(void);
112
113 /*
114  * Install the interface, most of structure initialization done in ef_clone()
115  */
116 static int
117 ef_attach(struct efnet *sc)
118 {
119         struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
120         struct ifaddr *ifa1, *ifa2;
121         struct sockaddr_dl *sdl1, *sdl2;
122
123         ifp->if_output = ether_output;
124         ifp->if_start = ef_start;
125         ifp->if_watchdog = NULL;
126         ifp->if_init = ef_init;
127         ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
128         ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
129         /*
130          * Attach the interface
131          */
132         ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
133
134         ifp->if_resolvemulti = 0;
135         ifp->if_type = IFT_XETHER;
136         ifp->if_flags |= IFF_RUNNING;
137
138         ifa1 = ifnet_addrs[ifp->if_index - 1];
139         ifa2 = ifnet_addrs[sc->ef_ifp->if_index - 1];
140         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
141         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
142         sdl1->sdl_type = IFT_ETHER;
143         sdl1->sdl_alen = ETHER_ADDR_LEN;
144         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
145         bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN);
146
147         EFDEBUG("%s%d: attached\n", ifp->if_name, ifp->if_unit);
148         return 1;
149 }
150
151 /*
152  * This is for _testing_only_, just removes interface from interfaces list
153  */
154 static int
155 ef_detach(struct efnet *sc)
156 {
157         struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
158         int s;
159
160         s = splimp();
161
162         if (ifp->if_flags & IFF_UP) {
163                 if_down(ifp);
164                 if (ifp->if_flags & IFF_RUNNING) {
165                     /* find internet addresses and delete routes */
166                     struct ifaddr *ifa;
167                     TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
168                             rtinit(ifa, (int)RTM_DELETE, 0);
169                     }
170                 }
171         }
172
173         TAILQ_REMOVE(&ifnet, ifp, if_link);
174         splx(s);
175         return 0;
176 }
177
178 static void
179 ef_init(void *foo) {
180         return;
181 }
182
183 static int
184 ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
185 {
186 /*      struct ef_link *sc = (struct ef_link*)ifp->if_softc;*/
187         struct ifaddr *ifa = (struct ifaddr*)data;
188         int s, error;
189
190         EFDEBUG("IOCTL %ld for %s%d\n", cmd, ifp->if_name, ifp->if_unit);
191         error = 0;
192         s = splimp();
193         switch (cmd) {
194             case SIOCSIFADDR:
195                 if (ifp->if_unit == ETHER_FT_8023 && 
196                     ifa->ifa_addr->sa_family != AF_IPX) {
197                         error = EAFNOSUPPORT;
198                         break;
199                 }
200                 ifp->if_flags |= IFF_UP; 
201                 /* FALL THROUGH */
202             case SIOCGIFADDR:
203             case SIOCSIFMTU:
204                 error = ether_ioctl(ifp, cmd, data);
205                 break;
206             case SIOCSIFFLAGS:
207                 error = 0;
208                 break;
209             default:
210                 error = EINVAL;
211         }
212         splx(s);
213         return error;
214 }
215
216 /*
217  * Currently packet prepared in the ether_output(), but this can be a better
218  * place.
219  */
220 static void
221 ef_start(struct ifnet *ifp)
222 {
223         struct efnet *sc = (struct efnet*)ifp->if_softc;
224         struct ifnet *p;
225         struct mbuf *m;
226
227         ifp->if_flags |= IFF_OACTIVE;
228         p = sc->ef_ifp;
229
230         EFDEBUG("\n");
231         for (;;) {
232                 IF_DEQUEUE(&ifp->if_snd, m);
233                 if (m == 0)
234                         break;
235                 if (ifp->if_bpf)
236                         bpf_mtap(ifp, m);
237                 if (IF_QFULL(&p->if_snd)) {
238                         IF_DROP(&p->if_snd);
239                         ifp->if_oerrors++;
240                         m_freem(m);
241                         continue;
242                 }
243                 IF_ENQUEUE(&p->if_snd, m);
244                 if ((p->if_flags & IFF_OACTIVE) == 0) {
245                         p->if_start(p);
246                         ifp->if_opackets++;
247                 }
248         }
249         ifp->if_flags &= ~IFF_OACTIVE;
250         return;
251 }
252
253 /*
254  * Inline functions do not put additional overhead to procedure call or
255  * parameter passing but simplify the code
256  */
257 static int __inline
258 ef_inputEII(struct mbuf *m, struct ether_header *eh, struct llc* l,
259         u_short ether_type, struct ifqueue **inq)
260 {
261         switch(ether_type) {
262 #ifdef IPX
263             case ETHERTYPE_IPX:
264                 schednetisr(NETISR_IPX);
265                 *inq = &ipxintrq;
266                 break;
267 #endif
268 #ifdef INET
269             case ETHERTYPE_IP:
270                 if (ipflow_fastforward(m))
271                         return 1;
272                 schednetisr(NETISR_IP);
273                 *inq = &ipintrq;
274                 break;
275
276             case ETHERTYPE_ARP:
277                 schednetisr(NETISR_ARP);
278                 *inq = &arpintrq;
279                 break;
280 #endif
281             default:
282                 return EPROTONOSUPPORT;
283         }
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, struct ifqueue **inq)
290 {
291         switch(ether_type) {
292 #ifdef IPX
293             case ETHERTYPE_IPX:
294                 m_adj(m, 8);
295                 schednetisr(NETISR_IPX);
296                 *inq = &ipxintrq;
297                 break;
298 #endif
299             default:
300                 return EPROTONOSUPPORT;
301         }
302         return 0;
303 }
304
305 static int __inline
306 ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l,
307         u_short ether_type, struct ifqueue **inq)
308 {
309         switch(ether_type) {
310 #ifdef IPX
311             case 0xe0:
312                 m_adj(m, 3);
313                 schednetisr(NETISR_IPX);
314                 *inq = &ipxintrq;
315                 break;
316 #endif
317             default:
318                 return EPROTONOSUPPORT;
319         }
320         return 0;
321 }
322 /*
323  * Called from ether_input()
324  */
325 static int
326 ef_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
327 {
328         u_short ether_type;
329         int s, ft = -1;
330         struct ifqueue *inq;
331         struct efnet *efp;
332         struct ifnet *eifp;
333         struct llc *l;
334         struct ef_link *efl;
335
336         ether_type = ntohs(eh->ether_type);
337         if (ether_type < ETHERMTU) {
338                 l = mtod(m, struct llc*);
339                 if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) {
340                         /* 
341                          * Novell's "802.3" frame
342                          */
343                         ft = ETHER_FT_8023;
344                 } else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) {
345                         /*
346                          * 802.2/SNAP
347                          */
348                         ft = ETHER_FT_SNAP;
349                         ether_type = ntohs(l->llc_un.type_snap.ether_type);
350                 } else if (l->llc_dsap == l->llc_ssap) {
351                         /*
352                          * 802.3/802.2
353                          */
354                         ft = ETHER_FT_8022;
355                         ether_type = l->llc_ssap;
356                 }
357         } else
358                 ft = ETHER_FT_EII;
359
360         if (ft == -1) {
361                 EFDEBUG("Unrecognised ether_type %x\n", ether_type);
362                 return EPROTONOSUPPORT;
363         }
364
365         /*
366          * Check if interface configured for the given frame
367          */
368         efp = NULL;
369         SLIST_FOREACH(efl, &efdev, el_next) {
370                 if (efl->el_ifp == ifp) {
371                         efp = efl->el_units[ft];
372                         break;
373                 }
374         }
375         if (efp == NULL) {
376                 EFDEBUG("Can't find if for %d\n", ft);
377                 return EPROTONOSUPPORT;
378         }
379         eifp = &efp->ef_ac.ac_if;
380         if ((eifp->if_flags & IFF_UP) == 0)
381                 return EPROTONOSUPPORT;
382         eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
383         m->m_pkthdr.rcvif = eifp;
384
385         if (eifp->if_bpf) {
386                 struct mbuf m0;
387                 m0.m_next = m;
388                 m0.m_len = sizeof(struct ether_header);
389                 m0.m_data = (char *)eh;
390                 bpf_mtap(eifp, &m0);
391         }
392         /*
393          * Now we ready to adjust mbufs and pass them to protocol intr's
394          */
395         inq = NULL;
396         switch(ft) {
397             case ETHER_FT_EII:
398                 if (ef_inputEII(m, eh, l, ether_type, &inq) != 0)
399                         return EPROTONOSUPPORT;
400                 break;
401 #ifdef IPX
402             case ETHER_FT_8023:         /* only IPX can be here */
403                 schednetisr(NETISR_IPX);
404                 inq = &ipxintrq;
405                 break;
406 #endif
407             case ETHER_FT_SNAP:
408                 if (ef_inputSNAP(m, eh, l, ether_type, &inq) != 0)
409                         return EPROTONOSUPPORT;
410                 break;
411             case ETHER_FT_8022:
412                 if (ef_input8022(m, eh, l, ether_type, &inq) != 0)
413                         return EPROTONOSUPPORT;
414                 break;
415         }
416
417         if (inq == NULL) {
418                 EFDEBUG("No support for frame %d and proto %04x\n",
419                         ft, ether_type);
420                 return EPROTONOSUPPORT;
421         }
422         s = splimp();
423         if (IF_QFULL(inq)) {
424                 IF_DROP(inq);
425                 m_freem(m);
426         } else
427                 IF_ENQUEUE(inq, m);
428         splx(s);
429         return 0;
430 }
431
432 static int
433 ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
434         int *hlen)
435 {
436         struct mbuf *m = *mp;
437         u_char *cp;
438         short type;
439
440         if (ifp->if_type != IFT_XETHER)
441                 return ENETDOWN;
442         switch (ifp->if_unit) {
443             case ETHER_FT_EII:
444 #ifdef IPX
445                 type = htons(ETHERTYPE_IPX);
446 #else
447                 return EPFNOSUPPORT;
448 #endif
449                 break;
450             case ETHER_FT_8023:
451                 type = htons(m->m_pkthdr.len);
452                 break;
453             case ETHER_FT_8022:
454                 M_PREPEND(m, ETHER_HDR_LEN + 3, M_WAIT);
455                 if (m == NULL) {
456                         *mp = NULL;
457                         return ENOBUFS;
458                 }
459                 /*
460                  * Ensure that ethernet header and next three bytes
461                  * will fit into single mbuf
462                  */
463                 m = m_pullup(m, ETHER_HDR_LEN + 3);
464                 if (m == NULL) {
465                         *mp = NULL;
466                         return ENOBUFS;
467                 }
468                 m_adj(m, ETHER_HDR_LEN);
469                 type = htons(m->m_pkthdr.len);
470                 cp = mtod(m, u_char *);
471                 *cp++ = 0xE0;
472                 *cp++ = 0xE0;
473                 *cp++ = 0x03;
474                 *hlen += 3;
475                 break;
476             case ETHER_FT_SNAP:
477                 M_PREPEND(m, 8, M_WAIT);
478                 if (m == NULL) {
479                         *mp = NULL;
480                         return ENOBUFS;
481                 }
482                 type = htons(m->m_pkthdr.len);
483                 cp = mtod(m, u_char *);
484                 bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8);
485                 *hlen += 8;
486                 break;
487             default:
488                 return EPFNOSUPPORT;
489         }
490         *mp = m;
491         *tp = type;
492         return 0;
493 }
494
495 /*
496  * Create clone from the given interface
497  */
498 static int
499 ef_clone(struct ef_link *efl, int ft)
500 {
501         struct efnet *efp;
502         struct ifnet *eifp;
503         struct ifnet *ifp = efl->el_ifp;
504         char cbuf[IFNAMSIZ], *ifname;
505         int ifnlen;
506
507         efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR,
508             M_WAITOK | M_ZERO);
509         if (efp == NULL)
510                 return ENOMEM;
511         efp->ef_ifp = ifp;
512         eifp = &efp->ef_ac.ac_if;
513         ifnlen = 1 + snprintf(cbuf, sizeof(cbuf), "%s%df", ifp->if_name,
514             ifp->if_unit);
515         ifname = (char*)malloc(ifnlen, M_IFADDR, M_WAITOK);
516         eifp->if_name = strcpy(ifname, cbuf);
517         eifp->if_unit = ft;
518         eifp->if_softc = efp;
519         if (ifp->if_ioctl)
520                 eifp->if_ioctl = ef_ioctl;
521         efl->el_units[ft] = efp;
522         return 0;
523 }
524
525 static int
526 ef_load(void)
527 {
528         struct ifnet *ifp;
529         struct efnet *efp;
530         struct ef_link *efl = NULL;
531         int error = 0, d;
532
533         TAILQ_FOREACH(ifp, &ifnet, if_link) {
534                 if (ifp->if_type != IFT_ETHER) continue;
535                 EFDEBUG("Found interface %s%d\n", ifp->if_name, ifp->if_unit);
536                 efl = (struct ef_link*)malloc(sizeof(struct ef_link), 
537                     M_IFADDR, M_WAITOK);
538                 if (efl == NULL) {
539                         error = ENOMEM;
540                         break;
541                 }
542                 bzero(efl, sizeof(*efl));
543
544                 efl->el_ifp = ifp;
545 #ifdef ETHER_II
546                 error = ef_clone(efl, ETHER_FT_EII);
547                 if (error) break;
548 #endif
549 #ifdef ETHER_8023
550                 error = ef_clone(efl, ETHER_FT_8023);
551                 if (error) break;
552 #endif
553 #ifdef ETHER_8022
554                 error = ef_clone(efl, ETHER_FT_8022);
555                 if (error) break;
556 #endif
557 #ifdef ETHER_SNAP
558                 error = ef_clone(efl, ETHER_FT_SNAP);
559                 if (error) break;
560 #endif
561                 efcount++;
562                 SLIST_INSERT_HEAD(&efdev, efl, el_next);
563         }
564         if (error) {
565                 if (efl)
566                         SLIST_INSERT_HEAD(&efdev, efl, el_next);
567                 SLIST_FOREACH(efl, &efdev, el_next) {
568                         for (d = 0; d < EF_NFT; d++)
569                                 if (efl->el_units[d])
570                                         free(efl->el_units[d], M_IFADDR);
571                         free(efl, M_IFADDR);
572                 }
573                 return error;
574         }
575         SLIST_FOREACH(efl, &efdev, el_next) {
576                 for (d = 0; d < EF_NFT; d++) {
577                         efp = efl->el_units[d];
578                         if (efp)
579                                 ef_attach(efp);
580                 }
581         }
582         ef_inputp = ef_input;
583         ef_outputp = ef_output;
584         EFDEBUG("Loaded\n");
585         return 0;
586 }
587
588 static int
589 ef_unload(void)
590 {
591         struct efnet *efp;
592         struct ef_link *efl;
593         int d;
594
595         ef_inputp = NULL;
596         ef_outputp = NULL;
597         SLIST_FOREACH(efl, &efdev, el_next) {
598                 for (d = 0; d < EF_NFT; d++) {
599                         efp = efl->el_units[d];
600                         if (efp) {
601                                 ef_detach(efp);
602                         }
603                 }
604         }
605         EFDEBUG("Unloaded\n");
606         return 0;
607 }
608
609 static int 
610 if_ef_modevent(module_t mod, int type, void *data)
611 {
612         switch ((modeventtype_t)type) {
613             case MOD_LOAD:
614                 return ef_load();
615             case MOD_UNLOAD:
616                 return ef_unload();
617             default:
618                 break;
619         }
620         return 0;
621 }
622
623 static moduledata_t if_ef_mod = {
624         "if_ef", if_ef_modevent, NULL
625 };
626
627 DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE);