2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/tools/tools/netrate/pktgen/pktgen.c,v 1.3 2008/04/02 13:27:24 sephe Exp $
39 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/ioccom.h>
43 #include <sys/in_cksum.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
48 #include <sys/socket.h>
49 #include <sys/systm.h>
50 #include <sys/serialize.h>
53 #include <net/if_dl.h>
54 #include <net/if_var.h>
55 #include <net/ifq_var.h>
56 #include <net/ethernet.h>
58 #include <netinet/in.h>
59 #include <netinet/ip.h>
60 #include <netinet/udp_var.h>
64 #define CDEV_NAME "pktg"
65 #define CDEV_MAJOR 151
68 uint32_t pktg_flags; /* PKTG_F_ */
71 struct callout pktg_stop;
74 void (*pktg_thread)(void *);
78 struct ifnet *pktg_ifp;
80 in_addr_t pktg_saddr; /* host byte order */
81 in_addr_t pktg_daddr; /* host byte order */
82 u_short pktg_sport; /* host byte order */
83 u_short pktg_dport; /* host byte order */
90 uint8_t pktg_dst_lladdr[ETHER_ADDR_LEN];
93 #define PKTG_F_CONFIG 0x1
94 #define PKTG_F_STOP 0x2
95 #define PKTG_F_RUNNING 0x4
97 static int pktgen_modevent(module_t, int, void *);
98 static int pktgen_config(struct pktgen *,
99 const struct pktgen_conf *);
100 static int pktgen_start(struct pktgen *, int);
101 static void pktgen_stop_cb(void *);
102 static void pktgen_udp_thread(void *);
103 static void pktgen_udp_thread1(void *);
105 static d_open_t pktgen_open;
106 static d_close_t pktgen_close;
107 static d_ioctl_t pktgen_ioctl;
109 static struct dev_ops pktgen_ops = {
110 { CDEV_NAME, CDEV_MAJOR, 0 },
111 .d_open = pktgen_open,
112 .d_close = pktgen_close,
113 .d_ioctl = pktgen_ioctl,
116 static int pktgen_refcnt;
118 MALLOC_DECLARE(M_PKTGEN);
119 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator");
121 DEV_MODULE(pktgen, pktgen_modevent, NULL);
124 pktgen_modevent(module_t mod, int type, void *data)
130 dev_ops_add(&pktgen_ops, 0, 0);
134 if (pktgen_refcnt > 0)
136 dev_ops_remove(&pktgen_ops, 0, 0);
147 pktgen_open(struct dev_open_args *ap)
149 cdev_t dev = ap->a_head.a_dev;
153 error = suser_cred(ap->a_cred, 0);
159 if (dev->si_drv1 != NULL) {
164 pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK);
165 callout_init(&pktg->pktg_stop);
167 dev = make_dev(&pktgen_ops, minor(dev), UID_ROOT, GID_WHEEL, 0600,
168 CDEV_NAME "%d", lminor(dev));
170 pktg->pktg_refcnt = 1;
179 pktgen_close(struct dev_close_args *ap)
181 cdev_t dev = ap->a_head.a_dev;
182 struct pktgen *pktg = dev->si_drv1;
186 KKASSERT(pktg->pktg_refcnt > 0);
187 if (--pktg->pktg_refcnt == 0)
188 kfree(pktg, M_PKTGEN);
191 KKASSERT(pktgen_refcnt > 0);
199 pktgen_ioctl(struct dev_ioctl_args *ap __unused)
201 cdev_t dev = ap->a_head.a_dev;
202 caddr_t data = ap->a_data;
203 struct pktgen *pktg = dev->si_drv1;
210 error = pktgen_start(pktg, minor(dev));
214 error = pktgen_config(pktg, (const struct pktgen_conf *)data);
227 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf)
229 const struct sockaddr_in *sin;
230 const struct sockaddr *sa;
232 int yield, nsaddr, ndaddr, nsport, ndport, thread1;
234 if (pktg->pktg_flags & PKTG_F_RUNNING)
237 if (conf->pc_cpuid < 0 || conf->pc_cpuid >= ncpus)
239 if (conf->pc_datalen <= 0)
241 if (conf->pc_duration <= 0)
244 yield = conf->pc_yield;
246 yield = PKTGEN_YIELD_DEFAULT;
248 if (conf->pc_nsaddr <= 0 && conf->pc_ndaddr <= 0 &&
249 conf->pc_nsport <= 0 && conf->pc_ndport <= 0)
254 nsaddr = conf->pc_nsaddr;
257 ndaddr = conf->pc_ndaddr;
261 nsport = conf->pc_nsport;
264 ndport = conf->pc_ndport;
268 ifp = ifunit(conf->pc_ifname);
272 sa = &conf->pc_dst_lladdr;
273 if (sa->sa_family != AF_LINK)
274 return EPROTONOSUPPORT;
275 if (sa->sa_len != ETHER_ADDR_LEN)
276 return EPROTONOSUPPORT;
277 if (ETHER_IS_MULTICAST(sa->sa_data) ||
278 bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0)
279 return EADDRNOTAVAIL;
282 if (sin->sin_family != AF_INET)
283 return EPROTONOSUPPORT;
284 if (sin->sin_port == 0)
288 if (sin->sin_family != AF_INET)
289 return EPROTONOSUPPORT;
290 if (sin->sin_port == 0)
293 /* Accept the config */
294 pktg->pktg_flags |= PKTG_F_CONFIG;
298 pktg->pktg_duration = conf->pc_duration;
299 pktg->pktg_cpuid = conf->pc_cpuid;
300 pktg->pktg_ifp = ifp;
301 pktg->pktg_datalen = conf->pc_datalen;
302 pktg->pktg_yield = yield;
303 bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN);
305 pktg->pktg_saddr = ntohl(conf->pc_src.sin_addr.s_addr);
306 pktg->pktg_daddr = ntohl(conf->pc_dst.sin_addr.s_addr);
307 pktg->pktg_nsaddr = nsaddr;
308 pktg->pktg_ndaddr = ndaddr;
310 pktg->pktg_sport = ntohs(conf->pc_src.sin_port);
311 pktg->pktg_dport = ntohs(conf->pc_dst.sin_port);
312 pktg->pktg_nsport = nsport;
313 pktg->pktg_ndport = ndport;
315 pktg->pktg_thread = thread1 ? pktgen_udp_thread1 : pktgen_udp_thread;
321 pktgen_start(struct pktgen *pktg, int m)
323 if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0)
325 if (pktg->pktg_flags & PKTG_F_RUNNING)
328 pktg->pktg_flags |= PKTG_F_RUNNING;
330 lwkt_create(pktg->pktg_thread, pktg, NULL, NULL, 0,
331 pktg->pktg_cpuid, "pktgen %d", m);
336 pktgen_stop_cb(void *arg)
338 struct pktgen *pktg = arg;
340 pktg->pktg_flags |= PKTG_F_STOP;
344 pktgen_udp_thread1(void *arg)
346 struct pktgen *pktg = arg;
347 struct ifnet *ifp = pktg->pktg_ifp;
350 struct ether_header *eh;
354 int sw_csum, csum_flags;
356 uint64_t err_cnt, cnt;
357 in_addr_t saddr, daddr;
358 u_short sport, dport;
359 struct timeval start, end;
361 rel_mplock(); /* Don't need MP lock */
363 callout_reset(&pktg->pktg_stop, pktg->pktg_duration * hz,
364 pktgen_stop_cb, pktg);
369 ip_len = pktg->pktg_datalen + sizeof(*ui);
370 len = ip_len + ETHER_HDR_LEN;
372 psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr)
374 ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr));
376 sw_csum = (CSUM_UDP | CSUM_IP) & ~ifp->if_hwassist;
377 csum_flags = (CSUM_UDP | CSUM_IP) & ifp->if_hwassist;
379 saddr = pktg->pktg_saddr;
380 daddr = pktg->pktg_daddr;
381 sport = pktg->pktg_sport;
382 dport = pktg->pktg_dport;
385 while ((pktg->pktg_flags & PKTG_F_STOP) == 0) {
386 m = m_getl(len, MB_WAIT, MT_DATA, M_PKTHDR, NULL);
387 m->m_len = m->m_pkthdr.len = len;
389 m_adj(m, ETHER_HDR_LEN);
391 ui = mtod(m, struct udpiphdr *);
392 ui->ui_pr = IPPROTO_UDP;
393 ui->ui_src.s_addr = htonl(saddr);
394 ui->ui_dst.s_addr = htonl(daddr);
395 ui->ui_sport = htons(sport);
396 ui->ui_dport = htons(dport);
398 ui->ui_sum = in_pseudo(ui->ui_src.s_addr,
399 ui->ui_dst.s_addr, psum);
400 m->m_pkthdr.csum_flags = (CSUM_IP | CSUM_UDP);
401 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
403 ip = (struct ip *)ui;
405 ip->ip_ttl = 64; /* XXX */
406 ip->ip_tos = 0; /* XXX */
407 ip->ip_vhl = IP_VHL_BORING;
409 ip->ip_id = ip_newid();
411 if (sw_csum & CSUM_DELAY_DATA)
413 m->m_pkthdr.csum_flags = csum_flags;
415 ip->ip_len = htons(ip->ip_len);
417 if (sw_csum & CSUM_DELAY_IP)
418 ip->ip_sum = in_cksum_hdr(ip);
420 M_PREPEND(m, ETHER_HDR_LEN, MB_WAIT);
421 eh = mtod(m, struct ether_header *);
422 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN);
423 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
424 eh->ether_type = htons(ETHERTYPE_IP);
426 lwkt_serialize_enter(ifp->if_serializer);
427 error = ifq_handoff(ifp, m, NULL);
428 lwkt_serialize_exit(ifp->if_serializer);
437 if (loop == pktg->pktg_yield) {
443 saddr = pktg->pktg_saddr + (r % pktg->pktg_nsaddr);
444 daddr = pktg->pktg_daddr + (r % pktg->pktg_ndaddr);
445 sport = pktg->pktg_sport + (r % pktg->pktg_nsport);
446 dport = pktg->pktg_dport + (r % pktg->pktg_ndport);
451 timevalsub(&end, &start);
452 kprintf("cnt %llu, err %llu, time %ld.%ld\n", cnt, err_cnt,
453 end.tv_sec, end.tv_usec);
455 pktg->pktg_flags &= ~(PKTG_F_STOP | PKTG_F_CONFIG | PKTG_F_RUNNING);
457 KKASSERT(pktg->pktg_refcnt > 0);
458 if (--pktg->pktg_refcnt == 0)
459 kfree(pktg, M_PKTGEN); /* XXX */
461 KKASSERT(pktgen_refcnt > 0);
468 pktgen_udp_thread(void *arg)
470 struct pktgen *pktg = arg;
471 struct ifnet *ifp = pktg->pktg_ifp;
474 struct ether_header *eh;
478 int sw_csum, csum_flags;
480 uint64_t err_cnt, cnt;
481 in_addr_t saddr, daddr;
482 u_short sport, dport;
483 struct timeval start, end;
485 rel_mplock(); /* Don't need MP lock */
487 callout_reset(&pktg->pktg_stop, pktg->pktg_duration * hz,
488 pktgen_stop_cb, pktg);
493 ip_len = pktg->pktg_datalen + sizeof(*ui);
494 len = ip_len + ETHER_HDR_LEN;
496 saddr = htonl(pktg->pktg_saddr);
497 daddr = htonl(pktg->pktg_daddr);
498 sport = htons(pktg->pktg_sport);
499 dport = htons(pktg->pktg_dport);
501 sum = in_pseudo(saddr, daddr,
502 htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr)
504 ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr));
506 sw_csum = (CSUM_UDP | CSUM_IP) & ~ifp->if_hwassist;
507 csum_flags = (CSUM_UDP | CSUM_IP) & ifp->if_hwassist;
510 while ((pktg->pktg_flags & PKTG_F_STOP) == 0) {
511 m = m_getl(len, MB_WAIT, MT_DATA, M_PKTHDR, NULL);
512 m->m_len = m->m_pkthdr.len = len;
514 m_adj(m, ETHER_HDR_LEN);
516 ui = mtod(m, struct udpiphdr *);
517 ui->ui_pr = IPPROTO_UDP;
518 ui->ui_src.s_addr = saddr;
519 ui->ui_dst.s_addr = daddr;
520 ui->ui_sport = sport;
521 ui->ui_dport = dport;
524 m->m_pkthdr.csum_flags = (CSUM_IP | CSUM_UDP);
525 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
527 ip = (struct ip *)ui;
529 ip->ip_ttl = 64; /* XXX */
530 ip->ip_tos = 0; /* XXX */
531 ip->ip_vhl = IP_VHL_BORING;
533 ip->ip_id = ip_newid();
535 if (sw_csum & CSUM_DELAY_DATA)
537 m->m_pkthdr.csum_flags = csum_flags;
539 ip->ip_len = htons(ip->ip_len);
541 if (sw_csum & CSUM_DELAY_IP)
542 ip->ip_sum = in_cksum_hdr(ip);
544 M_PREPEND(m, ETHER_HDR_LEN, MB_WAIT);
545 eh = mtod(m, struct ether_header *);
546 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN);
547 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
548 eh->ether_type = htons(ETHERTYPE_IP);
550 lwkt_serialize_enter(ifp->if_serializer);
551 error = ifq_handoff(ifp, m, NULL);
552 lwkt_serialize_exit(ifp->if_serializer);
561 if (loop == pktg->pktg_yield) {
569 timevalsub(&end, &start);
570 kprintf("cnt %llu, err %llu, time %ld.%06ld\n", cnt, err_cnt,
571 end.tv_sec, end.tv_usec);
573 pktg->pktg_flags &= ~(PKTG_F_STOP | PKTG_F_CONFIG | PKTG_F_RUNNING);
575 KKASSERT(pktg->pktg_refcnt > 0);
576 if (--pktg->pktg_refcnt == 0)
577 kfree(pktg, M_PKTGEN); /* XXX */
579 KKASSERT(pktgen_refcnt > 0);