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