PATCH: pktgen.c update
Subsystems (2)
I got all excited about the pktgen.c module, and went in and changed it significantly to suite my needs better. I would be happy to see these changes integrated into the mainstream kernel, but even if that is not appropriate, I will appreciate any feedback, especially about how to make it sleep more efficiently when there is an inter-packet-gap set.
Thanks,
Ben
Signature · 3 lines
Ben Greear <[email protected]> <Ben_Greear AT excite.com> President of Candela Technologies Inc http://www.candelatech.com ScryMUD: http://scry.wanfear.com http://scry.wanfear.com/~greear
net/core/pktgen.c +741 -185
@@ -1,4 +1,5 @@ -/* $Id: pktgen.c,v 1.1.2.1 2002/03/01 12:15:05 davem Exp $ +/* -*-linux-c-*- +* $Id: pktgen.c,v 1.1.2.1 2002/03/01 12:15:05 davem Exp $ * pktgen.c: Packet Generator for performance evaluation. * * Copyright 2001, 2002 by Robert Olsson <[email protected]>
@@ -18,6 +19,20 @@* MAC address typo fixed. 010417 --ro * Integrated. 020301 --DaveM * Added multiskb option 020301 --DaveM +* Significant re-work of the module: +* * Updated to support generation over multiple interfaces at once +* by creating 32 /proc/net/pg* files. Each file can be manipulated +* individually. +* * Converted many counters to __u64 to allow longer runs. +* * Allow configuration of ranges, like min/max IP address, MACs, +* and UDP-ports, for both source and destination, and can +* set to use a random distribution or sequentially walk the range. +* * Can now change some values after starting. +* * Place 12-byte packet in UDP payload with magic number, +* sequence number, and timestamp. Will write receiver next. +* * The new changes seem to have a performance inpact of around 1%, +* as far as I can tell. +* --Ben Greear <[email protected]> * * See Documentation/networking/pktgen.txt for how to use this. */
@@ -56,69 +71,189 @@#define cycles () ((u32)get_cycles())static char version[]__initdata = -"pktgen.c: v1.0 010812: Packet Generator for packet performance testing.\n" ; +"pktgen.c: v1.1: Packet Generator for packet performance testing.\n" ; -/* Parameters */ -static char pg_outdev[32 ], pg_dst[32 ]; -static int pkt_size =ETH_ZLEN ; -static int nfrags =0 ; -static __u32 pg_count =100000 ;/* Default No packets to send */ -static __u32 pg_ipg =0 ;/* Default Interpacket gap in nsec */ -static int pg_multiskb =0 ;/* Use multiple SKBs during packet gen. */ +/* Used to help with determining the pkts on receive */ +#define PKTGEN_MAGIC 0xbe9be955 -static int debug; -static int forced_stop; -static int pg_cpu_speed; -static int pg_busy; +/* Keep information per interface */ +struct pktgen_info { +/* Parameters */ + +/* If min != max, then we will either do a linear iteration, or +* we will do a random selection from within the range. +*/ +__u32 flags;/* 1<<0 IP-Src Random, 1<<1 IP-Dst Random, +* 1<<2 UDP-Src Random, 1<<3 UDP-Dst Random +* 1<<4 MAC-Src Random, 1<<5 MAC-Dst Random +* 1<<6 Specify-Src-Mac (default is to use Interface's MAC Addr) +* 1<<7 Specify-Src-IP (default is to use Interface's IP Addr) +*/ + +int pkt_size;/* = ETH_ZLEN; */ +int nfrags; +__u32 pg_ipg;/* Default Interpacket gap in nsec */ +__u64 pg_count;/* Default No packets to send */ +__u64 pg_sofar;/* How many pkts we've sent so far */ +__u64 errors;/* Errors when trying to transmit, pkts will be re-sent */ +struct timeval started_at; +struct timeval stopped_at; +__u64 idle_acc; +__u32 seq_num; + +int pg_multiskb;/* Use multiple SKBs during packet gen. */ +int forced_stop; +int pg_busy; +int do_run_run;/* if this changes to false, the test will stop */ + +char pg_outdev[32 ]; +char dst_min[32 ]; +char dst_max[32 ]; +char src_min[32 ]; +char src_max[32 ]; + +/* If we're doing ranges, random or incremental, then this +* defines the min/max for those ranges. +*/ +__u32 saddr_min;/* inclusive, source IP address */ +__u32 saddr_max;/* exclusive, source IP address */ +__u32 daddr_min;/* inclusive, dest IP address */ +__u32 daddr_max;/* exclusive, dest IP address */ + +__u16 udp_src_min;/* inclusive, source UDP port */ +__u16 udp_src_max;/* exclusive, source UDP port */ +__u16 udp_dst_min;/* inclusive, dest UDP port */ +__u16 udp_dst_max;/* exclusive, dest UDP port */ + +__u32 src_mac_count;/* How many MACs to iterate through */ +__u32 dst_mac_count;/* How many MACs to iterate through */ + +unsigned char dst_mac[6 ]; +unsigned char src_mac[6 ]; + +__u32 cur_dst_mac_offset; +__u32 cur_src_mac_offset; +__u32 cur_saddr; +__u32 cur_daddr; +__u16 cur_udp_dst; +__u16 cur_udp_src; + +__u8 hh[14 ]; +/* = { +0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, ++ We fill in SRC address later +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x08, 0x00 +}; +*/ +__u16 pad;/* pad out the hh struct to an even 16 bytes */ +char pg_result[512 ]; + +/* proc file names */ +char fname[80 ]; +char busy_fname[80 ]; -static __u8 hh[14 ] = { -0x00 ,0x80 ,0xC8 ,0x79 ,0xB3 ,0xCB , +struct proc_dir_entry *pg_proc_ent; +struct proc_dir_entry *pg_busy_proc_ent; +}; -/* We fill in SRC address later */ -0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 , -0x08 ,0x00 +struct pktgen_hdr { +__u32 pgh_magic; +__u32 seq_num; +struct timeval timestamp; }; -static unsigned char *pg_dstmac = hh; -static char pg_result[512 ]; +static int pg_cpu_speed; +static int debug; + +/* Module parameters, defaults. */ +static int pg_count_d =100000 ; +static int pg_ipg_d =0 ; +static int pg_multiskb_d =0 ; + + +#define MAX_PKTGEN 32 +static struct pktgen_info pginfos[MAX_PKTGEN ]; + + +/** Convert to miliseconds */ +inline __u64 tv_to_ms (const struct timeval * tv) { +__u64 ms = tv->tv_usec /1000 ; + ms += (__u64 )tv->tv_sec * (__u64 )1000 ; +return ms; +} + +inline __u64 getCurMs (void ) { +struct timeval tv; +do_gettimeofday (&tv); +return tv_to_ms (&tv); +} + -static struct net_device *pg_setup_inject (u32 *saddrp) +static struct net_device *pg_setup_inject (struct pktgen_info * info) {struct net_device *odev; -int p1, p2; -u32 saddr;rtnl_lock (); - odev =__dev_get_by_name (pg_outdev); + odev =__dev_get_by_name (info->pg_outdev);if (!odev) { -sprintf (pg_result,"No such netdevice: \"%s\"" , pg_outdev); +sprintf (info->pg_result,"No such netdevice: \"%s\"" , info->pg_outdev); goto out_unlock; }if (odev->type !=ARPHRD_ETHER ) { -sprintf (pg_result,"Not ethernet device: \"%s\"" , pg_outdev); +sprintf (info->pg_result,"Not ethernet device: \"%s\"" , info->pg_outdev); goto out_unlock; }if (!netif_running (odev)) { -sprintf (pg_result,"Device is down: \"%s\"" , pg_outdev); +sprintf (info->pg_result,"Device is down: \"%s\"" , info->pg_outdev); goto out_unlock; } -for (p1 =6 , p2 =0 ; p1 < odev->addr_len +6 ; p1++) - hh[p1] = odev->dev_addr[p2++]; - - saddr =0 ; -if (odev->ip_ptr) { -struct in_device *in_dev = odev->ip_ptr; - -if (in_dev->ifa_list) - saddr = in_dev->ifa_list->ifa_address; - } +/* Default to the interface's mac if not explicitly set. */ +if (!(info->flags & (1 <<6 ))) { +memcpy (&(info->hh[6 ]), odev->dev_addr,6 ); + } +else { +memcpy (&(info->hh[6 ]), info->src_mac,6 ); + } + +/* Set up Dest MAC */ +memcpy (&(info->hh[0 ]), info->dst_mac,6 ); + + info->saddr_min =0 ; + info->saddr_max =0 ; +if (strlen (info->src_min) ==0 ) { +if (odev->ip_ptr) { +struct in_device *in_dev = odev->ip_ptr; + +if (in_dev->ifa_list) { + info->saddr_min = in_dev->ifa_list->ifa_address; + info->saddr_max = info->saddr_min; + } + } + } +else { + info->saddr_min =in_aton (info->src_min); + info->saddr_max =in_aton (info->src_max); + } + + info->daddr_min =in_aton (info->dst_min); + info->daddr_max =in_aton (info->dst_max); + +/* Initialize current values. */ + info->cur_dst_mac_offset =0 ; + info->cur_src_mac_offset =0 ; + info->cur_saddr = info->saddr_min; + info->cur_daddr = info->daddr_min; + info->cur_udp_dst = info->udp_dst_min; + info->cur_udp_src = info->udp_src_min; +atomic_inc (&odev->refcnt);rtnl_unlock (); - *saddrp = saddr;return odev; out_unlock:
@@ -126,9 +261,7 @@return NULL ; } -static u32 idle_acc_lo, idle_acc_hi; - -static void nanospin (int pg_ipg) +static void nanospin (int pg_ipg,struct pktgen_info * info) {u32 idle_start, idle;
@@ -140,9 +273,7 @@if (idle *1000 >= pg_ipg * pg_cpu_speed)break ; } - idle_acc_lo += idle; -if (idle_acc_lo < idle) - idle_acc_hi++; + info->idle_acc += idle; }static int calc_mhz (void )
@@ -173,17 +304,136 @@ } } -static struct sk_buff *fill_packet (struct net_device *odev,__u32 saddr) + +/* Increment/randomize headers according to flags and current values +* for IP src/dest, UDP src/dst port, MAC-Addr src/dst +*/ +static void mod_cur_headers (struct pktgen_info * info) { +__u32 imn; +__u32 imx; + +/* Deal with source MAC */ +if (info->src_mac_count >1 ) { +__u32 mc; +__u32 tmp; +if (info->flags & (1 <<4 )) { + mc =net_random () % (info->src_mac_count); + } +else { + mc = info->cur_src_mac_offset++; +if (info->cur_src_mac_offset > info->src_mac_count) { + info->cur_src_mac_offset =0 ; + } + } + + tmp = info->src_mac[5 ] + (mc &0xFF ); + info->hh[11 ] = tmp; + tmp = (info->src_mac[4 ] + ((mc >>8 ) &0xFF ) + (tmp >>8 )); + info->hh[10 ] = tmp; + tmp = (info->src_mac[3 ] + ((mc >>16 ) &0xFF ) + (tmp >>8 )); + info->hh[9 ] = tmp; + tmp = (info->src_mac[2 ] + ((mc >>24 ) &0xFF ) + (tmp >>8 )); + info->hh[8 ] = tmp; + tmp = (info->src_mac[1 ] + (tmp >>8 )); + info->hh[7 ] = tmp; + } + +/* Deal with Destination MAC */ +if (info->dst_mac_count >1 ) { +__u32 mc; +__u32 tmp; +if (info->flags & (1 <<5 )) { + mc =net_random () % (info->dst_mac_count); + } +else { + mc = info->cur_dst_mac_offset++; +if (info->cur_dst_mac_offset > info->dst_mac_count) { + info->cur_dst_mac_offset =0 ; + } + } + + tmp = info->dst_mac[5 ] + (mc &0xFF ); + info->hh[5 ] = tmp; + tmp = (info->dst_mac[4 ] + ((mc >>8 ) &0xFF ) + (tmp >>8 )); + info->hh[4 ] = tmp; + tmp = (info->dst_mac[3 ] + ((mc >>16 ) &0xFF ) + (tmp >>8 )); + info->hh[3 ] = tmp; + tmp = (info->dst_mac[2 ] + ((mc >>24 ) &0xFF ) + (tmp >>8 )); + info->hh[2 ] = tmp; + tmp = (info->dst_mac[1 ] + (tmp >>8 )); + info->hh[1 ] = tmp; + } + +if (info->udp_src_min < info->udp_src_max) { +if (info->flags & (1 <<2 )) { + info->cur_udp_src = ((net_random () % (info->udp_src_max - info->udp_src_min)) + + info->udp_src_min); + } +else { + info->cur_udp_src++; +if (info->cur_udp_src >= info->udp_src_max) { + info->cur_udp_src = info->udp_src_min; + } + } + } + +if (info->udp_dst_min < info->udp_dst_max) { +if (info->flags & (1 <<3 )) { + info->cur_udp_dst = ((net_random () % (info->udp_dst_max - info->udp_dst_min)) + + info->udp_dst_min); + } +else { + info->cur_udp_dst++; +if (info->cur_udp_dst >= info->udp_dst_max) { + info->cur_udp_dst = info->udp_dst_min; + } + } + } + +if ((imn =ntohl (info->saddr_min)) < (imx =ntohl (info->saddr_max))) { +__u32 t; +if (info->flags & (1 <<0 )) { + t = ((net_random () % (imx - imn)) + imn); + } +else { + t =ntohl (info->cur_saddr); + t++; +if (t >= imx) { + t = imn; + } + } + info->cur_saddr =htonl (t); + } + +if ((imn =ntohl (info->daddr_min)) < (imx =ntohl (info->daddr_max))) { +__u32 t; +if (info->flags & (1 <<1 )) { + t = ((net_random () % (imx - imn)) + imn); + } +else { + t =ntohl (info->cur_daddr); + t++; +if (t >= imx) { + t = imn; + } + } + info->cur_daddr =htonl (t); + } +}/* mod_cur_headers */ + + +static struct sk_buff *fill_packet (struct net_device *odev,struct pktgen_info * info) { -struct sk_buff *skb; +struct sk_buff *skb =NULL ;__u8 *eth;struct udphdr *udph;int datalen, iplen;struct iphdr *iph; - - skb =alloc_skb (pkt_size +64 +16 ,GFP_ATOMIC ); +struct pktgen_hdr *pgh =NULL ; + + skb =alloc_skb (info->pkt_size +64 +16 ,GFP_ATOMIC );if (!skb) { -sprintf (pg_result,"No memory" ); +sprintf (info->pg_result,"No memory" );return NULL ; }
@@ -194,15 +444,20 @@ iph = (struct iphdr *)skb_put (skb,sizeof (struct iphdr )); udph = (struct udphdr *)skb_put (skb,sizeof (struct udphdr )); -/* Copy the ethernet header */ -memcpy (eth, hh,14 ); - - datalen = pkt_size -14 -20 -8 ;/* Eth + IPh + UDPh */ -if (datalen <0 ) - datalen =0 ; - - udph->source =htons (9 ); - udph->dest =htons (9 ); +/* Update any of the values, used when we're incrementing various +* fields. +*/ +mod_cur_headers (info); + +memcpy (eth, info->hh,14 ); + + datalen = info->pkt_size -14 -20 -8 ;/* Eth + IPh + UDPh */ +if (datalen <sizeof (struct pktgen_hdr )) { + datalen =sizeof (struct pktgen_hdr ); + } + + udph->source =htons (info->cur_udp_src); + udph->dest =htons (info->cur_udp_dst); udph->len =htons (datalen +8 );/* DATA + udphdr */ udph->check =0 ;/* No checksum */
@@ -211,8 +466,8 @@ iph->ttl =3 ; iph->tos =0 ; iph->protocol =IPPROTO_UDP ;/* UDP */ - iph->saddr = saddr; - iph->daddr =in_aton (pg_dst); + iph->saddr = info->cur_saddr; + iph->daddr = info->cur_daddr; iph->frag_off =0 ; iplen =20 +8 + datalen; iph->tot_len =htons (iplen);
@@ -223,12 +478,15 @@ skb->dev = odev; skb->pkt_type =PACKET_HOST ; -if (nfrags <=0 ) { -skb_put (skb, datalen); +if (info->nfrags <=0 ) { + pgh = (struct pktgen_hdr *)skb_put (skb, datalen); }else { -int frags = nfrags; +int frags = info->nfrags;int i; +/* TODO: Verify this is OK...it sure is ugly. --Ben */ + pgh = (struct pktgen_hdr *)(((char *)(udph)) +8 ); +if (frags >MAX_SKB_FRAGS ) frags =MAX_SKB_FRAGS ;if (datalen > frags*PAGE_SIZE ) {
@@ -272,63 +530,95 @@ } } +/* Stamp the time, and sequence number, convert them to network byte order */ +if (pgh) { + pgh->pgh_magic =__constant_htonl (PKTGEN_MAGIC ); +do_gettimeofday (&(pgh->timestamp)); + pgh->timestamp.tv_usec =htonl (pgh->timestamp.tv_usec); + pgh->timestamp.tv_sec =htonl (pgh->timestamp.tv_sec); + pgh->seq_num =htonl (info->seq_num); + } +return skb; } -static void pg_inject (void ) +static void pg_inject (struct pktgen_info * info) { -u32 saddr;struct net_device *odev; -struct sk_buff *skb; -struct timeval start, stop; -u32 total, idle; -int pc, lcount; - - odev =pg_setup_inject (&saddr); +struct sk_buff *skb =NULL ; +__u64 total =0 ; +__u64 idle =0 ; +__u64 lcount =0 ; +int nr_frags =0 ; + + odev =pg_setup_inject (info);if (!odev)return ; - skb =fill_packet (odev, saddr); -if (skb ==NULL ) - goto out_reldev; - - forced_stop =0 ; - idle_acc_hi =0 ; - idle_acc_lo =0 ; - pc =0 ; - lcount = pg_count; -do_gettimeofday (&start); - -for (;;) { + info->do_run_run =1 ;/* Cranke yeself! */ + info->forced_stop =0 ; + info->idle_acc =0 ; + info->pg_sofar =0 ; + lcount = info->pg_count; +do_gettimeofday (&(info->started_at)); + +while (info->do_run_run) { +/* Want to set a time-stamp, so build a new pkt each time */ +if (skb) { +kfree_skb (skb); + } + skb =fill_packet (odev, info); +if (skb ==NULL ) + goto out_reldev; + nr_frags =skb_shinfo (skb)->nr_frags; +spin_lock_bh (&odev->xmit_lock);if (!netif_queue_stopped (odev)) {struct sk_buff *skb2 = skb; -if (pg_multiskb) +if (info->pg_multiskb) skb2 =skb_copy (skb,GFP_ATOMIC );else atomic_inc (&skb->users);if (!skb2) goto skip;if (odev->hard_start_xmit (skb2, odev)) { -kfree_skb (skb2); -if (net_ratelimit ()) -printk (KERN_INFO "Hard xmit error\n" ); +if (net_ratelimit ()) { +printk (KERN_INFO "Hard xmit error\n" ); + } + info->errors++; } - pc++; +else { + info->pg_sofar++; + info->seq_num++; + } } skip:spin_unlock_bh (&odev->xmit_lock); -if (pg_ipg) -nanospin (pg_ipg); -if (forced_stop) +if (info->pg_ipg) { +/* Try not to busy-spin if we have larger sleep times. +* TODO: Investigate better ways to do this. +*/ +if (info->pg_ipg <10000 ) {/* 10 usecs or less */ +nanospin (info->pg_ipg, info); + } +else if (info->pg_ipg <10000000 ) {/* 10ms or less */ +udelay (info->pg_ipg /1000 ); + } +else { +mdelay (info->pg_ipg /1000000 ); + } + } + +if (info->forced_stop) goto out_intr;if (signal_pending (current)) - goto out_intr; +break ; -if (--lcount ==0 ) { +/* If lcount is zero, then run forever */ +if ((lcount !=0 ) && (--lcount ==0 )) {if (atomic_read (&skb->users) !=1 ) {u32 idle_start, idle;
@@ -339,9 +629,7 @@schedule (); } idle =cycles () - idle_start; - idle_acc_lo += idle; -if (idle_acc_lo < idle) - idle_acc_hi++; + info->idle_acc += idle; }break ; }
@@ -361,53 +649,63 @@do_softirq (); }while (netif_queue_stopped (odev)); idle =cycles () - idle_start; - idle_acc_lo += idle; -if (idle_acc_lo < idle) - idle_acc_hi++; + info->idle_acc += idle; } - } + }/* while we should be running */ -do_gettimeofday (&stop); +do_gettimeofday (&(info->stopped_at)); - total = (stop.tv_sec - start.tv_sec) *1000000 + - stop.tv_usec - start.tv_usec; + total = (info->stopped_at.tv_sec - info->started_at.tv_sec) *1000000 + + info->stopped_at.tv_usec - info->started_at.tv_usec; - idle = (((idle_acc_hi<<20 )/pg_cpu_speed)<<12 )+idle_acc_lo/pg_cpu_speed; + idle = (__u32)(info->idle_acc)/(__u32)(pg_cpu_speed); { -char *p = pg_result; - - p +=sprintf (p,"OK: %u(c%u+d%u) usec, %u (%dbyte,%dfrags) %upps %uMB/sec" , +char *p = info->pg_result; +__u64 pps = (__u32)(info->pg_sofar *1000 ) / ((__u32)(total) /1000 ); +__u64 bps = pps *8 * (info->pkt_size +4 );/* take 32bit ethernet CRC into account */ + p +=sprintf (p,"OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) %llupps %lluMb/sec (%llubps) errors: %llu" , total, total-idle, idle, - pc, skb->len,skb_shinfo (skb)->nr_frags, - ((pc*1000 )/(total/1000 )), - (((pc*1000 )/(total/1000 ))*pkt_size)/1024 /1024 + info->pg_sofar, + skb->len +4 ,/* Add 4 to account for the ethernet checksum */ + nr_frags, + pps, + bps/1024 /1024 , bps, info->errors ); } - + out_relskb: -kfree_skb (skb); +if (skb) { +kfree_skb (skb); + skb =NULL ; + } + out_reldev:dev_put (odev);return ; out_intr: -sprintf (pg_result,"Interrupted" ); +sprintf (info->pg_result,"Interrupted" ); goto out_relskb; }/* proc/net/pg */ -static struct proc_dir_entry *pg_proc_ent =0 ; -static struct proc_dir_entry *pg_busy_proc_ent =0 ; -static int proc_pg_busy_read (char *buf ,char **start,off_t offset,int len,int *eof,void *data) {char *p; +int idx = (int )(data); +struct pktgen_info * info =NULL ; + +if ((idx <0 ) || (idx >=MAX_PKTGEN )) { +printk ("ERROR: idx: %i is out of range in proc_pg_write\n" , idx); +return -EINVAL ; + } + info = &(pginfos[idx]); p = buf; - p +=sprintf (p,"%d\n" , pg_busy); + p +=sprintf (p,"%d\n" , info->pg_busy); *eof =1 ;return p-buf;
@@ -418,16 +716,72 @@ {char *p;int i; +int idx = (int )(data); +struct pktgen_info * info =NULL ; +__u64 sa; +__u64 stopped; +__u64 now =getCurMs (); + +if ((idx <0 ) || (idx >=MAX_PKTGEN )) { +printk ("ERROR: idx: %i is out of range in proc_pg_write\n" , idx); +return -EINVAL ; + } + info = &(pginfos[idx]); p = buf; - p +=sprintf (p,"Params: count=%u pkt_size=%u frags %d ipg %u multiskb %d odev \"%s\" dst %s dstmac " , - pg_count, pkt_size, nfrags, pg_ipg, pg_multiskb, - pg_outdev, pg_dst); -for (i =0 ; i <6 ; i++) - p +=sprintf (p,"%02X%s" , pg_dstmac[i], i ==5 ?"\n" :":" ); - -if (pg_result[0 ]) - p +=sprintf (p,"Result: %s\n" , pg_result); + p +=sprintf (p,"VERSION-1\n" );/* Help with parsing compatibility */ + p +=sprintf (p,"Params: count %llu pkt_size: %u frags: %d ipg: %u multiskb: %d odev \"%s\"\n" , + info->pg_count, info->pkt_size, info->nfrags, info->pg_ipg, + info->pg_multiskb, info->pg_outdev); + p +=sprintf (p," dst_min: %s dst_max: %s src_min: %s src_max: %s\n" , + info->dst_min, info->dst_max, info->src_min, info->src_max); + p +=sprintf (p," src_mac: " ); +for (i =0 ; i <6 ; i++) { + p +=sprintf (p,"%02X%s" , info->src_mac[i], i ==5 ?" " :":" ); + } + p +=sprintf (p,"dst_mac: " ); +for (i =0 ; i <6 ; i++) { + p +=sprintf (p,"%02X%s" , info->dst_mac[i], i ==5 ?"\n" :":" ); + } + p +=sprintf (p," udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n" , + info->udp_src_min, info->udp_src_max, info->udp_dst_min, + info->udp_dst_max); + p +=sprintf (p," src_mac_count: %d dst_mac_count: %d\n Flags: " , + info->src_mac_count, info->dst_mac_count); +if (info->flags & (1 <<0 )) { + p +=sprintf (p,"IPSRC_RND " ); + } +if (info->flags & (1 <<1 )) { + p +=sprintf (p,"IPDST_RND " ); + } +if (info->flags & (1 <<2 )) { + p +=sprintf (p,"UDPSRC_RND " ); + } +if (info->flags & (1 <<3 )) { + p +=sprintf (p,"UDPDST_RND " ); + } +if (info->flags & (1 <<4 )) { + p +=sprintf (p,"MACSRC_RND " ); + } +if (info->flags & (1 <<5 )) { + p +=sprintf (p,"MACDST_RND " ); + } + p +=sprintf (p,"\n" ); + + sa =tv_to_ms (&(info->started_at)); + stopped =tv_to_ms (&(info->stopped_at)); +if (info->do_run_run) { + stopped = now;/* not really stopped, more like last-running-at */ + } + p +=sprintf (p,"Current:\n pkts-sofar: %llu errors: %llu\n started: %llums stopped: %llums now: %llums idle: %lluns\n" , + info->pg_sofar, info->errors, sa, stopped, now, info->idle_acc); + p +=sprintf (p," seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n" , + info->seq_num, info->cur_dst_mac_offset, info->cur_src_mac_offset); + p +=sprintf (p," cur_saddr: 0x%x cur_daddr: 0x%x cur_udp_dst: %d cur_udp_src: %d\n" , + info->cur_saddr, info->cur_daddr, info->cur_udp_dst, info->cur_udp_src); + +if (info->pg_result[0 ]) + p +=sprintf (p,"Result: %s\n" , info->pg_result);else p +=sprintf (p,"Result: Idle\n" ); *eof =1 ;
@@ -498,7 +852,17 @@int i =0 , max, len;char name[16 ], valstr[32 ];unsigned long value =0 ; - +int idx = (int )(data); +struct pktgen_info * info =NULL ; +char * pg_result =NULL ; + +if ((idx <0 ) || (idx >=MAX_PKTGEN )) { +printk ("ERROR: idx: %i is out of range in proc_pg_write\n" , idx); +return -EINVAL ; + } + info = &(pginfos[idx]); + pg_result = &(info->pg_result[0 ]); +if (count <1 ) {sprintf (pg_result,"Wrong command format" );return -EINVAL ;
@@ -521,85 +885,206 @@if (debug)printk ("pg: %s,%lu\n" , name, count); -/* Only stop is allowed when we are running */ -if (!strcmp (name,"stop" )) { - forced_stop =1 ; -if (pg_busy) + info->forced_stop =1 ; +if (info->do_run_run) {strcpy (pg_result,"Stopping" ); + } +else { +strcpy (pg_result,"Already stopped...\n" ); + } + info->do_run_run =0 ;return count; } -if (pg_busy) { -strcpy (pg_result,"Busy" ); -return -EINVAL ; - } -if (!strcmp (name,"pkt_size" )) { len =num_arg (&buffer[i],10 , &value); i += len;if (value <14 +20 +8 ) value =14 +20 +8 ; - pkt_size = value; -sprintf (pg_result,"OK: pkt_size=%u" , pkt_size); + info->pkt_size = value; +sprintf (pg_result,"OK: pkt_size=%u" , info->pkt_size);return count; }if (!strcmp (name,"frags" )) { len =num_arg (&buffer[i],10 , &value); i += len; - nfrags = value; -sprintf (pg_result,"OK: frags=%u" , nfrags); + info->nfrags = value; +sprintf (pg_result,"OK: frags=%u" , info->nfrags);return count; }if (!strcmp (name,"ipg" )) { len =num_arg (&buffer[i],10 , &value); i += len; - pg_ipg = value; -sprintf (pg_result,"OK: ipg=%u" , pg_ipg); + info->pg_ipg = value; +sprintf (pg_result,"OK: ipg=%u" , info->pg_ipg); +return count; + } +if (!strcmp (name,"udp_src_min" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->udp_src_min = value; +sprintf (pg_result,"OK: udp_src_min=%u" , info->udp_src_min); +return count; + } +if (!strcmp (name,"udp_dst_min" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->udp_dst_min = value; +sprintf (pg_result,"OK: udp_dst_min=%u" , info->udp_dst_min); +return count; + } +if (!strcmp (name,"udp_src_max" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->udp_src_max = value; +sprintf (pg_result,"OK: udp_src_max=%u" , info->udp_src_max); +return count; + } +if (!strcmp (name,"udp_dst_max" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->udp_dst_max = value; +sprintf (pg_result,"OK: udp_dst_max=%u" , info->udp_dst_max);return count; }if (!strcmp (name,"multiskb" )) { len =num_arg (&buffer[i],10 , &value); i += len; - pg_multiskb = (value ?1 :0 ); -sprintf (pg_result,"OK: multiskb=%d" , pg_multiskb); + info->pg_multiskb = (value ?1 :0 ); +sprintf (pg_result,"OK: multiskb=%d" , info->pg_multiskb);return count; }if (!strcmp (name,"count" )) { len =num_arg (&buffer[i],10 , &value); i += len; - pg_count = value; -sprintf (pg_result,"OK: count=%u" , pg_count); + info->pg_count = value; +sprintf (pg_result,"OK: count=%llu" , info->pg_count); +return count; + } +if (!strcmp (name,"src_mac_count" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->src_mac_count = value; +sprintf (pg_result,"OK: src_mac_count=%d" , info->src_mac_count); +return count; + } +if (!strcmp (name,"dst_mac_count" )) { + len =num_arg (&buffer[i],10 , &value); + i += len; + info->dst_mac_count = value; +sprintf (pg_result,"OK: dst_mac_count=%d" , info->dst_mac_count);return count; }if (!strcmp (name,"odev" )) { - len =strn_len (&buffer[i],sizeof (pg_outdev) -1 ); -memset (pg_outdev,0 ,sizeof (pg_outdev)); -strncpy (pg_outdev, &buffer[i], len); + len =strn_len (&buffer[i],sizeof (info->pg_outdev) -1 ); +memset (info->pg_outdev,0 ,sizeof (info->pg_outdev)); +strncpy (info->pg_outdev, &buffer[i], len); + i += len; +sprintf (pg_result,"OK: odev=%s" , info->pg_outdev); +return count; + } +if (!strcmp (name,"flag" )) { +char f[32 ]; +memset (f,0 ,32 ); + len =strn_len (&buffer[i],sizeof (f) -1 ); +strncpy (f, &buffer[i], len); i += len; -sprintf (pg_result,"OK: odev=%s" , pg_outdev); +if (strcmp (f,"IPSRC_RND" ) ==0 ) { + info->flags |= (1 <<0 ); + } +else if (strcmp (f,"!IPSRC_RND" ) ==0 ) { + info->flags &= ~(1 <<0 ); + } +else if (strcmp (f,"IPDST_RND" ) ==0 ) { + info->flags |= (1 <<1 ); + } +else if (strcmp (f,"!IPDST_RND" ) ==0 ) { + info->flags &= ~(1 <<1 ); + } +else if (strcmp (f,"UDPSRC_RND" ) ==0 ) { + info->flags |= (1 <<2 ); + } +else if (strcmp (f,"!UDPSRC_RND" ) ==0 ) { + info->flags &= ~(1 <<2 ); + } +else if (strcmp (f,"UDPDST_RND" ) ==0 ) { + info->flags |= (1 <<3 ); + } +else if (strcmp (f,"!UDPDST_RND" ) ==0 ) { + info->flags &= ~(1 <<3 ); + } +else if (strcmp (f,"MACSRC_RND" ) ==0 ) { + info->flags |= (1 <<4 ); + } +else if (strcmp (f,"!MACSRC_RND" ) ==0 ) { + info->flags &= ~(1 <<4 ); + } +else if (strcmp (f,"MACDST_RND" ) ==0 ) { + info->flags |= (1 <<5 ); + } +else if (strcmp (f,"!MACDST_RND" ) ==0 ) { + info->flags &= ~(1 <<5 ); + } +else { +sprintf (pg_result,"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s" , + f, +"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n" ); +return count; + } +sprintf (pg_result,"OK: flags=0x%x" , info->flags);return count; } -if (!strcmp (name,"dst" )) { - len =strn_len (&buffer[i],sizeof (pg_dst) -1 ); -memset (pg_dst,0 ,sizeof (pg_dst)); -strncpy (pg_dst, &buffer[i], len); +if (!strcmp (name,"dst_min" ) || !strcmp (name,"dst" )) { + len =strn_len (&buffer[i],sizeof (info->dst_min) -1 ); +memset (info->dst_min,0 ,sizeof (info->dst_min)); +strncpy (info->dst_min, &buffer[i], len);if (debug) -printk ("pg: dst set to: %s\n" , pg_dst); +printk ("pg: dst_min set to: %s\n" , info->dst_min); i += len; -sprintf (pg_result,"OK: dst=%s" , pg_dst); +sprintf (pg_result,"OK: dst_min=%s" , info->dst_min); +return count; + } +if (!strcmp (name,"dst_max" )) { + len =strn_len (&buffer[i],sizeof (info->dst_max) -1 ); +memset (info->dst_max,0 ,sizeof (info->dst_max)); +strncpy (info->dst_max, &buffer[i], len); +if (debug) +printk ("pg: dst_max set to: %s\n" , info->dst_max); + i += len; +sprintf (pg_result,"OK: dst_max=%s" , info->dst_max); +return count; + } +if (!strcmp (name,"src_min" )) { + len =strn_len (&buffer[i],sizeof (info->src_min) -1 ); +memset (info->src_min,0 ,sizeof (info->src_min)); +strncpy (info->src_min, &buffer[i], len); +if (debug) +printk ("pg: src_min set to: %s\n" , info->src_min); + i += len; +sprintf (pg_result,"OK: src_min=%s" , info->src_min); +return count; + } +if (!strcmp (name,"src_max" )) { + len =strn_len (&buffer[i],sizeof (info->src_max) -1 ); +memset (info->src_max,0 ,sizeof (info->src_max)); +strncpy (info->src_max, &buffer[i], len); +if (debug) +printk ("pg: src_max set to: %s\n" , info->src_max); + i += len; +sprintf (pg_result,"OK: src_max=%s" , info->src_max);return count; }if (!strcmp (name,"dstmac" )) {char *v = valstr; -unsigned char *m = pg_dstmac; +unsigned char *m = info->dst_mac; len =strn_len (&buffer[i],sizeof (valstr) -1 );memset (valstr,0 ,sizeof (valstr));strncpy (valstr, &buffer[i], len); i += len; -for (*m =0 ;*v && m < pg_dstmac +6 ; v++) { +for (*m =0 ;*v && m < info->dst_mac +6 ; v++) {if (*v >='0' && *v <='9' ) { *m *=16 ; *m += *v -'0' ;
@@ -620,54 +1105,125 @@sprintf (pg_result,"OK: dstmac" );return count; } +if (!strcmp (name,"srcmac" )) { +char *v = valstr; +unsigned char *m = info->src_mac; + + len =strn_len (&buffer[i],sizeof (valstr) -1 ); +memset (valstr,0 ,sizeof (valstr)); +strncpy (valstr, &buffer[i], len); + i += len; + +for (*m =0 ;*v && m < info->src_mac +6 ; v++) { +if (*v >='0' && *v <='9' ) { + *m *=16 ; + *m += *v -'0' ; + } +if (*v >='A' && *v <='F' ) { + *m *=16 ; + *m += *v -'A' +10 ; + } +if (*v >='a' && *v <='f' ) { + *m *=16 ; + *m += *v -'a' +10 ; + } +if (*v ==':' ) { + m++; + *m =0 ; + } + } +sprintf (pg_result,"OK: srcmac" ); +return count; + }if (!strcmp (name,"inject" ) || !strcmp (name,"start" )) {MOD_INC_USE_COUNT ; - pg_busy =1 ; -strcpy (pg_result,"Starting" ); -pg_inject (); - pg_busy =0 ; +if (info->pg_busy) { +strcpy (info->pg_result,"Already running...\n" ); + } +else { + info->pg_busy =1 ; +strcpy (info->pg_result,"Starting" ); +pg_inject (info); + info->pg_busy =0 ; + }MOD_DEC_USE_COUNT ;return count; } -sprintf (pg_result,"No such parameter \"%s\"" , name); +sprintf (info->pg_result,"No such parameter \"%s\"" , name);return -EINVAL ; }static int __init pg_init (void ) { +int i;printk (version);cycles_calibrate ();if (pg_cpu_speed ==0 ) {printk ("pktgen: Error: your machine does not have working cycle counter.\n" );return -EINVAL ; } - pg_proc_ent =create_proc_entry ("net/pg" ,0600 ,0 ); -if (!pg_proc_ent) { -printk ("pktgen: Error: cannot create net/pg procfs entry.\n" ); -return -ENOMEM ; - } - pg_proc_ent->read_proc = proc_pg_read; - pg_proc_ent->write_proc = proc_pg_write; - pg_proc_ent->data =0 ; - - pg_busy_proc_ent =create_proc_entry ("net/pg_busy" ,0 ,0 ); -if (!pg_busy_proc_ent) { -printk ("pktgen: Error: cannot create net/pg_busy procfs entry.\n" ); -remove_proc_entry ("net/pg" ,NULL ); -return -ENOMEM ; - } - pg_busy_proc_ent->read_proc = proc_pg_busy_read; - pg_busy_proc_ent->data =0 ; -return 0 ; +for (i =0 ; i<MAX_PKTGEN ; i++) { +memset (&(pginfos[i]),0 ,sizeof (pginfos[i])); + pginfos[i].pkt_size =ETH_ZLEN ; + pginfos[i].nfrags =0 ; + pginfos[i].pg_multiskb = pg_multiskb_d; + pginfos[i].pg_ipg = pg_ipg_d; + pginfos[i].pg_count = pg_count_d; + pginfos[i].pg_sofar =0 ; + pginfos[i].hh[12 ] =0x08 ;/* fill in protocol. Rest is filled in later. */ + pginfos[i].hh[13 ] =0x00 ; + pginfos[i].udp_src_min =9 ;/* sink NULL */ + pginfos[i].udp_src_max =9 ; + pginfos[i].udp_dst_min =9 ; + pginfos[i].udp_dst_max =9 ; + +sprintf (pginfos[i].fname,"net/pg%i" , i); + pginfos[i].pg_proc_ent =create_proc_entry (pginfos[i].fname,0600 ,0 ); +if (!pginfos[i].pg_proc_ent) { +printk ("pktgen: Error: cannot create net/pg procfs entry.\n" ); + goto cleanup_mem; + } + pginfos[i].pg_proc_ent->read_proc = proc_pg_read; + pginfos[i].pg_proc_ent->write_proc = proc_pg_write; + pginfos[i].pg_proc_ent->data = (void *)(i); + +sprintf (pginfos[i].busy_fname,"net/pg_busy%i" , i); + pginfos[i].pg_busy_proc_ent =create_proc_entry (pginfos[i].busy_fname,0 ,0 ); +if (!pginfos[i].pg_busy_proc_ent) { +printk ("pktgen: Error: cannot create net/pg_busy procfs entry.\n" ); + goto cleanup_mem; + } + pginfos[i].pg_busy_proc_ent->read_proc = proc_pg_busy_read; + pginfos[i].pg_busy_proc_ent->data = (void *)(i); + } +return 0 ; + +cleanup_mem: +for (i =0 ; i<MAX_PKTGEN ; i++) { +if (strlen (pginfos[i].fname)) { +remove_proc_entry (pginfos[i].fname,NULL ); + } +if (strlen (pginfos[i].busy_fname)) { +remove_proc_entry (pginfos[i].busy_fname,NULL ); + } + } +return -ENOMEM ; } static void __exit pg_cleanup(void) { -remove_proc_entry ("net/pg" ,NULL ); -remove_proc_entry ("net/pg_busy" ,NULL ); +int i; +for (i =0 ; i<MAX_PKTGEN ; i++) { +if (strlen (pginfos[i].fname)) { +remove_proc_entry (pginfos[i].fname,NULL ); + } +if (strlen (pginfos[i].busy_fname)) { +remove_proc_entry (pginfos[i].busy_fname,NULL ); + } + } }module_init (pg_init);
@@ -676,7 +1232,7 @@MODULE_AUTHOR ("Robert Olsson <[email protected]" );MODULE_DESCRIPTION ("Packet Generator tool" );MODULE_LICENSE ("GPL" ); -MODULE_PARM (pg_count,"i" ); -MODULE_PARM (pg_ipg,"i" ); +MODULE_PARM (pg_count_d,"i" ); +MODULE_PARM (pg_ipg_d,"i" );MODULE_PARM (pg_cpu_speed,"i" ); -MODULE_PARM (pg_multiskb,"i" ); +MODULE_PARM (pg_multiskb_d,"i" );
Documentation/networking/pktgen.txt +33 -7
@@ -4,6 +4,9 @@ in the place where insmod may find it. 2. Cut script "ipg" (see below). 3. Edit script to set preferred device and destination IP address. +3a. Create more scripts for different interfaces. Up to thirty-two + pktgen processes can be configured and run at once by using the + 32 /proc/net/pg* files. 4. Run in shell: ". ipg" 5. After this two commands are defined: A. "pg" to start generator and to get results.
@@ -12,12 +15,33 @@ pgset "multiskb 0" use single SKB for all transmits pgset "pkt_size 9014" sets packet size to 9014 pgset "frags 5" packet will consist of 5 fragments - pgset "count 200000" sets number of packets to send + pgset "count 200000" sets number of packets to send, set to zero + for continious sends untill explicitly + stopped. pgset "ipg 5000" sets artificial gap inserted between packets to 5000 nanoseconds pgset "dst 10.0.0.1" sets IP destination address (BEWARE! This generator is very aggressive!) + pgset "dst_min 10.0.0.1" Same as dst + pgset "dst_max 10.0.0.254" Set the maximum destination IP. + pgset "src_min 10.0.0.1" Set the minimum (or only) source IP. + pgset "src_max 10.0.0.254" Set the maximum source IP. pgset "dstmac 00:00:00:00:00:00" sets MAC destination address + pgset "srcmac 00:00:00:00:00:00" sets MAC source address + pgset "src_mac_count 1" Sets the number of MACs we'll range through. The + 'minimum' MAC is what you set with srcmac. + pgset "dst_mac_count 1" Sets the number of MACs we'll range through. The + 'minimum' MAC is what you set with dstmac. + pgset "flag [name]" Set a flag to determine behaviour. Current flags + are: IPSRC_RND #IP Source is random (between min/max), + IPDST_RND, UDPSRC_RND, + UDPDST_RND, MACSRC_RND, MACDST_RND + pgset "udp_src_min 9" set UDP source port min, If < udp_src_max, then + cycle through the port range. + pgset "udp_src_max 9" set UDP source port max. + pgset "udp_dst_min 9" set UDP destination port min, If < udp_dst_max, then + cycle through the port range. + pgset "udp_dst_max 9" set UDP destination port max. pgset stop aborts injection Also, ^C aborts generator.
@@ -26,22 +50,24 @@ #! /bin/sh -modprobe pktgen.o +modprobe pktgen + +PGDEV=/proc/net/pg0 function pgset() { local result - echo $1 > /proc/net/pg + echo $1 > $PGDEV - result=`cat /proc/net/pg | fgrep "Result: OK:"` + result=`cat $PGDEV | fgrep "Result: OK:"` if [ "$result" = "" ]; then - cat /proc/net/pg | fgrep Result: + cat $PGDEV | fgrep Result: fi } function pg() { - echo inject > /proc/net/pg - cat /proc/net/pg + echo inject > $PGDEV + cat $PGDEV } pgset "odev eth0"
Ben, can you submit these pktgen improvements to Robert Olsson, the author of pktgen?
He has submitted updates to me recently, and your changes will surely collide.