1 /* 2 * Copyright (c) 2008 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/socket.h> 49 #include <sys/systm.h> 50 #include <sys/serialize.h> 51 52 #include <net/if.h> 53 #include <net/if_dl.h> 54 #include <net/if_var.h> 55 #include <net/ifq_var.h> 56 #include <net/ethernet.h> 57 58 #include <netinet/in.h> 59 #include <netinet/ip.h> 60 #include <netinet/udp_var.h> 61 62 #include "pktgen.h" 63 64 #define CDEV_NAME "pktg" 65 #define CDEV_MAJOR 151 66 67 struct pktgen { 68 uint32_t pktg_flags; /* PKTG_F_ */ 69 int pktg_refcnt; 70 71 uint64_t pktg_tx_cnt; 72 uint64_t pktg_err_cnt; 73 struct timeval pktg_start; 74 struct timeval pktg_end; 75 76 struct callout pktg_stop; 77 int pktg_duration; 78 int pktg_cpuid; 79 void (*pktg_thread)(void *); 80 81 int pktg_datalen; 82 int pktg_yield; 83 struct ifnet *pktg_ifp; 84 85 in_addr_t pktg_saddr; /* host byte order */ 86 in_addr_t pktg_daddr; /* host byte order */ 87 u_short pktg_sport; /* host byte order */ 88 u_short pktg_dport; /* host byte order */ 89 90 int pktg_nsaddr; 91 int pktg_ndaddr; 92 int pktg_nsport; 93 int pktg_ndport; 94 95 uint8_t pktg_dst_lladdr[ETHER_ADDR_LEN]; 96 }; 97 98 #define PKTG_F_CONFIG 0x1 99 #define PKTG_F_STOP 0x2 100 #define PKTG_F_RUNNING 0x4 101 102 static int pktgen_modevent(module_t, int, void *); 103 static int pktgen_config(struct pktgen *, 104 const struct pktgen_conf *); 105 static int pktgen_start(struct pktgen *, int); 106 static void pktgen_thread_exit(struct pktgen *, uint64_t, uint64_t); 107 static void pktgen_stop_cb(void *); 108 static void pktgen_udp_thread(void *); 109 static void pktgen_udp_thread1(void *); 110 111 static d_open_t pktgen_open; 112 static d_close_t pktgen_close; 113 static d_ioctl_t pktgen_ioctl; 114 115 static struct dev_ops pktgen_ops = { 116 { CDEV_NAME, CDEV_MAJOR, 0 }, 117 .d_open = pktgen_open, 118 .d_close = pktgen_close, 119 .d_ioctl = pktgen_ioctl, 120 }; 121 122 static int pktgen_refcnt; 123 124 MALLOC_DECLARE(M_PKTGEN); 125 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator"); 126 127 DEV_MODULE(pktgen, pktgen_modevent, NULL); 128 129 static int 130 pktgen_modevent(module_t mod, int type, void *data) 131 { 132 int error = 0; 133 134 switch (type) { 135 case MOD_LOAD: 136 make_dev(&pktgen_ops, 0, UID_ROOT, GID_WHEEL, 0600, 137 CDEV_NAME"%d", 0); 138 break; 139 140 case MOD_UNLOAD: 141 if (pktgen_refcnt > 0) 142 return EBUSY; 143 dev_ops_remove_all(&pktgen_ops); 144 break; 145 146 default: 147 error = EOPNOTSUPP; 148 break; 149 } 150 return error; 151 } 152 153 static int 154 pktgen_open(struct dev_open_args *ap) 155 { 156 cdev_t dev = ap->a_head.a_dev; 157 struct pktgen *pktg; 158 int error; 159 160 error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0); 161 if (error) 162 return error; 163 164 get_mplock(); 165 166 if (dev->si_drv1 != NULL) { 167 rel_mplock(); 168 return EBUSY; 169 } 170 171 pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK); 172 callout_init(&pktg->pktg_stop); 173 174 dev->si_drv1 = pktg; 175 pktg->pktg_refcnt = 1; 176 177 pktgen_refcnt++; 178 179 rel_mplock(); 180 return 0; 181 } 182 183 static int 184 pktgen_close(struct dev_close_args *ap) 185 { 186 cdev_t dev = ap->a_head.a_dev; 187 struct pktgen *pktg = dev->si_drv1; 188 189 get_mplock(); 190 191 KKASSERT(pktg->pktg_refcnt > 0); 192 if (--pktg->pktg_refcnt == 0) 193 kfree(pktg, M_PKTGEN); 194 dev->si_drv1 = NULL; 195 196 KKASSERT(pktgen_refcnt > 0); 197 pktgen_refcnt--; 198 199 rel_mplock(); 200 return 0; 201 } 202 203 static int 204 pktgen_ioctl(struct dev_ioctl_args *ap __unused) 205 { 206 cdev_t dev = ap->a_head.a_dev; 207 caddr_t data = ap->a_data; 208 struct pktgen *pktg = dev->si_drv1; 209 int error; 210 211 get_mplock(); 212 213 switch (ap->a_cmd) { 214 case PKTGENSTART: 215 error = pktgen_start(pktg, minor(dev)); 216 break; 217 218 case PKTGENSCONF: 219 error = pktgen_config(pktg, (const struct pktgen_conf *)data); 220 break; 221 222 default: 223 error = EOPNOTSUPP; 224 break; 225 } 226 227 rel_mplock(); 228 return error; 229 } 230 231 static int 232 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf) 233 { 234 const struct sockaddr_in *sin; 235 const struct sockaddr *sa; 236 struct ifnet *ifp; 237 int yield, nsaddr, ndaddr, nsport, ndport, thread1; 238 239 if (pktg->pktg_flags & PKTG_F_RUNNING) 240 return EBUSY; 241 242 if (conf->pc_cpuid < 0 || conf->pc_cpuid >= ncpus) 243 return EINVAL; 244 if (conf->pc_datalen <= 0) 245 return EINVAL; 246 if (conf->pc_duration <= 0) 247 return EINVAL; 248 249 yield = conf->pc_yield; 250 if (yield <= 0) 251 yield = PKTGEN_YIELD_DEFAULT; 252 253 if (conf->pc_nsaddr <= 0 && conf->pc_ndaddr <= 0 && 254 conf->pc_nsport <= 0 && conf->pc_ndport <= 0) 255 thread1 = 0; 256 else 257 thread1 = 1; 258 259 nsaddr = conf->pc_nsaddr; 260 if (nsaddr <= 0) 261 nsaddr = 1; 262 ndaddr = conf->pc_ndaddr; 263 if (ndaddr <= 0) 264 ndaddr = 1; 265 266 nsport = conf->pc_nsport; 267 if (nsport <= 0) 268 nsport = 1; 269 ndport = conf->pc_ndport; 270 if (ndport <= 0) 271 ndport = 1; 272 273 ifp = ifunit(conf->pc_ifname); 274 if (ifp == NULL) 275 return ENXIO; 276 277 sa = &conf->pc_dst_lladdr; 278 if (sa->sa_family != AF_LINK) 279 return EPROTONOSUPPORT; 280 if (sa->sa_len != ETHER_ADDR_LEN) 281 return EPROTONOSUPPORT; 282 if (ETHER_IS_MULTICAST(sa->sa_data) || 283 bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) 284 return EADDRNOTAVAIL; 285 286 sin = &conf->pc_src; 287 if (sin->sin_family != AF_INET) 288 return EPROTONOSUPPORT; 289 if (sin->sin_port == 0) 290 return EINVAL; 291 292 sin = &conf->pc_dst; 293 if (sin->sin_family != AF_INET) 294 return EPROTONOSUPPORT; 295 if (sin->sin_port == 0) 296 return EINVAL; 297 298 /* Accept the config */ 299 pktg->pktg_flags |= PKTG_F_CONFIG; 300 pktg->pktg_refcnt++; 301 pktgen_refcnt++; 302 303 pktg->pktg_duration = conf->pc_duration; 304 pktg->pktg_cpuid = conf->pc_cpuid; 305 pktg->pktg_ifp = ifp; 306 pktg->pktg_datalen = conf->pc_datalen; 307 pktg->pktg_yield = yield; 308 bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN); 309 310 pktg->pktg_saddr = ntohl(conf->pc_src.sin_addr.s_addr); 311 pktg->pktg_daddr = ntohl(conf->pc_dst.sin_addr.s_addr); 312 pktg->pktg_nsaddr = nsaddr; 313 pktg->pktg_ndaddr = ndaddr; 314 315 pktg->pktg_sport = ntohs(conf->pc_src.sin_port); 316 pktg->pktg_dport = ntohs(conf->pc_dst.sin_port); 317 pktg->pktg_nsport = nsport; 318 pktg->pktg_ndport = ndport; 319 320 pktg->pktg_thread = thread1 ? pktgen_udp_thread1 : pktgen_udp_thread; 321 322 return 0; 323 } 324 325 static int 326 pktgen_start(struct pktgen *pktg, int m) 327 { 328 if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0) 329 return EINVAL; 330 if (pktg->pktg_flags & PKTG_F_RUNNING) 331 return EBUSY; 332 333 pktg->pktg_flags |= PKTG_F_RUNNING; 334 335 lwkt_create(pktg->pktg_thread, pktg, NULL, NULL, 0, 336 pktg->pktg_cpuid, "pktgen %d", m); 337 return 0; 338 } 339 340 static void 341 pktgen_stop_cb(void *arg) 342 { 343 struct pktgen *pktg = arg; 344 345 pktg->pktg_flags |= PKTG_F_STOP; 346 } 347 348 static void 349 pktgen_udp_thread1(void *arg) 350 { 351 struct pktgen *pktg = arg; 352 struct ifnet *ifp = pktg->pktg_ifp; 353 struct ip *ip; 354 struct udpiphdr *ui; 355 struct ether_header *eh; 356 struct mbuf *m; 357 u_short ulen, psum; 358 int len, ip_len; 359 int sw_csum, csum_flags; 360 int loop, r, error; 361 uint64_t err_cnt, cnt; 362 in_addr_t saddr, daddr; 363 u_short sport, dport; 364 365 rel_mplock(); /* Don't need MP lock */ 366 367 callout_reset(&pktg->pktg_stop, pktg->pktg_duration * hz, 368 pktgen_stop_cb, pktg); 369 370 cnt = err_cnt = 0; 371 r = loop = 0; 372 373 ip_len = pktg->pktg_datalen + sizeof(*ui); 374 len = ip_len + ETHER_HDR_LEN; 375 376 psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) 377 + IPPROTO_UDP); 378 ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr)); 379 380 sw_csum = (CSUM_UDP | CSUM_IP) & ~ifp->if_hwassist; 381 csum_flags = (CSUM_UDP | CSUM_IP) & ifp->if_hwassist; 382 383 saddr = pktg->pktg_saddr; 384 daddr = pktg->pktg_daddr; 385 sport = pktg->pktg_sport; 386 dport = pktg->pktg_dport; 387 388 microtime(&pktg->pktg_start); 389 while ((pktg->pktg_flags & PKTG_F_STOP) == 0) { 390 m = m_getl(len, MB_WAIT, MT_DATA, M_PKTHDR, NULL); 391 m->m_len = m->m_pkthdr.len = len; 392 393 m_adj(m, ETHER_HDR_LEN); 394 395 ui = mtod(m, struct udpiphdr *); 396 ui->ui_pr = IPPROTO_UDP; 397 ui->ui_src.s_addr = htonl(saddr); 398 ui->ui_dst.s_addr = htonl(daddr); 399 ui->ui_sport = htons(sport); 400 ui->ui_dport = htons(dport); 401 ui->ui_ulen = ulen; 402 ui->ui_sum = in_pseudo(ui->ui_src.s_addr, 403 ui->ui_dst.s_addr, psum); 404 m->m_pkthdr.csum_flags = (CSUM_IP | CSUM_UDP); 405 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 406 407 ip = (struct ip *)ui; 408 ip->ip_len = ip_len; 409 ip->ip_ttl = 64; /* XXX */ 410 ip->ip_tos = 0; /* XXX */ 411 ip->ip_vhl = IP_VHL_BORING; 412 ip->ip_off = 0; 413 ip->ip_id = ip_newid(); 414 415 if (sw_csum & CSUM_DELAY_DATA) 416 in_delayed_cksum(m); 417 m->m_pkthdr.csum_flags = csum_flags; 418 419 ip->ip_len = htons(ip->ip_len); 420 ip->ip_sum = 0; 421 if (sw_csum & CSUM_DELAY_IP) 422 ip->ip_sum = in_cksum_hdr(ip); 423 424 M_PREPEND(m, ETHER_HDR_LEN, MB_WAIT); 425 eh = mtod(m, struct ether_header *); 426 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN); 427 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN); 428 eh->ether_type = htons(ETHERTYPE_IP); 429 430 ifnet_serialize_tx(ifp); 431 error = ifq_handoff(ifp, m, NULL); 432 ifnet_deserialize_tx(ifp); 433 434 loop++; 435 if (error) { 436 err_cnt++; 437 loop = 0; 438 lwkt_yield(); 439 } else { 440 cnt++; 441 if (loop == pktg->pktg_yield) { 442 loop = 0; 443 lwkt_yield(); 444 } 445 446 r++; 447 saddr = pktg->pktg_saddr + (r % pktg->pktg_nsaddr); 448 daddr = pktg->pktg_daddr + (r % pktg->pktg_ndaddr); 449 sport = pktg->pktg_sport + (r % pktg->pktg_nsport); 450 dport = pktg->pktg_dport + (r % pktg->pktg_ndport); 451 } 452 } 453 microtime(&pktg->pktg_end); 454 455 pktgen_thread_exit(pktg, cnt, err_cnt); 456 } 457 458 static void 459 pktgen_udp_thread(void *arg) 460 { 461 struct pktgen *pktg = arg; 462 struct ifnet *ifp = pktg->pktg_ifp; 463 struct ip *ip; 464 struct udpiphdr *ui; 465 struct ether_header *eh; 466 struct mbuf *m; 467 u_short ulen, sum; 468 int len, ip_len; 469 int sw_csum, csum_flags; 470 int loop, error; 471 uint64_t err_cnt, cnt; 472 in_addr_t saddr, daddr; 473 u_short sport, dport; 474 475 rel_mplock(); /* Don't need MP lock */ 476 477 callout_reset(&pktg->pktg_stop, pktg->pktg_duration * hz, 478 pktgen_stop_cb, pktg); 479 480 cnt = err_cnt = 0; 481 loop = 0; 482 483 ip_len = pktg->pktg_datalen + sizeof(*ui); 484 len = ip_len + ETHER_HDR_LEN; 485 486 saddr = htonl(pktg->pktg_saddr); 487 daddr = htonl(pktg->pktg_daddr); 488 sport = htons(pktg->pktg_sport); 489 dport = htons(pktg->pktg_dport); 490 491 sum = in_pseudo(saddr, daddr, 492 htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) 493 + IPPROTO_UDP)); 494 ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr)); 495 496 sw_csum = (CSUM_UDP | CSUM_IP) & ~ifp->if_hwassist; 497 csum_flags = (CSUM_UDP | CSUM_IP) & ifp->if_hwassist; 498 499 microtime(&pktg->pktg_start); 500 while ((pktg->pktg_flags & PKTG_F_STOP) == 0) { 501 m = m_getl(len, MB_WAIT, MT_DATA, M_PKTHDR, NULL); 502 m->m_len = m->m_pkthdr.len = len; 503 504 m_adj(m, ETHER_HDR_LEN); 505 506 ui = mtod(m, struct udpiphdr *); 507 ui->ui_pr = IPPROTO_UDP; 508 ui->ui_src.s_addr = saddr; 509 ui->ui_dst.s_addr = daddr; 510 ui->ui_sport = sport; 511 ui->ui_dport = dport; 512 ui->ui_ulen = ulen; 513 ui->ui_sum = sum; 514 m->m_pkthdr.csum_flags = (CSUM_IP | CSUM_UDP); 515 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 516 517 ip = (struct ip *)ui; 518 ip->ip_len = ip_len; 519 ip->ip_ttl = 64; /* XXX */ 520 ip->ip_tos = 0; /* XXX */ 521 ip->ip_vhl = IP_VHL_BORING; 522 ip->ip_off = 0; 523 ip->ip_id = ip_newid(); 524 525 if (sw_csum & CSUM_DELAY_DATA) 526 in_delayed_cksum(m); 527 m->m_pkthdr.csum_flags = csum_flags; 528 529 ip->ip_len = htons(ip->ip_len); 530 ip->ip_sum = 0; 531 if (sw_csum & CSUM_DELAY_IP) 532 ip->ip_sum = in_cksum_hdr(ip); 533 534 M_PREPEND(m, ETHER_HDR_LEN, MB_WAIT); 535 eh = mtod(m, struct ether_header *); 536 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN); 537 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN); 538 eh->ether_type = htons(ETHERTYPE_IP); 539 540 ifnet_serialize_tx(ifp); 541 error = ifq_handoff(ifp, m, NULL); 542 ifnet_deserialize_tx(ifp); 543 544 loop++; 545 if (error) { 546 err_cnt++; 547 loop = 0; 548 lwkt_yield(); 549 } else { 550 cnt++; 551 if (loop == pktg->pktg_yield) { 552 loop = 0; 553 lwkt_yield(); 554 } 555 } 556 } 557 microtime(&pktg->pktg_end); 558 559 pktgen_thread_exit(pktg, cnt, err_cnt); 560 } 561 562 static void 563 pktgen_thread_exit(struct pktgen *pktg, uint64_t tx_cnt, uint64_t err_cnt) 564 { 565 struct timeval end; 566 567 pktg->pktg_tx_cnt = tx_cnt; 568 pktg->pktg_err_cnt = err_cnt; 569 570 end = pktg->pktg_end; 571 timevalsub(&end, &pktg->pktg_start); 572 kprintf("cnt %llu, err %llu, time %ld.%06ld\n", 573 pktg->pktg_tx_cnt, pktg->pktg_err_cnt, end.tv_sec, end.tv_usec); 574 575 pktg->pktg_flags &= ~(PKTG_F_STOP | PKTG_F_CONFIG | PKTG_F_RUNNING); 576 577 KKASSERT(pktg->pktg_refcnt > 0); 578 if (--pktg->pktg_refcnt == 0) 579 kfree(pktg, M_PKTGEN); /* XXX */ 580 581 KKASSERT(pktgen_refcnt > 0); 582 pktgen_refcnt--; 583 584 lwkt_exit(); 585 } 586