A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / map / map.c
index 93a10c6..74a9905 100644 (file)
  * limitations under the License.
  */
 
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/adj/adj.h>
+#include <vnet/map/map_dpo.h>
+
 #include "map.h"
 
 #ifndef __SSE4_2__
@@ -30,7 +35,7 @@ crc_u32 (u32 data, u32 value)
 
 /*
  * This code supports the following MAP modes:
- * 
+ *
  * Algorithmic Shared IPv4 address (ea_bits_len > 0):
  *   ea_bits_len + ip4_prefix > 32
  *   psid_length > 0, ip6_prefix < 64, ip4_prefix <= 32
@@ -128,8 +133,9 @@ ip6_get_port (ip6_header_t * ip6, map_dir_e dir, u16 buffer_len)
   if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP)
     {
       return (dir ==
-             MAP_SENDER) ? ((udp_header_t *) (l4))->
-       src_port : ((udp_header_t *) (l4))->dst_port;
+             MAP_SENDER) ? ((udp_header_t *) (l4))->src_port : ((udp_header_t
+                                                                 *)
+                                                                (l4))->dst_port;
     }
   else if (l4_protocol == IP_PROTOCOL_ICMP6)
     {
@@ -158,19 +164,12 @@ map_create_domain (ip4_address_t * ip4_prefix,
                   u8 psid_offset,
                   u8 psid_length, u32 * map_domain_index, u16 mtu, u8 flags)
 {
+  u8 suffix_len, suffix_shift;
   map_main_t *mm = &map_main;
-  ip4_main_t *im4 = &ip4_main;
-  ip6_main_t *im6 = &ip6_main;
+  dpo_id_t dpo_v4 = DPO_NULL;
+  dpo_id_t dpo_v6 = DPO_NULL;
+  fib_node_index_t fei;
   map_domain_t *d;
-  ip_adjacency_t adj;
-  ip4_add_del_route_args_t args4;
-  ip6_add_del_route_args_t args6;
-  u8 suffix_len;
-  uword *p;
-
-  /* EA bits must be within the first 64 bits */
-  if (ea_bits_len > 0 && (ip6_prefix_len + ea_bits_len) > 64)
-    return -1;
 
   /* Sanity check on the src prefix length */
   if (flags & MAP_DOMAIN_TRANSLATION)
@@ -186,11 +185,35 @@ map_create_domain (ip4_address_t * ip4_prefix,
       if (ip6_src_len != 128)
        {
          clib_warning
-           ("MAP-E requires a BR address, not a prefix (ip6_src_len should be 128).");
+           ("MAP-E requires a BR address, not a prefix (ip6_src_len should "
+            "be 128).");
          return -1;
        }
     }
 
+  /* How many, and which bits to grab from the IPv4 DA */
+  if (ip4_prefix_len + ea_bits_len < 32)
+    {
+      flags |= MAP_DOMAIN_PREFIX;
+      suffix_shift = 32 - ip4_prefix_len - ea_bits_len;
+      suffix_len = ea_bits_len;
+    }
+  else
+    {
+      suffix_shift = 0;
+      suffix_len = 32 - ip4_prefix_len;
+    }
+
+  /* EA bits must be within the first 64 bits */
+  if (ea_bits_len > 0 && ((ip6_prefix_len + ea_bits_len) > 64 ||
+                         ip6_prefix_len + suffix_len + psid_length > 64))
+    {
+      clib_warning
+       ("Embedded Address bits must be within the first 64 bits of "
+        "the IPv6 prefix");
+      return -1;
+    }
+
   /* Get domain index */
   pool_get_aligned (mm->domains, d, CLIB_CACHE_LINE_BYTES);
   memset (d, 0, sizeof (*d));
@@ -208,89 +231,89 @@ map_create_domain (ip4_address_t * ip4_prefix,
   d->psid_length = psid_length;
   d->mtu = mtu;
   d->flags = flags;
-
-  /* How many, and which bits to grab from the IPv4 DA */
-  if (ip4_prefix_len + ea_bits_len < 32)
-    {
-      d->flags |= MAP_DOMAIN_PREFIX;
-      suffix_len = d->suffix_shift = 32 - ip4_prefix_len - ea_bits_len;
-    }
-  else
-    {
-      d->suffix_shift = 0;
-      suffix_len = 32 - ip4_prefix_len;
-    }
+  d->suffix_shift = suffix_shift;
   d->suffix_mask = (1 << suffix_len) - 1;
 
   d->psid_shift = 16 - psid_length - psid_offset;
   d->psid_mask = (1 << d->psid_length) - 1;
   d->ea_shift = 64 - ip6_prefix_len - suffix_len - d->psid_length;
 
-  /* Init IP adjacency */
-  memset (&adj, 0, sizeof (adj));
-  adj.explicit_fib_index = ~0;
-  adj.lookup_next_index =
-    (d->
-     flags & MAP_DOMAIN_TRANSLATION) ? IP_LOOKUP_NEXT_MAP_T :
-    IP_LOOKUP_NEXT_MAP;
-  p = (uword *) & adj.rewrite_data[0];
-  *p = (uword) (*map_domain_index);
-
-  if (ip4_get_route (im4, 0, 0, (u8 *) ip4_prefix, ip4_prefix_len))
-    {
-      clib_warning ("IPv4 route already defined: %U/%d", format_ip4_address,
-                   ip4_prefix, ip4_prefix_len);
-      pool_put (mm->domains, d);
-      return -1;
-    }
-
-  /* Create ip4 adjacency */
-  memset (&args4, 0, sizeof (args4));
-  args4.table_index_or_table_id = 0;
-  args4.flags = IP4_ROUTE_FLAG_ADD;
-  args4.dst_address.as_u32 = ip4_prefix->as_u32;
-  args4.dst_address_length = ip4_prefix_len;
+  /* MAP data-plane object */
+  if (d->flags & MAP_DOMAIN_TRANSLATION)
+    map_t_dpo_create (DPO_PROTO_IP4, *map_domain_index, &dpo_v4);
+  else
+    map_dpo_create (DPO_PROTO_IP4, *map_domain_index, &dpo_v4);
+
+  /* Create ip4 route */
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = d->ip4_prefix_len,
+    .fp_addr = {
+               .ip4 = d->ip4_prefix,
+               }
+    ,
+  };
+  fib_table_entry_special_dpo_add (0, &pfx,
+                                  FIB_SOURCE_MAP,
+                                  FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
+  dpo_reset (&dpo_v4);
 
-  args4.adj_index = ~0;
-  args4.add_adj = &adj;
-  args4.n_add_adj = 1;
-  ip4_add_del_route (im4, &args4);
+  /*
+   * Multiple MAP domains may share same source IPv6 TEP.
+   * In this case the route will exist and be MAP sourced.
+   * Find the adj (if any) already contributed and modify it
+   */
+  fib_prefix_t pfx6 = {
+    .fp_proto = FIB_PROTOCOL_IP6,
+    .fp_len = d->ip6_src_len,
+    .fp_addr = {
+               .ip6 = d->ip6_src,
+               }
+    ,
+  };
+  fei = fib_table_lookup_exact_match (0, &pfx6);
 
-  /* Multiple MAP domains may share same source IPv6 TEP */
-  u32 ai = ip6_get_route (im6, 0, 0, ip6_src, ip6_src_len);
-  if (ai > 0)
+  if (FIB_NODE_INDEX_INVALID != fei)
     {
-      ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
-      ip_adjacency_t *adj6 = ip_get_adjacency (lm6, ai);
-      if (adj6->lookup_next_index != IP_LOOKUP_NEXT_MAP &&
-         adj6->lookup_next_index != IP_LOOKUP_NEXT_MAP_T)
+      dpo_id_t dpo = DPO_NULL;
+
+      if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_MAP, &dpo))
        {
-         clib_warning ("BR source address already assigned: %U",
-                       format_ip6_address, ip6_src);
-         pool_put (mm->domains, d);
-         return -1;
-       }
-      /* Shared source */
-      p = (uword *) & adj6->rewrite_data[0];
-      p[0] = ~0;
+         /*
+          * modify the existing MAP to indicate it's shared
+          * skip to route add.
+          */
+         const dpo_id_t *md_dpo;
+         map_dpo_t *md;
+
+         ASSERT (DPO_LOAD_BALANCE == dpo.dpoi_type);
 
-      /* Add refcount, so we don't accidentially delete the route underneath someone */
-      p[1]++;
+         md_dpo = load_balance_get_bucket (dpo.dpoi_index, 0);
+         md = map_dpo_get (md_dpo->dpoi_index);
+
+         md->md_domain = ~0;
+         dpo_copy (&dpo_v6, md_dpo);
+         dpo_reset (&dpo);
+
+         goto route_add;
+       }
     }
+
+  if (d->flags & MAP_DOMAIN_TRANSLATION)
+    map_t_dpo_create (DPO_PROTO_IP6, *map_domain_index, &dpo_v6);
   else
-    {
-      /* Create ip6 adjacency. */
-      memset (&args6, 0, sizeof (args6));
-      args6.table_index_or_table_id = 0;
-      args6.flags = IP6_ROUTE_FLAG_ADD;
-      args6.dst_address.as_u64[0] = ip6_src->as_u64[0];
-      args6.dst_address.as_u64[1] = ip6_src->as_u64[1];
-      args6.dst_address_length = ip6_src_len;
-      args6.adj_index = ~0;
-      args6.add_adj = &adj;
-      args6.n_add_adj = 1;
-      ip6_add_del_route (im6, &args6);
-    }
+    map_dpo_create (DPO_PROTO_IP6, *map_domain_index, &dpo_v6);
+
+route_add:
+  /*
+   * Create ip6 route. This is a reference counted add. If the prefix
+   * already exists and is MAP sourced, it is now MAP source n+1 times
+   * and will need to be removed n+1 times.
+   */
+  fib_table_entry_special_dpo_add (0, &pfx6,
+                                  FIB_SOURCE_MAP,
+                                  FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v6);
+  dpo_reset (&dpo_v6);
 
   /* Validate packet/byte counters */
   map_domain_counter_lock (mm);
@@ -320,12 +343,7 @@ int
 map_delete_domain (u32 map_domain_index)
 {
   map_main_t *mm = &map_main;
-  ip4_main_t *im4 = &ip4_main;
-  ip6_main_t *im6 = &ip6_main;
   map_domain_t *d;
-  ip_adjacency_t adj;
-  ip4_add_del_route_args_t args4;
-  ip6_add_del_route_args_t args6;
 
   if (pool_is_free_index (mm->domains, map_domain_index))
     {
@@ -336,48 +354,26 @@ map_delete_domain (u32 map_domain_index)
 
   d = pool_elt_at_index (mm->domains, map_domain_index);
 
-  memset (&adj, 0, sizeof (adj));
-  adj.explicit_fib_index = ~0;
-  adj.lookup_next_index =
-    (d->
-     flags & MAP_DOMAIN_TRANSLATION) ? IP_LOOKUP_NEXT_MAP_T :
-    IP_LOOKUP_NEXT_MAP;
-
-  /* Delete ip4 adjacency */
-  memset (&args4, 0, sizeof (args4));
-  args4.table_index_or_table_id = 0;
-  args4.flags = IP4_ROUTE_FLAG_DEL;
-  args4.dst_address.as_u32 = d->ip4_prefix.as_u32;
-  args4.dst_address_length = d->ip4_prefix_len;
-  args4.adj_index = 0;
-  args4.add_adj = &adj;
-  args4.n_add_adj = 0;
-  ip4_add_del_route (im4, &args4);
-
-  /* Delete ip6 adjacency */
-  u32 ai = ip6_get_route (im6, 0, 0, &d->ip6_src, d->ip6_src_len);
-  if (ai > 0)
-    {
-      ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
-      ip_adjacency_t *adj6 = ip_get_adjacency (lm6, ai);
-
-      uword *p = (uword *) & adj6->rewrite_data[0];
-      /* Delete route when no other domains use this source */
-      if (p[1] == 0)
-       {
-         memset (&args6, 0, sizeof (args6));
-         args6.table_index_or_table_id = 0;
-         args6.flags = IP6_ROUTE_FLAG_DEL;
-         args6.dst_address.as_u64[0] = d->ip6_src.as_u64[0];
-         args6.dst_address.as_u64[1] = d->ip6_src.as_u64[1];
-         args6.dst_address_length = d->ip6_src_len;
-         args6.adj_index = 0;
-         args6.add_adj = &adj;
-         args6.n_add_adj = 0;
-         ip6_add_del_route (im6, &args6);
-       }
-      p[1]--;
-    }
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = d->ip4_prefix_len,
+    .fp_addr = {
+               .ip4 = d->ip4_prefix,
+               }
+    ,
+  };
+  fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_MAP);
+
+  fib_prefix_t pfx6 = {
+    .fp_proto = FIB_PROTOCOL_IP6,
+    .fp_len = d->ip6_src_len,
+    .fp_addr = {
+               .ip6 = d->ip6_src,
+               }
+    ,
+  };
+  fib_table_entry_special_remove (0, &pfx6, FIB_SOURCE_MAP);
+
   /* Deleting rules */
   if (d->rules)
     clib_mem_free (d->rules);
@@ -437,17 +433,18 @@ static void
 map_pre_resolve (ip4_address_t * ip4, ip6_address_t * ip6)
 {
   map_main_t *mm = &map_main;
-  ip4_main_t *im4 = &ip4_main;
   ip6_main_t *im6 = &ip6_main;
 
   if (ip6->as_u64[0] != 0 || ip6->as_u64[1] != 0)
     {
-      mm->adj6_index = ip6_fib_lookup_with_table (im6, 0, ip6);
+      // FIXME NOT an ADJ
+      mm->adj6_index = ip6_fib_table_fwding_lookup (im6, 0, ip6);
       clib_warning ("FIB lookup results in: %u", mm->adj6_index);
     }
   if (ip4->as_u32 != 0)
     {
-      mm->adj4_index = ip4_fib_lookup_with_table (im4, 0, ip4, 0);
+      // FIXME NOT an ADJ
+      mm->adj4_index = ip4_fib_table_lookup_lb (0, ip4);
       clib_warning ("FIB lookup results in: %u", mm->adj4_index);
     }
 }
@@ -1842,126 +1839,236 @@ map_ip6_reass_conf_buffers (u32 buffers)
 }
 
 /* *INDENT-OFF* */
+
+/*?
+ * Configure MAP reassembly behaviour
+ *
+ * @cliexpar
+ * @cliexstart{map params reassembly}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_ip4_reass_lifetime_command, static) = {
   .path = "map params reassembly",
-  .short_help = "[ip4 | ip6] [lifetime <lifetime-ms>] [pool-size <pool-size>] [buffers <buffers>] [ht-ratio <ht-ratio>]",
+  .short_help = "map params reassembly [ip4 | ip6] [lifetime <lifetime-ms>] "
+                "[pool-size <pool-size>] [buffers <buffers>] "
+                "[ht-ratio <ht-ratio>]",
   .function = map_params_reass_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Set or copy the IP TOS/Traffic Class field
+ *
+ * @cliexpar
+ * @cliexstart{map params traffic-class}
+ *
+ * This command is used to set the traffic-class field in translated
+ * or encapsulated packets. If copy is specifed (the default) then the
+ * traffic-class/TOS field is copied from the original packet to the
+ * translated / encapsulating header.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_traffic_class_command, static) = {
   .path = "map params traffic-class",
-  .short_help = 
-  "traffic-class {0x0-0xff | copy}",
+  .short_help = "map params traffic-class {0x0-0xff | copy}",
   .function = map_traffic_class_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Bypass IP4/IP6 lookup
+ *
+ * @cliexpar
+ * @cliexstart{map params pre-resolve}
+ *
+ * Bypass a second FIB lookup of the translated or encapsulated
+ * packet, and forward the packet directly to the specified
+ * next-hop. This optimization trades forwarding flexibility for
+ * performance.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_pre_resolve_command, static) = {
   .path = "map params pre-resolve",
-  .short_help = 
-  "pre-resolve {ip4-nh <address>} | {ip6-nh <address>}",
+  .short_help = " map params pre-resolve {ip4-nh <address>} "
+                "| {ip6-nh <address>}",
   .function = map_pre_resolve_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Enable or disable the MAP-E inbound security check
+ *
+ * @cliexpar
+ * @cliexstart{map params security-check}
+ *
+ * By default, a decapsulated packet's IPv4 source address will be
+ * verified against the outer header's IPv6 source address. Disabling
+ * this feature will allow IPv4 source address spoofing.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_security_check_command, static) = {
   .path = "map params security-check",
-  .short_help = 
-  "security-check on|off",
+  .short_help = "map params security-check on|off",
   .function = map_security_check_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Specifiy the IPv4 source address used for relayed ICMP error messages
+ *
+ * @cliexpar
+ * @cliexstart{map params icmp source-address}
+ *
+ * This command specifies which IPv4 source address (must be local to
+ * the system), that is used for relayed received IPv6 ICMP error
+ * messages.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_icmp_relay_source_address_command, static) = {
   .path = "map params icmp source-address",
-   .short_help = "source-address <ip4-address>",
+  .short_help = "map params icmp source-address <ip4-address>",
   .function = map_icmp_relay_source_address_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Send IPv6 ICMP unreachables
+ *
+ * @cliexpar
+ * @cliexstart{map params icmp6 unreachables}
+ *
+ * Send IPv6 ICMP unreachable messages back if security check fails or
+ * no MAP domain exists.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_icmp_unreachables_command, static) = {
   .path = "map params icmp6 unreachables",
-  .short_help = "unreachables {on|off}",
+  .short_help = "map params icmp6 unreachables {on|off}",
   .function = map_icmp_unreachables_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Configure MAP fragmentation behaviour
+ *
+ * @cliexpar
+ * @cliexstart{map params fragment}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_fragment_command, static) = {
   .path = "map params fragment",
-  .short_help = "[inner|outer] [ignore-df [on|off]]",
+  .short_help = "map params fragment inner|outer",
   .function = map_fragment_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Ignore the IPv4 Don't fragment bit
+ *
+ * @cliexpar
+ * @cliexstart{map params fragment ignore-df}
+ *
+ * Allows fragmentation of the IPv4 packet even if the DF bit is
+ * set. The choice between inner or outer fragmentation of tunnel
+ * packets is complicated. The benefit of inner fragmentation is that
+ * the ultimate endpoint must reassemble, instead of the tunnel
+ * endpoint.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_fragment_df_command, static) = {
   .path = "map params fragment ignore-df",
-  .short_help = "on|off",
+  .short_help = "map params fragment ignore-df on|off",
   .function = map_fragment_df_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Specifiy if the inbound security check should be done on fragments
+ *
+ * @cliexpar
+ * @cliexstart{map params security-check fragments}
+ *
+ * Typically the inbound on-decapsulation security check is only done
+ * on the first packet. The packet that contains the L4
+ * information. While a security check on every fragment is possible,
+ * it has a cost. State must be created on the first fragment.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_security_check_frag_command, static) = {
   .path = "map params security-check fragments",
-  .short_help = 
-  "fragments on|off",
+  .short_help = "map params security-check fragments on|off",
   .function = map_security_check_frag_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Add MAP domain
+ *
+ * @cliexpar
+ * @cliexstart{map add domain}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_add_domain_command, static) = {
   .path = "map add domain",
-  .short_help = 
-  "map add domain ip4-pfx <ip4-pfx> ip6-pfx <ip6-pfx> ip6-src <ip6-pfx> "
-      "ea-bits-len <n> psid-offset <n> psid-len <n> [map-t] [mtu <mtu>]",
+  .short_help = "map add domain ip4-pfx <ip4-pfx> ip6-pfx <ip6-pfx> "
+      "ip6-src <ip6-pfx> ea-bits-len <n> psid-offset <n> psid-len <n> "
+      "[map-t] [mtu <mtu>]",
   .function = map_add_domain_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Add MAP rule to a domain
+ *
+ * @cliexpar
+ * @cliexstart{map add rule}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_add_rule_command, static) = {
   .path = "map add rule",
-  .short_help = 
-  "map add rule index <domain> psid <psid> ip6-dst <ip6-addr>",
+  .short_help = "map add rule index <domain> psid <psid> ip6-dst <ip6-addr>",
   .function = map_add_rule_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Delete MAP domain
+ *
+ * @cliexpar
+ * @cliexstart{map del domain}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(map_del_command, static) = {
   .path = "map del domain",
-  .short_help = 
-  "map del domain index <domain>",
+  .short_help = "map del domain index <domain>",
   .function = map_del_domain_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Show MAP domains
+ *
+ * @cliexpar
+ * @cliexstart{show map domain}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(show_map_domain_command, static) = {
   .path = "show map domain",
+  .short_help = "show map domain index <n> [counters]",
   .function = show_map_domain_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Show MAP statistics
+ *
+ * @cliexpar
+ * @cliexstart{show map stats}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(show_map_stats_command, static) = {
   .path = "show map stats",
+  .short_help = "show map stats",
   .function = show_map_stats_command_fn,
 };
-/* *INDENT-ON* */
 
-/* *INDENT-OFF* */
+/*?
+ * Show MAP fragmentation information
+ *
+ * @cliexpar
+ * @cliexstart{show map fragments}
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND(show_map_fragments_command, static) = {
   .path = "show map fragments",
+  .short_help = "show map fragments",
   .function = show_map_fragments_command_fn,
 };
 /* *INDENT-ON* */
@@ -2035,6 +2142,8 @@ map_init (vlib_main_t * vm)
   mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
   map_ip6_reass_reinit (NULL, NULL);
 
+  map_dpo_module_init ();
+
   return 0;
 }