-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp-protector.c
159 lines (126 loc) · 4.25 KB
/
tcp-protector.c
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
#include <uapi/linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#define PROTO_TCP 6
struct hdr_cursor {
void *pos;
};
static __always_inline int parse_ethhdr(struct hdr_cursor *nh,
void *data_end,
struct ethhdr **ethhdr)
{
struct ethhdr *eth = nh->pos;
int hdrsize = sizeof(*eth);
/* Byte-count bounds check; check if current pointer + size of header
* is after data_end.
*/
if (nh->pos + hdrsize > data_end)
return -1;
nh->pos += hdrsize;
*ethhdr = eth;
return eth->h_proto; /* network-byte-order */
}
static __always_inline int parse_iphdr(struct hdr_cursor *nh,
void *data_end,
struct iphdr **iphdr)
{
struct iphdr *iph = nh->pos;
int hdrsize;
if (iph + 1 > data_end)
return -1;
hdrsize = iph->ihl * 4;
/* Sanity check packet field is valid */
if(hdrsize < sizeof(*iph))
return -1;
/* Variable-length IPv4 header, need to use byte-based arithmetic */
if (nh->pos + hdrsize > data_end)
return -1;
nh->pos += hdrsize;
*iphdr = iph;
return iph->protocol;
}
static __always_inline int parse_tcphdr(struct hdr_cursor *nh,
void *data_end,
struct tcphdr **tcphdr)
{
int len;
struct tcphdr *h = nh->pos;
if (h + 1 > data_end)
return -1;
len = h->doff * 4;
/* Sanity check packet field is valid */
if(len < sizeof(*h))
return -1;
/* Variable-length TCP header, need to use byte-based arithmetic */
if (nh->pos + len > data_end)
return -1;
nh->pos += len;
*tcphdr = h;
return len;
}
// define output data structure in C
struct data_t {
int srcip;
int data;
int issyn;
int isfin;
};
BPF_PERF_OUTPUT(events);
BPF_TABLE("percpu_hash", uint32_t, uint32_t, iplist, 6524288);
int xdp_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth;
struct iphdr *ip4h;
struct icmphdr *icmp4h;
struct tcphdr *tcph;
/* Default action XDP_PASS, imply everything we couldn't parse, or that
* we don't want to deal with, we just pass up the stack and let the
* kernel deal with it.
*/
__u32 action = XDP_PASS; /* Default action */
/* These keep track of the next header type and iterator pointer */
struct hdr_cursor nh;
int nh_type;
/* Start next header cursor position at data start */
nh.pos = data;
/* Packet parsing in steps: Get each header one at a time, aborting if
* parsing fails. Each helper function does sanity checking (is the
* header type in the packet correct?), and bounds checking.
*/
nh_type = parse_ethhdr(&nh, data_end, ð);
if(nh_type == bpf_htons(ETH_P_IP)){
nh_type = parse_iphdr(&nh, data_end, &ip4h);
if (nh_type >= 0){
uint32_t * value;
value = iplist.lookup(&(ip4h->saddr));
if(value){
return XDP_DROP;
}
if (nh_type == PROTO_TCP){
if (parse_tcphdr(&nh, data_end, &tcph) > 0){
uint32_t payload_offset = sizeof(*eth) + sizeof(*ip4h) + sizeof(*tcph);
uint32_t payload_length = ntohs(ip4h->tot_len) - (ip4h->ihl << 2) - (tcph->doff << 2);
struct data_t data = {};
if (tcph->syn == 1){
data.srcip = ip4h->saddr;
data.issyn = 1;
if (tcph->fin == 1){
data.isfin = 1;
}
if (payload_length >= 0){
data.data = payload_length;
}
events.perf_submit(ctx, &data, sizeof(data));
}
}
}
}
}
return XDP_PASS;
}