@@ -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()). */
@@ -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
@@ -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. */
similarity index 73%
rename from ovn/lib/logical-fields.h
rename to 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 */
@@ -484,4 +484,5 @@ lflow_destroy(void)
{
expr_symtab_destroy(&symtab);
shash_destroy(&symtab);
+ ovn_destroy_ovnfields();
}
@@ -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
* ==========================================
@@ -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"
@@ -199,6 +199,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);
@@ -1675,6 +1681,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));
@@ -3164,3 +3175,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);
+ }
+}
@@ -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)
@@ -16,8 +16,7 @@ ovn_lib_libovn_la_SOURCES = \
ovn/lib/ovn-l7.h \
ovn/lib/ovn-util.c \
ovn/lib/ovn-util.h \
- ovn/lib/logical-fields.c \
- ovn/lib/logical-fields.h
+ ovn/lib/logical-fields.c
nodist_ovn_lib_libovn_la_SOURCES = \
ovn/lib/ovn-nb-idl.c \
ovn/lib/ovn-nb-idl.h \
@@ -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)
@@ -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);
}
@@ -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"
@@ -1103,6 +1103,39 @@
</p>
</dd>
+ <dt><code><var>ovn_field</var> = <var>constant</var>;</code></dt>
+ <dd>
+ <p>
+ Sets OVN field <var>ovn_field</var> to constant value
+ <var>constant</var>.
+ </p>
+
+ <p>
+ <code>OVN</code> supports setting the values of certain fields
+ which are not yet supported in OpenFlow to set or modify them.
+ </p>
+
+ <p>
+ Below are the supported <code>OVN fields</code>:
+ </p>
+
+ <ul>
+ <li>
+ <code>icmp4.frag_mtu</code>
+ <p>
+ This field sets the low-order 16 bits of the ICMP4 header field
+ that is labelled "unused" in the ICMP specification as defined
+ in the RFC 1191 with the value specified in
+ <var>constant</var>.
+ </p>
+
+ <p>
+ Eg. icmp4.frag_mtu = 1500;
+ </p>
+ </li>
+ </ul>
+ </dd>
+
<dt><code><var>field1</var> = <var>field2</var>;</code></dt>
<dd>
<p>
@@ -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;
}
}
@@ -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)
@@ -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"