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

patch: rewrite bpf spec before loading to avoid bpf map lookup during runtime #376

Merged
merged 2 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 24 additions & 3 deletions control/bpf_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,20 @@ func (p bpfIfParams) CheckVersionRequirement(version *internal.Version) (err err
}

type loadBpfOptions struct {
PinPath string
CollectionOptions *ebpf.CollectionOptions
PinPath string
BigEndianTproxyPort uint32
CollectionOptions *ebpf.CollectionOptions
}

func loadBpfObjectsWithConstants(obj interface{}, opts *ebpf.CollectionOptions, constants map[string]interface{}) error {
spec, err := loadBpf()
if err != nil {
return err
}
if err := spec.RewriteConstants(constants); err != nil {
return err
}
return spec.LoadAndAssign(obj, opts)
}

func fullLoadBpfObjects(
Expand All @@ -205,7 +217,16 @@ func fullLoadBpfObjects(
opts *loadBpfOptions,
) (err error) {
retryLoadBpf:
if err = loadBpfObjects(bpf, opts.CollectionOptions); err != nil {
constants := map[string]interface{}{
"PARAM": struct {
tproxyPort uint32
controlPlanePid uint32
}{
tproxyPort: uint32(opts.BigEndianTproxyPort),
controlPlanePid: uint32(os.Getpid()),
},
}
if err = loadBpfObjectsWithConstants(bpf, opts.CollectionOptions, constants); err != nil {
if errors.Is(err, ebpf.ErrMapIncompatible) {
// Map property is incompatible. Remove the old map and try again.
prefix := "use pinned map "
Expand Down
14 changes: 3 additions & 11 deletions control/control_plane.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ func NewControlPlane(
} else {
bpf = new(bpfObjects)
if err = fullLoadBpfObjects(log, bpf, &loadBpfOptions{
PinPath: pinPath,
CollectionOptions: collectionOpts,
PinPath: pinPath,
BigEndianTproxyPort: uint32(common.Htons(global.TproxyPort)),
CollectionOptions: collectionOpts,
}); err != nil {
if log.Level == logrus.PanicLevel {
log.Panicln(err)
Expand All @@ -186,11 +187,6 @@ func NewControlPlane(
}
}()

// Write params.
if err = core.bpf.ParamMap.Update(consts.ControlPlanePidKey, uint32(os.Getpid()), ebpf.UpdateAny); err != nil {
return nil, err
}

/// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection.
// Bind to LAN
if len(global.LanInterface) > 0 {
Expand Down Expand Up @@ -687,10 +683,6 @@ func (c *ControlPlane) Serve(readyChan chan<- bool, listener *Listener) (err err
if err := c.core.bpf.ListenSocketMap.Update(consts.OneKey, uint64(udpFile.Fd()), ebpf.UpdateAny); err != nil {
return err
}
// Port.
if err := c.core.bpf.ParamMap.Update(consts.BigEndianTproxyPortKey, uint32(common.Htons(listener.port)), ebpf.UpdateAny); err != nil {
return err
}

sentReady = true
readyChan <- true
Expand Down
49 changes: 18 additions & 31 deletions control/kern/tproxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,7 @@ enum {

// Param keys:
static const __u32 zero_key = 0;
static const __u32 tproxy_port_key = 1;
static const __u32 one_key = 1;
static const __u32 disable_l4_tx_checksum_key
__attribute__((unused, deprecated)) = 2;
static const __u32 disable_l4_rx_checksum_key
__attribute__((unused, deprecated)) = 3;
static const __u32 control_plane_pid_key = 4;
static const __u32 control_plane_nat_direct_key
__attribute__((unused, deprecated)) = 5;
static const __u32 control_plane_dns_routing_key
__attribute__((unused, deprecated)) = 6;

// Outbound Connectivity Map:

Expand Down Expand Up @@ -163,6 +153,13 @@ struct tuples {
__u8 dscp;
};

struct dae_param {
__u32 tproxy_port;
__u32 control_plane_pid;
};

static volatile const struct dae_param PARAM = {};

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key,
Expand Down Expand Up @@ -196,15 +193,6 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} routing_tuples_map SEC(".maps");

// Params:
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, MAX_PARAM_LEN);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} param_map SEC(".maps");

// Link to type:
#define LinkType_None 0
#define LinkType_Ethernet 1
Expand Down Expand Up @@ -1279,13 +1267,13 @@ int tproxy_lan_egress(struct __sk_buff *skb) {
return TC_ACT_PIPE;
}

__be16 *tproxy_port = bpf_map_lookup_elem(&param_map, &tproxy_port_key);
__be16 tproxy_port = PARAM.tproxy_port;
if (!tproxy_port) {
return TC_ACT_PIPE;
}
struct tuples tuples;
get_tuples(skb, &tuples, &iph, &ipv6h, &tcph, &udph, l4proto);
if (*tproxy_port != tuples.five.sport) {
if (tproxy_port != tuples.five.sport) {
return TC_ACT_PIPE;
}

Expand Down Expand Up @@ -1540,13 +1528,12 @@ static __always_inline bool pid_is_control_plane(struct __sk_buff *skb,
*p = pid_pname;
}
// Get tproxy pid and compare if they are equal.
__u32 *pid_tproxy;
if (!(pid_tproxy =
bpf_map_lookup_elem(&param_map, &control_plane_pid_key))) {
__u32 pid_tproxy;
if (!(pid_tproxy = PARAM.control_plane_pid)) {
bpf_printk("control_plane_pid is not set.");
return false;
}
return pid_pname->pid == *pid_tproxy;
return pid_pname->pid == pid_tproxy;
} else {
if (p) {
*p = NULL;
Expand Down Expand Up @@ -1621,11 +1608,11 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
// We should know if this packet is from tproxy.
// We do not need to check the source ip because we have skipped packets not
// from localhost.
__be16 *tproxy_port = bpf_map_lookup_elem(&param_map, &tproxy_port_key);
__be16 tproxy_port = PARAM.tproxy_port;
if (!tproxy_port) {
return TC_ACT_OK;
}
bool tproxy_response = *tproxy_port == tuples.five.sport;
bool tproxy_response = tproxy_port == tuples.five.sport;
// Double check to avoid conflicts when binding wan and lan to the same
// interface.
if (tproxy_response && l4proto == IPPROTO_TCP) {
Expand Down Expand Up @@ -1965,11 +1952,11 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
__u16 tproxy_typ = bpf_ntohs(*(__u16 *)&ethh.h_source[4]);
if (*(__u32 *)&ethh.h_source[0] != bpf_htonl(0x02000203) || tproxy_typ > 1) {
// Check for security. Reject packets that is UDP and sent to tproxy port.
__be16 *tproxy_port = bpf_map_lookup_elem(&param_map, &tproxy_port_key);
__be16 tproxy_port = PARAM.tproxy_port;
if (!tproxy_port) {
goto accept;
}
if (unlikely(*tproxy_port == tuples.five.dport)) {
if (unlikely(tproxy_port == tuples.five.dport)) {
struct bpf_sock_tuple tuple = {0};
__u32 tuple_size;

Expand Down Expand Up @@ -2103,7 +2090,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
// saddr should be tproxy ip.
__be32 *tproxy_ip = tuples.five.sip.u6_addr32;
// __builtin_memcpy(tproxy_ip, saddr, sizeof(tproxy_ip));
__be16 *tproxy_port = bpf_map_lookup_elem(&param_map, &tproxy_port_key);
__be16 tproxy_port = PARAM.tproxy_port;
if (!tproxy_port) {
return TC_ACT_OK;
}
Expand All @@ -2118,7 +2105,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {

// Rewrite dst port.
if ((ret = rewrite_port(skb, link_h_len, l4proto, ihl, tuples.five.dport,
*tproxy_port, true, true))) {
tproxy_port, true, true))) {
bpf_printk("Shot Port: %d", ret);
return TC_ACT_SHOT;
}
Expand Down