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/net/dummynet/ip_dummynet_glue.c,v 1.9 2008/08/23 08:26:04 sephe Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/kernel.h> 39 #include <sys/mbuf.h> 40 #include <sys/msgport.h> 41 #include <sys/socketvar.h> 42 #include <sys/sysctl.h> 43 44 #include <net/if.h> 45 #include <net/if_var.h> 46 #include <net/route.h> 47 #include <net/ethernet.h> 48 #include <net/netisr.h> 49 #include <net/netmsg2.h> 50 51 #include <netinet/in.h> 52 #include <netinet/in_var.h> 53 #include <netinet/ip.h> 54 #include <netinet/ip_var.h> 55 56 #include <net/dummynet/ip_dummynet.h> 57 58 static void ip_dn_ether_output(struct netmsg *); 59 static void ip_dn_ether_demux(struct netmsg *); 60 static void ip_dn_ip_input(struct netmsg *); 61 static void ip_dn_ip_output(struct netmsg *); 62 63 static void ip_dn_sockopt_dispatch(struct netmsg *); 64 static void ip_dn_freepkt_dispatch(struct netmsg *); 65 static void ip_dn_dispatch(struct netmsg *); 66 67 static void ip_dn_freepkt(struct dn_pkt *); 68 69 static int ip_dn_sockopt_flush(struct sockopt *); 70 static int ip_dn_sockopt_get(struct sockopt *); 71 static int ip_dn_sockopt_config(struct sockopt *); 72 73 ip_dn_io_t *ip_dn_io_ptr; 74 int ip_dn_cpu = 0; 75 76 TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu); 77 78 SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet"); 79 SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, cpu, CTLFLAG_RD, 80 &ip_dn_cpu, 0, "CPU to run dummynet"); 81 82 void 83 ip_dn_queue(struct mbuf *m) 84 { 85 struct netmsg_packet *nmp; 86 lwkt_port_t port; 87 88 M_ASSERTPKTHDR(m); 89 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 90 ("mbuf is not tagged for dummynet!\n")); 91 92 nmp = &m->m_hdr.mh_netmsg; 93 netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0, 94 ip_dn_dispatch); 95 nmp->nm_packet = m; 96 97 port = cpu_portfn(ip_dn_cpu); 98 lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg); 99 } 100 101 void 102 ip_dn_packet_free(struct dn_pkt *pkt) 103 { 104 struct netmsg_packet *nmp; 105 lwkt_port_t port; 106 struct mbuf *m = pkt->dn_m; 107 108 M_ASSERTPKTHDR(m); 109 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 110 ("mbuf is not tagged for dummynet!\n")); 111 112 if (pkt->cpuid == mycpuid) { 113 ip_dn_freepkt(pkt); 114 return; 115 } 116 117 nmp = &m->m_hdr.mh_netmsg; 118 netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0, 119 ip_dn_freepkt_dispatch); 120 nmp->nm_packet = m; 121 122 port = cpu_portfn(pkt->cpuid); 123 lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg); 124 } 125 126 void 127 ip_dn_packet_redispatch(struct dn_pkt *pkt) 128 { 129 static const netisr_fn_t dispatches[DN_TO_MAX] = { 130 [DN_TO_IP_OUT] = ip_dn_ip_output, 131 [DN_TO_IP_IN] = ip_dn_ip_input, 132 [DN_TO_ETH_DEMUX] = ip_dn_ether_demux, 133 [DN_TO_ETH_OUT] = ip_dn_ether_output 134 }; 135 136 struct netmsg_packet *nmp; 137 struct mbuf *m; 138 netisr_fn_t dispatch; 139 lwkt_port_t port; 140 int dir; 141 142 dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK); 143 KASSERT(dir < DN_TO_MAX, 144 ("unknown dummynet redispatch dir %d\n", dir)); 145 146 dispatch = dispatches[dir]; 147 KASSERT(dispatch != NULL, 148 ("unsupported dummynet redispatch dir %d\n", dir)); 149 150 m = pkt->dn_m; 151 M_ASSERTPKTHDR(m); 152 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 153 ("mbuf is not tagged for dummynet!\n")); 154 155 nmp = &m->m_hdr.mh_netmsg; 156 netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0, dispatch); 157 nmp->nm_packet = m; 158 159 port = cpu_portfn(pkt->cpuid); 160 lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg); 161 } 162 163 int 164 ip_dn_sockopt(struct sockopt *sopt) 165 { 166 int error = 0; 167 168 /* Disallow sets in really-really secure mode. */ 169 if (sopt->sopt_dir == SOPT_SET) { 170 if (securelevel >= 3) 171 return EPERM; 172 } 173 174 switch (sopt->sopt_name) { 175 case IP_DUMMYNET_GET: 176 error = ip_dn_sockopt_get(sopt); 177 break; 178 179 case IP_DUMMYNET_FLUSH: 180 error = ip_dn_sockopt_flush(sopt); 181 break; 182 183 case IP_DUMMYNET_DEL: 184 case IP_DUMMYNET_CONFIGURE: 185 error = ip_dn_sockopt_config(sopt); 186 break; 187 188 default: 189 kprintf("%s -- unknown option %d\n", __func__, sopt->sopt_name); 190 error = EINVAL; 191 break; 192 } 193 return error; 194 } 195 196 static void 197 ip_dn_freepkt(struct dn_pkt *pkt) 198 { 199 struct rtentry *rt = pkt->ro.ro_rt; 200 201 /* Unreference route entry */ 202 if (rt != NULL) { 203 if (rt->rt_refcnt <= 0) { /* XXX assert? */ 204 kprintf("-- warning, refcnt now %ld, decreasing\n", 205 rt->rt_refcnt); 206 } 207 RTFREE(rt); 208 } 209 210 /* Unreference packet private data */ 211 if (pkt->dn_unref_priv) 212 pkt->dn_unref_priv(pkt->dn_priv); 213 214 /* Free the parent mbuf, this will free 'pkt' as well */ 215 m_freem(pkt->dn_m); 216 } 217 218 static void 219 ip_dn_freepkt_dispatch(struct netmsg *nmsg) 220 { 221 struct netmsg_packet *nmp; 222 struct mbuf *m; 223 struct m_tag *mtag; 224 struct dn_pkt *pkt; 225 226 nmp = (struct netmsg_packet *)nmsg; 227 m = nmp->nm_packet; 228 M_ASSERTPKTHDR(m); 229 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 230 ("mbuf is not tagged for dummynet!\n")); 231 232 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 233 KKASSERT(mtag != NULL); 234 235 pkt = m_tag_data(mtag); 236 KASSERT(pkt->cpuid == mycpuid, 237 ("%s: dummynet packet was delivered to wrong cpu! " 238 "target cpuid %d, mycpuid %d\n", __func__, 239 pkt->cpuid, mycpuid)); 240 241 ip_dn_freepkt(pkt); 242 } 243 244 static void 245 ip_dn_dispatch(struct netmsg *nmsg) 246 { 247 struct netmsg_packet *nmp; 248 struct mbuf *m; 249 struct m_tag *mtag; 250 struct dn_pkt *pkt; 251 252 KASSERT(ip_dn_cpu == mycpuid, 253 ("%s: dummynet packet was delivered to wrong cpu! " 254 "dummynet cpuid %d, mycpuid %d\n", __func__, 255 ip_dn_cpu, mycpuid)); 256 257 nmp = (struct netmsg_packet *)nmsg; 258 m = nmp->nm_packet; 259 M_ASSERTPKTHDR(m); 260 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 261 ("mbuf is not tagged for dummynet!\n")); 262 263 if (DUMMYNET_LOADED) { 264 if (ip_dn_io_ptr(m) == 0) 265 return; 266 } 267 268 /* 269 * ip_dn_io_ptr() failed or dummynet(4) is not loaded 270 */ 271 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 272 KKASSERT(mtag != NULL); 273 274 pkt = m_tag_data(mtag); 275 ip_dn_packet_free(pkt); 276 } 277 278 static void 279 ip_dn_ip_output(struct netmsg *nmsg) 280 { 281 struct netmsg_packet *nmp; 282 struct mbuf *m; 283 struct m_tag *mtag; 284 struct dn_pkt *pkt; 285 struct rtentry *rt; 286 ip_dn_unref_priv_t unref_priv; 287 void *priv; 288 289 nmp = (struct netmsg_packet *)nmsg; 290 m = nmp->nm_packet; 291 M_ASSERTPKTHDR(m); 292 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 293 ("mbuf is not tagged for dummynet!\n")); 294 295 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 296 KKASSERT(mtag != NULL); 297 298 pkt = m_tag_data(mtag); 299 KASSERT(pkt->cpuid == mycpuid, 300 ("%s: dummynet packet was delivered to wrong cpu! " 301 "target cpuid %d, mycpuid %d\n", __func__, 302 pkt->cpuid, mycpuid)); 303 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT, 304 ("wrong direction %d, should be %d\n", 305 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT)); 306 307 priv = pkt->dn_priv; 308 unref_priv = pkt->dn_unref_priv; 309 rt = pkt->ro.ro_rt; 310 311 if (rt != NULL && !(rt->rt_flags & RTF_UP)) { 312 /* 313 * Recorded rtentry is gone, when the packet 314 * was on delay line. 315 */ 316 ip_dn_freepkt(pkt); 317 return; 318 } 319 320 ip_output(pkt->dn_m, NULL, NULL, 0, NULL, NULL); 321 322 if (rt != NULL) { 323 if (rt->rt_refcnt <= 0) { /* XXX assert? */ 324 kprintf("-- warning, refcnt now %ld, decreasing\n", 325 rt->rt_refcnt); 326 } 327 RTFREE(rt); 328 } 329 if (unref_priv) 330 unref_priv(priv); 331 } 332 333 static void 334 ip_dn_ip_input(struct netmsg *nmsg) 335 { 336 struct netmsg_packet *nmp; 337 struct mbuf *m; 338 struct m_tag *mtag; 339 struct dn_pkt *pkt; 340 ip_dn_unref_priv_t unref_priv; 341 void *priv; 342 343 nmp = (struct netmsg_packet *)nmsg; 344 m = nmp->nm_packet; 345 M_ASSERTPKTHDR(m); 346 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 347 ("mbuf is not tagged for dummynet!\n")); 348 349 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 350 KKASSERT(mtag != NULL); 351 352 pkt = m_tag_data(mtag); 353 KASSERT(pkt->cpuid == mycpuid, 354 ("%s: dummynet packet was delivered to wrong cpu! " 355 "target cpuid %d, mycpuid %d\n", __func__, 356 pkt->cpuid, mycpuid)); 357 KASSERT(pkt->ro.ro_rt == NULL, 358 ("route entry is not NULL for ip_input\n")); 359 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN, 360 ("wrong direction %d, should be %d\n", 361 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN)); 362 363 priv = pkt->dn_priv; 364 unref_priv = pkt->dn_unref_priv; 365 366 ip_input(m); 367 368 if (unref_priv) 369 unref_priv(priv); 370 } 371 372 static void 373 ip_dn_ether_demux(struct netmsg *nmsg) 374 { 375 struct netmsg_packet *nmp; 376 struct mbuf *m; 377 struct m_tag *mtag; 378 struct dn_pkt *pkt; 379 ip_dn_unref_priv_t unref_priv; 380 void *priv; 381 382 nmp = (struct netmsg_packet *)nmsg; 383 m = nmp->nm_packet; 384 M_ASSERTPKTHDR(m); 385 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 386 ("mbuf is not tagged for dummynet!\n")); 387 388 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 389 KKASSERT(mtag != NULL); 390 391 pkt = m_tag_data(mtag); 392 KASSERT(pkt->cpuid == mycpuid, 393 ("%s: dummynet packet was delivered to wrong cpu! " 394 "target cpuid %d, mycpuid %d\n", __func__, 395 pkt->cpuid, mycpuid)); 396 KASSERT(pkt->ro.ro_rt == NULL, 397 ("route entry is not NULL for ether_demux\n")); 398 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX, 399 ("wrong direction %d, should be %d\n", 400 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX)); 401 402 priv = pkt->dn_priv; 403 unref_priv = pkt->dn_unref_priv; 404 405 /* 406 * Make sure that ether header is contiguous 407 */ 408 if (m->m_len < ETHER_HDR_LEN && 409 (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) { 410 kprintf("%s: pullup fail, dropping pkt\n", __func__); 411 goto back; 412 } 413 ether_demux_oncpu(m->m_pkthdr.rcvif, m); 414 back: 415 if (unref_priv) 416 unref_priv(priv); 417 } 418 419 static void 420 ip_dn_ether_output(struct netmsg *nmsg) 421 { 422 struct netmsg_packet *nmp; 423 struct mbuf *m; 424 struct m_tag *mtag; 425 struct dn_pkt *pkt; 426 ip_dn_unref_priv_t unref_priv; 427 void *priv; 428 429 nmp = (struct netmsg_packet *)nmsg; 430 m = nmp->nm_packet; 431 M_ASSERTPKTHDR(m); 432 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 433 ("mbuf is not tagged for dummynet!\n")); 434 435 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 436 KKASSERT(mtag != NULL); 437 438 pkt = m_tag_data(mtag); 439 KASSERT(pkt->cpuid == mycpuid, 440 ("%s: dummynet packet was delivered to wrong cpu! " 441 "target cpuid %d, mycpuid %d\n", __func__, 442 pkt->cpuid, mycpuid)); 443 KASSERT(pkt->ro.ro_rt == NULL, 444 ("route entry is not NULL for ether_output_frame\n")); 445 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT, 446 ("wrong direction %d, should be %d\n", 447 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT)); 448 449 priv = pkt->dn_priv; 450 unref_priv = pkt->dn_unref_priv; 451 452 ether_output_frame(pkt->ifp, m); 453 454 if (unref_priv) 455 unref_priv(priv); 456 } 457 458 static void 459 ip_dn_sockopt_dispatch(struct netmsg *nmsg) 460 { 461 lwkt_msg *msg = &nmsg->nm_lmsg; 462 struct dn_sopt *dn_sopt = msg->u.ms_resultp; 463 int error; 464 465 KASSERT(ip_dn_cpu == mycpuid, 466 ("%s: dummynet sockopt is done on wrong cpu! " 467 "dummynet cpuid %d, mycpuid %d\n", __func__, 468 ip_dn_cpu, mycpuid)); 469 470 if (DUMMYNET_LOADED) 471 error = ip_dn_ctl_ptr(dn_sopt); 472 else 473 error = ENOPROTOOPT; 474 lwkt_replymsg(msg, error); 475 } 476 477 static int 478 ip_dn_sockopt_flush(struct sockopt *sopt) 479 { 480 struct dn_sopt dn_sopt; 481 struct netmsg smsg; 482 483 bzero(&dn_sopt, sizeof(dn_sopt)); 484 dn_sopt.dn_sopt_name = sopt->sopt_name; 485 486 netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch); 487 smsg.nm_lmsg.u.ms_resultp = &dn_sopt; 488 lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0); 489 490 return smsg.nm_lmsg.ms_error; 491 } 492 493 static int 494 ip_dn_sockopt_get(struct sockopt *sopt) 495 { 496 struct dn_sopt dn_sopt; 497 struct netmsg smsg; 498 int error; 499 500 bzero(&dn_sopt, sizeof(dn_sopt)); 501 dn_sopt.dn_sopt_name = sopt->sopt_name; 502 503 netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch); 504 smsg.nm_lmsg.u.ms_resultp = &dn_sopt; 505 lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0); 506 507 error = smsg.nm_lmsg.ms_error; 508 if (error) { 509 KKASSERT(dn_sopt.dn_sopt_arg == NULL); 510 KKASSERT(dn_sopt.dn_sopt_arglen == 0); 511 return error; 512 } 513 514 soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen); 515 kfree(dn_sopt.dn_sopt_arg, M_TEMP); 516 return 0; 517 } 518 519 static int 520 ip_dn_sockopt_config(struct sockopt *sopt) 521 { 522 struct dn_ioc_pipe tmp_ioc_pipe; 523 struct dn_sopt dn_sopt; 524 struct netmsg smsg; 525 int error; 526 527 error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe, 528 sizeof tmp_ioc_pipe); 529 if (error) 530 return error; 531 532 bzero(&dn_sopt, sizeof(dn_sopt)); 533 dn_sopt.dn_sopt_name = sopt->sopt_name; 534 dn_sopt.dn_sopt_arg = &tmp_ioc_pipe; 535 dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe); 536 537 netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch); 538 smsg.nm_lmsg.u.ms_resultp = &dn_sopt; 539 lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0); 540 541 return smsg.nm_lmsg.ms_error; 542 } 543