Skip to content

Commit 52c592e

Browse files
committed
packet03: implement assignments 1 through 4
Implement assignments for the packet03-redirecting lesson and place corresponding solutions to the package-solutions directory. Signed-off-by: Anton Protopopov <a.s.protopopov@gmail.com>
1 parent 19707d2 commit 52c592e

9 files changed

Lines changed: 1152 additions & 31 deletions

File tree

common/common_defines.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ struct config {
66
int ifindex;
77
char *ifname;
88
char ifname_buf[IF_NAMESIZE];
9+
int redirect_ifindex;
10+
char *redirect_ifname;
11+
char redirect_ifname_buf[IF_NAMESIZE];
912
bool do_unload;
1013
char filename[512];
1114
char progsec[32];

common/common_params.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ void parse_cmdline_args(int argc, char **argv,
9191
}
9292

9393
/* Parse commands line args */
94-
while ((opt = getopt_long(argc, argv, "hd:ASNFUq",
94+
while ((opt = getopt_long(argc, argv, "hd:r:ASNFUq",
9595
long_options, &longindex)) != -1) {
9696
switch (opt) {
9797
case 'd':
@@ -109,6 +109,21 @@ void parse_cmdline_args(int argc, char **argv,
109109
goto error;
110110
}
111111
break;
112+
case 'r':
113+
if (strlen(optarg) >= IF_NAMESIZE) {
114+
fprintf(stderr, "ERR: --redirect-dev name too long\n");
115+
goto error;
116+
}
117+
cfg->redirect_ifname = (char *)&cfg->redirect_ifname_buf;
118+
strncpy(cfg->redirect_ifname, optarg, IF_NAMESIZE);
119+
cfg->redirect_ifindex = if_nametoindex(cfg->redirect_ifname);
120+
if (cfg->redirect_ifindex == 0) {
121+
fprintf(stderr,
122+
"ERR: --redirect-dev name unknown err(%d):%s\n",
123+
errno, strerror(errno));
124+
goto error;
125+
}
126+
break;
112127
case 'A':
113128
cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */
114129
break;

packet-solutions/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
22

33
XDP_TARGETS := xdp_prog_kern
4-
USER_TARGETS :=
4+
USER_TARGETS := xdp_prog_user
55

66
LIBBPF_DIR = ../libbpf/src/
77
COMMON_DIR = ../common
88

99
COPY_LOADER := xdp_loader
10+
COPY_STATS := xdp_stats
1011
EXTRA_DEPS := $(COMMON_DIR)/parsing_helpers.h
1112

1213
include $(COMMON_DIR)/common.mk

packet-solutions/xdp_prog_kern.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@
77
// The parsing helper functions from the packet01 lesson have moved here
88
#include "../common/parsing_helpers.h"
99

10+
/* Defines xdp_stats_map */
11+
#include "../common/xdp_stats_kern_user.h"
12+
#include "../common/xdp_stats_kern.h"
13+
14+
#ifndef memcpy
15+
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
16+
#endif
17+
18+
struct bpf_map_def SEC("maps") tx_port = {
19+
.type = BPF_MAP_TYPE_DEVMAP,
20+
.key_size = sizeof(int),
21+
.value_size = sizeof(int),
22+
.max_entries = 256,
23+
};
24+
25+
struct bpf_map_def SEC("maps") redirect_params = {
26+
.type = BPF_MAP_TYPE_HASH,
27+
.key_size = ETH_ALEN,
28+
.value_size = ETH_ALEN,
29+
.max_entries = 1,
30+
};
1031

1132
/* Pops the outermost VLAN tag off the packet. Returns the popped VLAN ID on
1233
* success or negative errno on failure.
@@ -126,5 +147,273 @@ int xdp_vlan_swap_func(struct xdp_md *ctx)
126147
return XDP_PASS;
127148
}
128149

150+
static __always_inline void swap_src_dst_mac(struct ethhdr *eth)
151+
{
152+
__u8 h_tmp[ETH_ALEN];
153+
memcpy(h_tmp, eth->h_source, ETH_ALEN);
154+
memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
155+
memcpy(eth->h_dest, h_tmp, ETH_ALEN);
156+
}
157+
158+
static __always_inline void swap_src_dst_ipv6(struct ipv6hdr *ipv6)
159+
{
160+
struct in6_addr tmp = ipv6->saddr;
161+
ipv6->saddr = ipv6->daddr;
162+
ipv6->daddr = tmp;
163+
}
164+
165+
static __always_inline void swap_src_dst_ipv4(struct iphdr *iphdr)
166+
{
167+
__be32 tmp = iphdr->saddr;
168+
iphdr->saddr = iphdr->daddr;
169+
iphdr->daddr = tmp;
170+
}
171+
172+
static __always_inline __u16 csum16_add(__u16 csum, __u16 addend)
173+
{
174+
csum += addend;
175+
return csum + (csum < addend);
176+
}
177+
178+
/* Solution to packet03/assignment-1 */
179+
SEC("xdp_icmp_echo")
180+
int xdp_icmp_echo_func(struct xdp_md *ctx)
181+
{
182+
void *data_end = (void *)(long)ctx->data_end;
183+
void *data = (void *)(long)ctx->data;
184+
struct hdr_cursor nh;
185+
struct ethhdr *eth;
186+
int eth_type;
187+
int ip_type;
188+
int icmp_type;
189+
struct iphdr *iphdr;
190+
struct ipv6hdr *ipv6hdr;
191+
__u16 echo_reply, m0, m1;
192+
struct icmphdr *icmphdr;
193+
__u32 action = XDP_PASS;
194+
195+
/* These keep track of the next header type and iterator pointer */
196+
nh.pos = data;
197+
198+
/* Parse Ethernet and IP/IPv6 headers */
199+
eth_type = parse_ethhdr(&nh, data_end, &eth);
200+
if (eth_type == ETH_P_IP) {
201+
ip_type = parse_iphdr(&nh, data_end, &iphdr);
202+
if (ip_type != IPPROTO_ICMP)
203+
goto out;
204+
} else if (eth_type == ETH_P_IPV6) {
205+
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
206+
if (ip_type != IPPROTO_ICMPV6)
207+
goto out;
208+
} else {
209+
goto out;
210+
}
211+
212+
icmp_type = parse_icmphdr(&nh, data_end, &icmphdr);
213+
if (eth_type == ETH_P_IP && icmp_type == ICMP_ECHO) {
214+
/* Swap IP source and destination */
215+
swap_src_dst_ipv4(iphdr);
216+
echo_reply = ICMP_ECHOREPLY;
217+
} else if (eth_type == ETH_P_IPV6 && icmp_type == ICMPV6_ECHO_REQUEST) {
218+
/* Swap IPv6 source and destination */
219+
swap_src_dst_ipv6(ipv6hdr);
220+
echo_reply = ICMPV6_ECHO_REPLY;
221+
} else {
222+
goto out;
223+
}
224+
225+
/* Swap Ethernet source and destination */
226+
swap_src_dst_mac(eth);
227+
228+
/* Patch the packet and update the checksum */
229+
m0 = * (__u16 *) icmphdr;
230+
icmphdr->type = echo_reply;
231+
m1 = * (__u16 *) icmphdr;
232+
icmphdr->checksum = ~(csum16_add(csum16_add(~icmphdr->checksum, ~m0), m1));
233+
234+
action = XDP_TX;
235+
236+
out:
237+
return xdp_stats_record_action(ctx, action);
238+
}
239+
240+
/* Assignment 2 */
241+
SEC("xdp_redirect")
242+
int xdp_redirect_func(struct xdp_md *ctx)
243+
{
244+
void *data_end = (void *)(long)ctx->data_end;
245+
void *data = (void *)(long)ctx->data;
246+
struct hdr_cursor nh;
247+
struct ethhdr *eth;
248+
int eth_type;
249+
int action = XDP_PASS;
250+
unsigned char dst[ETH_ALEN] = { /* TODO: put your values here */ };
251+
unsigned ifindex = 0/* TODO: put your values here */;
252+
253+
/* These keep track of the next header type and iterator pointer */
254+
nh.pos = data;
255+
256+
/* Parse Ethernet and IP/IPv6 headers */
257+
eth_type = parse_ethhdr(&nh, data_end, &eth);
258+
if (eth_type == -1)
259+
goto out;
260+
261+
/* Set a proper destination address */
262+
memcpy(eth->h_dest, dst, ETH_ALEN);
263+
action = bpf_redirect(ifindex, 0);
264+
265+
out:
266+
return xdp_stats_record_action(ctx, action);
267+
}
268+
269+
/* Assignment 3 */
270+
SEC("xdp_redirect_map")
271+
int xdp_redirect_map_func(struct xdp_md *ctx)
272+
{
273+
void *data_end = (void *)(long)ctx->data_end;
274+
void *data = (void *)(long)ctx->data;
275+
struct hdr_cursor nh;
276+
struct ethhdr *eth;
277+
int eth_type;
278+
int action = XDP_PASS;
279+
unsigned char *dst;
280+
281+
/* These keep track of the next header type and iterator pointer */
282+
nh.pos = data;
283+
284+
/* Parse Ethernet and IP/IPv6 headers */
285+
eth_type = parse_ethhdr(&nh, data_end, &eth);
286+
if (eth_type == -1)
287+
goto out;
288+
289+
/* Do we know where to redirect this packet? */
290+
dst = bpf_map_lookup_elem(&redirect_params, eth->h_source);
291+
if (!dst)
292+
goto out;
293+
294+
/* Set a proper destination address */
295+
memcpy(eth->h_dest, dst, ETH_ALEN);
296+
action = bpf_redirect_map(&tx_port, 0, 0);
297+
298+
out:
299+
return xdp_stats_record_action(ctx, action);
300+
}
301+
302+
#define AF_INET 2
303+
#define AF_INET6 10
304+
#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF)
305+
306+
/* from include/net/ip.h */
307+
static __always_inline int ip_decrease_ttl(struct iphdr *iph)
308+
{
309+
__u32 check = iph->check;
310+
check += bpf_htons(0x0100);
311+
iph->check = (__u16)(check + (check >= 0xFFFF));
312+
return --iph->ttl;
313+
}
314+
315+
/* Solution to packet03/assignment-4 */
316+
SEC("xdp_router")
317+
int xdp_router_func(struct xdp_md *ctx)
318+
{
319+
void *data_end = (void *)(long)ctx->data_end;
320+
void *data = (void *)(long)ctx->data;
321+
struct bpf_fib_lookup fib_params = {};
322+
struct ethhdr *eth = data;
323+
struct ipv6hdr *ip6h;
324+
struct iphdr *iph;
325+
__u16 h_proto;
326+
__u64 nh_off;
327+
int rc;
328+
int action = XDP_PASS;
329+
330+
nh_off = sizeof(*eth);
331+
if (data + nh_off > data_end) {
332+
action = XDP_DROP;
333+
goto out;
334+
}
335+
336+
h_proto = eth->h_proto;
337+
if (h_proto == bpf_htons(ETH_P_IP)) {
338+
iph = data + nh_off;
339+
340+
if (iph + 1 > data_end) {
341+
action = XDP_DROP;
342+
goto out;
343+
}
344+
345+
if (iph->ttl <= 1)
346+
goto out;
347+
348+
fib_params.family = AF_INET;
349+
fib_params.tos = iph->tos;
350+
fib_params.l4_protocol = iph->protocol;
351+
fib_params.sport = 0;
352+
fib_params.dport = 0;
353+
fib_params.tot_len = bpf_ntohs(iph->tot_len);
354+
fib_params.ipv4_src = iph->saddr;
355+
fib_params.ipv4_dst = iph->daddr;
356+
} else if (h_proto == bpf_htons(ETH_P_IPV6)) {
357+
struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src;
358+
struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst;
359+
360+
ip6h = data + nh_off;
361+
if (ip6h + 1 > data_end) {
362+
action = XDP_DROP;
363+
goto out;
364+
}
365+
366+
if (ip6h->hop_limit <= 1)
367+
goto out;
368+
369+
fib_params.family = AF_INET6;
370+
fib_params.flowinfo = *(__be32 *) ip6h & IPV6_FLOWINFO_MASK;
371+
fib_params.l4_protocol = ip6h->nexthdr;
372+
fib_params.sport = 0;
373+
fib_params.dport = 0;
374+
fib_params.tot_len = bpf_ntohs(ip6h->payload_len);
375+
*src = ip6h->saddr;
376+
*dst = ip6h->daddr;
377+
} else {
378+
goto out;
379+
}
380+
381+
fib_params.ifindex = ctx->ingress_ifindex;
382+
383+
rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0);
384+
switch (rc) {
385+
case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */
386+
if (h_proto == bpf_htons(ETH_P_IP))
387+
ip_decrease_ttl(iph);
388+
else if (h_proto == bpf_htons(ETH_P_IPV6))
389+
ip6h->hop_limit--;
390+
391+
memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
392+
memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
393+
action = bpf_redirect_map(&tx_port, fib_params.ifindex, 0);
394+
break;
395+
case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */
396+
case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */
397+
case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */
398+
action = XDP_DROP;
399+
break;
400+
case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */
401+
case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */
402+
case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */
403+
case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */
404+
case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */
405+
/* PASS */
406+
break;
407+
}
408+
409+
out:
410+
return xdp_stats_record_action(ctx, action);
411+
}
412+
413+
SEC("xdp_pass")
414+
int xdp_pass_func(struct xdp_md *ctx)
415+
{
416+
return XDP_PASS;
417+
}
129418

130419
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)