Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F71813163
xdp_prog_kern.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
xdp_prog_kern.c
View Options
/* SPDX-License-Identifier: GPL-2.0 */
#include
<linux/bpf.h>
#include
<linux/in.h>
#include
<bpf/bpf_helpers.h>
#include
<bpf/bpf_endian.h>
// The parsing helper functions from the packet01 lesson have moved here
#include
"common/parsing_helpers.h"
#include
"common/rewrite_helpers.h"
/* Defines xdp_stats_map */
#include
"common/xdp_stats_kern_user.h"
#include
"common/xdp_stats_kern.h"
#ifndef memcpy
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
#endif
struct
{
__uint
(
type
,
BPF_MAP_TYPE_DEVMAP
);
__type
(
key
,
int
);
__type
(
value
,
int
);
__uint
(
max_entries
,
256
);
}
tx_port
SEC
(
".maps"
);
struct
{
__uint
(
type
,
BPF_MAP_TYPE_HASH
);
__type
(
key
,
ETH_ALEN
);
__type
(
value
,
ETH_ALEN
);
__uint
(
max_entries
,
1
);
}
redirect_params
SEC
(
".maps"
);
static
__always_inline
__u16
csum_fold_helper
(
__u32
csum
)
{
return
~
((
csum
&
0xffff
)
+
(
csum
>>
16
));
}
/*
* The icmp_checksum_diff function takes pointers to old and new structures and
* the old checksum and returns the new checksum. It uses the bpf_csum_diff
* helper to compute the checksum difference. Note that the sizes passed to the
* bpf_csum_diff helper should be multiples of 4, as it operates on 32-bit
* words.
*/
static
__always_inline
__u16
icmp_checksum_diff
(
__u16
seed
,
struct
icmphdr_common
*
icmphdr_new
,
struct
icmphdr_common
*
icmphdr_old
)
{
__u32
csum
,
size
=
sizeof
(
struct
icmphdr_common
);
csum
=
bpf_csum_diff
((
__be32
*
)
icmphdr_old
,
size
,
(
__be32
*
)
icmphdr_new
,
size
,
seed
);
return
csum_fold_helper
(
csum
);
}
/* Solution to packet03/assignment-1 */
SEC
(
"xdp_icmp_echo"
)
int
xdp_icmp_echo_func
(
struct
xdp_md
*
ctx
)
{
void
*
data_end
=
(
void
*
)(
long
)
ctx
->
data_end
;
void
*
data
=
(
void
*
)(
long
)
ctx
->
data
;
struct
hdr_cursor
nh
;
struct
ethhdr
*
eth
;
int
eth_type
;
int
ip_type
;
int
icmp_type
;
struct
iphdr
*
iphdr
;
struct
ipv6hdr
*
ipv6hdr
;
__u16
echo_reply
,
old_csum
;
struct
icmphdr_common
*
icmphdr
;
struct
icmphdr_common
icmphdr_old
;
__u32
action
=
XDP_PASS
;
/* These keep track of the next header type and iterator pointer */
nh
.
pos
=
data
;
/* Parse Ethernet and IP/IPv6 headers */
eth_type
=
parse_ethhdr
(
&
nh
,
data_end
,
&
eth
);
if
(
eth_type
==
bpf_htons
(
ETH_P_IP
))
{
ip_type
=
parse_iphdr
(
&
nh
,
data_end
,
&
iphdr
);
if
(
ip_type
!=
IPPROTO_ICMP
)
goto
out
;
}
else
if
(
eth_type
==
bpf_htons
(
ETH_P_IPV6
))
{
ip_type
=
parse_ip6hdr
(
&
nh
,
data_end
,
&
ipv6hdr
);
if
(
ip_type
!=
IPPROTO_ICMPV6
)
goto
out
;
}
else
{
goto
out
;
}
/*
* We are using a special parser here which returns a stucture
* containing the "protocol-independent" part of an ICMP or ICMPv6
* header. For purposes of this Assignment we are not interested in
* the rest of the structure.
*/
icmp_type
=
parse_icmphdr_common
(
&
nh
,
data_end
,
&
icmphdr
);
if
(
eth_type
==
bpf_htons
(
ETH_P_IP
)
&&
icmp_type
==
ICMP_ECHO
)
{
/* Swap IP source and destination */
swap_src_dst_ipv4
(
iphdr
);
echo_reply
=
ICMP_ECHOREPLY
;
}
else
if
(
eth_type
==
bpf_htons
(
ETH_P_IPV6
)
&&
icmp_type
==
ICMPV6_ECHO_REQUEST
)
{
/* Swap IPv6 source and destination */
swap_src_dst_ipv6
(
ipv6hdr
);
echo_reply
=
ICMPV6_ECHO_REPLY
;
}
else
{
goto
out
;
}
/* Swap Ethernet source and destination */
swap_src_dst_mac
(
eth
);
/* Patch the packet and update the checksum.*/
old_csum
=
icmphdr
->
cksum
;
icmphdr
->
cksum
=
0
;
icmphdr_old
=
*
icmphdr
;
icmphdr
->
type
=
echo_reply
;
icmphdr
->
cksum
=
icmp_checksum_diff
(
~
old_csum
,
icmphdr
,
&
icmphdr_old
);
/* Another, less generic, but a bit more efficient way to update the
* checksum is listed below. As only one 16-bit word changed, the sum
* can be patched using this formula: sum' = ~(~sum + ~m0 + m1), where
* sum' is a new sum, sum is an old sum, m0 and m1 are the old and new
* 16-bit words, correspondingly. In the formula above the + operation
* is defined as the following function:
*
* static __always_inline __u16 csum16_add(__u16 csum, __u16 addend)
* {
* csum += addend;
* return csum + (csum < addend);
* }
*
* So an alternative code to update the checksum might look like this:
*
* __u16 m0 = * (__u16 *) icmphdr;
* icmphdr->type = echo_reply;
* __u16 m1 = * (__u16 *) icmphdr;
* icmphdr->checksum = ~(csum16_add(csum16_add(~icmphdr->checksum, ~m0), m1));
*/
action
=
XDP_TX
;
out
:
return
xdp_stats_record_action
(
ctx
,
action
);
}
/* Solution to packet03/assignment-2 */
SEC
(
"xdp_redirect"
)
int
xdp_redirect_func
(
struct
xdp_md
*
ctx
)
{
void
*
data_end
=
(
void
*
)(
long
)
ctx
->
data_end
;
void
*
data
=
(
void
*
)(
long
)
ctx
->
data
;
struct
hdr_cursor
nh
;
struct
ethhdr
*
eth
;
int
eth_type
;
int
action
=
XDP_PASS
;
unsigned
char
dst
[
ETH_ALEN
]
=
{
/* TODO: put your values here */
};
unsigned
ifindex
=
0
/* TODO: put your values here */
;
/* These keep track of the next header type and iterator pointer */
nh
.
pos
=
data
;
/* Parse Ethernet and IP/IPv6 headers */
eth_type
=
parse_ethhdr
(
&
nh
,
data_end
,
&
eth
);
if
(
eth_type
==
-1
)
goto
out
;
/* Set a proper destination address */
memcpy
(
eth
->
h_dest
,
dst
,
ETH_ALEN
);
action
=
bpf_redirect
(
ifindex
,
0
);
out
:
return
xdp_stats_record_action
(
ctx
,
action
);
}
/* Solution to packet03/assignment-3 */
SEC
(
"xdp_redirect_map"
)
int
xdp_redirect_map_func
(
struct
xdp_md
*
ctx
)
{
void
*
data_end
=
(
void
*
)(
long
)
ctx
->
data_end
;
void
*
data
=
(
void
*
)(
long
)
ctx
->
data
;
struct
hdr_cursor
nh
;
struct
ethhdr
*
eth
;
int
eth_type
;
int
action
=
XDP_PASS
;
unsigned
char
*
dst
;
/* These keep track of the next header type and iterator pointer */
nh
.
pos
=
data
;
/* Parse Ethernet and IP/IPv6 headers */
eth_type
=
parse_ethhdr
(
&
nh
,
data_end
,
&
eth
);
if
(
eth_type
==
-1
)
goto
out
;
/* Do we know where to redirect this packet? */
dst
=
bpf_map_lookup_elem
(
&
redirect_params
,
eth
->
h_source
);
if
(
!
dst
)
goto
out
;
/* Set a proper destination address */
memcpy
(
eth
->
h_dest
,
dst
,
ETH_ALEN
);
action
=
bpf_redirect_map
(
&
tx_port
,
0
,
0
);
out
:
return
xdp_stats_record_action
(
ctx
,
action
);
}
#ifndef AF_INET
#define AF_INET 2
#endif
#ifndef AF_INET6
#define AF_INET6 10
#endif
#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF)
/* from include/net/ip.h */
static
__always_inline
int
ip_decrease_ttl
(
struct
iphdr
*
iph
)
{
__u32
check
=
iph
->
check
;
check
+=
bpf_htons
(
0x0100
);
iph
->
check
=
(
__u16
)(
check
+
(
check
>=
0xFFFF
));
return
--
iph
->
ttl
;
}
/* Solution to packet03/assignment-4 */
/* xdp_router is the name of the xdp program */
SEC
(
"xdp_router"
)
int
xdp_router_func
(
struct
xdp_md
*
ctx
)
{
/* this is the packet context*/
void
*
data_end
=
(
void
*
)(
long
)
ctx
->
data_end
;
void
*
data
=
(
void
*
)(
long
)
ctx
->
data
;
struct
bpf_fib_lookup
fib_params
=
{};
struct
ethhdr
*
eth
=
data
;
struct
ipv6hdr
*
ip6h
;
struct
iphdr
*
iph
;
__u16
h_proto
;
__u64
nh_off
;
int
rc
;
/* default action is to pass */
int
action
=
XDP_PASS
;
nh_off
=
sizeof
(
*
eth
);
if
(
data
+
nh_off
>
data_end
)
{
action
=
XDP_DROP
;
goto
out
;
}
/* determine if this is IP4 or IPv6 by looking at the Ethernet protocol field */
h_proto
=
eth
->
h_proto
;
if
(
h_proto
==
bpf_htons
(
ETH_P_IP
))
{
/* IPv4 part of the code */
iph
=
data
+
nh_off
;
if
(
iph
+
1
>
data_end
)
{
action
=
XDP_DROP
;
goto
out
;
}
/* as a real router, we need to check the TTL to prevent never ending loops*/
if
(
iph
->
ttl
<=
1
)
goto
out
;
/* populate the fib_params fields to prepare for the lookup */
fib_params
.
family
=
AF_INET
;
fib_params
.
tos
=
iph
->
tos
;
fib_params
.
l4_protocol
=
iph
->
protocol
;
fib_params
.
sport
=
0
;
fib_params
.
dport
=
0
;
fib_params
.
tot_len
=
bpf_ntohs
(
iph
->
tot_len
);
fib_params
.
ipv4_src
=
iph
->
saddr
;
fib_params
.
ipv4_dst
=
iph
->
daddr
;
}
else
if
(
h_proto
==
bpf_htons
(
ETH_P_IPV6
))
{
/* IPv6 part of the code */
struct
in6_addr
*
src
=
(
struct
in6_addr
*
)
fib_params
.
ipv6_src
;
struct
in6_addr
*
dst
=
(
struct
in6_addr
*
)
fib_params
.
ipv6_dst
;
ip6h
=
data
+
nh_off
;
if
(
ip6h
+
1
>
data_end
)
{
action
=
XDP_DROP
;
goto
out
;
}
/* as a real router, we need to check the TTL to prevent never ending loops*/
if
(
ip6h
->
hop_limit
<=
1
)
goto
out
;
/* populate the fib_params fields to prepare for the lookup */
fib_params
.
family
=
AF_INET6
;
fib_params
.
flowinfo
=
*
(
__be32
*
)
ip6h
&
IPV6_FLOWINFO_MASK
;
fib_params
.
l4_protocol
=
ip6h
->
nexthdr
;
fib_params
.
sport
=
0
;
fib_params
.
dport
=
0
;
fib_params
.
tot_len
=
bpf_ntohs
(
ip6h
->
payload_len
);
*
src
=
ip6h
->
saddr
;
*
dst
=
ip6h
->
daddr
;
}
else
{
goto
out
;
}
fib_params
.
ifindex
=
ctx
->
ingress_ifindex
;
/* this is where the FIB lookup happens. If the lookup is successful */
/* it will populate the fib_params.ifindex with the egress interface index */
rc
=
bpf_fib_lookup
(
ctx
,
&
fib_params
,
sizeof
(
fib_params
),
0
);
switch
(
rc
)
{
case
BPF_FIB_LKUP_RET_SUCCESS
:
/* lookup successful */
/* we are a router, so we need to decrease the ttl */
if
(
h_proto
==
bpf_htons
(
ETH_P_IP
))
ip_decrease_ttl
(
iph
);
else
if
(
h_proto
==
bpf_htons
(
ETH_P_IPV6
))
ip6h
->
hop_limit
--
;
/* set the correct new source and destionation mac addresses */
/* can be found in fib_params.dmac and fib_params.smac */
memcpy
(
eth
->
h_dest
,
fib_params
.
dmac
,
ETH_ALEN
);
memcpy
(
eth
->
h_source
,
fib_params
.
smac
,
ETH_ALEN
);
/* and done, now we set the action to bpf_redirect_map with fib_params.ifindex which is the egress port as paramater */
action
=
bpf_redirect_map
(
&
tx_port
,
fib_params
.
ifindex
,
0
);
break
;
case
BPF_FIB_LKUP_RET_BLACKHOLE
:
/* dest is blackholed; can be dropped */
case
BPF_FIB_LKUP_RET_UNREACHABLE
:
/* dest is unreachable; can be dropped */
case
BPF_FIB_LKUP_RET_PROHIBIT
:
/* dest not allowed; can be dropped */
action
=
XDP_DROP
;
break
;
case
BPF_FIB_LKUP_RET_NOT_FWDED
:
/* packet is not forwarded */
case
BPF_FIB_LKUP_RET_FWD_DISABLED
:
/* fwding is not enabled on ingress */
case
BPF_FIB_LKUP_RET_UNSUPP_LWT
:
/* fwd requires encapsulation */
case
BPF_FIB_LKUP_RET_NO_NEIGH
:
/* no neighbor entry for nh */
case
BPF_FIB_LKUP_RET_FRAG_NEEDED
:
/* fragmentation required to fwd */
/* PASS */
break
;
}
out
:
/* and done, update stats and return action */
return
xdp_stats_record_action
(
ctx
,
action
);
}
SEC
(
"xdp_pass"
)
int
xdp_pass_func
(
struct
xdp_md
*
ctx
)
{
return
xdp_stats_record_action
(
ctx
,
XDP_PASS
);
}
char
_license
[]
SEC
(
"license"
)
=
"GPL"
;
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Jan 30, 11:45 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3308627
Default Alt Text
xdp_prog_kern.c (10 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment