Skip to content

Instantly share code, notes, and snippets.

@upa
Created July 17, 2015 11:23
Show Gist options
  • Save upa/05a4070eb821cd50127e to your computer and use it in GitHub Desktop.
Save upa/05a4070eb821cd50127e to your computer and use it in GitHub Desktop.
URL and DNS packet dump via netmap
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <asm-generic/int-ll64.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS /* D and nm_pkt_copy */
#include <net/netmap.h>
#include <net/netmap_user.h>
#define POLL_TIMEOUT 1
#define BURST_MAX 1024
#define URLDUMP_THREAD_MAX 16
/* tagged vlan flame */
struct ether_vlan {
__u8 ether_dhost[ETH_ALEN];
__u8 ether_shost[ETH_ALEN];
__u16 vlan_tpid;
__u16 vlan_tci;
__u16 ether_type;
} __attribute__ ((__packed__));
/* instance for urldump thread for each netmap rx ring */
struct urldump {
int fd;
char * dev;
struct netmap_ring * ring;
uint32_t dns_srate_count; /* sampling counter */
};
#define PORTLISTMAX 256
int portlist[PORTLISTMAX];
int portnum;
/* DNS dump */
char * dns_output = NULL;
int dns_output_fd = 0; /* DNS output file */
int dns_srate = 1; /* DNS sampling date */
void
dns_dump (struct urldump * u,
char * pkt, uint16_t pkt_len, struct udphdr * udp)
{
int ret;
if (!dns_output || !dns_output_fd)
return;
/* check port number, and write */
if (ntohs (udp->source) == 53 || ntohs (udp->dest) == 53) {
if (u->dns_srate_count % dns_srate == 0) {
ret = write (dns_output_fd, pkt, pkt_len);
if (ret < 0) {
perror ("failed to write dns output");
}
u->dns_srate_count = 0;
}
u->dns_srate_count++;
}
return;
}
void
dump (struct urldump * u, char * pkt, uint16_t pkt_len, struct timeval * ts)
{
int n, i, len;
char * p, get[2048], host[2048], post[2048], put[2048], ua[2048];
char src[64], dst[64];
u_int16_t ether_type, ether_size;
struct ether_header * eth;
struct iphdr * ip;
struct ip6_hdr * ip6;
struct tcphdr * tcp;
ip = NULL;
ip6 = NULL;
tcp = NULL;
get[0] = '\0';
host[0] = '\0';
post[0] = '\0';
put[0] = '\0';
ua[0] = '\0';
eth = (struct ether_header *) pkt;
ether_type = eth->ether_type == htons (ETHERTYPE_VLAN) ?
((struct ether_vlan *) pkt)->ether_type : eth->ether_type;
ether_size = eth->ether_type == htons (ETHERTYPE_VLAN) ?
sizeof (struct ether_vlan) : sizeof (struct ether_header);
switch (ntohs (ether_type)) {
default :
return;
case ETHERTYPE_IP :
ip = (struct iphdr *)(pkt + ether_size);
tcp = (struct tcphdr *)(pkt + ether_size + ip->ihl * 4);
len = pkt_len - ether_size + ip->ihl * 4;
if (ip->protocol == IPPROTO_UDP) {
dns_dump (u, pkt, pkt_len, (struct udphdr *) tcp);
return;
}
if (ip->protocol != IPPROTO_TCP)
return;
break;
case ETHERTYPE_IPV6 :
ip6 = (struct ip6_hdr *)(pkt + ether_size);
tcp = (struct tcphdr *)(pkt + ether_size + sizeof (*ip6));
len = pkt_len - ether_size + sizeof (*ip6);
if (ip6->ip6_nxt != IPPROTO_UDP) {
dns_dump (u, pkt, pkt_len, (struct udphdr *) tcp);
return;
}
if (ip6->ip6_nxt != IPPROTO_TCP)
return;
break;
}
/* check destination port */
for (n = 0; n < portnum; n++) {
if (tcp->dest == portlist[n]) {
break;
}
}
if (n == portnum) {
return;
}
p = (char *)(tcp + 1);
for (n = 0; n < len; n++, p++) {
/* Host: */
if (*p == 'H') {
if (*(p + 1) == 'o' && *(p + 2) == 's' &&
*(p + 3) == 't' && *(p + 4) == ':' &&
*(p + 5) == ' ') {
p += 6;
n += 6;
i = 0;
while (n < len && *p != '\n' && *p != '\r') {
host[i++] = *p++;
n++;
}
host[i] = '\0';
}
}
/* GET */
if (*p == 'G') {
if (*(p + 1) == 'E' && *(p + 2) == 'T' &&
*(p + 3) == ' ') {
p += 4;
n += 4;
i = 0;
while (n < len && *p != '\n' && *p != '\r') {
get[i++] = *p++;
n++;
}
get[i] = '\0';
}
}
/* POST */
if (*p == 'P') {
if (*(p + 1) == 'O' && *(p + 2) == 'S' &&
*(p + 3) == 'T' && *(p + 4) == ' ') {
p += 5;
n += 5;
i = 0;
while (n < len && *p != '\n' && *p != '\r') {
post[i++] = *p++;
n++;
}
post[i] = '\0';
}
}
/* PUT */
if (*p == 'P') {
if (*(p + 1) == 'U' && *(p + 2) == 'T' &&
*(p + 3) == ' ') {
p += 4;
n += 4;
i = 0;
while (n < len && *p != '\n' && *p != '\r') {
put[i++] = *p++;
n++;
}
put[i] = '\0';
}
}
/* User-Agent: */
if (*p == 'U') {
if (*(p + 1) == 's' && *(p + 2) == 'e' &&
*(p + 3) == 'r' && *(p + 4) == '-' &&
*(p + 5) == 'A' && *(p + 6) == 'g' &&
*(p + 7) == 'e' && *(p + 8) == 'n' &&
*(p + 9) == 't' && *(p + 10) == ':' &&
*(p + 11) == ' ') {
p += 12;
n += 12;
i = 0;
while (n < len && *p != '\n' && *p != '\r') {
ua[i++] = *p++;
n++;
}
ua[i] = '\0';
}
}
}
if (get[0] == '\0' && host[0] == '\0' &&
post[0] == '\0' && put[0] == '\0' && ua[0] == '\0')
return;
switch (ntohs (ether_type)) {
case ETHERTYPE_IP :
inet_ntop (AF_INET, &ip->saddr, src, sizeof (src));
inet_ntop (AF_INET, &ip->daddr, dst, sizeof (dst));
break;
case ETHERTYPE_IPV6 :
inet_ntop (AF_INET6, &ip6->ip6_src, src, sizeof (src));
inet_ntop (AF_INET6, &ip6->ip6_dst, dst, sizeof (dst));
break;
}
printf ("%lu %s:%u->%s:%u "
"Host:%s GET:%s POST:%s PUT:%s User-Agent:%s\n",
ts->tv_sec, src, ntohs (tcp->source), dst, ntohs (tcp->dest),
host, get, post, put, ua);
//fflush (stdin);
return;
}
u_int
process_ring (struct urldump * u, struct netmap_ring * ring)
{
char * pkt;
u_int idx, burst, m;
struct netmap_slot * slot;
idx = ring->cur;
burst = BURST_MAX;
m = nm_ring_space (ring);
if (m < BURST_MAX)
burst = m;
m = burst;
while (burst-- > 0) {
slot = &ring->slot[idx];
pkt = (char *) NETMAP_BUF (ring, slot->buf_idx);
dump (u, pkt, slot->len, &ring->ts);
idx = nm_ring_next (ring, idx);
}
ring->head = ring->cur = idx;
return m;
}
void *
urldumper (void * param)
{
struct urldump * urldump = (struct urldump *) param;
struct pollfd x[1];
pthread_detach (pthread_self ());
x[0].fd = urldump->fd;
x[0].events = POLLIN;
while (1) {
if (poll (x, 1, POLL_TIMEOUT) == 0)
continue;
process_ring (urldump, urldump->ring);
}
return NULL;
}
int
extract_netmap_ring (char * ifname, int q, struct netmap_ring ** ring,
int x, int w)
{
int fd;
char * mem;
struct nmreq nmr;
struct netmap_if * nifp;
/* open netmap for ring */
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, ifname);
nmr.nr_version = NETMAP_API;
nmr.nr_ringid = q | (NETMAP_NO_TX_POLL | NETMAP_DO_RX_POLL);
nmr.nr_flags = w;
if (ioctl (fd, NIOCREGIF, &nmr) < 0) {
D ("unable to register interface %s", ifname);
return -1;
}
mem = mmap (NULL, nmr.nr_memsize,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED) {
D ("unable to mmap");
return -1;
}
nifp = NETMAP_IF (mem, nmr.nr_offset);
if (x > 0)
*ring = NETMAP_TXRING (nifp, q);
else
*ring = NETMAP_RXRING (nifp, q);
return fd;
}
#define extract_netmap_hw_tx_ring(i, q, r) \
extract_netmap_ring (i, q, r, 1, NR_REG_ONE_NIC)
#define extract_netmap_hw_rx_ring(i, q, r) \
extract_netmap_ring (i, q, r, 0, NR_REG_ONE_NIC)
#define extract_netmap_sw_tx_ring(i, q, r) \
extract_netmap_ring (i, q, r, 1, NR_REG_SW)
#define extract_netmap_sw_rx_ring(i, q, r) \
extract_netmap_ring (i, q, r, 0, NR_REG_SW)
int
set_if_promisc (char * ifname)
{
int fd;
struct ifreq ifr;
fd = socket (AF_INET, SOCK_DGRAM, 0);
memset (&ifr, 0, sizeof (ifr));
strncpy (ifr.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl (fd, SIOCGIFFLAGS, &ifr) != 0) {
D ("failed to get interface status");
return -1;
}
ifr.ifr_flags |= IFF_UP|IFF_PROMISC;
if (ioctl (fd, SIOCSIFFLAGS, &ifr) != 0) {
D ("failed to set interface to promisc");
return -1;
}
return 0;
}
void
usage (void)
{
printf ("usage: urldnsdump\n"
"\t" "-i : interface\n"
"\t" "-p : port (multiple)\n"
"\t" "-d [output file] : enable dns dump\n"
"\t" "-s : dns dump sampling rate (default 1)\n");
return;
}
int
main (int argc, char ** argv)
{
int fd, qnum, n, ch;
char * dev;
struct nmreq nmr;
struct urldump * urldump;
pthread_t tids[URLDUMP_THREAD_MAX];
for (n = 0; n < PORTLISTMAX; n++) {
portlist[n] = 0;
}
dev = NULL;
portnum = 0;
while ((ch = getopt (argc, argv, "i:p:d:s:")) != -1) {
switch (ch) {
case 'i' :
dev = optarg;
break;
case 'p' :
portlist[portnum++] = htons (atoi (optarg));
break;
case 'd':
dns_output = optarg;
dns_output_fd = creat (dns_output, S_IRUSR|S_IWUSR|
S_IRGRP|S_IWGRP);
if (dns_output_fd < 0) {
D ("failed to open \"%s\"", dns_output);
perror ("creat");
return -1;
}
break;
case 's' :
dns_srate = atoi (optarg);
if (dns_srate < 1) {
D ("sampling rate must be larger than 0");
return -1;
}
break;
default :
usage ();
return -1;
}
}
if (dev == NULL) {
D ("interface is not specified");
usage ();
return -1;
}
/* check number of netmap rx rings */
fd = open ("/dev/netmap", O_RDWR);
if (fd < 0) {
D ("Unable to open /dev/netmap");
perror ("open");
return -1;
}
memset (&nmr, 0, sizeof (nmr));
nmr.nr_version = NETMAP_API;
strncpy (nmr.nr_name, dev, IFNAMSIZ - 1);
if (ioctl (fd, NIOCGINFO, &nmr) < 0) {
D ("unable to get interface info for %s", dev);
return -1;
}
qnum = nmr.nr_rx_rings < URLDUMP_THREAD_MAX ?
nmr.nr_rx_rings : URLDUMP_THREAD_MAX;
close (fd);
/* register rings and start urldumper threads for each ring */
for (n = 0; n < qnum; n++) {
urldump = (struct urldump *) malloc (sizeof (struct urldump));
memset (urldump, 0, sizeof (struct urldump));
urldump->dev = dev;
urldump->fd = extract_netmap_hw_rx_ring
(dev, n, &urldump->ring);
urldump->dns_srate_count = 0;
D ("Start thread for Ring %d", n);
pthread_create (&tids[n], NULL, urldumper, urldump);
}
set_if_promisc (dev);
while (1) {
/* controlling urldump will be implmeneted here */
sleep (60);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment