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