forked from Netronome/bpf-samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xdpdump_kern.c
142 lines (115 loc) · 3.08 KB
/
xdpdump_kern.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
// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
// Copyright (c) 2018 Netronome Systems, Inc.
#include <stdbool.h>
#include <stddef.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/string.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include "bpf_endian.h"
#include "bpf_helpers.h"
#include "xdpdump_common.h"
struct bpf_map_def SEC("maps") perf_map = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.max_entries = MAX_CPU,
};
static __always_inline bool parse_udp(void *data, __u64 off, void *data_end,
struct pkt_meta *pkt)
{
struct udphdr *udp;
udp = data + off;
if (udp + 1 > data_end)
return false;
pkt->port16[0] = udp->source;
pkt->port16[1] = udp->dest;
return true;
}
static __always_inline bool parse_tcp(void *data, __u64 off, void *data_end,
struct pkt_meta *pkt)
{
struct tcphdr *tcp;
tcp = data + off;
if (tcp + 1 > data_end)
return false;
pkt->port16[0] = tcp->source;
pkt->port16[1] = tcp->dest;
pkt->seq = tcp->seq;
return true;
}
static __always_inline bool parse_ip4(void *data, __u64 off, void *data_end,
struct pkt_meta *pkt)
{
struct iphdr *iph;
iph = data + off;
if (iph + 1 > data_end)
return false;
if (iph->ihl != 5)
return false;
pkt->src = iph->saddr;
pkt->dst = iph->daddr;
pkt->l4_proto = iph->protocol;
return true;
}
static __always_inline bool parse_ip6(void *data, __u64 off, void *data_end,
struct pkt_meta *pkt)
{
struct ipv6hdr *ip6h;
ip6h = data + off;
if (ip6h + 1 > data_end)
return false;
memcpy(pkt->srcv6, ip6h->saddr.s6_addr32, 16);
memcpy(pkt->dstv6, ip6h->daddr.s6_addr32, 16);
pkt->l4_proto = ip6h->nexthdr;
return true;
}
SEC("xdp")
int process_packet(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct pkt_meta pkt = {};
__u32 off;
/* parse packet for IP Addresses and Ports */
off = sizeof(struct ethhdr);
if (data + off > data_end)
return XDP_PASS;
pkt.l3_proto = bpf_htons(eth->h_proto);
if (pkt.l3_proto == ETH_P_IP) {
if (!parse_ip4(data, off, data_end, &pkt))
return XDP_PASS;
off += sizeof(struct iphdr);
} else if (pkt.l3_proto == ETH_P_IPV6) {
if (!parse_ip6(data, off, data_end, &pkt))
return XDP_PASS;
off += sizeof(struct ipv6hdr);
}
if (data + off > data_end)
return XDP_PASS;
/* obtain port numbers for UDP and TCP traffic */
if (pkt.l4_proto == IPPROTO_TCP) {
if (!parse_tcp(data, off, data_end, &pkt))
return XDP_PASS;
off += sizeof(struct tcphdr);
} else if (pkt.l4_proto == IPPROTO_UDP) {
if (!parse_udp(data, off, data_end, &pkt))
return XDP_PASS;
off += sizeof(struct udphdr);
} else {
pkt.port16[0] = 0;
pkt.port16[1] = 0;
}
pkt.pkt_len = data_end - data;
pkt.data_len = data_end - data - off;
bpf_perf_event_output(ctx, &perf_map,
(__u64)pkt.pkt_len << 32 | BPF_F_CURRENT_CPU,
&pkt, sizeof(pkt));
return XDP_PASS;
}
char _license[] SEC("license") = "Dual BSD/GPL";