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"
