1/* -*-linux-c-*-
2 * $Id: pktgen.c,v 1.1.1.1 2008/10/15 03:27:33 james26_jang Exp $
3 * pktgen.c: Packet Generator for performance evaluation.
4 *
5 * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
6 *                                 Uppsala University, Sweden
7 *
8 * A tool for loading the network with preconfigurated packets.
9 * The tool is implemented as a linux module.  Parameters are output
10 * device, IPG (interpacket gap), number of packets, and whether
11 * to use multiple SKBs or just the same one.
12 * pktgen uses the installed interface's output routine.
13 *
14 * Additional hacking by:
15 *
16 * Jens.Laas@data.slu.se
17 * Improved by ANK. 010120.
18 * Improved by ANK even more. 010212.
19 * MAC address typo fixed. 010417 --ro
20 * Integrated.  020301 --DaveM
21 * Added multiskb option 020301 --DaveM
22 * Scaling of results. 020417--sigurdur@linpro.no
23 * Significant re-work of the module:
24 *   *  Updated to support generation over multiple interfaces at once
25 *       by creating 32 /proc/net/pg* files.  Each file can be manipulated
26 *       individually.
27 *   *  Converted many counters to __u64 to allow longer runs.
28 *   *  Allow configuration of ranges, like min/max IP address, MACs,
29 *       and UDP-ports, for both source and destination, and can
30 *       set to use a random distribution or sequentially walk the range.
31 *   *  Can now change some values after starting.
32 *   *  Place 12-byte packet in UDP payload with magic number,
33 *       sequence number, and timestamp.  Will write receiver next.
34 *   *  The new changes seem to have a performance impact of around 1%,
35 *       as far as I can tell.
36 *   --Ben Greear <greearb@candelatech.com>
37 *
38 * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
39 * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
40 * as a "fastpath" with a configurable number of clones after alloc's.
41 *
42 * clone_skb=0 means all packets are allocated this also means ranges time
43 * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
44 * clones.
45 *
46 * Also moved to /proc/net/pktgen/
47 * --ro
48 *
49 * See Documentation/networking/pktgen.txt for how to use this.
50 */
51
52#include <linux/module.h>
53#include <linux/kernel.h>
54#include <linux/sched.h>
55#include <linux/types.h>
56#include <linux/string.h>
57#include <linux/ptrace.h>
58#include <linux/errno.h>
59#include <linux/ioport.h>
60#include <linux/slab.h>
61#include <linux/interrupt.h>
62#include <linux/pci.h>
63#include <linux/delay.h>
64#include <linux/init.h>
65#include <linux/inet.h>
66#include <asm/byteorder.h>
67#include <asm/bitops.h>
68#include <asm/io.h>
69#include <asm/dma.h>
70#include <asm/uaccess.h>
71
72#include <linux/in.h>
73#include <linux/ip.h>
74#include <linux/udp.h>
75#include <linux/skbuff.h>
76#include <linux/netdevice.h>
77#include <linux/inetdevice.h>
78#include <linux/rtnetlink.h>
79#include <linux/proc_fs.h>
80#include <linux/if_arp.h>
81#include <net/checksum.h>
82#include <asm/timex.h>
83
84#define cycles()	((u32)get_cycles())
85
86
87#define VERSION "pktgen version 1.2"
88static char version[] __initdata =
89  "pktgen.c: v1.2: Packet Generator for packet performance testing.\n";
90
91/* Used to help with determining the pkts on receive */
92
93#define PKTGEN_MAGIC 0xbe9be955
94
95
96/* Keep information per interface */
97struct pktgen_info {
98        /* Parameters */
99
100        /* If min != max, then we will either do a linear iteration, or
101         * we will do a random selection from within the range.
102         */
103        __u32 flags;
104
105#define F_IPSRC_RND   (1<<0)  /* IP-Src Random  */
106#define F_IPDST_RND   (1<<1)  /* IP-Dst Random  */
107#define F_UDPSRC_RND  (1<<2)  /* UDP-Src Random */
108#define F_UDPDST_RND  (1<<3)  /* UDP-Dst Random */
109#define F_MACSRC_RND  (1<<4)  /* MAC-Src Random */
110#define F_MACDST_RND  (1<<5)  /* MAC-Dst Random */
111#define F_SET_SRCMAC  (1<<6)  /* Specify-Src-Mac
112				 (default is to use Interface's MAC Addr) */
113#define F_SET_SRCIP   (1<<7)  /*  Specify-Src-IP
114				  (default is to use Interface's IP Addr) */
115
116
117        int pkt_size;    /* = ETH_ZLEN; */
118        int nfrags;
119        __u32 ipg;       /* Default Interpacket gap in nsec */
120        __u64 count;     /* Default No packets to send */
121        __u64 sofar;     /* How many pkts we've sent so far */
122        __u64 errors;    /* Errors when trying to transmit, pkts will be re-sent */
123        struct timeval started_at;
124        struct timeval stopped_at;
125        __u64 idle_acc;
126        __u32 seq_num;
127
128        int clone_skb;   /* Use multiple SKBs during packet gen.  If this number
129                          * is greater than 1, then that many coppies of the same
130                          * packet will be sent before a new packet is allocated.
131                          * For instance, if you want to send 1024 identical packets
132                          * before creating a new packet, set clone_skb to 1024.
133                          */
134        int busy;
135        int do_run_run;   /* if this changes to false, the test will stop */
136
137        char outdev[32];
138        char dst_min[32];
139        char dst_max[32];
140        char src_min[32];
141        char src_max[32];
142
143        /* If we're doing ranges, random or incremental, then this
144         * defines the min/max for those ranges.
145         */
146        __u32 saddr_min; /* inclusive, source IP address */
147        __u32 saddr_max; /* exclusive, source IP address */
148        __u32 daddr_min; /* inclusive, dest IP address */
149        __u32 daddr_max; /* exclusive, dest IP address */
150
151        __u16 udp_src_min; /* inclusive, source UDP port */
152        __u16 udp_src_max; /* exclusive, source UDP port */
153        __u16 udp_dst_min; /* inclusive, dest UDP port */
154        __u16 udp_dst_max; /* exclusive, dest UDP port */
155
156        __u32 src_mac_count; /* How many MACs to iterate through */
157        __u32 dst_mac_count; /* How many MACs to iterate through */
158
159        unsigned char dst_mac[6];
160        unsigned char src_mac[6];
161
162        __u32 cur_dst_mac_offset;
163        __u32 cur_src_mac_offset;
164        __u32 cur_saddr;
165        __u32 cur_daddr;
166        __u16 cur_udp_dst;
167        __u16 cur_udp_src;
168
169        __u8 hh[14];
170        /* = {
171           0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
172
173           We fill in SRC address later
174           0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175           0x08, 0x00
176           };
177        */
178        __u16 pad; /* pad out the hh struct to an even 16 bytes */
179        char result[512];
180
181        /* proc file names */
182        char fname[80];
183        char busy_fname[80];
184
185        struct proc_dir_entry *proc_ent;
186        struct proc_dir_entry *busy_proc_ent;
187};
188
189struct pktgen_hdr {
190        __u32 pgh_magic;
191        __u32 seq_num;
192        struct timeval timestamp;
193};
194
195static int cpu_speed;
196static int debug;
197
198/* Module parameters, defaults. */
199static int count_d = 100000;
200static int ipg_d = 0;
201static int clone_skb_d = 0;
202
203
204#define MAX_PKTGEN 8
205static struct pktgen_info pginfos[MAX_PKTGEN];
206
207
208/** Convert to miliseconds */
209inline __u64 tv_to_ms(const struct timeval* tv) {
210        __u64 ms = tv->tv_usec / 1000;
211        ms += (__u64)tv->tv_sec * (__u64)1000;
212        return ms;
213}
214
215inline __u64 getCurMs(void) {
216        struct timeval tv;
217        do_gettimeofday(&tv);
218        return tv_to_ms(&tv);
219}
220
221#define PG_PROC_DIR "pktgen"
222static struct proc_dir_entry *proc_dir = 0;
223
224static struct net_device *setup_inject(struct pktgen_info* info)
225{
226	struct net_device *odev;
227
228	rtnl_lock();
229	odev = __dev_get_by_name(info->outdev);
230	if (!odev) {
231		sprintf(info->result, "No such netdevice: \"%s\"", info->outdev);
232		goto out_unlock;
233	}
234
235	if (odev->type != ARPHRD_ETHER) {
236		sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev);
237		goto out_unlock;
238	}
239
240	if (!netif_running(odev)) {
241		sprintf(info->result, "Device is down: \"%s\"", info->outdev);
242		goto out_unlock;
243	}
244
245        /* Default to the interface's mac if not explicitly set. */
246        if (!(info->flags & F_SET_SRCMAC)) {
247                memcpy(&(info->hh[6]), odev->dev_addr, 6);
248        }
249        else {
250                memcpy(&(info->hh[6]), info->src_mac, 6);
251        }
252
253        /* Set up Dest MAC */
254        memcpy(&(info->hh[0]), info->dst_mac, 6);
255
256	info->saddr_min = 0;
257	info->saddr_max = 0;
258        if (strlen(info->src_min) == 0) {
259                if (odev->ip_ptr) {
260                        struct in_device *in_dev = odev->ip_ptr;
261
262                        if (in_dev->ifa_list) {
263                                info->saddr_min = in_dev->ifa_list->ifa_address;
264                                info->saddr_max = info->saddr_min;
265                        }
266                }
267	}
268        else {
269                info->saddr_min = in_aton(info->src_min);
270                info->saddr_max = in_aton(info->src_max);
271        }
272
273        info->daddr_min = in_aton(info->dst_min);
274        info->daddr_max = in_aton(info->dst_max);
275
276        /* Initialize current values. */
277        info->cur_dst_mac_offset = 0;
278        info->cur_src_mac_offset = 0;
279        info->cur_saddr = info->saddr_min;
280        info->cur_daddr = info->daddr_min;
281        info->cur_udp_dst = info->udp_dst_min;
282        info->cur_udp_src = info->udp_src_min;
283
284	atomic_inc(&odev->refcnt);
285	rtnl_unlock();
286
287	return odev;
288
289out_unlock:
290	rtnl_unlock();
291	return NULL;
292}
293
294static void nanospin(int ipg, struct pktgen_info* info)
295{
296	u32 idle_start, idle;
297
298	idle_start = cycles();
299
300	for (;;) {
301		barrier();
302		idle = cycles() - idle_start;
303		if (idle * 1000 >= ipg * cpu_speed)
304			break;
305	}
306	info->idle_acc += idle;
307}
308
309static int calc_mhz(void)
310{
311	struct timeval start, stop;
312	u32 start_s, elapsed;
313
314	do_gettimeofday(&start);
315	start_s = cycles();
316	do {
317		barrier();
318		elapsed = cycles() - start_s;
319		if (elapsed == 0)
320			return 0;
321	} while (elapsed < 1000 * 50000);
322	do_gettimeofday(&stop);
323	return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
324}
325
326static void cycles_calibrate(void)
327{
328	int i;
329
330	for (i = 0; i < 3; i++) {
331		int res = calc_mhz();
332		if (res > cpu_speed)
333			cpu_speed = res;
334	}
335}
336
337
338/* Increment/randomize headers according to flags and current values
339 * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
340 */
341static void mod_cur_headers(struct pktgen_info* info) {
342        __u32 imn;
343        __u32 imx;
344
345	/*  Deal with source MAC */
346        if (info->src_mac_count > 1) {
347                __u32 mc;
348                __u32 tmp;
349                if (info->flags & F_MACSRC_RND) {
350                        mc = net_random() % (info->src_mac_count);
351                }
352                else {
353                        mc = info->cur_src_mac_offset++;
354                        if (info->cur_src_mac_offset > info->src_mac_count) {
355                                info->cur_src_mac_offset = 0;
356                        }
357                }
358
359                tmp = info->src_mac[5] + (mc & 0xFF);
360                info->hh[11] = tmp;
361                tmp = (info->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
362                info->hh[10] = tmp;
363                tmp = (info->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
364                info->hh[9] = tmp;
365                tmp = (info->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
366                info->hh[8] = tmp;
367                tmp = (info->src_mac[1] + (tmp >> 8));
368                info->hh[7] = tmp;
369        }
370
371        /*  Deal with Destination MAC */
372        if (info->dst_mac_count > 1) {
373                __u32 mc;
374                __u32 tmp;
375                if (info->flags & F_MACDST_RND) {
376                        mc = net_random() % (info->dst_mac_count);
377                }
378                else {
379                        mc = info->cur_dst_mac_offset++;
380                        if (info->cur_dst_mac_offset > info->dst_mac_count) {
381                                info->cur_dst_mac_offset = 0;
382                        }
383                }
384
385                tmp = info->dst_mac[5] + (mc & 0xFF);
386                info->hh[5] = tmp;
387                tmp = (info->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
388                info->hh[4] = tmp;
389                tmp = (info->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
390                info->hh[3] = tmp;
391                tmp = (info->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
392                info->hh[2] = tmp;
393                tmp = (info->dst_mac[1] + (tmp >> 8));
394                info->hh[1] = tmp;
395        }
396
397        if (info->udp_src_min < info->udp_src_max) {
398                if (info->flags & F_UDPSRC_RND) {
399                        info->cur_udp_src = ((net_random() % (info->udp_src_max - info->udp_src_min))
400                                             + info->udp_src_min);
401                }
402                else {
403                     info->cur_udp_src++;
404                     if (info->cur_udp_src >= info->udp_src_max) {
405                             info->cur_udp_src = info->udp_src_min;
406                     }
407                }
408        }
409
410        if (info->udp_dst_min < info->udp_dst_max) {
411                if (info->flags & F_UDPDST_RND) {
412                        info->cur_udp_dst = ((net_random() % (info->udp_dst_max - info->udp_dst_min))
413                                             + info->udp_dst_min);
414                }
415                else {
416                     info->cur_udp_dst++;
417                     if (info->cur_udp_dst >= info->udp_dst_max) {
418                             info->cur_udp_dst = info->udp_dst_min;
419                     }
420                }
421        }
422
423        if ((imn = ntohl(info->saddr_min)) < (imx = ntohl(info->saddr_max))) {
424                __u32 t;
425                if (info->flags & F_IPSRC_RND) {
426                        t = ((net_random() % (imx - imn)) + imn);
427                }
428                else {
429                     t = ntohl(info->cur_saddr);
430                     t++;
431                     if (t >= imx) {
432                             t = imn;
433                     }
434                }
435                info->cur_saddr = htonl(t);
436        }
437
438        if ((imn = ntohl(info->daddr_min)) < (imx = ntohl(info->daddr_max))) {
439                __u32 t;
440                if (info->flags & F_IPDST_RND) {
441                        t = ((net_random() % (imx - imn)) + imn);
442                }
443                else {
444                     t = ntohl(info->cur_daddr);
445                     t++;
446                     if (t >= imx) {
447                             t = imn;
448                     }
449                }
450                info->cur_daddr = htonl(t);
451        }
452}/* mod_cur_headers */
453
454
455static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_info* info)
456{
457	struct sk_buff *skb = NULL;
458	__u8 *eth;
459	struct udphdr *udph;
460	int datalen, iplen;
461	struct iphdr *iph;
462        struct pktgen_hdr *pgh = NULL;
463
464	skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC);
465	if (!skb) {
466		sprintf(info->result, "No memory");
467		return NULL;
468	}
469
470	skb_reserve(skb, 16);
471
472	/*  Reserve for ethernet and IP header  */
473	eth = (__u8 *) skb_push(skb, 14);
474	iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
475	udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
476
477        /* Update any of the values, used when we're incrementing various
478         * fields.
479         */
480        mod_cur_headers(info);
481
482	memcpy(eth, info->hh, 14);
483
484	datalen = info->pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
485	if (datalen < sizeof(struct pktgen_hdr)) {
486		datalen = sizeof(struct pktgen_hdr);
487        }
488
489	udph->source = htons(info->cur_udp_src);
490	udph->dest = htons(info->cur_udp_dst);
491	udph->len = htons(datalen + 8); /* DATA + udphdr */
492	udph->check = 0;  /* No checksum */
493
494	iph->ihl = 5;
495	iph->version = 4;
496	iph->ttl = 3;
497	iph->tos = 0;
498	iph->protocol = IPPROTO_UDP; /* UDP */
499	iph->saddr = info->cur_saddr;
500	iph->daddr = info->cur_daddr;
501	iph->frag_off = 0;
502	iplen = 20 + 8 + datalen;
503	iph->tot_len = htons(iplen);
504	iph->check = 0;
505	iph->check = ip_fast_csum((void *) iph, iph->ihl);
506	skb->protocol = __constant_htons(ETH_P_IP);
507	skb->mac.raw = ((u8 *)iph) - 14;
508	skb->dev = odev;
509	skb->pkt_type = PACKET_HOST;
510
511	if (info->nfrags <= 0) {
512                pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
513	} else {
514		int frags = info->nfrags;
515		int i;
516
517                /* TODO: Verify this is OK...it sure is ugly. --Ben */
518                pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
519
520		if (frags > MAX_SKB_FRAGS)
521			frags = MAX_SKB_FRAGS;
522		if (datalen > frags*PAGE_SIZE) {
523			skb_put(skb, datalen-frags*PAGE_SIZE);
524			datalen = frags*PAGE_SIZE;
525		}
526
527		i = 0;
528		while (datalen > 0) {
529			struct page *page = alloc_pages(GFP_KERNEL, 0);
530			skb_shinfo(skb)->frags[i].page = page;
531			skb_shinfo(skb)->frags[i].page_offset = 0;
532			skb_shinfo(skb)->frags[i].size =
533				(datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
534			datalen -= skb_shinfo(skb)->frags[i].size;
535			skb->len += skb_shinfo(skb)->frags[i].size;
536			skb->data_len += skb_shinfo(skb)->frags[i].size;
537			i++;
538			skb_shinfo(skb)->nr_frags = i;
539		}
540
541		while (i < frags) {
542			int rem;
543
544			if (i == 0)
545				break;
546
547			rem = skb_shinfo(skb)->frags[i - 1].size / 2;
548			if (rem == 0)
549				break;
550
551			skb_shinfo(skb)->frags[i - 1].size -= rem;
552
553			skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
554			get_page(skb_shinfo(skb)->frags[i].page);
555			skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i - 1].page;
556			skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i - 1].size;
557			skb_shinfo(skb)->frags[i].size = rem;
558			i++;
559			skb_shinfo(skb)->nr_frags = i;
560		}
561	}
562
563        /* Stamp the time, and sequence number, convert them to network byte order */
564        if (pgh) {
565                pgh->pgh_magic = htonl(PKTGEN_MAGIC);
566                do_gettimeofday(&(pgh->timestamp));
567                pgh->timestamp.tv_usec = htonl(pgh->timestamp.tv_usec);
568                pgh->timestamp.tv_sec = htonl(pgh->timestamp.tv_sec);
569                pgh->seq_num = htonl(info->seq_num);
570        }
571
572	return skb;
573}
574
575
576static void inject(struct pktgen_info* info)
577{
578	struct net_device *odev = NULL;
579	struct sk_buff *skb = NULL;
580	__u64 total = 0;
581        __u64 idle = 0;
582	__u64 lcount = 0;
583        int nr_frags = 0;
584	int last_ok = 1;           /* Was last skb sent?
585	                            * Or a failed transmit of some sort?  This will keep
586                                    * sequence numbers in order, for example.
587                                    */
588        __u64 fp = 0;
589        __u32 fp_tmp = 0;
590
591	odev = setup_inject(info);
592	if (!odev)
593		return;
594
595        info->do_run_run = 1; /* Cranke yeself! */
596	info->idle_acc = 0;
597	info->sofar = 0;
598	lcount = info->count;
599
600
601        /* Build our initial pkt and place it as a re-try pkt. */
602	skb = fill_packet(odev, info);
603	if (skb == NULL) goto out_reldev;
604
605	do_gettimeofday(&(info->started_at));
606
607	while(info->do_run_run) {
608
609                /* Set a time-stamp, so build a new pkt each time */
610
611                if (last_ok) {
612                        if (++fp_tmp >= info->clone_skb ) {
613                                kfree_skb(skb);
614                                skb = fill_packet(odev, info);
615                                if (skb == NULL) {
616                                        break;
617                                }
618                                fp++;
619                                fp_tmp = 0; /* reset counter */
620                        }
621                        atomic_inc(&skb->users);
622                }
623
624                nr_frags = skb_shinfo(skb)->nr_frags;
625
626		spin_lock_bh(&odev->xmit_lock);
627		if (!netif_queue_stopped(odev)) {
628
629			if (odev->hard_start_xmit(skb, odev)) {
630				if (net_ratelimit()) {
631                                   printk(KERN_INFO "Hard xmit error\n");
632                                }
633                                info->errors++;
634				last_ok = 0;
635			}
636                        else {
637		           last_ok = 1;
638                           info->sofar++;
639                           info->seq_num++;
640                        }
641		}
642		else {
643                        /* Re-try it next time */
644			last_ok = 0;
645                }
646
647
648		spin_unlock_bh(&odev->xmit_lock);
649
650		if (info->ipg) {
651                        /* Try not to busy-spin if we have larger sleep times.
652                         * TODO:  Investigate better ways to do this.
653                         */
654                        if (info->ipg < 10000) { /* 10 usecs or less */
655                                nanospin(info->ipg, info);
656                        }
657                        else if (info->ipg < 10000000) { /* 10ms or less */
658                                udelay(info->ipg / 1000);
659                        }
660                        else {
661                                mdelay(info->ipg / 1000000);
662                        }
663                }
664
665		if (signal_pending(current)) {
666                        break;
667                }
668
669                /* If lcount is zero, then run forever */
670		if ((lcount != 0) && (--lcount == 0)) {
671			if (atomic_read(&skb->users) != 1) {
672				u32 idle_start, idle;
673
674				idle_start = cycles();
675				while (atomic_read(&skb->users) != 1) {
676					if (signal_pending(current)) {
677                                                break;
678                                        }
679					schedule();
680				}
681				idle = cycles() - idle_start;
682				info->idle_acc += idle;
683			}
684			break;
685		}
686
687		if (netif_queue_stopped(odev) || current->need_resched) {
688			u32 idle_start, idle;
689
690			idle_start = cycles();
691			do {
692				if (signal_pending(current)) {
693                                        info->do_run_run = 0;
694                                        break;
695                                }
696				if (!netif_running(odev)) {
697                                        info->do_run_run = 0;
698					break;
699                                }
700				if (current->need_resched)
701					schedule();
702				else
703					do_softirq();
704			} while (netif_queue_stopped(odev));
705			idle = cycles() - idle_start;
706			info->idle_acc += idle;
707		}
708	}/* while we should be running */
709
710	do_gettimeofday(&(info->stopped_at));
711
712	total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 +
713		info->stopped_at.tv_usec - info->started_at.tv_usec;
714
715	idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed);
716
717        {
718		char *p = info->result;
719                __u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000);
720                __u64 bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */
721		p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) %llupps %lluMb/sec (%llubps)  errors: %llu",
722			     (unsigned long long) total,
723			     (unsigned long long) (total - idle),
724			     (unsigned long long) idle,
725			     (unsigned long long) info->sofar,
726                             skb->len + 4, /* Add 4 to account for the ethernet checksum */
727                             nr_frags,
728			     (unsigned long long) pps,
729			     (unsigned long long) (bps / (u64) 1024 / (u64) 1024),
730			     (unsigned long long) bps,
731			     (unsigned long long) info->errors
732			     );
733	}
734
735out_reldev:
736        if (odev) {
737                dev_put(odev);
738                odev = NULL;
739        }
740
741        /* TODO:  Is this worth printing out (other than for debug?) */
742        printk("fp = %llu\n", (unsigned long long) fp);
743	return;
744
745}
746
747/* proc/net/pktgen/pg */
748
749static int proc_busy_read(char *buf , char **start, off_t offset,
750			     int len, int *eof, void *data)
751{
752	char *p;
753        int idx = (int)(long)(data);
754        struct pktgen_info* info = NULL;
755
756        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
757                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
758                return -EINVAL;
759        }
760        info = &(pginfos[idx]);
761
762	p = buf;
763	p += sprintf(p, "%d\n", info->busy);
764	*eof = 1;
765
766	return p-buf;
767}
768
769static int proc_read(char *buf , char **start, off_t offset,
770			int len, int *eof, void *data)
771{
772	char *p;
773	int i;
774        int idx = (int)(long)(data);
775        struct pktgen_info* info = NULL;
776        __u64 sa;
777        __u64 stopped;
778        __u64 now = getCurMs();
779
780        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
781                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
782                return -EINVAL;
783        }
784        info = &(pginfos[idx]);
785
786	p = buf;
787        p += sprintf(p, "%s\n", VERSION); /* Help with parsing compatibility */
788	p += sprintf(p, "Params: count %llu  pkt_size: %u  frags: %d  ipg: %u  clone_skb: %d odev \"%s\"\n",
789		     (unsigned long long) info->count,
790		     info->pkt_size, info->nfrags, info->ipg,
791                     info->clone_skb, info->outdev);
792        p += sprintf(p, "     dst_min: %s  dst_max: %s  src_min: %s  src_max: %s\n",
793                     info->dst_min, info->dst_max, info->src_min, info->src_max);
794        p += sprintf(p, "     src_mac: ");
795	for (i = 0; i < 6; i++) {
796		p += sprintf(p, "%02X%s", info->src_mac[i], i == 5 ? "  " : ":");
797        }
798        p += sprintf(p, "dst_mac: ");
799	for (i = 0; i < 6; i++) {
800		p += sprintf(p, "%02X%s", info->dst_mac[i], i == 5 ? "\n" : ":");
801        }
802        p += sprintf(p, "     udp_src_min: %d  udp_src_max: %d  udp_dst_min: %d  udp_dst_max: %d\n",
803                     info->udp_src_min, info->udp_src_max, info->udp_dst_min,
804                     info->udp_dst_max);
805        p += sprintf(p, "     src_mac_count: %d  dst_mac_count: %d\n     Flags: ",
806                     info->src_mac_count, info->dst_mac_count);
807        if (info->flags &  F_IPSRC_RND) {
808                p += sprintf(p, "IPSRC_RND  ");
809        }
810        if (info->flags & F_IPDST_RND) {
811                p += sprintf(p, "IPDST_RND  ");
812        }
813        if (info->flags & F_UDPSRC_RND) {
814                p += sprintf(p, "UDPSRC_RND  ");
815        }
816        if (info->flags & F_UDPDST_RND) {
817                p += sprintf(p, "UDPDST_RND  ");
818        }
819        if (info->flags & F_MACSRC_RND) {
820                p += sprintf(p, "MACSRC_RND  ");
821        }
822        if (info->flags & F_MACDST_RND) {
823                p += sprintf(p, "MACDST_RND  ");
824        }
825        p += sprintf(p, "\n");
826
827        sa = tv_to_ms(&(info->started_at));
828        stopped = tv_to_ms(&(info->stopped_at));
829        if (info->do_run_run) {
830                stopped = now; /* not really stopped, more like last-running-at */
831        }
832        p += sprintf(p, "Current:\n     pkts-sofar: %llu  errors: %llu\n     started: %llums  stopped: %llums  now: %llums  idle: %lluns\n",
833                     (unsigned long long) info->sofar,
834		     (unsigned long long) info->errors,
835		     (unsigned long long) sa,
836		     (unsigned long long) stopped,
837		     (unsigned long long) now,
838		     (unsigned long long) info->idle_acc);
839        p += sprintf(p, "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
840                     info->seq_num, info->cur_dst_mac_offset, info->cur_src_mac_offset);
841        p += sprintf(p, "     cur_saddr: 0x%x  cur_daddr: 0x%x  cur_udp_dst: %d  cur_udp_src: %d\n",
842                     info->cur_saddr, info->cur_daddr, info->cur_udp_dst, info->cur_udp_src);
843
844	if (info->result[0])
845		p += sprintf(p, "Result: %s\n", info->result);
846	else
847		p += sprintf(p, "Result: Idle\n");
848	*eof = 1;
849
850	return p - buf;
851}
852
853static int count_trail_chars(const char *user_buffer, unsigned int maxlen)
854{
855	int i;
856
857	for (i = 0; i < maxlen; i++) {
858		char c;
859
860		if (get_user(c, &user_buffer[i]))
861			return -EFAULT;
862		switch (c) {
863		case '\"':
864		case '\n':
865		case '\r':
866		case '\t':
867		case ' ':
868		case '=':
869			break;
870		default:
871			goto done;
872		};
873	}
874done:
875	return i;
876}
877
878static unsigned long num_arg(const char *user_buffer, unsigned long maxlen,
879			     unsigned long *num)
880{
881	int i = 0;
882
883	*num = 0;
884
885	for(; i < maxlen; i++) {
886		char c;
887
888		if (get_user(c, &user_buffer[i]))
889			return -EFAULT;
890		if ((c >= '0') && (c <= '9')) {
891			*num *= 10;
892			*num += c -'0';
893		} else
894			break;
895	}
896	return i;
897}
898
899static int strn_len(const char *user_buffer, unsigned int maxlen)
900{
901	int i = 0;
902
903	for(; i < maxlen; i++) {
904		char c;
905
906		if (get_user(c, &user_buffer[i]))
907			return -EFAULT;
908		switch (c) {
909		case '\"':
910		case '\n':
911		case '\r':
912		case '\t':
913		case ' ':
914			goto done_str;
915		default:
916			break;
917		};
918	}
919done_str:
920	return i;
921}
922
923static int proc_write(struct file *file, const char *user_buffer,
924			 unsigned long count, void *data)
925{
926	int i = 0, max, len;
927	char name[16], valstr[32];
928	unsigned long value = 0;
929        int idx = (int)(long)(data);
930        struct pktgen_info* info = NULL;
931        char* result = NULL;
932	int tmp;
933
934        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
935                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
936                return -EINVAL;
937        }
938        info = &(pginfos[idx]);
939        result = &(info->result[0]);
940
941	if (count < 1) {
942		sprintf(result, "Wrong command format");
943		return -EINVAL;
944	}
945
946	max = count - i;
947	tmp = count_trail_chars(&user_buffer[i], max);
948	if (tmp < 0)
949		return tmp;
950	i += tmp;
951
952	/* Read variable name */
953
954	len = strn_len(&user_buffer[i], sizeof(name) - 1);
955	if (len < 0)
956		return len;
957	memset(name, 0, sizeof(name));
958	copy_from_user(name, &user_buffer[i], len);
959	i += len;
960
961	max = count -i;
962	len = count_trail_chars(&user_buffer[i], max);
963	if (len < 0)
964		return len;
965	i += len;
966
967	if (debug)
968		printk("pg: %s,%lu\n", name, count);
969
970	if (!strcmp(name, "stop")) {
971		if (info->do_run_run) {
972			strcpy(result, "Stopping");
973                }
974                else {
975                        strcpy(result, "Already stopped...\n");
976                }
977                info->do_run_run = 0;
978		return count;
979	}
980
981	if (!strcmp(name, "pkt_size")) {
982		len = num_arg(&user_buffer[i], 10, &value);
983		if (len < 0)
984			return len;
985		i += len;
986		if (value < 14+20+8)
987			value = 14+20+8;
988		info->pkt_size = value;
989		sprintf(result, "OK: pkt_size=%u", info->pkt_size);
990		return count;
991	}
992	if (!strcmp(name, "frags")) {
993		len = num_arg(&user_buffer[i], 10, &value);
994		if (len < 0)
995			return len;
996		i += len;
997		info->nfrags = value;
998		sprintf(result, "OK: frags=%u", info->nfrags);
999		return count;
1000	}
1001	if (!strcmp(name, "ipg")) {
1002		len = num_arg(&user_buffer[i], 10, &value);
1003		if (len < 0)
1004			return len;
1005		i += len;
1006		info->ipg = value;
1007		sprintf(result, "OK: ipg=%u", info->ipg);
1008		return count;
1009	}
1010 	if (!strcmp(name, "udp_src_min")) {
1011		len = num_arg(&user_buffer[i], 10, &value);
1012		if (len < 0)
1013			return len;
1014		i += len;
1015	 	info->udp_src_min = value;
1016		sprintf(result, "OK: udp_src_min=%u", info->udp_src_min);
1017		return count;
1018	}
1019 	if (!strcmp(name, "udp_dst_min")) {
1020		len = num_arg(&user_buffer[i], 10, &value);
1021		if (len < 0)
1022			return len;
1023		i += len;
1024	 	info->udp_dst_min = value;
1025		sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min);
1026		return count;
1027	}
1028 	if (!strcmp(name, "udp_src_max")) {
1029		len = num_arg(&user_buffer[i], 10, &value);
1030		if (len < 0)
1031			return len;
1032		i += len;
1033	 	info->udp_src_max = value;
1034		sprintf(result, "OK: udp_src_max=%u", info->udp_src_max);
1035		return count;
1036	}
1037 	if (!strcmp(name, "udp_dst_max")) {
1038		len = num_arg(&user_buffer[i], 10, &value);
1039		if (len < 0)
1040			return len;
1041		i += len;
1042	 	info->udp_dst_max = value;
1043		sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max);
1044		return count;
1045	}
1046	if (!strcmp(name, "clone_skb")) {
1047		len = num_arg(&user_buffer[i], 10, &value);
1048		if (len < 0)
1049			return len;
1050		i += len;
1051                info->clone_skb = value;
1052
1053		sprintf(result, "OK: clone_skb=%d", info->clone_skb);
1054		return count;
1055	}
1056	if (!strcmp(name, "count")) {
1057		len = num_arg(&user_buffer[i], 10, &value);
1058		if (len < 0)
1059			return len;
1060		i += len;
1061		info->count = value;
1062		sprintf(result, "OK: count=%llu", (unsigned long long) info->count);
1063		return count;
1064	}
1065	if (!strcmp(name, "src_mac_count")) {
1066		len = num_arg(&user_buffer[i], 10, &value);
1067		if (len < 0)
1068			return len;
1069		i += len;
1070		info->src_mac_count = value;
1071		sprintf(result, "OK: src_mac_count=%d", info->src_mac_count);
1072		return count;
1073	}
1074	if (!strcmp(name, "dst_mac_count")) {
1075		len = num_arg(&user_buffer[i], 10, &value);
1076		if (len < 0)
1077			return len;
1078		i += len;
1079		info->dst_mac_count = value;
1080		sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count);
1081		return count;
1082	}
1083	if (!strcmp(name, "odev")) {
1084		len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1);
1085		if (len < 0)
1086			return len;
1087		memset(info->outdev, 0, sizeof(info->outdev));
1088		copy_from_user(info->outdev, &user_buffer[i], len);
1089		i += len;
1090		sprintf(result, "OK: odev=%s", info->outdev);
1091		return count;
1092	}
1093	if (!strcmp(name, "flag")) {
1094                char f[32];
1095                memset(f, 0, 32);
1096		len = strn_len(&user_buffer[i], sizeof(f) - 1);
1097		if (len < 0)
1098			return len;
1099		copy_from_user(f, &user_buffer[i], len);
1100		i += len;
1101                if (strcmp(f, "IPSRC_RND") == 0) {
1102                        info->flags |= F_IPSRC_RND;
1103                }
1104                else if (strcmp(f, "!IPSRC_RND") == 0) {
1105                        info->flags &= ~F_IPSRC_RND;
1106                }
1107                else if (strcmp(f, "IPDST_RND") == 0) {
1108                        info->flags |= F_IPDST_RND;
1109                }
1110                else if (strcmp(f, "!IPDST_RND") == 0) {
1111                        info->flags &= ~F_IPDST_RND;
1112                }
1113                else if (strcmp(f, "UDPSRC_RND") == 0) {
1114                        info->flags |= F_UDPSRC_RND;
1115                }
1116                else if (strcmp(f, "!UDPSRC_RND") == 0) {
1117                        info->flags &= ~F_UDPSRC_RND;
1118                }
1119                else if (strcmp(f, "UDPDST_RND") == 0) {
1120                        info->flags |= F_UDPDST_RND;
1121                }
1122                else if (strcmp(f, "!UDPDST_RND") == 0) {
1123                        info->flags &= ~F_UDPDST_RND;
1124                }
1125                else if (strcmp(f, "MACSRC_RND") == 0) {
1126                        info->flags |= F_MACSRC_RND;
1127                }
1128                else if (strcmp(f, "!MACSRC_RND") == 0) {
1129                        info->flags &= ~F_MACSRC_RND;
1130                }
1131                else if (strcmp(f, "MACDST_RND") == 0) {
1132                        info->flags |= F_MACDST_RND;
1133                }
1134                else if (strcmp(f, "!MACDST_RND") == 0) {
1135                        info->flags &= ~F_MACDST_RND;
1136                }
1137                else {
1138                        sprintf(result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
1139                                f,
1140                                "IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
1141                        return count;
1142                }
1143		sprintf(result, "OK: flags=0x%x", info->flags);
1144		return count;
1145	}
1146	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
1147		len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1);
1148		if (len < 0)
1149			return len;
1150		memset(info->dst_min, 0, sizeof(info->dst_min));
1151		copy_from_user(info->dst_min, &user_buffer[i], len);
1152		if(debug)
1153			printk("pg: dst_min set to: %s\n", info->dst_min);
1154		i += len;
1155		sprintf(result, "OK: dst_min=%s", info->dst_min);
1156		return count;
1157	}
1158	if (!strcmp(name, "dst_max")) {
1159		len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1);
1160		if (len < 0)
1161			return len;
1162		memset(info->dst_max, 0, sizeof(info->dst_max));
1163		copy_from_user(info->dst_max, &user_buffer[i], len);
1164		if(debug)
1165			printk("pg: dst_max set to: %s\n", info->dst_max);
1166		i += len;
1167		sprintf(result, "OK: dst_max=%s", info->dst_max);
1168		return count;
1169	}
1170	if (!strcmp(name, "src_min")) {
1171		len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1);
1172		if (len < 0)
1173			return len;
1174		memset(info->src_min, 0, sizeof(info->src_min));
1175		copy_from_user(info->src_min, &user_buffer[i], len);
1176		if(debug)
1177			printk("pg: src_min set to: %s\n", info->src_min);
1178		i += len;
1179		sprintf(result, "OK: src_min=%s", info->src_min);
1180		return count;
1181	}
1182	if (!strcmp(name, "src_max")) {
1183		len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1);
1184		if (len < 0)
1185			return len;
1186		memset(info->src_max, 0, sizeof(info->src_max));
1187		copy_from_user(info->src_max, &user_buffer[i], len);
1188		if(debug)
1189			printk("pg: src_max set to: %s\n", info->src_max);
1190		i += len;
1191		sprintf(result, "OK: src_max=%s", info->src_max);
1192		return count;
1193	}
1194	if (!strcmp(name, "dstmac")) {
1195		char *v = valstr;
1196		unsigned char *m = info->dst_mac;
1197
1198		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1199		if (len < 0)
1200			return len;
1201		memset(valstr, 0, sizeof(valstr));
1202		copy_from_user(valstr, &user_buffer[i], len);
1203		i += len;
1204
1205		for(*m = 0;*v && m < info->dst_mac + 6; v++) {
1206			if (*v >= '0' && *v <= '9') {
1207				*m *= 16;
1208				*m += *v - '0';
1209			}
1210			if (*v >= 'A' && *v <= 'F') {
1211				*m *= 16;
1212				*m += *v - 'A' + 10;
1213			}
1214			if (*v >= 'a' && *v <= 'f') {
1215				*m *= 16;
1216				*m += *v - 'a' + 10;
1217			}
1218			if (*v == ':') {
1219				m++;
1220				*m = 0;
1221			}
1222		}
1223		sprintf(result, "OK: dstmac");
1224		return count;
1225	}
1226	if (!strcmp(name, "srcmac")) {
1227		char *v = valstr;
1228		unsigned char *m = info->src_mac;
1229
1230		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1231		if (len < 0)
1232			return len;
1233		memset(valstr, 0, sizeof(valstr));
1234		copy_from_user(valstr, &user_buffer[i], len);
1235		i += len;
1236
1237		for(*m = 0;*v && m < info->src_mac + 6; v++) {
1238			if (*v >= '0' && *v <= '9') {
1239				*m *= 16;
1240				*m += *v - '0';
1241			}
1242			if (*v >= 'A' && *v <= 'F') {
1243				*m *= 16;
1244				*m += *v - 'A' + 10;
1245			}
1246			if (*v >= 'a' && *v <= 'f') {
1247				*m *= 16;
1248				*m += *v - 'a' + 10;
1249			}
1250			if (*v == ':') {
1251				m++;
1252				*m = 0;
1253			}
1254		}
1255		sprintf(result, "OK: srcmac");
1256		return count;
1257	}
1258
1259	if (!strcmp(name, "inject") || !strcmp(name, "start")) {
1260		MOD_INC_USE_COUNT;
1261                if (info->busy) {
1262                        strcpy(info->result, "Already running...\n");
1263                }
1264                else {
1265                        info->busy = 1;
1266                        strcpy(info->result, "Starting");
1267                        inject(info);
1268                        info->busy = 0;
1269                }
1270		MOD_DEC_USE_COUNT;
1271		return count;
1272	}
1273
1274	sprintf(info->result, "No such parameter \"%s\"", name);
1275	return -EINVAL;
1276}
1277
1278
1279int create_proc_dir(void)
1280{
1281        int     len;
1282        /*  does proc_dir already exists */
1283        len = strlen(PG_PROC_DIR);
1284
1285        for (proc_dir = proc_net->subdir; proc_dir;
1286             proc_dir=proc_dir->next) {
1287                if ((proc_dir->namelen == len) &&
1288                    (! memcmp(proc_dir->name, PG_PROC_DIR, len)))
1289                        break;
1290        }
1291        if (!proc_dir)
1292                proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
1293        if (!proc_dir) return -ENODEV;
1294        return 1;
1295}
1296
1297int remove_proc_dir(void)
1298{
1299        remove_proc_entry(PG_PROC_DIR, proc_net);
1300        return 1;
1301}
1302
1303static int __init init(void)
1304{
1305        int i;
1306	printk(version);
1307	cycles_calibrate();
1308	if (cpu_speed == 0) {
1309		printk("pktgen: Error: your machine does not have working cycle counter.\n");
1310		return -EINVAL;
1311	}
1312
1313	create_proc_dir();
1314
1315        for (i = 0; i<MAX_PKTGEN; i++) {
1316                memset(&(pginfos[i]), 0, sizeof(pginfos[i]));
1317                pginfos[i].pkt_size = ETH_ZLEN;
1318                pginfos[i].nfrags = 0;
1319                pginfos[i].clone_skb = clone_skb_d;
1320                pginfos[i].ipg = ipg_d;
1321                pginfos[i].count = count_d;
1322                pginfos[i].sofar = 0;
1323                pginfos[i].hh[12] = 0x08; /* fill in protocol.  Rest is filled in later. */
1324                pginfos[i].hh[13] = 0x00;
1325                pginfos[i].udp_src_min = 9; /* sink NULL */
1326                pginfos[i].udp_src_max = 9;
1327                pginfos[i].udp_dst_min = 9;
1328                pginfos[i].udp_dst_max = 9;
1329
1330                sprintf(pginfos[i].fname, "net/%s/pg%i", PG_PROC_DIR, i);
1331                pginfos[i].proc_ent = create_proc_entry(pginfos[i].fname, 0600, 0);
1332                if (!pginfos[i].proc_ent) {
1333                        printk("pktgen: Error: cannot create net/%s/pg procfs entry.\n", PG_PROC_DIR);
1334                        goto cleanup_mem;
1335                }
1336                pginfos[i].proc_ent->read_proc = proc_read;
1337                pginfos[i].proc_ent->write_proc = proc_write;
1338                pginfos[i].proc_ent->data = (void*)(long)(i);
1339
1340                sprintf(pginfos[i].busy_fname, "net/%s/pg_busy%i",  PG_PROC_DIR, i);
1341                pginfos[i].busy_proc_ent = create_proc_entry(pginfos[i].busy_fname, 0, 0);
1342                if (!pginfos[i].busy_proc_ent) {
1343                        printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR);
1344                        goto cleanup_mem;
1345                }
1346                pginfos[i].busy_proc_ent->read_proc = proc_busy_read;
1347                pginfos[i].busy_proc_ent->data = (void*)(long)(i);
1348        }
1349        return 0;
1350
1351cleanup_mem:
1352        for (i = 0; i<MAX_PKTGEN; i++) {
1353                if (strlen(pginfos[i].fname)) {
1354                        remove_proc_entry(pginfos[i].fname, NULL);
1355                }
1356                if (strlen(pginfos[i].busy_fname)) {
1357                        remove_proc_entry(pginfos[i].busy_fname, NULL);
1358                }
1359        }
1360	return -ENOMEM;
1361}
1362
1363
1364static void __exit cleanup(void)
1365{
1366        int i;
1367        for (i = 0; i<MAX_PKTGEN; i++) {
1368                if (strlen(pginfos[i].fname)) {
1369                        remove_proc_entry(pginfos[i].fname, NULL);
1370                }
1371                if (strlen(pginfos[i].busy_fname)) {
1372                        remove_proc_entry(pginfos[i].busy_fname, NULL);
1373                }
1374        }
1375	remove_proc_dir();
1376}
1377
1378module_init(init);
1379module_exit(cleanup);
1380
1381MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
1382MODULE_DESCRIPTION("Packet Generator tool");
1383MODULE_LICENSE("GPL");
1384MODULE_PARM(count_d, "i");
1385MODULE_PARM(ipg_d, "i");
1386MODULE_PARM(cpu_speed, "i");
1387MODULE_PARM(clone_skb_d, "i");
1388
1389
1390
1391