-
Notifications
You must be signed in to change notification settings - Fork 0
/
GenevePacket.cpp
129 lines (116 loc) · 5.09 KB
/
GenevePacket.cpp
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
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
/**
* GenevePacket class takes in a raw packet buffer and attempts to interpret it as a Geneve-encapsulated packet.
* The status member variable will be GP_STATUS_OK if this is successful and everything passes sanity checks,
* otherwise will be one of the other codes defined in GeneveHeader.h.
*
* This code is based on the version of Geneve as defined in RFC 8926.
*/
#include "GenevePacket.h"
#include <cstring>
/**
* Interpret the packet as a GENEVE packet. Does sanity checks to ensure the packet is correct. Callers should
* verify the class's status member is GP_STATUS_OK before using - otherwise handle the error code provided in
* that variable.
*
* @param pktBuf Pointer to the UDP packet payload received.
* @param pktLen Length of pktBuf
*/
GenevePacket::GenevePacket(unsigned char *pktBuf, ssize_t pktLen)
: status(GP_STATUS_EMPTY)
{
// 1) Process the Geneve Header in the passed in raw packet buffer. Since we're receiving via a UDP socket,
// the outer IPv4 header has been removed for us by the OS, so our first byte is the outer Geneve Reader.
// Geneve Header:
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Virtual Network Identifier (VNI) | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// ~ Variable-Length Options ~
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
unsigned char workBuf[8];
unsigned char *pktPtr; // Pointer to where the packet is being processed.
if(pktLen < 8)
{
status = GP_STATUS_TOO_SHORT;
return;
}
// Version check. This must be 0, by the current RFC.
if( (pktBuf[0] & 0xc0) != 0)
{
status = GP_STATUS_BAD_VER;
return;
}
// This application has no use for the O or C bits. Reserved must be ignored.
// GWLB uses L3 IPv4 encapsulation - ethertype 0x0800. Make sure that's what is here.
if(be16toh(*(uint16_t *)&pktBuf[2]) != 0x0800)
{
status = GP_STATUS_BAD_ETHERTYPE;
return;
}
bzero(workBuf, 8);
// This next copy is deliberately dest off by 1 to allow using be32toh which expects 32 bits. The off-by-one
// and the bzero just before ensure the first 8 bits are 0.
memcpy(&workBuf[1], &pktBuf[4], 3);
geneveVni = be32toh(*(uint32_t *)&workBuf);
// Geneve options processing. The options length is expressed in 4-byte multiples (RFC 8926 section 3.4).
int optLen = (pktBuf[0] & 0x3f) * 4;
if( optLen > pktLen - 8)
{
status = GP_STATUS_BAD_OPTLEN;
return;
}
// Work through the packet buffer, option-by-option, moving pktPtr as needed.
pktPtr = &pktBuf[8];
struct GeneveOption go;
while(pktPtr < &pktBuf[8 + optLen])
{
// Convert option to host format and in the struct. See RFC 8926 section 3.5.
bzero(&go, sizeof(go));
go.optClass = be16toh(*(uint16_t *)&pktPtr[0]);
go.optType = (uint8_t)pktPtr[2];
go.r = (unsigned char)(pktPtr[3] & 0xe0 >> 5);
go.optLen = (pktPtr[3] & 0x1f) * 4;
if(go.optLen > 0) go.optData = &pktPtr[4];
geneveOptions.push_back(go);
// check for AWS specific options for GWLB.
if(go.optClass == 0x108 && go.optType == 1 && go.optLen == 8)
{
gwlbeEniIdValid = true;
gwlbeEniId = be64toh(*(uint64_t *)go.optData);
}
else if(go.optClass == 0x108 && go.optType == 2 && go.optLen == 8)
{
attachmentIdValid = true;
attachmentId = be64toh(*(uint64_t *)go.optData);
}
else if(go.optClass == 0x108 && go.optType == 3 && go.optLen == 4)
{
flowCookieValid = true;
flowCookie = be32toh(*(uint32_t *)go.optData);
}
pktPtr += 4 + go.optLen;
}
header = std::vector<unsigned char>(pktBuf, pktBuf + 8 + optLen);
headerLen = 8 + optLen;
// If the three mandatory options for GWLB weren't seen, this can't be a valid packet from it.
if(!gwlbeEniIdValid || !attachmentIdValid || !flowCookieValid)
status = GP_STATUS_MISSING_GWLB_OPTIONS;
else
status = GP_STATUS_OK;
}
/**
* Output a human-readable string of the contents of this GenevePacket.
*
* @param os Output stream to write to.
* @param m The GenevePacket to write.
* @return The same output stream, with the text added.
*/
auto operator<<(std::ostream& os, GenevePacket const& m) -> std::ostream&
{
return os << " Status: " << (m.status) << std::hex << " GWLBe ENI ID: " << (m.gwlbeEniIdValid?m.gwlbeEniId:0) << " Attachment ID: " << (m.attachmentIdValid?m.attachmentId:0) << " Flow Cookie: " << (m.flowCookieValid?m.flowCookie:0) << std::dec;
}