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.4 2008/04/02 14:18:55 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_ */
72 uint64_t pktg_err_cnt
;
73 struct timeval pktg_start
;
74 struct timeval pktg_end
;
76 struct callout pktg_stop
;
79 void (*pktg_thread
)(void *);
83 struct ifnet
*pktg_ifp
;
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 */
95 uint8_t pktg_dst_lladdr
[ETHER_ADDR_LEN
];
98 #define PKTG_F_CONFIG 0x1
99 #define PKTG_F_STOP 0x2
100 #define PKTG_F_RUNNING 0x4
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 *);
111 static d_open_t pktgen_open
;
112 static d_close_t pktgen_close
;
113 static d_ioctl_t pktgen_ioctl
;
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
,
122 static int pktgen_refcnt
;
124 MALLOC_DECLARE(M_PKTGEN
);
125 MALLOC_DEFINE(M_PKTGEN
, CDEV_NAME
, "Packet generator");
127 DEV_MODULE(pktgen
, pktgen_modevent
, NULL
);
130 pktgen_modevent(module_t mod
, int type
, void *data
)
136 dev_ops_add(&pktgen_ops
, 0, 0);
140 if (pktgen_refcnt
> 0)
142 dev_ops_remove(&pktgen_ops
, 0, 0);
153 pktgen_open(struct dev_open_args
*ap
)
155 cdev_t dev
= ap
->a_head
.a_dev
;
159 error
= suser_cred(ap
->a_cred
, 0);
165 if (dev
->si_drv1
!= NULL
) {
170 pktg
= kmalloc(sizeof(*pktg
), M_PKTGEN
, M_ZERO
| M_WAITOK
);
171 callout_init(&pktg
->pktg_stop
);
173 dev
= make_dev(&pktgen_ops
, minor(dev
), UID_ROOT
, GID_WHEEL
, 0600,
174 CDEV_NAME
"%d", lminor(dev
));
176 pktg
->pktg_refcnt
= 1;
185 pktgen_close(struct dev_close_args
*ap
)
187 cdev_t dev
= ap
->a_head
.a_dev
;
188 struct pktgen
*pktg
= dev
->si_drv1
;
192 KKASSERT(pktg
->pktg_refcnt
> 0);
193 if (--pktg
->pktg_refcnt
== 0)
194 kfree(pktg
, M_PKTGEN
);
197 KKASSERT(pktgen_refcnt
> 0);
205 pktgen_ioctl(struct dev_ioctl_args
*ap __unused
)
207 cdev_t dev
= ap
->a_head
.a_dev
;
208 caddr_t data
= ap
->a_data
;
209 struct pktgen
*pktg
= dev
->si_drv1
;
216 error
= pktgen_start(pktg
, minor(dev
));
220 error
= pktgen_config(pktg
, (const struct pktgen_conf
*)data
);
233 pktgen_config(struct pktgen
*pktg
, const struct pktgen_conf
*conf
)
235 const struct sockaddr_in
*sin
;
236 const struct sockaddr
*sa
;
238 int yield
, nsaddr
, ndaddr
, nsport
, ndport
, thread1
;
240 if (pktg
->pktg_flags
& PKTG_F_RUNNING
)
243 if (conf
->pc_cpuid
< 0 || conf
->pc_cpuid
>= ncpus
)
245 if (conf
->pc_datalen
<= 0)
247 if (conf
->pc_duration
<= 0)
250 yield
= conf
->pc_yield
;
252 yield
= PKTGEN_YIELD_DEFAULT
;
254 if (conf
->pc_nsaddr
<= 0 && conf
->pc_ndaddr
<= 0 &&
255 conf
->pc_nsport
<= 0 && conf
->pc_ndport
<= 0)
260 nsaddr
= conf
->pc_nsaddr
;
263 ndaddr
= conf
->pc_ndaddr
;
267 nsport
= conf
->pc_nsport
;
270 ndport
= conf
->pc_ndport
;
274 ifp
= ifunit(conf
->pc_ifname
);
278 sa
= &conf
->pc_dst_lladdr
;
279 if (sa
->sa_family
!= AF_LINK
)
280 return EPROTONOSUPPORT
;
281 if (sa
->sa_len
!= ETHER_ADDR_LEN
)
282 return EPROTONOSUPPORT
;
283 if (ETHER_IS_MULTICAST(sa
->sa_data
) ||
284 bcmp(sa
->sa_data
, ifp
->if_broadcastaddr
, ifp
->if_addrlen
) == 0)
285 return EADDRNOTAVAIL
;
288 if (sin
->sin_family
!= AF_INET
)
289 return EPROTONOSUPPORT
;
290 if (sin
->sin_port
== 0)
294 if (sin
->sin_family
!= AF_INET
)
295 return EPROTONOSUPPORT
;
296 if (sin
->sin_port
== 0)
299 /* Accept the config */
300 pktg
->pktg_flags
|= PKTG_F_CONFIG
;
304 pktg
->pktg_duration
= conf
->pc_duration
;
305 pktg
->pktg_cpuid
= conf
->pc_cpuid
;
306 pktg
->pktg_ifp
= ifp
;
307 pktg
->pktg_datalen
= conf
->pc_datalen
;
308 pktg
->pktg_yield
= yield
;
309 bcopy(sa
->sa_data
, pktg
->pktg_dst_lladdr
, ETHER_ADDR_LEN
);
311 pktg
->pktg_saddr
= ntohl(conf
->pc_src
.sin_addr
.s_addr
);
312 pktg
->pktg_daddr
= ntohl(conf
->pc_dst
.sin_addr
.s_addr
);
313 pktg
->pktg_nsaddr
= nsaddr
;
314 pktg
->pktg_ndaddr
= ndaddr
;
316 pktg
->pktg_sport
= ntohs(conf
->pc_src
.sin_port
);
317 pktg
->pktg_dport
= ntohs(conf
->pc_dst
.sin_port
);
318 pktg
->pktg_nsport
= nsport
;
319 pktg
->pktg_ndport
= ndport
;
321 pktg
->pktg_thread
= thread1
? pktgen_udp_thread1
: pktgen_udp_thread
;
327 pktgen_start(struct pktgen
*pktg
, int m
)
329 if ((pktg
->pktg_flags
& PKTG_F_CONFIG
) == 0)
331 if (pktg
->pktg_flags
& PKTG_F_RUNNING
)
334 pktg
->pktg_flags
|= PKTG_F_RUNNING
;
336 lwkt_create(pktg
->pktg_thread
, pktg
, NULL
, NULL
, 0,
337 pktg
->pktg_cpuid
, "pktgen %d", m
);
342 pktgen_stop_cb(void *arg
)
344 struct pktgen
*pktg
= arg
;
346 pktg
->pktg_flags
|= PKTG_F_STOP
;
350 pktgen_udp_thread1(void *arg
)
352 struct pktgen
*pktg
= arg
;
353 struct ifnet
*ifp
= pktg
->pktg_ifp
;
356 struct ether_header
*eh
;
360 int sw_csum
, csum_flags
;
362 uint64_t err_cnt
, cnt
;
363 in_addr_t saddr
, daddr
;
364 u_short sport
, dport
;
366 rel_mplock(); /* Don't need MP lock */
368 callout_reset(&pktg
->pktg_stop
, pktg
->pktg_duration
* hz
,
369 pktgen_stop_cb
, pktg
);
374 ip_len
= pktg
->pktg_datalen
+ sizeof(*ui
);
375 len
= ip_len
+ ETHER_HDR_LEN
;
377 psum
= htons((u_short
)pktg
->pktg_datalen
+ sizeof(struct udphdr
)
379 ulen
= htons(pktg
->pktg_datalen
+ sizeof(struct udphdr
));
381 sw_csum
= (CSUM_UDP
| CSUM_IP
) & ~ifp
->if_hwassist
;
382 csum_flags
= (CSUM_UDP
| CSUM_IP
) & ifp
->if_hwassist
;
384 saddr
= pktg
->pktg_saddr
;
385 daddr
= pktg
->pktg_daddr
;
386 sport
= pktg
->pktg_sport
;
387 dport
= pktg
->pktg_dport
;
389 microtime(&pktg
->pktg_start
);
390 while ((pktg
->pktg_flags
& PKTG_F_STOP
) == 0) {
391 m
= m_getl(len
, MB_WAIT
, MT_DATA
, M_PKTHDR
, NULL
);
392 m
->m_len
= m
->m_pkthdr
.len
= len
;
394 m_adj(m
, ETHER_HDR_LEN
);
396 ui
= mtod(m
, struct udpiphdr
*);
397 ui
->ui_pr
= IPPROTO_UDP
;
398 ui
->ui_src
.s_addr
= htonl(saddr
);
399 ui
->ui_dst
.s_addr
= htonl(daddr
);
400 ui
->ui_sport
= htons(sport
);
401 ui
->ui_dport
= htons(dport
);
403 ui
->ui_sum
= in_pseudo(ui
->ui_src
.s_addr
,
404 ui
->ui_dst
.s_addr
, psum
);
405 m
->m_pkthdr
.csum_flags
= (CSUM_IP
| CSUM_UDP
);
406 m
->m_pkthdr
.csum_data
= offsetof(struct udphdr
, uh_sum
);
408 ip
= (struct ip
*)ui
;
410 ip
->ip_ttl
= 64; /* XXX */
411 ip
->ip_tos
= 0; /* XXX */
412 ip
->ip_vhl
= IP_VHL_BORING
;
414 ip
->ip_id
= ip_newid();
416 if (sw_csum
& CSUM_DELAY_DATA
)
418 m
->m_pkthdr
.csum_flags
= csum_flags
;
420 ip
->ip_len
= htons(ip
->ip_len
);
422 if (sw_csum
& CSUM_DELAY_IP
)
423 ip
->ip_sum
= in_cksum_hdr(ip
);
425 M_PREPEND(m
, ETHER_HDR_LEN
, MB_WAIT
);
426 eh
= mtod(m
, struct ether_header
*);
427 bcopy(pktg
->pktg_dst_lladdr
, eh
->ether_dhost
, ETHER_ADDR_LEN
);
428 bcopy(IF_LLADDR(ifp
), eh
->ether_shost
, ETHER_ADDR_LEN
);
429 eh
->ether_type
= htons(ETHERTYPE_IP
);
431 lwkt_serialize_enter(ifp
->if_serializer
);
432 error
= ifq_handoff(ifp
, m
, NULL
);
433 lwkt_serialize_exit(ifp
->if_serializer
);
442 if (loop
== pktg
->pktg_yield
) {
448 saddr
= pktg
->pktg_saddr
+ (r
% pktg
->pktg_nsaddr
);
449 daddr
= pktg
->pktg_daddr
+ (r
% pktg
->pktg_ndaddr
);
450 sport
= pktg
->pktg_sport
+ (r
% pktg
->pktg_nsport
);
451 dport
= pktg
->pktg_dport
+ (r
% pktg
->pktg_ndport
);
454 microtime(&pktg
->pktg_end
);
456 pktgen_thread_exit(pktg
, cnt
, err_cnt
);
460 pktgen_udp_thread(void *arg
)
462 struct pktgen
*pktg
= arg
;
463 struct ifnet
*ifp
= pktg
->pktg_ifp
;
466 struct ether_header
*eh
;
470 int sw_csum
, csum_flags
;
472 uint64_t err_cnt
, cnt
;
473 in_addr_t saddr
, daddr
;
474 u_short sport
, dport
;
476 rel_mplock(); /* Don't need MP lock */
478 callout_reset(&pktg
->pktg_stop
, pktg
->pktg_duration
* hz
,
479 pktgen_stop_cb
, pktg
);
484 ip_len
= pktg
->pktg_datalen
+ sizeof(*ui
);
485 len
= ip_len
+ ETHER_HDR_LEN
;
487 saddr
= htonl(pktg
->pktg_saddr
);
488 daddr
= htonl(pktg
->pktg_daddr
);
489 sport
= htons(pktg
->pktg_sport
);
490 dport
= htons(pktg
->pktg_dport
);
492 sum
= in_pseudo(saddr
, daddr
,
493 htons((u_short
)pktg
->pktg_datalen
+ sizeof(struct udphdr
)
495 ulen
= htons(pktg
->pktg_datalen
+ sizeof(struct udphdr
));
497 sw_csum
= (CSUM_UDP
| CSUM_IP
) & ~ifp
->if_hwassist
;
498 csum_flags
= (CSUM_UDP
| CSUM_IP
) & ifp
->if_hwassist
;
500 microtime(&pktg
->pktg_start
);
501 while ((pktg
->pktg_flags
& PKTG_F_STOP
) == 0) {
502 m
= m_getl(len
, MB_WAIT
, MT_DATA
, M_PKTHDR
, NULL
);
503 m
->m_len
= m
->m_pkthdr
.len
= len
;
505 m_adj(m
, ETHER_HDR_LEN
);
507 ui
= mtod(m
, struct udpiphdr
*);
508 ui
->ui_pr
= IPPROTO_UDP
;
509 ui
->ui_src
.s_addr
= saddr
;
510 ui
->ui_dst
.s_addr
= daddr
;
511 ui
->ui_sport
= sport
;
512 ui
->ui_dport
= dport
;
515 m
->m_pkthdr
.csum_flags
= (CSUM_IP
| CSUM_UDP
);
516 m
->m_pkthdr
.csum_data
= offsetof(struct udphdr
, uh_sum
);
518 ip
= (struct ip
*)ui
;
520 ip
->ip_ttl
= 64; /* XXX */
521 ip
->ip_tos
= 0; /* XXX */
522 ip
->ip_vhl
= IP_VHL_BORING
;
524 ip
->ip_id
= ip_newid();
526 if (sw_csum
& CSUM_DELAY_DATA
)
528 m
->m_pkthdr
.csum_flags
= csum_flags
;
530 ip
->ip_len
= htons(ip
->ip_len
);
532 if (sw_csum
& CSUM_DELAY_IP
)
533 ip
->ip_sum
= in_cksum_hdr(ip
);
535 M_PREPEND(m
, ETHER_HDR_LEN
, MB_WAIT
);
536 eh
= mtod(m
, struct ether_header
*);
537 bcopy(pktg
->pktg_dst_lladdr
, eh
->ether_dhost
, ETHER_ADDR_LEN
);
538 bcopy(IF_LLADDR(ifp
), eh
->ether_shost
, ETHER_ADDR_LEN
);
539 eh
->ether_type
= htons(ETHERTYPE_IP
);
541 lwkt_serialize_enter(ifp
->if_serializer
);
542 error
= ifq_handoff(ifp
, m
, NULL
);
543 lwkt_serialize_exit(ifp
->if_serializer
);
552 if (loop
== pktg
->pktg_yield
) {
558 microtime(&pktg
->pktg_end
);
560 pktgen_thread_exit(pktg
, cnt
, err_cnt
);
564 pktgen_thread_exit(struct pktgen
*pktg
, uint64_t tx_cnt
, uint64_t err_cnt
)
568 pktg
->pktg_tx_cnt
= tx_cnt
;
569 pktg
->pktg_err_cnt
= err_cnt
;
571 end
= pktg
->pktg_end
;
572 timevalsub(&end
, &pktg
->pktg_start
);
573 kprintf("cnt %llu, err %llu, time %ld.%06ld\n",
574 pktg
->pktg_tx_cnt
, pktg
->pktg_err_cnt
, end
.tv_sec
, end
.tv_usec
);
576 pktg
->pktg_flags
&= ~(PKTG_F_STOP
| PKTG_F_CONFIG
| PKTG_F_RUNNING
);
578 KKASSERT(pktg
->pktg_refcnt
> 0);
579 if (--pktg
->pktg_refcnt
== 0)
580 kfree(pktg
, M_PKTGEN
); /* XXX */
582 KKASSERT(pktgen_refcnt
> 0);