summaryrefslogtreecommitdiff
path: root/common/src/vxlan.cpp
blob: d5c86d70685791827deee5a1b2f1e7f276827f18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <string.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/ether.h>

#include "vxlan.h"

#define CHECKSUM_CARRY(x) (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))
static inline int checksum(uint16_t *data, int len)
{
    int sum = 0;
    int nleft = len;
    uint16_t ans = 0;
    uint16_t *w = data;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1)
    {
        *(char *)(&ans) = *(char *)w;
        sum += ans;
    }

    return sum;
}

static inline void eth_header_encode(struct ethhdr *eth_hdr, const u_char src_mac[], const u_char dst_mac[], uint16_t proto)
{
    memcpy(eth_hdr->h_source, src_mac, ETH_ALEN);
    memcpy(eth_hdr->h_dest, dst_mac, ETH_ALEN);
    eth_hdr->h_proto = htons(proto);
}

static inline void ip_header_encode(struct ip *ip_hdr, const in_addr_t src_ip, const in_addr_t dst_ip, uint16_t ipid, uint8_t proto, uint16_t pld_len)
{
    ip_hdr->ip_v = 4;                                    /* version 4 */
    ip_hdr->ip_hl = 5;                                   /* 20 byte header */
    ip_hdr->ip_tos = 0;                                  /* IP tos */
    ip_hdr->ip_len = htons(sizeof(struct ip) + pld_len); /* total length */
    ip_hdr->ip_id = htons(ipid);                         /* IP ID */
    ip_hdr->ip_off = htons(0);                           /* fragmentation flags */
    ip_hdr->ip_ttl = 80;                                 /* time to live */
    ip_hdr->ip_p = proto;                                /* transport protocol */
    ip_hdr->ip_sum = 0;                                  /* do this later */
    ip_hdr->ip_src.s_addr = src_ip;
    ip_hdr->ip_dst.s_addr = dst_ip;

    int sum = checksum((uint16_t *)ip_hdr, 20);
    ip_hdr->ip_sum = CHECKSUM_CARRY(sum);
}

static inline void udp_header_encode(struct udphdr *udp_hdr, uint16_t udp_sport, uint16_t udp_dport, uint16_t pld_len)
{
    int udp_hlen = sizeof(struct udphdr) + pld_len;

    udp_hdr->uh_sport = htons(udp_sport);
    udp_hdr->uh_dport = htons(udp_dport);
    udp_hdr->uh_ulen = htons(udp_hlen);
    /*
     * UDP Checksum: It SHOULD be transmitted as zero
     * https://datatracker.ietf.org/doc/html/rfc7348#section-5
     */
    udp_hdr->uh_sum = 0;
}

// return  0 : success
// return -1 : error
int vxlan_frame_decode(struct vxlan_hdr **vxlan_hdr, const char *data, uint16_t len)
{
    if (len < VXLAN_FRAME_HDR_LEN)
    {
        return -1;
    }

    struct ethhdr *eth_hdr = (struct ethhdr *)data;
    if (eth_hdr->h_proto != htons(ETH_P_IP))
    {
        return -1;
    }

    struct ip *ip_hdr = (struct ip *)((char *)eth_hdr + sizeof(struct ethhdr));
    if (ip_hdr->ip_p != IPPROTO_UDP)
    {
        return -1;
    }

    struct udphdr *udp_hdr = (struct udphdr *)((char *)ip_hdr + sizeof(struct ip));
    if (udp_hdr->uh_dport != htons(4789))
    {
        return -1;
    }

    *vxlan_hdr = (struct vxlan_hdr *)((char *)udp_hdr + sizeof(struct udphdr));

    return 0;
}

void vxlan_frame_encode(char *buff,
                        const u_char eth_src_mac[], const u_char eth_dst_mac[],
                        const in_addr_t ip_src_addr, const in_addr_t ip_dst_addr, uint16_t ip_id,
                        uint16_t udp_src_port, uint16_t udp_pld_len,
                        uint8_t vni_opt_dir, uint8_t vni_opt_traffic, uint8_t vni_opt_sf_index)
{
    struct ethhdr *eth_hdr = (struct ethhdr *)buff;
    struct ip *ip_hdr = (struct ip *)((char *)eth_hdr + sizeof(struct ethhdr));
    struct udphdr *udp_hdr = (struct udphdr *)((char *)ip_hdr + sizeof(struct ip));
    struct vxlan_hdr *vxlan_hdr = (struct vxlan_hdr *)((char *)udp_hdr + sizeof(struct udphdr));

    // MUST be set to zero
    memset(vxlan_hdr, 0, sizeof(struct vxlan_hdr));
    vxlan_hdr->flags = VXLAN_FLAGS;
    vxlan_set_opt(vxlan_hdr, VNI_OPT_DIR, vni_opt_dir);
    vxlan_set_opt(vxlan_hdr, VNI_OPT_TRAFFIC, vni_opt_traffic);
    vxlan_set_opt(vxlan_hdr, VNI_OPT_SFINDEX, vni_opt_sf_index);

    eth_header_encode(eth_hdr, eth_src_mac, eth_dst_mac, ETH_P_IP);
    ip_header_encode(ip_hdr, ip_src_addr, ip_dst_addr, ip_id, IPPROTO_UDP, sizeof(struct udphdr) + sizeof(struct vxlan_hdr) + udp_pld_len);
    udp_header_encode(udp_hdr, udp_src_port, VXLAN_DST_PORT, sizeof(struct vxlan_hdr) + udp_pld_len);
}

uint16_t calculate_vxlan_source_port(struct four_tuple *innermost_tuple4)
{
    /*
     * When calculating the UDP source port number in this manner, it
     * is RECOMMENDED that the value be in the dynamic/private port
     * range 49152-65535 [RFC6335].
     */
    uint64_t hash = four_tuple_hash(innermost_tuple4);
    uint16_t port = (uint16_t)(hash % (65535 - 49152) + 49152);

    return port;
}