Skip to content

Commit abbc7b0

Browse files
bpf: icmp*: fix up sample length
For IPv4 we are supposed to sample the full IPv4 header plus the first 8 bytes of the L4 datagram. Not the first 64 bytes. For IPv6 we are meant to sample as much as fits into the MTU - but ctx_full_len() includes the packet's current L2 header. Consider this when calculating the sample size for the ICMP error msg's payload. Otherwise calling ctx_adjust_troom() for a full-length sample ends up adding an *additional* L2 header at the end of the packet. While doing so convert the ICMP-related tests over to scapy, so that we don't have to fix up the various golden checksum values. Signed-off-by: Julian Wiedmann <jwi@isovalent.com>
1 parent 8c664d0 commit abbc7b0

9 files changed

Lines changed: 254 additions & 349 deletions

bpf/lib/icmp.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
#include "overloadable.h"
1515
#ifdef ENABLE_IPV4
1616

17-
#define ICMP_PACKET_MAX_SAMPLE_SIZE 64
17+
#define ICMP_PACKET_MAX_SAMPLE_SIZE 8
1818

1919
static __always_inline
2020
int generate_icmp4_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
2121
{
22+
__u64 full_len = ctx_full_len(ctx);
23+
__u64 new_len, sample_len;
2224
void *data, *data_end;
2325
struct ethhdr *ethhdr;
2426
struct iphdr *ip4;
@@ -29,7 +31,6 @@ int generate_icmp4_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
2931
__be32 daddr;
3032
__u8 tos;
3133
__wsum csum;
32-
int sample_len;
3334
int ret;
3435
const int inner_offset = sizeof(struct ethhdr) + sizeof(struct iphdr) +
3536
sizeof(struct icmphdr);
@@ -51,17 +52,24 @@ int generate_icmp4_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
5152
daddr = ip4->daddr;
5253
tos = ip4->tos;
5354

54-
/* Resize to ethernet header + 64 bytes or less */
55-
sample_len = (int)ctx_full_len(ctx);
56-
if (sample_len > ICMP_PACKET_MAX_SAMPLE_SIZE)
57-
sample_len = ICMP_PACKET_MAX_SAMPLE_SIZE;
58-
ctx_adjust_troom(ctx, (__s32)(sample_len + sizeof(struct ethhdr) - ctx_full_len(ctx)));
55+
/* Trim down to sample size (IPv4 header + 8 bytes datagram) */
56+
if (full_len < sizeof(struct ethhdr))
57+
return DROP_INVALID;
58+
59+
sample_len = ipv4_hdrlen(ip4) + ICMP_PACKET_MAX_SAMPLE_SIZE;
60+
new_len = sizeof(struct ethhdr) + sample_len;
61+
if (new_len > full_len) {
62+
new_len = full_len;
63+
sample_len = full_len - sizeof(struct ethhdr);
64+
}
65+
66+
ctx_adjust_troom(ctx, (__s32)(new_len - full_len));
5967

6068
data = ctx_data(ctx);
6169
data_end = ctx_data_end(ctx);
6270

6371
/* Calculate the checksum of the ICMP sample */
64-
csum = icmp_wsum_accumulate(data + sizeof(struct ethhdr), data_end, sample_len);
72+
csum = icmp_wsum_accumulate(data + sizeof(struct ethhdr), data_end, (int)sample_len);
6573

6674
/* We need to insert a IPv4 and ICMP header before the original packet.
6775
* Make that room.

bpf/lib/icmp6.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ bool icmp6_ndisc_validate(struct __ctx_buff *ctx, const struct ipv6hdr *ip6,
571571
return true;
572572
}
573573

574-
#define ICMPV6_PACKET_MAX_SAMPLE_SIZE 1280 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
574+
#define ICMPV6_PACKET_MAX_SAMPLE_SIZE (IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr))
575575

576576
/* The IPv6 pseudo-header */
577577
struct ipv6_pseudo_header_t {
@@ -590,6 +590,8 @@ struct ipv6_pseudo_header_t {
590590
static __always_inline
591591
int generate_icmp6_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
592592
{
593+
__u64 full_len = ctx_full_len(ctx);
594+
__u64 new_len, sample_len;
593595
void *data, *data_end;
594596
struct ethhdr *ethhdr;
595597
struct ipv6hdr *ip6;
@@ -600,7 +602,6 @@ int generate_icmp6_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
600602
struct in6_addr saddr;
601603
struct in6_addr daddr;
602604
__wsum csum;
603-
__u64 sample_len;
604605
int i;
605606
int ret;
606607
const int inner_offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +
@@ -622,11 +623,19 @@ int generate_icmp6_reply(struct __ctx_buff *ctx, __u8 icmp_type, __u8 icmp_code)
622623
memcpy(&saddr, &ip6->saddr, sizeof(struct in6_addr));
623624
memcpy(&daddr, &ip6->daddr, sizeof(struct in6_addr));
624625

625-
/* Resize to min MTU - IPv6 hdr + ICMPv6 hdr */
626-
sample_len = ctx_full_len(ctx);
627-
if (sample_len > (__u64)ICMPV6_PACKET_MAX_SAMPLE_SIZE)
628-
sample_len = ICMPV6_PACKET_MAX_SAMPLE_SIZE;
629-
ctx_adjust_troom(ctx, (__s32)(sample_len + sizeof(struct ethhdr) - ctx_full_len(ctx)));
626+
/* Trim down to sample size */
627+
if (full_len < sizeof(struct ethhdr))
628+
return DROP_INVALID;
629+
630+
sample_len = ICMPV6_PACKET_MAX_SAMPLE_SIZE;
631+
new_len = sizeof(struct ethhdr) + sample_len;
632+
if (new_len > full_len) {
633+
new_len = full_len;
634+
sample_len = full_len - sizeof(struct ethhdr);
635+
}
636+
637+
ctx_adjust_troom(ctx, (__s32)(new_len - full_len));
638+
630639

631640
data = ctx_data(ctx);
632641
data_end = ctx_data_end(ctx);

bpf/tests/lib/icmp.h

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,8 @@
55

66
struct validate_icmpv6_reply_args {
77
const struct __ctx_buff *ctx;
8-
const __u8 *src_mac;
9-
const __u8 *dst_mac;
10-
const __u8 *src_ip;
11-
const __u8 *dst_ip;
12-
__u8 icmp_type;
13-
__u8 icmp_code;
14-
__u16 checksum;
8+
const __u8 *buf_expected;
9+
__u16 buf_len;
1510
__u32 dst_idx;
1611
__u32 retval;
1712
};
@@ -21,9 +16,6 @@ validate_icmpv6_reply(const struct validate_icmpv6_reply_args *args)
2116
{
2217
void *data, *data_end;
2318
__u32 *status_code;
24-
struct ethhdr *l2;
25-
struct ipv6hdr *l3;
26-
struct icmp6hdr *l4;
2719
struct ratelimit_value *value;
2820

2921
test_init();
@@ -39,33 +31,12 @@ validate_icmpv6_reply(const struct validate_icmpv6_reply_args *args)
3931
test_log("Status code: %d", *status_code);
4032
assert(*status_code == args->retval);
4133

42-
l2 = data + sizeof(__u32);
43-
if ((void *)l2 + sizeof(struct ethhdr) > data_end)
44-
test_fatal("l2 header out of bounds");
45-
46-
assert(memcmp(l2->h_dest, args->dst_mac, ETH_ALEN) == 0);
47-
assert(memcmp(l2->h_source, args->src_mac, ETH_ALEN) == 0);
48-
assert(l2->h_proto == __bpf_htons(ETH_P_IPV6));
49-
50-
l3 = data + sizeof(__u32) + sizeof(struct ethhdr);
51-
if ((void *)l3 + sizeof(struct ipv6hdr) > data_end)
52-
test_fatal("l3 header out of bounds");
53-
54-
assert(!memcmp(&l3->saddr, args->src_ip, sizeof(l3->saddr)));
55-
assert(!memcmp(&l3->daddr, args->dst_ip, sizeof(l3->daddr)));
56-
57-
assert(l3->hop_limit == 64);
58-
assert(l3->version == 6);
59-
assert(l3->nexthdr == IPPROTO_ICMPV6);
60-
61-
l4 = data + sizeof(__u32) + sizeof(struct ethhdr) +
62-
sizeof(struct ipv6hdr);
63-
if ((void *)l4 + sizeof(struct icmp6hdr) > data_end)
64-
test_fatal("l4 header out of bounds");
65-
66-
assert(l4->icmp6_type == args->icmp_type);
67-
assert(l4->icmp6_code == args->icmp_code);
68-
assert(l4->icmp6_cksum == bpf_htons(args->checksum));
34+
ASSERT_CTX_BUF_OFF2("icmpv6_reply",
35+
"Ether", args->ctx, sizeof(__u32),
36+
"icmpv6_reply",
37+
args->buf_expected,
38+
args->buf_len,
39+
args->buf_len);
6940

7041
struct ratelimit_key key = {
7142
.usage = RATELIMIT_USAGE_ICMPV6,

bpf/tests/scapy/lb_pkt_defs.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@
2121
Raw("S"*1)
2222
)
2323

24+
lb4_udp_clusterip = (
25+
Ether(src=mac_one, dst=mac_two) /
26+
IP(src=v4_ext_one, dst=v4_svc_one) /
27+
UDP(sport=tcp_src_one, dport=tcp_svc_one) /
28+
Raw(b"X" * 100)
29+
)
30+
31+
lb4_udp_clusterip_icmp_unreach = (
32+
Ether(src=mac_two, dst=mac_one) /
33+
IP(src=v4_svc_one, dst=v4_ext_one, id=0) /
34+
ICMP(type="dest-unreach", code="port-unreachable") /
35+
IPerror(bytes(lb4_udp_clusterip[IP])[:28])
36+
)
37+
2438
lb6_clusterip = (
2539
Ether(src=mac_one, dst=mac_two) /
2640
IPv6(src=v6_ext_node_one, dst=v6_svc_one) /
@@ -35,6 +49,20 @@
3549
Raw("S"*1)
3650
)
3751

52+
lb6_udp_clusterip = (
53+
Ether(src=mac_one, dst=mac_two) /
54+
IPv6(src=v6_ext_node_one, dst=v6_svc_one) /
55+
UDP(sport=tcp_src_one, dport=tcp_svc_one) /
56+
Raw(b"X" * 100)
57+
)
58+
59+
lb6_udp_clusterip_icmp_unreach = (
60+
Ether(src=mac_two, dst=mac_one) /
61+
IPv6(src=v6_svc_one, dst=v6_ext_node_one) /
62+
ICMPv6DestUnreach(code=4, cksum=58197) /
63+
IPerror6(bytes(lb6_udp_clusterip[IPv6]))
64+
)
65+
3866
# Packets for testing N/S LB path with ExternalIPs.
3967

4068
lb4_ns_external_ip = (
@@ -184,6 +212,20 @@
184212
Raw(load="S" * 1)
185213
)
186214

215+
lb4_ew_udp_nodeport = (
216+
Ether(src=mac_one, dst=mac_two) /
217+
IP(src=v4_pod_one, dst=v4_svc_one) /
218+
UDP(sport=tcp_src_one, dport=tcp_svc_one) /
219+
Raw(b"X" * 100)
220+
)
221+
222+
lb4_ew_udp_nodeport_icmp_unreach = (
223+
Ether(src=mac_two, dst=mac_one) /
224+
IP(src=v4_svc_one, dst=v4_pod_one, id=0) /
225+
ICMP(type="dest-unreach", code="port-unreachable") /
226+
IPerror(bytes(lb4_ew_udp_nodeport[IP])[:28])
227+
)
228+
187229
lb6_ew_nodeport_fragment1 = (
188230
Ether(src=mac_one, dst=mac_two) /
189231
IPv6(src=v6_pod_two, dst=v6_svc_one, nh=44) /
@@ -214,3 +256,16 @@
214256
Raw(load="S" * 1)
215257
)
216258

259+
lb6_ew_udp_nodeport = (
260+
Ether(src=mac_one, dst=mac_two) /
261+
IPv6(src=v6_pod_one, dst=v6_svc_one) /
262+
UDP(sport=tcp_src_one, dport=tcp_svc_one) /
263+
Raw(b"X" * 100)
264+
)
265+
266+
lb6_ew_udp_nodeport_icmp_unreach = (
267+
Ether(src=mac_two, dst=mac_one) /
268+
IPv6(src=v6_svc_one, dst=v6_pod_one) /
269+
ICMPv6DestUnreach(code=4, cksum=1618) /
270+
IPerror6(bytes(lb6_ew_udp_nodeport[IPv6]))
271+
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright Authors of Cilium
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from scapy.all import *
5+
6+
from pkt_defs_common import *
7+
8+
v4_lxc_to_external = (
9+
Ether(src=mac_one, dst=mac_two) /
10+
IP(src=v4_pod_one, dst=v4_ext_one) /
11+
TCP(sport=tcp_src_one, dport=tcp_dst_one,flags="S")
12+
)
13+
14+
v4_lxc_to_external_icmp_unreach = (
15+
Ether(src=mac_two, dst=mac_one) /
16+
IP(src=v4_ext_one, dst=v4_pod_one, id=0) /
17+
ICMP(type="dest-unreach", code="communication-prohibited") /
18+
IPerror(bytes(v4_lxc_to_external[IP])[:28])
19+
)
20+
21+
v6_lxc_to_external = (
22+
Ether(src=mac_one, dst=mac_two) /
23+
IPv6(src=v6_pod_one, dst=v6_ext_node_one) /
24+
TCP(sport=tcp_src_one, dport=tcp_dst_one,flags="S")
25+
)
26+
27+
v6_lxc_to_external_icmp_unreach = (
28+
Ether(src=mac_two, dst=mac_one) /
29+
IPv6(src=v6_ext_node_one, dst=v6_pod_one) /
30+
ICMPv6DestUnreach(code=1) /
31+
IPerror6(bytes(v6_lxc_to_external[IPv6]))
32+
)

bpf/tests/scapy/pkt_defs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from selftest_pkt_defs import *
1010
from ipsec_from_netdev_pkt_defs import *
1111
from ipv6_ndp_pkt_defs import *
12+
from lxc_policy_reject_defs import *
1213
from tc_l2_announce_pkt_defs import *
1314
from tc_l2_announce6_pkt_defs import *
1415
from wg_from_netdev_pkt_defs import *

0 commit comments

Comments
 (0)