diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/acl/acl.c | 7 | ||||
-rw-r--r-- | src/plugins/acl/acl.h | 5 | ||||
-rw-r--r-- | src/plugins/acl/fa_node.c | 70 | ||||
-rw-r--r-- | src/plugins/acl/fa_node.h | 8 |
4 files changed, 77 insertions, 13 deletions
diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index 6657d370e63..98c74b9b358 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -2008,6 +2008,11 @@ acl_set_aclplugin_fn (vlib_main_t * vm, } goto done; } + if (unformat (input, "l4-match-nonfirst-fragment %u", &val)) + { + am->l4_match_nonfirst_fragment = (val != 0); + goto done; + } if (unformat (input, "session")) { if (unformat (input, "clear")) { acl_main_t *am = &acl_main; @@ -2205,6 +2210,8 @@ acl_init (vlib_main_t * vm) foreach_acl_eh #undef _ + am->l4_match_nonfirst_fragment = 1; + return error; } diff --git a/src/plugins/acl/acl.h b/src/plugins/acl/acl.h index f5a1fe0f934..d708c521dfa 100644 --- a/src/plugins/acl/acl.h +++ b/src/plugins/acl/acl.h @@ -181,6 +181,9 @@ typedef struct { /* EH values that we can skip over */ uword *fa_ipv6_known_eh_bitmap; + /* whether to match L4 ACEs with ports on the non-initial fragment */ + int l4_match_nonfirst_fragment; + /* conn table per-interface conn table parameters */ u32 fa_conn_table_hash_num_buckets; uword fa_conn_table_hash_memory_size; @@ -235,6 +238,7 @@ typedef struct { _(HOPBYHOP , 0 , "IPv6ExtHdrHopByHop") \ _(ROUTING , 43 , "IPv6ExtHdrRouting") \ _(DESTOPT , 60 , "IPv6ExtHdrDestOpt") \ + _(FRAGMENT , 44 , "IPv6ExtHdrFragment") \ _(MOBILITY , 135, "Mobility Header") \ _(HIP , 139, "Experimental use Host Identity Protocol") \ _(SHIM6 , 140, "Shim6 Protocol") \ @@ -247,7 +251,6 @@ typedef struct { Also, Fragment header needs special processing. _(NONEXT , 59 , "NoNextHdr") \ - _(FRAGMENT , 44 , "IPv6ExtHdrFragment") \ ESP is hiding its internal format, so no point in trying to go past it. 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) { diff --git a/src/plugins/acl/fa_node.h b/src/plugins/acl/fa_node.h index 76a40a38486..8edd0069217 100644 --- a/src/plugins/acl/fa_node.h +++ b/src/plugins/acl/fa_node.h @@ -22,10 +22,12 @@ typedef union { u64 as_u64; struct { - u8 tcp_flags_valid; u8 tcp_flags; - u8 is_input; - u8 l4_valid; + u8 tcp_flags_valid:1; + u8 is_input:1; + u8 l4_valid:1; + u8 is_nonfirst_fragment:1; + u8 flags_reserved:4; }; } fa_packet_info_t; |