1 /* $NetBSD: sockin.c,v 1.15 2009/03/18 10:22:45 cegger Exp $ */ 2 3 /* 4 * Copyright (c) 2008, 2009 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: sockin.c,v 1.15 2009/03/18 10:22:45 cegger Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/condvar.h> 33 #include <sys/domain.h> 34 #include <sys/kmem.h> 35 #include <sys/kthread.h> 36 #include <sys/mbuf.h> 37 #include <sys/mutex.h> 38 #include <sys/poll.h> 39 #include <sys/protosw.h> 40 #include <sys/queue.h> 41 #include <sys/socket.h> 42 #include <sys/socketvar.h> 43 #include <sys/time.h> 44 45 #include <net/radix.h> 46 47 #include <netinet/in.h> 48 #include <netinet/in_systm.h> 49 #include <netinet/ip.h> 50 51 #include <rump/rumpuser.h> 52 53 #include "rump_private.h" 54 55 /* 56 * An inet communication domain which uses the socket interface. 57 * Currently supports only IPv4 UDP, but could easily be extended to 58 * support IPv6 and TCP by adding more stuff to the protosw. 59 */ 60 61 DOMAIN_DEFINE(sockindomain); 62 63 static void sockin_init(void); 64 static int sockin_usrreq(struct socket *, int, struct mbuf *, 65 struct mbuf *, struct mbuf *, struct lwp *); 66 static int sockin_ctloutput(int op, struct socket *, struct sockopt *); 67 68 const struct protosw sockinsw[] = { 69 { 70 .pr_type = SOCK_DGRAM, 71 .pr_domain = &sockindomain, 72 .pr_protocol = IPPROTO_UDP, 73 .pr_flags = PR_ATOMIC|PR_ADDR, 74 .pr_usrreq = sockin_usrreq, 75 .pr_ctloutput = sockin_ctloutput, 76 }, 77 { 78 .pr_type = SOCK_STREAM, 79 .pr_domain = &sockindomain, 80 .pr_protocol = IPPROTO_TCP, 81 .pr_flags = PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN|PR_ABRTACPTDIS, 82 .pr_usrreq = sockin_usrreq, 83 .pr_ctloutput = sockin_ctloutput, 84 }}; 85 86 struct domain sockindomain = { 87 .dom_family = PF_INET, 88 .dom_name = "socket_inet", 89 .dom_init = sockin_init, 90 .dom_externalize = NULL, 91 .dom_dispose = NULL, 92 .dom_protosw = sockinsw, 93 .dom_protoswNPROTOSW = &sockinsw[__arraycount(sockinsw)], 94 .dom_rtattach = rn_inithead, 95 .dom_rtoffset = 32, 96 .dom_maxrtkey = sizeof(struct sockaddr_in), 97 .dom_ifattach = NULL, 98 .dom_ifdetach = NULL, 99 .dom_ifqueues = { NULL }, 100 .dom_link = { NULL }, 101 .dom_mowner = MOWNER_INIT("",""), 102 .dom_rtcache = { NULL }, 103 .dom_sockaddr_cmp = NULL 104 }; 105 106 #define SO2S(so) ((intptr_t)(so->so_internal)) 107 #define SOCKIN_SBSIZE 65536 108 109 struct sockin_unit { 110 struct socket *su_so; 111 112 LIST_ENTRY(sockin_unit) su_entries; 113 }; 114 static LIST_HEAD(, sockin_unit) su_ent = LIST_HEAD_INITIALIZER(su_ent); 115 static kmutex_t su_mtx; 116 static bool rebuild; 117 static int nsock; 118 119 static int 120 registersock(struct socket *so, int news) 121 { 122 struct sockin_unit *su; 123 124 su = kmem_alloc(sizeof(*su), KM_NOSLEEP); 125 if (!su) 126 return ENOMEM; 127 128 so->so_internal = (void *)(intptr_t)news; 129 su->su_so = so; 130 131 mutex_enter(&su_mtx); 132 LIST_INSERT_HEAD(&su_ent, su, su_entries); 133 nsock++; 134 rebuild = true; 135 mutex_exit(&su_mtx); 136 137 return 0; 138 } 139 140 static void 141 removesock(struct socket *so) 142 { 143 struct sockin_unit *su_iter; 144 int error; 145 146 mutex_enter(&su_mtx); 147 LIST_FOREACH(su_iter, &su_ent, su_entries) { 148 if (su_iter->su_so == so) 149 break; 150 } 151 if (!su_iter) 152 panic("no such socket"); 153 154 LIST_REMOVE(su_iter, su_entries); 155 nsock--; 156 rebuild = true; 157 mutex_exit(&su_mtx); 158 159 rumpuser_close(SO2S(su_iter->su_so), &error); 160 kmem_free(su_iter, sizeof(*su_iter)); 161 } 162 163 static void 164 sockin_process(struct socket *so) 165 { 166 struct sockaddr_in from; 167 struct iovec io; 168 struct msghdr rmsg; 169 struct mbuf *m; 170 ssize_t n; 171 size_t plen; 172 int error; 173 174 m = m_gethdr(M_WAIT, MT_DATA); 175 if (so->so_proto->pr_type == SOCK_DGRAM) { 176 plen = IP_MAXPACKET; 177 MEXTMALLOC(m, plen, M_DONTWAIT); 178 } else { 179 plen = MCLBYTES; 180 MCLGET(m, M_DONTWAIT); 181 } 182 if ((m->m_flags & M_EXT) == 0) { 183 m_freem(m); 184 return; 185 } 186 187 memset(&rmsg, 0, sizeof(rmsg)); 188 io.iov_base = mtod(m, void *); 189 io.iov_len = plen; 190 rmsg.msg_iov = &io; 191 rmsg.msg_iovlen = 1; 192 rmsg.msg_name = (struct sockaddr *)&from; 193 rmsg.msg_namelen = sizeof(from); 194 195 n = rumpuser_net_recvmsg(SO2S(so), &rmsg, 0, &error); 196 if (n <= 0) { 197 m_freem(m); 198 199 /* Treat a TCP socket a goner */ 200 if (so->so_proto->pr_type == SOCK_STREAM && error != EAGAIN) { 201 mutex_enter(softnet_lock); 202 soisdisconnected(so); 203 mutex_exit(softnet_lock); 204 removesock(so); 205 } 206 return; 207 } 208 m->m_len = m->m_pkthdr.len = n; 209 210 mutex_enter(softnet_lock); 211 if (so->so_proto->pr_type == SOCK_DGRAM) { 212 if (!sbappendaddr(&so->so_rcv, rmsg.msg_name, m, NULL)) { 213 m_freem(m); 214 } 215 } else { 216 sbappendstream(&so->so_rcv, m); 217 } 218 219 sorwakeup(so); 220 mutex_exit(softnet_lock); 221 } 222 223 static void 224 sockin_accept(struct socket *so) 225 { 226 struct socket *nso; 227 struct sockaddr_in sin; 228 int news, error, slen; 229 230 slen = sizeof(sin); 231 news = rumpuser_net_accept(SO2S(so), (struct sockaddr *)&sin, 232 &slen, &error); 233 if (news == -1) 234 return; 235 236 mutex_enter(softnet_lock); 237 nso = sonewconn(so, SS_ISCONNECTED); 238 if (nso == NULL) 239 goto errout; 240 if (registersock(nso, news) != 0) 241 goto errout; 242 mutex_exit(softnet_lock); 243 return; 244 245 errout: 246 rumpuser_close(news, &error); 247 if (nso) 248 soclose(nso); 249 mutex_exit(softnet_lock); 250 } 251 252 #define POLLTIMEOUT 100 /* check for new entries every 100ms */ 253 254 /* XXX: doesn't handle socket (kernel) locking properly? */ 255 static void 256 sockinworker(void *arg) 257 { 258 struct pollfd *pfds = NULL, *npfds; 259 struct sockin_unit *su_iter; 260 struct socket *so; 261 int cursock = 0, i, rv, error; 262 263 /* 264 * Loop reading requests. Check for new sockets periodically 265 * (could be smarter, but I'm lazy). 266 */ 267 for (;;) { 268 if (rebuild) { 269 npfds = NULL; 270 mutex_enter(&su_mtx); 271 if (nsock) 272 npfds = kmem_alloc(nsock * sizeof(*npfds), 273 KM_NOSLEEP); 274 if (npfds || nsock == 0) { 275 if (pfds) 276 kmem_free(pfds, cursock*sizeof(*pfds)); 277 pfds = npfds; 278 cursock = nsock; 279 rebuild = false; 280 281 i = 0; 282 LIST_FOREACH(su_iter, &su_ent, su_entries) { 283 pfds[i].fd = SO2S(su_iter->su_so); 284 pfds[i].events = POLLIN; 285 pfds[i].revents = 0; 286 i++; 287 } 288 KASSERT(i == nsock); 289 } 290 mutex_exit(&su_mtx); 291 } 292 293 /* find affected sockets & process */ 294 rv = rumpuser_poll(pfds, cursock, POLLTIMEOUT, &error); 295 for (i = 0; i < cursock && rv > 0; i++) { 296 if (pfds[i].revents & POLLIN) { 297 mutex_enter(&su_mtx); 298 LIST_FOREACH(su_iter, &su_ent, su_entries) { 299 if (SO2S(su_iter->su_so)==pfds[i].fd) { 300 so = su_iter->su_so; 301 mutex_exit(&su_mtx); 302 if(so->so_options&SO_ACCEPTCONN) 303 sockin_accept(so); 304 else 305 sockin_process(so); 306 mutex_enter(&su_mtx); 307 break; 308 } 309 } 310 /* if we can't find it, just wing it */ 311 KASSERT(rebuild || su_iter); 312 mutex_exit(&su_mtx); 313 pfds[i].revents = 0; 314 rv--; 315 i = -1; 316 continue; 317 } 318 319 /* something else? ignore */ 320 if (pfds[i].revents) { 321 pfds[i].revents = 0; 322 rv--; 323 } 324 } 325 KASSERT(rv <= 0); 326 } 327 328 } 329 330 static void 331 sockin_init(void) 332 { 333 int rv; 334 335 if (rump_threads) { 336 if ((rv = kthread_create(PRI_NONE, 0, NULL, sockinworker, 337 NULL, NULL, "sockwork")) != 0) 338 panic("sockin_init: could not create worker thread\n"); 339 } else { 340 printf("sockin_init: no threads => no worker thread\n"); 341 } 342 mutex_init(&su_mtx, MUTEX_DEFAULT, IPL_NONE); 343 } 344 345 static int 346 sockin_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, 347 struct mbuf *control, struct lwp *l) 348 { 349 int error = 0, rv; 350 351 switch (req) { 352 case PRU_ATTACH: 353 { 354 int news, dummy; 355 356 sosetlock(so); 357 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 358 error = soreserve(so, SOCKIN_SBSIZE, SOCKIN_SBSIZE); 359 if (error) 360 break; 361 } 362 363 news = rumpuser_net_socket(PF_INET, so->so_proto->pr_type, 364 0, &error); 365 if (news == -1) 366 break; 367 368 if ((error = registersock(so, news)) != 0) 369 rumpuser_close(news, &dummy); 370 371 break; 372 } 373 374 case PRU_ACCEPT: 375 /* we do all the work in the worker thread */ 376 break; 377 378 case PRU_BIND: 379 rumpuser_net_bind(SO2S(so), mtod(nam, const struct sockaddr *), 380 sizeof(struct sockaddr_in), &error); 381 break; 382 383 case PRU_CONNECT: 384 rv = rumpuser_net_connect(SO2S(so), 385 mtod(nam, struct sockaddr *), sizeof(struct sockaddr_in), 386 &error); 387 if (rv == 0) 388 soisconnected(so); 389 break; 390 391 case PRU_LISTEN: 392 rumpuser_net_listen(SO2S(so), so->so_qlimit, &error); 393 break; 394 395 case PRU_SEND: 396 { 397 struct sockaddr *saddr; 398 struct msghdr mhdr; 399 struct iovec iov[16]; 400 struct mbuf *m2; 401 size_t tot; 402 int i, s; 403 404 memset(&mhdr, 0, sizeof(mhdr)); 405 406 tot = 0; 407 for (i = 0, m2 = m; m2; m2 = m2->m_next, i++) { 408 if (i > 16) 409 panic("lazy bum"); 410 iov[i].iov_base = m2->m_data; 411 iov[i].iov_len = m2->m_len; 412 tot += m2->m_len; 413 414 } 415 mhdr.msg_iov = iov; 416 mhdr.msg_iovlen = i; 417 s = SO2S(so); 418 419 if (nam) { 420 saddr = mtod(nam, struct sockaddr *); 421 mhdr.msg_name = saddr; 422 mhdr.msg_namelen = saddr->sa_len; 423 } 424 425 rumpuser_net_sendmsg(s, &mhdr, 0, &error); 426 427 m_freem(m); 428 m_freem(control); 429 430 /* this assumes too many things to list.. buthey, testing */ 431 if (!rump_threads) 432 sockin_process(so); 433 } 434 break; 435 436 case PRU_SHUTDOWN: 437 removesock(so); 438 break; 439 440 case PRU_SOCKADDR: 441 case PRU_PEERADDR: 442 { 443 int slen = nam->m_len; 444 enum rumpuser_getnametype which; 445 446 if (req == PRU_SOCKADDR) 447 which = RUMPUSER_SOCKNAME; 448 else 449 which = RUMPUSER_PEERNAME; 450 rumpuser_net_getname(SO2S(so), 451 mtod(nam, struct sockaddr *), &slen, which, &error); 452 if (error == 0) 453 nam->m_len = slen; 454 break; 455 } 456 457 default: 458 panic("sockin_usrreq: IMPLEMENT ME, req %d not supported", req); 459 } 460 461 return error; 462 } 463 464 static int 465 sockin_ctloutput(int op, struct socket *so, struct sockopt *sopt) 466 { 467 468 /* XXX: we should also do something here */ 469 return 0; 470 } 471