LoginSignup
1
1

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