linux/net/core/pktgen.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Authors:
 * Copyright 2001, 2002 by Robert Olsson <[email protected]>
 *                             Uppsala University and
 *                             Swedish University of Agricultural Sciences
 *
 * Alexey Kuznetsov  <[email protected]>
 * Ben Greear <[email protected]>
 * Jens Låås <[email protected]>
 *
 * A tool for loading the network with preconfigurated packets.
 * The tool is implemented as a linux module.  Parameters are output
 * device, delay (to hard_xmit), 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:
 *
 * [email protected]
 * 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. [email protected]
 * Significant re-work of the module:
 *   *  Convert to threaded model to more efficiently be able to transmit
 *       and receive on multiple interfaces at once.
 *   *  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 most values after starting.
 *   *  Place 12-byte packet in UDP payload with magic number,
 *       sequence number, and timestamp.
 *   *  Add receiver code that detects dropped pkts, re-ordered pkts, and
 *       latencies (with micro-second) precision.
 *   *  Add IOCTL interface to easily get counters & configuration.
 *   --Ben Greear <[email protected]>
 *
 * 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
 *
 * Sept 10:  Fixed threading/locking.  Lots of bone-headed and more clever
 *    mistakes.  Also merged in DaveM's patch in the -pre6 patch.
 * --Ben Greear <[email protected]>
 *
 * Integrated to 2.5.x 021029 --Lucio Maciel ([email protected])
 *
 * 021124 Finished major redesign and rewrite for new functionality.
 * See Documentation/networking/pktgen.rst for how to use this.
 *
 * The new operation:
 * For each CPU one thread/process is created at start. This process checks
 * for running devices in the if_list and sends packets until count is 0 it
 * also the thread checks the thread->control which is used for inter-process
 * communication. controlling process "posts" operations to the threads this
 * way.
 * The if_list is RCU protected, and the if_lock remains to protect updating
 * of if_list, from "add_device" as it invoked from userspace (via proc write).
 *
 * By design there should only be *one* "controlling" process. In practice
 * multiple write accesses gives unpredictable result. Understood by "write"
 * to /proc gives result code thats should be read be the "writer".
 * For practical use this should be no problem.
 *
 * Note when adding devices to a specific CPU there good idea to also assign
 * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
 * --ro
 *
 * Fix refcount off by one if first packet fails, potential null deref,
 * memleak 030710- KJP
 *
 * First "ranges" functionality for ipv6 030726 --ro
 *
 * Included flow support. 030802 ANK.
 *
 * Fixed unaligned access on IA-64 Grant Grundler <[email protected]>
 *
 * Remove if fix from added Harald Welte <[email protected]> 040419
 * ia64 compilation fix from  Aron Griffis <[email protected]> 040604
 *
 * New xmit() return, do_div and misc clean up by Stephen Hemminger
 * <[email protected]> 040923
 *
 * Randy Dunlap fixed u64 printk compiler warning
 *
 * Remove FCS from BW calculation.  Lennert Buytenhek <[email protected]>
 * New time handling. Lennert Buytenhek <[email protected]> 041213
 *
 * Corrections from Nikolai Malykh ([email protected])
 * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
 *
 * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <[email protected]>
 * 050103
 *
 * MPLS support by Steven Whitehouse <[email protected]>
 *
 * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <[email protected]>
 *
 * Fixed src_mac command to set source mac of packet to value specified in
 * command by Adit Ranadive <[email protected]>
 */

#define pr_fmt(fmt)

#include <linux/sys.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/capability.h>
#include <linux/hrtimer.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/wait.h>
#include <linux/etherdevice.h>
#include <linux/kthread.h>
#include <linux/prefetch.h>
#include <linux/mmzone.h>
#include <net/net_namespace.h>
#include <net/checksum.h>
#include <net/ipv6.h>
#include <net/udp.h>
#include <net/ip6_checksum.h>
#include <net/addrconf.h>
#ifdef CONFIG_XFRM
#include <net/xfrm.h>
#endif
#include <net/netns/generic.h>
#include <asm/byteorder.h>
#include <linux/rcupdate.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/timex.h>
#include <linux/uaccess.h>
#include <asm/dma.h>
#include <asm/div64.h>		/* do_div */

#define VERSION
#define IP_NAME_SZ
#define MAX_MPLS_LABELS
#define MPLS_STACK_BOTTOM
/* Max number of internet mix entries that can be specified in imix_weights. */
#define MAX_IMIX_ENTRIES
#define IMIX_PRECISION

#define func_enter()

#define PKT_FLAGS			\

#define pf
enum pkt_flags {};
#undef pf

/* Device flag bits */
#define pf
PKT_FLAGS
#undef pf

#define pf
static char *pkt_flag_names[] =;
#undef pf

#define NR_PKT_FLAGS

/* Thread control flag bits */
#define T_STOP
#define T_RUN
#define T_REMDEVALL
#define T_REMDEV

/* Xmit modes */
#define M_START_XMIT
#define M_NETIF_RECEIVE
#define M_QUEUE_XMIT

/* If lock -- protects updating of if_list */
#define if_lock(t)
#define if_unlock(t)

/* Used to help with determining the pkts on receive */
#define PKTGEN_MAGIC
#define PG_PROC_DIR
#define PGCTRL

#define MAX_CFLOWS

#define VLAN_TAG_SIZE(x)
#define SVLAN_TAG_SIZE(x)

struct imix_pkt {};

struct flow_state {};

/* flow flag bits */
#define F_INIT

struct pktgen_dev {};

struct pktgen_hdr {};


static unsigned int pg_net_id __read_mostly;

struct pktgen_net {};

struct pktgen_thread {};

#define REMOVE
#define FIND

static const char version[] =;

static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
					  const char *ifname, bool exact);
static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
static void pktgen_run_all_threads(struct pktgen_net *pn);
static void pktgen_reset_all_threads(struct pktgen_net *pn);
static void pktgen_stop_all_threads(struct pktgen_net *pn);

static void pktgen_stop(struct pktgen_thread *t);
static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
static void fill_imix_distribution(struct pktgen_dev *pkt_dev);

/* Module parameters, defaults. */
static int pg_count_d __read_mostly =;
static int pg_delay_d __read_mostly;
static int pg_clone_skb_d  __read_mostly;
static int debug  __read_mostly;

static DEFINE_MUTEX(pktgen_thread_lock);

static struct notifier_block pktgen_notifier_block =;

/*
 * /proc handling functions
 *
 */

static int pgctrl_show(struct seq_file *seq, void *v)
{}

static ssize_t pgctrl_write(struct file *file, const char __user *buf,
			    size_t count, loff_t *ppos)
{}

static int pgctrl_open(struct inode *inode, struct file *file)
{}

static const struct proc_ops pktgen_proc_ops =;

static int pktgen_if_show(struct seq_file *seq, void *v)
{}


static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
		     __u32 *num)
{}

static int count_trail_chars(const char __user * user_buffer,
			     unsigned int maxlen)
{}

static long num_arg(const char __user *user_buffer, unsigned long maxlen,
				unsigned long *num)
{}

static int strn_len(const char __user * user_buffer, unsigned int maxlen)
{}

/* Parses imix entries from user buffer.
 * The user buffer should consist of imix entries separated by spaces
 * where each entry consists of size and weight delimited by commas.
 * "size1,weight_1 size2,weight_2 ... size_n,weight_n" for example.
 */
static ssize_t get_imix_entries(const char __user *buffer,
				struct pktgen_dev *pkt_dev)
{}

static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
{}

static __u32 pktgen_read_flag(const char *f, bool *disable)
{}

static ssize_t pktgen_if_write(struct file *file,
			       const char __user * user_buffer, size_t count,
			       loff_t * offset)
{}

static int pktgen_if_open(struct inode *inode, struct file *file)
{}

static const struct proc_ops pktgen_if_proc_ops =;

static int pktgen_thread_show(struct seq_file *seq, void *v)
{}

static ssize_t pktgen_thread_write(struct file *file,
				   const char __user * user_buffer,
				   size_t count, loff_t * offset)
{}

static int pktgen_thread_open(struct inode *inode, struct file *file)
{}

static const struct proc_ops pktgen_thread_proc_ops =;

/* Think find or remove for NN */
static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
					      const char *ifname, int remove)
{}

/*
 * mark a device for removal
 */
static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)
{}

static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)
{}

static int pktgen_device_event(struct notifier_block *unused,
			       unsigned long event, void *ptr)
{}

static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn,
						 struct pktgen_dev *pkt_dev,
						 const char *ifname)
{}


/* Associate pktgen_dev with a device. */

static int pktgen_setup_dev(const struct pktgen_net *pn,
			    struct pktgen_dev *pkt_dev, const char *ifname)
{}

/* Read pkt_dev from the interface and set up internal pktgen_dev
 * structure to have the right information to create/send packets
 */
static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
{}


static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
{}

static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
{}

static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
{}

static inline int f_pick(struct pktgen_dev *pkt_dev)
{}


#ifdef CONFIG_XFRM
/* If there was already an IPSEC SA, we keep it as is, else
 * we go look for it ...
*/
#define DUMMY_MARK
static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
{}
#endif
static void set_cur_queue_map(struct pktgen_dev *pkt_dev)
{}

/* 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_dev *pkt_dev)
{}

static void fill_imix_distribution(struct pktgen_dev *pkt_dev)
{}

#ifdef CONFIG_XFRM
static u32 pktgen_dst_metrics[RTAX_MAX + 1] =;

static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
{}

static void free_SAs(struct pktgen_dev *pkt_dev)
{}

static int process_ipsec(struct pktgen_dev *pkt_dev,
			      struct sk_buff *skb, __be16 protocol)
{}
#endif

static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
{}

static inline __be16 build_tci(unsigned int id, unsigned int cfi,
			       unsigned int prio)
{}

static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
				int datalen)
{}

static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
					struct pktgen_dev *pkt_dev)
{}

static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
					struct pktgen_dev *pkt_dev)
{}

static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
					struct pktgen_dev *pkt_dev)
{}

static struct sk_buff *fill_packet(struct net_device *odev,
				   struct pktgen_dev *pkt_dev)
{}

static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
{}

/* Set up structure for sending pkts, clear counters */

static void pktgen_run(struct pktgen_thread *t)
{}

static void pktgen_handle_all_threads(struct pktgen_net *pn, u32 flags)
{}

static void pktgen_stop_all_threads(struct pktgen_net *pn)
{}

static int thread_is_running(const struct pktgen_thread *t)
{}

static int pktgen_wait_thread_run(struct pktgen_thread *t)
{}

static int pktgen_wait_all_threads_run(struct pktgen_net *pn)
{}

static void pktgen_run_all_threads(struct pktgen_net *pn)
{}

static void pktgen_reset_all_threads(struct pktgen_net *pn)
{}

static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
{}

/* Set stopped-at timer, remove from running list, do counters & statistics */
static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
{}

static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
{}

static void pktgen_stop(struct pktgen_thread *t)
{}

/*
 * one of our devices needs to be removed - find it
 * and remove it
 */
static void pktgen_rem_one_if(struct pktgen_thread *t)
{}

static void pktgen_rem_all_ifs(struct pktgen_thread *t)
{}

static void pktgen_rem_thread(struct pktgen_thread *t)
{}

static void pktgen_resched(struct pktgen_dev *pkt_dev)
{}

static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
{}

static void pktgen_xmit(struct pktgen_dev *pkt_dev)
{}

/*
 * Main loop of the thread goes here
 */

static int pktgen_thread_worker(void *arg)
{}

static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
					  const char *ifname, bool exact)
{}

/*
 * Adds a dev at front of if_list.
 */

static int add_dev_to_thread(struct pktgen_thread *t,
			     struct pktgen_dev *pkt_dev)
{}

/* Called under thread lock */

static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
{}

static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
{}

/*
 * Removes a device from the thread if_list.
 */
static void _rem_dev_from_if_list(struct pktgen_thread *t,
				  struct pktgen_dev *pkt_dev)
{}

static int pktgen_remove_device(struct pktgen_thread *t,
				struct pktgen_dev *pkt_dev)
{}

static int __net_init pg_net_init(struct net *net)
{}

static void __net_exit pg_net_exit(struct net *net)
{}

static struct pernet_operations pg_net_ops =;

static int __init pg_init(void)
{}

static void __exit pg_cleanup(void)
{}

module_init();
module_exit(pg_cleanup);

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();
MODULE_VERSION();
module_param(pg_count_d, int, 0);
MODULE_PARM_DESC();
module_param(pg_delay_d, int, 0);
MODULE_PARM_DESC();
module_param(pg_clone_skb_d, int, 0);
MODULE_PARM_DESC();
module_param(debug, int, 0);
MODULE_PARM_DESC();