1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GRE/VXLANのpcapファイルをlibpcap apiを利用して生成

Last updated at Posted at 2015-10-14

トンネルプロトコルの検証用に、pcapファイルのパケットにGREヘッダおよびVXLANヘッダを付与するツールを作ってみた。
PCAPファイルの読み書きにはlibpcap apiを使用した。
wiresharkで解析できているので問題ないはず。
チェックサムのとこは適当・・・


GREヘッダの付与

pcap2gre.h
# ifndef __PCAP2GRE_H__
# define __PCAP2GRE_H__

# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <stdint.h>
# include <string.h>
# include <limits.h>
# include <pthread.h>
# include <assert.h>
# include <pcap.h>
# include <arpa/inet.h>
# include <net/ethernet.h>
# include <netinet/ether.h>
# include <netinet/ip.h>
# include <netinet/tcp.h>
# include <netinet/udp.h>
# include <inttypes.h>
# define __STDC_FORMAT_MACROS

# define CONF_INT	0
# define CONF_STR	1
# define CONF_SIZE	1024

# define TCPDUMP_MAGIC		0xa1b2c3d4
# define PCAP_VERSION_MAJOR	2
# define PCAP_VERSION_MINOR	4
# define DLT_EN10MB			1				/* Ethernet (10Mb) */
# define PCAP_SNAPLEN		0xffff

struct pd_timeval {
	uint32_t	tv_sec;
	uint32_t	tv_usec;
};

struct pd_pkthdr {
	struct pd_timeval	ts;
	uint32_t			caplen;
	uint32_t			len;
};

typedef struct ether_h {
	struct ether_addr	ether_dhost;
	struct ether_addr	ether_shost;
	uint16_t			ntag;
}ether_t;

union gre_opt_id
{
	uint32_t	vsid:24;	/* Virtual Subnet ID (VSID)			*/
	uint32_t	flowid:8;	/* Flow ID							*/
};

union gre_opt
{
	struct {
		uint32_t	csum:16;	/* Checksum							*/
		uint32_t	pad1:16;
	};

	/* RFC2890 Support */
	uint32_t		key;		/* Key								*/
	uint32_t		seq;		/* Sequence Number					*/

	/* NVGRE Support */
	struct {
		uint32_t	vsid:24;	/* Virtual Subnet ID (VSID)			*/
		uint32_t	flowid:8;	/* Flow ID							*/
	};
};

struct gre
{
	#if __BYTE_ORDER == __LITTLE_ENDIAN
	    uint8_t		pad1:4;		/* Reserved0 (bits 4-7)				*/
		uint8_t		sflg:1;		/* Sequence Number Present (bit 3)	*/
		uint8_t		kflg:1;		/* Key Present (bit 2)				*/
		uint8_t		pad0:1;		/* Padding							*/
	    uint8_t		cflg:1;		/* Checksum Present (bit 0)			*/
		uint8_t		ver:3;		/* Version Number (bits 5-7)		*/
	    uint8_t		pad2:5;		/* Reserved0 (bits 0-4)				*/
	#endif

	#if __BYTE_ORDER == __BIG_ENDIAN
	    uint8_t		cflg:1;		/* Checksum Present (bit 0)			*/
		uint8_t		pad0:1;		/* Padding							*/
		uint8_t		kflg:1;		/* Key Present (bit 2)				*/
		uint8_t		sflg:1;		/* Sequence Number Present (bit 3)	*/
	    uint8_t		pad1:4;		/* Reserved0 (bits 4-7)				*/
	    uint8_t		pad2:5;		/* Reserved0 (bits 0-4)				*/
		uint8_t		ver:3;		/* Version Number (bits 5-7)		*/
	#endif

	uint16_t		proto;		/* Protocol Type (2 octets)			*/
	union gre_opt	opt[3];		/* Optional Fields					*/
};

typedef enum gretype {
	GRE_RFC2784 = 0,
	GRE_RFC2890,
	GRE_NVGRE,
	GRE_MAX
} gretype_e;

struct sys_conf {
	uint32_t			gre_type;
	struct in_addr		src;
	struct in_addr		dst;
	bool				checksum_bit;
	bool				key_bit;
	bool				sequence_num_bit;
	uint32_t			key;
	uint32_t			sequence_num;
	uint32_t			vsid:24;
	uint32_t			flowid:8;
} conf_t;

# endif // __PCAP2GRE_H__
pcap2gre.c
# include "pcap2gre.h"

void print_usage(void)
{
	printf("Usage: pcap2gre [pcap in] [pcap out]\n");
	return;
}

const char *gre_name(gretype_e type)
{
	switch (type)
	{
		case GRE_RFC2784:
			return "GRE/RFC2784";
		case GRE_RFC2890:
			return "Key and Sequence Number Extensions to GRE/RFC2890";
		case GRE_NVGRE:
			return "NVGRE draft";
		default:
			assert(false);
			return NULL;
	}
}

bool parse_element(	const char	*buf		,
					const int	type		,
					const char	*findKey	,
					void		*getvalue	)
{
	char key[CONF_SIZE], value[CONF_SIZE];

	if (sscanf(buf, "%s = %s", key, value) != 2)
	{
		if (sscanf(buf, "%s =", key) == 1)
		{
			value[0] = '\0';
		}
		else
		{
			printf("pcap2gre.conf format error. %s\n", buf);
			return false;
		}
	}

	if (strcmp(findKey, key) == 0)
	{
		if (CONF_INT == type)
		{
			*(uint32_t *)getvalue = atoi(value);
			return true;
		}
		else
		{
			snprintf((char *)getvalue, CONF_SIZE, "%s", value);
			return true;
		}
	}
	return false;
}

bool parse_conf(struct sys_conf *conf)
{
	int			ret;
	FILE		*fp;
	char		row[PATH_MAX];
	char		bufs[CONF_SIZE];
	uint32_t	bufi;

	fp = fopen("pcap2gre.conf", "r");
	if (NULL == fp) {
		fprintf(stderr, "pcap2gre.conf\n");
		return false;
	}

	memset(conf, 0x00, sizeof(conf_t));

	while (NULL != fgets(row , sizeof(row) , fp)) {

		if (row[strlen(row) - 1] == '\n') row[strlen(row) - 1] = '\0';
		if (row[0] == '#' || row[0] == '\0') continue;

		if (true == parse_element(row, CONF_INT, "GRE_TYPE", &conf->gre_type)) {
			continue;
		}

		if (true == parse_element(row, CONF_STR, "TUNNEL_IP_SRC", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->src));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "TUNNEL_IP_DST", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->dst));
			continue;
		}

		if (true == parse_element(row, CONF_INT, "CHECKSUM_BIT", &bufi)) {
			if (1 == bufi) conf->checksum_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "KEY_BIT", &bufi)) {
			if (1 == bufi) conf->key_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "SEQUENCE_NUM_BIT", &bufi)) {
			if (1 == bufi) conf->sequence_num_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "KEY", &conf->key)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "SEQUENCE_NUMBER", &conf->sequence_num)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VSID", &bufi)) {
			conf->vsid = bufi;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "FLOWID", &bufi)) {
			conf->flowid = bufi;
			continue;
		}

	}

	fclose(fp);
	return true;
}

unsigned short pkt_checksum(unsigned short *data, size_t len, unsigned short add)
{
	unsigned int L_sum = add;
	union	{
				unsigned char	L_8bit[2];
				unsigned short	L_16bit;
	} L_Tail;
	
	 while( len > 1 ){
		L_sum += ntohs(*data++);
		len -= 2;
	}

	if(len == 1){
		L_Tail.L_16bit = 0;
		L_Tail.L_8bit[0] = *(u_int8_t *)data;
		L_sum += ntohs(L_Tail.L_16bit);
	}
	
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);

	return	~(unsigned short)L_sum;
}

int main (int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
	FILE		*fp;
	pcap_t		*pd;
	char		ebuf[PCAP_ERRBUF_SIZE];
	char		cap_in[PATH_MAX];
	char		cap_out[PATH_MAX];
	uint32_t	i = 0;
	uint32_t	j = 0;

	struct pcap_file_header	pfhdr;
	struct pcap_pkthdr		phdr_in;
	struct pd_pkthdr		phdr_out;
	const uint8_t			*data;
	const unsigned char		*pkt;

	struct sys_conf		conf;
	char				bufsrc[INET_ADDRSTRLEN];
	char				bufdst[INET_ADDRSTRLEN];

	if (argc > 2) {
		strncpy(cap_in, argv[1], PATH_MAX);
		strncpy(cap_out, argv[2], PATH_MAX);
	} else {
		print_usage();
		exit(EXIT_FAILURE);
	}

	assert(parse_conf(&conf));

	printf("in[%s] out[%s]\n", argv[1], argv[2]);
	printf("GRE[%d:%s]\ntunnel_ip src[%s] dst[%s]\n",
		conf.gre_type, gre_name(conf.gre_type),
		inet_ntop(AF_INET, (const void *)&conf.src, bufsrc, INET_ADDRSTRLEN),
		inet_ntop(AF_INET, (const void *)&conf.dst, bufdst, INET_ADDRSTRLEN));
	printf("checksum_bit[%d] key_bit[%d] sequence_num_bit[%d]\n",
		conf.checksum_bit, conf.key_bit, conf.sequence_num_bit);
	printf("key[%d] sequence_num[%d] vsid[%d] flowid[%d]\n\n",
		conf.key, conf.sequence_num, conf.vsid, conf.flowid);

	memset(&pfhdr, 0x00, sizeof(struct pcap_file_header));
	pfhdr.magic = TCPDUMP_MAGIC;
	pfhdr.version_major = PCAP_VERSION_MAJOR;
	pfhdr.version_minor = PCAP_VERSION_MINOR;
	pfhdr.snaplen       = PCAP_SNAPLEN;
	pfhdr.linktype      = DLT_EN10MB;

	struct ether_h 		*eth_hdr;
	struct ether_addr	smac;

	uint16_t ip_size;
	uint16_t csum;
	uint16_t etype;
	uint16_t *etypep;
	uint16_t elength;
	uint8_t  *head;

	/* Tunnel ipv4 header */
	struct ip	iph_out;
	struct ip	iph_in;
	memset(&iph_out, 0x00, sizeof(struct ip));
	memset(&iph_in, 0x00, sizeof(struct ip));

	iph_out.ip_v   = 4;
	iph_out.ip_hl  = 5;
	iph_out.ip_hl  = 5;
	iph_out.ip_off = 0x0040;
	iph_out.ip_ttl = 0x40;
	iph_out.ip_p   = 0x2f;
	memcpy(&iph_out.ip_src, &conf.src, sizeof(struct in_addr));
	memcpy(&iph_out.ip_dst, &conf.dst, sizeof(struct in_addr));

	iph_in.ip_v   = 4;
	iph_in.ip_hl  = 5;
	iph_in.ip_off = 0x0040;
	iph_in.ip_ttl = 0x40;
	iph_in.ip_p   = 0x2f;
	memcpy(&iph_in.ip_src, &conf.dst, sizeof(struct in_addr));
	memcpy(&iph_in.ip_dst, &conf.src, sizeof(struct in_addr));

	/* GRE header */
	struct gre	gre_hdr;
	uint32_t	opt_cnt  = 0;
	uint32_t	gre_size = 0;
	memset(&gre_hdr, 0x00, sizeof(struct gre));

	uint32_t	vsid;

	switch (conf.gre_type)
	{
		case GRE_RFC2784:
			/* Checksum Present */
			if (conf.checksum_bit) {
				gre_hdr.cflg = 1;
				opt_cnt++;
			}
			break;
		case GRE_RFC2890:
			/* Checksum Present */
			if (conf.checksum_bit) {
				gre_hdr.cflg = 1;
				opt_cnt++;
			}
			/* Key Present */
			if (conf.key_bit) {
				gre_hdr.kflg = 1;
				gre_hdr.opt[opt_cnt].key = htonl(conf.key);
				opt_cnt++;
			}
			/* Sequence Number Present */
			if (conf.sequence_num_bit) {
				gre_hdr.sflg = 1;
				gre_hdr.opt[opt_cnt].seq = htonl(conf.sequence_num);
				opt_cnt++;
			}
			break;
		case GRE_NVGRE:
		default:
			gre_hdr.kflg = 1;
			vsid = htonl(conf.vsid);
			printf("vsid [0x%x] [0x%x]\n\n", conf.vsid, vsid);
			gre_hdr.opt[opt_cnt].vsid = vsid>>8;
			gre_hdr.opt[opt_cnt].flowid = conf.flowid;
			opt_cnt++;
			break;
	}

	gre_size = 4 + (opt_cnt * 4);

	/* pcap open */
	assert(pd = pcap_open_offline(cap_in, ebuf));
	assert(fp = fopen(cap_out, "wb"));

	fwrite(&pfhdr, sizeof(struct pcap_file_header), 1, fp);

	while (1) {

		if ((data = pcap_next(pd, &phdr_in)) != NULL) {
			eth_hdr = (struct ether_h *)data;

			if (!i) {
				memcpy(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr));
			}

			i++;

			printf("[%d] caplen[%d] len[%d] ts[%"PRId64".%"PRId64"] src[%s] dst[%s]\n",
				i, phdr_in.caplen, phdr_in.len, phdr_in.ts.tv_sec, phdr_in.ts.tv_usec,
				ether_ntoa(&eth_hdr->ether_shost), ether_ntoa(&eth_hdr->ether_dhost));

			/* Ether header 読み飛ばし */
			head = (uint8_t *)data;
			elength = 12;

			for (j = 0; j < 2; j++) {
				etype = ntohs(*(uint16_t *)(head + elength));
				printf("\tether_type[0x%x]\n", etype);
		
				switch (etype) {

					case 0x8100:
					case 0x88a8:
					case 0x9100:
					case 0x9200:
					case 0x9300:
						elength += 4;
						break;
			
					case 0x86dd:
						etypep = (uint16_t *)(head + elength);
						*etypep = 0x0008;
						goto loopend;
					default:
						goto loopend;
				}
			}
loopend:
			/* pcap header */
			memset(&phdr_out, 0x00, sizeof(struct pd_pkthdr));
			phdr_out.ts.tv_sec  = (uint32_t)phdr_in.ts.tv_sec;
			phdr_out.ts.tv_usec = (uint32_t)phdr_in.ts.tv_usec;
			phdr_out.caplen  = phdr_in.caplen + 20 + gre_size;
			phdr_out.len     = phdr_in.len + 20 + gre_size;

			fwrite(&phdr_out, sizeof(struct pd_pkthdr), 1, fp);

			/* Ether header */
			elength += 2;
			fwrite(eth_hdr, elength, 1, fp);
			printf("\tether_length[%d]\n", elength);

			ip_size  = 20 + gre_size + (phdr_in.caplen - elength);

			/* in or out 判定 */
			if(0 == memcmp(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr))) {
				/* Tunnel IP header (out) */
				iph_out.ip_len = htons(ip_size);
				iph_out.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_out, 20, 0);
				iph_out.ip_sum = htons(csum);
				fwrite(&iph_out, 20, 1, fp);
			} else {
				/* Tunnel IP header (in) */
				iph_in.ip_len = htons(ip_size);
				iph_in.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_in, 20, 0);
				iph_in.ip_sum = htons(csum);
				fwrite(&iph_in, 20, 1, fp);
			}

			/* GRE header */
			gre_hdr.proto = htons(etype);

			if (gre_hdr.cflg) {
				gre_hdr.opt[0].csum = 0;
				csum = pkt_checksum((unsigned short *)&gre_hdr, gre_size, 0);
				printf("\tcsum[0x%x]\n", csum);
				csum = pkt_checksum((unsigned short *)(head + elength), (phdr_in.caplen - elength), csum);
				printf("\tcsum[0x%x]\n", csum);
				gre_hdr.opt[0].csum = htons(csum);
			}

			fwrite(&gre_hdr, gre_size, 1, fp);

			/* Carrier Protocol */
			fwrite((head + elength), (phdr_in.caplen - elength), 1, fp);
		} else {
			break;
		}
	}

funcend:
	pcap_close(pd);
	fclose(fp);

	exit(EXIT_SUCCESS);
}
pcap2gre.conf
# RFC2784 
#
#  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |C|       Reserved0       | Ver |         Protocol Type         |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |      Checksum (optional)      |       Reserved1 (Optional)    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
# RFC2890  Key and Sequence Number Extensions to GRE
# https://tools.ietf.org/html/rfc2890
#
#  The proposed GRE header will have the following format:
#
#   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |C| |K|S| Reserved0       | Ver |         Protocol Type         |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |      Checksum (optional)      |       Reserved1 (Optional)    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |                         Key (optional)                        |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |                 Sequence Number (Optional)                    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
# NVGRE draft
# https://tools.ietf.org/html/draft-sridharan-virtualization-nvgre-07
#
#  GRE Header:
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |0| |1|0|   Reserved0     | Ver |   Protocol Type 0x6558        |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |               Virtual Subnet ID (VSID)        |    FlowID     |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
#
# GRE_TYPE
#   0 : RFC2784
#   1 : RFC2890
#   2 : NVGRE
GRE_TYPE = 0

TUNNEL_IP_SRC = 2.2.2.1
TUNNEL_IP_DST = 2.2.2.2

# CHECKSUM_BIT = 0
CHECKSUM_BIT = 1

KEY_BIT = 0
# KEY_BIT = 1

SEQUENCE_NUM_BIT = 0
# SEQUENCE_NUM_BIT = 1

# KEY = 100
# SEQUENCE_NUMBER = 200
# VSID = 100
# FLOWID = 15

VXLANヘッダの付与

pcap2vxlan.h
# ifndef __PCAP2VXLAN_H__
# define __PCAP2VXLAN_H__

# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <stdint.h>
# include <string.h>
# include <limits.h>
# include <pthread.h>
# include <assert.h>
# include <pcap.h>
# include <arpa/inet.h>
# include <net/ethernet.h>
# include <netinet/ether.h>
# include <netinet/ip.h>
# include <netinet/tcp.h>
# include <netinet/udp.h>
# include <inttypes.h>
# define __STDC_FORMAT_MACROS

# define CONF_INT	0
# define CONF_STR	1
# define CONF_SIZE	1024

# define TCPDUMP_MAGIC		0xa1b2c3d4
# define PCAP_VERSION_MAJOR	2
# define PCAP_VERSION_MINOR	4
# define DLT_EN10MB			1				/* Ethernet (10Mb) */
# define PCAP_SNAPLEN		0xffff

struct pd_timeval {
	uint32_t	tv_sec;
	uint32_t	tv_usec;
};

struct pd_pkthdr {
	struct pd_timeval	ts;
	uint32_t			caplen;
	uint32_t			len;
};

typedef struct ether_h {
	struct ether_addr	ether_dhost;
	struct ether_addr	ether_shost;
	uint16_t			ntag;
}ether_t;

/*
 *  VXLAN Header:
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |R|R|R|R|I|R|R|R|            Reserved                           |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |                VXLAN Network Identifier (VNI) |   Reserved    |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

struct vxlan
{
	#if __BYTE_ORDER == __LITTLE_ENDIAN
		uint32_t	r6_8:3;		/* 6 to 8 R bit */
		uint32_t	i:1;		/* I bit		*/
		uint32_t	r1_4:4;		/* 1 to 4 R bit */
	#endif

	#if __BYTE_ORDER == __BIG_ENDIAN
		uint32_t	r1_4:4;		/* 1 to 4 R bit */
		uint32_t	i:1;		/* I bit		*/
		uint32_t	r6_8:3;		/* 6 to 8 R bit */
	#endif

	uint32_t		pad1:24;

	uint32_t		vni:24;
	uint32_t		pad2:8;
};

typedef struct sys_conf {
	struct ether_addr	src_mac;
	struct ether_addr	dst_mac;
	struct in_addr		src_ip;
	struct in_addr		dst_ip;
	uint32_t			vxlan_port;
	uint32_t			vxlan_vni;
} conf_t;

# endif // __PCAP2VXLAN_H__
pcap2vxlan.c
# include "pcap2vxlan.h"

void print_usage(void)
{
	printf("Usage: pcap2vxlan [pcap in] [pcap out]\n");

	return;
}

bool parse_element(	const char	*buf		,
					const int	type		,
					const char	*findKey	,
					void		*getvalue	)
{
	char key[CONF_SIZE], value[CONF_SIZE];

	if (sscanf(buf, "%s = %s", key, value) != 2) {
		if (sscanf(buf, "%s =", key) == 1) {
			value[0] = '\0';
		} else {
			printf("pcap2gre.conf format error. %s\n", buf);
			return false;
		}
	}

	if (strcmp(findKey, key) == 0) {
		if (CONF_INT == type) {
			*(uint32_t *)getvalue = atoi(value);
			return true;
		} else {
			snprintf((char *)getvalue, CONF_SIZE, "%s", value);
			return true;
		}
	}
	return false;
}

bool parse_conf(struct sys_conf *conf)
{
	int			ret;
	FILE		*fp;
	char		row[PATH_MAX];
	char		bufs[CONF_SIZE];
	uint32_t	bufi;

	fp = fopen("pcap2vxlan.conf", "r");
	if (NULL == fp) {
		fprintf(stderr, "pcap2gre.conf\n");
		return false;
	}

	memset(conf, 0x00, sizeof(conf_t));

	while (NULL != fgets(row , sizeof(row) , fp)) {

		if (row[strlen(row) - 1] == '\n') row[strlen(row) - 1] = '\0';
		if (row[0] == '#' || row[0] == '\0') continue;

		if (true == parse_element(row, CONF_STR, "OUTER_SRC_MAC", bufs)) {
			assert(ether_aton_r(bufs, &conf->src_mac));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_DST_MAC", bufs)) {
			assert(ether_aton_r(bufs, &conf->dst_mac));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_SRC_IP", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->src_ip));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_DST_IP", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->dst_ip));
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VXLAN_PORT", &conf->vxlan_port)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VXLAN_VNI", &conf->vxlan_vni)) {
			continue;
		}
	}

	fclose(fp);
	return true;
}

unsigned short pkt_checksum(unsigned short *data, size_t len, unsigned short add)
{
	unsigned int L_sum = add;
	union	{
				unsigned char	L_8bit[2];
				unsigned short	L_16bit;
	} L_Tail;
	
	 while( len > 1 ){
		L_sum += ntohs(*data++);
		len -= 2;
	}

	if(len == 1){
		L_Tail.L_16bit = 0;
		L_Tail.L_8bit[0] = *(u_int8_t *)data;
		L_sum += ntohs(L_Tail.L_16bit);
	}
	
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);

	return	~(unsigned short)L_sum;
}

int main (int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
	FILE		*fp;
	pcap_t		*pd;
	char		ebuf[PCAP_ERRBUF_SIZE];
	char		cap_in[PATH_MAX];
	char		cap_out[PATH_MAX];
	uint32_t	i = 0;

	struct pcap_file_header	pfhdr;
	struct pcap_pkthdr		phdr_in;
	struct pd_pkthdr		phdr_out;
	const uint8_t			*data;
	const unsigned char		*pkt;

	struct sys_conf		conf;
	char				bufsrc[INET_ADDRSTRLEN];
	char				bufdst[INET_ADDRSTRLEN];


	if (argc > 2) {
		strncpy(cap_in, argv[1], PATH_MAX);
		strncpy(cap_out, argv[2], PATH_MAX);
	} else {
		print_usage();
		exit(EXIT_FAILURE);
	}

	assert(parse_conf(&conf));

	printf("in[%s] out[%s]\n", argv[1], argv[2]);
	printf("mac_src[%s] mac_dst[%s]\n",
		ether_ntoa(&conf.src_mac), ether_ntoa(&conf.dst_mac));
	printf("ip_src[%s] ip_dst[%s]\n",
		inet_ntop(AF_INET, (const void *)&conf.src_ip, bufsrc, INET_ADDRSTRLEN),
		inet_ntop(AF_INET, (const void *)&conf.dst_ip, bufdst, INET_ADDRSTRLEN));
	printf("vxlan_port[%d] vxlan_vni[%d]\n\n", conf.vxlan_port, conf.vxlan_vni);

	memset(&pfhdr, 0x00, sizeof(struct pcap_file_header));
	pfhdr.magic = TCPDUMP_MAGIC;
	pfhdr.version_major = PCAP_VERSION_MAJOR;
	pfhdr.version_minor = PCAP_VERSION_MINOR;
	pfhdr.snaplen       = PCAP_SNAPLEN;
	pfhdr.linktype      = DLT_EN10MB;

	/* ether */
	struct ether_h	ether_out;
	struct ether_h	ether_in;
	memset(&ether_out, 0x00, sizeof(struct ether_h));
	memset(&ether_in, 0x00, sizeof(struct ether_h));

	memcpy(&ether_out.ether_shost, &conf.src_mac, sizeof(struct ether_addr));
	memcpy(&ether_out.ether_dhost, &conf.dst_mac, sizeof(struct ether_addr));
	ether_out.ntag = 0x0008;

	memcpy(&ether_in.ether_shost, &conf.dst_mac, sizeof(struct ether_addr));
	memcpy(&ether_in.ether_dhost, &conf.src_mac, sizeof(struct ether_addr));
	ether_in.ntag = 0x0008;

	/* ipv4 */
	struct ip	iph_out;
	struct ip	iph_in;
	memset(&iph_out, 0x00, sizeof(struct ip));
	memset(&iph_in, 0x00, sizeof(struct ip));

	iph_out.ip_v   = 4;
	iph_out.ip_hl  = 5;
	iph_out.ip_hl  = 5;
	iph_out.ip_off = 0x0040;
	iph_out.ip_ttl = 0x40;
	iph_out.ip_p   = 0x11;
	memcpy(&iph_out.ip_src, &conf.src_ip, sizeof(struct in_addr));
	memcpy(&iph_out.ip_dst, &conf.dst_ip, sizeof(struct in_addr));

	iph_in.ip_v   = 4;
	iph_in.ip_hl  = 5;
	iph_in.ip_off = 0x0040;
	iph_in.ip_ttl = 0x40;
	iph_in.ip_p   = 0x11;
	memcpy(&iph_in.ip_src, &conf.dst_ip, sizeof(struct in_addr));
	memcpy(&iph_in.ip_dst, &conf.src_ip, sizeof(struct in_addr));

	/* udp */
	struct udphdr	udph_out;
	struct udphdr	udph_in;
	memset(&udph_out, 0x00, sizeof(struct udphdr));
	memset(&udph_in, 0x00, sizeof(struct udphdr));

	uint16_t	dst_port;
	dst_port = conf.vxlan_port;

	udph_out.dest = htons(dst_port);
	udph_in.dest  = htons(dst_port);

	/* vxlan */
	struct vxlan	vxlanh;
	memset(&vxlanh, 0x00, sizeof(struct vxlan));

	vxlanh.i = 1;

	uint32_t	vni;
	vni = htonl(conf.vxlan_vni);
	vxlanh.vni = vni>>8;

	/* pcap open */
	assert(pd = pcap_open_offline(cap_in, ebuf));
	assert(fp = fopen(cap_out, "wb"));

	fwrite(&pfhdr, sizeof(struct pcap_file_header), 1, fp);

	struct ether_h 		*eth_hdr;
	struct ether_addr	smac;
	uint16_t			ip_size;
	uint16_t			udp_size;
	uint16_t			csum;

	while (1) {

		if ((data = pcap_next(pd, &phdr_in)) != NULL) {
			eth_hdr = (struct ether_h *)data;

			if (!i) {
				memcpy(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr));
			}

			i++;

			printf("[%d] caplen[%d] len[%d] ts[%"PRId64".%"PRId64"] src[%s] dst[%s]\n",
				i, phdr_in.caplen, phdr_in.len, phdr_in.ts.tv_sec, phdr_in.ts.tv_usec,
				ether_ntoa(&eth_hdr->ether_shost), ether_ntoa(&eth_hdr->ether_dhost));

			memset(&phdr_out, 0x00, sizeof(struct pd_pkthdr));
			phdr_out.ts.tv_sec  = (uint32_t)phdr_in.ts.tv_sec;
			phdr_out.ts.tv_usec = (uint32_t)phdr_in.ts.tv_usec;
			phdr_out.caplen  = phdr_in.caplen + 50;
			phdr_out.len     = phdr_in.len + 50;

			fwrite(&phdr_out, sizeof(struct pd_pkthdr), 1, fp);

			ip_size  = 36 + phdr_in.caplen;
			udp_size = 16 + phdr_in.caplen;

			/* in or out 判定 */
			if(0 == memcmp(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr))) {

				udph_out.source = pkt_checksum((unsigned short *)&eth_hdr, sizeof(struct ether_h), 0);
				iph_out.ip_len = htons(ip_size);
				udph_out.len   = htons(udp_size);
				iph_out.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_out, 20, 0);
				iph_out.ip_sum = htons(csum);

				fwrite(&ether_out, 14, 1, fp);
				fwrite(&iph_out, 20, 1, fp);
				fwrite(&udph_out, 8, 1, fp);
				fwrite(&vxlanh, 8, 1, fp);
			} else {
				udph_in.source = pkt_checksum((unsigned short *)&eth_hdr, sizeof(struct ether_h), 0);
				iph_in.ip_len = htons(ip_size);
				udph_in.len   = htons(udp_size);
				iph_in.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_in, 20, 0);
				iph_in.ip_sum = htons(csum);

				fwrite(&ether_in, 14, 1, fp);
				fwrite(&iph_in, 20, 1, fp);
				fwrite(&udph_in, 8, 1, fp);
				fwrite(&vxlanh, 8, 1, fp);
			}

			fwrite(data, phdr_in.caplen, 1, fp);

		} else {
			break;
		}
	}

	pcap_close(pd);
	fclose(fp);

	exit(EXIT_SUCCESS);
}
pcap2vxlan.conf
# VXLAN Header:
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |R|R|R|R|I|R|R|R|            Reserved                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                VXLAN Network Identifier (VNI) |   Reserved    |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#

OUTER_SRC_MAC = 10:10:10:10:10:10
OUTER_DST_MAC = 20:20:20:20:20:20

OUTER_SRC_IP = 3.3.3.1
OUTER_DST_IP = 3.3.3.1

VXLAN_PORT = 4789
VXLAN_VNI = 100
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?