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/malloc.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/dummynet/ip_dummynet.h> 56 57 static void ip_dn_ether_output(netmsg_t); 58 static void ip_dn_ether_demux(netmsg_t); 59 static void ip_dn_ip_input(netmsg_t); 60 static void ip_dn_ip_output(netmsg_t); 61 62 static void ip_dn_sockopt_dispatch(netmsg_t); 63 static void ip_dn_freepkt_dispatch(netmsg_t); 64 static void ip_dn_dispatch(netmsg_t); 65 66 static void ip_dn_freepkt(struct dn_pkt *); 67 68 static int ip_dn_sockopt_flush(struct sockopt *); 69 static int ip_dn_sockopt_get(struct sockopt *); 70 static int ip_dn_sockopt_config(struct sockopt *); 71 72 ip_dn_io_t *ip_dn_io_ptr; 73 ip_dn_ctl_t *ip_dn_ctl_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 /* ip_input() expects ip_off/ip_len in network byte order. */ 353 KKASSERT(m->m_len >= sizeof(*ip)); 354 ip = mtod(m, struct ip *); 355 ip->ip_len = htons(ip->ip_len); 356 ip->ip_off = htons(ip->ip_off); 357 358 ip_input(m); 359 360 if (unref_priv) 361 unref_priv(priv); 362 } 363 364 static void 365 ip_dn_ether_demux(netmsg_t nmsg) 366 { 367 struct netmsg_packet *nmp; 368 struct mbuf *m; 369 struct m_tag *mtag; 370 struct dn_pkt *pkt; 371 ip_dn_unref_priv_t unref_priv; 372 void *priv; 373 374 nmp = &nmsg->packet; 375 m = nmp->nm_packet; 376 M_ASSERTPKTHDR(m); 377 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 378 ("mbuf is not tagged for dummynet!")); 379 380 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 381 KKASSERT(mtag != NULL); 382 383 pkt = m_tag_data(mtag); 384 KASSERT(pkt->cpuid == mycpuid, 385 ("%s: dummynet packet was delivered to wrong cpu! " 386 "target cpuid %d, mycpuid %d", __func__, 387 pkt->cpuid, mycpuid)); 388 KASSERT(pkt->ro.ro_rt == NULL, 389 ("route entry is not NULL for ether_demux")); 390 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX, 391 ("wrong direction %d, should be %d", 392 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX)); 393 394 priv = pkt->dn_priv; 395 unref_priv = pkt->dn_unref_priv; 396 397 /* 398 * Make sure that ether header is contiguous 399 */ 400 if (m->m_len < ETHER_HDR_LEN && 401 (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) { 402 kprintf("%s: pullup fail, dropping pkt\n", __func__); 403 goto back; 404 } 405 ether_demux_oncpu(m->m_pkthdr.rcvif, m); 406 back: 407 if (unref_priv) 408 unref_priv(priv); 409 } 410 411 static void 412 ip_dn_ether_output(netmsg_t nmsg) 413 { 414 struct netmsg_packet *nmp; 415 struct mbuf *m; 416 struct m_tag *mtag; 417 struct dn_pkt *pkt; 418 ip_dn_unref_priv_t unref_priv; 419 void *priv; 420 421 nmp = &nmsg->packet; 422 m = nmp->nm_packet; 423 M_ASSERTPKTHDR(m); 424 KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED, 425 ("mbuf is not tagged for dummynet!")); 426 427 mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); 428 KKASSERT(mtag != NULL); 429 430 pkt = m_tag_data(mtag); 431 KASSERT(pkt->cpuid == mycpuid, 432 ("%s: dummynet packet was delivered to wrong cpu! " 433 "target cpuid %d, mycpuid %d", __func__, 434 pkt->cpuid, mycpuid)); 435 KASSERT(pkt->ro.ro_rt == NULL, 436 ("route entry is not NULL for ether_output_frame")); 437 KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT, 438 ("wrong direction %d, should be %d", 439 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT)); 440 441 priv = pkt->dn_priv; 442 unref_priv = pkt->dn_unref_priv; 443 444 ether_output_frame(pkt->ifp, m); 445 446 if (unref_priv) 447 unref_priv(priv); 448 } 449 450 static void 451 ip_dn_sockopt_dispatch(netmsg_t nmsg) 452 { 453 lwkt_msg *msg = &nmsg->lmsg; 454 struct dn_sopt *dn_sopt = msg->u.ms_resultp; 455 int error; 456 457 KASSERT(ip_dn_cpu == mycpuid, 458 ("%s: dummynet sockopt is done on wrong cpu! " 459 "dummynet cpuid %d, mycpuid %d", __func__, 460 ip_dn_cpu, mycpuid)); 461 462 if (DUMMYNET_LOADED) 463 error = ip_dn_ctl_ptr(dn_sopt); 464 else 465 error = ENOPROTOOPT; 466 lwkt_replymsg(msg, error); 467 } 468 469 static int 470 ip_dn_sockopt_flush(struct sockopt *sopt) 471 { 472 struct dn_sopt dn_sopt; 473 struct netmsg_base smsg; 474 475 bzero(&dn_sopt, sizeof(dn_sopt)); 476 dn_sopt.dn_sopt_name = sopt->sopt_name; 477 478 netmsg_init(&smsg, NULL, &curthread->td_msgport, 479 0, ip_dn_sockopt_dispatch); 480 smsg.lmsg.u.ms_resultp = &dn_sopt; 481 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 482 483 return smsg.lmsg.ms_error; 484 } 485 486 static int 487 ip_dn_sockopt_get(struct sockopt *sopt) 488 { 489 struct dn_sopt dn_sopt; 490 struct netmsg_base smsg; 491 int error; 492 493 bzero(&dn_sopt, sizeof(dn_sopt)); 494 dn_sopt.dn_sopt_name = sopt->sopt_name; 495 496 netmsg_init(&smsg, NULL, &curthread->td_msgport, 497 0, ip_dn_sockopt_dispatch); 498 smsg.lmsg.u.ms_resultp = &dn_sopt; 499 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 500 501 error = smsg.lmsg.ms_error; 502 if (error) { 503 KKASSERT(dn_sopt.dn_sopt_arg == NULL); 504 KKASSERT(dn_sopt.dn_sopt_arglen == 0); 505 return error; 506 } 507 508 soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen); 509 kfree(dn_sopt.dn_sopt_arg, M_TEMP); 510 return 0; 511 } 512 513 static int 514 ip_dn_sockopt_config(struct sockopt *sopt) 515 { 516 struct dn_ioc_pipe tmp_ioc_pipe; 517 struct dn_sopt dn_sopt; 518 struct netmsg_base smsg; 519 int error; 520 521 error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe, 522 sizeof tmp_ioc_pipe); 523 if (error) 524 return error; 525 526 bzero(&dn_sopt, sizeof(dn_sopt)); 527 dn_sopt.dn_sopt_name = sopt->sopt_name; 528 dn_sopt.dn_sopt_arg = &tmp_ioc_pipe; 529 dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe); 530 531 netmsg_init(&smsg, NULL, &curthread->td_msgport, 532 0, ip_dn_sockopt_dispatch); 533 smsg.lmsg.u.ms_resultp = &dn_sopt; 534 lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0); 535 536 return smsg.lmsg.ms_error; 537 } 538