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