diff options
author | Andrew Yourtchenko <ayourtch@gmail.com> | 2017-04-04 14:10:40 +0000 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2017-04-06 15:30:21 +0000 |
commit | d1b05647427c79cfd5322991bbe663fae65f37b5 (patch) | |
tree | c7449b51cd46575c7978d8cac795b15a84f4163a /src/plugins/acl/fa_node.c | |
parent | 0eb2b16f95c0c43302be79a1c4df8b828ac97e37 (diff) |
acl-plugin: make the IPv4/IPv6 non-first fragment handling in line with ACL (VPP-682)
This fixes the previously-implicit "drop all non-first fragments" behavior
to be more in line with security rules: a non-first fragment is treated
for the purposes of matching the ACL as a packet with the port
match succeeding. This allows to change the behavior to permit
the fragmented packets for the default "permit specific rules"
ruleset, but also gives the flexibility to block the non-initial
fragments by inserting into the begining a bogus rule
which would deny the L4 traffic.
Also, add a knob which allows to potentially turn this behavior off
in case of a dire need (and revert to dropping all non-initial fragments),
via a debug CLI.
Change-Id: I546b372b65ff2157d9c68b1d32f9e644f1dd71b4
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
(cherry picked from commit 9fc0c26c6b28fd6c8b8142ea52f52eafa7e8c7ac)
Diffstat (limited to 'src/plugins/acl/fa_node.c')
-rw-r--r-- | src/plugins/acl/fa_node.c | 70 |
1 files changed, 61 insertions, 9 deletions
diff --git a/src/plugins/acl/fa_node.c b/src/plugins/acl/fa_node.c index 1f9117a6321..e12cbaa731d 100644 --- a/src/plugins/acl/fa_node.c +++ b/src/plugins/acl/fa_node.c @@ -191,7 +191,21 @@ acl_match_5tuple (acl_main_t * am, u32 acl_index, fa_5tuple_t * pkt_5tuple, { if (pkt_5tuple->l4.proto != r->proto) continue; - /* A sanity check just to ensure what we jave just matched was a valid L4 extracted from the packet */ + + if (PREDICT_FALSE (pkt_5tuple->pkt.is_nonfirst_fragment && + am->l4_match_nonfirst_fragment)) + { + /* non-initial fragment with frag match configured - match this rule */ + *trace_bitmap |= 0x80000000; + *r_action = r->is_permit; + if (r_acl_match_p) + *r_acl_match_p = acl_index; + if (r_rule_match_p) + *r_rule_match_p = i; + return 1; + } + + /* A sanity check just to ensure we are about to match the ports extracted from the packet */ if (PREDICT_FALSE (!pkt_5tuple->pkt.l4_valid)) continue; @@ -312,6 +326,10 @@ acl_fill_5tuple (acl_main_t * am, vlib_buffer_t * b0, int is_ip6, l3_offset = 0; } + /* key[0..3] contains src/dst address and is cleared/set below */ + /* Remainder of the key and per-packet non-key data */ + p5tuple_pkt->kv.key[4] = 0; + p5tuple_pkt->kv.value = 0; if (is_ip6) { @@ -333,12 +351,33 @@ acl_fill_5tuple (acl_main_t * am, vlib_buffer_t * b0, int is_ip6, int need_skip_eh = clib_bitmap_get (am->fa_ipv6_known_eh_bitmap, proto); if (PREDICT_FALSE (need_skip_eh)) { - /* FIXME: add fragment header special handling. Currently causes treated as unknown header. */ while (need_skip_eh && offset_within_packet (b0, l4_offset)) { - u8 nwords = *(u8 *) get_ptr_to_offset (b0, 1 + l4_offset); - proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); - l4_offset += 8 * (1 + (u16) nwords); + /* Fragment header needs special handling */ + if (PREDICT_FALSE(ACL_EH_FRAGMENT == proto)) + { + proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); + u16 frag_offset; + clib_memcpy (&frag_offset, get_ptr_to_offset (b0, 2 + l4_offset), sizeof(frag_offset)); + frag_offset = ntohs(frag_offset) >> 3; + if (frag_offset) + { + p5tuple_pkt->pkt.is_nonfirst_fragment = 1; + /* invalidate L4 offset so we don't try to find L4 info */ + l4_offset += b0->current_length; + } + else + { + /* First fragment: skip the frag header and move on. */ + l4_offset += 8; + } + } + else + { + u8 nwords = *(u8 *) get_ptr_to_offset (b0, 1 + l4_offset); + proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); + l4_offset += 8 * (1 + (u16) nwords); + } #ifdef FA_NODE_VERBOSE_DEBUG clib_warning ("ACL_FA_NODE_DBG: new proto: %d, new offset: %d", proto, l4_offset); @@ -369,13 +408,26 @@ acl_fill_5tuple (acl_main_t * am, vlib_buffer_t * b0, int is_ip6, offsetof (ip4_header_t, protocol) + l3_offset); l4_offset = l3_offset + sizeof (ip4_header_t); + u16 flags_and_fragment_offset; + clib_memcpy (&flags_and_fragment_offset, + get_ptr_to_offset (b0, + offsetof (ip4_header_t, + flags_and_fragment_offset)) + l3_offset, + sizeof(flags_and_fragment_offset)); + flags_and_fragment_offset = ntohs (flags_and_fragment_offset); + + /* non-initial fragments have non-zero offset */ + if ((PREDICT_FALSE(0xfff & flags_and_fragment_offset))) + { + p5tuple_pkt->pkt.is_nonfirst_fragment = 1; + /* invalidate L4 offset so we don't try to find L4 info */ + l4_offset += b0->current_length; + } + } - /* Remainder of the key and per-packet non-key data */ - p5tuple_pkt->kv.key[4] = 0; - p5tuple_pkt->kv.value = 0; + p5tuple_pkt->l4.proto = proto; if (PREDICT_TRUE (offset_within_packet (b0, l4_offset))) { - p5tuple_pkt->l4.proto = proto; p5tuple_pkt->pkt.l4_valid = 1; if (icmp_protos[is_ip6] == proto) { |