ACL-plugin does not match UDP next-header, VPP-687
[vpp.git] / src / plugins / acl / fa_node.c
index ac619a7..b706fce 100644 (file)
@@ -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;
 
@@ -293,7 +307,7 @@ static int
 offset_within_packet (vlib_buffer_t * b0, int offset)
 {
   /* For the purposes of this code, "within" means we have at least 8 bytes after it */
-  return (offset < (b0->current_length - 8));
+  return (offset <= (b0->current_length - 8));
 }
 
 static void
@@ -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)
        {
@@ -533,6 +585,10 @@ acl_fa_conn_list_add_session (acl_main_t * am, u32 sess_id)
 
   if (~0 == am->fa_conn_list_head[list_id]) {
     am->fa_conn_list_head[list_id] = sess_id;
+    /* If it is a first conn in any list, kick off the cleaner */
+    vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
+                                 ACL_FA_CLEANER_RESCHEDULE, 0);
+
   }
 }
 
@@ -556,7 +612,7 @@ acl_fa_conn_list_delete_session (acl_main_t *am, u32 sess_id)
     am->fa_conn_list_head[sess->link_list_id] = sess->link_next_idx;
   }
   if (am->fa_conn_list_tail[sess->link_list_id] == sess_id) {
-    am->fa_conn_list_tail[sess->link_list_id] = sess->link_next_idx;
+    am->fa_conn_list_tail[sess->link_list_id] = sess->link_prev_idx;
   }
 }
 
@@ -982,14 +1038,6 @@ acl_out_ip4_fa_node_fn (vlib_main_t * vm,
 
 /* *INDENT-OFF* */
 #define foreach_acl_fa_cleaner_error \
-_(EVENT_CYCLE, "event processing cycle")  \
-_(TIMER_RESTARTED, "restarted session timers")  \
-_(DELETED_SESSIONS, "deleted sessions")  \
-_(ALREADY_DELETED, "timer event for already deleted session")  \
-_(DELETE_BY_SW_IF_INDEX, "delete by sw_if_index event")  \
-_(DELETE_BY_SW_IF_INDEX_OK, "delete by sw_if_index completed ok")  \
-_(WAIT_WITHOUT_TIMEOUT, "process waits without timeout")  \
-_(WAIT_WITH_TIMEOUT, "process waits with timeout")  \
 _(UNKNOWN_EVENT, "unknown event received")  \
 /* end  of errors */
 
@@ -1067,7 +1115,7 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
   f64 cpu_cps = vm->clib_time.clocks_per_second;
   u64 next_expire;
   /* We should call timer wheel at least twice a second */
-  u64 max_timer_wait_interval = cpu_cps / 2; 
+  u64 max_timer_wait_interval = cpu_cps / 2;
   am->fa_current_cleaner_timer_wait_interval = max_timer_wait_interval;
 
   u32 *expired = NULL;
@@ -1079,10 +1127,24 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
     {
       u32 count_deleted_sessions = 0;
       u32 count_already_deleted = 0;
-      u32 count_timer_restarted = 0;
       now = clib_cpu_time_now ();
       next_expire = now + am->fa_current_cleaner_timer_wait_interval;
+      int has_pending_conns = 0;
+      u8 tt;
+      for(tt = 0; tt < ACL_N_TIMEOUTS; tt++)
+        {
+          if (~0 != am->fa_conn_list_head[tt])
+            has_pending_conns = 1;
+        }
 
+      /* If no pending connections then no point in timing out */
+      if (!has_pending_conns)
+        {
+          am->fa_cleaner_cnt_wait_without_timeout++;
+          (void) vlib_process_wait_for_event (vm);
+          event_type = vlib_process_get_events (vm, &event_data);
+        }
+      else
        {
          f64 timeout = ((i64) next_expire - (i64) now) / cpu_cps;
          if (timeout <= 0)
@@ -1095,11 +1157,7 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
              /* Timing wheel code is happier if it is called regularly */
              if (timeout > 0.5)
                timeout = 0.5;
-             vlib_node_increment_counter (vm,
-                                          acl_fa_session_cleaner_process_node.
-                                          index,
-                                          ACL_FA_CLEANER_ERROR_WAIT_WITH_TIMEOUT,
-                                          1);
+              am->fa_cleaner_cnt_wait_with_timeout++;
              (void) vlib_process_wait_for_event_or_clock (vm, timeout);
              event_type = vlib_process_get_events (vm, &event_data);
            }
@@ -1119,11 +1177,7 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
            uword *sw_if_index0;
            vec_foreach (sw_if_index0, event_data)
            {
-             vlib_node_increment_counter (vm,
-                                          acl_fa_session_cleaner_process_node.
-                                          index,
-                                          ACL_FA_CLEANER_ERROR_DELETE_BY_SW_IF_INDEX,
-                                          1);
+              am->fa_cleaner_cnt_delete_by_sw_index++;
 #ifdef FA_NODE_VERBOSE_DEBUG
              clib_warning
                ("ACL_FA_NODE_CLEAN: ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX: %d",
@@ -1134,11 +1188,7 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
                acl_fa_clean_sessions_by_sw_if_index (am, *sw_if_index0,
                                                      &count);
              count_deleted_sessions += count;
-             vlib_node_increment_counter (vm,
-                                          acl_fa_session_cleaner_process_node.
-                                          index,
-                                          ACL_FA_CLEANER_ERROR_DELETE_BY_SW_IF_INDEX_OK,
-                                          result);
+              am->fa_cleaner_cnt_delete_by_sw_index_ok += result;
            }
          }
          break;
@@ -1147,17 +1197,21 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
          clib_warning ("ACL plugin connection cleaner: unknown event %u",
                        event_type);
 #endif
-         vlib_node_increment_counter (vm,
-                                      acl_fa_session_cleaner_process_node.
-                                      index,
-                                      ACL_FA_CLEANER_ERROR_UNKNOWN_EVENT, 1);
+          vlib_node_increment_counter (vm,
+                                       acl_fa_session_cleaner_process_node.
+                                       index,
+                                       ACL_FA_CLEANER_ERROR_UNKNOWN_EVENT, 1);
+          am->fa_cleaner_cnt_unknown_event++;
          break;
        }
 
       {
         u8 tt = 0;
         for(tt = 0; tt < ACL_N_TIMEOUTS; tt++) {
-          while((vec_len(expired) < 2*am->fa_max_deleted_sessions_per_interval) && (~0 != am->fa_conn_list_head[tt]) && (acl_fa_conn_has_timed_out(am, now, am->fa_conn_list_head[tt]))) {
+          while((vec_len(expired) < 2*am->fa_max_deleted_sessions_per_interval)
+                && (~0 != am->fa_conn_list_head[tt])
+                && (acl_fa_conn_has_timed_out(am, now,
+                                              am->fa_conn_list_head[tt]))) {
             u32 sess_id = am->fa_conn_list_head[tt];
             vec_add1(expired, sess_id);
             acl_fa_conn_list_delete_session(am, sess_id);
@@ -1165,7 +1219,6 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
         }
       }
 
-
       u32 *psid = NULL;
       vec_foreach (psid, expired)
       {
@@ -1181,15 +1234,22 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
                /* clib_warning ("ACL_FA_NODE_CLEAN: Restarting timer for session %d",
                   (int) session_index); */
 
-               /* Pretend we did this in the past, at last_active moment */
-               count_timer_restarted++;
+                /* There was activity on the session, so the idle timeout
+                   has not passed. Enqueue for another time period. */
+
+                acl_fa_conn_list_add_session(am, session_index);
+
+               /* FIXME: When/if moving to timer wheel,
+                   pretend we did this in the past,
+                   at last_active moment, so the timer is accurate */
+                am->fa_cleaner_cnt_timer_restarted++;
              }
            else
              {
                /* clib_warning ("ACL_FA_NODE_CLEAN: Deleting session %d",
                   (int) session_index); */
                acl_fa_delete_session (am, sw_if_index, session_index);
-               count_deleted_sessions++;
+                count_deleted_sessions++;
              }
          }
        else
@@ -1210,22 +1270,9 @@ acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
         if (am->fa_current_cleaner_timer_wait_interval < max_timer_wait_interval)
           am->fa_current_cleaner_timer_wait_interval += cpu_cps * am->fa_cleaner_wait_time_increment;
       }
-
-      vlib_node_increment_counter (vm,
-                                  acl_fa_session_cleaner_process_node.index,
-                                  ACL_FA_CLEANER_ERROR_EVENT_CYCLE, 1);
-      vlib_node_increment_counter (vm,
-                                  acl_fa_session_cleaner_process_node.index,
-                                  ACL_FA_CLEANER_ERROR_TIMER_RESTARTED,
-                                  count_timer_restarted);
-      vlib_node_increment_counter (vm,
-                                  acl_fa_session_cleaner_process_node.index,
-                                  ACL_FA_CLEANER_ERROR_DELETED_SESSIONS,
-                                  count_deleted_sessions);
-      vlib_node_increment_counter (vm,
-                                  acl_fa_session_cleaner_process_node.index,
-                                  ACL_FA_CLEANER_ERROR_ALREADY_DELETED,
-                                  count_already_deleted);
+      am->fa_cleaner_cnt_event_cycles++;
+      am->fa_cleaner_cnt_deleted_sessions += count_deleted_sessions;
+      am->fa_cleaner_cnt_already_deleted += count_already_deleted;
     }
   /* NOT REACHED */
   return 0;