static __always_inline __u16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
__u32 len, __u8 proto,
__u32 csum)
{
__u64 s = csum;
s += (__u32)saddr;
s += (__u32)daddr; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
s += proto + len; #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
s += (proto + len) << 8; #else #error Unknown endian #endif
s = (s & 0xffffffff) + (s >> 32);
s = (s & 0xffffffff) + (s >> 32);
return csum_fold((__u32)s);
}
static __always_inline __u16 csum_ipv6_magic(conststruct in6_addr *saddr, conststruct in6_addr *daddr,
__u32 len, __u8 proto, __u32 csum)
{
__u64 sum = csum; int i;
__pragma_loop_unroll for (i = 0; i < 4; i++)
sum += (__u32)saddr->in6_u.u6_addr32[i];
__pragma_loop_unroll for (i = 0; i < 4; i++)
sum += (__u32)daddr->in6_u.u6_addr32[i];
/* Don't combine additions to avoid 32-bit overflow. */
sum += bpf_htonl(len);
sum += bpf_htonl(proto);
sum = (sum & 0xffffffff) + (sum >> 32);
sum = (sum & 0xffffffff) + (sum >> 32);
for (i = 0; i < MAX_ALLOWED_PORTS; i++) {
__u32 key = i;
__u16 *value;
value = bpf_map_lookup_elem(&allowed_ports, &key);
if (!value) break; /* 0 is a terminator value. Check it first to avoid matching on * a forbidden port == 0 and returning true.
*/ if (*value == 0) break;
hdr->ipv6 = (void *)hdr->eth + sizeof(*hdr->eth); if (hdr->ipv6 + 1 > data_end) return XDP_DROP; if (hdr->ipv6->version != 6) return XDP_DROP;
/* XXX: Extension headers are not supported and could circumvent * XDP SYN flood protection.
*/ if (hdr->ipv6->nexthdr != NEXTHDR_TCP) return XDP_PASS;
static __always_inline int syncookie_handle_syn(struct header_pointers *hdr, void *ctx, void *data, void *data_end, bool xdp)
{
__u32 old_pkt_size, new_pkt_size; /* Unlike clang 10, clang 11 and 12 generate code that doesn't pass the * BPF verifier if tsopt is not volatile. Volatile forces it to store * the pointer value and use it directly, otherwise tcp_mkoptions is * (mis)compiled like this: * if (!tsopt) * return buf - start; * reg = stored_return_value_of_tscookie_init; * if (reg) * tsopt = tsopt_buf; * else * tsopt = NULL; * ... * *buf++ = tsopt[1]; * It creates a dead branch where tsopt is assigned NULL, but the * verifier can't prove it's dead and blocks the program.
*/
__be32 * volatile tsopt = NULL;
__be32 tsopt_buf[2] = {};
__u16 ip_len;
__u32 cookie;
__s64 value;
/* Checksum is not yet verified, but both checksum failure and TCP * header checks return XDP_DROP, so the order doesn't matter.
*/ if (hdr->tcp->fin || hdr->tcp->rst) return XDP_DROP;
/* Issue SYN cookies on allowed ports, drop SYN packets on blocked * ports.
*/ if (!check_port_allowed(bpf_ntohs(hdr->tcp->dest))) return XDP_DROP;
if (hdr->ipv4) { /* Check the IPv4 and TCP checksums before creating a SYNACK. */
value = bpf_csum_diff(0, 0, (void *)hdr->ipv4, hdr->ipv4->ihl * 4, 0); if (value < 0) return XDP_ABORTED; if (csum_fold(value) != 0) return XDP_DROP; /* Bad IPv4 checksum. */
value = bpf_csum_diff(0, 0, (void *)hdr->tcp, hdr->tcp_len, 0); if (value < 0) return XDP_ABORTED; if (csum_tcpudp_magic(hdr->ipv4->saddr, hdr->ipv4->daddr,
hdr->tcp_len, IPPROTO_TCP, value) != 0) return XDP_DROP; /* Bad TCP checksum. */
ip_len = sizeof(*hdr->ipv4);
value = bpf_tcp_raw_gen_syncookie_ipv4(hdr->ipv4, hdr->tcp,
hdr->tcp_len);
} elseif (hdr->ipv6) { /* Check the TCP checksum before creating a SYNACK. */
value = bpf_csum_diff(0, 0, (void *)hdr->tcp, hdr->tcp_len, 0); if (value < 0) return XDP_ABORTED; if (csum_ipv6_magic(&hdr->ipv6->saddr, &hdr->ipv6->daddr,
hdr->tcp_len, IPPROTO_TCP, value) != 0) return XDP_DROP; /* Bad TCP checksum. */
/* Check that there is enough space for a SYNACK. It also covers * the check that the destination of the __builtin_memmove below * doesn't overflow.
*/ if (data + sizeof(*hdr->eth) + ip_len + TCP_MAXLEN > data_end) return XDP_ABORTED;
if (hdr->ipv4) { if (hdr->ipv4->ihl * 4 > sizeof(*hdr->ipv4)) { struct tcphdr *new_tcp_header;
/* Set the new packet size. */
old_pkt_size = data_end - data;
new_pkt_size = sizeof(*hdr->eth) + ip_len + hdr->tcp->doff * 4; if (xdp) { if (bpf_xdp_adjust_tail(ctx, new_pkt_size - old_pkt_size)) return XDP_ABORTED;
} else { if (bpf_skb_change_tail(ctx, new_pkt_size, 0)) return XDP_ABORTED;
}
values_inc_synacks();
return XDP_TX;
}
static __always_inline int syncookie_handle_ack(struct header_pointers *hdr)
{ int err;
if (hdr->tcp->rst) return XDP_DROP;
if (hdr->ipv4)
err = bpf_tcp_raw_check_syncookie_ipv4(hdr->ipv4, hdr->tcp); elseif (hdr->ipv6)
err = bpf_tcp_raw_check_syncookie_ipv6(hdr->ipv6, hdr->tcp); else return XDP_ABORTED; if (err) return XDP_DROP;
return XDP_PASS;
}
static __always_inline int syncookie_part1(void *ctx, void *data, void *data_end, struct header_pointers *hdr, bool xdp)
{ int ret;
ret = tcp_dissect(data, data_end, hdr); if (ret != XDP_TX) return ret;
ret = tcp_lookup(ctx, hdr, xdp); if (ret != XDP_TX) return ret;
/* Packet is TCP and doesn't belong to an established connection. */
if ((hdr->tcp->syn ^ hdr->tcp->ack) != 1) return XDP_DROP;
/* Grow the TCP header to TCP_MAXLEN to be able to pass any hdr->tcp_len * to bpf_tcp_raw_gen_syncookie_ipv{4,6} and pass the verifier.
*/ if (xdp) { if (bpf_xdp_adjust_tail(ctx, TCP_MAXLEN - hdr->tcp_len)) return XDP_ABORTED;
} else { /* Without volatile the verifier throws this error: * R9 32-bit pointer arithmetic prohibited
*/ volatile u64 old_len = data_end - data;
static __always_inline int syncookie_part2(void *ctx, void *data, void *data_end, struct header_pointers *hdr, bool xdp)
{ if (hdr->ipv4) {
hdr->eth = data;
hdr->ipv4 = (void *)hdr->eth + sizeof(*hdr->eth); /* IPV4_MAXLEN is needed when calculating checksum. * At least sizeof(struct iphdr) is needed here to access ihl.
*/ if ((void *)hdr->ipv4 + IPV4_MAXLEN > data_end) return XDP_ABORTED;
hdr->tcp = (void *)hdr->ipv4 + hdr->ipv4->ihl * 4;
} elseif (hdr->ipv6) {
hdr->eth = data;
hdr->ipv6 = (void *)hdr->eth + sizeof(*hdr->eth);
hdr->tcp = (void *)hdr->ipv6 + sizeof(*hdr->ipv6);
} else { return XDP_ABORTED;
}
if ((void *)hdr->tcp + TCP_MAXLEN > data_end) return XDP_ABORTED;
/* We run out of registers, tcp_len gets spilled to the stack, and the * verifier forgets its min and max values checked above in tcp_dissect.
*/
hdr->tcp_len = hdr->tcp->doff * 4; if (hdr->tcp_len < sizeof(*hdr->tcp)) return XDP_ABORTED;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.