1 /* $NetBSD: if_virt.c,v 1.10 2009/05/27 23:41:20 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: if_virt.c,v 1.10 2009/05/27 23:41:20 pooka Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/condvar.h> 33 #include <sys/fcntl.h> 34 #include <sys/kmem.h> 35 #include <sys/kthread.h> 36 #include <sys/mutex.h> 37 #include <sys/sockio.h> 38 #include <sys/socketvar.h> 39 40 #include <net/if.h> 41 #include <net/if_ether.h> 42 #include <net/if_tap.h> 43 44 #include <netinet/in.h> 45 #include <netinet/in_var.h> 46 47 #include <rump/rump.h> 48 #include <rump/rumpuser.h> 49 50 #include "rump_private.h" 51 #include "rump_net_private.h" 52 53 /* 54 * Virtual interface for userspace purposes. Uses tap(4) to 55 * interface with the kernel and just simply shovels data 56 * to/from /dev/tap. 57 */ 58 59 #define VIRTIF_BASE "virt" 60 61 static int virtif_init(struct ifnet *); 62 static int virtif_ioctl(struct ifnet *, u_long, void *); 63 static void virtif_start(struct ifnet *); 64 static void virtif_stop(struct ifnet *, int); 65 66 struct virtif_sc { 67 struct ethercom sc_ec; 68 int sc_tapfd; 69 kmutex_t sc_sendmtx; 70 kcondvar_t sc_sendcv; 71 }; 72 73 static void virtif_worker(void *); 74 static void virtif_sender(void *); 75 76 #if 0 77 /* 78 * Create a socket and call ifioctl() to configure the interface. 79 * This trickles down to virtif_ioctl(). 80 */ 81 static int 82 configaddr(struct ifnet *ifp, struct ifaliasreq *ia) 83 { 84 struct socket *so; 85 int error; 86 87 strcpy(ia->ifra_name, ifp->if_xname); 88 error = socreate(ia->ifra_addr.sa_family, &so, SOCK_DGRAM, 89 0, curlwp, NULL); 90 if (error) 91 return error; 92 error = ifioctl(so, SIOCAIFADDR, ia, curlwp); 93 soclose(so); 94 95 return error; 96 } 97 #endif 98 99 int 100 rump_virtif_create(int num) 101 { 102 struct virtif_sc *sc; 103 struct ifnet *ifp; 104 uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0x0a, 0x00, 0x0b, 0x0e, 0x01 }; 105 char tapdev[16]; 106 int fd, error; 107 108 snprintf(tapdev, sizeof(tapdev), "/dev/tap%d", num); 109 fd = rumpuser_open(tapdev, O_RDWR, &error); 110 if (fd == -1) { 111 printf("virtif_create: can't open /dev/tap %d\n", error); 112 return error; 113 } 114 KASSERT(num < 0x100); 115 enaddr[2] = arc4random() & 0xff; 116 enaddr[5] = num; 117 118 sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); 119 sc->sc_tapfd = fd; 120 121 ifp = &sc->sc_ec.ec_if; 122 sprintf(ifp->if_xname, "%s%d", VIRTIF_BASE, num); 123 ifp->if_softc = sc; 124 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 125 ifp->if_init = virtif_init; 126 ifp->if_ioctl = virtif_ioctl; 127 ifp->if_start = virtif_start; 128 ifp->if_stop = virtif_stop; 129 130 mutex_init(&sc->sc_sendmtx, MUTEX_DEFAULT, IPL_NONE); 131 cv_init(&sc->sc_sendcv, "virtsnd"); 132 133 if_attach(ifp); 134 ether_ifattach(ifp, enaddr); 135 136 return 0; 137 } 138 139 static int 140 virtif_init(struct ifnet *ifp) 141 { 142 int rv; 143 144 if (rump_threads) { 145 rv = kthread_create(PRI_NONE, 0, NULL, virtif_worker, ifp, 146 NULL, "virtifi"); 147 /* XXX: should do proper cleanup */ 148 if (rv) { 149 panic("if_virt: can't create worker"); 150 } 151 rv = kthread_create(PRI_NONE, 0, NULL, virtif_sender, ifp, 152 NULL, "virtifs"); 153 if (rv) { 154 panic("if_virt: can't create sender"); 155 } 156 } else { 157 printf("WARNING: threads not enabled, receive NOT working\n"); 158 } 159 ifp->if_flags |= IFF_RUNNING; 160 161 return 0; 162 } 163 164 static int 165 virtif_ioctl(struct ifnet *ifp, u_long cmd, void *data) 166 { 167 int s, rv; 168 169 s = splnet(); 170 rv = ether_ioctl(ifp, cmd, data); 171 if (rv == ENETRESET) 172 rv = 0; 173 splx(s); 174 175 return rv; 176 } 177 178 /* just send everything in-context */ 179 static void 180 virtif_start(struct ifnet *ifp) 181 { 182 struct virtif_sc *sc = ifp->if_softc; 183 184 mutex_enter(&sc->sc_sendmtx); 185 cv_signal(&sc->sc_sendcv); 186 mutex_exit(&sc->sc_sendmtx); 187 } 188 189 static void 190 virtif_stop(struct ifnet *ifp, int disable) 191 { 192 193 panic("%s: unimpl", __func__); 194 } 195 196 static void 197 virtif_worker(void *arg) 198 { 199 struct ifnet *ifp = arg; 200 struct virtif_sc *sc = ifp->if_softc; 201 struct mbuf *m; 202 size_t plen = ETHER_MAX_LEN_JUMBO+1; 203 ssize_t n; 204 int error; 205 206 for (;;) { 207 m = m_gethdr(M_WAIT, MT_DATA); 208 MEXTMALLOC(m, plen, M_WAIT); 209 210 n = rumpuser_read(sc->sc_tapfd, mtod(m, void *), plen, &error); 211 KASSERT(n < ETHER_MAX_LEN_JUMBO); 212 if (n <= 0) { 213 m_freem(m); 214 break; 215 } 216 m->m_len = m->m_pkthdr.len = n; 217 m->m_pkthdr.rcvif = ifp; 218 ether_input(ifp, m); 219 } 220 221 panic("virtif_workin is a lazy boy %d\n", error); 222 } 223 224 static void 225 virtif_sender(void *arg) 226 { 227 struct ifnet *ifp = arg; 228 struct virtif_sc *sc = ifp->if_softc; 229 struct mbuf *m, *m0; 230 struct rumpuser_iovec io[16]; 231 int i, error; 232 233 mutex_enter(&sc->sc_sendmtx); 234 for (;;) { 235 IF_DEQUEUE(&ifp->if_snd, m0); 236 if (!m0) { 237 cv_wait(&sc->sc_sendcv, &sc->sc_sendmtx); 238 continue; 239 } 240 mutex_exit(&sc->sc_sendmtx); 241 242 m = m0; 243 for (i = 0; i < 16 && m; i++) { 244 io[i].iov_base = mtod(m, void *); 245 io[i].iov_len = m->m_len; 246 m = m->m_next; 247 } 248 if (i == 16) 249 panic("lazy bum"); 250 rumpuser_writev(sc->sc_tapfd, io, i, &error); 251 m_freem(m0); 252 mutex_enter(&sc->sc_sendmtx); 253 } 254 255 mutex_exit(softnet_lock); 256 } 257 258 /* 259 * dummyif is a nada-interface. 260 * As it requires nothing external, it can be used for testing 261 * interface configuration. 262 */ 263 static int dummyif_init(struct ifnet *); 264 static void dummyif_start(struct ifnet *); 265 266 void 267 rump_dummyif_create() 268 { 269 struct ifnet *ifp; 270 struct ethercom *ec; 271 uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0x0a, 0x00, 0x0b, 0x0e, 0x01 }; 272 273 enaddr[2] = arc4random() & 0xff; 274 enaddr[5] = arc4random() & 0xff; 275 276 ec = kmem_zalloc(sizeof(*ec), KM_SLEEP); 277 278 ifp = &ec->ec_if; 279 strlcpy(ifp->if_xname, "dummy0", sizeof(ifp->if_xname)); 280 ifp->if_softc = ifp; 281 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 282 ifp->if_init = dummyif_init; 283 ifp->if_ioctl = virtif_ioctl; 284 ifp->if_start = dummyif_start; 285 286 if_attach(ifp); 287 ether_ifattach(ifp, enaddr); 288 } 289 290 static int 291 dummyif_init(struct ifnet *ifp) 292 { 293 294 ifp->if_flags |= IFF_RUNNING; 295 return 0; 296 } 297 298 static void 299 dummyif_start(struct ifnet *ifp) 300 { 301 302 } 303