Centralize if queue handling.
[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.5 2003/09/15 23:38:13 hsu 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)
260 {
261         int isr;
262
263         switch(ether_type) {
264 #ifdef IPX
265         case ETHERTYPE_IPX:
266                 isr = NETISR_IPX;
267                 break;
268 #endif
269 #ifdef INET
270         case ETHERTYPE_IP:
271                 if (ipflow_fastforward(m))
272                         return (0);
273                 isr = NETISR_IP;
274                 break;
275         case ETHERTYPE_ARP:
276                 isr = NETISR_ARP;
277                 break;
278 #endif
279         default:
280                 return (EPROTONOSUPPORT);
281         }
282         netisr_dispatch(isr, m);
283         return (0);
284 }
285
286 static int __inline
287 ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l,
288         u_short ether_type)
289 {
290         int isr;
291
292         switch(ether_type) {
293 #ifdef IPX
294         case ETHERTYPE_IPX:
295                 m_adj(m, 8);
296                 isr = NETISR_IPX;
297                 break;
298 #endif
299         default:
300                 return (EPROTONOSUPPORT);
301         }
302         netisr_dispatch(isr, m);
303         return (0);
304 }
305
306 static int __inline
307 ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l,
308         u_short ether_type)
309 {
310         int isr;
311
312         switch(ether_type) {
313 #ifdef IPX
314         case 0xe0:
315                 m_adj(m, 3);
316                 isr = NETISR_IPX;
317                 break;
318 #endif
319         default:
320                 return (EPROTONOSUPPORT);
321         }
322         netisr_dispatch(isr, m);
323         return (0);
324 }
325
326 /*
327  * Called from ether_input()
328  */
329 static int
330 ef_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
331 {
332         u_short ether_type;
333         int s, ft = -1;
334         struct efnet *efp;
335         struct ifnet *eifp;
336         struct llc *l;
337         struct ef_link *efl;
338         int isr;
339
340         ether_type = ntohs(eh->ether_type);
341         if (ether_type < ETHERMTU) {
342                 l = mtod(m, struct llc*);
343                 if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) {
344                         /* 
345                          * Novell's "802.3" frame
346                          */
347                         ft = ETHER_FT_8023;
348                 } else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) {
349                         /*
350                          * 802.2/SNAP
351                          */
352                         ft = ETHER_FT_SNAP;
353                         ether_type = ntohs(l->llc_un.type_snap.ether_type);
354                 } else if (l->llc_dsap == l->llc_ssap) {
355                         /*
356                          * 802.3/802.2
357                          */
358                         ft = ETHER_FT_8022;
359                         ether_type = l->llc_ssap;
360                 }
361         } else
362                 ft = ETHER_FT_EII;
363
364         if (ft == -1) {
365                 EFDEBUG("Unrecognised ether_type %x\n", ether_type);
366                 return EPROTONOSUPPORT;
367         }
368
369         /*
370          * Check if interface configured for the given frame
371          */
372         efp = NULL;
373         SLIST_FOREACH(efl, &efdev, el_next) {
374                 if (efl->el_ifp == ifp) {
375                         efp = efl->el_units[ft];
376                         break;
377                 }
378         }
379         if (efp == NULL) {
380                 EFDEBUG("Can't find if for %d\n", ft);
381                 return EPROTONOSUPPORT;
382         }
383         eifp = &efp->ef_ac.ac_if;
384         if ((eifp->if_flags & IFF_UP) == 0)
385                 return EPROTONOSUPPORT;
386         eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
387         m->m_pkthdr.rcvif = eifp;
388
389         if (eifp->if_bpf) {
390                 struct mbuf m0;
391                 m0.m_next = m;
392                 m0.m_len = sizeof(struct ether_header);
393                 m0.m_data = (char *)eh;
394                 bpf_mtap(eifp, &m0);
395         }
396         /*
397          * Now we ready to adjust mbufs and pass them to protocol intr's
398          */
399         switch(ft) {
400         case ETHER_FT_EII:
401                 return (ef_inputEII(m, eh, l, ether_type));
402                 break;
403 #ifdef IPX
404         case ETHER_FT_8023:             /* only IPX can be here */
405                 isr = NETISR_IPX;
406                 break;
407 #endif
408         case ETHER_FT_SNAP:
409                 return (ef_inputSNAP(m, eh, l, ether_type));
410                 break;
411         case ETHER_FT_8022:
412                 return (ef_input8022(m, eh, l, ether_type));
413                 break;
414         default:
415                 EFDEBUG("No support for frame %d and proto %04x\n",
416                         ft, ether_type);
417                 return (EPROTONOSUPPORT);
418         }
419         netisr_dispatch(isr, m);
420         return (0);
421 }
422
423 static int
424 ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
425         int *hlen)
426 {
427         struct mbuf *m = *mp;
428         u_char *cp;
429         short type;
430
431         if (ifp->if_type != IFT_XETHER)
432                 return ENETDOWN;
433         switch (ifp->if_unit) {
434             case ETHER_FT_EII:
435 #ifdef IPX
436                 type = htons(ETHERTYPE_IPX);
437 #else
438                 return EPFNOSUPPORT;
439 #endif
440                 break;
441             case ETHER_FT_8023:
442                 type = htons(m->m_pkthdr.len);
443                 break;
444             case ETHER_FT_8022:
445                 M_PREPEND(m, ETHER_HDR_LEN + 3, M_WAIT);
446                 if (m == NULL) {
447                         *mp = NULL;
448                         return ENOBUFS;
449                 }
450                 /*
451                  * Ensure that ethernet header and next three bytes
452                  * will fit into single mbuf
453                  */
454                 m = m_pullup(m, ETHER_HDR_LEN + 3);
455                 if (m == NULL) {
456                         *mp = NULL;
457                         return ENOBUFS;
458                 }
459                 m_adj(m, ETHER_HDR_LEN);
460                 type = htons(m->m_pkthdr.len);
461                 cp = mtod(m, u_char *);
462                 *cp++ = 0xE0;
463                 *cp++ = 0xE0;
464                 *cp++ = 0x03;
465                 *hlen += 3;
466                 break;
467             case ETHER_FT_SNAP:
468                 M_PREPEND(m, 8, M_WAIT);
469                 if (m == NULL) {
470                         *mp = NULL;
471                         return ENOBUFS;
472                 }
473                 type = htons(m->m_pkthdr.len);
474                 cp = mtod(m, u_char *);
475                 bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8);
476                 *hlen += 8;
477                 break;
478             default:
479                 return EPFNOSUPPORT;
480         }
481         *mp = m;
482         *tp = type;
483         return 0;
484 }
485
486 /*
487  * Create clone from the given interface
488  */
489 static int
490 ef_clone(struct ef_link *efl, int ft)
491 {
492         struct efnet *efp;
493         struct ifnet *eifp;
494         struct ifnet *ifp = efl->el_ifp;
495         char cbuf[IFNAMSIZ], *ifname;
496         int ifnlen;
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         eifp = &efp->ef_ac.ac_if;
504         ifnlen = 1 + snprintf(cbuf, sizeof(cbuf), "%s%df", ifp->if_name,
505             ifp->if_unit);
506         ifname = (char*)malloc(ifnlen, M_IFADDR, M_WAITOK);
507         eifp->if_name = strcpy(ifname, cbuf);
508         eifp->if_unit = ft;
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%d\n", ifp->if_name, ifp->if_unit);
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);