Created
July 17, 2015 11:23
-
-
Save upa/05a4070eb821cd50127e to your computer and use it in GitHub Desktop.
URL and DNS packet dump via 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
#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