Skip to content

Commit

Permalink
Add TPR validation logic
Browse files Browse the repository at this point in the history
Summary:
Check base diff for context. tl;dr - Validate TPR header server-id with host's server-id. (same as xdpdecap).

Note: We need to create copies of `parse_hdr_opt` and `tcp_hdr_opt_lookup_server_id` since the existing functions accept xdp_md struct and `parse_hdr_opt` is a non-inline function called in a loop. The bpf verifier doesn't allow void* to be passed as a function param of a non-inline function and making that function inline singnificantly increases verifier time (since it is called in a loop). Check for more details: D49670259

This is still a no-op as this program isn't used yet

Reviewed By: avasylev

Differential Revision: D50141892

fbshipit-source-id: 828bb7fc82e985f6a6a7616ac976a3b6b18020de
  • Loading branch information
Nikhil Dixit Limaye authored and facebook-github-bot committed Oct 12, 2023
1 parent 33424d7 commit 745374f
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 2 deletions.
87 changes: 85 additions & 2 deletions katran/decap/tc_bpf/tc_decap_stats.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,96 @@
*/

#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <stdbool.h>
#include <stddef.h>

#include "katran/lib/linux_includes/bpf.h"
#include "katran/lib/linux_includes/bpf_helpers.h"

SEC("tc")
int tcdecapinfo(struct __sk_buff* skb) {
#include "katran/lib/bpf/balancer_consts.h"
#include "katran/lib/bpf/pckt_encap.h"
#include "katran/lib/bpf/pckt_parsing.h"

#include "pckt_helpers.h"
#include "tc_decap_stats_maps.h"

__attribute__((__always_inline__)) static inline void validate_tpr_server_id(
void* data,
__u64 off,
void* data_end,
bool is_ipv6,
struct __sk_buff* skb,
struct decap_tpr_stats* data_stats) {
__u16 inner_pkt_bytes;
struct packet_description inner_pckt = {};
if (process_l3_headers(data, data_end, off, is_ipv6, &inner_pckt.flow) >= 0) {
return;
}
if (inner_pckt.flow.proto != IPPROTO_TCP) {
return;
}
if (!parse_tcp(data, data_end, is_ipv6, &inner_pckt)) {
return;
}
// only check for TCP non SYN packets
if (!(inner_pckt.flags & F_SYN_SET)) {
// lookup server id from tpr header option and compare against server_id on
// this host (if available)
__u32 s_key = 0;
__u32* server_id_host = bpf_map_lookup_elem(&tpr_server_id, &s_key);
if (server_id_host && *server_id_host > 0) {
__u32 server_id = 0;
tcp_hdr_opt_lookup_server_id_skb(skb, is_ipv6, &server_id);
if (server_id > 0) {
data_stats->tpr_total += 1;
if (*server_id_host != server_id) {
data_stats->tpr_misrouted += 1;
}
}
}
}
}

__attribute__((__always_inline__)) static inline int process_packet(
void* data,
__u64 off,
void* data_end,
bool is_ipv6,
struct __sk_buff* skb) {
struct packet_description pckt = {};
struct decap_tpr_stats* data_stats;
__u32 key = 0;
data_stats = bpf_map_lookup_elem(&decap_tpr_counters, &key);
if (!data_stats) {
return XDP_PASS;
}
validate_tpr_server_id(data, off, data_end, is_ipv6, skb, data_stats);
return TC_ACT_UNSPEC;
}

SEC("tc")
int tcdecapstats(struct __sk_buff* skb) {
void* data = (void*)(long)skb->data;
void* data_end = (void*)(long)skb->data_end;
__u32 eth_proto;
struct ethhdr* eth = data;
__u32 nh_off = sizeof(struct ethhdr);

if (data + nh_off > data_end) {
return TC_ACT_UNSPEC;
}
eth_proto = eth->h_proto;
if (eth_proto == BE_ETH_P_IP) {
return process_packet(data, nh_off, data_end, false, skb);
} else if (eth_proto == BE_ETH_P_IPV6) {
return process_packet(data, nh_off, data_end, true, skb);
} else {
return TC_ACT_UNSPEC;
}
}

char _license[] SEC("license") = "GPL";
52 changes: 52 additions & 0 deletions katran/decap/tc_bpf/tc_decap_stats_maps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Copyright (C) 2019-present, Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef __DECAP_STATS_MAPS_H
#define __DECAP_STATS_MAPS_H

#include "katran/lib/linux_includes/bpf.h"
#include "katran/lib/linux_includes/bpf_helpers.h"

#include "katran/lib/bpf/balancer_consts.h"

#ifndef DECAP_STATS_MAP_SIZE
#define DECAP_STATS_MAP_SIZE 1
#endif

struct decap_tpr_stats {
__u64 tpr_misrouted;
__u64 tpr_total;
};

// map for tpr related counters
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, struct decap_tpr_stats);
__uint(max_entries, DECAP_STATS_MAP_SIZE);
__uint(map_flags, NO_FLAGS);
} decap_tpr_counters SEC(".maps");

// map, which contains server_id info
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, 1);
__uint(map_flags, NO_FLAGS);
} tpr_server_id SEC(".maps");

#endif // of __DECAP_STATS_MAPS_H
53 changes: 53 additions & 0 deletions katran/lib/bpf/pckt_parsing.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ int parse_hdr_opt(const struct xdp_md *xdp, struct hdr_opt_state *state)
return parse_hdr_opt_raw(data, data_end, state);
}

int parse_hdr_opt_skb(
const struct __sk_buff* skb,
struct hdr_opt_state* state) {
__u8 *tcp_opt, kind, hdr_len;

const void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
return parse_hdr_opt_raw(data, data_end, state);
}

__attribute__((__always_inline__)) static inline int
tcp_hdr_opt_lookup_server_id(
const struct xdp_md* xdp,
Expand Down Expand Up @@ -254,6 +264,49 @@ tcp_hdr_opt_lookup_server_id(
*server_id = opt_state.server_id;
return 0;
}
__attribute__((__always_inline__)) static inline int
tcp_hdr_opt_lookup_server_id_skb(
const struct __sk_buff* skb,
bool is_ipv6,
__u32* server_id) {
const void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
struct tcphdr* tcp_hdr;
__u8 tcp_hdr_opt_len = 0;
__u64 tcp_offset = 0;
struct hdr_opt_state opt_state = {};
int err = 0;

tcp_offset = calc_offset(is_ipv6, false /* is_icmp */);
tcp_hdr = (struct tcphdr*)(data + tcp_offset);
if (tcp_hdr + 1 > data_end) {
return FURTHER_PROCESSING;
}
tcp_hdr_opt_len = (tcp_hdr->doff * 4) - sizeof(struct tcphdr);
if (tcp_hdr_opt_len < TCP_HDR_OPT_LEN_TPR) {
return FURTHER_PROCESSING;
}

opt_state.hdr_bytes_remaining = tcp_hdr_opt_len;
opt_state.byte_offset = sizeof(struct tcphdr) + tcp_offset;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) || \
!defined TCP_HDR_OPT_SKIP_UNROLL_LOOP
// For linux kernel version < 5.3, there isn't support in the bpf verifier
// for validating bounded loops, so we need to unroll the loop
#pragma clang loop unroll(full)
#endif
for (int i = 0; i < TCP_HDR_OPT_MAX_OPT_CHECKS; i++) {
err = parse_hdr_opt_skb(skb, &opt_state);
if (err || !opt_state.hdr_bytes_remaining) {
break;
}
}
if (!opt_state.server_id) {
return FURTHER_PROCESSING;
}
*server_id = opt_state.server_id;
return 0;
}
#endif // TCP_SERVER_ID_ROUTING) || DECAP_TPR_STATS

#ifdef TCP_SERVER_ID_ROUTING
Expand Down

0 comments on commit 745374f

Please sign in to comment.