+always_inline u64
+reverse_l4_u64_fastpath (u64 l4, int is_ip6)
+{
+ fa_session_l4_key_t l4i = {.as_u64 = l4 };
+ fa_session_l4_key_t l4o;
+
+ l4o.port[1] = l4i.port[0];
+ l4o.port[0] = l4i.port[1];
+
+ l4o.non_port_l4_data = l4i.non_port_l4_data;
+ l4o.is_input = 1 - l4i.is_input;
+ return l4o.as_u64;
+}
+
+always_inline u64
+reverse_l4_u64_slowpath (u64 l4, int is_ip6)
+{
+ fa_session_l4_key_t l4i = {.as_u64 = l4 };
+ fa_session_l4_key_t l4o;
+
+ if (l4i.proto == icmp_protos[is_ip6])
+ {
+ static const u8 *icmp_invmap[] = { icmp4_invmap, icmp6_invmap };
+ static const u8 *icmp_valid_new[] =
+ { icmp4_valid_new, icmp6_valid_new };
+ static const u8 icmp_invmap_size[] = { sizeof (icmp4_invmap),
+ sizeof (icmp6_invmap)
+ };
+ static const u8 icmp_valid_new_size[] = { sizeof (icmp4_valid_new),
+ sizeof (icmp6_valid_new)
+ };
+ int type = is_ip6 ? l4i.port[0] - 128 : l4i.port[0];
+
+ l4o.non_port_l4_data = l4i.non_port_l4_data;
+ l4o.port[0] = l4i.port[0];
+ l4o.port[1] = l4i.port[1];
+
+
+ /*
+ * ONLY ICMP messages defined in icmp4_valid_new/icmp6_valid_new table
+ * are allowed to create stateful ACL.
+ * The other messages will be forwarded without creating a reverse session.
+ */
+
+ if (type >= 0 && (type <= icmp_valid_new_size[is_ip6])
+ && (icmp_valid_new[is_ip6][type])
+ && (type <= icmp_invmap_size[is_ip6]) && icmp_invmap[is_ip6][type])
+ {
+ /*
+ * we set the inverse direction and correct the port,
+ * if it is okay to add the reverse session.
+ * If not, then the same session will be added twice
+ * to bihash, which is the same as adding just one session.
+ */
+ l4o.is_input = 1 - l4i.is_input;
+ l4o.port[0] = icmp_invmap[is_ip6][type] - 1;
+ }
+
+ return l4o.as_u64;
+ }
+ else
+ return reverse_l4_u64_fastpath (l4, is_ip6);
+}
+
+always_inline u64
+reverse_l4_u64 (u64 l4, int is_ip6)
+{
+ fa_session_l4_key_t l4i = {.as_u64 = l4 };
+
+ if (PREDICT_FALSE (l4i.is_slowpath))
+ {
+ return reverse_l4_u64_slowpath (l4, is_ip6);
+ }
+ else
+ {
+ return reverse_l4_u64_fastpath (l4, is_ip6);
+ }
+}
+
+always_inline void
+reverse_session_add_del (acl_main_t * am, const int is_ip6,
+ clib_bihash_kv_40_8_t * pkv, int is_add)
+{
+ clib_bihash_kv_40_8_t kv2;
+ /* the first 4xu64 is two addresses, so just swap them */
+ kv2.key[0] = pkv->key[2];
+ kv2.key[1] = pkv->key[3];
+ kv2.key[2] = pkv->key[0];
+ kv2.key[3] = pkv->key[1];
+ /* the last u64 needs special treatment (ports, etc.) */
+ kv2.key[4] = reverse_l4_u64 (pkv->key[4], is_ip6);
+ kv2.value = pkv->value;
+ clib_bihash_add_del_40_8 (&am->fa_sessions_hash, &kv2, is_add);
+}