[ovs-dev,v2,3/6] ovn: Add a new OVN field icmp4.frag_mtu
diff mbox series

Message ID 20190412130543.28527-1-nusiddiq@redhat.com
State Superseded
Headers show
Series
  • Address MTU issue for larger packets in OVN
Related show

Commit Message

Numan Siddique April 12, 2019, 1:05 p.m. UTC
From: Numan Siddique <nusiddiq@redhat.com>

In order to support OVN specific fields (which are not yet
supported in OpenvSwitch to set or modify values) a generic
OVN field support is added in this patch. These OVN fields
gets translated to controller actions.

This patch adds only one field for now - icmp4.frag_mtu.
It should be fairly straightforward to add similar fields in the
near future.

Example usage.
action=(icmp4 {"eth.dst <-> eth.src; "
        "icmp4.type = 3; /* Destination Unreachable */ "
        "icmp4.code = 4; /* Fragmentation Needed */ "
         icmp4.frag_mtu = 1442;
         ...
         "next; };")

action=(icmp4.frag_mtu = 1500; ..)

pinctrl module of ovn-controller will set the specified value
in the the low-order 16 bits of the ICMP4 header field that is
labelled "unused" in the ICMP specification as defined in the RFC 1191.

Upcoming patch will use it to send an icmp4 packet if the
source IPv4 packet destined to go via external gateway needs to
be fragmented.

Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
---
 include/ovn/actions.h                     | 27 +++++++++-
 include/ovn/automake.mk                   |  3 +-
 include/ovn/expr.h                        |  5 ++
 {ovn/lib => include/ovn}/logical-fields.h | 39 ++++++++++++++
 ovn/controller/lflow.c                    |  1 +
 ovn/controller/lflow.h                    |  2 +-
 ovn/controller/pinctrl.c                  | 62 ++++++++++++++++++++++-
 ovn/lib/actions.c                         | 46 ++++++++++++++++-
 ovn/lib/automake.mk                       |  1 -
 ovn/lib/expr.c                            | 17 ++++++-
 ovn/lib/logical-fields.c                  | 36 ++++++++++++-
 ovn/northd/ovn-northd.c                   |  2 +-
 ovn/utilities/ovn-trace.c                 | 25 ++++++++-
 tests/ovn.at                              |  8 +++
 tests/test-ovn.c                          |  2 +-
 15 files changed, 263 insertions(+), 13 deletions(-)
 rename {ovn/lib => include/ovn}/logical-fields.h (73%)

Patch
diff mbox series

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 1c0c67ce6..89e28c50c 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -80,7 +80,8 @@  struct ovn_extend_table;
     OVNACT(LOG,               ovnact_log)             \
     OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)        \
     OVNACT(ND_NS,             ovnact_nest)            \
-    OVNACT(SET_METER,         ovnact_set_meter)
+    OVNACT(SET_METER,         ovnact_set_meter)       \
+    OVNACT(OVNFIELD_LOAD,     ovnact_load)
 
 /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ovnact_type {
@@ -142,6 +143,20 @@  ovnact_end(const struct ovnact *ovnacts, size_t ovnacts_len)
 #define OVNACT_FOR_EACH(POS, OVNACTS, OVNACTS_LEN)                      \
     for ((POS) = (OVNACTS); (POS) < ovnact_end(OVNACTS, OVNACTS_LEN);  \
          (POS) = ovnact_next(POS))
+
+static inline int
+ovnacts_count(const struct ovnact *ovnacts, size_t ovnacts_len)
+{
+    uint8_t n_ovnacts = 0;
+    if (ovnacts) {
+        const struct ovnact *a;
+
+        OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
+            n_ovnacts++;
+        }
+    }
+    return n_ovnacts;
+}
 
 /* Action structure for each OVNACT_*. */
 
@@ -452,6 +467,10 @@  enum action_opcode {
         * The actions, in OpenFlow 1.3 format, follow the action_header.
         */
     ACTION_OPCODE_ND_NA_ROUTER,
+
+     /* MTU value (to put in the icmp4 header field - frag_mtu) follow the
+     * action header. */
+    ACTION_OPCODE_PUT_ICMP4_FRAG_MTU,
 };
 
 /* Header. */
@@ -461,6 +480,12 @@  struct action_header {
 };
 BUILD_ASSERT_DECL(sizeof(struct action_header) == 8);
 
+OVS_PACKED(
+struct ovnfield_act_header {
+    ovs_be16 id; /* one of enum ovnfield_id. */
+    ovs_be16 len; /* Length of the ovnfield data. */
+});
+
 struct ovnact_parse_params {
     /* A table of "struct expr_symbol"s to support (as one would provide to
      * expr_parse()). */
diff --git a/include/ovn/automake.mk b/include/ovn/automake.mk
index d2924c2f6..54b0e2c0e 100644
--- a/include/ovn/automake.mk
+++ b/include/ovn/automake.mk
@@ -2,4 +2,5 @@  ovnincludedir = $(includedir)/ovn
 ovninclude_HEADERS = \
 	include/ovn/actions.h \
 	include/ovn/expr.h \
-	include/ovn/lex.h
+	include/ovn/lex.h  \
+	include/ovn/logical-fields.h
diff --git a/include/ovn/expr.h b/include/ovn/expr.h
index 3995e62f0..a68fd6e1c 100644
--- a/include/ovn/expr.h
+++ b/include/ovn/expr.h
@@ -58,6 +58,7 @@ 
 #include "openvswitch/list.h"
 #include "openvswitch/match.h"
 #include "openvswitch/meta-flow.h"
+#include "logical-fields.h"
 
 struct ds;
 struct expr;
@@ -244,6 +245,7 @@  struct expr_symbol {
     int width;
 
     const struct mf_field *field;     /* Fields only, otherwise NULL. */
+    const struct ovn_field *ovn_field;  /* OVN Fields only, otherwise NULL. */
     const struct expr_symbol *parent; /* Subfields only, otherwise NULL. */
     int parent_ofs;                   /* Subfields only, otherwise 0. */
     char *predicate;                  /* Predicates only, otherwise NULL. */
@@ -284,6 +286,9 @@  struct expr_symbol *expr_symtab_add_string(struct shash *symtab,
 struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab,
                                               const char *name,
                                               const char *expansion);
+struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab,
+                                              const char *name,
+                                              enum ovn_field_id id);
 void expr_symtab_destroy(struct shash *symtab);
 
 /* Expression type. */
diff --git a/ovn/lib/logical-fields.h b/include/ovn/logical-fields.h
similarity index 73%
rename from ovn/lib/logical-fields.h
rename to include/ovn/logical-fields.h
index 95759a8bb..164b338b5 100644
--- a/ovn/lib/logical-fields.h
+++ b/include/ovn/logical-fields.h
@@ -81,4 +81,43 @@  enum mff_log_flags {
     MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT),
 };
 
+/* OVN logical fields
+ * ===================
+ * These are the fields which OVN supports modifying which gets translated
+ * to OFFlow controller action.
+ *
+ * OpenvSwitch doesn't support modifying these fields yet. If a field is
+ * supported later by OpenvSwitch, it can be deleted from here.
+ */
+
+enum ovn_field_id {
+    /*
+     * Name: "icmp4.frag_mtu" -
+     * Type: be16
+     * Description: Sets the low-order 16 bits of the ICMP4 header field
+     * (that is labelled "unused" in the ICMP specification) of the ICMP4
+     * packet as per the RFC 1191.
+     */
+    OVN_ICMP4_FRAG_MTU,
+
+    OVN_FIELD_N_IDS
+};
+
+struct ovn_field {
+    enum ovn_field_id id;
+    const char *name;
+    unsigned int n_bytes;       /* Width of the field in bytes. */
+    unsigned int n_bits;        /* Number of significant bits in field. */
+};
+
+static inline const struct ovn_field *
+ovn_field_from_id(enum ovn_field_id id)
+{
+    extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS];
+    ovs_assert((unsigned int) id < OVN_FIELD_N_IDS);
+    return &ovn_fields[id];
+}
+
+const struct ovn_field *ovn_field_from_name(const char *name);
+void ovn_destroy_ovnfields(void);
 #endif /* ovn/lib/logical-fields.h */
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 8db81927e..432edbc70 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -495,4 +495,5 @@  lflow_destroy(void)
 {
     expr_symtab_destroy(&symtab);
     shash_destroy(&symtab);
+    ovn_destroy_ovnfields();
 }
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index d19338140..efa066737 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -16,7 +16,7 @@ 
 #ifndef OVN_LFLOW_H
 #define OVN_LFLOW_H 1
 
-#include "ovn/lib/logical-fields.h"
+#include "ovn/logical-fields.h"
 
 /* Logical_Flow table translation to OpenFlow
  * ==========================================
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 58bee8726..63202e6d4 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -43,9 +43,9 @@ 
 #include "ovn/actions.h"
 #include "ovn/lex.h"
 #include "ovn/lib/acl-log.h"
-#include "ovn/lib/logical-fields.h"
 #include "ovn/lib/ovn-l7.h"
 #include "ovn/lib/ovn-util.h"
+#include "ovn/logical-fields.h"
 #include "openvswitch/poll-loop.h"
 #include "openvswitch/rconn.h"
 #include "socket-util.h"
@@ -200,6 +200,12 @@  static void pinctrl_handle_nd_ns(struct rconn *swconn,
                                  struct dp_packet *pkt_in,
                                  const struct match *md,
                                  struct ofpbuf *userdata);
+static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
+                                              const struct flow *in_flow,
+                                              struct dp_packet *pkt_in,
+                                              struct ofputil_packet_in *pin,
+                                              struct ofpbuf *userdata,
+                                              struct ofpbuf *continuation);
 static void init_ipv6_ras(void);
 static void destroy_ipv6_ras(void);
 static void ipv6_ra_wait(long long int send_ipv6_ra_time);
@@ -1676,6 +1682,11 @@  process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
                                  &userdata);
         break;
 
+    case ACTION_OPCODE_PUT_ICMP4_FRAG_MTU:
+        pinctrl_handle_put_icmp4_frag_mtu(swconn, &headers, &packet,
+                                          &pin, &userdata, &continuation);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -3178,3 +3189,52 @@  exit:
     queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
     dp_packet_uninit(pkt_out_ptr);
 }
+
+/* Called with in the pinctrl_handler thread context. */
+static void
+pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
+                                  const struct flow *in_flow,
+                                  struct dp_packet *pkt_in,
+                                  struct ofputil_packet_in *pin,
+                                  struct ofpbuf *userdata,
+                                  struct ofpbuf *continuation)
+{
+    enum ofp_version version = rconn_get_version(swconn);
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+    struct dp_packet *pkt_out = NULL;
+
+    /* This action only works for ICMPv4 packets. */
+    if (!is_icmpv4(in_flow, NULL)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "put_icmp4_frag_mtu action on non-ICMPv4 packet");
+        goto exit;
+    }
+
+    ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
+    if (!mtu) {
+        goto exit;
+    }
+
+    pkt_out = dp_packet_clone(pkt_in);
+    pkt_out->l2_5_ofs = pkt_in->l2_5_ofs;
+    pkt_out->l2_pad_size = pkt_in->l2_pad_size;
+    pkt_out->l3_ofs = pkt_in->l3_ofs;
+    pkt_out->l4_ofs = pkt_in->l4_ofs;
+
+    struct ip_header *nh = dp_packet_l3(pkt_out);
+    struct icmp_header *ih = dp_packet_l4(pkt_out);
+    ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu;
+    ih->icmp_fields.frag.mtu = *mtu;
+    ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu);
+    nh->ip_csum = 0;
+    nh->ip_csum = csum(nh, sizeof *nh);
+
+    pin->packet = dp_packet_data(pkt_out);
+    pin->packet_len = dp_packet_size(pkt_out);
+
+exit:
+    queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
+    if (pkt_out) {
+        dp_packet_delete(pkt_out);
+    }
+}
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index eb7e5badd..e8940b091 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -23,7 +23,6 @@ 
 #include "ovn-l7.h"
 #include "hash.h"
 #include "lib/packets.h"
-#include "logical-fields.h"
 #include "nx-match.h"
 #include "openvswitch/dynamic-string.h"
 #include "openvswitch/hmap.h"
@@ -367,7 +366,13 @@  static void
 parse_LOAD(struct action_context *ctx, const struct expr_field *lhs)
 {
     size_t ofs = ctx->ovnacts->size;
-    struct ovnact_load *load = ovnact_put_LOAD(ctx->ovnacts);
+    struct ovnact_load *load;
+    if (lhs->symbol->ovn_field) {
+        load = ovnact_put_OVNFIELD_LOAD(ctx->ovnacts);
+    } else {
+        load = ovnact_put_LOAD(ctx->ovnacts);
+    }
+
     load->dst = *lhs;
 
     char *error = expr_type_check(lhs, lhs->n_bits, true);
@@ -2297,6 +2302,43 @@  ovnact_set_meter_free(struct ovnact_set_meter *ct OVS_UNUSED)
 {
 }
 
+static void
+format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s)
+{
+    const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
+    switch (f->id) {
+    case OVN_ICMP4_FRAG_MTU:
+        ds_put_format(s, "%s = %u;", f->name,
+                      ntohs(load->imm.value.be16_int));
+        break;
+
+    case OVN_FIELD_N_IDS:
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
+static void
+encode_OVNFIELD_LOAD(const struct ovnact_load *load,
+            const struct ovnact_encode_params *ep OVS_UNUSED,
+            struct ofpbuf *ofpacts)
+{
+    const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
+    switch (f->id) {
+    case OVN_ICMP4_FRAG_MTU: {
+        size_t oc_offset = encode_start_controller_op(
+            ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, NX_CTLR_NO_METER,
+            ofpacts);
+        ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16));
+        encode_finish_controller_op(oc_offset, ofpacts);
+        break;
+    }
+    case OVN_FIELD_N_IDS:
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
 /* Parses an assignment or exchange or put_dhcp_opts action. */
 static void
 parse_set_action(struct action_context *ctx)
diff --git a/ovn/lib/automake.mk b/ovn/lib/automake.mk
index 6178fc2d5..f8a5b5f0f 100644
--- a/ovn/lib/automake.mk
+++ b/ovn/lib/automake.mk
@@ -17,7 +17,6 @@  ovn_lib_libovn_la_SOURCES = \
 	ovn/lib/ovn-util.c \
 	ovn/lib/ovn-util.h \
 	ovn/lib/logical-fields.c \
-	ovn/lib/logical-fields.h
 nodist_ovn_lib_libovn_la_SOURCES = \
 	ovn/lib/ovn-nb-idl.c \
 	ovn/lib/ovn-nb-idl.h \
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index 8cfdf34fa..e21206120 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -17,7 +17,6 @@ 
 #include <config.h>
 #include "byte-order.h"
 #include "openvswitch/json.h"
-#include "logical-fields.h"
 #include "nx-match.h"
 #include "openvswitch/dynamic-string.h"
 #include "openvswitch/match.h"
@@ -26,6 +25,7 @@ 
 #include "openvswitch/shash.h"
 #include "ovn/expr.h"
 #include "ovn/lex.h"
+#include "ovn/logical-fields.h"
 #include "simap.h"
 #include "sset.h"
 #include "util.h"
@@ -1381,6 +1381,8 @@  expr_symbol_format(const struct expr_symbol *symbol, struct ds *s)
         expr_field_format(&f, s);
     } else if (symbol->predicate) {
         ds_put_cstr(s, symbol->predicate);
+    } else if (symbol->ovn_field) {
+        ds_put_cstr(s, symbol->name);
     } else {
         nx_format_field_name(symbol->field->id, OFP13_VERSION, s);
     }
@@ -1556,6 +1558,19 @@  expr_symtab_add_predicate(struct shash *symtab, const char *name,
     return symbol;
 }
 
+struct expr_symbol *
+expr_symtab_add_ovn_field(struct shash *symtab, const char *name,
+                          enum ovn_field_id id)
+{
+    const struct ovn_field *ovn_field = ovn_field_from_id(id);
+    struct expr_symbol *symbol;
+
+    symbol = add_symbol(symtab, name, ovn_field->n_bits, NULL,
+                        EXPR_L_NOMINAL, false, true);
+    symbol->ovn_field = ovn_field;
+    return symbol;
+}
+
 /* Destroys 'symtab' and all of its symbols. */
 void
 expr_symtab_destroy(struct shash *symtab)
diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c
index a8b5e3c51..579537d6b 100644
--- a/ovn/lib/logical-fields.c
+++ b/ovn/lib/logical-fields.c
@@ -15,13 +15,25 @@ 
 
 #include <config.h>
 
-#include "logical-fields.h"
-
 #include "openvswitch/shash.h"
 #include "ovn/expr.h"
+#include "ovn/logical-fields.h"
 #include "ovs-thread.h"
 #include "packets.h"
 
+/* Silence a warning. */
+extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS];
+
+const struct ovn_field ovn_fields[OVN_FIELD_N_IDS] = {
+    {
+        OVN_ICMP4_FRAG_MTU,
+        "icmp4.frag_mtu",
+        2, 16,
+    },
+};
+
+static struct shash ovnfield_by_name;
+
 static void
 add_subregister(const char *name,
                 const char *parent_name, int parent_idx,
@@ -203,4 +215,24 @@  ovn_init_symtab(struct shash *symtab)
     expr_symtab_add_predicate(symtab, "sctp", "ip.proto == 132");
     expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
     expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
+
+    shash_init(&ovnfield_by_name);
+    for (int i = 0; i < OVN_FIELD_N_IDS; i++) {
+        const struct ovn_field *of = &ovn_fields[i];
+        ovs_assert(of->id == i); /* Fields must be in the enum order. */
+        shash_add_once(&ovnfield_by_name, of->name, of);
+    }
+    expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU);
+}
+
+const struct ovn_field *
+ovn_field_from_name(const char *name)
+{
+    return shash_find_data(&ovnfield_by_name, name);
+}
+
+void
+ovn_destroy_ovnfields(void)
+{
+    shash_destroy(&ovnfield_by_name);
 }
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 05b8aad4f..5614f9fa3 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -29,12 +29,12 @@ 
 #include "openvswitch/json.h"
 #include "ovn/lex.h"
 #include "ovn/lib/chassis-index.h"
-#include "ovn/lib/logical-fields.h"
 #include "ovn/lib/ovn-l7.h"
 #include "ovn/lib/ovn-nb-idl.h"
 #include "ovn/lib/ovn-sb-idl.h"
 #include "ovn/lib/ovn-util.h"
 #include "ovn/actions.h"
+#include "ovn/logical-fields.h"
 #include "packets.h"
 #include "openvswitch/poll-loop.h"
 #include "smap.h"
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 8899496a5..e03179c8f 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -35,8 +35,8 @@ 
 #include "ovn/actions.h"
 #include "ovn/expr.h"
 #include "ovn/lex.h"
+#include "ovn/logical-fields.h"
 #include "ovn/lib/acl-log.h"
-#include "ovn/lib/logical-fields.h"
 #include "ovn/lib/ovn-l7.h"
 #include "ovn/lib/ovn-sb-idl.h"
 #include "ovn/lib/ovn-util.h"
@@ -1938,6 +1938,25 @@  execute_log(const struct ovnact_log *log, struct flow *uflow,
     free(packet_str);
 }
 
+static void
+execute_ovnfield_load(const struct ovnact_load *load,
+                      struct ovs_list *super)
+{
+    const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
+    switch (f->id) {
+    case OVN_ICMP4_FRAG_MTU: {
+        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
+                             "icmp4.frag_mtu = %u",
+                             ntohs(load->imm.value.be16_int));
+        break;
+    }
+
+    case OVN_FIELD_N_IDS:
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
 static void
 trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
               const struct ovntrace_datapath *dp, struct flow *uflow,
@@ -2106,6 +2125,10 @@  trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
             execute_tcp_reset(ovnact_get_TCP_RESET(a), dp, uflow, table_id,
                               pipeline, super);
             break;
+
+        case OVNACT_OVNFIELD_LOAD:
+            execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super);
+            break;
         }
 
     }
diff --git a/tests/ovn.at b/tests/ovn.at
index e7746cb0f..02ab94a87 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1290,6 +1290,14 @@  icmp4 { };
     encodes as controller(userdata=00.00.00.0a.00.00.00.00)
     has prereqs ip4
 
+# icmp4 with icmp4.frag_mtu
+icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output;
+    encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
+    has prereqs ip4
+
+icmp4.frag_mtu = 1500;
+    encodes as controller(userdata=00.00.00.0d.00.00.00.00.05.dc,pause)
+
 # icmp6
 icmp6 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
     encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 9e3112bf9..7cce9c2ae 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -31,7 +31,7 @@ 
 #include "ovn/actions.h"
 #include "ovn/expr.h"
 #include "ovn/lex.h"
-#include "ovn/lib/logical-fields.h"
+#include "ovn/logical-fields.h"
 #include "ovn/lib/ovn-l7.h"
 #include "ovn/lib/extend-table.h"
 #include "ovs-thread.h"