Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SDF filter matching method added #434

Merged
merged 30 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
87e6805
SDF filter matching method added
retroruiner Oct 4, 2023
754f141
Corrected spelling
retroruiner Oct 4, 2023
03cdc99
Test
retroruiner Oct 4, 2023
b5fa39c
Deleted bpf_printk
retroruiner Oct 4, 2023
0e57204
Changed packet_src_ip conversion
retroruiner Oct 4, 2023
fe2f256
Changed how sdf filter is checked
retroruiner Oct 4, 2023
d7e47fc
changed match_sdf_filter_ipv4 to match_sdf_filter_ipv6
retroruiner Oct 4, 2023
c72f0be
Corrected accroing to comments
retroruiner Oct 5, 2023
3e15209
Deleted unecessary else clause
retroruiner Oct 5, 2023
5ab0a4c
Added port range check
retroruiner Oct 6, 2023
9372df5
Corrected the issue in match_sdf_filter methods
retroruiner Oct 6, 2023
fa79c6d
Corrected an error in the code connected to NULL return
retroruiner Oct 7, 2023
46f6125
Cannot seem to find out what causes the error in match_sdf_filter_ipv4
retroruiner Oct 7, 2023
2604787
Added SDF filter to handle_gtp_packet and corrected parse functions
retroruiner Oct 9, 2023
f58061e
Merge remote-tracking branch 'origin/main' into 83-multiple-pdr-per-s…
retroruiner Oct 9, 2023
afac8a6
Added parser for ip4 and ip6 to get protocol
retroruiner Oct 10, 2023
387cd34
Corrected parse_gtp method
retroruiner Oct 10, 2023
2f08039
Changed how sdf filter is checked in gtp
retroruiner Oct 11, 2023
257c2c0
Changed back
retroruiner Oct 11, 2023
c59361c
Midifed sdf filter checking in handle_gtp_packet
retroruiner Oct 11, 2023
41b64d4
Changed how inner_context is parsed
retroruiner Oct 11, 2023
72101e6
refacto & debug
Oct 15, 2023
77ddd97
fix review remarks
Oct 18, 2023
39bcb05
Merge branch 'main' into 83-multiple-pdr-per-session-support
pirog-spb Oct 18, 2023
efc8aad
fix matching by ip protocol
Oct 18, 2023
a71b791
update unit test
Oct 18, 2023
e62efc0
fix sole sdf scenario
Oct 19, 2023
a3feab3
remove hardcoded define
Oct 19, 2023
63c9e5a
restore enable log default value
Oct 20, 2023
6c87170
Merge branch 'main' into 83-multiple-pdr-per-session-support
pirog-spb Oct 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 128 additions & 15 deletions cmd/ebpf/xdp/n3n6_entrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#define _DEFAULT_SOURCE
#include <netinet/tcp.h>
retroruiner marked this conversation as resolved.
Show resolved Hide resolved
#include <linux/udp.h>
#include <sys/socket.h>

Expand All @@ -40,33 +42,130 @@
#include "xdp/utils/icmp.h"



#define DEFAULT_XDP_ACTION XDP_PASS


static __always_inline enum xdp_action send_to_gtp_tunnel(struct packet_context *ctx, int srcip, int dstip, __u8 tos, int teid) {
if (-1 == add_gtp_over_ip4_headers(ctx, srcip, dstip, tos, teid))
return XDP_ABORTED;

upf_printk("upf: send gtp pdu %pI4 -> %pI4", &ctx->ip4->saddr, &ctx->ip4->daddr);
increment_counter(ctx->n3_n6_counter, tx_n3);
return route_ipv4(ctx->xdp_ctx, ctx->eth, ctx->ip4);
}

static __always_inline enum xdp_action handle_n6_packet_ipv4(struct packet_context *ctx) {
static __always_inline __u8 match_sdf_filter_ipv4(struct packet_context *ctx, struct sdf_filter *sdf) {
struct iphdr *ip4 = ctx->ip4;
int l4_protocol = parse_ip4(ctx);
__u8 packet_protocol = ip4->protocol;
__u32 packet_src_ip = ip4->saddr;
__u32 packet_dst_ip = ip4->daddr;

__u16 packet_src_port = 0;
__u16 packet_dst_port = 0;

if(l4_protocol == IPPROTO_TCP) {
struct tcphdr *tcp_header = parse_tcp_src_dst(ctx);
packet_src_port = bpf_ntohs(tcp_header->th_sport);
packet_dst_port = bpf_ntohs(tcp_header->th_dport);
} else if(l4_protocol == IPPROTO_UDP) {
struct udphdr *udp_header = parse_udp_src_dst(ctx);
packet_src_port = bpf_ntohs(udp_header->source);
packet_dst_port = bpf_ntohs(udp_header->dest);
}

// upf_printk("SDF Filter source ip values: source ip:%p" ,
// &sdf->src_addr);
// upf_printk("SDF Filter destination values: destination ip:%p and mask: %p, destination port lower bound:%u, destination port upper bound:%u",
// &sdf->dst_addr.ip, &sdf->dst_addr.mask, sdf->dst_port.lower_bound, sdf->dst_port.upper_bound);
if (sdf->protocol != packet_protocol ||
(packet_src_ip & sdf->src_addr.mask) != sdf->src_addr.ip ||
retroruiner marked this conversation as resolved.
Show resolved Hide resolved
(packet_dst_ip & sdf->dst_addr.mask) != sdf->dst_addr.ip ||
packet_src_port < sdf->src_port.lower_bound || packet_src_port > sdf->src_port.upper_bound ||
packet_dst_port < sdf->dst_port.lower_bound || packet_dst_port > sdf->dst_port.upper_bound) {
return 0;
}


upf_printk("Packet with source ip:%pI4, destination ip:%pI4 matches SDF filter",
&ip4->saddr, &ip4->daddr);

return 1;
}

static __always_inline __u8 match_sdf_filter_ipv6(struct packet_context *ctx, struct sdf_filter *sdf) {
const struct ipv6hdr *ip6 = ctx->ip6;
int l4_protocol = parse_ip6(ctx);
__u8 packet_protocol = ip6->nexthdr;
struct in6_addr packet_src_ip = ip6->saddr;
struct in6_addr packet_dst_ip = ip6->daddr;

__uint128_t packet_src_ip_128 = *((__uint128_t*)packet_src_ip.s6_addr);
__uint128_t packet_dst_ip_128 = *((__uint128_t*)packet_dst_ip.s6_addr);


__u16 packet_src_port = 0;
__u16 packet_dst_port = 0;


if(l4_protocol == IPPROTO_TCP) {
struct tcphdr *tcp_header = parse_tcp_src_dst(ctx);
packet_src_port = bpf_ntohs(tcp_header->th_sport);
packet_dst_port = bpf_ntohs(tcp_header->th_dport);
} else if(l4_protocol == IPPROTO_UDP) {
struct udphdr *udp_header = parse_udp_src_dst(ctx);
packet_src_port = bpf_ntohs(udp_header->source);
packet_dst_port = bpf_ntohs(udp_header->dest);
}


// upf_printk("SDF Filter source ip values: source ip:%p and mask: %p, source port lower bound:%u, source port upper bound:%u, and protocol: %u",
// &sdf->src_addr.ip, &sdf->src_addr.mask, sdf->src_port.lower_bound, sdf->src_port.upper_bound, sdf->protocol);
// upf_printk("SDF Filter destination values: destination ip:%p and mask: %p, destination port lower bound:%u, destination port upper bound:%u",
// &sdf->dst_addr.ip, &sdf->dst_addr.mask, sdf->dst_port.lower_bound, sdf->dst_port.upper_bound);

if (sdf->protocol != packet_protocol ||
(packet_src_ip_128 & sdf->src_addr.mask) != sdf->src_addr.ip ||
(packet_dst_ip_128 & sdf->dst_addr.mask) != sdf->dst_addr.ip ||
packet_src_port < sdf->src_port.lower_bound || packet_src_port > sdf->src_port.upper_bound ||
packet_dst_port < sdf->dst_port.lower_bound || packet_dst_port > sdf->dst_port.upper_bound) {
return 0;
}

upf_printk("Packet with source ip:%pI6, destination ip:%pI6 matches SDF filter",
&ip6->saddr, &ip6->daddr);

return 1;
}

static __always_inline __u16 handle_n6_packet_ipv4(struct packet_context *ctx) {
const struct iphdr *ip4 = ctx->ip4;
struct pdr_info *pdr = bpf_map_lookup_elem(&pdr_map_downlink_ip4, &ip4->daddr);
if (!pdr) {
upf_printk("upf: no downlink session for ip:%pI4", &ip4->daddr);
return DEFAULT_XDP_ACTION;
}

struct far_info *far = bpf_map_lookup_elem(&far_map, &pdr->far_id);
struct sdf_filter *sdf = &pdr->sdf_rules.sdf_filter;
__u32 far_id = pdr->far_id;
__u32 qer_id = pdr->qer_id;
__u8 outer_header_removal = pdr->outer_header_removal;
if (sdf && (sdf->protocol >= 0 && sdf->protocol <= 3)) {
__u8 packet_matched = match_sdf_filter_ipv4(ctx, &pdr->sdf_rules.sdf_filter);
if(packet_matched) {
upf_printk("Packet with source ip:%pI4 and destination ip:%pI4 matches SDF filter", &ip4->saddr, &ip4->daddr);
far_id = pdr->sdf_rules.far_id;
qer_id = pdr->sdf_rules.qer_id;
outer_header_removal = pdr->sdf_rules.outer_header_removal;
}
}

struct far_info *far = bpf_map_lookup_elem(&far_map, &far_id);
if (!far) {
upf_printk("upf: no downlink session far for ip:%pI4 far:%d", &ip4->daddr, pdr->far_id);
upf_printk("upf: no downlink session far for ip:%pI4 far:%d", &ip4->daddr, far_id);
return XDP_DROP;
}

upf_printk("upf: downlink session for ip:%pI4 far:%d action:%d", &ip4->daddr, pdr->far_id, far->action);
upf_printk("upf: downlink session for ip:%pI4 far:%d action:%d", &ip4->daddr, far_id, far->action);

// Only forwarding action is supported at the moment
if (!(far->action & FAR_FORW))
Expand All @@ -76,13 +175,13 @@ static __always_inline enum xdp_action handle_n6_packet_ipv4(struct packet_conte
if (!(far->outer_header_creation & OHC_GTP_U_UDP_IPv4))
return XDP_DROP;

struct qer_info *qer = bpf_map_lookup_elem(&qer_map, &pdr->qer_id);
struct qer_info *qer = bpf_map_lookup_elem(&qer_map, &qer_id);
if (!qer) {
upf_printk("upf: no downlink session qer for ip:%pI4 qer:%d", &ip4->daddr, pdr->qer_id);
upf_printk("upf: no downlink session qer for ip:%pI4 qer:%d", &ip4->daddr, qer_id);
return XDP_DROP;
}

upf_printk("upf: qer:%d gate_status:%d mbr:%d", pdr->qer_id, qer->dl_gate_status, qer->dl_maximum_bitrate);
upf_printk("upf: qer:%d gate_status:%d mbr:%d", qer_id, qer->dl_gate_status, qer->dl_maximum_bitrate);

if (qer->dl_gate_status != GATE_STATUS_OPEN)
return XDP_DROP;
Expand All @@ -105,13 +204,27 @@ static __always_inline enum xdp_action handle_n6_packet_ipv6(struct packet_conte
return DEFAULT_XDP_ACTION;
}

struct far_info *far = bpf_map_lookup_elem(&far_map, &pdr->far_id);
struct sdf_filter *sdf = &pdr->sdf_rules.sdf_filter;
__u32 far_id = pdr->far_id;
__u32 qer_id = pdr->qer_id;
__u8 outer_header_removal = pdr->outer_header_removal;
if (sdf && (sdf->protocol >= 0 && sdf->protocol <= 3)) {
__u8 packet_matched = match_sdf_filter_ipv6(ctx, &pdr->sdf_rules.sdf_filter);
if(packet_matched) {
upf_printk("Packet with source ip:%pI6 and destination ip:%pI6 matches SDF filter", &ip6->saddr, &ip6->daddr);
far_id = pdr->sdf_rules.far_id;
qer_id = pdr->sdf_rules.qer_id;
outer_header_removal = pdr->sdf_rules.outer_header_removal;
}
}

struct far_info *far = bpf_map_lookup_elem(&far_map, &far_id);
if (!far) {
upf_printk("upf: no downlink session far for ip:%pI6c far:%d", &ip6->daddr, pdr->far_id);
upf_printk("upf: no downlink session far for ip:%pI6c far:%d", &ip6->daddr, far_id);
return XDP_DROP;
}

upf_printk("upf: downlink session for ip:%pI6c far:%d action:%d", &ip6->daddr, pdr->far_id, far->action);
upf_printk("upf: downlink session for ip:%pI6c far:%d action:%d", &ip6->daddr, far_id, far->action);

// Only forwarding action supported at the moment
if (!(far->action & FAR_FORW))
Expand All @@ -121,13 +234,13 @@ static __always_inline enum xdp_action handle_n6_packet_ipv6(struct packet_conte
if (!(far->outer_header_creation & OHC_GTP_U_UDP_IPv4))
return XDP_DROP;

struct qer_info *qer = bpf_map_lookup_elem(&qer_map, &pdr->qer_id);
struct qer_info *qer = bpf_map_lookup_elem(&qer_map, &qer_id);
if (!qer) {
upf_printk("upf: no downlink session qer for ip:%pI6c qer:%d", &ip6->daddr, pdr->qer_id);
upf_printk("upf: no downlink session qer for ip:%pI6c qer:%d", &ip6->daddr, qer_id);
return XDP_DROP;
}

upf_printk("upf: qer:%d gate_status:%d mbr:%d", pdr->qer_id, qer->dl_gate_status, qer->dl_maximum_bitrate);
upf_printk("upf: qer:%d gate_status:%d mbr:%d", qer_id, qer->dl_gate_status, qer->dl_maximum_bitrate);

if (qer->dl_gate_status != GATE_STATUS_OPEN)
return XDP_DROP;
Expand Down
37 changes: 37 additions & 0 deletions cmd/ebpf/xdp/pdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,47 @@ enum outer_header_removal_values {
OHR_S_TAG_C_TAG = 8,
};

// Possible optimizations:
// 0. Store SDFs in a separate map. PDR will have only id of corresponding SDF.
// 1. Combine SrcAddress.Type and DstAddress.Type into one __u8 field. Then to retrieve and put data will be used operators & and | .
// 2. Put all fields into one big structure. Sort in specific order to reduce paddings inside structure.

struct ip_w_mask {
__u8 type; // 0: any, 1: ip4, 2: ip6
// If type != any, ip field has meaningful value.
// If IPv4 -> lower 32 bits. If IPv6 -> all 128 bits.
__uint128_t ip;
// If type != any, mask field has meaningful value.
// If IPv4 mask -> lower 32 bits. If IPv6 mask -> all 128 bits.
// Should always be applied to matching ip (except type == any).
__uint128_t mask;
};

struct port_range {
__u16 lower_bound; // If not specified in SDF: 0
__u16 upper_bound; // If not specified in SDF: 65535
};

struct sdf_filter {
__u8 protocol; // Required by SDF. 0: icmp, 1: ip, 2: tcp, 3: udp
struct ip_w_mask src_addr;
struct port_range src_port;
struct ip_w_mask dst_addr;
struct port_range dst_port;
};

struct sdf_rules {
struct sdf_filter sdf_filter;
__u8 outer_header_removal;
__u32 far_id;
__u32 qer_id;
};

struct pdr_info {
__u8 outer_header_removal;
__u32 far_id;
__u32 qer_id;
struct sdf_rules sdf_rules;
};

/* ipv4 -> PDR */
Expand Down
3 changes: 2 additions & 1 deletion cmd/ebpf/xdp/utils/packet_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include <linux/ipv6.h>
#include <linux/types.h>
#include <linux/udp.h>

#include <netinet/tcp.h>
#include "xdp/utils/gtpu.h"

/* Header cursor to keep track of current parsing position */
Expand All @@ -35,5 +35,6 @@ struct packet_context {
struct iphdr *ip4;
struct ipv6hdr *ip6;
struct udphdr *udp;
struct tcphdr *tcp;
struct gtpuhdr *gtp;
};
25 changes: 25 additions & 0 deletions cmd/ebpf/xdp/utils/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/udp.h>
#include <netinet/tcp.h>

#include "xdp/utils/packet_context.h"
#include "xdp/utils/trace.h"
Expand Down Expand Up @@ -77,6 +78,30 @@ static __always_inline int parse_udp(struct packet_context *ctx) {
return bpf_ntohs(udp->dest);
}

static __always_inline struct udphdr *parse_udp_src_dst(struct packet_context *ctx) {
struct udphdr *udp = (struct udphdr *)ctx->data;
if ((const char *)(udp + 1) > ctx->data_end)
return NULL;

ctx->data += sizeof(*udp);
ctx->udp = udp;
return udp;
}

static __always_inline struct tcphdr *parse_tcp_src_dst(struct packet_context *ctx) {
struct tcphdr *tcp = (struct tcphdr *)ctx->data;
if ((const char *)(tcp + 1) > ctx->data_end)
return NULL;

ctx->data += sizeof(*tcp);
ctx->tcp = tcp;
return tcp;
}





static __always_inline void swap_mac(struct ethhdr *eth) {
__u8 mac[6];
__builtin_memcpy(mac, eth->h_source, sizeof(mac));
Expand Down
1 change: 0 additions & 1 deletion cmd/ebpf/xdp/utils/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ static __always_inline enum xdp_action do_route_ipv4(struct xdp_md *ctx, struct

if (ifindex == ctx->ingress_ifindex)
return XDP_TX;

return bpf_redirect(ifindex, 0);
}

Expand Down