Last active
August 29, 2015 14:24
-
-
Save upa/a4b30107e9ca2a692508 to your computer and use it in GitHub Desktop.
multi-source-flow generator using netmap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
srcgen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* multiple source address traffic generator using netmap | |
*/ | |
#include <stdio.h> | |
#define NETMAP_WITH_LIBS | |
#include <net/netmap_user.h> | |
#include <unistd.h> | |
#include <sys/poll.h> | |
#include <arpa/inet.h> | |
#include <sys/sysctl.h> | |
#include <ifaddrs.h> | |
#include <net/ethernet.h> | |
#include <netinet/in.h> | |
#include <netinet/ip.h> | |
#include <netinet/udp.h> | |
#include <net/if.h> | |
#include <sys/socket.h> | |
#include <netpacket/packet.h> | |
#define BURST_MAX 1024 | |
#define SRCIP_MAX 256 | |
#define MACCOPY(s, d) \ | |
do { \ | |
d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; \ | |
d[3] = s[3]; d[4] = s[4]; d[5] = s[5]; \ | |
} while (0) | |
struct srcgen { | |
int raw_mode; | |
char * ifname; | |
int sip_num; | |
struct in_addr sips[SRCIP_MAX]; | |
struct in_addr dip; | |
uint16_t csums[SRCIP_MAX]; | |
u_int8_t smac[ETH_ALEN]; | |
u_int8_t dmac[ETH_ALEN]; | |
u_int8_t tos; | |
int pktlen; | |
int interval; | |
/* netmap related */ | |
int fd; | |
char * mem; | |
struct netmap_if * nifp; | |
}; | |
static uint16_t | |
checksum(const void * data, uint16_t len, uint32_t sum) | |
{ | |
const uint8_t *addr = data; | |
uint32_t i; | |
/* Checksum all the pairs of bytes first... */ | |
for (i = 0; i < (len & ~1U); i += 2) { | |
sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); | |
if (sum > 0xFFFF) | |
sum -= 0xFFFF; | |
} | |
/* | |
* If there's a single byte left over, checksum it, too. | |
* Network byte order is big-endian, so the remaining byte is | |
* the high byte. | |
*/ | |
if (i < len) { | |
sum += addr[i] << 8; | |
if (sum > 0xFFFF) | |
sum -= 0xFFFF; | |
} | |
return sum; | |
} | |
static u_int16_t | |
wrapsum(u_int32_t sum) | |
{ | |
sum = ~sum & 0xFFFF; | |
return (htons(sum)); | |
} | |
struct ip * | |
build_packet (char * pkt, size_t len, struct srcgen * sg) | |
{ | |
memset (pkt, 0, len); | |
/* build ether header */ | |
struct ether_header * eth; | |
eth = (struct ether_header *) pkt; | |
MACCOPY (sg->smac, eth->ether_shost); | |
MACCOPY (sg->dmac, eth->ether_dhost); | |
eth->ether_type = htons (ETHERTYPE_IP); | |
/* build ip packet */ | |
struct ip * ip; | |
ip = (struct ip *)(eth + 1); | |
ip->ip_v = IPVERSION; | |
ip->ip_hl = 5; | |
ip->ip_id = 0; | |
ip->ip_tos = sg->tos; | |
ip->ip_ttl = 16; | |
ip->ip_len = htons (sg->pktlen - sizeof (*eth)); | |
ip->ip_off = htons (IP_DF); | |
ip->ip_p = IPPROTO_UDP; | |
ip->ip_dst = sg->dip; | |
/* ip->ip_src is filled when transmitting */ | |
ip->ip_sum = 0; | |
/* build udp packet */ | |
struct udphdr * udp; | |
udp = (struct udphdr *)(ip + 1); | |
udp->source = htons (48558); | |
udp->dest = htons (48558); | |
udp->len = htons (sg->pktlen - (sizeof (*eth) + sizeof (*ip))); | |
udp->check = 0; /* no udp checksum */ | |
return ip; | |
} | |
void | |
srcgen (struct srcgen * sg) | |
{ | |
int idx; | |
u_int burst, cur; | |
char pkt[2048], * nm_pkt; | |
struct ip * ip; | |
struct netmap_ring * ring; | |
struct netmap_slot * slot; | |
ip = build_packet (pkt, sizeof (pkt), sg); | |
/* pre-culculate checksum */ | |
for (idx = 0; idx < sg->sip_num; idx++) { | |
ip->ip_src = sg->sips[idx]; | |
ip->ip_sum = 0; | |
sg->csums[idx] = wrapsum (checksum (ip, sizeof (*ip), 0)); | |
} | |
idx = 0; | |
if (sg->raw_mode) { | |
int sock, ret; | |
struct sockaddr_ll ll; | |
sock = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL)); | |
memset (&ll, 0, sizeof (ll)); | |
ll.sll_family = AF_PACKET; | |
ll.sll_ifindex = if_nametoindex(sg->ifname); | |
ll.sll_protocol = htons(ETH_P_ALL); | |
bind(sock, (struct sockaddr *)&ll, sizeof(ll)); | |
while (1) { | |
/* set src ip address */ | |
ip->ip_src = sg->sips[idx]; | |
ip->ip_sum = sg->csums[idx]; | |
idx = (idx + 1) % sg->sip_num; | |
ret = write (sock, pkt, sg->pktlen); | |
if (ret < 0) { | |
perror ("write"); | |
} | |
if (sg->interval) | |
sleep (sg->interval); | |
} | |
} | |
ring = NETMAP_TXRING (sg->nifp, 0); | |
/* netmap mode */ | |
while (1) { | |
cur = ring->cur; | |
burst = nm_ring_space (ring) < BURST_MAX ? | |
nm_ring_space (ring) : BURST_MAX; | |
while (burst-- > 0) { | |
/* set src ip address */ | |
ip->ip_src = sg->sips[idx]; | |
ip->ip_sum = sg->csums[idx]; | |
idx = (idx + 1) % sg->sip_num; | |
/* xmit vai netmap ring */ | |
slot = &ring->slot[cur]; | |
nm_pkt = NETMAP_BUF (ring, slot->buf_idx); | |
nm_pkt_copy (pkt, nm_pkt, sg->pktlen); | |
slot->len = sg->pktlen; | |
cur = nm_ring_next (ring, cur); | |
if (sg->interval) | |
sleep (sg->interval); | |
} | |
ring->head = ring->cur = cur; | |
ioctl (sg->fd, NIOCTXSYNC, NULL); | |
} | |
return; | |
} | |
void | |
usage (void) | |
{ | |
printf ("srcgen\n" | |
"\t" "-i : interface\n" | |
"\t" "-s : source ip address (multiple -s allowed)\n" | |
"\t" "-d : destination ip address\n" | |
"\t" "-S : source mac address\n" | |
"\t" "-D : destination mac address\n" | |
"\t" "-l : packet length (default 64)\n" | |
"\t" "-r : raw socket mode\n" | |
"\t" "-t : interval (sec)\n" | |
"\t" "-q : ToS bit value\n" | |
); | |
return; | |
} | |
int | |
main (int argc, char ** argv) | |
{ | |
int fd, ret, mac[ETH_ALEN]; | |
char ch; | |
struct nmreq nmr; | |
struct srcgen sg; | |
memset (&sg, 0, sizeof (struct srcgen)); | |
/* parse arguments and setup default values */ | |
while ((ch = getopt (argc, argv, "i:s:d:S:D:l:rt:q:")) != -1) { | |
switch (ch) { | |
case 'i' : | |
sg.ifname = optarg; | |
break; | |
case 's' : | |
ret = inet_pton (AF_INET, optarg, | |
&sg.sips[sg.sip_num++]); | |
if (ret < 1) { | |
D ("invalid source ip \"%s\"", optarg); | |
return -1; | |
} | |
break; | |
case 'd' : | |
ret = inet_pton (AF_INET, optarg, &sg.dip); | |
if (ret < 1) { | |
D ("invalid destination ip \"%s\"", optarg); | |
return -1; | |
} | |
break; | |
case 'S' : | |
sscanf (optarg, "%02x:%02x:%02x:%02x:%02x:%02x", | |
&mac[0], &mac[1], &mac[2], | |
&mac[3], &mac[4], &mac[5]); | |
MACCOPY (mac, sg.smac); | |
break; | |
case 'D' : | |
sscanf (optarg, "%02x:%02x:%02x:%02x:%02x:%02x", | |
&mac[0], &mac[1], &mac[2], | |
&mac[3], &mac[4], &mac[5]); | |
MACCOPY (mac, sg.dmac); | |
break; | |
case 'l' : | |
sg.pktlen = atoi (optarg); | |
break; | |
case 'r' : | |
sg.raw_mode = 1; | |
break; | |
case 't' : | |
sg.interval = atoi (optarg); | |
break; | |
case 'q' : | |
sg.tos = atoi (optarg); | |
break; | |
default : | |
usage (); | |
return -1; | |
} | |
} | |
if (argc == 1) { | |
usage (); | |
return -1; | |
} | |
if (sg.ifname == NULL) { | |
D ("specify interface name"); | |
return -1; | |
} | |
if (sg.pktlen == 0) { | |
sg.pktlen = 64; | |
} | |
if (sg.raw_mode) { | |
goto srcgen; | |
} | |
/* setup netmap */ | |
fd = open ("/dev/netmap", O_RDWR); | |
if (fd < 0) { | |
D ("unable to open /dev/netmap"); | |
return -1; | |
} | |
memset (&nmr, 0, sizeof (nmr)); | |
strcpy (nmr.nr_name, sg.ifname); | |
nmr.nr_version = NETMAP_API; | |
nmr.nr_ringid = 0 | (NETMAP_NO_TX_POLL | NETMAP_DO_RX_POLL); | |
nmr.nr_flags |= NR_REG_ALL_NIC; | |
if (ioctl (fd, NIOCREGIF, &nmr) < 0) { | |
D ("unable to register interface \"%s\"", sg.ifname); | |
return -1; | |
} | |
sg.mem = mmap (NULL, nmr.nr_memsize, | |
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); | |
if (sg.mem == MAP_FAILED) { | |
D ("unable to mmap"); | |
return -1; | |
} | |
sg.nifp = NETMAP_IF (sg.mem, nmr.nr_offset); | |
sg.fd = fd; | |
srcgen: | |
srcgen (&sg); | |
/* not reached */ | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
IF=$1 | |
FLOWNUM=$2 | |
plen=60 | |
if [ "$FLOWNUM" = "" ]; then | |
echo $0 ifname flownum | |
exit 1 | |
fi | |
S="sudo ./srcgen -q 4 -i $1 -S a0:36:9f:2b:27:d2 -D 0:1:1:1:2:0 -l $plen -d 10.0.0.1" | |
for x in `seq 1 $FLOWNUM`; do | |
S="$S -s 172.16.0.$x" | |
done | |
echo $S | |
$S |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment