-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ebpf] - Send tcp rst when a backend is deleted #11762
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
Changes from all commits
efb04d1
8102a39
dea4c9f
74200f8
ddaaa73
df251f6
4e9f924
94cd0ba
e2416f2
dec255a
6410df5
adf4c9c
2ba1221
46c8a8d
caa1335
cf5173c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Project Calico BPF dataplane programs. | ||
| // Copyright (c) 2020-2026 Tigera, Inc. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later | ||
|
|
||
| #ifndef __CALI_TCP4_H__ | ||
| #define __CALI_TCP4_H__ | ||
|
|
||
| #include <linux/if_ether.h> | ||
| #include <linux/ip.h> | ||
|
|
||
| #include "bpf.h" | ||
| #include "log.h" | ||
| #include "skb.h" | ||
|
|
||
| static CALI_BPF_INLINE int tcp_v4_rst(struct cali_tc_ctx *ctx) { | ||
| if (skb_refresh_validate_ptrs(ctx, TCP_SIZE)) { | ||
| deny_reason(ctx, CALI_REASON_SHORT); | ||
| CALI_DEBUG("TCP reset : too short"); | ||
| return -1; | ||
| } | ||
| struct iphdr ip_orig = *ip_hdr(ctx); | ||
| struct tcphdr th_orig = *tcp_hdr(ctx); | ||
| int original_len = ctx->skb->len; | ||
|
|
||
| /* Trim to minimum size */ | ||
| __u32 len = skb_iphdr_offset(ctx) + IP_SIZE + TCP_SIZE /* max IP len */; | ||
| int err = bpf_skb_change_tail(ctx->skb, len, 0); | ||
| if (err) { | ||
| CALI_DEBUG("tcp reset reply: bpf_skb_change_tail (len=%d) failed (err=%d)", len, err); | ||
| return -1; | ||
| } | ||
|
|
||
| /* Revalidate all pointers */ | ||
| if (skb_refresh_validate_ptrs(ctx, TCP_SIZE)) { | ||
| deny_reason(ctx, CALI_REASON_SHORT); | ||
| CALI_DEBUG("TCP reset : too short"); | ||
| return -1; | ||
| } | ||
| ip_hdr(ctx)->version = 4; | ||
| ip_hdr(ctx)->ihl = 5; | ||
| ip_hdr(ctx)->tos = 0; | ||
| ip_hdr(ctx)->ttl = 64; | ||
| ip_hdr(ctx)->protocol = IPPROTO_TCP; | ||
| ip_hdr(ctx)->saddr = ip_orig.daddr; | ||
| ip_hdr(ctx)->daddr = ip_orig.saddr; | ||
| ip_hdr(ctx)->check = 0; | ||
| ip_hdr(ctx)->tot_len = bpf_htons(len - (CALI_F_L3_DEV ? 0 : ETH_SIZE)); | ||
| ctx->ipheader_len = 20; | ||
|
|
||
| struct tcphdr *th = ((void *)ip_hdr(ctx)) + IP_SIZE; | ||
| __builtin_memset(th, 0, sizeof(struct tcphdr)); | ||
| th->source = th_orig.dest; | ||
| th->dest = th_orig.source; | ||
| th->rst = 1; | ||
| th->doff = sizeof(struct tcphdr) / 4; | ||
| th->seq = 0; | ||
|
|
||
| if (th_orig.ack) { | ||
| th->seq = th_orig.ack_seq; | ||
| } else { | ||
| th->ack_seq = bpf_htonl(bpf_ntohl(th_orig.seq) + th_orig.syn + th_orig.fin + | ||
| original_len - (th_orig.doff << 2)); | ||
| th->ack = 1; | ||
| } | ||
|
sridhartigera marked this conversation as resolved.
|
||
| th->check = 0; | ||
|
|
||
| __wsum ip_csum = bpf_csum_diff(0, 0, ctx->ip_header, sizeof(struct iphdr), 0); | ||
| __wsum tcp_csum = bpf_csum_diff(0, 0, (__u32 *)th, len - sizeof(struct iphdr) - skb_iphdr_offset(ctx), 0); | ||
| if (bpf_l3_csum_replace(ctx->skb, | ||
| skb_iphdr_offset(ctx) + offsetof(struct iphdr, check), 0, ip_csum, 0)) { | ||
| CALI_DEBUG("TCP reset v4 reply: set ip csum failed"); | ||
| return -1; | ||
| } | ||
|
|
||
|
sridhartigera marked this conversation as resolved.
|
||
| err = bpf_l4_csum_replace(ctx->skb, skb_l4hdr_offset(ctx) + | ||
| offsetof(struct tcphdr, check), 0, tcp_csum, BPF_F_PSEUDO_HDR); | ||
| if (err) { | ||
| CALI_DEBUG("TCP reset v4 reply: set tcp csum failed %d", err); | ||
| return -1; | ||
| } | ||
|
sridhartigera marked this conversation as resolved.
|
||
| return 0; | ||
| } | ||
|
|
||
| #endif /* __CALI_TCP4_H__ */ | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Project Calico BPF dataplane programs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Copyright (c) 2020-2026 Tigera, Inc. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #ifndef __CALI_TCP6_H__ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #define __CALI_TCP6_H__ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <linux/if_ether.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <linux/ip.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "bpf.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "log.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "skb.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static CALI_BPF_INLINE int tcp_v6_rst(struct cali_tc_ctx *ctx) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (skb_refresh_validate_ptrs(ctx, TCP_SIZE)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deny_reason(ctx, CALI_REASON_SHORT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CALI_DEBUG("TCP reset : too short"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipv6_addr_t orig_src, orig_dst; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipv6hdr_ip_to_ipv6_addr_t(&orig_src, &ip_hdr(ctx)->saddr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipv6hdr_ip_to_ipv6_addr_t(&orig_dst, &ip_hdr(ctx)->daddr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct tcphdr th_orig = *tcp_hdr(ctx); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int original_len = ctx->skb->len; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Trim to minimum size */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __u32 len = skb_iphdr_offset(ctx) + IP_SIZE + TCP_SIZE /* max IP len */; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int err = bpf_skb_change_tail(ctx->skb, len, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CALI_DEBUG("tcp reset reply: bpf_skb_change_tail (len=%d) failed (err=%d)", len, err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Revalidate all pointers */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (skb_refresh_validate_ptrs(ctx, TCP_SIZE)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deny_reason(ctx, CALI_REASON_SHORT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CALI_DEBUG("TCP reset : too short"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ip_hdr(ctx)->version = 6; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ip_hdr(ctx)->hop_limit = 255; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ip_hdr(ctx)->nexthdr = IPPROTO_TCP; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipv6_addr_t_to_ipv6hdr_ip(&ip_hdr(ctx)->daddr, &orig_src); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipv6_addr_t_to_ipv6hdr_ip(&ip_hdr(ctx)->saddr, &orig_dst); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ip_hdr(ctx)->payload_len = bpf_htons(TCP_SIZE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx->ipheader_len = IP_SIZE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct tcphdr *th = ((void *)ip_hdr(ctx)) + IP_SIZE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __builtin_memset(th, 0, TCP_SIZE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->source = th_orig.dest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->dest = th_orig.source; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->rst = 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->doff = sizeof(struct tcphdr) / 4; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->seq = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (th_orig.ack) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->seq = th_orig.ack_seq; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->ack_seq = bpf_htonl(bpf_ntohl(th_orig.seq) + th_orig.syn + th_orig.fin + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| original_len - (th_orig.doff << 2)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+54
to
+62
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| th->rst = 1; | |
| th->doff = sizeof(struct tcphdr) / 4; | |
| th->seq = 0; | |
| if (th_orig.ack) { | |
| th->seq = th_orig.ack_seq; | |
| } else { | |
| th->ack_seq = bpf_htonl(bpf_ntohl(th_orig.seq) + th_orig.syn + th_orig.fin + | |
| original_len - (th_orig.doff << 2)); | |
| /* Initialize all TCP flags and related fields to known values. */ | |
| th->fin = 0; | |
| th->syn = 0; | |
| th->rst = 0; | |
| th->psh = 0; | |
| th->ack = 0; | |
| th->urg = 0; | |
| th->ece = 0; | |
| th->cwr = 0; | |
| th->res1 = 0; | |
| th->res2 = 0; | |
| th->window = 0; | |
| th->urg_ptr = 0; | |
| th->seq = 0; | |
| th->ack_seq = 0; | |
| th->rst = 1; | |
| th->doff = sizeof(struct tcphdr) / 4; | |
| if (th_orig.ack) { | |
| th->seq = th_orig.ack_seq; | |
| } else { | |
| th->ack_seq = bpf_htonl(bpf_ntohl(th_orig.seq) + th_orig.syn + th_orig.fin + | |
| original_len - (th_orig.doff << 2)); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // Project Calico BPF dataplane programs. | ||
| // Copyright (c) 2020-2026 Tigera, Inc. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later | ||
|
|
||
| #include "ut.h" | ||
| #include "bpf.h" | ||
| #include "nat.h" | ||
| #ifndef IPVER6 | ||
| #include "tcp4.h" | ||
| #else | ||
| #include "tcp6.h" | ||
| #endif | ||
| #include "parsing.h" | ||
| #include "jump.h" | ||
|
|
||
| const volatile struct cali_tc_preamble_globals __globals; | ||
|
|
||
| static CALI_BPF_INLINE int calico_unittest_entry (struct __sk_buff *skb) | ||
| { | ||
| volatile struct cali_tc_globals *globals = state_get_globals_tc(); | ||
|
|
||
| if (!globals) { | ||
| return TC_ACT_SHOT; | ||
| } | ||
|
|
||
| /* Set the globals for the rest of the prog chain. */ | ||
| #ifndef IPVER6 | ||
| globals->data = __globals.v4; | ||
| #else | ||
| globals->data = __globals.v6; | ||
| #endif | ||
| DECLARE_TC_CTX(_ctx, | ||
| .skb = skb, | ||
| .ipheader_len = IP_SIZE, | ||
| ); | ||
| struct cali_tc_ctx *ctx = &_ctx; | ||
| if (!ctx->counters) { | ||
| CALI_DEBUG("Counters map lookup failed: DROP\n"); | ||
| return TC_ACT_SHOT; | ||
| } | ||
| int ret = PARSING_OK; | ||
| #ifdef IPVER6 | ||
| ret = PARSING_OK_V6; | ||
| #endif | ||
| if (parse_packet_ip(ctx) != ret) { | ||
| return TC_ACT_UNSPEC; | ||
| } | ||
|
|
||
| tc_state_fill_from_iphdr(ctx); | ||
|
|
||
| switch (tc_state_fill_from_nexthdr(ctx, true)) { | ||
| case PARSING_ERROR: | ||
| goto deny; | ||
| case PARSING_ALLOW_WITHOUT_ENFORCING_POLICY: | ||
| goto allow; | ||
| } | ||
| #ifndef IPVER6 | ||
| return tcp_v4_rst(ctx); | ||
| #else | ||
| return tcp_v6_rst(ctx); | ||
| #endif | ||
|
|
||
| allow: | ||
| return TC_ACT_UNSPEC; | ||
|
|
||
| deny: | ||
| return TC_ACT_SHOT; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we remove this flag and set conntrack to rstseen? If for any reason it would be detected like a spurious RST, and there would be more traffic it would get reset. Idk if I am not overthinking. This would also send the the RST for any follow up packet, guess we may want it in case of RST packet drop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I guess we should only send the packet to the client. Wht if we see a packet from the server - the now removed endpoint? I guess sending RST does not hurt.
Iirc we discussed that the same connection cannot be reused fast enough to cause a reasonable race, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyways this is to a workload which doesn't exist, so even if there is a spurious RST, its good to reset the connection. I think there cannot be a RST from the server, unless the same connection is reused. Am I missing something?