1 /* 2 * Copyright (c) 2012 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/tools/tools/netrate/pktgen/pktgen.c,v 1.4 2008/04/02 14:18:55 sephe Exp $ 35 */ 36 37 #define _IP_VHL 38 39 #include <sys/param.h> 40 #include <sys/conf.h> 41 #include <sys/device.h> 42 #include <sys/in_cksum.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/mbuf.h> 46 #include <sys/proc.h> 47 #include <sys/priv.h> 48 #include <sys/queue.h> 49 #include <sys/socket.h> 50 #include <sys/systm.h> 51 #include <sys/serialize.h> 52 53 #include <net/if.h> 54 #include <net/if_dl.h> 55 #include <net/if_var.h> 56 #include <net/ifq_var.h> 57 #include <net/ethernet.h> 58 #include <net/netmsg2.h> 59 60 #include <netinet/in.h> 61 #include <netinet/ip.h> 62 #include <netinet/udp_var.h> 63 64 #include "pktgen.h" 65 66 #define CDEV_NAME "pktg" 67 68 #define PKTGEN_BUFSZ 2048 69 70 #ifndef PKTGEN_DEVCNT 71 #define PKTGEN_DEVCNT 4 72 #endif 73 74 struct pktgen; 75 76 struct netmsg_pktgen { 77 struct netmsg_base np_base; 78 struct pktgen *np_pktg; 79 struct ifaltq_subque *np_ifsq; 80 }; 81 82 struct pktgen_buf { 83 struct netmsg_base pb_nmsg; /* MUST BE THE FIRST */ 84 void *pb_buf; 85 volatile int pb_done; 86 int pb_inuse; 87 struct ifnet *pb_ifp; 88 struct ifaltq_subque *pb_ifsq; 89 int pb_len; 90 int pb_cpuid; 91 struct pktgen *pb_pktg; 92 LIST_ENTRY(pktgen_buf) pb_link; 93 }; 94 95 struct pktgen_pcpu { 96 struct callout pktg_stop; 97 LIST_HEAD(, pktgen_buf) pktg_buflist; 98 }; 99 100 struct pktgen { 101 uint32_t pktg_flags; /* PKTG_F_ */ 102 int pktg_refcnt; 103 104 int pktg_duration; 105 106 int pktg_datalen; 107 struct ifnet *pktg_ifp; 108 109 int pktg_pktenq; 110 111 struct sockaddr_in pktg_src; 112 int pktg_ndst; 113 struct sockaddr_in *pktg_dst; 114 uint8_t pktg_dst_lladdr[ETHER_ADDR_LEN]; 115 116 struct pktgen_pcpu pktg_pcpu[MAXCPU]; 117 }; 118 119 #define PKTG_F_CONFIG 0x1 120 #define PKTG_F_RUNNING 0x4 121 122 static int pktgen_modevent(module_t, int, void *); 123 124 static void pktgen_buf_free(void *); 125 static void pktgen_buf_ref(void *); 126 static void pktgen_buf_send(netmsg_t); 127 128 static int pktgen_config(struct pktgen *, 129 const struct pktgen_conf *); 130 static int pktgen_start(struct pktgen *, int); 131 static void pktgen_free(struct pktgen *); 132 static void pktgen_ref(struct pktgen *); 133 static void pktgen_pcpu_stop_cb(void *); 134 static void pktgen_mbuf(struct pktgen_buf *, struct mbuf *); 135 static void pktgen_start_ifsq(struct pktgen *, 136 struct ifaltq_subque *); 137 static void pktgen_start_ifsq_handler(netmsg_t); 138 139 static d_open_t pktgen_open; 140 static d_close_t pktgen_close; 141 static d_ioctl_t pktgen_ioctl; 142 143 static struct dev_ops pktgen_ops = { 144 { CDEV_NAME, 0, D_MPSAFE }, 145 .d_open = pktgen_open, 146 .d_close = pktgen_close, 147 .d_ioctl = pktgen_ioctl, 148 }; 149 150 static volatile int pktgen_refcnt; 151 static struct lwkt_token pktgen_tok = LWKT_TOKEN_INITIALIZER(pktgen_token); 152 153 MALLOC_DECLARE(M_PKTGEN); 154 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator"); 155 156 DEV_MODULE(pktgen, pktgen_modevent, NULL); 157 158 static int 159 pktgen_modevent(module_t mod, int type, void *data) 160 { 161 int error = 0, i; 162 163 switch (type) { 164 case MOD_LOAD: 165 for (i = 0; i < PKTGEN_DEVCNT; ++i) { 166 make_dev(&pktgen_ops, 0, UID_ROOT, GID_WHEEL, 0600, 167 CDEV_NAME"%d", i); 168 } 169 break; 170 171 case MOD_UNLOAD: 172 if (pktgen_refcnt > 0) 173 return EBUSY; 174 dev_ops_remove_all(&pktgen_ops); 175 break; 176 177 default: 178 error = EOPNOTSUPP; 179 break; 180 } 181 return error; 182 } 183 184 static int 185 pktgen_open(struct dev_open_args *ap) 186 { 187 cdev_t dev = ap->a_head.a_dev; 188 struct pktgen *pktg; 189 int error, i; 190 191 error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0); 192 if (error) 193 return error; 194 195 lwkt_gettoken(&pktgen_tok); 196 197 if (dev->si_drv1 != NULL) { 198 lwkt_reltoken(&pktgen_tok); 199 return EBUSY; 200 } 201 202 pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK); 203 for (i = 0; i < ncpus; ++i) { 204 struct pktgen_pcpu *p = &pktg->pktg_pcpu[i]; 205 206 callout_init_mp(&p->pktg_stop); 207 LIST_INIT(&p->pktg_buflist); 208 } 209 210 dev->si_drv1 = pktg; 211 pktg->pktg_refcnt = 1; 212 213 atomic_add_int(&pktgen_refcnt, 1); 214 215 lwkt_reltoken(&pktgen_tok); 216 return 0; 217 } 218 219 static int 220 pktgen_close(struct dev_close_args *ap) 221 { 222 cdev_t dev = ap->a_head.a_dev; 223 struct pktgen *pktg = dev->si_drv1; 224 225 lwkt_gettoken(&pktgen_tok); 226 dev->si_drv1 = NULL; 227 lwkt_reltoken(&pktgen_tok); 228 229 pktgen_free(pktg); 230 231 return 0; 232 } 233 234 static int 235 pktgen_ioctl(struct dev_ioctl_args *ap __unused) 236 { 237 cdev_t dev = ap->a_head.a_dev; 238 caddr_t data = ap->a_data; 239 struct pktgen *pktg = dev->si_drv1; 240 int error; 241 242 lwkt_gettoken(&pktgen_tok); 243 244 switch (ap->a_cmd) { 245 case PKTGENSTART: 246 error = pktgen_start(pktg, 0); 247 break; 248 249 case PKTGENMQSTART: 250 error = pktgen_start(pktg, 1); 251 break; 252 253 case PKTGENSCONF: 254 error = pktgen_config(pktg, (const struct pktgen_conf *)data); 255 break; 256 257 default: 258 error = EOPNOTSUPP; 259 break; 260 } 261 262 lwkt_reltoken(&pktgen_tok); 263 return error; 264 } 265 266 static int 267 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf) 268 { 269 const struct sockaddr_in *sin; 270 struct sockaddr_in *dst = NULL; 271 const struct sockaddr *sa; 272 struct ifnet *ifp; 273 size_t dst_size; 274 int i, error, pktenq; 275 276 if (pktg->pktg_flags & (PKTG_F_RUNNING | PKTG_F_CONFIG)) 277 return EBUSY; 278 279 if (conf->pc_datalen <= 0 || 280 conf->pc_datalen > ETHERMTU - sizeof(struct udpiphdr)) 281 return EINVAL; 282 if (conf->pc_duration <= 0) 283 return EINVAL; 284 285 sin = &conf->pc_src; 286 if (sin->sin_family != AF_INET) 287 return EPROTONOSUPPORT; 288 if (sin->sin_port == 0) 289 return EINVAL; 290 291 if (conf->pc_ndst <= 0) 292 return EINVAL; 293 dst_size = conf->pc_ndst * sizeof(struct sockaddr_in); 294 295 dst = kmalloc(dst_size, M_PKTGEN, M_WAITOK | M_NULLOK); 296 if (dst == NULL) 297 return ENOMEM; 298 299 error = copyin(conf->pc_dst, dst, dst_size); 300 if (error) 301 goto failed; 302 303 for (i = 0; i < conf->pc_ndst; ++i) { 304 sin = &dst[i]; 305 if (sin->sin_family != AF_INET) { 306 error = EPROTONOSUPPORT; 307 goto failed; 308 } 309 if (sin->sin_port == 0) { 310 error = EINVAL; 311 goto failed; 312 } 313 } 314 315 ifp = ifunit(conf->pc_ifname); 316 if (ifp == NULL) { 317 error = ENXIO; 318 goto failed; 319 } 320 321 pktenq = conf->pc_pktenq; 322 if (pktenq < 0 || pktenq > ifp->if_snd.altq_maxlen) { 323 error = ENOBUFS; 324 goto failed; 325 } else if (pktenq == 0) { 326 pktenq = (ifp->if_snd.altq_maxlen * 3) / 4; 327 } 328 329 sa = &conf->pc_dst_lladdr; 330 if (sa->sa_family != AF_LINK) { 331 error = EPROTONOSUPPORT; 332 goto failed; 333 } 334 if (sa->sa_len != ETHER_ADDR_LEN) { 335 error = EPROTONOSUPPORT; 336 goto failed; 337 } 338 if (ETHER_IS_MULTICAST(sa->sa_data) || 339 bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) { 340 error = EADDRNOTAVAIL; 341 goto failed; 342 } 343 344 /* 345 * Accept the config 346 */ 347 pktg->pktg_flags |= PKTG_F_CONFIG; 348 349 pktg->pktg_duration = conf->pc_duration; 350 pktg->pktg_datalen = conf->pc_datalen; 351 pktg->pktg_pktenq = pktenq; 352 pktg->pktg_ifp = ifp; 353 pktg->pktg_src = conf->pc_src; 354 pktg->pktg_ndst = conf->pc_ndst; 355 KKASSERT(pktg->pktg_dst == NULL); 356 pktg->pktg_dst = dst; 357 bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN); 358 359 return 0; 360 361 failed: 362 if (dst != NULL) 363 kfree(dst, M_PKTGEN); 364 return error; 365 } 366 367 static void 368 pktgen_start_ifsq(struct pktgen *pktg, struct ifaltq_subque *ifsq) 369 { 370 struct netmsg_pktgen *np; 371 372 np = kmalloc(sizeof(*np), M_LWKTMSG, M_WAITOK); 373 netmsg_init(&np->np_base, NULL, &netisr_afree_rport, 0, 374 pktgen_start_ifsq_handler); 375 np->np_pktg = pktg; 376 np->np_ifsq = ifsq; 377 378 lwkt_sendmsg(netisr_portfn(ifsq_get_cpuid(ifsq)), &np->np_base.lmsg); 379 } 380 381 static int 382 pktgen_start(struct pktgen *pktg, int mq) 383 { 384 struct ifaltq *ifq; 385 386 if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0) 387 return EINVAL; 388 if (pktg->pktg_flags & PKTG_F_RUNNING) 389 return EBUSY; 390 pktg->pktg_flags |= PKTG_F_RUNNING; 391 392 ifq = &pktg->pktg_ifp->if_snd; 393 if (!mq) { 394 pktgen_ref(pktg); 395 pktgen_start_ifsq(pktg, ifq_get_subq_default(ifq)); 396 } else { 397 int i; 398 399 for (i = 0; i < ifq->altq_subq_cnt; ++i) 400 pktgen_ref(pktg); 401 for (i = 0; i < ifq->altq_subq_cnt; ++i) 402 pktgen_start_ifsq(pktg, ifq_get_subq(ifq, i)); 403 } 404 return 0; 405 } 406 407 static void 408 pktgen_start_ifsq_handler(netmsg_t nmsg) 409 { 410 struct netmsg_pktgen *np = (struct netmsg_pktgen *)nmsg; 411 struct pktgen *pktg = np->np_pktg; 412 struct ifaltq_subque *ifsq = np->np_ifsq; 413 414 struct mbuf *m, *head = NULL, **next; 415 struct ifnet *ifp; 416 struct pktgen_pcpu *p; 417 int cpuid, i, alloc_cnt, keep_cnt; 418 419 u_short ulen, psum; 420 int len, ip_len; 421 422 /* Reply ASAP */ 423 lwkt_replymsg(&np->np_base.lmsg, 0); 424 425 ifp = pktg->pktg_ifp; 426 427 cpuid = ifsq_get_cpuid(ifsq); 428 KKASSERT(cpuid == mycpuid); 429 430 p = &pktg->pktg_pcpu[cpuid]; 431 432 keep_cnt = pktg->pktg_pktenq; 433 alloc_cnt = keep_cnt * 2; 434 435 /* 436 * Prefault enough mbuf into mbuf objcache 437 */ 438 next = &head; 439 for (i = 0; i < alloc_cnt; ++i) { 440 MGETHDR(m, MB_WAIT, MT_DATA); 441 *next = m; 442 next = &m->m_nextpkt; 443 } 444 445 for (i = 0; i < alloc_cnt - keep_cnt; ++i) { 446 m = head; 447 head = m->m_nextpkt; 448 m->m_nextpkt = NULL; 449 m_freem(m); 450 } 451 KKASSERT(head != NULL); 452 453 /* 454 * Setup the packets' data 455 */ 456 ip_len = pktg->pktg_datalen + sizeof(struct udpiphdr); 457 len = ip_len + ETHER_HDR_LEN; 458 459 psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) + 460 IPPROTO_UDP); 461 ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr)); 462 463 m = head; 464 i = 0; 465 while (m != NULL) { 466 struct mbuf *nextm; 467 const struct sockaddr_in *dst; 468 struct pktgen_buf *pb; 469 struct ip *ip; 470 struct udpiphdr *ui; 471 struct ether_header *eh; 472 473 pktgen_ref(pktg); 474 475 pb = kmalloc(sizeof(*pb), M_PKTGEN, M_WAITOK | M_ZERO); 476 pb->pb_ifp = ifp; 477 pb->pb_ifsq = ifsq; 478 pb->pb_inuse = 1; 479 pb->pb_buf = kmalloc(PKTGEN_BUFSZ, M_PKTGEN, M_WAITOK); 480 pb->pb_len = len; 481 pb->pb_cpuid = cpuid; 482 pb->pb_pktg = pktg; 483 netmsg_init(&pb->pb_nmsg, NULL, &netisr_adone_rport, 0, 484 pktgen_buf_send); 485 LIST_INSERT_HEAD(&p->pktg_buflist, pb, pb_link); 486 487 dst = &pktg->pktg_dst[i % pktg->pktg_ndst]; 488 ++i; 489 490 m->m_ext.ext_arg = pb; 491 m->m_ext.ext_buf = pb->pb_buf; 492 m->m_ext.ext_free = pktgen_buf_free; 493 m->m_ext.ext_ref = pktgen_buf_ref; 494 m->m_ext.ext_size = PKTGEN_BUFSZ; 495 496 m->m_data = m->m_ext.ext_buf; 497 m->m_flags |= M_EXT; 498 m->m_len = m->m_pkthdr.len = len; 499 500 m->m_data += ETHER_HDR_LEN; 501 m->m_len -= ETHER_HDR_LEN; 502 m->m_pkthdr.len -= ETHER_HDR_LEN; 503 504 ui = mtod(m, struct udpiphdr *); 505 ui->ui_pr = IPPROTO_UDP; 506 ui->ui_src.s_addr = pktg->pktg_src.sin_addr.s_addr; 507 ui->ui_dst.s_addr = dst->sin_addr.s_addr; 508 ui->ui_sport = pktg->pktg_src.sin_port; 509 ui->ui_dport = dst->sin_port; 510 ui->ui_ulen = ulen; 511 ui->ui_sum = in_pseudo(ui->ui_src.s_addr, ui->ui_dst.s_addr, 512 psum); 513 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 514 515 ip = (struct ip *)ui; 516 ip->ip_len = ip_len; 517 ip->ip_ttl = 64; /* XXX */ 518 ip->ip_tos = 0; /* XXX */ 519 ip->ip_vhl = IP_VHL_BORING; 520 ip->ip_off = 0; 521 ip->ip_sum = 0; 522 ip->ip_id = ip_newid(); 523 524 in_delayed_cksum(m); 525 526 ip->ip_len = htons(ip->ip_len); 527 ip->ip_sum = in_cksum_hdr(ip); 528 529 m->m_data -= ETHER_HDR_LEN; 530 m->m_len += ETHER_HDR_LEN; 531 m->m_pkthdr.len += ETHER_HDR_LEN; 532 533 eh = mtod(m, struct ether_header *); 534 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN); 535 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN); 536 eh->ether_type = htons(ETHERTYPE_IP); 537 538 nextm = m->m_nextpkt; 539 m->m_nextpkt = NULL; 540 541 ifq_dispatch(ifp, m, NULL); 542 543 m = nextm; 544 } 545 546 callout_reset(&p->pktg_stop, pktg->pktg_duration * hz, 547 pktgen_pcpu_stop_cb, p); 548 549 pktgen_free(pktg); 550 } 551 552 static void 553 pktgen_mbuf(struct pktgen_buf *pb, struct mbuf *m) 554 { 555 m->m_ext.ext_arg = pb; 556 m->m_ext.ext_buf = pb->pb_buf; 557 m->m_ext.ext_free = pktgen_buf_free; 558 m->m_ext.ext_ref = pktgen_buf_ref; 559 m->m_ext.ext_size = PKTGEN_BUFSZ; 560 561 m->m_data = m->m_ext.ext_buf; 562 m->m_flags |= M_EXT; 563 m->m_len = m->m_pkthdr.len = pb->pb_len; 564 } 565 566 static void 567 pktgen_buf_send(netmsg_t msg) 568 { 569 struct pktgen_buf *pb = (struct pktgen_buf *)msg; 570 struct mbuf *m; 571 572 KKASSERT(&curthread->td_msgport == netisr_portfn(pb->pb_cpuid)); 573 574 crit_enter(); 575 lwkt_replymsg(&pb->pb_nmsg.lmsg, 0); 576 crit_exit(); 577 578 MGETHDR(m, MB_WAIT, MT_DATA); 579 pktgen_mbuf(pb, m); 580 ifq_dispatch(pb->pb_ifp, m, NULL); 581 } 582 583 static void 584 pktgen_buf_free(void *arg) 585 { 586 struct pktgen_buf *pb = arg; 587 struct mbuf *m; 588 589 KKASSERT(pb->pb_inuse > 0); 590 if (pb->pb_done) { 591 if (atomic_fetchadd_int(&pb->pb_inuse, -1) == 1) { 592 struct pktgen *pktg; 593 594 pktg = pb->pb_pktg; 595 crit_enter(); 596 LIST_REMOVE(pb, pb_link); 597 crit_exit(); 598 kfree(pb->pb_buf, M_PKTGEN); 599 kfree(pb, M_PKTGEN); 600 601 pktgen_free(pktg); 602 } 603 return; 604 } 605 606 if (&curthread->td_msgport != netisr_portfn(pb->pb_cpuid)) { 607 KKASSERT(pb->pb_cpuid == mycpuid); 608 crit_enter(); 609 KKASSERT(pb->pb_nmsg.lmsg.ms_flags & MSGF_DONE); 610 lwkt_sendmsg(netisr_portfn(pb->pb_cpuid), &pb->pb_nmsg.lmsg); 611 crit_exit(); 612 return; 613 } 614 615 MGETHDR(m, MB_WAIT, MT_DATA); 616 pktgen_mbuf(pb, m); 617 ifsq_enqueue(pb->pb_ifsq, m, NULL); 618 } 619 620 static void 621 pktgen_buf_ref(void *arg) 622 { 623 struct pktgen_buf *pb = arg; 624 625 panic("%s should never be called\n", __func__); 626 627 KKASSERT(pb->pb_inuse > 0); 628 atomic_add_int(&pb->pb_inuse, 1); 629 } 630 631 static void 632 pktgen_free(struct pktgen *pktg) 633 { 634 KKASSERT(pktg->pktg_refcnt > 0); 635 if (atomic_fetchadd_int(&pktg->pktg_refcnt, -1) == 1) { 636 int i; 637 638 if (pktg->pktg_dst != NULL) 639 kfree(pktg->pktg_dst, M_PKTGEN); 640 641 for (i = 0; i < ncpus; ++i) 642 KKASSERT(LIST_EMPTY(&pktg->pktg_pcpu[i].pktg_buflist)); 643 kfree(pktg, M_PKTGEN); 644 } 645 646 KKASSERT(pktgen_refcnt > 0); 647 atomic_subtract_int(&pktgen_refcnt, 1); 648 } 649 650 static void 651 pktgen_pcpu_stop_cb(void *arg) 652 { 653 struct pktgen_pcpu *p = arg; 654 struct pktgen_buf *pb; 655 656 crit_enter(); 657 LIST_FOREACH(pb, &p->pktg_buflist, pb_link) 658 pb->pb_done = 1; 659 crit_exit(); 660 } 661 662 static void 663 pktgen_ref(struct pktgen *pktg) 664 { 665 atomic_add_int(&pktg->pktg_refcnt, 1); 666 atomic_add_int(&pktgen_refcnt, 1); 667 } 668