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 35 36 #include <sys/param.h> 37 #include <sys/kernel.h> 38 #include <sys/mbuf.h> 39 #include <sys/msgport.h> 40 #include <sys/socketvar.h> 41 #include <sys/sysctl.h> 42 43 #include <net/if.h> 44 #include <net/if_var.h> 45 #include <net/route.h> 46 #include <net/ethernet.h> 47 #include <net/netisr2.h> 48 #include <net/netmsg2.h> 49 50 #include <netinet/in.h> 51 #include <netinet/in_var.h> 52 #include <netinet/ip.h> 53 #include <netinet/ip_var.h> 54 55 #include <net/ipfw3/ip_fw.h> 56 #include <net/dummynet3/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!")); 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 = netisr_cpuport(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!")); 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", dir)); 137 138 dispatch = dispatches[dir]; 139 KASSERT(dispatch != NULL, 140 ("unsupported dummynet redispatch dir %d", 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!")); 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!")); 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", __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", __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!")); 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!")); 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", __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", 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 struct ip *ip; 328 329 nmp = &nmsg->packet; 330 m = nmp->nm_packet; 331 M_ASSERTPKTHDR(m); 332 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 333 ("mbuf is not tagged for dummynet!")); 334 335 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 336 KKASSERT(mtag != NULL); 337 338 pkt = m_tag_data(mtag); 339 KASSERT(pkt->cpuid == mycpuid, 340 ("%s: dummynet packet was delivered to wrong cpu! " 341 "target cpuid %d, mycpuid %d", __func__, 342 pkt->cpuid, mycpuid)); 343 KASSERT(pkt->ro.ro_rt == NULL, 344 ("route entry is not NULL for ip_input")); 345 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN, 346 ("wrong direction %d, should be %d", 347 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN)); 348 349 priv = pkt->dn_priv; 350 unref_priv = pkt->dn_unref_priv; 351 352 KKASSERT(m->m_len >= sizeof(*ip)); 353 354 ip_input(m); 355 356 if (unref_priv) 357 unref_priv(priv); 358 } 359 360 static void 361 ip_dn_ether_demux(netmsg_t nmsg) 362 { 363 struct netmsg_packet *nmp; 364 struct mbuf *m; 365 struct m_tag *mtag; 366 struct dn_pkt *pkt; 367 ip_dn_unref_priv_t unref_priv; 368 void *priv; 369 370 nmp = &nmsg->packet; 371 m = nmp->nm_packet; 372 M_ASSERTPKTHDR(m); 373 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 374 ("mbuf is not tagged for dummynet!")); 375 376 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 377 KKASSERT(mtag != NULL); 378 379 pkt = m_tag_data(mtag); 380 KASSERT(pkt->cpuid == mycpuid, 381 ("%s: dummynet packet was delivered to wrong cpu! " 382 "target cpuid %d, mycpuid %d", __func__, 383 pkt->cpuid, mycpuid)); 384 KASSERT(pkt->ro.ro_rt == NULL, 385 ("route entry is not NULL for ether_demux")); 386 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX, 387 ("wrong direction %d, should be %d", 388 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX)); 389 390 priv = pkt->dn_priv; 391 unref_priv = pkt->dn_unref_priv; 392 393 /* 394 * Make sure that ether header is contiguous 395 */ 396 if (m->m_len < ETHER_HDR_LEN && 397 (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) { 398 kprintf("%s: pullup fail, dropping pkt\n", __func__); 399 goto back; 400 } 401 ether_demux_oncpu(m->m_pkthdr.rcvif, m); 402 back: 403 if (unref_priv) 404 unref_priv(priv); 405 } 406 407 static void 408 ip_dn_ether_output(netmsg_t nmsg) 409 { 410 struct netmsg_packet *nmp; 411 struct mbuf *m; 412 struct m_tag *mtag; 413 struct dn_pkt *pkt; 414 ip_dn_unref_priv_t unref_priv; 415 void *priv; 416 417 nmp = &nmsg->packet; 418 m = nmp->nm_packet; 419 M_ASSERTPKTHDR(m); 420 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 421 ("mbuf is not tagged for dummynet!")); 422 423 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 424 KKASSERT(mtag != NULL); 425 426 pkt = m_tag_data(mtag); 427 KASSERT(pkt->cpuid == mycpuid, 428 ("%s: dummynet packet was delivered to wrong cpu! " 429 "target cpuid %d, mycpuid %d", __func__, 430 pkt->cpuid, mycpuid)); 431 KASSERT(pkt->ro.ro_rt == NULL, 432 ("route entry is not NULL for ether_output_frame")); 433 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT, 434 ("wrong direction %d, should be %d", 435 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT)); 436 437 priv = pkt->dn_priv; 438 unref_priv = pkt->dn_unref_priv; 439 440 ether_output_frame(pkt->ifp, m); 441 442 if (unref_priv) 443 unref_priv(priv); 444 } 445 446 static void 447 ip_dn_sockopt_dispatch(netmsg_t nmsg) 448 { 449 lwkt_msg *msg = &nmsg->lmsg; 450 struct dn_sopt *dn_sopt = msg->u.ms_resultp; 451 int error; 452 453 KASSERT(ip_dn_cpu == mycpuid, 454 ("%s: dummynet sockopt is done on wrong cpu! " 455 "dummynet cpuid %d, mycpuid %d", __func__, 456 ip_dn_cpu, mycpuid)); 457 458 if (DUMMYNET_LOADED) 459 error = ip_dn_ctl_ptr(dn_sopt); 460 else 461 error = ENOPROTOOPT; 462 lwkt_replymsg(msg, error); 463 } 464 465 static int 466 ip_dn_sockopt_flush(struct sockopt *sopt) 467 { 468 struct dn_sopt dn_sopt; 469 struct netmsg_base smsg; 470 471 bzero(&dn_sopt, sizeof(dn_sopt)); 472 dn_sopt.dn_sopt_name = sopt->sopt_name; 473 474 netmsg_init(&smsg, NULL, &curthread->td_msgport, 475 0, ip_dn_sockopt_dispatch); 476 smsg.lmsg.u.ms_resultp = &dn_sopt; 477 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 478 479 return smsg.lmsg.ms_error; 480 } 481 482 static int 483 ip_dn_sockopt_get(struct sockopt *sopt) 484 { 485 struct dn_sopt dn_sopt; 486 struct netmsg_base smsg; 487 int error; 488 489 bzero(&dn_sopt, sizeof(dn_sopt)); 490 dn_sopt.dn_sopt_name = sopt->sopt_name; 491 492 netmsg_init(&smsg, NULL, &curthread->td_msgport, 493 0, ip_dn_sockopt_dispatch); 494 smsg.lmsg.u.ms_resultp = &dn_sopt; 495 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 496 497 error = smsg.lmsg.ms_error; 498 if (error) { 499 KKASSERT(dn_sopt.dn_sopt_arg == NULL); 500 KKASSERT(dn_sopt.dn_sopt_arglen == 0); 501 return error; 502 } 503 504 soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen); 505 kfree(dn_sopt.dn_sopt_arg, M_TEMP); 506 return 0; 507 } 508 509 static int 510 ip_dn_sockopt_config(struct sockopt *sopt) 511 { 512 struct dn_ioc_pipe tmp_ioc_pipe; 513 struct dn_sopt dn_sopt; 514 struct netmsg_base smsg; 515 int error; 516 517 error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe, 518 sizeof tmp_ioc_pipe); 519 if (error) 520 return error; 521 522 bzero(&dn_sopt, sizeof(dn_sopt)); 523 dn_sopt.dn_sopt_name = sopt->sopt_name; 524 dn_sopt.dn_sopt_arg = &tmp_ioc_pipe; 525 dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe); 526 527 netmsg_init(&smsg, NULL, &curthread->td_msgport, 528 0, ip_dn_sockopt_dispatch); 529 smsg.lmsg.u.ms_resultp = &dn_sopt; 530 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 531 532 return smsg.lmsg.ms_error; 533 } 534