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