-
Notifications
You must be signed in to change notification settings - Fork 12
/
PacketHeaderV6.cpp
159 lines (137 loc) · 5.33 KB
/
PacketHeaderV6.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
/**
* PacketHeaderV4 class serves to interpret and provide a hashing function for an IPv6 header, looking at similiar fields
* to what GWLB does when producing a flow cookie.
*/
#include "PacketHeaderV6.h"
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <cstring>
#include <typeindex>
#include "utils.h"
using namespace std::string_literals;
/**
* Parse an IP packet header.
*
* @param pktbuf IP packet header to parse
* @param pktlen Length of the packet.
*/
PacketHeaderV6::PacketHeaderV6(unsigned char *pktbuf, ssize_t pktlen)
{
struct ip6_hdr *iph6 = (struct ip6_hdr *)pktbuf;
if(pktlen < sizeof(struct ip6_hdr))
throw std::invalid_argument("PacketHeaderV6 provided a packet too small to be an IPv6 packet.");
if( ((iph6->ip6_ctlun.ip6_un2_vfc & 0xF0) >> 4) != 6)
throw std::invalid_argument("PacketHeaderV6 provided a packet that isn't IPv6 : "s + std::to_string(iph6->ip6_ctlun.ip6_un2_vfc) + " -- "s + std::to_string(iph6->ip6_ctlun.ip6_un2_vfc >> 4) + " at offset "s + std::to_string(
offsetof(struct ip6_hdr, ip6_ctlun.ip6_un2_vfc)));
// The first 4 bits are version, next 8 are traffic class. The last 20 (0xFFFFF) are the flow label.
flow = be32toh(iph6->ip6_ctlun.ip6_un1.ip6_un1_flow) & 0xFFFFF;
memcpy(&src, &iph6->ip6_src, sizeof(struct in6_addr));
memcpy(&dst, &iph6->ip6_dst, sizeof(struct in6_addr));
prot = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
switch(prot)
{
case IPPROTO_UDP:
{
if(pktlen < sizeof(struct ip6_hdr) + sizeof(struct udphdr))
throw std::invalid_argument("PacketHeaderV6 provided a packet with protocol=UDP, but too small to carry UDP information.");
struct udphdr *udp = (struct udphdr *)(pktbuf + sizeof(struct ip6_hdr));
srcpt = be16toh(udp->uh_sport);
dstpt = be16toh(udp->uh_dport);
break;
}
case IPPROTO_TCP:
{
if(pktlen < sizeof(struct ip6_hdr) + sizeof(struct tcphdr))
throw std::invalid_argument("PacketHeaderV6 provided a packet with protocol=TCP, but too small to carry UDP information.");
struct tcphdr *tcp = (struct tcphdr *)(pktbuf + sizeof(struct ip6_hdr));
srcpt = be16toh(tcp->th_sport);
dstpt = be16toh(tcp->th_dport);
break;
}
default:
srcpt = 0;
dstpt = 0;
break;
}
}
PacketHeaderV6::PacketHeaderV6(uint8_t prot, uint32_t flow, struct in6_addr src, struct in6_addr dst, uint16_t srcpt, uint16_t dstpt)
: src(src), dst(dst), flow(flow), srcpt(srcpt), dstpt(dstpt), prot(prot)
{
}
/***
* Return a new packet header with the src/dst ips and ports swapped (i.e. this flow in the reverse direction)
* @return A new PacketHeaderV4 object
*/
PacketHeaderV6 PacketHeaderV6::reverse() const
{
return PacketHeaderV6(prot, flow, dst, src, dstpt, srcpt);
}
/**
* Compare if the headers in one packet match another. Meant to be used for the various sort and search functions.
*
* @param ph PacketHeaderV4 to compare againgst.
* @return true if PacketHeaders are the same, false otherwise.
*/
bool PacketHeaderV6::operator==(const PacketHeaderV6 &ph) const
{
#ifdef HASH_IS_SYMMETRICAL
return prot == ph.prot &&
((srcpt == ph.srcpt && dstpt == ph.dstpt && !memcmp(&src, &ph.src, sizeof(struct in6_addr)) && !memcmp(&dst, &ph.dst, sizeof(struct in6_addr))) ||
(srcpt == ph.dstpt && dstpt == ph.srcpt && !memcmp(&src, &ph.dst, sizeof(struct in6_addr)) && !memcmp(&dst, &ph.src, sizeof(struct in6_addr))));
#else
return prot == ph.prot && srcpt == ph.srcpt && dstpt == ph.dstpt &&
!memcmp(&src, &ph.src, sizeof(struct in6_addr)) && !memcmp(&dst, &ph.dst, sizeof(struct in6_addr));
#endif
}
/**
* Returns a hash value based on the protocol data.
*
* @return Hash value.
*/
std::size_t PacketHeaderV6::hash() const
{
return hashFunc(prot, (void *)&src, (void *)&dst, 16, srcpt, dstpt);
}
/**
* Returns a string with the contents of the PacketHeaderV4 for human consumption.
*
* @return The string.
*/
std::string PacketHeaderV6::text() const
{
std::string s = "PacketHeaderV6: ";
char srcip[INET6_ADDRSTRLEN], dstip[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &src, srcip, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &dst, dstip, INET6_ADDRSTRLEN);
s = s + stringFormat("SrcIP: %s, DstIP: %s, Flow: %x", srcip, dstip, flow);
if(prot == IPPROTO_UDP)
s = s + stringFormat(" Prot UDP, src pt %d, dst pt %d", srcpt, dstpt);
else if(prot == IPPROTO_TCP)
s = s + stringFormat(" Prot TCP, src pt %d, dst pt %d", srcpt, dstpt);
else
s = s + " Prot #" + std::to_string(prot);
//s = s + stringFormat(", hash %x", hash());
return s;
}
/**
* Output the human-readable content to an output stream.
* @param os Output stream to write to.
* @param m The PacketHeaderV4 to output
* @return The same output stream.
*/
std::ostream &operator<<(std::ostream &os, PacketHeaderV6 const &m)
{
return os << m.text();
}
/**
* Extend std::hash for PacketHeaderV6
*/
std::size_t std::hash<PacketHeaderV6>::operator()(const PacketHeaderV6& t) const
{
return t.hash();
};