e337edb383dc1a8d908644fcbfeec1b7e3f366c0
[dragonfly.git] / tools / tools / netrate / pktgen / pktgen.c
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 struct pktgen;
71
72 struct pktgen_buf {
73         struct netmsg_base      pb_nmsg;        /* MUST BE THE FIRST */
74         void                    *pb_buf;
75         volatile int            pb_done;
76         volatile int            pb_inuse;
77         struct ifnet            *pb_ifp;
78         int                     pb_len;
79         int                     pb_cpuid;
80         struct pktgen           *pb_pktg;
81         LIST_ENTRY(pktgen_buf)  pb_link;
82 };
83
84 struct pktgen {
85         uint32_t                pktg_flags;     /* PKTG_F_ */
86         int                     pktg_refcnt;
87
88         struct callout          pktg_stop;
89         int                     pktg_duration;
90
91         int                     pktg_datalen;
92         struct ifnet            *pktg_ifp;
93
94         struct sockaddr_in      pktg_src;
95         int                     pktg_ndst;
96         struct sockaddr_in      *pktg_dst;
97         uint8_t                 pktg_dst_lladdr[ETHER_ADDR_LEN];
98
99         LIST_HEAD(, pktgen_buf) pktg_buflist;
100 };
101
102 #define PKTG_F_CONFIG   0x1
103 #define PKTG_F_RUNNING  0x4
104
105 static int              pktgen_modevent(module_t, int, void *);
106
107 static void             pktgen_buf_free(void *);
108 static void             pktgen_buf_ref(void *);
109 static void             pktgen_buf_send(netmsg_t);
110
111 static int              pktgen_config(struct pktgen *,
112                             const struct pktgen_conf *);
113 static int              pktgen_start(struct pktgen *);
114 static void             pktgen_free(struct pktgen *);
115 static void             pktgen_stop_cb(void *);
116 static void             pktgen_mbuf(struct pktgen_buf *, struct mbuf *);
117
118 static d_open_t         pktgen_open;
119 static d_close_t        pktgen_close;
120 static d_ioctl_t        pktgen_ioctl;
121
122 static struct dev_ops   pktgen_ops = {
123         { CDEV_NAME, 0, D_MPSAFE },
124         .d_open =       pktgen_open,
125         .d_close =      pktgen_close,
126         .d_ioctl =      pktgen_ioctl,
127 };
128
129 static int              pktgen_refcnt;
130 static struct lwkt_token pktgen_tok = LWKT_TOKEN_INITIALIZER(pktgen_token);
131
132 MALLOC_DECLARE(M_PKTGEN);
133 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator");
134
135 DEV_MODULE(pktgen, pktgen_modevent, NULL);
136
137 static int
138 pktgen_modevent(module_t mod, int type, void *data)
139 {
140         int error = 0;
141
142         switch (type) {
143         case MOD_LOAD:
144                 make_dev(&pktgen_ops, 0, UID_ROOT, GID_WHEEL, 0600,
145                     CDEV_NAME"%d", 0);
146                 break;
147
148         case MOD_UNLOAD:
149                 if (pktgen_refcnt > 0)
150                         return EBUSY;
151                 dev_ops_remove_all(&pktgen_ops);
152                 break;
153
154         default:
155                 error = EOPNOTSUPP;
156                 break;
157         }
158         return error;
159 }
160
161 static int
162 pktgen_open(struct dev_open_args *ap)
163 {
164         cdev_t dev = ap->a_head.a_dev;
165         struct pktgen *pktg;
166         int error;
167
168         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
169         if (error)
170                 return error;
171
172         lwkt_gettoken(&pktgen_tok);
173
174         if (dev->si_drv1 != NULL) {
175                 lwkt_reltoken(&pktgen_tok);
176                 return EBUSY;
177         }
178
179         pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK);
180         callout_init_mp(&pktg->pktg_stop);
181         LIST_INIT(&pktg->pktg_buflist);
182
183         dev->si_drv1 = pktg;
184         pktg->pktg_refcnt = 1;
185
186         pktgen_refcnt++;
187
188         lwkt_reltoken(&pktgen_tok);
189         return 0;
190 }
191
192 static int
193 pktgen_close(struct dev_close_args *ap)
194 {
195         cdev_t dev = ap->a_head.a_dev;
196         struct pktgen *pktg = dev->si_drv1;
197
198         lwkt_gettoken(&pktgen_tok);
199
200         dev->si_drv1 = NULL;
201         pktgen_free(pktg);
202
203         lwkt_reltoken(&pktgen_tok);
204
205         return 0;
206 }
207
208 static int
209 pktgen_ioctl(struct dev_ioctl_args *ap __unused)
210 {
211         cdev_t dev = ap->a_head.a_dev;
212         caddr_t data = ap->a_data;
213         struct pktgen *pktg = dev->si_drv1;
214         int error;
215
216         lwkt_gettoken(&pktgen_tok);
217
218         switch (ap->a_cmd) {
219         case PKTGENSTART:
220                 error = pktgen_start(pktg);
221                 break;
222
223         case PKTGENSCONF:
224                 error = pktgen_config(pktg, (const struct pktgen_conf *)data);
225                 break;
226
227         default:
228                 error = EOPNOTSUPP;
229                 break;
230         }
231
232         lwkt_reltoken(&pktgen_tok);
233         return error;
234 }
235
236 static int
237 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf)
238 {
239         const struct sockaddr_in *sin;
240         struct sockaddr_in *dst = NULL;
241         const struct sockaddr *sa;
242         struct ifnet *ifp;
243         size_t dst_size;
244         int i, error;
245
246         if (pktg->pktg_flags & (PKTG_F_RUNNING | PKTG_F_CONFIG))
247                 return EBUSY;
248
249         if (conf->pc_datalen <= 0 ||
250             conf->pc_datalen > ETHERMTU - sizeof(struct udpiphdr))
251                 return EINVAL;
252         if (conf->pc_duration <= 0)
253                 return EINVAL;
254
255         sin = &conf->pc_src;
256         if (sin->sin_family != AF_INET)
257                 return EPROTONOSUPPORT;
258         if (sin->sin_port == 0)
259                 return EINVAL;
260
261         if (conf->pc_ndst <= 0)
262                 return EINVAL;
263         dst_size = conf->pc_ndst * sizeof(struct sockaddr_in);
264
265         dst = kmalloc(dst_size, M_PKTGEN, M_WAITOK | M_NULLOK);
266         if (dst == NULL)
267                 return ENOMEM;
268
269         error = copyin(conf->pc_dst, dst, dst_size);
270         if (error)
271                 goto failed;
272
273         for (i = 0; i < conf->pc_ndst; ++i) {
274                 sin = &dst[i];
275                 if (sin->sin_family != AF_INET) {
276                         error = EPROTONOSUPPORT;
277                         goto failed;
278                 }
279                 if (sin->sin_port == 0) {
280                         error = EINVAL;
281                         goto failed;
282                 }
283         }
284
285         ifp = ifunit(conf->pc_ifname);
286         if (ifp == NULL) {
287                 error = ENXIO;
288                 goto failed;
289         }
290
291         sa = &conf->pc_dst_lladdr;
292         if (sa->sa_family != AF_LINK) {
293                 error = EPROTONOSUPPORT;
294                 goto failed;
295         }
296         if (sa->sa_len != ETHER_ADDR_LEN) {
297                 error = EPROTONOSUPPORT;
298                 goto failed;
299         }
300         if (ETHER_IS_MULTICAST(sa->sa_data) ||
301             bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
302                 error = EADDRNOTAVAIL;
303                 goto failed;
304         }
305
306         /*
307          * Accept the config
308          */
309         pktg->pktg_flags |= PKTG_F_CONFIG;
310
311         pktg->pktg_duration = conf->pc_duration;
312         pktg->pktg_datalen = conf->pc_datalen;
313         pktg->pktg_ifp = ifp;
314         pktg->pktg_src = conf->pc_src;
315         pktg->pktg_ndst = conf->pc_ndst;
316         KKASSERT(pktg->pktg_dst == NULL);
317         pktg->pktg_dst = dst;
318         bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN);
319
320         return 0;
321
322 failed:
323         if (dst != NULL)
324                 kfree(dst, M_PKTGEN);
325         return error;
326 }
327
328 static int
329 pktgen_start(struct pktgen *pktg)
330 {
331         struct mbuf *m, *head = NULL, **next;
332         struct ifnet *ifp;
333         int cpuid, orig_cpuid, i, alloc_cnt, keep_cnt;
334
335         u_short ulen, psum;
336         int len, ip_len;
337
338         if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0)
339                 return EINVAL;
340         if (pktg->pktg_flags & PKTG_F_RUNNING)
341                 return EBUSY;
342         pktg->pktg_flags |= PKTG_F_RUNNING;
343
344         ifp = pktg->pktg_ifp;
345
346         orig_cpuid = mycpuid;
347         cpuid = ifp->if_start_cpuid(ifp);
348         if (cpuid != orig_cpuid)
349                 lwkt_migratecpu(cpuid);
350
351         alloc_cnt = ifp->if_snd.ifq_maxlen * 2;
352         keep_cnt = (ifp->if_snd.ifq_maxlen * 7) / 8;
353
354         /*
355          * Prefault enough mbuf into mbuf objcache
356          */
357         next = &head;
358         for (i = 0; i < alloc_cnt; ++i) {
359                 MGETHDR(m, MB_WAIT, MT_DATA);
360                 *next = m;
361                 next = &m->m_nextpkt;
362         }
363
364         for (i = 0; i < alloc_cnt - keep_cnt; ++i) {
365                 m = head;
366                 head = m->m_nextpkt;
367                 m->m_nextpkt = NULL;
368                 m_freem(m);
369         }
370         KKASSERT(head != NULL);
371
372         /*
373          * Setup the packets' data
374          */
375         ip_len = pktg->pktg_datalen + sizeof(struct udpiphdr);
376         len = ip_len + ETHER_HDR_LEN;
377
378         psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) +
379             IPPROTO_UDP);
380         ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr));
381
382         m = head;
383         i = 0;
384         while (m != NULL) {
385                 struct mbuf *nextm;
386                 const struct sockaddr_in *dst;
387                 struct pktgen_buf *pb;
388                 struct ip *ip;
389                 struct udpiphdr *ui;
390                 struct ether_header *eh;
391
392                 pktg->pktg_refcnt++;
393                 pktgen_refcnt++;
394
395                 pb = kmalloc(sizeof(*pb), M_PKTGEN, M_WAITOK | M_ZERO);
396                 pb->pb_ifp = ifp;
397                 pb->pb_inuse = 1;
398                 pb->pb_buf = kmalloc(PKTGEN_BUFSZ, M_PKTGEN, M_WAITOK);
399                 pb->pb_len = len;
400                 pb->pb_cpuid = cpuid;
401                 pb->pb_pktg = pktg;
402                 netmsg_init(&pb->pb_nmsg, NULL, &netisr_adone_rport, 0,
403                     pktgen_buf_send);
404                 LIST_INSERT_HEAD(&pktg->pktg_buflist, pb, pb_link);
405
406                 dst = &pktg->pktg_dst[i % pktg->pktg_ndst];
407                 ++i;
408
409                 m->m_ext.ext_arg = pb;
410                 m->m_ext.ext_buf = pb->pb_buf;
411                 m->m_ext.ext_free = pktgen_buf_free;
412                 m->m_ext.ext_ref = pktgen_buf_ref;
413                 m->m_ext.ext_size = PKTGEN_BUFSZ;
414
415                 m->m_data = m->m_ext.ext_buf;
416                 m->m_flags |= M_EXT;
417                 m->m_len = m->m_pkthdr.len = len;
418
419                 m->m_data += ETHER_HDR_LEN;
420                 m->m_len -= ETHER_HDR_LEN;
421                 m->m_pkthdr.len -= ETHER_HDR_LEN;
422
423                 ui = mtod(m, struct udpiphdr *);
424                 ui->ui_pr = IPPROTO_UDP;
425                 ui->ui_src.s_addr = pktg->pktg_src.sin_addr.s_addr;
426                 ui->ui_dst.s_addr = dst->sin_addr.s_addr;
427                 ui->ui_sport = pktg->pktg_src.sin_port;
428                 ui->ui_dport = dst->sin_port;
429                 ui->ui_ulen = ulen;
430                 ui->ui_sum = in_pseudo(ui->ui_src.s_addr, ui->ui_dst.s_addr,
431                     psum);
432                 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
433
434                 ip = (struct ip *)ui;
435                 ip->ip_len = ip_len;
436                 ip->ip_ttl = 64;        /* XXX */
437                 ip->ip_tos = 0;         /* XXX */
438                 ip->ip_vhl = IP_VHL_BORING;
439                 ip->ip_off = 0;
440                 ip->ip_sum = 0;
441                 ip->ip_id = ip_newid();
442
443                 in_delayed_cksum(m);
444
445                 ip->ip_len = htons(ip->ip_len);
446                 ip->ip_sum = in_cksum_hdr(ip);
447
448                 m->m_data -= ETHER_HDR_LEN;
449                 m->m_len += ETHER_HDR_LEN;
450                 m->m_pkthdr.len += ETHER_HDR_LEN;
451
452                 eh = mtod(m, struct ether_header *);
453                 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN);
454                 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
455                 eh->ether_type = htons(ETHERTYPE_IP);
456
457                 nextm = m->m_nextpkt;
458                 m->m_nextpkt = NULL;
459
460                 ifq_dispatch(ifp, m, NULL);
461
462                 m = nextm;
463         }
464
465         callout_reset(&pktg->pktg_stop, pktg->pktg_duration * hz,
466             pktgen_stop_cb, pktg);
467
468         if (cpuid != orig_cpuid)
469                 lwkt_migratecpu(orig_cpuid);
470
471         return 0;
472 }
473
474 static void
475 pktgen_mbuf(struct pktgen_buf *pb, struct mbuf *m)
476 {
477         m->m_ext.ext_arg = pb;
478         m->m_ext.ext_buf = pb->pb_buf;
479         m->m_ext.ext_free = pktgen_buf_free;
480         m->m_ext.ext_ref = pktgen_buf_ref;
481         m->m_ext.ext_size = PKTGEN_BUFSZ;
482
483         m->m_data = m->m_ext.ext_buf;
484         m->m_flags |= M_EXT;
485         m->m_len = m->m_pkthdr.len = pb->pb_len;
486 }
487
488 static void
489 pktgen_buf_send(netmsg_t msg)
490 {
491         struct pktgen_buf *pb = (struct pktgen_buf *)msg;
492         struct mbuf *m;
493
494         KKASSERT(&curthread->td_msgport == netisr_portfn(pb->pb_cpuid));
495
496         crit_enter();
497         lwkt_replymsg(&pb->pb_nmsg.lmsg, 0);
498         crit_exit();
499
500         MGETHDR(m, MB_WAIT, MT_DATA);
501         pktgen_mbuf(pb, m);
502         ifq_dispatch(pb->pb_ifp, m, NULL);
503 }
504
505 static void
506 pktgen_buf_free(void *arg)
507 {
508         struct pktgen_buf *pb = arg;
509         struct mbuf *m;
510
511         KKASSERT(pb->pb_inuse > 0);
512         if (pb->pb_done) {
513                 if (atomic_fetchadd_int(&pb->pb_inuse, -1) == 1) {
514                         struct pktgen *pktg;
515
516                         lwkt_gettoken(&pktgen_tok);
517
518                         pktg = pb->pb_pktg;
519                         LIST_REMOVE(pb, pb_link);
520                         kfree(pb->pb_buf, M_PKTGEN);
521                         kfree(pb, M_PKTGEN);
522
523                         pktgen_free(pktg);
524
525                         lwkt_reltoken(&pktgen_tok);
526                 }
527                 return;
528         }
529
530         if (&curthread->td_msgport != netisr_portfn(pb->pb_cpuid)) {
531                 KKASSERT(pb->pb_cpuid == mycpuid);
532                 crit_enter();
533                 KKASSERT(pb->pb_nmsg.lmsg.ms_flags & MSGF_DONE);
534                 lwkt_sendmsg(netisr_portfn(pb->pb_cpuid), &pb->pb_nmsg.lmsg);
535                 crit_exit();
536                 return;
537         }
538
539         MGETHDR(m, MB_WAIT, MT_DATA);
540         pktgen_mbuf(pb, m);
541         ifq_enqueue(&pb->pb_ifp->if_snd, m, NULL);
542 }
543
544 static void
545 pktgen_buf_ref(void *arg)
546 {
547         struct pktgen_buf *pb = arg;
548
549         panic("%s should never be called\n", __func__);
550
551         KKASSERT(pb->pb_inuse > 0);
552         atomic_add_int(&pb->pb_inuse, 1);
553 }
554
555 static void
556 pktgen_free(struct pktgen *pktg)
557 {
558         KKASSERT(pktg->pktg_refcnt > 0);
559         if (--pktg->pktg_refcnt == 0) {
560                 if (pktg->pktg_dst != NULL)
561                         kfree(pktg->pktg_dst, M_PKTGEN);
562                 KKASSERT(LIST_EMPTY(&pktg->pktg_buflist));
563                 kfree(pktg, M_PKTGEN);
564         }
565
566         KKASSERT(pktgen_refcnt > 0);
567         pktgen_refcnt--;
568 }
569
570 static void
571 pktgen_stop_cb(void *arg)
572 {
573         struct pktgen *pktg = arg;
574         struct pktgen_buf *pb;
575
576         lwkt_gettoken(&pktgen_tok);
577         LIST_FOREACH(pb, &pktg->pktg_buflist, pb_link)
578                 pb->pb_done = 1;
579         lwkt_reltoken(&pktgen_tok);
580 }