vkernel - more crit section work to avoid pthread reentrancy.
[dragonfly.git] / sys / dev / virtual / net / if_vke.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sys/dev/virtual/net/if_vke.c,v 1.10 2008/05/27 23:44:46 dillon Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/proc.h>
42 #include <sys/serialize.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/sysctl.h>
46
47 #include <machine/md_var.h>
48 #include <machine/cothread.h>
49
50 #include <net/ethernet.h>
51 #include <net/if.h>
52 #include <net/bpf.h>
53 #include <net/if_arp.h>
54 #include <net/ifq_var.h>
55
56 #include <netinet/in_var.h>
57
58 #include <sys/stat.h>
59 #include <net/tap/if_tap.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66
67 #define VKE_DEVNAME             "vke"
68
69 #define VKE_CHUNK       8 /* number of mbufs to queue before interrupting */
70
71 #define NETFIFOSIZE     256
72 #define NETFIFOMASK     (NETFIFOSIZE -1)
73 #define NETFIFOINDEX(u) ((u) & NETFIFOMASK)
74
75 #define VKE_COTD_RUN    0
76 #define VKE_COTD_EXIT   1
77 #define VKE_COTD_DEAD   2
78
79 struct vke_fifo {
80         struct mbuf     *array[NETFIFOSIZE];
81         int             rindex;
82         int             windex;
83 };
84 typedef struct vke_fifo *fifo_t;
85
86 struct vke_softc {
87         struct arpcom           arpcom;
88         int                     sc_fd;
89         int                     sc_unit;
90
91         cothread_t              cotd_tx;
92         cothread_t              cotd_rx;
93
94         int                     cotd_tx_exit;
95         int                     cotd_rx_exit;
96
97         void                    *sc_txbuf;
98         int                     sc_txbuf_len;
99
100         fifo_t                  sc_txfifo;
101         fifo_t                  sc_txfifo_done;
102         fifo_t                  sc_rxfifo;
103
104         struct sysctl_ctx_list  sc_sysctl_ctx;
105         struct sysctl_oid       *sc_sysctl_tree;
106
107         int                     sc_tap_unit;    /* unit of backend tap(4) */
108         in_addr_t               sc_addr;        /* address */
109         in_addr_t               sc_mask;        /* netmask */
110 };
111
112 static void     vke_start(struct ifnet *);
113 static void     vke_init(void *);
114 static int      vke_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
115
116 static int      vke_attach(const struct vknetif_info *, int);
117 static int      vke_stop(struct vke_softc *);
118 static int      vke_init_addr(struct ifnet *, in_addr_t, in_addr_t);
119 static void     vke_tx_intr(cothread_t cotd);
120 static void     vke_tx_thread(cothread_t cotd);
121 static void     vke_rx_intr(cothread_t cotd);
122 static void     vke_rx_thread(cothread_t cotd);
123
124 static int vke_txfifo_enqueue(struct vke_softc *sc, struct mbuf *m);
125 static struct mbuf *vke_txfifo_dequeue(struct vke_softc *sc);
126
127 static int vke_txfifo_done_enqueue(struct vke_softc *sc, struct mbuf *m);
128 static struct mbuf * vke_txfifo_done_dequeue(struct vke_softc *sc, struct mbuf *nm);
129
130 static struct mbuf *vke_rxfifo_dequeue(struct vke_softc *sc, struct mbuf *nm);
131 static struct mbuf *vke_rxfifo_sniff(struct vke_softc *sc);
132
133 static void
134 vke_sysinit(void *arg __unused)
135 {
136         int i, unit;
137
138         KASSERT(NetifNum <= VKNETIF_MAX, ("too many netifs: %d\n", NetifNum));
139
140         unit = 0;
141         for (i = 0; i < NetifNum; ++i) {
142                 if (vke_attach(&NetifInfo[i], unit) == 0)
143                         ++unit;
144         }
145 }
146 SYSINIT(vke, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, vke_sysinit, NULL);
147
148 /*
149  * vke_txfifo_done_enqueue() - Add an mbuf to the transmit done fifo.  Since
150  * the cothread cannot free transmit mbufs after processing we put them on
151  * the done fifo so the kernel can free them.
152  */
153 static int
154 vke_txfifo_done_enqueue(struct vke_softc *sc, struct mbuf *m)
155 {
156         fifo_t fifo = sc->sc_txfifo_done;
157
158         if (NETFIFOINDEX(fifo->windex + 1) == NETFIFOINDEX(fifo->rindex))
159                 return (-1);
160
161         fifo->array[NETFIFOINDEX(fifo->windex)] = m;
162         cpu_sfence();
163         ++fifo->windex;
164         return (0);
165 }
166
167 /*
168  * vke_txfifo_done_dequeue() - Remove an mbuf from the transmit done fifo.
169  */
170 static struct mbuf *
171 vke_txfifo_done_dequeue(struct vke_softc *sc, struct mbuf *nm)
172 {
173         fifo_t fifo = sc->sc_txfifo_done;
174         struct mbuf *m;
175
176         if (NETFIFOINDEX(fifo->rindex) == NETFIFOINDEX(fifo->windex))
177                 return (NULL);
178
179         m = fifo->array[NETFIFOINDEX(fifo->rindex)];
180         fifo->array[NETFIFOINDEX(fifo->rindex)] = nm;
181         cpu_lfence();
182         ++fifo->rindex;
183         return (m);
184 }
185
186 /*
187  * vke_txfifo_enqueue() - Add an mbuf to the transmit fifo.  Wake up the
188  * cothread via cothread_signal().
189  */
190 static int
191 vke_txfifo_enqueue(struct vke_softc *sc, struct mbuf *m)
192 {
193         fifo_t fifo = sc->sc_txfifo;
194         cothread_t cotd = sc->cotd_tx;
195
196         if (NETFIFOINDEX(fifo->windex + 1) == NETFIFOINDEX(fifo->rindex))
197                 return (-1);
198
199         fifo->array[NETFIFOINDEX(fifo->windex)] = m;
200         cpu_sfence();
201         cothread_signal(cotd);
202         ++fifo->windex;
203
204         return (0);
205 }
206
207 /*
208  * vke_txfifo_dequeue() - Return next mbuf on the transmit fifo if one
209  * exists.
210  */
211 static struct mbuf *
212 vke_txfifo_dequeue(struct vke_softc *sc)
213 {
214         fifo_t fifo = sc->sc_txfifo;
215         struct mbuf *m;
216
217         if (NETFIFOINDEX(fifo->rindex) == NETFIFOINDEX(fifo->windex))
218                 return (NULL);
219
220         m = fifo->array[NETFIFOINDEX(fifo->rindex)];
221         fifo->array[NETFIFOINDEX(fifo->rindex)] = NULL;
222
223         cpu_lfence();
224         ++fifo->rindex;
225         return (m);
226 }
227
228 /*
229  * vke_rxfifo_dequeue() - Return next mbuf on the receice fifo if one
230  * exists replacing it with newm which should point to a newly allocated
231  * mbuf.
232  */
233 static struct mbuf *
234 vke_rxfifo_dequeue(struct vke_softc *sc, struct mbuf *newm)
235 {
236         fifo_t fifo = sc->sc_rxfifo;
237         struct mbuf *m;
238
239         if (NETFIFOINDEX(fifo->rindex) == NETFIFOINDEX(fifo->windex))
240                 return (NULL);
241
242         m = fifo->array[NETFIFOINDEX(fifo->rindex)];
243         fifo->array[NETFIFOINDEX(fifo->rindex)] = newm;
244         cpu_lfence();
245         ++fifo->rindex;
246         return (m);
247 }
248
249 /*
250  * Return the next mbuf if available but do NOT remove it from the FIFO.
251  */
252 static struct mbuf *
253 vke_rxfifo_sniff(struct vke_softc *sc)
254 {
255         fifo_t fifo = sc->sc_rxfifo;
256         struct mbuf *m;
257
258         if (NETFIFOINDEX(fifo->rindex) == NETFIFOINDEX(fifo->windex))
259                 return (NULL);
260
261         m = fifo->array[NETFIFOINDEX(fifo->rindex)];
262         cpu_lfence();
263         return (m);
264 }
265
266 static void
267 vke_init(void *xsc)
268 {
269         struct vke_softc *sc = xsc;
270         struct ifnet *ifp = &sc->arpcom.ac_if;
271         int i;
272
273         ASSERT_SERIALIZED(ifp->if_serializer);
274
275
276         vke_stop(sc);
277
278         ifp->if_flags |= IFF_RUNNING;
279         ifp->if_flags &= ~IFF_OACTIVE;
280
281         if (sc->sc_addr != 0) {
282                 in_addr_t addr, mask;
283
284                 addr = sc->sc_addr;
285                 mask = sc->sc_mask;
286
287                 /*
288                  * Make sure vkernel assigned
289                  * address will not be added
290                  * again.
291                  */
292                 sc->sc_addr = 0;
293                 sc->sc_mask = 0;
294
295                 vke_init_addr(ifp, addr, mask);
296         }
297
298         sc->sc_txfifo = kmalloc(sizeof(*sc->sc_txfifo), M_DEVBUF, M_WAITOK);
299         sc->sc_txfifo_done = kmalloc(sizeof(*sc->sc_txfifo_done), M_DEVBUF, M_WAITOK);
300
301         sc->sc_rxfifo = kmalloc(sizeof(*sc->sc_rxfifo), M_DEVBUF, M_WAITOK);
302         for (i = 0; i < NETFIFOSIZE; i++) {
303                 sc->sc_rxfifo->array[i] = m_getcl(MB_WAIT, MT_DATA, M_PKTHDR);
304                 sc->sc_txfifo->array[i] = NULL;
305                 sc->sc_txfifo_done->array[i] = NULL;
306         }
307
308         sc->cotd_tx_exit = sc->cotd_rx_exit = VKE_COTD_RUN;
309         sc->cotd_tx = cothread_create(vke_tx_thread, vke_tx_intr, sc, "vke_tx");
310         sc->cotd_rx = cothread_create(vke_rx_thread, vke_rx_intr, sc, "vke_rx");
311 }
312
313 static void
314 vke_start(struct ifnet *ifp)
315 {
316         struct vke_softc *sc = ifp->if_softc;
317         struct mbuf *m;
318         cothread_t cotd = sc->cotd_tx;
319         int count;
320
321         ASSERT_SERIALIZED(ifp->if_serializer);
322
323         if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
324                 return;
325
326         cothread_lock(cotd, 0);
327         count = 0;
328
329         while ((m = ifq_dequeue(&ifp->if_snd, NULL)) != NULL) {
330                 if (vke_txfifo_enqueue(sc, m) != -1) {
331                         if (count++ == VKE_CHUNK) {
332                                 cothread_signal(cotd);
333                                 count = 0;
334                         }
335                 } else {
336                         m_freem(m);
337                 }
338         }
339
340         if (count) {
341                 cothread_signal(cotd);
342         }
343
344         cothread_unlock(cotd, 0);
345 }
346
347 static int
348 vke_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
349 {
350         struct vke_softc *sc = ifp->if_softc;
351         int error = 0;
352
353         ASSERT_SERIALIZED(ifp->if_serializer);
354
355         switch (cmd) {
356         case SIOCSIFFLAGS:
357                 if (ifp->if_flags & IFF_UP) {
358                         if ((ifp->if_flags & IFF_RUNNING) == 0)
359                                 vke_init(sc);
360                 } else {
361                         if (ifp->if_flags & IFF_RUNNING)
362                                 vke_stop(sc);
363                 }
364                 break;
365         case SIOCGIFMEDIA:
366         case SIOCSIFMEDIA:
367                 error = EOPNOTSUPP;
368                 /* TODO */
369                 break;
370         case SIOCGIFSTATUS: {
371                 struct ifstat *ifs = (struct ifstat *)data;
372                 int len;
373
374                 len = strlen(ifs->ascii);
375                 if (len < sizeof(ifs->ascii)) {
376                         ksnprintf(ifs->ascii + len, sizeof(ifs->ascii) - len,
377                                   "\tBacked by tap%d\n", sc->sc_tap_unit);
378                 }
379                 break;
380         }
381         case SIOCSIFADDR:
382                 if (((struct ifaddr *)data)->ifa_addr->sa_family == AF_INET) {
383                         /*
384                          * If we are explicitly requested to change address,
385                          * we should invalidate address/netmask passed in
386                          * from vkernel command line.
387                          */
388                         sc->sc_addr = 0;
389                         sc->sc_mask = 0;
390                 }
391                 /* FALL THROUGH */
392         default:
393                 error = ether_ioctl(ifp, cmd, data);
394                 break;
395         }
396         return error;
397 }
398
399 static int
400 vke_stop(struct vke_softc *sc)
401 {
402         struct ifnet *ifp = &sc->arpcom.ac_if;
403         int i;
404
405         ASSERT_SERIALIZED(ifp->if_serializer);
406
407         ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
408
409         if (sc) {
410                 if (sc->cotd_tx) {
411                         cothread_lock(sc->cotd_tx, 0);
412                         if (sc->cotd_tx_exit == VKE_COTD_RUN)
413                                 sc->cotd_tx_exit = VKE_COTD_EXIT;
414                         cothread_signal(sc->cotd_tx);
415                         cothread_unlock(sc->cotd_tx, 0);
416                         cothread_delete(&sc->cotd_tx);
417                 }
418                 if (sc->cotd_rx) {
419                         cothread_lock(sc->cotd_rx, 0);
420                         if (sc->cotd_rx_exit == VKE_COTD_RUN)
421                                 sc->cotd_rx_exit = VKE_COTD_EXIT;
422                         cothread_signal(sc->cotd_rx);
423                         cothread_unlock(sc->cotd_rx, 0);
424                         cothread_delete(&sc->cotd_rx);
425                 }
426
427                 for (i = 0; i < NETFIFOSIZE; i++) {
428                         if (sc->sc_rxfifo && sc->sc_rxfifo->array[i]) {
429                                 m_freem(sc->sc_rxfifo->array[i]);
430                                 sc->sc_rxfifo->array[i] = NULL;
431                         }
432                         if (sc->sc_txfifo && sc->sc_txfifo->array[i]) {
433                                 m_freem(sc->sc_txfifo->array[i]);
434                                 sc->sc_txfifo->array[i] = NULL;
435                         }
436                         if (sc->sc_txfifo_done && sc->sc_txfifo_done->array[i]) {
437                                 m_freem(sc->sc_txfifo_done->array[i]);
438                                 sc->sc_txfifo_done->array[i] = NULL;
439                         }
440                 }
441
442                 if (sc->sc_txfifo) {
443                         kfree(sc->sc_txfifo, M_DEVBUF);
444                         sc->sc_txfifo = NULL;
445                 }
446
447                 if (sc->sc_txfifo_done) {
448                         kfree(sc->sc_txfifo_done, M_DEVBUF);
449                         sc->sc_txfifo_done = NULL;
450                 }
451
452                 if (sc->sc_rxfifo) {
453                         kfree(sc->sc_rxfifo, M_DEVBUF);
454                         sc->sc_rxfifo = NULL;
455                 }
456         }
457
458
459         return 0;
460 }
461
462 /*
463  * vke_rx_intr() is the interrupt function for the receive cothread.
464  */
465 static void
466 vke_rx_intr(cothread_t cotd)
467 {
468         struct mbuf *m;
469         struct mbuf *nm;
470         struct vke_softc *sc = cotd->arg;
471         struct ifnet *ifp = &sc->arpcom.ac_if;
472         static int count = 0;
473
474         ifnet_serialize_all(ifp);
475         cothread_lock(cotd, 0);
476
477         if (sc->cotd_rx_exit != VKE_COTD_RUN) {
478                 cothread_unlock(cotd, 0);
479                 ifnet_deserialize_all(ifp);
480                 return;
481         }
482
483         while ((m = vke_rxfifo_sniff(sc)) != NULL) {
484                 nm = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
485                 if (nm) {
486                         vke_rxfifo_dequeue(sc, nm);
487                         ifp->if_input(ifp, m);
488                         if (count++ == VKE_CHUNK) {
489                                 cothread_signal(cotd);
490                                 count = 0;
491                         }
492                 } else {
493                         vke_rxfifo_dequeue(sc, m);
494                 }
495         }
496
497         if (count)
498                 cothread_signal(cotd);
499
500         cothread_unlock(cotd, 0);
501         ifnet_deserialize_all(ifp);
502 }
503
504 /*
505  * vke_tx_intr() is the interrupt function for the transmit cothread.
506  * Calls vke_start() to handle processing transmit mbufs.
507  */
508 static void
509 vke_tx_intr(cothread_t cotd)
510 {
511         struct vke_softc *sc = cotd->arg;
512         struct ifnet *ifp = &sc->arpcom.ac_if;
513         struct mbuf *m;
514
515         ifnet_serialize_all(ifp);
516         cothread_lock(cotd, 0);
517
518         if (sc->cotd_tx_exit != VKE_COTD_RUN) {
519                 cothread_unlock(cotd, 0);
520                 ifnet_deserialize_all(ifp);
521                 return;
522         }
523
524         if ((ifp->if_flags & IFF_RUNNING) == 0)
525                 ifp->if_start(ifp);
526
527         /* Free TX mbufs that have been processed */
528         while ((m = vke_txfifo_done_dequeue(sc, NULL)) != NULL) {
529                 m_freem(m);
530         }
531
532         cothread_unlock(cotd, 0);
533
534         ifnet_deserialize_all(ifp);
535 }
536
537 /*
538  * vke_rx_thread() is the body of the receive cothread.
539  */
540 static void
541 vke_rx_thread(cothread_t cotd)
542 {
543         struct mbuf *m;
544         struct vke_softc *sc = cotd->arg;
545         struct ifnet *ifp = &sc->arpcom.ac_if;
546         int count;
547         fifo_t fifo = sc->sc_rxfifo;
548         fd_set fdset;
549         struct timeval tv;
550
551         /* Select timeout cannot be infinite since we need to check for
552          * the exit flag sc->cotd_rx_exit.
553          */
554         tv.tv_sec = 0;
555         tv.tv_usec = 500000;
556
557         FD_ZERO(&fdset);
558
559         cothread_lock(cotd, 1);
560
561         for (;;) {
562                 int n;
563                 count = 0;
564                 while (sc->cotd_rx_exit == VKE_COTD_RUN) {
565                         /* Wait for the RX FIFO to drain */
566                         while (NETFIFOINDEX(fifo->windex + 1) ==
567                                         NETFIFOINDEX(fifo->rindex)) {
568                                         usleep(20000);
569                         }
570
571                         if ((m = fifo->array[NETFIFOINDEX(fifo->windex)]) !=
572                                         NULL) {
573                                 cothread_unlock(cotd, 1);
574                                 n = read(sc->sc_fd, mtod(m, void *), MCLBYTES);
575                                 cothread_lock(cotd, 1);
576                                 if (n <= 0)
577                                         break;
578                                 ifp->if_ipackets++;
579                                 m->m_pkthdr.rcvif = ifp;
580                                 m->m_pkthdr.len = m->m_len = n;
581                                 ++fifo->windex;
582                                 cpu_sfence();
583                                 if (count++ == VKE_CHUNK) {
584                                         cothread_intr(cotd);
585                                         count = 0;
586                                 }
587                         }
588                 }
589
590                 if (count) {
591                         cothread_intr(cotd);
592                 }
593
594                 if (sc->cotd_rx_exit != VKE_COTD_RUN)
595                         break;
596
597                 cothread_unlock(cotd, 1);
598
599                 /* Set up data for select() call */
600                 FD_SET(sc->sc_fd, &fdset);
601
602                 if (select(sc->sc_fd + 1, &fdset, NULL, NULL, &tv) == -1)
603                         kprintf(VKE_DEVNAME "%d: select failed for TAP device\n", sc->sc_unit);
604
605                 cothread_lock(cotd, 1);
606         }
607
608         sc->cotd_rx_exit = VKE_COTD_DEAD;
609         cothread_unlock(cotd, 1);
610 }
611
612 /*
613  * vke_tx_thread() is the body of the transmit cothread.
614  */
615 static void
616 vke_tx_thread(cothread_t cotd)
617 {
618         struct mbuf *m;
619         struct vke_softc *sc = cotd->arg;
620         struct ifnet *ifp = &sc->arpcom.ac_if;
621         int count = 0;
622
623         cothread_lock(cotd, 1);
624
625         while (sc->cotd_tx_exit == VKE_COTD_RUN) {
626                 /* Write outgoing packets to the TAP interface */
627                 while ((m = vke_txfifo_dequeue(sc)) != NULL) {
628                         if (m->m_pkthdr.len <= MCLBYTES) {
629                                 m_copydata(m, 0, m->m_pkthdr.len, sc->sc_txbuf);
630                                 sc->sc_txbuf_len = m->m_pkthdr.len;
631                                 cothread_unlock(cotd, 1);
632
633                                 if (write(sc->sc_fd, sc->sc_txbuf, sc->sc_txbuf_len) < 0) {
634                                         cothread_lock(cotd, 1);
635                                         ifp->if_oerrors++;
636                                 } else {
637                                         cothread_lock(cotd, 1);
638                                         vke_txfifo_done_enqueue(sc, m);
639                                         ifp->if_opackets++;
640                                         if (count++ == VKE_CHUNK) {
641                                                 cothread_intr(cotd);
642                                                 count = 0;
643                                         }
644                                 }
645                         }
646                 }
647
648                 if (count) {
649                         cothread_intr(cotd);
650                 }
651
652                 cothread_wait(cotd);    /* interlocks cothread lock */
653         }
654         /* NOT REACHED */
655         sc->cotd_tx_exit = VKE_COTD_DEAD;
656         cothread_unlock(cotd, 1);
657 }
658
659 static int
660 vke_attach(const struct vknetif_info *info, int unit)
661 {
662         struct vke_softc *sc;
663         struct ifnet *ifp;
664         struct tapinfo tapinfo;
665         uint8_t enaddr[ETHER_ADDR_LEN];
666         int fd;
667
668         KKASSERT(info->tap_fd >= 0);
669         fd = info->tap_fd;
670
671         /*
672          * This is only a TAP device if tap_unit is non-zero.  If
673          * connecting to a virtual socket we generate a unique MAC.
674          */
675         if (info->tap_unit >= 0) {
676                 if (ioctl(fd, TAPGIFINFO, &tapinfo) < 0) {
677                         kprintf(VKE_DEVNAME "%d: ioctl(TAPGIFINFO) "
678                                 "failed: %s\n", unit, strerror(errno));
679                         return ENXIO;
680                 }
681
682                 if (ioctl(fd, SIOCGIFADDR, enaddr) < 0) {
683                         kprintf(VKE_DEVNAME "%d: ioctl(SIOCGIFADDR) "
684                                 "failed: %s\n", unit, strerror(errno));
685                         return ENXIO;
686                 }
687         } else {
688                 int fd = open("/dev/urandom", O_RDONLY);
689                 if (fd >= 0) {
690                         read(fd, enaddr + 2, 4);
691                         close(fd);
692                 }
693                 enaddr[4] = (int)getpid() >> 8;
694                 enaddr[5] = (int)getpid() & 255;
695
696         }
697         enaddr[1] += 1;
698
699         sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
700
701         sc->sc_txbuf = kmalloc(MCLBYTES, M_DEVBUF, M_WAITOK);
702         sc->sc_fd = fd;
703         sc->sc_unit = unit;
704         sc->sc_tap_unit = info->tap_unit;
705         sc->sc_addr = info->netif_addr;
706         sc->sc_mask = info->netif_mask;
707
708         ifp = &sc->arpcom.ac_if;
709         if_initname(ifp, VKE_DEVNAME, sc->sc_unit);
710
711         /* NB: after if_initname() */
712         sysctl_ctx_init(&sc->sc_sysctl_ctx);
713         sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx,
714                                              SYSCTL_STATIC_CHILDREN(_hw),
715                                              OID_AUTO, ifp->if_xname,
716                                              CTLFLAG_RD, 0, "");
717         if (sc->sc_sysctl_tree == NULL) {
718                 kprintf(VKE_DEVNAME "%d: can't add sysctl node\n", unit);
719         } else {
720                 SYSCTL_ADD_INT(&sc->sc_sysctl_ctx,
721                                SYSCTL_CHILDREN(sc->sc_sysctl_tree),
722                                OID_AUTO, "tap_unit",
723                                CTLFLAG_RD, &sc->sc_tap_unit, 0,
724                                "Backend tap(4) unit");
725         }
726
727         ifp->if_softc = sc;
728         ifp->if_ioctl = vke_ioctl;
729         ifp->if_start = vke_start;
730         ifp->if_init = vke_init;
731         ifp->if_mtu = tapinfo.mtu;
732         ifp->if_baudrate = tapinfo.baudrate;
733         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
734         ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
735         ifq_set_ready(&ifp->if_snd);
736
737         /* TODO: if_media */
738
739         ether_ifattach(ifp, enaddr, NULL);
740
741         if (bootverbose && sc->sc_addr != 0) {
742                 if_printf(ifp, "pre-configured "
743                           "address 0x%08x, netmask 0x%08x\n",
744                           ntohl(sc->sc_addr), ntohl(sc->sc_mask));
745         }
746
747         return 0;
748 }
749
750 static int
751 vke_init_addr(struct ifnet *ifp, in_addr_t addr, in_addr_t mask)
752 {
753         struct ifaliasreq ifra;
754         struct sockaddr_in *sin;
755         int ret;
756
757         ASSERT_SERIALIZED(ifp->if_serializer);
758
759         if (bootverbose) {
760                 if_printf(ifp, "add pre-configured "
761                           "address 0x%08x, netmask 0x%08x\n",
762                           ntohl(addr), ntohl(mask));
763         }
764
765         bzero(&ifra, sizeof(ifra));
766
767         /* NB: no need to set ifaliasreq.ifra_name */
768
769         sin = (struct sockaddr_in *)&ifra.ifra_addr;
770         sin->sin_family = AF_INET;
771         sin->sin_len = sizeof(*sin);
772         sin->sin_addr.s_addr = addr;
773
774         if (mask != 0) {
775                 sin = (struct sockaddr_in *)&ifra.ifra_mask;
776                 sin->sin_len = sizeof(*sin);
777                 sin->sin_addr.s_addr = mask;
778         }
779
780         /*
781          * Temporarily release serializer, in_control() will hold
782          * it again before calling ifnet.if_ioctl().
783          */
784         ifnet_deserialize_all(ifp);
785         ret = in_control(NULL, SIOCAIFADDR, (caddr_t)&ifra, ifp, NULL);
786         ifnet_serialize_all(ifp);
787
788         return ret;
789 }