Parallelize ifnet.if_addrhead accessing by duplicating the list itself
[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.26 2008/03/07 11:34:19 sephe 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 #include <sys/thread2.h>
44
45 #include <net/ethernet.h>
46 #include <net/if_llc.h>
47 #include <net/if.h>
48 #include <net/if_arp.h>
49 #include <net/if_dl.h>
50 #include <net/if_types.h>
51 #include <net/netisr.h>
52 #include <net/route.h>
53 #include <net/bpf.h>
54
55 #ifdef INET
56 #include <netinet/in.h>
57 #include <netinet/in_var.h>
58 #include <netinet/if_ether.h>
59 #endif
60
61 #ifdef IPX
62 #include <netproto/ipx/ipx.h>
63 #include <netproto/ipx/ipx_if.h>
64 #endif
65
66 /* internal frame types */
67 #define ETHER_FT_EII            0       /* Ethernet_II - default */
68 #define ETHER_FT_8023           1       /* 802.3 (Novell) */
69 #define ETHER_FT_8022           2       /* 802.2 */
70 #define ETHER_FT_SNAP           3       /* SNAP */
71 #define EF_NFT                  4       /* total number of frame types */
72
73 #ifdef EF_DEBUG
74 #define EFDEBUG(format, args...) kprintf("%s: "format, __func__ ,## args)
75 #else
76 #define EFDEBUG(format, args...)
77 #endif
78
79 #define EFERROR(format, args...) kprintf("%s: "format, __func__ ,## args)
80
81 struct efnet {
82         struct arpcom   ef_ac;
83         struct ifnet *  ef_ifp;
84         int             ef_frametype;
85 };
86
87 struct ef_link {
88         SLIST_ENTRY(ef_link) el_next;
89         struct ifnet    *el_ifp;                /* raw device for this clones */
90         struct efnet    *el_units[EF_NFT];      /* our clones */
91 };
92
93 static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL};
94 static int efcount;
95
96 extern int (*ef_inputp)(struct ifnet*, const struct ether_header *eh,
97                 struct mbuf *m);
98 extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp,
99                 struct sockaddr *dst, short *tp, int *hlen);
100
101 /*
102 static void ef_reset (struct ifnet *);
103 */
104 static int ef_attach(struct efnet *sc);
105 static int ef_detach(struct efnet *sc);
106 static void ef_init(void *);
107 static int ef_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
108 static void ef_start(struct ifnet *);
109 static int ef_input(struct ifnet*, const struct ether_header *, struct mbuf *);
110 static int ef_output(struct ifnet *ifp, struct mbuf **mp,
111                 struct sockaddr *dst, short *tp, int *hlen);
112
113 static int ef_load(void);
114 static int ef_unload(void);
115
116 /*
117  * Install the interface, most of structure initialization done in ef_clone()
118  */
119 static int
120 ef_attach(struct efnet *sc)
121 {
122         struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
123
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, IF_LLADDR(sc->ef_ifp), NULL);
133
134         ifp->if_resolvemulti = 0;
135         ifp->if_type = IFT_XETHER;
136         ifp->if_flags |= IFF_RUNNING;
137
138         EFDEBUG("%s: attached\n", ifp->if_xname);
139         return 1;
140 }
141
142 /*
143  * This is for _testing_only_, just removes interface from interfaces list
144  */
145 static int
146 ef_detach(struct efnet *sc)
147 {
148         ether_ifdetach(&sc->ef_ac.ac_if);
149         return 0;
150 }
151
152 static void
153 ef_init(void *foo __unused)
154 {
155 }
156
157 static int
158 ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
159 {
160         struct efnet *sc = ifp->if_softc;
161         struct ifaddr *ifa = (struct ifaddr*)data;
162         int error;
163
164         EFDEBUG("IOCTL %ld for %s\n", cmd, ifp->if_xname);
165         error = 0;
166         crit_enter();
167         switch (cmd) {
168             case SIOCSIFADDR:
169                 if (sc->ef_frametype == ETHER_FT_8023 && 
170                     ifa->ifa_addr->sa_family != AF_IPX) {
171                         error = EAFNOSUPPORT;
172                         break;
173                 }
174                 ifp->if_flags |= IFF_UP; 
175                 /* FALL THROUGH */
176             case SIOCGIFADDR:
177             case SIOCSIFMTU:
178                 error = ether_ioctl(ifp, cmd, data);
179                 break;
180             case SIOCSIFFLAGS:
181                 error = 0;
182                 break;
183             default:
184                 error = EINVAL;
185         }
186         crit_exit();
187         return error;
188 }
189
190 /*
191  * Currently packet prepared in the ether_output(), but this can be a better
192  * place.
193  */
194 static void
195 ef_start(struct ifnet *ifp)
196 {
197         struct efnet *sc = (struct efnet*)ifp->if_softc;
198         struct ifnet *p;
199         struct mbuf *m;
200
201         ifp->if_flags |= IFF_OACTIVE;
202         p = sc->ef_ifp;
203
204         EFDEBUG("\n");
205         for (;;) {
206                 IF_DEQUEUE(&ifp->if_snd, m);
207                 if (m == 0)
208                         break;
209                 BPF_MTAP(ifp, m);
210                 if (IF_QFULL(&p->if_snd)) {
211                         IF_DROP(&p->if_snd);
212                         ifp->if_oerrors++;
213                         m_freem(m);
214                         continue;
215                 }
216                 IF_ENQUEUE(&p->if_snd, m);
217                 if ((p->if_flags & IFF_OACTIVE) == 0) {
218                         p->if_start(p);
219                         ifp->if_opackets++;
220                 }
221         }
222         ifp->if_flags &= ~IFF_OACTIVE;
223         return;
224 }
225
226 /*
227  * Inline functions do not put additional overhead to procedure call or
228  * parameter passing but simplify the code
229  */
230 static int __inline
231 ef_inputEII(struct mbuf *m, struct llc* l, u_short ether_type)
232 {
233         int isr;
234
235         switch(ether_type) {
236 #ifdef IPX
237         case ETHERTYPE_IPX:
238                 isr = NETISR_IPX;
239                 break;
240 #endif
241 #ifdef INET
242         case ETHERTYPE_IP:
243                 if (ipflow_fastforward(m, NULL))
244                         return (0);
245                 isr = NETISR_IP;
246                 break;
247         case ETHERTYPE_ARP:
248                 isr = NETISR_ARP;
249                 break;
250 #endif
251         default:
252                 return (EPROTONOSUPPORT);
253         }
254         netisr_dispatch(isr, m);
255         return (0);
256 }
257
258 static int __inline
259 ef_inputSNAP(struct mbuf *m, struct llc* l, u_short ether_type)
260 {
261         int isr;
262
263         switch(ether_type) {
264 #ifdef IPX
265         case ETHERTYPE_IPX:
266                 m_adj(m, 8);
267                 isr = NETISR_IPX;
268                 break;
269 #endif
270         default:
271                 return (EPROTONOSUPPORT);
272         }
273         netisr_dispatch(isr, m);
274         return (0);
275 }
276
277 static int __inline
278 ef_input8022(struct mbuf *m, struct llc* l, u_short ether_type)
279 {
280         int isr;
281
282         switch(ether_type) {
283 #ifdef IPX
284         case 0xe0:
285                 m_adj(m, 3);
286                 isr = NETISR_IPX;
287                 break;
288 #endif
289         default:
290                 return (EPROTONOSUPPORT);
291         }
292         netisr_dispatch(isr, m);
293         return (0);
294 }
295
296 /*
297  * Called from ether_input()
298  */
299 static int
300 ef_input(struct ifnet *ifp, const struct ether_header *eh, struct mbuf *m)
301 {
302         u_short ether_type;
303         int ft = -1;
304         struct efnet *efp;
305         struct ifnet *eifp;
306         struct llc *l;
307         struct ef_link *efl;
308         int isr;
309
310         ether_type = ntohs(eh->ether_type);
311         if (ether_type < ETHERMTU) {
312                 l = mtod(m, struct llc*);
313                 if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) {
314                         /* 
315                          * Novell's "802.3" frame
316                          */
317                         ft = ETHER_FT_8023;
318                 } else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) {
319                         /*
320                          * 802.2/SNAP
321                          */
322                         ft = ETHER_FT_SNAP;
323                         ether_type = ntohs(l->llc_un.type_snap.ether_type);
324                 } else if (l->llc_dsap == l->llc_ssap) {
325                         /*
326                          * 802.3/802.2
327                          */
328                         ft = ETHER_FT_8022;
329                         ether_type = l->llc_ssap;
330                 }
331         } else
332                 ft = ETHER_FT_EII;
333
334         if (ft == -1) {
335                 EFDEBUG("Unrecognised ether_type %x\n", ether_type);
336                 return EPROTONOSUPPORT;
337         }
338
339         /*
340          * Check if interface configured for the given frame
341          */
342         efp = NULL;
343         SLIST_FOREACH(efl, &efdev, el_next) {
344                 if (efl->el_ifp == ifp) {
345                         efp = efl->el_units[ft];
346                         break;
347                 }
348         }
349         if (efp == NULL) {
350                 EFDEBUG("Can't find if for %d\n", ft);
351                 return EPROTONOSUPPORT;
352         }
353         eifp = &efp->ef_ac.ac_if;
354         if ((eifp->if_flags & IFF_UP) == 0)
355                 return EPROTONOSUPPORT;
356         eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
357         m->m_pkthdr.rcvif = eifp;
358
359         if (eifp->if_bpf)
360                 bpf_ptap(eifp->if_bpf, m, eh, ETHER_HDR_LEN);
361
362         /*
363          * Now we ready to adjust mbufs and pass them to protocol intr's
364          */
365         switch(ft) {
366         case ETHER_FT_EII:
367                 return (ef_inputEII(m, l, ether_type));
368                 break;
369 #ifdef IPX
370         case ETHER_FT_8023:             /* only IPX can be here */
371                 isr = NETISR_IPX;
372                 break;
373 #endif
374         case ETHER_FT_SNAP:
375                 return (ef_inputSNAP(m, l, ether_type));
376                 break;
377         case ETHER_FT_8022:
378                 return (ef_input8022(m, l, ether_type));
379                 break;
380         default:
381                 EFDEBUG("No support for frame %d and proto %04x\n",
382                         ft, ether_type);
383                 return (EPROTONOSUPPORT);
384         }
385         netisr_dispatch(isr, m);
386         return (0);
387 }
388
389 static int
390 ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
391         int *hlen)
392 {
393         struct efnet *sc = (struct efnet*)ifp->if_softc;
394         struct mbuf *m = *mp;
395         u_char *cp;
396         short type;
397
398         if (ifp->if_type != IFT_XETHER)
399                 return ENETDOWN;
400         switch (sc->ef_frametype) {
401             case ETHER_FT_EII:
402 #ifdef IPX
403                 type = htons(ETHERTYPE_IPX);
404 #else
405                 return EPFNOSUPPORT;
406 #endif
407                 break;
408             case ETHER_FT_8023:
409                 type = htons(m->m_pkthdr.len);
410                 break;
411             case ETHER_FT_8022:
412                 M_PREPEND(m, ETHER_HDR_LEN + 3, MB_WAIT);
413                 if (m == NULL) {
414                         *mp = NULL;
415                         return ENOBUFS;
416                 }
417                 /*
418                  * Ensure that ethernet header and next three bytes
419                  * will fit into single mbuf
420                  */
421                 m = m_pullup(m, ETHER_HDR_LEN + 3);
422                 if (m == NULL) {
423                         *mp = NULL;
424                         return ENOBUFS;
425                 }
426                 m_adj(m, ETHER_HDR_LEN);
427                 type = htons(m->m_pkthdr.len);
428                 cp = mtod(m, u_char *);
429                 *cp++ = 0xE0;
430                 *cp++ = 0xE0;
431                 *cp++ = 0x03;
432                 *hlen += 3;
433                 break;
434             case ETHER_FT_SNAP:
435                 M_PREPEND(m, 8, MB_WAIT);
436                 if (m == NULL) {
437                         *mp = NULL;
438                         return ENOBUFS;
439                 }
440                 type = htons(m->m_pkthdr.len);
441                 cp = mtod(m, u_char *);
442                 bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8);
443                 *hlen += 8;
444                 break;
445             default:
446                 return EPFNOSUPPORT;
447         }
448         *mp = m;
449         *tp = type;
450         return 0;
451 }
452
453 /*
454  * Create clone from the given interface
455  */
456 static int
457 ef_clone(struct ef_link *efl, int ft)
458 {
459         struct efnet *efp;
460         struct ifnet *eifp;
461         struct ifnet *ifp = efl->el_ifp;
462
463         efp = (struct efnet*)kmalloc(sizeof(struct efnet), M_IFADDR,
464             M_WAITOK | M_ZERO);
465         efp->ef_ifp = ifp;
466         efp->ef_frametype = ft;
467         eifp = &efp->ef_ac.ac_if;
468         ksnprintf(eifp->if_xname, IFNAMSIZ,
469             "%sf%d", ifp->if_xname, efp->ef_frametype);
470         eifp->if_dname = "ef";
471         eifp->if_dunit = IF_DUNIT_NONE;
472         eifp->if_softc = efp;
473         if (ifp->if_ioctl)
474                 eifp->if_ioctl = ef_ioctl;
475         efl->el_units[ft] = efp;
476         return 0;
477 }
478
479 static int
480 ef_load(void)
481 {
482         struct ifnet *ifp;
483         struct efnet *efp;
484         struct ef_link *efl = NULL;
485         int error = 0, d;
486
487         TAILQ_FOREACH(ifp, &ifnet, if_link) {
488                 if (ifp->if_type != IFT_ETHER) continue;
489                 EFDEBUG("Found interface %s\n", ifp->if_xname);
490                 efl = (struct ef_link*)kmalloc(sizeof(struct ef_link), 
491                     M_IFADDR, M_WAITOK | M_ZERO);
492
493                 efl->el_ifp = ifp;
494 #ifdef ETHER_II
495                 error = ef_clone(efl, ETHER_FT_EII);
496                 if (error) break;
497 #endif
498 #ifdef ETHER_8023
499                 error = ef_clone(efl, ETHER_FT_8023);
500                 if (error) break;
501 #endif
502 #ifdef ETHER_8022
503                 error = ef_clone(efl, ETHER_FT_8022);
504                 if (error) break;
505 #endif
506 #ifdef ETHER_SNAP
507                 error = ef_clone(efl, ETHER_FT_SNAP);
508                 if (error) break;
509 #endif
510                 efcount++;
511                 SLIST_INSERT_HEAD(&efdev, efl, el_next);
512         }
513         if (error) {
514                 if (efl)
515                         SLIST_INSERT_HEAD(&efdev, efl, el_next);
516                 SLIST_FOREACH(efl, &efdev, el_next) {
517                         for (d = 0; d < EF_NFT; d++)
518                                 if (efl->el_units[d])
519                                         kfree(efl->el_units[d], M_IFADDR);
520                         kfree(efl, M_IFADDR);
521                 }
522                 return error;
523         }
524         SLIST_FOREACH(efl, &efdev, el_next) {
525                 for (d = 0; d < EF_NFT; d++) {
526                         efp = efl->el_units[d];
527                         if (efp)
528                                 ef_attach(efp);
529                 }
530         }
531         ef_inputp = ef_input;
532         ef_outputp = ef_output;
533         EFDEBUG("Loaded\n");
534         return 0;
535 }
536
537 static int
538 ef_unload(void)
539 {
540         struct efnet *efp;
541         struct ef_link *efl;
542         int d;
543
544         ef_inputp = NULL;
545         ef_outputp = NULL;
546         SLIST_FOREACH(efl, &efdev, el_next) {
547                 for (d = 0; d < EF_NFT; d++) {
548                         efp = efl->el_units[d];
549                         if (efp) {
550                                 ef_detach(efp);
551                         }
552                 }
553         }
554         EFDEBUG("Unloaded\n");
555         return 0;
556 }
557
558 static int 
559 if_ef_modevent(module_t mod, int type, void *data)
560 {
561         switch ((modeventtype_t)type) {
562             case MOD_LOAD:
563                 return ef_load();
564             case MOD_UNLOAD:
565                 return ef_unload();
566             default:
567                 break;
568         }
569         return 0;
570 }
571
572 static moduledata_t if_ef_mod = {
573         "if_ef", if_ef_modevent, NULL
574 };
575
576 DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE);