Skip to content

Commit

Permalink
bpf: reduce complexity of logic to handle IPv4 fragments
Browse files Browse the repository at this point in the history
Refactor the logic to handle IPv4 fragments to have only a single call
to `ctx_load_bytes()` in order to load the l4 ports tuple.

This change fixes a complexity issue in 5.4 kernels which prevents the
BPF program from being loaded by the verifier as it would reach the
maximum amount of instructions that can be processed.

Signed-off-by: Gilberto Bertin <gilberto@isovalent.com>
  • Loading branch information
jibi committed Nov 16, 2020
1 parent e9bf184 commit a78f75e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 49 deletions.
10 changes: 5 additions & 5 deletions bpf/lib/conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -535,14 +535,14 @@ static __always_inline int ipv4_ct_extract_l4_ports(struct __ctx_buff *ctx,
if (!revalidate_data(ctx, &data, &data_end, &ip4))
return DROP_CT_INVALID_HDR;

if (unlikely(ipv4_is_fragment(ip4)))
return ipv4_handle_fragment(ctx, ip4, off, dir,
(struct ipv4_frag_l4ports *)&tuple->dport,
has_l4_header);
#endif
return ipv4_handle_fragmentation(ctx, ip4, off, dir,
(struct ipv4_frag_l4ports *)&tuple->dport,
has_l4_header);
#else
/* load sport + dport into tuple */
if (ctx_load_bytes(ctx, off, &tuple->dport, 4) < 0)
return DROP_CT_INVALID_HDR;
#endif

return CTX_ACT_OK;
}
Expand Down
68 changes: 33 additions & 35 deletions bpf/lib/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,32 +108,13 @@ ipv4_frag_get_l4ports(const struct ipv4_frag_id *frag_id,
}

static __always_inline int
ipv4_frag_register_datagram(struct __ctx_buff *ctx, int l4_off, int dir,
const struct ipv4_frag_id *frag_id,
struct ipv4_frag_l4ports *ports)
ipv4_handle_fragmentation(struct __ctx_buff *ctx,
const struct iphdr *ip4, int l4_off, int dir,
struct ipv4_frag_l4ports *ports,
bool *has_l4_header)
{
int ret;

ret = ctx_load_bytes(ctx, l4_off, ports, 4);
if (ret < 0)
return ret;

if (map_update_elem(&IPV4_FRAG_DATAGRAMS_MAP, frag_id, ports, BPF_ANY))
update_metrics(ctx_full_len(ctx), dir, REASON_FRAG_PACKET_UPDATE);

/* Do not return an error if map update failed, as nothing prevents us
* to process the current packet normally.
*/
return 0;
}

static __always_inline int
ipv4_handle_fragment(struct __ctx_buff *ctx,
const struct iphdr *ip4, int l4_off, int dir,
struct ipv4_frag_l4ports *ports,
bool *has_l4_header)
{
bool not_first_fragment;
bool is_fragment, not_first_fragment;

struct ipv4_frag_id frag_id = {
.daddr = ip4->daddr,
Expand All @@ -143,20 +124,37 @@ ipv4_handle_fragment(struct __ctx_buff *ctx,
.pad = 0,
};

update_metrics(ctx_full_len(ctx), dir, REASON_FRAG_PACKET);
is_fragment = ipv4_is_fragment(ip4);

not_first_fragment = ipv4_is_not_first_fragment(ip4);
if (has_l4_header)
*has_l4_header = !not_first_fragment;
if (unlikely(is_fragment)) {
update_metrics(ctx_full_len(ctx), dir, REASON_FRAG_PACKET);

if (likely(not_first_fragment))
return ipv4_frag_get_l4ports(&frag_id, ports);
not_first_fragment = ipv4_is_not_first_fragment(ip4);
if (has_l4_header)
*has_l4_header = !not_first_fragment;

/* First logical fragment for this datagram (not necessarily the first
* we receive). Fragment has L4 header, we can retrieve L4 ports and
* create an entry in datagrams map.
*/
return ipv4_frag_register_datagram(ctx, l4_off, dir, &frag_id, ports);
if (likely(not_first_fragment))
return ipv4_frag_get_l4ports(&frag_id, ports);
}

/* load sport + dport into tuple */
ret = ctx_load_bytes(ctx, l4_off, ports, 4);
if (ret < 0)
return ret;

if (unlikely(is_fragment)) {
/* First logical fragment for this datagram (not necessarily the first
* we receive). Fragment has L4 header, create an entry in datagrams map.
*/
if (map_update_elem(&IPV4_FRAG_DATAGRAMS_MAP, &frag_id, ports, BPF_ANY))
update_metrics(ctx_full_len(ctx), dir, REASON_FRAG_PACKET_UPDATE);

/* Do not return an error if map update failed, as nothing prevents us
* to process the current packet normally.
*/
}

return 0;
}
#endif

Expand Down
15 changes: 6 additions & 9 deletions bpf/lib/lb.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,15 +354,12 @@ static __always_inline int extract_l4_port(struct __ctx_buff *ctx, __u8 nexthdr,
if (ip4) {
struct ipv4_frag_l4ports ports = { };

if (unlikely(ipv4_is_fragment(ip4))) {
ret = ipv4_handle_fragment(ctx, ip4, l4_off,
dir, &ports,
NULL);
if (IS_ERR(ret))
return ret;
*port = ports.dport;
break;
}
ret = ipv4_handle_fragmentation(ctx, ip4, l4_off,
dir, &ports, NULL);
if (IS_ERR(ret))
return ret;
*port = ports.dport;
break;
}
#endif
/* Port offsets for UDP and TCP are the same */
Expand Down

0 comments on commit a78f75e

Please sign in to comment.