トンネルプロトコルの検証用に、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, ð_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(ð_hdr->ether_shost), ether_ntoa(ð_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, ð_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(ðer_out, 0x00, sizeof(struct ether_h));
memset(ðer_in, 0x00, sizeof(struct ether_h));
memcpy(ðer_out.ether_shost, &conf.src_mac, sizeof(struct ether_addr));
memcpy(ðer_out.ether_dhost, &conf.dst_mac, sizeof(struct ether_addr));
ether_out.ntag = 0x0008;
memcpy(ðer_in.ether_shost, &conf.dst_mac, sizeof(struct ether_addr));
memcpy(ðer_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, ð_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(ð_hdr->ether_shost), ether_ntoa(ð_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, ð_hdr->ether_shost, sizeof(struct ether_addr))) {
udph_out.source = pkt_checksum((unsigned short *)ð_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(ðer_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 *)ð_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(ðer_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