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