Skip to content

Commit

Permalink
ovn: Add a new OVN field icmp4.frag_mtu
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Mark Michelson <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
numansiddique authored and blp committed Apr 22, 2019
1 parent faa3beb commit 086470c
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 14 deletions.
27 changes: 26 additions & 1 deletion include/ovn/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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_*. */

Expand Down Expand Up @@ -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. */
Expand All @@ -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()). */
Expand Down
3 changes: 2 additions & 1 deletion include/ovn/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions include/ovn/expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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. */
Expand Down
39 changes: 39 additions & 0 deletions ovn/lib/logical-fields.h → include/ovn/logical-fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
1 change: 1 addition & 0 deletions ovn/controller/lflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,5 @@ lflow_destroy(void)
{
expr_symtab_destroy(&symtab);
shash_destroy(&symtab);
ovn_destroy_ovnfields();
}
2 changes: 1 addition & 1 deletion ovn/controller/lflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
* ==========================================
Expand Down
62 changes: 61 additions & 1 deletion ovn/controller/pinctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -3164,3 +3175,52 @@ pinctrl_handle_put_nd_ra_opts(
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);
}
}
46 changes: 44 additions & 2 deletions ovn/lib/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions ovn/lib/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
17 changes: 16 additions & 1 deletion ovn/lib/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 086470c

Please sign in to comment.