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