/* -*-linux-c-*-
|
/* -*-linux-c-*-
|
* $Id: pktgen.c,v 1.1.1.1 2004-04-17 22:13:21 phoenix Exp $
|
* $Id: pktgen.c,v 1.1.1.1 2004-04-17 22:13:21 phoenix Exp $
|
* pktgen.c: Packet Generator for performance evaluation.
|
* pktgen.c: Packet Generator for performance evaluation.
|
*
|
*
|
* Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
|
* Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
|
* Uppsala University, Sweden
|
* Uppsala University, Sweden
|
*
|
*
|
* A tool for loading the network with preconfigurated packets.
|
* A tool for loading the network with preconfigurated packets.
|
* The tool is implemented as a linux module. Parameters are output
|
* The tool is implemented as a linux module. Parameters are output
|
* device, IPG (interpacket gap), number of packets, and whether
|
* device, IPG (interpacket gap), number of packets, and whether
|
* to use multiple SKBs or just the same one.
|
* to use multiple SKBs or just the same one.
|
* pktgen uses the installed interface's output routine.
|
* pktgen uses the installed interface's output routine.
|
*
|
*
|
* Additional hacking by:
|
* Additional hacking by:
|
*
|
*
|
* Jens.Laas@data.slu.se
|
* Jens.Laas@data.slu.se
|
* Improved by ANK. 010120.
|
* Improved by ANK. 010120.
|
* Improved by ANK even more. 010212.
|
* Improved by ANK even more. 010212.
|
* MAC address typo fixed. 010417 --ro
|
* MAC address typo fixed. 010417 --ro
|
* Integrated. 020301 --DaveM
|
* Integrated. 020301 --DaveM
|
* Added multiskb option 020301 --DaveM
|
* Added multiskb option 020301 --DaveM
|
* Scaling of results. 020417--sigurdur@linpro.no
|
* Scaling of results. 020417--sigurdur@linpro.no
|
* Significant re-work of the module:
|
* Significant re-work of the module:
|
* * Updated to support generation over multiple interfaces at once
|
* * Updated to support generation over multiple interfaces at once
|
* by creating 32 /proc/net/pg* files. Each file can be manipulated
|
* by creating 32 /proc/net/pg* files. Each file can be manipulated
|
* individually.
|
* individually.
|
* * Converted many counters to __u64 to allow longer runs.
|
* * Converted many counters to __u64 to allow longer runs.
|
* * Allow configuration of ranges, like min/max IP address, MACs,
|
* * Allow configuration of ranges, like min/max IP address, MACs,
|
* and UDP-ports, for both source and destination, and can
|
* and UDP-ports, for both source and destination, and can
|
* set to use a random distribution or sequentially walk the range.
|
* set to use a random distribution or sequentially walk the range.
|
* * Can now change some values after starting.
|
* * Can now change some values after starting.
|
* * Place 12-byte packet in UDP payload with magic number,
|
* * Place 12-byte packet in UDP payload with magic number,
|
* sequence number, and timestamp. Will write receiver next.
|
* sequence number, and timestamp. Will write receiver next.
|
* * The new changes seem to have a performance impact of around 1%,
|
* * The new changes seem to have a performance impact of around 1%,
|
* as far as I can tell.
|
* as far as I can tell.
|
* --Ben Greear <greearb@candelatech.com>
|
* --Ben Greear <greearb@candelatech.com>
|
*
|
*
|
* Renamed multiskb to clone_skb and cleaned up sending core for two distinct
|
* 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
|
* 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.
|
* 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
|
* 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
|
* stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
|
* clones.
|
* clones.
|
*
|
*
|
* Also moved to /proc/net/pktgen/
|
* Also moved to /proc/net/pktgen/
|
* --ro
|
* --ro
|
*
|
*
|
* Fix refcount off by one if first packet fails, potential null deref,
|
* Fix refcount off by one if first packet fails, potential null deref,
|
* memleak 030710- KJP
|
* memleak 030710- KJP
|
*
|
*
|
* See Documentation/networking/pktgen.txt for how to use this.
|
* See Documentation/networking/pktgen.txt for how to use this.
|
*/
|
*/
|
|
|
#include <linux/module.h>
|
#include <linux/module.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
#include <linux/slab.h>
|
#include <linux/slab.h>
|
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
#include <linux/pci.h>
|
#include <linux/pci.h>
|
#include <linux/delay.h>
|
#include <linux/delay.h>
|
#include <linux/init.h>
|
#include <linux/init.h>
|
#include <linux/inet.h>
|
#include <linux/inet.h>
|
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
#include <asm/bitops.h>
|
#include <asm/bitops.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
#include <asm/dma.h>
|
#include <asm/dma.h>
|
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
|
|
#include <linux/in.h>
|
#include <linux/in.h>
|
#include <linux/ip.h>
|
#include <linux/ip.h>
|
#include <linux/udp.h>
|
#include <linux/udp.h>
|
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
#include <linux/inetdevice.h>
|
#include <linux/inetdevice.h>
|
#include <linux/rtnetlink.h>
|
#include <linux/rtnetlink.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
#include <net/checksum.h>
|
#include <net/checksum.h>
|
#include <asm/timex.h>
|
#include <asm/timex.h>
|
|
|
#define cycles() ((u32)get_cycles())
|
#define cycles() ((u32)get_cycles())
|
|
|
|
|
#define VERSION "pktgen version 1.3"
|
#define VERSION "pktgen version 1.3"
|
static char version[] __initdata =
|
static char version[] __initdata =
|
"pktgen.c: v1.3: Packet Generator for packet performance testing.\n";
|
"pktgen.c: v1.3: Packet Generator for packet performance testing.\n";
|
|
|
/* Used to help with determining the pkts on receive */
|
/* Used to help with determining the pkts on receive */
|
|
|
#define PKTGEN_MAGIC 0xbe9be955
|
#define PKTGEN_MAGIC 0xbe9be955
|
|
|
|
|
/* Keep information per interface */
|
/* Keep information per interface */
|
struct pktgen_info {
|
struct pktgen_info {
|
/* Parameters */
|
/* Parameters */
|
|
|
/* If min != max, then we will either do a linear iteration, or
|
/* If min != max, then we will either do a linear iteration, or
|
* we will do a random selection from within the range.
|
* we will do a random selection from within the range.
|
*/
|
*/
|
__u32 flags;
|
__u32 flags;
|
|
|
#define F_IPSRC_RND (1<<0) /* IP-Src Random */
|
#define F_IPSRC_RND (1<<0) /* IP-Src Random */
|
#define F_IPDST_RND (1<<1) /* IP-Dst Random */
|
#define F_IPDST_RND (1<<1) /* IP-Dst Random */
|
#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
|
#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
|
#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
|
#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
|
#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
|
#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
|
#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
|
#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
|
#define F_SET_SRCMAC (1<<6) /* Specify-Src-Mac
|
#define F_SET_SRCMAC (1<<6) /* Specify-Src-Mac
|
(default is to use Interface's MAC Addr) */
|
(default is to use Interface's MAC Addr) */
|
#define F_SET_SRCIP (1<<7) /* Specify-Src-IP
|
#define F_SET_SRCIP (1<<7) /* Specify-Src-IP
|
(default is to use Interface's IP Addr) */
|
(default is to use Interface's IP Addr) */
|
|
|
|
|
int pkt_size; /* = ETH_ZLEN; */
|
int pkt_size; /* = ETH_ZLEN; */
|
int nfrags;
|
int nfrags;
|
__u32 ipg; /* Default Interpacket gap in nsec */
|
__u32 ipg; /* Default Interpacket gap in nsec */
|
__u64 count; /* Default No packets to send */
|
__u64 count; /* Default No packets to send */
|
__u64 sofar; /* How many pkts we've sent so far */
|
__u64 sofar; /* How many pkts we've sent so far */
|
__u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
|
__u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
|
struct timeval started_at;
|
struct timeval started_at;
|
struct timeval stopped_at;
|
struct timeval stopped_at;
|
__u64 idle_acc;
|
__u64 idle_acc;
|
__u32 seq_num;
|
__u32 seq_num;
|
|
|
int clone_skb; /* Use multiple SKBs during packet gen. If this number
|
int clone_skb; /* Use multiple SKBs during packet gen. If this number
|
* is greater than 1, then that many coppies of the same
|
* is greater than 1, then that many coppies of the same
|
* packet will be sent before a new packet is allocated.
|
* packet will be sent before a new packet is allocated.
|
* For instance, if you want to send 1024 identical packets
|
* For instance, if you want to send 1024 identical packets
|
* before creating a new packet, set clone_skb to 1024.
|
* before creating a new packet, set clone_skb to 1024.
|
*/
|
*/
|
int busy;
|
int busy;
|
int do_run_run; /* if this changes to false, the test will stop */
|
int do_run_run; /* if this changes to false, the test will stop */
|
|
|
char outdev[32];
|
char outdev[32];
|
char dst_min[32];
|
char dst_min[32];
|
char dst_max[32];
|
char dst_max[32];
|
char src_min[32];
|
char src_min[32];
|
char src_max[32];
|
char src_max[32];
|
|
|
/* If we're doing ranges, random or incremental, then this
|
/* If we're doing ranges, random or incremental, then this
|
* defines the min/max for those ranges.
|
* defines the min/max for those ranges.
|
*/
|
*/
|
__u32 saddr_min; /* inclusive, source IP address */
|
__u32 saddr_min; /* inclusive, source IP address */
|
__u32 saddr_max; /* exclusive, source IP address */
|
__u32 saddr_max; /* exclusive, source IP address */
|
__u32 daddr_min; /* inclusive, dest IP address */
|
__u32 daddr_min; /* inclusive, dest IP address */
|
__u32 daddr_max; /* exclusive, dest IP address */
|
__u32 daddr_max; /* exclusive, dest IP address */
|
|
|
__u16 udp_src_min; /* inclusive, source UDP port */
|
__u16 udp_src_min; /* inclusive, source UDP port */
|
__u16 udp_src_max; /* exclusive, source UDP port */
|
__u16 udp_src_max; /* exclusive, source UDP port */
|
__u16 udp_dst_min; /* inclusive, dest UDP port */
|
__u16 udp_dst_min; /* inclusive, dest UDP port */
|
__u16 udp_dst_max; /* exclusive, dest UDP port */
|
__u16 udp_dst_max; /* exclusive, dest UDP port */
|
|
|
__u32 src_mac_count; /* How many MACs to iterate through */
|
__u32 src_mac_count; /* How many MACs to iterate through */
|
__u32 dst_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 dst_mac[6];
|
unsigned char src_mac[6];
|
unsigned char src_mac[6];
|
|
|
__u32 cur_dst_mac_offset;
|
__u32 cur_dst_mac_offset;
|
__u32 cur_src_mac_offset;
|
__u32 cur_src_mac_offset;
|
__u32 cur_saddr;
|
__u32 cur_saddr;
|
__u32 cur_daddr;
|
__u32 cur_daddr;
|
__u16 cur_udp_dst;
|
__u16 cur_udp_dst;
|
__u16 cur_udp_src;
|
__u16 cur_udp_src;
|
|
|
__u8 hh[14];
|
__u8 hh[14];
|
/* = {
|
/* = {
|
0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
|
0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
|
|
|
We fill in SRC address later
|
We fill in SRC address later
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x08, 0x00
|
0x08, 0x00
|
};
|
};
|
*/
|
*/
|
__u16 pad; /* pad out the hh struct to an even 16 bytes */
|
__u16 pad; /* pad out the hh struct to an even 16 bytes */
|
char result[512];
|
char result[512];
|
|
|
/* proc file names */
|
/* proc file names */
|
char fname[80];
|
char fname[80];
|
char busy_fname[80];
|
char busy_fname[80];
|
|
|
struct proc_dir_entry *proc_ent;
|
struct proc_dir_entry *proc_ent;
|
struct proc_dir_entry *busy_proc_ent;
|
struct proc_dir_entry *busy_proc_ent;
|
};
|
};
|
|
|
struct pktgen_hdr {
|
struct pktgen_hdr {
|
__u32 pgh_magic;
|
__u32 pgh_magic;
|
__u32 seq_num;
|
__u32 seq_num;
|
struct timeval timestamp;
|
struct timeval timestamp;
|
};
|
};
|
|
|
static int cpu_speed;
|
static int cpu_speed;
|
static int debug;
|
static int debug;
|
|
|
/* Module parameters, defaults. */
|
/* Module parameters, defaults. */
|
static int count_d = 100000;
|
static int count_d = 100000;
|
static int ipg_d = 0;
|
static int ipg_d = 0;
|
static int clone_skb_d = 0;
|
static int clone_skb_d = 0;
|
|
|
|
|
#define MAX_PKTGEN 8
|
#define MAX_PKTGEN 8
|
static struct pktgen_info pginfos[MAX_PKTGEN];
|
static struct pktgen_info pginfos[MAX_PKTGEN];
|
|
|
|
|
/** Convert to miliseconds */
|
/** Convert to miliseconds */
|
inline __u64 tv_to_ms(const struct timeval* tv) {
|
inline __u64 tv_to_ms(const struct timeval* tv) {
|
__u64 ms = tv->tv_usec / 1000;
|
__u64 ms = tv->tv_usec / 1000;
|
ms += (__u64)tv->tv_sec * (__u64)1000;
|
ms += (__u64)tv->tv_sec * (__u64)1000;
|
return ms;
|
return ms;
|
}
|
}
|
|
|
inline __u64 getCurMs(void) {
|
inline __u64 getCurMs(void) {
|
struct timeval tv;
|
struct timeval tv;
|
do_gettimeofday(&tv);
|
do_gettimeofday(&tv);
|
return tv_to_ms(&tv);
|
return tv_to_ms(&tv);
|
}
|
}
|
|
|
#define PG_PROC_DIR "pktgen"
|
#define PG_PROC_DIR "pktgen"
|
static struct proc_dir_entry *proc_dir = 0;
|
static struct proc_dir_entry *proc_dir = 0;
|
|
|
static struct net_device *setup_inject(struct pktgen_info* info)
|
static struct net_device *setup_inject(struct pktgen_info* info)
|
{
|
{
|
struct net_device *odev;
|
struct net_device *odev;
|
|
|
rtnl_lock();
|
rtnl_lock();
|
odev = __dev_get_by_name(info->outdev);
|
odev = __dev_get_by_name(info->outdev);
|
if (!odev) {
|
if (!odev) {
|
sprintf(info->result, "No such netdevice: \"%s\"", info->outdev);
|
sprintf(info->result, "No such netdevice: \"%s\"", info->outdev);
|
goto out_unlock;
|
goto out_unlock;
|
}
|
}
|
|
|
if (odev->type != ARPHRD_ETHER) {
|
if (odev->type != ARPHRD_ETHER) {
|
sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev);
|
sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev);
|
goto out_unlock;
|
goto out_unlock;
|
}
|
}
|
|
|
if (!netif_running(odev)) {
|
if (!netif_running(odev)) {
|
sprintf(info->result, "Device is down: \"%s\"", info->outdev);
|
sprintf(info->result, "Device is down: \"%s\"", info->outdev);
|
goto out_unlock;
|
goto out_unlock;
|
}
|
}
|
|
|
/* Default to the interface's mac if not explicitly set. */
|
/* Default to the interface's mac if not explicitly set. */
|
if (!(info->flags & F_SET_SRCMAC)) {
|
if (!(info->flags & F_SET_SRCMAC)) {
|
memcpy(&(info->hh[6]), odev->dev_addr, 6);
|
memcpy(&(info->hh[6]), odev->dev_addr, 6);
|
}
|
}
|
else {
|
else {
|
memcpy(&(info->hh[6]), info->src_mac, 6);
|
memcpy(&(info->hh[6]), info->src_mac, 6);
|
}
|
}
|
|
|
/* Set up Dest MAC */
|
/* Set up Dest MAC */
|
memcpy(&(info->hh[0]), info->dst_mac, 6);
|
memcpy(&(info->hh[0]), info->dst_mac, 6);
|
|
|
info->saddr_min = 0;
|
info->saddr_min = 0;
|
info->saddr_max = 0;
|
info->saddr_max = 0;
|
if (strlen(info->src_min) == 0) {
|
if (strlen(info->src_min) == 0) {
|
if (odev->ip_ptr) {
|
if (odev->ip_ptr) {
|
struct in_device *in_dev = odev->ip_ptr;
|
struct in_device *in_dev = odev->ip_ptr;
|
|
|
if (in_dev->ifa_list) {
|
if (in_dev->ifa_list) {
|
info->saddr_min = in_dev->ifa_list->ifa_address;
|
info->saddr_min = in_dev->ifa_list->ifa_address;
|
info->saddr_max = info->saddr_min;
|
info->saddr_max = info->saddr_min;
|
}
|
}
|
}
|
}
|
}
|
}
|
else {
|
else {
|
info->saddr_min = in_aton(info->src_min);
|
info->saddr_min = in_aton(info->src_min);
|
info->saddr_max = in_aton(info->src_max);
|
info->saddr_max = in_aton(info->src_max);
|
}
|
}
|
|
|
info->daddr_min = in_aton(info->dst_min);
|
info->daddr_min = in_aton(info->dst_min);
|
info->daddr_max = in_aton(info->dst_max);
|
info->daddr_max = in_aton(info->dst_max);
|
|
|
/* Initialize current values. */
|
/* Initialize current values. */
|
info->cur_dst_mac_offset = 0;
|
info->cur_dst_mac_offset = 0;
|
info->cur_src_mac_offset = 0;
|
info->cur_src_mac_offset = 0;
|
info->cur_saddr = info->saddr_min;
|
info->cur_saddr = info->saddr_min;
|
info->cur_daddr = info->daddr_min;
|
info->cur_daddr = info->daddr_min;
|
info->cur_udp_dst = info->udp_dst_min;
|
info->cur_udp_dst = info->udp_dst_min;
|
info->cur_udp_src = info->udp_src_min;
|
info->cur_udp_src = info->udp_src_min;
|
|
|
atomic_inc(&odev->refcnt);
|
atomic_inc(&odev->refcnt);
|
rtnl_unlock();
|
rtnl_unlock();
|
|
|
return odev;
|
return odev;
|
|
|
out_unlock:
|
out_unlock:
|
rtnl_unlock();
|
rtnl_unlock();
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
static void nanospin(int ipg, struct pktgen_info* info)
|
static void nanospin(int ipg, struct pktgen_info* info)
|
{
|
{
|
u32 idle_start, idle;
|
u32 idle_start, idle;
|
|
|
idle_start = cycles();
|
idle_start = cycles();
|
|
|
for (;;) {
|
for (;;) {
|
barrier();
|
barrier();
|
idle = cycles() - idle_start;
|
idle = cycles() - idle_start;
|
if (idle * 1000 >= ipg * cpu_speed)
|
if (idle * 1000 >= ipg * cpu_speed)
|
break;
|
break;
|
}
|
}
|
info->idle_acc += idle;
|
info->idle_acc += idle;
|
}
|
}
|
|
|
static int calc_mhz(void)
|
static int calc_mhz(void)
|
{
|
{
|
struct timeval start, stop;
|
struct timeval start, stop;
|
u32 start_s, elapsed;
|
u32 start_s, elapsed;
|
|
|
do_gettimeofday(&start);
|
do_gettimeofday(&start);
|
start_s = cycles();
|
start_s = cycles();
|
do {
|
do {
|
barrier();
|
barrier();
|
elapsed = cycles() - start_s;
|
elapsed = cycles() - start_s;
|
if (elapsed == 0)
|
if (elapsed == 0)
|
return 0;
|
return 0;
|
} while (elapsed < 1000 * 50000);
|
} while (elapsed < 1000 * 50000);
|
do_gettimeofday(&stop);
|
do_gettimeofday(&stop);
|
return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
|
return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
|
}
|
}
|
|
|
static void cycles_calibrate(void)
|
static void cycles_calibrate(void)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
int res = calc_mhz();
|
int res = calc_mhz();
|
if (res > cpu_speed)
|
if (res > cpu_speed)
|
cpu_speed = res;
|
cpu_speed = res;
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Increment/randomize headers according to flags and current values
|
/* Increment/randomize headers according to flags and current values
|
* for IP src/dest, UDP src/dst port, MAC-Addr src/dst
|
* for IP src/dest, UDP src/dst port, MAC-Addr src/dst
|
*/
|
*/
|
static void mod_cur_headers(struct pktgen_info* info) {
|
static void mod_cur_headers(struct pktgen_info* info) {
|
__u32 imn;
|
__u32 imn;
|
__u32 imx;
|
__u32 imx;
|
|
|
/* Deal with source MAC */
|
/* Deal with source MAC */
|
if (info->src_mac_count > 1) {
|
if (info->src_mac_count > 1) {
|
__u32 mc;
|
__u32 mc;
|
__u32 tmp;
|
__u32 tmp;
|
if (info->flags & F_MACSRC_RND) {
|
if (info->flags & F_MACSRC_RND) {
|
mc = net_random() % (info->src_mac_count);
|
mc = net_random() % (info->src_mac_count);
|
}
|
}
|
else {
|
else {
|
mc = info->cur_src_mac_offset++;
|
mc = info->cur_src_mac_offset++;
|
if (info->cur_src_mac_offset > info->src_mac_count) {
|
if (info->cur_src_mac_offset > info->src_mac_count) {
|
info->cur_src_mac_offset = 0;
|
info->cur_src_mac_offset = 0;
|
}
|
}
|
}
|
}
|
|
|
tmp = info->src_mac[5] + (mc & 0xFF);
|
tmp = info->src_mac[5] + (mc & 0xFF);
|
info->hh[11] = tmp;
|
info->hh[11] = tmp;
|
tmp = (info->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
|
tmp = (info->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
|
info->hh[10] = tmp;
|
info->hh[10] = tmp;
|
tmp = (info->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
|
tmp = (info->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
|
info->hh[9] = tmp;
|
info->hh[9] = tmp;
|
tmp = (info->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
|
tmp = (info->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
|
info->hh[8] = tmp;
|
info->hh[8] = tmp;
|
tmp = (info->src_mac[1] + (tmp >> 8));
|
tmp = (info->src_mac[1] + (tmp >> 8));
|
info->hh[7] = tmp;
|
info->hh[7] = tmp;
|
}
|
}
|
|
|
/* Deal with Destination MAC */
|
/* Deal with Destination MAC */
|
if (info->dst_mac_count > 1) {
|
if (info->dst_mac_count > 1) {
|
__u32 mc;
|
__u32 mc;
|
__u32 tmp;
|
__u32 tmp;
|
if (info->flags & F_MACDST_RND) {
|
if (info->flags & F_MACDST_RND) {
|
mc = net_random() % (info->dst_mac_count);
|
mc = net_random() % (info->dst_mac_count);
|
}
|
}
|
else {
|
else {
|
mc = info->cur_dst_mac_offset++;
|
mc = info->cur_dst_mac_offset++;
|
if (info->cur_dst_mac_offset > info->dst_mac_count) {
|
if (info->cur_dst_mac_offset > info->dst_mac_count) {
|
info->cur_dst_mac_offset = 0;
|
info->cur_dst_mac_offset = 0;
|
}
|
}
|
}
|
}
|
|
|
tmp = info->dst_mac[5] + (mc & 0xFF);
|
tmp = info->dst_mac[5] + (mc & 0xFF);
|
info->hh[5] = tmp;
|
info->hh[5] = tmp;
|
tmp = (info->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
|
tmp = (info->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
|
info->hh[4] = tmp;
|
info->hh[4] = tmp;
|
tmp = (info->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
|
tmp = (info->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
|
info->hh[3] = tmp;
|
info->hh[3] = tmp;
|
tmp = (info->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
|
tmp = (info->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
|
info->hh[2] = tmp;
|
info->hh[2] = tmp;
|
tmp = (info->dst_mac[1] + (tmp >> 8));
|
tmp = (info->dst_mac[1] + (tmp >> 8));
|
info->hh[1] = tmp;
|
info->hh[1] = tmp;
|
}
|
}
|
|
|
if (info->udp_src_min < info->udp_src_max) {
|
if (info->udp_src_min < info->udp_src_max) {
|
if (info->flags & F_UDPSRC_RND) {
|
if (info->flags & F_UDPSRC_RND) {
|
info->cur_udp_src = ((net_random() % (info->udp_src_max - info->udp_src_min))
|
info->cur_udp_src = ((net_random() % (info->udp_src_max - info->udp_src_min))
|
+ info->udp_src_min);
|
+ info->udp_src_min);
|
}
|
}
|
else {
|
else {
|
info->cur_udp_src++;
|
info->cur_udp_src++;
|
if (info->cur_udp_src >= info->udp_src_max) {
|
if (info->cur_udp_src >= info->udp_src_max) {
|
info->cur_udp_src = info->udp_src_min;
|
info->cur_udp_src = info->udp_src_min;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (info->udp_dst_min < info->udp_dst_max) {
|
if (info->udp_dst_min < info->udp_dst_max) {
|
if (info->flags & F_UDPDST_RND) {
|
if (info->flags & F_UDPDST_RND) {
|
info->cur_udp_dst = ((net_random() % (info->udp_dst_max - info->udp_dst_min))
|
info->cur_udp_dst = ((net_random() % (info->udp_dst_max - info->udp_dst_min))
|
+ info->udp_dst_min);
|
+ info->udp_dst_min);
|
}
|
}
|
else {
|
else {
|
info->cur_udp_dst++;
|
info->cur_udp_dst++;
|
if (info->cur_udp_dst >= info->udp_dst_max) {
|
if (info->cur_udp_dst >= info->udp_dst_max) {
|
info->cur_udp_dst = info->udp_dst_min;
|
info->cur_udp_dst = info->udp_dst_min;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if ((imn = ntohl(info->saddr_min)) < (imx = ntohl(info->saddr_max))) {
|
if ((imn = ntohl(info->saddr_min)) < (imx = ntohl(info->saddr_max))) {
|
__u32 t;
|
__u32 t;
|
if (info->flags & F_IPSRC_RND) {
|
if (info->flags & F_IPSRC_RND) {
|
t = ((net_random() % (imx - imn)) + imn);
|
t = ((net_random() % (imx - imn)) + imn);
|
}
|
}
|
else {
|
else {
|
t = ntohl(info->cur_saddr);
|
t = ntohl(info->cur_saddr);
|
t++;
|
t++;
|
if (t >= imx) {
|
if (t >= imx) {
|
t = imn;
|
t = imn;
|
}
|
}
|
}
|
}
|
info->cur_saddr = htonl(t);
|
info->cur_saddr = htonl(t);
|
}
|
}
|
|
|
if ((imn = ntohl(info->daddr_min)) < (imx = ntohl(info->daddr_max))) {
|
if ((imn = ntohl(info->daddr_min)) < (imx = ntohl(info->daddr_max))) {
|
__u32 t;
|
__u32 t;
|
if (info->flags & F_IPDST_RND) {
|
if (info->flags & F_IPDST_RND) {
|
t = ((net_random() % (imx - imn)) + imn);
|
t = ((net_random() % (imx - imn)) + imn);
|
}
|
}
|
else {
|
else {
|
t = ntohl(info->cur_daddr);
|
t = ntohl(info->cur_daddr);
|
t++;
|
t++;
|
if (t >= imx) {
|
if (t >= imx) {
|
t = imn;
|
t = imn;
|
}
|
}
|
}
|
}
|
info->cur_daddr = htonl(t);
|
info->cur_daddr = htonl(t);
|
}
|
}
|
}/* mod_cur_headers */
|
}/* mod_cur_headers */
|
|
|
|
|
static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_info* info)
|
static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_info* info)
|
{
|
{
|
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
__u8 *eth;
|
__u8 *eth;
|
struct udphdr *udph;
|
struct udphdr *udph;
|
int datalen, iplen;
|
int datalen, iplen;
|
struct iphdr *iph;
|
struct iphdr *iph;
|
struct pktgen_hdr *pgh = NULL;
|
struct pktgen_hdr *pgh = NULL;
|
|
|
skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC);
|
skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC);
|
if (!skb) {
|
if (!skb) {
|
sprintf(info->result, "No memory");
|
sprintf(info->result, "No memory");
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
skb_reserve(skb, 16);
|
skb_reserve(skb, 16);
|
|
|
/* Reserve for ethernet and IP header */
|
/* Reserve for ethernet and IP header */
|
eth = (__u8 *) skb_push(skb, 14);
|
eth = (__u8 *) skb_push(skb, 14);
|
iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
|
iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
|
udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
|
udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
|
|
|
/* Update any of the values, used when we're incrementing various
|
/* Update any of the values, used when we're incrementing various
|
* fields.
|
* fields.
|
*/
|
*/
|
mod_cur_headers(info);
|
mod_cur_headers(info);
|
|
|
memcpy(eth, info->hh, 14);
|
memcpy(eth, info->hh, 14);
|
|
|
datalen = info->pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
|
datalen = info->pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
|
if (datalen < sizeof(struct pktgen_hdr)) {
|
if (datalen < sizeof(struct pktgen_hdr)) {
|
datalen = sizeof(struct pktgen_hdr);
|
datalen = sizeof(struct pktgen_hdr);
|
}
|
}
|
|
|
udph->source = htons(info->cur_udp_src);
|
udph->source = htons(info->cur_udp_src);
|
udph->dest = htons(info->cur_udp_dst);
|
udph->dest = htons(info->cur_udp_dst);
|
udph->len = htons(datalen + 8); /* DATA + udphdr */
|
udph->len = htons(datalen + 8); /* DATA + udphdr */
|
udph->check = 0; /* No checksum */
|
udph->check = 0; /* No checksum */
|
|
|
iph->ihl = 5;
|
iph->ihl = 5;
|
iph->version = 4;
|
iph->version = 4;
|
iph->ttl = 3;
|
iph->ttl = 3;
|
iph->tos = 0;
|
iph->tos = 0;
|
iph->protocol = IPPROTO_UDP; /* UDP */
|
iph->protocol = IPPROTO_UDP; /* UDP */
|
iph->saddr = info->cur_saddr;
|
iph->saddr = info->cur_saddr;
|
iph->daddr = info->cur_daddr;
|
iph->daddr = info->cur_daddr;
|
iph->frag_off = 0;
|
iph->frag_off = 0;
|
iplen = 20 + 8 + datalen;
|
iplen = 20 + 8 + datalen;
|
iph->tot_len = htons(iplen);
|
iph->tot_len = htons(iplen);
|
iph->check = 0;
|
iph->check = 0;
|
iph->check = ip_fast_csum((void *) iph, iph->ihl);
|
iph->check = ip_fast_csum((void *) iph, iph->ihl);
|
skb->protocol = __constant_htons(ETH_P_IP);
|
skb->protocol = __constant_htons(ETH_P_IP);
|
skb->mac.raw = ((u8 *)iph) - 14;
|
skb->mac.raw = ((u8 *)iph) - 14;
|
skb->dev = odev;
|
skb->dev = odev;
|
skb->pkt_type = PACKET_HOST;
|
skb->pkt_type = PACKET_HOST;
|
|
|
if (info->nfrags <= 0) {
|
if (info->nfrags <= 0) {
|
pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
|
pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
|
} else {
|
} else {
|
int frags = info->nfrags;
|
int frags = info->nfrags;
|
int i;
|
int i;
|
|
|
/* TODO: Verify this is OK...it sure is ugly. --Ben */
|
/* TODO: Verify this is OK...it sure is ugly. --Ben */
|
pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
|
pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
|
|
|
if (frags > MAX_SKB_FRAGS)
|
if (frags > MAX_SKB_FRAGS)
|
frags = MAX_SKB_FRAGS;
|
frags = MAX_SKB_FRAGS;
|
if (datalen > frags*PAGE_SIZE) {
|
if (datalen > frags*PAGE_SIZE) {
|
skb_put(skb, datalen-frags*PAGE_SIZE);
|
skb_put(skb, datalen-frags*PAGE_SIZE);
|
datalen = frags*PAGE_SIZE;
|
datalen = frags*PAGE_SIZE;
|
}
|
}
|
|
|
i = 0;
|
i = 0;
|
while (datalen > 0) {
|
while (datalen > 0) {
|
struct page *page = alloc_pages(GFP_KERNEL, 0);
|
struct page *page = alloc_pages(GFP_KERNEL, 0);
|
skb_shinfo(skb)->frags[i].page = page;
|
skb_shinfo(skb)->frags[i].page = page;
|
skb_shinfo(skb)->frags[i].page_offset = 0;
|
skb_shinfo(skb)->frags[i].page_offset = 0;
|
skb_shinfo(skb)->frags[i].size =
|
skb_shinfo(skb)->frags[i].size =
|
(datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
|
(datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
|
datalen -= skb_shinfo(skb)->frags[i].size;
|
datalen -= skb_shinfo(skb)->frags[i].size;
|
skb->len += skb_shinfo(skb)->frags[i].size;
|
skb->len += skb_shinfo(skb)->frags[i].size;
|
skb->data_len += skb_shinfo(skb)->frags[i].size;
|
skb->data_len += skb_shinfo(skb)->frags[i].size;
|
i++;
|
i++;
|
skb_shinfo(skb)->nr_frags = i;
|
skb_shinfo(skb)->nr_frags = i;
|
}
|
}
|
|
|
while (i < frags) {
|
while (i < frags) {
|
int rem;
|
int rem;
|
|
|
if (i == 0)
|
if (i == 0)
|
break;
|
break;
|
|
|
rem = skb_shinfo(skb)->frags[i - 1].size / 2;
|
rem = skb_shinfo(skb)->frags[i - 1].size / 2;
|
if (rem == 0)
|
if (rem == 0)
|
break;
|
break;
|
|
|
skb_shinfo(skb)->frags[i - 1].size -= rem;
|
skb_shinfo(skb)->frags[i - 1].size -= rem;
|
|
|
skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
|
skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
|
get_page(skb_shinfo(skb)->frags[i].page);
|
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 = 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].page_offset += skb_shinfo(skb)->frags[i - 1].size;
|
skb_shinfo(skb)->frags[i].size = rem;
|
skb_shinfo(skb)->frags[i].size = rem;
|
i++;
|
i++;
|
skb_shinfo(skb)->nr_frags = i;
|
skb_shinfo(skb)->nr_frags = i;
|
}
|
}
|
}
|
}
|
|
|
/* Stamp the time, and sequence number, convert them to network byte order */
|
/* Stamp the time, and sequence number, convert them to network byte order */
|
if (pgh) {
|
if (pgh) {
|
pgh->pgh_magic = htonl(PKTGEN_MAGIC);
|
pgh->pgh_magic = htonl(PKTGEN_MAGIC);
|
do_gettimeofday(&(pgh->timestamp));
|
do_gettimeofday(&(pgh->timestamp));
|
pgh->timestamp.tv_usec = htonl(pgh->timestamp.tv_usec);
|
pgh->timestamp.tv_usec = htonl(pgh->timestamp.tv_usec);
|
pgh->timestamp.tv_sec = htonl(pgh->timestamp.tv_sec);
|
pgh->timestamp.tv_sec = htonl(pgh->timestamp.tv_sec);
|
pgh->seq_num = htonl(info->seq_num);
|
pgh->seq_num = htonl(info->seq_num);
|
}
|
}
|
|
|
return skb;
|
return skb;
|
}
|
}
|
|
|
|
|
static void inject(struct pktgen_info* info)
|
static void inject(struct pktgen_info* info)
|
{
|
{
|
struct net_device *odev = NULL;
|
struct net_device *odev = NULL;
|
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
__u64 total = 0;
|
__u64 total = 0;
|
__u64 idle = 0;
|
__u64 idle = 0;
|
__u64 lcount = 0;
|
__u64 lcount = 0;
|
int nr_frags = 0;
|
int nr_frags = 0;
|
int last_ok = 1; /* Was last skb sent?
|
int last_ok = 1; /* Was last skb sent?
|
* Or a failed transmit of some sort? This will keep
|
* Or a failed transmit of some sort? This will keep
|
* sequence numbers in order, for example.
|
* sequence numbers in order, for example.
|
*/
|
*/
|
__u64 fp = 0;
|
__u64 fp = 0;
|
__u32 fp_tmp = 0;
|
__u32 fp_tmp = 0;
|
|
|
odev = setup_inject(info);
|
odev = setup_inject(info);
|
if (!odev)
|
if (!odev)
|
return;
|
return;
|
|
|
info->do_run_run = 1; /* Cranke yeself! */
|
info->do_run_run = 1; /* Cranke yeself! */
|
info->idle_acc = 0;
|
info->idle_acc = 0;
|
info->sofar = 0;
|
info->sofar = 0;
|
lcount = info->count;
|
lcount = info->count;
|
|
|
|
|
/* Build our initial pkt and place it as a re-try pkt. */
|
/* Build our initial pkt and place it as a re-try pkt. */
|
skb = fill_packet(odev, info);
|
skb = fill_packet(odev, info);
|
if (skb == NULL) goto out_reldev;
|
if (skb == NULL) goto out_reldev;
|
|
|
do_gettimeofday(&(info->started_at));
|
do_gettimeofday(&(info->started_at));
|
|
|
while(info->do_run_run) {
|
while(info->do_run_run) {
|
|
|
/* Set a time-stamp, so build a new pkt each time */
|
/* Set a time-stamp, so build a new pkt each time */
|
|
|
if (last_ok) {
|
if (last_ok) {
|
if (++fp_tmp >= info->clone_skb ) {
|
if (++fp_tmp >= info->clone_skb ) {
|
kfree_skb(skb);
|
kfree_skb(skb);
|
skb = fill_packet(odev, info);
|
skb = fill_packet(odev, info);
|
if (skb == NULL) {
|
if (skb == NULL) {
|
goto out_reldev;
|
goto out_reldev;
|
}
|
}
|
fp++;
|
fp++;
|
fp_tmp = 0; /* reset counter */
|
fp_tmp = 0; /* reset counter */
|
}
|
}
|
}
|
}
|
|
|
nr_frags = skb_shinfo(skb)->nr_frags;
|
nr_frags = skb_shinfo(skb)->nr_frags;
|
|
|
spin_lock_bh(&odev->xmit_lock);
|
spin_lock_bh(&odev->xmit_lock);
|
if (!netif_queue_stopped(odev)) {
|
if (!netif_queue_stopped(odev)) {
|
|
|
atomic_inc(&skb->users);
|
atomic_inc(&skb->users);
|
|
|
if (odev->hard_start_xmit(skb, odev)) {
|
if (odev->hard_start_xmit(skb, odev)) {
|
|
|
atomic_dec(&skb->users);
|
atomic_dec(&skb->users);
|
if (net_ratelimit()) {
|
if (net_ratelimit()) {
|
printk(KERN_INFO "Hard xmit error\n");
|
printk(KERN_INFO "Hard xmit error\n");
|
}
|
}
|
info->errors++;
|
info->errors++;
|
last_ok = 0;
|
last_ok = 0;
|
}
|
}
|
else {
|
else {
|
last_ok = 1;
|
last_ok = 1;
|
info->sofar++;
|
info->sofar++;
|
info->seq_num++;
|
info->seq_num++;
|
}
|
}
|
}
|
}
|
else {
|
else {
|
/* Re-try it next time */
|
/* Re-try it next time */
|
last_ok = 0;
|
last_ok = 0;
|
}
|
}
|
|
|
|
|
spin_unlock_bh(&odev->xmit_lock);
|
spin_unlock_bh(&odev->xmit_lock);
|
|
|
if (info->ipg) {
|
if (info->ipg) {
|
/* Try not to busy-spin if we have larger sleep times.
|
/* Try not to busy-spin if we have larger sleep times.
|
* TODO: Investigate better ways to do this.
|
* TODO: Investigate better ways to do this.
|
*/
|
*/
|
if (info->ipg < 10000) { /* 10 usecs or less */
|
if (info->ipg < 10000) { /* 10 usecs or less */
|
nanospin(info->ipg, info);
|
nanospin(info->ipg, info);
|
}
|
}
|
else if (info->ipg < 10000000) { /* 10ms or less */
|
else if (info->ipg < 10000000) { /* 10ms or less */
|
udelay(info->ipg / 1000);
|
udelay(info->ipg / 1000);
|
}
|
}
|
else {
|
else {
|
mdelay(info->ipg / 1000000);
|
mdelay(info->ipg / 1000000);
|
}
|
}
|
}
|
}
|
|
|
if (signal_pending(current)) {
|
if (signal_pending(current)) {
|
break;
|
break;
|
}
|
}
|
|
|
/* If lcount is zero, then run forever */
|
/* If lcount is zero, then run forever */
|
if ((lcount != 0) && (--lcount == 0)) {
|
if ((lcount != 0) && (--lcount == 0)) {
|
if (atomic_read(&skb->users) != 1) {
|
if (atomic_read(&skb->users) != 1) {
|
u32 idle_start, idle;
|
u32 idle_start, idle;
|
|
|
idle_start = cycles();
|
idle_start = cycles();
|
while (atomic_read(&skb->users) != 1) {
|
while (atomic_read(&skb->users) != 1) {
|
if (signal_pending(current)) {
|
if (signal_pending(current)) {
|
break;
|
break;
|
}
|
}
|
schedule();
|
schedule();
|
}
|
}
|
idle = cycles() - idle_start;
|
idle = cycles() - idle_start;
|
info->idle_acc += idle;
|
info->idle_acc += idle;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
if (netif_queue_stopped(odev) || current->need_resched) {
|
if (netif_queue_stopped(odev) || current->need_resched) {
|
u32 idle_start, idle;
|
u32 idle_start, idle;
|
|
|
idle_start = cycles();
|
idle_start = cycles();
|
do {
|
do {
|
if (signal_pending(current)) {
|
if (signal_pending(current)) {
|
info->do_run_run = 0;
|
info->do_run_run = 0;
|
break;
|
break;
|
}
|
}
|
if (!netif_running(odev)) {
|
if (!netif_running(odev)) {
|
info->do_run_run = 0;
|
info->do_run_run = 0;
|
break;
|
break;
|
}
|
}
|
if (current->need_resched)
|
if (current->need_resched)
|
schedule();
|
schedule();
|
else
|
else
|
do_softirq();
|
do_softirq();
|
} while (netif_queue_stopped(odev));
|
} while (netif_queue_stopped(odev));
|
idle = cycles() - idle_start;
|
idle = cycles() - idle_start;
|
info->idle_acc += idle;
|
info->idle_acc += idle;
|
}
|
}
|
}/* while we should be running */
|
}/* while we should be running */
|
|
|
do_gettimeofday(&(info->stopped_at));
|
do_gettimeofday(&(info->stopped_at));
|
|
|
total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 +
|
total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 +
|
info->stopped_at.tv_usec - info->started_at.tv_usec;
|
info->stopped_at.tv_usec - info->started_at.tv_usec;
|
|
|
idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed);
|
idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed);
|
|
|
{
|
{
|
char *p = info->result;
|
char *p = info->result;
|
__u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000);
|
__u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000);
|
__u64 bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */
|
__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",
|
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,
|
(unsigned long long) (total - idle),
|
(unsigned long long) (total - idle),
|
(unsigned long long) idle,
|
(unsigned long long) idle,
|
(unsigned long long) info->sofar,
|
(unsigned long long) info->sofar,
|
skb->len + 4, /* Add 4 to account for the ethernet checksum */
|
skb->len + 4, /* Add 4 to account for the ethernet checksum */
|
nr_frags,
|
nr_frags,
|
(unsigned long long) pps,
|
(unsigned long long) pps,
|
(unsigned long long) (bps / (u64) 1024 / (u64) 1024),
|
(unsigned long long) (bps / (u64) 1024 / (u64) 1024),
|
(unsigned long long) bps,
|
(unsigned long long) bps,
|
(unsigned long long) info->errors
|
(unsigned long long) info->errors
|
);
|
);
|
}
|
}
|
|
|
kfree_skb(skb);
|
kfree_skb(skb);
|
|
|
out_reldev:
|
out_reldev:
|
if (odev) {
|
if (odev) {
|
dev_put(odev);
|
dev_put(odev);
|
odev = NULL;
|
odev = NULL;
|
}
|
}
|
|
|
return;
|
return;
|
|
|
}
|
}
|
|
|
/* proc/net/pktgen/pg */
|
/* proc/net/pktgen/pg */
|
|
|
static int proc_busy_read(char *buf , char **start, off_t offset,
|
static int proc_busy_read(char *buf , char **start, off_t offset,
|
int len, int *eof, void *data)
|
int len, int *eof, void *data)
|
{
|
{
|
char *p;
|
char *p;
|
int idx = (int)(long)(data);
|
int idx = (int)(long)(data);
|
struct pktgen_info* info = NULL;
|
struct pktgen_info* info = NULL;
|
|
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
info = &(pginfos[idx]);
|
info = &(pginfos[idx]);
|
|
|
p = buf;
|
p = buf;
|
p += sprintf(p, "%d\n", info->busy);
|
p += sprintf(p, "%d\n", info->busy);
|
*eof = 1;
|
*eof = 1;
|
|
|
return p-buf;
|
return p-buf;
|
}
|
}
|
|
|
static int proc_read(char *buf , char **start, off_t offset,
|
static int proc_read(char *buf , char **start, off_t offset,
|
int len, int *eof, void *data)
|
int len, int *eof, void *data)
|
{
|
{
|
char *p;
|
char *p;
|
int i;
|
int i;
|
int idx = (int)(long)(data);
|
int idx = (int)(long)(data);
|
struct pktgen_info* info = NULL;
|
struct pktgen_info* info = NULL;
|
__u64 sa;
|
__u64 sa;
|
__u64 stopped;
|
__u64 stopped;
|
__u64 now = getCurMs();
|
__u64 now = getCurMs();
|
|
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
info = &(pginfos[idx]);
|
info = &(pginfos[idx]);
|
|
|
p = buf;
|
p = buf;
|
p += sprintf(p, "%s\n", VERSION); /* Help with parsing compatibility */
|
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",
|
p += sprintf(p, "Params: count %llu pkt_size: %u frags: %d ipg: %u clone_skb: %d odev \"%s\"\n",
|
(unsigned long long) info->count,
|
(unsigned long long) info->count,
|
info->pkt_size, info->nfrags, info->ipg,
|
info->pkt_size, info->nfrags, info->ipg,
|
info->clone_skb, info->outdev);
|
info->clone_skb, info->outdev);
|
p += sprintf(p, " dst_min: %s dst_max: %s src_min: %s src_max: %s\n",
|
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);
|
info->dst_min, info->dst_max, info->src_min, info->src_max);
|
p += sprintf(p, " src_mac: ");
|
p += sprintf(p, " src_mac: ");
|
for (i = 0; i < 6; i++) {
|
for (i = 0; i < 6; i++) {
|
p += sprintf(p, "%02X%s", info->src_mac[i], i == 5 ? " " : ":");
|
p += sprintf(p, "%02X%s", info->src_mac[i], i == 5 ? " " : ":");
|
}
|
}
|
p += sprintf(p, "dst_mac: ");
|
p += sprintf(p, "dst_mac: ");
|
for (i = 0; i < 6; i++) {
|
for (i = 0; i < 6; i++) {
|
p += sprintf(p, "%02X%s", info->dst_mac[i], i == 5 ? "\n" : ":");
|
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",
|
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_src_min, info->udp_src_max, info->udp_dst_min,
|
info->udp_dst_max);
|
info->udp_dst_max);
|
p += sprintf(p, " src_mac_count: %d dst_mac_count: %d\n Flags: ",
|
p += sprintf(p, " src_mac_count: %d dst_mac_count: %d\n Flags: ",
|
info->src_mac_count, info->dst_mac_count);
|
info->src_mac_count, info->dst_mac_count);
|
if (info->flags & F_IPSRC_RND) {
|
if (info->flags & F_IPSRC_RND) {
|
p += sprintf(p, "IPSRC_RND ");
|
p += sprintf(p, "IPSRC_RND ");
|
}
|
}
|
if (info->flags & F_IPDST_RND) {
|
if (info->flags & F_IPDST_RND) {
|
p += sprintf(p, "IPDST_RND ");
|
p += sprintf(p, "IPDST_RND ");
|
}
|
}
|
if (info->flags & F_UDPSRC_RND) {
|
if (info->flags & F_UDPSRC_RND) {
|
p += sprintf(p, "UDPSRC_RND ");
|
p += sprintf(p, "UDPSRC_RND ");
|
}
|
}
|
if (info->flags & F_UDPDST_RND) {
|
if (info->flags & F_UDPDST_RND) {
|
p += sprintf(p, "UDPDST_RND ");
|
p += sprintf(p, "UDPDST_RND ");
|
}
|
}
|
if (info->flags & F_MACSRC_RND) {
|
if (info->flags & F_MACSRC_RND) {
|
p += sprintf(p, "MACSRC_RND ");
|
p += sprintf(p, "MACSRC_RND ");
|
}
|
}
|
if (info->flags & F_MACDST_RND) {
|
if (info->flags & F_MACDST_RND) {
|
p += sprintf(p, "MACDST_RND ");
|
p += sprintf(p, "MACDST_RND ");
|
}
|
}
|
p += sprintf(p, "\n");
|
p += sprintf(p, "\n");
|
|
|
sa = tv_to_ms(&(info->started_at));
|
sa = tv_to_ms(&(info->started_at));
|
stopped = tv_to_ms(&(info->stopped_at));
|
stopped = tv_to_ms(&(info->stopped_at));
|
if (info->do_run_run) {
|
if (info->do_run_run) {
|
stopped = now; /* not really stopped, more like last-running-at */
|
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",
|
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->sofar,
|
(unsigned long long) info->errors,
|
(unsigned long long) info->errors,
|
(unsigned long long) sa,
|
(unsigned long long) sa,
|
(unsigned long long) stopped,
|
(unsigned long long) stopped,
|
(unsigned long long) now,
|
(unsigned long long) now,
|
(unsigned long long) info->idle_acc);
|
(unsigned long long) info->idle_acc);
|
p += sprintf(p, " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
|
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);
|
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",
|
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);
|
info->cur_saddr, info->cur_daddr, info->cur_udp_dst, info->cur_udp_src);
|
|
|
if (info->result[0])
|
if (info->result[0])
|
p += sprintf(p, "Result: %s\n", info->result);
|
p += sprintf(p, "Result: %s\n", info->result);
|
else
|
else
|
p += sprintf(p, "Result: Idle\n");
|
p += sprintf(p, "Result: Idle\n");
|
*eof = 1;
|
*eof = 1;
|
|
|
return p - buf;
|
return p - buf;
|
}
|
}
|
|
|
static int count_trail_chars(const char *user_buffer, unsigned int maxlen)
|
static int count_trail_chars(const char *user_buffer, unsigned int maxlen)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < maxlen; i++) {
|
for (i = 0; i < maxlen; i++) {
|
char c;
|
char c;
|
|
|
if (get_user(c, &user_buffer[i]))
|
if (get_user(c, &user_buffer[i]))
|
return -EFAULT;
|
return -EFAULT;
|
switch (c) {
|
switch (c) {
|
case '\"':
|
case '\"':
|
case '\n':
|
case '\n':
|
case '\r':
|
case '\r':
|
case '\t':
|
case '\t':
|
case ' ':
|
case ' ':
|
case '=':
|
case '=':
|
break;
|
break;
|
default:
|
default:
|
goto done;
|
goto done;
|
};
|
};
|
}
|
}
|
done:
|
done:
|
return i;
|
return i;
|
}
|
}
|
|
|
static unsigned long num_arg(const char *user_buffer, unsigned long maxlen,
|
static unsigned long num_arg(const char *user_buffer, unsigned long maxlen,
|
unsigned long *num)
|
unsigned long *num)
|
{
|
{
|
int i = 0;
|
int i = 0;
|
|
|
*num = 0;
|
*num = 0;
|
|
|
for(; i < maxlen; i++) {
|
for(; i < maxlen; i++) {
|
char c;
|
char c;
|
|
|
if (get_user(c, &user_buffer[i]))
|
if (get_user(c, &user_buffer[i]))
|
return -EFAULT;
|
return -EFAULT;
|
if ((c >= '0') && (c <= '9')) {
|
if ((c >= '0') && (c <= '9')) {
|
*num *= 10;
|
*num *= 10;
|
*num += c -'0';
|
*num += c -'0';
|
} else
|
} else
|
break;
|
break;
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
static int strn_len(const char *user_buffer, unsigned int maxlen)
|
static int strn_len(const char *user_buffer, unsigned int maxlen)
|
{
|
{
|
int i = 0;
|
int i = 0;
|
|
|
for(; i < maxlen; i++) {
|
for(; i < maxlen; i++) {
|
char c;
|
char c;
|
|
|
if (get_user(c, &user_buffer[i]))
|
if (get_user(c, &user_buffer[i]))
|
return -EFAULT;
|
return -EFAULT;
|
switch (c) {
|
switch (c) {
|
case '\"':
|
case '\"':
|
case '\n':
|
case '\n':
|
case '\r':
|
case '\r':
|
case '\t':
|
case '\t':
|
case ' ':
|
case ' ':
|
goto done_str;
|
goto done_str;
|
default:
|
default:
|
break;
|
break;
|
};
|
};
|
}
|
}
|
done_str:
|
done_str:
|
return i;
|
return i;
|
}
|
}
|
|
|
static int proc_write(struct file *file, const char *user_buffer,
|
static int proc_write(struct file *file, const char *user_buffer,
|
unsigned long count, void *data)
|
unsigned long count, void *data)
|
{
|
{
|
int i = 0, max, len;
|
int i = 0, max, len;
|
char name[16], valstr[32];
|
char name[16], valstr[32];
|
unsigned long value = 0;
|
unsigned long value = 0;
|
int idx = (int)(long)(data);
|
int idx = (int)(long)(data);
|
struct pktgen_info* info = NULL;
|
struct pktgen_info* info = NULL;
|
char* result = NULL;
|
char* result = NULL;
|
int tmp;
|
int tmp;
|
|
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
if ((idx < 0) || (idx >= MAX_PKTGEN)) {
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
printk("ERROR: idx: %i is out of range in proc_write\n", idx);
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
info = &(pginfos[idx]);
|
info = &(pginfos[idx]);
|
result = &(info->result[0]);
|
result = &(info->result[0]);
|
|
|
if (count < 1) {
|
if (count < 1) {
|
sprintf(result, "Wrong command format");
|
sprintf(result, "Wrong command format");
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
max = count - i;
|
max = count - i;
|
tmp = count_trail_chars(&user_buffer[i], max);
|
tmp = count_trail_chars(&user_buffer[i], max);
|
if (tmp < 0)
|
if (tmp < 0)
|
return tmp;
|
return tmp;
|
i += tmp;
|
i += tmp;
|
|
|
/* Read variable name */
|
/* Read variable name */
|
|
|
len = strn_len(&user_buffer[i], sizeof(name) - 1);
|
len = strn_len(&user_buffer[i], sizeof(name) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(name, 0, sizeof(name));
|
memset(name, 0, sizeof(name));
|
if (copy_from_user(name, &user_buffer[i], len))
|
if (copy_from_user(name, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
i += len;
|
i += len;
|
|
|
max = count -i;
|
max = count -i;
|
len = count_trail_chars(&user_buffer[i], max);
|
len = count_trail_chars(&user_buffer[i], max);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
|
|
if (debug)
|
if (debug)
|
printk("pg: %s,%lu\n", name, count);
|
printk("pg: %s,%lu\n", name, count);
|
|
|
if (!strcmp(name, "stop")) {
|
if (!strcmp(name, "stop")) {
|
if (info->do_run_run) {
|
if (info->do_run_run) {
|
strcpy(result, "Stopping");
|
strcpy(result, "Stopping");
|
}
|
}
|
else {
|
else {
|
strcpy(result, "Already stopped...\n");
|
strcpy(result, "Already stopped...\n");
|
}
|
}
|
info->do_run_run = 0;
|
info->do_run_run = 0;
|
return count;
|
return count;
|
}
|
}
|
|
|
if (!strcmp(name, "pkt_size")) {
|
if (!strcmp(name, "pkt_size")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
if (value < 14+20+8)
|
if (value < 14+20+8)
|
value = 14+20+8;
|
value = 14+20+8;
|
info->pkt_size = value;
|
info->pkt_size = value;
|
sprintf(result, "OK: pkt_size=%u", info->pkt_size);
|
sprintf(result, "OK: pkt_size=%u", info->pkt_size);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "frags")) {
|
if (!strcmp(name, "frags")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->nfrags = value;
|
info->nfrags = value;
|
sprintf(result, "OK: frags=%u", info->nfrags);
|
sprintf(result, "OK: frags=%u", info->nfrags);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "ipg")) {
|
if (!strcmp(name, "ipg")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->ipg = value;
|
info->ipg = value;
|
sprintf(result, "OK: ipg=%u", info->ipg);
|
sprintf(result, "OK: ipg=%u", info->ipg);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "udp_src_min")) {
|
if (!strcmp(name, "udp_src_min")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->udp_src_min = value;
|
info->udp_src_min = value;
|
sprintf(result, "OK: udp_src_min=%u", info->udp_src_min);
|
sprintf(result, "OK: udp_src_min=%u", info->udp_src_min);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "udp_dst_min")) {
|
if (!strcmp(name, "udp_dst_min")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->udp_dst_min = value;
|
info->udp_dst_min = value;
|
sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min);
|
sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "udp_src_max")) {
|
if (!strcmp(name, "udp_src_max")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->udp_src_max = value;
|
info->udp_src_max = value;
|
sprintf(result, "OK: udp_src_max=%u", info->udp_src_max);
|
sprintf(result, "OK: udp_src_max=%u", info->udp_src_max);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "udp_dst_max")) {
|
if (!strcmp(name, "udp_dst_max")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->udp_dst_max = value;
|
info->udp_dst_max = value;
|
sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max);
|
sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "clone_skb")) {
|
if (!strcmp(name, "clone_skb")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->clone_skb = value;
|
info->clone_skb = value;
|
|
|
sprintf(result, "OK: clone_skb=%d", info->clone_skb);
|
sprintf(result, "OK: clone_skb=%d", info->clone_skb);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "count")) {
|
if (!strcmp(name, "count")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->count = value;
|
info->count = value;
|
sprintf(result, "OK: count=%llu", (unsigned long long) info->count);
|
sprintf(result, "OK: count=%llu", (unsigned long long) info->count);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "src_mac_count")) {
|
if (!strcmp(name, "src_mac_count")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->src_mac_count = value;
|
info->src_mac_count = value;
|
sprintf(result, "OK: src_mac_count=%d", info->src_mac_count);
|
sprintf(result, "OK: src_mac_count=%d", info->src_mac_count);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "dst_mac_count")) {
|
if (!strcmp(name, "dst_mac_count")) {
|
len = num_arg(&user_buffer[i], 10, &value);
|
len = num_arg(&user_buffer[i], 10, &value);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
i += len;
|
i += len;
|
info->dst_mac_count = value;
|
info->dst_mac_count = value;
|
sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count);
|
sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "odev")) {
|
if (!strcmp(name, "odev")) {
|
len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1);
|
len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(info->outdev, 0, sizeof(info->outdev));
|
memset(info->outdev, 0, sizeof(info->outdev));
|
if (copy_from_user(info->outdev, &user_buffer[i], len))
|
if (copy_from_user(info->outdev, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
i += len;
|
i += len;
|
sprintf(result, "OK: odev=%s", info->outdev);
|
sprintf(result, "OK: odev=%s", info->outdev);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "flag")) {
|
if (!strcmp(name, "flag")) {
|
char f[32];
|
char f[32];
|
len = strn_len(&user_buffer[i], sizeof(f) - 1);
|
len = strn_len(&user_buffer[i], sizeof(f) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(f, 0, 32);
|
memset(f, 0, 32);
|
if (copy_from_user(f, &user_buffer[i], len))
|
if (copy_from_user(f, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
i += len;
|
i += len;
|
if (strcmp(f, "IPSRC_RND") == 0) {
|
if (strcmp(f, "IPSRC_RND") == 0) {
|
info->flags |= F_IPSRC_RND;
|
info->flags |= F_IPSRC_RND;
|
}
|
}
|
else if (strcmp(f, "!IPSRC_RND") == 0) {
|
else if (strcmp(f, "!IPSRC_RND") == 0) {
|
info->flags &= ~F_IPSRC_RND;
|
info->flags &= ~F_IPSRC_RND;
|
}
|
}
|
else if (strcmp(f, "IPDST_RND") == 0) {
|
else if (strcmp(f, "IPDST_RND") == 0) {
|
info->flags |= F_IPDST_RND;
|
info->flags |= F_IPDST_RND;
|
}
|
}
|
else if (strcmp(f, "!IPDST_RND") == 0) {
|
else if (strcmp(f, "!IPDST_RND") == 0) {
|
info->flags &= ~F_IPDST_RND;
|
info->flags &= ~F_IPDST_RND;
|
}
|
}
|
else if (strcmp(f, "UDPSRC_RND") == 0) {
|
else if (strcmp(f, "UDPSRC_RND") == 0) {
|
info->flags |= F_UDPSRC_RND;
|
info->flags |= F_UDPSRC_RND;
|
}
|
}
|
else if (strcmp(f, "!UDPSRC_RND") == 0) {
|
else if (strcmp(f, "!UDPSRC_RND") == 0) {
|
info->flags &= ~F_UDPSRC_RND;
|
info->flags &= ~F_UDPSRC_RND;
|
}
|
}
|
else if (strcmp(f, "UDPDST_RND") == 0) {
|
else if (strcmp(f, "UDPDST_RND") == 0) {
|
info->flags |= F_UDPDST_RND;
|
info->flags |= F_UDPDST_RND;
|
}
|
}
|
else if (strcmp(f, "!UDPDST_RND") == 0) {
|
else if (strcmp(f, "!UDPDST_RND") == 0) {
|
info->flags &= ~F_UDPDST_RND;
|
info->flags &= ~F_UDPDST_RND;
|
}
|
}
|
else if (strcmp(f, "MACSRC_RND") == 0) {
|
else if (strcmp(f, "MACSRC_RND") == 0) {
|
info->flags |= F_MACSRC_RND;
|
info->flags |= F_MACSRC_RND;
|
}
|
}
|
else if (strcmp(f, "!MACSRC_RND") == 0) {
|
else if (strcmp(f, "!MACSRC_RND") == 0) {
|
info->flags &= ~F_MACSRC_RND;
|
info->flags &= ~F_MACSRC_RND;
|
}
|
}
|
else if (strcmp(f, "MACDST_RND") == 0) {
|
else if (strcmp(f, "MACDST_RND") == 0) {
|
info->flags |= F_MACDST_RND;
|
info->flags |= F_MACDST_RND;
|
}
|
}
|
else if (strcmp(f, "!MACDST_RND") == 0) {
|
else if (strcmp(f, "!MACDST_RND") == 0) {
|
info->flags &= ~F_MACDST_RND;
|
info->flags &= ~F_MACDST_RND;
|
}
|
}
|
else {
|
else {
|
sprintf(result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
|
sprintf(result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
|
f,
|
f,
|
"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
|
"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
|
return count;
|
return count;
|
}
|
}
|
sprintf(result, "OK: flags=0x%x", info->flags);
|
sprintf(result, "OK: flags=0x%x", info->flags);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
|
if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
|
len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1);
|
len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(info->dst_min, 0, sizeof(info->dst_min));
|
memset(info->dst_min, 0, sizeof(info->dst_min));
|
if (copy_from_user(info->dst_min, &user_buffer[i], len))
|
if (copy_from_user(info->dst_min, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
if(debug)
|
if(debug)
|
printk("pg: dst_min set to: %s\n", info->dst_min);
|
printk("pg: dst_min set to: %s\n", info->dst_min);
|
i += len;
|
i += len;
|
sprintf(result, "OK: dst_min=%s", info->dst_min);
|
sprintf(result, "OK: dst_min=%s", info->dst_min);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "dst_max")) {
|
if (!strcmp(name, "dst_max")) {
|
len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1);
|
len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(info->dst_max, 0, sizeof(info->dst_max));
|
memset(info->dst_max, 0, sizeof(info->dst_max));
|
if (copy_from_user(info->dst_max, &user_buffer[i], len))
|
if (copy_from_user(info->dst_max, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
if(debug)
|
if(debug)
|
printk("pg: dst_max set to: %s\n", info->dst_max);
|
printk("pg: dst_max set to: %s\n", info->dst_max);
|
i += len;
|
i += len;
|
sprintf(result, "OK: dst_max=%s", info->dst_max);
|
sprintf(result, "OK: dst_max=%s", info->dst_max);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "src_min")) {
|
if (!strcmp(name, "src_min")) {
|
len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1);
|
len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(info->src_min, 0, sizeof(info->src_min));
|
memset(info->src_min, 0, sizeof(info->src_min));
|
if (copy_from_user(info->src_min, &user_buffer[i], len))
|
if (copy_from_user(info->src_min, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
if(debug)
|
if(debug)
|
printk("pg: src_min set to: %s\n", info->src_min);
|
printk("pg: src_min set to: %s\n", info->src_min);
|
i += len;
|
i += len;
|
sprintf(result, "OK: src_min=%s", info->src_min);
|
sprintf(result, "OK: src_min=%s", info->src_min);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "src_max")) {
|
if (!strcmp(name, "src_max")) {
|
len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1);
|
len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(info->src_max, 0, sizeof(info->src_max));
|
memset(info->src_max, 0, sizeof(info->src_max));
|
if (copy_from_user(info->src_max, &user_buffer[i], len))
|
if (copy_from_user(info->src_max, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
if(debug)
|
if(debug)
|
printk("pg: src_max set to: %s\n", info->src_max);
|
printk("pg: src_max set to: %s\n", info->src_max);
|
i += len;
|
i += len;
|
sprintf(result, "OK: src_max=%s", info->src_max);
|
sprintf(result, "OK: src_max=%s", info->src_max);
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "dstmac")) {
|
if (!strcmp(name, "dstmac")) {
|
char *v = valstr;
|
char *v = valstr;
|
unsigned char *m = info->dst_mac;
|
unsigned char *m = info->dst_mac;
|
|
|
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
|
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(valstr, 0, sizeof(valstr));
|
memset(valstr, 0, sizeof(valstr));
|
if (copy_from_user(valstr, &user_buffer[i], len))
|
if (copy_from_user(valstr, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
i += len;
|
i += len;
|
|
|
for(*m = 0;*v && m < info->dst_mac + 6; v++) {
|
for(*m = 0;*v && m < info->dst_mac + 6; v++) {
|
if (*v >= '0' && *v <= '9') {
|
if (*v >= '0' && *v <= '9') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - '0';
|
*m += *v - '0';
|
}
|
}
|
if (*v >= 'A' && *v <= 'F') {
|
if (*v >= 'A' && *v <= 'F') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - 'A' + 10;
|
*m += *v - 'A' + 10;
|
}
|
}
|
if (*v >= 'a' && *v <= 'f') {
|
if (*v >= 'a' && *v <= 'f') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - 'a' + 10;
|
*m += *v - 'a' + 10;
|
}
|
}
|
if (*v == ':') {
|
if (*v == ':') {
|
m++;
|
m++;
|
*m = 0;
|
*m = 0;
|
}
|
}
|
}
|
}
|
sprintf(result, "OK: dstmac");
|
sprintf(result, "OK: dstmac");
|
return count;
|
return count;
|
}
|
}
|
if (!strcmp(name, "srcmac")) {
|
if (!strcmp(name, "srcmac")) {
|
char *v = valstr;
|
char *v = valstr;
|
unsigned char *m = info->src_mac;
|
unsigned char *m = info->src_mac;
|
|
|
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
|
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
|
if (len < 0)
|
if (len < 0)
|
return len;
|
return len;
|
memset(valstr, 0, sizeof(valstr));
|
memset(valstr, 0, sizeof(valstr));
|
if (copy_from_user(valstr, &user_buffer[i], len))
|
if (copy_from_user(valstr, &user_buffer[i], len))
|
return -EFAULT;
|
return -EFAULT;
|
i += len;
|
i += len;
|
|
|
for(*m = 0;*v && m < info->src_mac + 6; v++) {
|
for(*m = 0;*v && m < info->src_mac + 6; v++) {
|
if (*v >= '0' && *v <= '9') {
|
if (*v >= '0' && *v <= '9') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - '0';
|
*m += *v - '0';
|
}
|
}
|
if (*v >= 'A' && *v <= 'F') {
|
if (*v >= 'A' && *v <= 'F') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - 'A' + 10;
|
*m += *v - 'A' + 10;
|
}
|
}
|
if (*v >= 'a' && *v <= 'f') {
|
if (*v >= 'a' && *v <= 'f') {
|
*m *= 16;
|
*m *= 16;
|
*m += *v - 'a' + 10;
|
*m += *v - 'a' + 10;
|
}
|
}
|
if (*v == ':') {
|
if (*v == ':') {
|
m++;
|
m++;
|
*m = 0;
|
*m = 0;
|
}
|
}
|
}
|
}
|
sprintf(result, "OK: srcmac");
|
sprintf(result, "OK: srcmac");
|
return count;
|
return count;
|
}
|
}
|
|
|
if (!strcmp(name, "inject") || !strcmp(name, "start")) {
|
if (!strcmp(name, "inject") || !strcmp(name, "start")) {
|
MOD_INC_USE_COUNT;
|
MOD_INC_USE_COUNT;
|
if (info->busy) {
|
if (info->busy) {
|
strcpy(info->result, "Already running...\n");
|
strcpy(info->result, "Already running...\n");
|
}
|
}
|
else {
|
else {
|
info->busy = 1;
|
info->busy = 1;
|
strcpy(info->result, "Starting");
|
strcpy(info->result, "Starting");
|
inject(info);
|
inject(info);
|
info->busy = 0;
|
info->busy = 0;
|
}
|
}
|
MOD_DEC_USE_COUNT;
|
MOD_DEC_USE_COUNT;
|
return count;
|
return count;
|
}
|
}
|
|
|
sprintf(info->result, "No such parameter \"%s\"", name);
|
sprintf(info->result, "No such parameter \"%s\"", name);
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
|
|
int create_proc_dir(void)
|
int create_proc_dir(void)
|
{
|
{
|
int len;
|
int len;
|
/* does proc_dir already exists */
|
/* does proc_dir already exists */
|
len = strlen(PG_PROC_DIR);
|
len = strlen(PG_PROC_DIR);
|
|
|
for (proc_dir = proc_net->subdir; proc_dir;
|
for (proc_dir = proc_net->subdir; proc_dir;
|
proc_dir=proc_dir->next) {
|
proc_dir=proc_dir->next) {
|
if ((proc_dir->namelen == len) &&
|
if ((proc_dir->namelen == len) &&
|
(! memcmp(proc_dir->name, PG_PROC_DIR, len)))
|
(! memcmp(proc_dir->name, PG_PROC_DIR, len)))
|
break;
|
break;
|
}
|
}
|
if (!proc_dir)
|
if (!proc_dir)
|
proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
|
proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
|
if (!proc_dir) return -ENODEV;
|
if (!proc_dir) return -ENODEV;
|
return 1;
|
return 1;
|
}
|
}
|
|
|
int remove_proc_dir(void)
|
int remove_proc_dir(void)
|
{
|
{
|
remove_proc_entry(PG_PROC_DIR, proc_net);
|
remove_proc_entry(PG_PROC_DIR, proc_net);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static int __init init(void)
|
static int __init init(void)
|
{
|
{
|
int i;
|
int i;
|
printk(version);
|
printk(version);
|
cycles_calibrate();
|
cycles_calibrate();
|
if (cpu_speed == 0) {
|
if (cpu_speed == 0) {
|
printk("pktgen: Error: your machine does not have working cycle counter.\n");
|
printk("pktgen: Error: your machine does not have working cycle counter.\n");
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
create_proc_dir();
|
create_proc_dir();
|
|
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
memset(&(pginfos[i]), 0, sizeof(pginfos[i]));
|
memset(&(pginfos[i]), 0, sizeof(pginfos[i]));
|
pginfos[i].pkt_size = ETH_ZLEN;
|
pginfos[i].pkt_size = ETH_ZLEN;
|
pginfos[i].nfrags = 0;
|
pginfos[i].nfrags = 0;
|
pginfos[i].clone_skb = clone_skb_d;
|
pginfos[i].clone_skb = clone_skb_d;
|
pginfos[i].ipg = ipg_d;
|
pginfos[i].ipg = ipg_d;
|
pginfos[i].count = count_d;
|
pginfos[i].count = count_d;
|
pginfos[i].sofar = 0;
|
pginfos[i].sofar = 0;
|
pginfos[i].hh[12] = 0x08; /* fill in protocol. Rest is filled in later. */
|
pginfos[i].hh[12] = 0x08; /* fill in protocol. Rest is filled in later. */
|
pginfos[i].hh[13] = 0x00;
|
pginfos[i].hh[13] = 0x00;
|
pginfos[i].udp_src_min = 9; /* sink NULL */
|
pginfos[i].udp_src_min = 9; /* sink NULL */
|
pginfos[i].udp_src_max = 9;
|
pginfos[i].udp_src_max = 9;
|
pginfos[i].udp_dst_min = 9;
|
pginfos[i].udp_dst_min = 9;
|
pginfos[i].udp_dst_max = 9;
|
pginfos[i].udp_dst_max = 9;
|
|
|
sprintf(pginfos[i].fname, "net/%s/pg%i", PG_PROC_DIR, i);
|
sprintf(pginfos[i].fname, "net/%s/pg%i", PG_PROC_DIR, i);
|
pginfos[i].proc_ent = create_proc_entry(pginfos[i].fname, 0600, 0);
|
pginfos[i].proc_ent = create_proc_entry(pginfos[i].fname, 0600, 0);
|
if (!pginfos[i].proc_ent) {
|
if (!pginfos[i].proc_ent) {
|
printk("pktgen: Error: cannot create net/%s/pg procfs entry.\n", PG_PROC_DIR);
|
printk("pktgen: Error: cannot create net/%s/pg procfs entry.\n", PG_PROC_DIR);
|
goto cleanup_mem;
|
goto cleanup_mem;
|
}
|
}
|
pginfos[i].proc_ent->read_proc = proc_read;
|
pginfos[i].proc_ent->read_proc = proc_read;
|
pginfos[i].proc_ent->write_proc = proc_write;
|
pginfos[i].proc_ent->write_proc = proc_write;
|
pginfos[i].proc_ent->data = (void*)(long)(i);
|
pginfos[i].proc_ent->data = (void*)(long)(i);
|
|
|
sprintf(pginfos[i].busy_fname, "net/%s/pg_busy%i", PG_PROC_DIR, 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);
|
pginfos[i].busy_proc_ent = create_proc_entry(pginfos[i].busy_fname, 0, 0);
|
if (!pginfos[i].busy_proc_ent) {
|
if (!pginfos[i].busy_proc_ent) {
|
printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR);
|
printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR);
|
goto cleanup_mem;
|
goto cleanup_mem;
|
}
|
}
|
pginfos[i].busy_proc_ent->read_proc = proc_busy_read;
|
pginfos[i].busy_proc_ent->read_proc = proc_busy_read;
|
pginfos[i].busy_proc_ent->data = (void*)(long)(i);
|
pginfos[i].busy_proc_ent->data = (void*)(long)(i);
|
}
|
}
|
return 0;
|
return 0;
|
|
|
cleanup_mem:
|
cleanup_mem:
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
if (strlen(pginfos[i].fname)) {
|
if (strlen(pginfos[i].fname)) {
|
remove_proc_entry(pginfos[i].fname, NULL);
|
remove_proc_entry(pginfos[i].fname, NULL);
|
}
|
}
|
if (strlen(pginfos[i].busy_fname)) {
|
if (strlen(pginfos[i].busy_fname)) {
|
remove_proc_entry(pginfos[i].busy_fname, NULL);
|
remove_proc_entry(pginfos[i].busy_fname, NULL);
|
}
|
}
|
}
|
}
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
|
|
|
|
static void __exit cleanup(void)
|
static void __exit cleanup(void)
|
{
|
{
|
int i;
|
int i;
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
for (i = 0; i<MAX_PKTGEN; i++) {
|
if (strlen(pginfos[i].fname)) {
|
if (strlen(pginfos[i].fname)) {
|
remove_proc_entry(pginfos[i].fname, NULL);
|
remove_proc_entry(pginfos[i].fname, NULL);
|
}
|
}
|
if (strlen(pginfos[i].busy_fname)) {
|
if (strlen(pginfos[i].busy_fname)) {
|
remove_proc_entry(pginfos[i].busy_fname, NULL);
|
remove_proc_entry(pginfos[i].busy_fname, NULL);
|
}
|
}
|
}
|
}
|
remove_proc_dir();
|
remove_proc_dir();
|
}
|
}
|
|
|
module_init(init);
|
module_init(init);
|
module_exit(cleanup);
|
module_exit(cleanup);
|
|
|
MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
|
MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
|
MODULE_DESCRIPTION("Packet Generator tool");
|
MODULE_DESCRIPTION("Packet Generator tool");
|
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
MODULE_PARM(count_d, "i");
|
MODULE_PARM(count_d, "i");
|
MODULE_PARM(ipg_d, "i");
|
MODULE_PARM(ipg_d, "i");
|
MODULE_PARM(cpu_speed, "i");
|
MODULE_PARM(cpu_speed, "i");
|
MODULE_PARM(clone_skb_d, "i");
|
MODULE_PARM(clone_skb_d, "i");
|
|
|
|
|
|
|
|
|