diff -Naur -X /root/dontdiff linux-2.5.44-ori/Documentation/networking/pktgen.txt linux-2.5.44/Documentation/networking/pktgen.txt --- linux-2.5.44-ori/Documentation/networking/pktgen.txt 1969-12-31 21:00:00.000000000 -0300 +++ linux-2.5.44/Documentation/networking/pktgen.txt 2002-10-29 15:28:51.000000000 -0300 @@ -0,0 +1,77 @@ +How to use the Linux packet generator module. + +1. Enable CONFIG_NET_PKTGEN to compile and build pktgen.o, install it + 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/pktgen/pg* files. +4. Run in shell: ". ipg" +5. After this two commands are defined: + A. "pg" to start generator and to get results. + B. "pgset" to change generator parameters. F.e. + pgset "clone_skb 100" sets the number of coppies of the same packet + will be sent before a new packet is allocated + pgset "clone_skb 0" use multiple SKBs for packet generation + 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, 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. + +---- cut here + +#! /bin/sh + +modprobe pktgen + +PGDEV=/proc/net/pktgen/pg0 + +function pgset() { + local result + + echo $1 > $PGDEV + + result=`cat $PGDEV | fgrep "Result: OK:"` + if [ "$result" = "" ]; then + cat $PGDEV | fgrep Result: + fi +} + +function pg() { + echo inject > $PGDEV + cat $PGDEV +} + +pgset "odev eth0" +pgset "dst 0.0.0.0" + +---- cut here diff -Naur -X /root/dontdiff linux-2.5.44-ori/arch/i386/defconfig linux-2.5.44/arch/i386/defconfig --- linux-2.5.44-ori/arch/i386/defconfig 2002-10-19 01:02:24.000000000 -0300 +++ linux-2.5.44/arch/i386/defconfig 2002-10-29 15:55:04.000000000 -0300 @@ -443,6 +443,11 @@ # CONFIG_NET_SCHED is not set # +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# # Network device support # CONFIG_NETDEVICES=y diff -Naur -X /root/dontdiff linux-2.5.44-ori/net/Config.help linux-2.5.44/net/Config.help --- linux-2.5.44-ori/net/Config.help 2002-10-19 01:01:15.000000000 -0300 +++ linux-2.5.44/net/Config.help 2002-10-29 15:53:00.000000000 -0300 @@ -512,3 +512,18 @@ performance will be written to /proc/net/profile. If you don't know what it is about, you don't need it: say N. +CONFIG_NET_PKTGEN + This module will inject preconfigured packets, at a configurable + rate, out of a given interface. It is used for network interface + stress testing and performance analysis. If you don't understand + what was just said, you don't need it: say N. + + Documentation on how to use the packet generator can be found + at . + + This code is also available as a module called pktgen.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + + diff -Naur -X /root/dontdiff linux-2.5.44-ori/net/Config.in linux-2.5.44/net/Config.in --- linux-2.5.44-ori/net/Config.in 2002-10-19 01:01:18.000000000 -0300 +++ linux-2.5.44/net/Config.in 2002-10-29 14:13:11.000000000 -0300 @@ -93,4 +93,10 @@ #bool 'Network code profiler' CONFIG_NET_PROFILE endmenu +mainmenu_option next_comment +comment 'Network testing' +tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN +endmenu + + endmenu diff -Naur -X /root/dontdiff linux-2.5.44-ori/net/core/Makefile linux-2.5.44/net/core/Makefile --- linux-2.5.44-ori/net/core/Makefile 2002-10-19 01:02:31.000000000 -0300 +++ linux-2.5.44/net/core/Makefile 2002-10-29 14:13:33.000000000 -0300 @@ -17,6 +17,7 @@ obj-$(CONFIG_NETFILTER) += netfilter.o obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_PROFILE) += profile.o +obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NET_RADIO) += wireless.o # Ugly. I wish all wireless drivers were moved in drivers/net/wireless obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o diff -Naur -X /root/dontdiff linux-2.5.44-ori/net/core/pktgen.c linux-2.5.44/net/core/pktgen.c --- linux-2.5.44-ori/net/core/pktgen.c 1969-12-31 21:00:00.000000000 -0300 +++ linux-2.5.44/net/core/pktgen.c 2002-10-29 15:50:23.000000000 -0300 @@ -0,0 +1,1388 @@ +/* -*-linux-c-*- + * $Id: pktgen.c,v 1.8 2002/07/15 19:30:17 robert Exp $ + * pktgen.c: Packet Generator for performance evaluation. + * + * Copyright 2001, 2002 by Robert Olsson + * Uppsala University, Sweden + * + * A tool for loading the network with preconfigurated packets. + * The tool is implemented as a linux module. Parameters are output + * device, IPG (interpacket gap), number of packets, and whether + * to use multiple SKBs or just the same one. + * pktgen uses the installed interface's output routine. + * + * Additional hacking by: + * + * Jens.Laas@data.slu.se + * Improved by ANK. 010120. + * Improved by ANK even more. 010212. + * MAC address typo fixed. 010417 --ro + * Integrated. 020301 --DaveM + * Added multiskb option 020301 --DaveM + * Scaling of results. 020417--sigurdur@linpro.no + * 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 impact of around 1%, + * as far as I can tell. + * --Ben Greear + * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br) + * + * Renamed multiskb to clone_skb and cleaned up sending core for two distinct + * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0 + * as a "fastpath" with a configurable number of clones after alloc's. + * + * clone_skb=0 means all packets are allocated this also means ranges time + * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100 + * clones. + * + * Also moved to /proc/net/pktgen/ + * --ro + * + * See Documentation/networking/pktgen.txt for how to use this. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define cycles() ((u32)get_cycles()) + + +#define VERSION "pktgen version 1.2" +static char version[] __initdata = + "pktgen.c: v1.2: Packet Generator for packet performance testing.\n"; + +/* Used to help with determining the pkts on receive */ + +#define PKTGEN_MAGIC 0xbe9be955 + + +/* 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; + +#define F_IPSRC_RND (1<<0) /* IP-Src Random */ +#define F_IPDST_RND (1<<1) /* IP-Dst Random */ +#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */ +#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */ +#define F_MACSRC_RND (1<<4) /* MAC-Src Random */ +#define F_MACDST_RND (1<<5) /* MAC-Dst Random */ +#define F_SET_SRCMAC (1<<6) /* Specify-Src-Mac + (default is to use Interface's MAC Addr) */ +#define F_SET_SRCIP (1<<7) /* Specify-Src-IP + (default is to use Interface's IP Addr) */ + + + int pkt_size; /* = ETH_ZLEN; */ + int nfrags; + __u32 ipg; /* Default Interpacket gap in nsec */ + __u64 count; /* Default No packets to send */ + __u64 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 clone_skb; /* Use multiple SKBs during packet gen. If this number + * is greater than 1, then that many coppies of the same + * packet will be sent before a new packet is allocated. + * For instance, if you want to send 1024 identical packets + * before creating a new packet, set clone_skb to 1024. + */ + int busy; + int do_run_run; /* if this changes to false, the test will stop */ + + char 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 result[512]; + + /* proc file names */ + char fname[80]; + char busy_fname[80]; + + struct proc_dir_entry *proc_ent; + struct proc_dir_entry *busy_proc_ent; +}; + +struct pktgen_hdr { + __u32 pgh_magic; + __u32 seq_num; + struct timeval timestamp; +}; + +static int cpu_speed; +static int debug; + +/* Module parameters, defaults. */ +static int count_d = 100000; +static int ipg_d = 0; +static int clone_skb_d = 0; + + +#define MAX_PKTGEN 8 +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); +} + +#define PG_PROC_DIR "pktgen" +static struct proc_dir_entry *proc_dir = 0; + +static struct net_device *setup_inject(struct pktgen_info* info) +{ + struct net_device *odev; + + rtnl_lock(); + odev = __dev_get_by_name(info->outdev); + if (!odev) { + sprintf(info->result, "No such netdevice: \"%s\"", info->outdev); + goto out_unlock; + } + + if (odev->type != ARPHRD_ETHER) { + sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev); + goto out_unlock; + } + + if (!netif_running(odev)) { + sprintf(info->result, "Device is down: \"%s\"", info->outdev); + goto out_unlock; + } + + /* Default to the interface's mac if not explicitly set. */ + if (!(info->flags & F_SET_SRCMAC)) { + 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(); + + return odev; + +out_unlock: + rtnl_unlock(); + return NULL; +} + +static void nanospin(int ipg, struct pktgen_info* info) +{ + u32 idle_start, idle; + + idle_start = cycles(); + + for (;;) { + barrier(); + idle = cycles() - idle_start; + if (idle * 1000 >= ipg * cpu_speed) + break; + } + info->idle_acc += idle; +} + +static int calc_mhz(void) +{ + struct timeval start, stop; + u32 start_s, elapsed; + + do_gettimeofday(&start); + start_s = cycles(); + do { + barrier(); + elapsed = cycles() - start_s; + if (elapsed == 0) + return 0; + } while (elapsed < 1000 * 50000); + do_gettimeofday(&stop); + return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec)); +} + +static void cycles_calibrate(void) +{ + int i; + + for (i = 0; i < 3; i++) { + int res = calc_mhz(); + if (res > cpu_speed) + cpu_speed = res; + } +} + + +/* 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 & F_MACSRC_RND) { + 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 & F_MACDST_RND) { + 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 & F_UDPSRC_RND) { + 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 & F_UDPDST_RND) { + 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 & F_IPSRC_RND) { + 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 & F_IPDST_RND) { + 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 = NULL; + __u8 *eth; + struct udphdr *udph; + int datalen, iplen; + struct iphdr *iph; + struct pktgen_hdr *pgh = NULL; + + skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC); + if (!skb) { + sprintf(info->result, "No memory"); + return NULL; + } + + skb_reserve(skb, 16); + + /* Reserve for ethernet and IP header */ + eth = (__u8 *) skb_push(skb, 14); + iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)); + udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr)); + + /* 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 */ + + iph->ihl = 5; + iph->version = 4; + iph->ttl = 3; + iph->tos = 0; + iph->protocol = IPPROTO_UDP; /* UDP */ + iph->saddr = info->cur_saddr; + iph->daddr = info->cur_daddr; + iph->frag_off = 0; + iplen = 20 + 8 + datalen; + iph->tot_len = htons(iplen); + iph->check = 0; + iph->check = ip_fast_csum((void *) iph, iph->ihl); + skb->protocol = __constant_htons(ETH_P_IP); + skb->mac.raw = ((u8 *)iph) - 14; + skb->dev = odev; + skb->pkt_type = PACKET_HOST; + + if (info->nfrags <= 0) { + pgh = (struct pktgen_hdr *)skb_put(skb, datalen); + } else { + 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) { + skb_put(skb, datalen-frags*PAGE_SIZE); + datalen = frags*PAGE_SIZE; + } + + i = 0; + while (datalen > 0) { + struct page *page = alloc_pages(GFP_KERNEL, 0); + skb_shinfo(skb)->frags[i].page = page; + skb_shinfo(skb)->frags[i].page_offset = 0; + skb_shinfo(skb)->frags[i].size = + (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); + datalen -= skb_shinfo(skb)->frags[i].size; + skb->len += skb_shinfo(skb)->frags[i].size; + skb->data_len += skb_shinfo(skb)->frags[i].size; + i++; + skb_shinfo(skb)->nr_frags = i; + } + + while (i < frags) { + int rem; + + if (i == 0) + break; + + rem = skb_shinfo(skb)->frags[i - 1].size / 2; + if (rem == 0) + break; + + skb_shinfo(skb)->frags[i - 1].size -= rem; + + skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1]; + get_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i - 1].page; + skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i - 1].size; + skb_shinfo(skb)->frags[i].size = rem; + i++; + skb_shinfo(skb)->nr_frags = i; + } + } + + /* Stamp the time, and sequence number, convert them to network byte order */ + if (pgh) { + pgh->pgh_magic = 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 inject(struct pktgen_info* info) +{ + struct net_device *odev = NULL; + struct sk_buff *skb = NULL; + __u64 total = 0; + __u64 idle = 0; + __u64 lcount = 0; + int nr_frags = 0; + int last_ok = 1; /* Was last skb sent? + * Or a failed transmit of some sort? This will keep + * sequence numbers in order, for example. + */ + __u64 fp = 0; + __u32 fp_tmp = 0; + + odev = setup_inject(info); + if (!odev) + return; + + info->do_run_run = 1; /* Cranke yeself! */ + info->idle_acc = 0; + info->sofar = 0; + lcount = info->count; + + + /* Build our initial pkt and place it as a re-try pkt. */ + skb = fill_packet(odev, info); + if (skb == NULL) goto out_reldev; + + do_gettimeofday(&(info->started_at)); + + while(info->do_run_run) { + + /* Set a time-stamp, so build a new pkt each time */ + + if (last_ok) { + if (++fp_tmp >= info->clone_skb ) { + kfree_skb(skb); + skb = fill_packet(odev, info); + if (skb == NULL) { + break; + } + fp++; + fp_tmp = 0; /* reset counter */ + } + atomic_inc(&skb->users); + } + + nr_frags = skb_shinfo(skb)->nr_frags; + + spin_lock_bh(&odev->xmit_lock); + if (!netif_queue_stopped(odev)) { + + if (odev->hard_start_xmit(skb, odev)) { + if (net_ratelimit()) { + printk(KERN_INFO "Hard xmit error\n"); + } + info->errors++; + last_ok = 0; + } + else { + last_ok = 1; + info->sofar++; + info->seq_num++; + } + } + else { + /* Re-try it next time */ + last_ok = 0; + } + + + spin_unlock_bh(&odev->xmit_lock); + + if (info->ipg) { + /* Try not to busy-spin if we have larger sleep times. + * TODO: Investigate better ways to do this. + */ + if (info->ipg < 10000) { /* 10 usecs or less */ + nanospin(info->ipg, info); + } + else if (info->ipg < 10000000) { /* 10ms or less */ + udelay(info->ipg / 1000); + } + else { + mdelay(info->ipg / 1000000); + } + } + + if (signal_pending(current)) { + break; + } + + /* If lcount is zero, then run forever */ + if ((lcount != 0) && (--lcount == 0)) { + if (atomic_read(&skb->users) != 1) { + u32 idle_start, idle; + + idle_start = cycles(); + while (atomic_read(&skb->users) != 1) { + if (signal_pending(current)) { + break; + } + schedule(); + } + idle = cycles() - idle_start; + info->idle_acc += idle; + } + break; + } + + if (netif_queue_stopped(odev) || need_resched()) { + u32 idle_start, idle; + + idle_start = cycles(); + do { + if (signal_pending(current)) { + info->do_run_run = 0; + break; + } + if (!netif_running(odev)) { + info->do_run_run = 0; + break; + } + if (need_resched()) + schedule(); + else + do_softirq(); + } while (netif_queue_stopped(odev)); + idle = cycles() - idle_start; + info->idle_acc += idle; + } + }/* while we should be running */ + + do_gettimeofday(&(info->stopped_at)); + + total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 + + info->stopped_at.tv_usec - info->started_at.tv_usec; + + idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed); + + { + char *p = info->result; + __u64 pps = (__u32)(info->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", + (unsigned long long) total, + (unsigned long long) (total - idle), + (unsigned long long) idle, + (unsigned long long) info->sofar, + skb->len + 4, /* Add 4 to account for the ethernet checksum */ + nr_frags, + (unsigned long long) pps, + (unsigned long long) (bps / (u64) 1024 / (u64) 1024), + (unsigned long long) bps, + (unsigned long long) info->errors + ); + } + +out_reldev: + if (odev) { + dev_put(odev); + odev = NULL; + } + + /* TODO: Is this worth printing out (other than for debug?) */ + printk("fp = %llu\n", (unsigned long long) fp); + return; + +} + +/* proc/net/pktgen/pg */ + +static int proc_busy_read(char *buf , char **start, off_t offset, + int len, int *eof, void *data) +{ + char *p; + int idx = (int)(long)(data); + struct pktgen_info* info = NULL; + + if ((idx < 0) || (idx >= MAX_PKTGEN)) { + printk("ERROR: idx: %i is out of range in proc_write\n", idx); + return -EINVAL; + } + info = &(pginfos[idx]); + + p = buf; + p += sprintf(p, "%d\n", info->busy); + *eof = 1; + + return p-buf; +} + +static int proc_read(char *buf , char **start, off_t offset, + int len, int *eof, void *data) +{ + char *p; + int i; + int idx = (int)(long)(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_write\n", idx); + return -EINVAL; + } + info = &(pginfos[idx]); + + p = buf; + p += sprintf(p, "%s\n", VERSION); /* Help with parsing compatibility */ + p += sprintf(p, "Params: count %llu pkt_size: %u frags: %d ipg: %u clone_skb: %d odev \"%s\"\n", + (unsigned long long) info->count, + info->pkt_size, info->nfrags, info->ipg, + info->clone_skb, info->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 & F_IPSRC_RND) { + p += sprintf(p, "IPSRC_RND "); + } + if (info->flags & F_IPDST_RND) { + p += sprintf(p, "IPDST_RND "); + } + if (info->flags & F_UDPSRC_RND) { + p += sprintf(p, "UDPSRC_RND "); + } + if (info->flags & F_UDPDST_RND) { + p += sprintf(p, "UDPDST_RND "); + } + if (info->flags & F_MACSRC_RND) { + p += sprintf(p, "MACSRC_RND "); + } + if (info->flags & F_MACDST_RND) { + 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", + (unsigned long long) info->sofar, + (unsigned long long) info->errors, + (unsigned long long) sa, + (unsigned long long) stopped, + (unsigned long long) now, + (unsigned long long) 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->result[0]) + p += sprintf(p, "Result: %s\n", info->result); + else + p += sprintf(p, "Result: Idle\n"); + *eof = 1; + + return p - buf; +} + +static int count_trail_chars(const char *user_buffer, unsigned int maxlen) +{ + int i; + + for (i = 0; i < maxlen; i++) { + char c; + + if (get_user(c, &user_buffer[i])) + return -EFAULT; + switch (c) { + case '\"': + case '\n': + case '\r': + case '\t': + case ' ': + case '=': + break; + default: + goto done; + }; + } +done: + return i; +} + +static unsigned long num_arg(const char *user_buffer, unsigned long maxlen, + unsigned long *num) +{ + int i = 0; + + *num = 0; + + for(; i < maxlen; i++) { + char c; + + if (get_user(c, &user_buffer[i])) + return -EFAULT; + if ((c >= '0') && (c <= '9')) { + *num *= 10; + *num += c -'0'; + } else + break; + } + return i; +} + +static int strn_len(const char *user_buffer, unsigned int maxlen) +{ + int i = 0; + + for(; i < maxlen; i++) { + char c; + + if (get_user(c, &user_buffer[i])) + return -EFAULT; + switch (c) { + case '\"': + case '\n': + case '\r': + case '\t': + case ' ': + goto done_str; + default: + break; + }; + } +done_str: + return i; +} + +static int proc_write(struct file *file, const char *user_buffer, + unsigned long count, void *data) +{ + int i = 0, max, len; + char name[16], valstr[32]; + unsigned long value = 0; + int idx = (int)(long)(data); + struct pktgen_info* info = NULL; + char* result = NULL; + int tmp; + + if ((idx < 0) || (idx >= MAX_PKTGEN)) { + printk("ERROR: idx: %i is out of range in proc_write\n", idx); + return -EINVAL; + } + info = &(pginfos[idx]); + result = &(info->result[0]); + + if (count < 1) { + sprintf(result, "Wrong command format"); + return -EINVAL; + } + + max = count - i; + tmp = count_trail_chars(&user_buffer[i], max); + if (tmp < 0) + return tmp; + i += tmp; + + /* Read variable name */ + + len = strn_len(&user_buffer[i], sizeof(name) - 1); + if (len < 0) + return len; + memset(name, 0, sizeof(name)); + copy_from_user(name, &user_buffer[i], len); + i += len; + + max = count -i; + len = count_trail_chars(&user_buffer[i], max); + if (len < 0) + return len; + i += len; + + if (debug) + printk("pg: %s,%lu\n", name, count); + + if (!strcmp(name, "stop")) { + if (info->do_run_run) { + strcpy(result, "Stopping"); + } + else { + strcpy(result, "Already stopped...\n"); + } + info->do_run_run = 0; + return count; + } + + if (!strcmp(name, "pkt_size")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + if (value < 14+20+8) + value = 14+20+8; + info->pkt_size = value; + sprintf(result, "OK: pkt_size=%u", info->pkt_size); + return count; + } + if (!strcmp(name, "frags")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->nfrags = value; + sprintf(result, "OK: frags=%u", info->nfrags); + return count; + } + if (!strcmp(name, "ipg")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->ipg = value; + sprintf(result, "OK: ipg=%u", info->ipg); + return count; + } + if (!strcmp(name, "udp_src_min")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->udp_src_min = value; + sprintf(result, "OK: udp_src_min=%u", info->udp_src_min); + return count; + } + if (!strcmp(name, "udp_dst_min")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->udp_dst_min = value; + sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min); + return count; + } + if (!strcmp(name, "udp_src_max")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->udp_src_max = value; + sprintf(result, "OK: udp_src_max=%u", info->udp_src_max); + return count; + } + if (!strcmp(name, "udp_dst_max")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->udp_dst_max = value; + sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max); + return count; + } + if (!strcmp(name, "clone_skb")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->clone_skb = value; + + sprintf(result, "OK: clone_skb=%d", info->clone_skb); + return count; + } + if (!strcmp(name, "count")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->count = value; + sprintf(result, "OK: count=%llu", (unsigned long long) info->count); + return count; + } + if (!strcmp(name, "src_mac_count")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->src_mac_count = value; + sprintf(result, "OK: src_mac_count=%d", info->src_mac_count); + return count; + } + if (!strcmp(name, "dst_mac_count")) { + len = num_arg(&user_buffer[i], 10, &value); + if (len < 0) + return len; + i += len; + info->dst_mac_count = value; + sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count); + return count; + } + if (!strcmp(name, "odev")) { + len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1); + if (len < 0) + return len; + memset(info->outdev, 0, sizeof(info->outdev)); + copy_from_user(info->outdev, &user_buffer[i], len); + i += len; + sprintf(result, "OK: odev=%s", info->outdev); + return count; + } + if (!strcmp(name, "flag")) { + char f[32]; + memset(f, 0, 32); + len = strn_len(&user_buffer[i], sizeof(f) - 1); + if (len < 0) + return len; + copy_from_user(f, &user_buffer[i], len); + i += len; + if (strcmp(f, "IPSRC_RND") == 0) { + info->flags |= F_IPSRC_RND; + } + else if (strcmp(f, "!IPSRC_RND") == 0) { + info->flags &= ~F_IPSRC_RND; + } + else if (strcmp(f, "IPDST_RND") == 0) { + info->flags |= F_IPDST_RND; + } + else if (strcmp(f, "!IPDST_RND") == 0) { + info->flags &= ~F_IPDST_RND; + } + else if (strcmp(f, "UDPSRC_RND") == 0) { + info->flags |= F_UDPSRC_RND; + } + else if (strcmp(f, "!UDPSRC_RND") == 0) { + info->flags &= ~F_UDPSRC_RND; + } + else if (strcmp(f, "UDPDST_RND") == 0) { + info->flags |= F_UDPDST_RND; + } + else if (strcmp(f, "!UDPDST_RND") == 0) { + info->flags &= ~F_UDPDST_RND; + } + else if (strcmp(f, "MACSRC_RND") == 0) { + info->flags |= F_MACSRC_RND; + } + else if (strcmp(f, "!MACSRC_RND") == 0) { + info->flags &= ~F_MACSRC_RND; + } + else if (strcmp(f, "MACDST_RND") == 0) { + info->flags |= F_MACDST_RND; + } + else if (strcmp(f, "!MACDST_RND") == 0) { + info->flags &= ~F_MACDST_RND; + } + else { + sprintf(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(result, "OK: flags=0x%x", info->flags); + return count; + } + if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) { + len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1); + if (len < 0) + return len; + memset(info->dst_min, 0, sizeof(info->dst_min)); + copy_from_user(info->dst_min, &user_buffer[i], len); + if(debug) + printk("pg: dst_min set to: %s\n", info->dst_min); + i += len; + sprintf(result, "OK: dst_min=%s", info->dst_min); + return count; + } + if (!strcmp(name, "dst_max")) { + len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1); + if (len < 0) + return len; + memset(info->dst_max, 0, sizeof(info->dst_max)); + copy_from_user(info->dst_max, &user_buffer[i], len); + if(debug) + printk("pg: dst_max set to: %s\n", info->dst_max); + i += len; + sprintf(result, "OK: dst_max=%s", info->dst_max); + return count; + } + if (!strcmp(name, "src_min")) { + len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1); + if (len < 0) + return len; + memset(info->src_min, 0, sizeof(info->src_min)); + copy_from_user(info->src_min, &user_buffer[i], len); + if(debug) + printk("pg: src_min set to: %s\n", info->src_min); + i += len; + sprintf(result, "OK: src_min=%s", info->src_min); + return count; + } + if (!strcmp(name, "src_max")) { + len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1); + if (len < 0) + return len; + memset(info->src_max, 0, sizeof(info->src_max)); + copy_from_user(info->src_max, &user_buffer[i], len); + if(debug) + printk("pg: src_max set to: %s\n", info->src_max); + i += len; + sprintf(result, "OK: src_max=%s", info->src_max); + return count; + } + if (!strcmp(name, "dstmac")) { + char *v = valstr; + unsigned char *m = info->dst_mac; + + len = strn_len(&user_buffer[i], sizeof(valstr) - 1); + if (len < 0) + return len; + memset(valstr, 0, sizeof(valstr)); + copy_from_user(valstr, &user_buffer[i], len); + i += len; + + for(*m = 0;*v && m < info->dst_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(result, "OK: dstmac"); + return count; + } + if (!strcmp(name, "srcmac")) { + char *v = valstr; + unsigned char *m = info->src_mac; + + len = strn_len(&user_buffer[i], sizeof(valstr) - 1); + if (len < 0) + return len; + memset(valstr, 0, sizeof(valstr)); + copy_from_user(valstr, &user_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(result, "OK: srcmac"); + return count; + } + + if (!strcmp(name, "inject") || !strcmp(name, "start")) { + MOD_INC_USE_COUNT; + if (info->busy) { + strcpy(info->result, "Already running...\n"); + } + else { + info->busy = 1; + strcpy(info->result, "Starting"); + inject(info); + info->busy = 0; + } + MOD_DEC_USE_COUNT; + return count; + } + + sprintf(info->result, "No such parameter \"%s\"", name); + return -EINVAL; +} + + +int create_proc_dir(void) +{ + int len; + /* does proc_dir already exists */ + len = strlen(PG_PROC_DIR); + + for (proc_dir = proc_net->subdir; proc_dir; + proc_dir=proc_dir->next) { + if ((proc_dir->namelen == len) && + (! memcmp(proc_dir->name, PG_PROC_DIR, len))) + break; + } + if (!proc_dir) + proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net); + if (!proc_dir) return -ENODEV; + return 1; +} + +int remove_proc_dir(void) +{ + remove_proc_entry(PG_PROC_DIR, proc_net); + return 1; +} + +static int __init init(void) +{ + int i; + printk(version); + cycles_calibrate(); + if (cpu_speed == 0) { + printk("pktgen: Error: your machine does not have working cycle counter.\n"); + return -EINVAL; + } + + create_proc_dir(); + + for (i = 0; iread_proc = proc_read; + pginfos[i].proc_ent->write_proc = proc_write; + pginfos[i].proc_ent->data = (void*)(long)(i); + + sprintf(pginfos[i].busy_fname, "net/%s/pg_busy%i", PG_PROC_DIR, i); + pginfos[i].busy_proc_ent = create_proc_entry(pginfos[i].busy_fname, 0, 0); + if (!pginfos[i].busy_proc_ent) { + printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR); + goto cleanup_mem; + } + pginfos[i].busy_proc_ent->read_proc = proc_busy_read; + pginfos[i].busy_proc_ent->data = (void*)(long)(i); + } + return 0; + +cleanup_mem: + for (i = 0; i