release.
* New unixctl "ofctl/packet-out" command, which can be used to
instruct a flow monitor to issue OpenFlow packet-out messages.
+ - Tunnels:
+ * TLV mappings for protocols such as Geneve are now segregated on
+ a per-OpenFlow bridge basis rather than globally. (The interface
+ has not changed.)
v2.6.0 - xx xxx xxxx
---------------------
bool minimatch_matches_flow(const struct minimatch *, const struct flow *);
-void minimatch_format(const struct minimatch *, struct ds *, int priority);
+void minimatch_format(const struct minimatch *, const struct tun_table *,
+ struct ds *, int priority);
char *minimatch_to_string(const struct minimatch *, int priority);
#endif /* match.h */
OVS_WARN_UNUSED_RESULT;
char *parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
- const char *s, const struct simap *portno_names);
+ const struct tun_table *tun_table, const char *s,
+ const struct simap *portno_names);
char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
int command,
void ofputil_match_to_ofp10_match(const struct match *, struct ofp10_match *);
/* Work with ofp11_match. */
-enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, struct match *,
+enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, const struct tun_table *,
+ struct match *,
uint16_t *padded_match_len);
enum ofperr ofputil_pull_ofp11_mask(struct ofpbuf *, struct match *,
struct mf_bitmap *bm);
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *,
enum ofputil_protocol,
+ const struct tun_table *,
struct ofpbuf *ofpacts,
ofp_port_t max_port,
uint8_t max_table);
};
enum ofperr ofputil_decode_flow_stats_request(
- struct ofputil_flow_stats_request *, const struct ofp_header *);
+ struct ofputil_flow_stats_request *, const struct ofp_header *,
+ const struct tun_table *);
struct ofpbuf *ofputil_encode_flow_stats_request(
const struct ofputil_flow_stats_request *, enum ofputil_protocol);
bool flow_age_extension,
struct ofpbuf *ofpacts);
void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
- struct ovs_list *replies);
+ struct ovs_list *replies,
+ const struct tun_table *);
/* Aggregate stats reply, independent of protocol. */
struct ofputil_aggregate_stats {
void ofputil_packet_in_destroy(struct ofputil_packet_in *);
enum ofperr ofputil_decode_packet_in(const struct ofp_header *, bool loose,
+ const struct tun_table *,
struct ofputil_packet_in *,
size_t *total_len, uint32_t *buffer_id,
struct ofpbuf *continuation);
enum ofperr ofputil_decode_packet_in_private(
const struct ofp_header *, bool loose,
+ const struct tun_table *,
struct ofputil_packet_in_private *,
size_t *total_len, uint32_t *buffer_id);
uint64_t map; /* 1-bit for each present TLV. */
uint8_t len; /* Length of data in 'opts'. */
} present;
- struct tun_table *tab; /* Types & lengths for 'opts' and 'opt_map'. */
+ const struct tun_table *tab; /* Types & lengths for 'opts' and 'opt_map'. */
#if UINTPTR_MAX == UINT32_MAX
uint8_t pad[4]; /* Pad to 64-bit boundary. */
};
/* Allocation of options inside struct match. This is important if we don't
- * have access to a global allocation table - either because there isn't one
+ * have access to an allocation table - either because there isn't one
* (ovs-ofctl) or if we need to keep the allocation outside of packet
* processing context (Packet-In). These structures never have dynamically
* allocated memory because the address space is never fragmented. */
/* Appends a string describing 'rule' to 's'. */
void
-cls_rule_format(const struct cls_rule *rule, struct ds *s)
+cls_rule_format(const struct cls_rule *rule, const struct tun_table *tun_table,
+ struct ds *s)
{
- minimatch_format(&rule->match, s, rule->priority);
+ minimatch_format(&rule->match, tun_table, s, rule->priority);
}
/* Returns true if 'rule' matches every packet, false otherwise. */
/* Classifier rule properties. These are RCU protected and may run
* concurrently with modifiers and each other. */
bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
-void cls_rule_format(const struct cls_rule *, struct ds *);
+void cls_rule_format(const struct cls_rule *, const struct tun_table *,
+ struct ds *);
bool cls_rule_is_catchall(const struct cls_rule *);
bool cls_rule_is_loose_match(const struct cls_rule *rule,
const struct minimatch *criteria);
}
if (filter) {
- char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter, filter,
+ char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter, NULL, filter,
&names_portno);
if (err) {
dpctl_error(dpctl_p, 0, "Failed to parse filter (%s)", err);
struct minimatch minimatch;
odp_flow_key_to_flow(f.key, f.key_len, &flow);
- odp_flow_key_to_mask(f.mask, f.mask_len, f.key, f.key_len,
- &wc, &flow);
+ odp_flow_key_to_mask(f.mask, f.mask_len, &wc, &flow);
match_init(&match, &flow, &wc);
match_init(&match_filter, &flow_filter, &wc);
{
enum odp_key_fitness fitness;
- fitness = odp_flow_key_to_mask_udpif(mask_key, mask_key_len, key,
- key_len, wc, flow);
+ fitness = odp_flow_key_to_mask(mask_key, mask_key_len, wc, flow);
if (fitness) {
/* This should not happen: it indicates that
* odp_flow_key_from_mask() and odp_flow_key_to_mask()
{
odp_port_t in_port;
- if (odp_flow_key_to_flow_udpif(key, key_len, flow)) {
+ if (odp_flow_key_to_flow(key, key_len, flow)) {
/* This should not happen: it indicates that odp_flow_key_from_flow()
* and odp_flow_key_to_flow() disagree on the acceptable form of a
* flow. Log the problem as an error, with enough details to enable
struct ofpbuf *actions, struct ofpbuf *put_actions)
{
struct dp_netdev *dp = pmd->dp;
- struct flow_tnl orig_tunnel;
- int err;
if (OVS_UNLIKELY(!dp->upcall_cb)) {
return ENODEV;
}
- /* Upcall processing expects the Geneve options to be in the translated
- * format but we need to retain the raw format for datapath use. */
- orig_tunnel.flags = flow->tunnel.flags;
- if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
- orig_tunnel.metadata.present.len = flow->tunnel.metadata.present.len;
- memcpy(orig_tunnel.metadata.opts.gnv, flow->tunnel.metadata.opts.gnv,
- flow->tunnel.metadata.present.len);
- err = tun_metadata_from_geneve_udpif(&orig_tunnel, &orig_tunnel,
- &flow->tunnel);
- if (err) {
- return err;
- }
- }
-
if (OVS_UNLIKELY(!VLOG_DROP_DBG(&upcall_rl))) {
struct ds ds = DS_EMPTY_INITIALIZER;
char *packet_str;
ds_destroy(&ds);
}
- err = dp->upcall_cb(packet_, flow, ufid, pmd->core_id, type, userdata,
- actions, wc, put_actions, dp->upcall_aux);
- if (err && err != ENOSPC) {
- return err;
- }
-
- /* Translate tunnel metadata masks to datapath format. */
- if (wc) {
- if (wc->masks.tunnel.metadata.present.map) {
- struct geneve_opt opts[TLV_TOT_OPT_SIZE /
- sizeof(struct geneve_opt)];
-
- if (orig_tunnel.flags & FLOW_TNL_F_UDPIF) {
- tun_metadata_to_geneve_udpif_mask(&flow->tunnel,
- &wc->masks.tunnel,
- orig_tunnel.metadata.opts.gnv,
- orig_tunnel.metadata.present.len,
- opts);
- } else {
- orig_tunnel.metadata.present.len = 0;
- }
-
- memset(&wc->masks.tunnel.metadata, 0,
- sizeof wc->masks.tunnel.metadata);
- memcpy(&wc->masks.tunnel.metadata.opts.gnv, opts,
- orig_tunnel.metadata.present.len);
- }
- wc->masks.tunnel.metadata.present.len = 0xff;
- }
-
- /* Restore tunnel metadata. We need to use the saved options to ensure
- * that any unknown options are not lost. The generated mask will have
- * the same structure, matching on types and lengths but wildcarding
- * option data we don't care about. */
- if (orig_tunnel.flags & FLOW_TNL_F_UDPIF) {
- memcpy(&flow->tunnel.metadata.opts.gnv, orig_tunnel.metadata.opts.gnv,
- orig_tunnel.metadata.present.len);
- flow->tunnel.metadata.present.len = orig_tunnel.metadata.present.len;
- flow->tunnel.flags |= FLOW_TNL_F_UDPIF;
- }
-
- return err;
+ return dp->upcall_cb(packet_, flow, ufid, pmd->core_id, type, userdata,
+ actions, wc, put_actions, dp->upcall_aux);
}
static inline uint32_t
wc->masks.tunnel.metadata.present.map =
flow->tunnel.metadata.present.map;
WC_MASK_FIELD(wc, tunnel.metadata.opts.u8);
+ WC_MASK_FIELD(wc, tunnel.metadata.tab);
}
} else {
WC_MASK_FIELD(wc, tunnel.metadata.present.len);
struct dp_packet pkt;
struct flow flow;
- error = ofputil_decode_packet_in(oh, true, &pi, NULL, &buffer_id, NULL);
+ error = ofputil_decode_packet_in(oh, true, NULL, &pi, NULL,
+ &buffer_id, NULL);
if (error) {
VLOG_WARN_RL(&rl, "failed to decode packet-in: %s",
ofperr_to_string(error));
/* Appends a string representation of 'match' to 's'. If 'priority' is
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
void
-minimatch_format(const struct minimatch *match, struct ds *s, int priority)
+minimatch_format(const struct minimatch *match,
+ const struct tun_table *tun_table,struct ds *s, int priority)
{
struct match megamatch;
minimatch_expand(match, &megamatch);
+ megamatch.flow.tunnel.metadata.tab = tun_table;
+
match_format(&megamatch, s, priority);
}
static enum ofperr
nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
- struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask)
+ struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask,
+ const struct tun_table *tun_table)
{
ovs_assert((cookie != NULL) == (cookie_mask != NULL));
match_init_catchall(match);
+ match->flow.tunnel.metadata.tab = tun_table;
if (cookie) {
*cookie = *cookie_mask = htonll(0);
}
}
}
+ match->flow.tunnel.metadata.tab = NULL;
return 0;
}
static enum ofperr
nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
struct match *match,
- ovs_be64 *cookie, ovs_be64 *cookie_mask)
+ ovs_be64 *cookie, ovs_be64 *cookie_mask,
+ const struct tun_table *tun_table)
{
uint8_t *p = NULL;
}
}
- return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask);
+ return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask,
+ tun_table);
}
/* Parses the nx_match formatted match description in 'b' with length
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match,
- ovs_be64 *cookie, ovs_be64 *cookie_mask)
+ ovs_be64 *cookie, ovs_be64 *cookie_mask,
+ const struct tun_table *tun_table)
{
- return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask);
+ return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask,
+ tun_table);
}
/* Behaves the same as nx_pull_match(), but skips over unknown NXM headers,
enum ofperr
nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len,
struct match *match,
- ovs_be64 *cookie, ovs_be64 *cookie_mask)
+ ovs_be64 *cookie, ovs_be64 *cookie_mask,
+ const struct tun_table *tun_table)
{
- return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask);
+ return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask,
+ tun_table);
}
static enum ofperr
-oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
+oxm_pull_match__(struct ofpbuf *b, bool strict,
+ const struct tun_table *tun_table, struct match *match)
{
struct ofp11_match_header *omh = b->data;
uint8_t *p;
}
return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
- strict, match, NULL, NULL);
+ strict, match, NULL, NULL, tun_table);
}
/* Parses the oxm formatted match description preceded by a struct
*
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
-oxm_pull_match(struct ofpbuf *b, struct match *match)
+oxm_pull_match(struct ofpbuf *b, const struct tun_table *tun_table,
+ struct match *match)
{
- return oxm_pull_match__(b, true, match);
+ return oxm_pull_match__(b, true, tun_table, match);
}
/* Behaves the same as oxm_pull_match() with one exception. Skips over unknown
* OXM headers instead of failing with an error when they are encountered. */
enum ofperr
-oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
+oxm_pull_match_loose(struct ofpbuf *b, const struct tun_table *tun_table,
+ struct match *match)
{
- return oxm_pull_match__(b, false, match);
+ return oxm_pull_match__(b, false, tun_table, match);
}
/* Parses the OXM match description in the 'oxm_len' bytes in 'oxm'. Stores
*
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
-oxm_decode_match(const void *oxm, size_t oxm_len, struct match *match)
+oxm_decode_match(const void *oxm, size_t oxm_len,
+ const struct tun_table *tun_table, struct match *match)
{
- return nx_pull_raw(oxm, oxm_len, true, match, NULL, NULL);
+ return nx_pull_raw(oxm, oxm_len, true, match, NULL, NULL, tun_table);
}
/* Verify an array of OXM TLVs treating value of each TLV as a mask,
/* Decoding matches. */
enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len,
struct match *,
- ovs_be64 *cookie, ovs_be64 *cookie_mask);
+ ovs_be64 *cookie, ovs_be64 *cookie_mask,
+ const struct tun_table *);
enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len,
struct match *, ovs_be64 *cookie,
- ovs_be64 *cookie_mask);
-enum ofperr oxm_pull_match(struct ofpbuf *, struct match *);
-enum ofperr oxm_pull_match_loose(struct ofpbuf *, struct match *);
-enum ofperr oxm_decode_match(const void *, size_t, struct match *);
+ ovs_be64 *cookie_mask,
+ const struct tun_table *);
+enum ofperr oxm_pull_match(struct ofpbuf *, const struct tun_table *,
+ struct match *);
+enum ofperr oxm_pull_match_loose(struct ofpbuf *, const struct tun_table *,
+ struct match *);
+enum ofperr oxm_decode_match(const void *, size_t, const struct tun_table *,
+ struct match *);
enum ofperr oxm_pull_field_array(const void *, size_t fields_len,
struct field_array *);
{
enum odp_key_fitness fitness;
- fitness = odp_tun_key_from_attr(a, true, tun_key);
+ fitness = odp_tun_key_from_attr(a, tun_key);
ovs_assert(fitness != ODP_FIT_ERROR);
}
}
static enum odp_key_fitness
-odp_tun_key_from_attr__(const struct nlattr *attr,
- const struct nlattr *flow_attrs, size_t flow_attr_len,
- const struct flow_tnl *src_tun, struct flow_tnl *tun,
- bool udpif)
+odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,
+ struct flow_tnl *tun)
{
unsigned int left;
const struct nlattr *a;
break;
}
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
- if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len,
- src_tun, udpif, tun)) {
- return ODP_FIT_ERROR;
- }
+ tun_metadata_from_geneve_nlattr(a, is_mask, tun);
break;
default:
}
enum odp_key_fitness
-odp_tun_key_from_attr(const struct nlattr *attr, bool udpif,
- struct flow_tnl *tun)
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
{
memset(tun, 0, sizeof *tun);
- return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun, udpif);
+ return odp_tun_key_from_attr__(attr, false, tun);
}
static void
case OVS_KEY_ATTR_TUNNEL: {
enum odp_key_fitness res;
- res = odp_tun_key_from_attr(nla, true, &md->tunnel);
+ res = odp_tun_key_from_attr(nla, &md->tunnel);
if (res == ODP_FIT_ERROR) {
memset(&md->tunnel, 0, sizeof md->tunnel);
} else if (res == ODP_FIT_PERFECT) {
static enum odp_key_fitness
odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
- const struct nlattr *src_key, size_t src_key_len,
- struct flow *flow, const struct flow *src_flow,
- bool udpif)
+ struct flow *flow, const struct flow *src_flow)
{
const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
uint64_t expected_attrs;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
enum odp_key_fitness res;
- res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL],
- is_mask ? src_key : NULL,
- src_key_len, &src_flow->tunnel,
- &flow->tunnel, udpif);
+ res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], is_mask,
+ &flow->tunnel);
if (res == ODP_FIT_ERROR) {
return ODP_FIT_ERROR;
} else if (res == ODP_FIT_PERFECT) {
odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
struct flow *flow)
{
- return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow, false);
+ return odp_flow_key_to_flow__(key, key_len, flow, flow);
}
-static enum odp_key_fitness
-odp_flow_key_to_mask__(const struct nlattr *mask_key, size_t mask_key_len,
- const struct nlattr *flow_key, size_t flow_key_len,
- struct flow_wildcards *mask,
- const struct flow *src_flow,
- bool udpif)
+/* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key'
+ * to a mask structure in 'mask'. 'flow' must be a previously translated flow
+ * corresponding to 'mask' and similarly flow_key/flow_key_len must be the
+ * attributes from that flow. Returns an ODP_FIT_* value that indicates how
+ * well 'key' fits our expectations for what a flow key should contain. */
+enum odp_key_fitness
+odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
+ struct flow_wildcards *mask, const struct flow *src_flow)
{
if (mask_key_len) {
return odp_flow_key_to_flow__(mask_key, mask_key_len,
- flow_key, flow_key_len,
- &mask->masks, src_flow, udpif);
+ &mask->masks, src_flow);
} else {
/* A missing mask means that the flow should be exact matched.
return ODP_FIT_PERFECT;
}
}
-/* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key'
- * to a mask structure in 'mask'. 'flow' must be a previously translated flow
- * corresponding to 'mask' and similarly flow_key/flow_key_len must be the
- * attributes from that flow. Returns an ODP_FIT_* value that indicates how
- * well 'key' fits our expectations for what a flow key should contain. */
-enum odp_key_fitness
-odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
- const struct nlattr *flow_key, size_t flow_key_len,
- struct flow_wildcards *mask, const struct flow *flow)
-{
- return odp_flow_key_to_mask__(mask_key, mask_key_len,
- flow_key, flow_key_len,
- mask, flow, false);
-}
-
-/* These functions are similar to their non-"_udpif" variants but output a
- * 'flow' that is suitable for fast-path packet processing.
- *
- * Some fields have different representation for flow setup and per-
- * packet processing (i.e. different between ofproto-dpif and userspace
- * datapath). In particular, with the non-"_udpif" functions, struct
- * tun_metadata is in the per-flow format (using 'present.map' and 'opts.u8');
- * with these functions, struct tun_metadata is in the per-packet format
- * (using 'present.len' and 'opts.gnv'). */
-enum odp_key_fitness
-odp_flow_key_to_flow_udpif(const struct nlattr *key, size_t key_len,
- struct flow *flow)
-{
- return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow, true);
-}
-
-enum odp_key_fitness
-odp_flow_key_to_mask_udpif(const struct nlattr *mask_key, size_t mask_key_len,
- const struct nlattr *flow_key, size_t flow_key_len,
- struct flow_wildcards *mask,
- const struct flow *flow)
-{
- return odp_flow_key_to_mask__(mask_key, mask_key_len,
- flow_key, flow_key_len,
- mask, flow, true);
-}
/* Returns 'fitness' as a string, for use in debug messages. */
const char *
uint32_t keybuf[DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4)];
};
-enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, bool udpif,
+enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
struct flow_tnl *);
int odp_ufid_from_string(const char *s_, ovs_u128 *ufid);
void odp_format_ufid(const ovs_u128 *ufid, struct ds *);
+
void odp_flow_format(const struct nlattr *key, size_t key_len,
const struct nlattr *mask, size_t mask_len,
const struct hmap *portno_names, struct ds *,
struct flow *);
enum odp_key_fitness odp_flow_key_to_mask(const struct nlattr *mask_key,
size_t mask_key_len,
- const struct nlattr *flow_key,
- size_t flow_key_len,
struct flow_wildcards *mask,
const struct flow *flow);
-enum odp_key_fitness odp_flow_key_to_flow_udpif(const struct nlattr *, size_t,
- struct flow *);
-enum odp_key_fitness odp_flow_key_to_mask_udpif(const struct nlattr *mask_key,
- size_t mask_key_len,
- const struct nlattr *flow_key,
- size_t flow_key_len,
- struct flow_wildcards *mask,
- const struct flow *flow);
-
const char *odp_key_fitness_to_string(enum odp_key_fitness);
void commit_odp_tunnel_action(const struct flow *, struct flow *base,
* problem. */
char *
parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
- const char *s, const struct simap *portno_names)
+ const struct tun_table *tun_table, const char *s,
+ const struct simap *portno_names)
{
char *pos, *key, *value_s;
char *error = NULL;
if (wc) {
memset(wc, 0, sizeof *wc);
}
+ flow->tunnel.metadata.tab = tun_table;
pos = copy = xstrdup(s);
while (ofputil_parse_key_value(&pos, &key, &value_s)) {
size_t total_len;
enum ofperr error;
- error = ofputil_decode_packet_in_private(oh, true,
+ error = ofputil_decode_packet_in_private(oh, true, NULL,
&pin, &total_len, &buffer_id);
if (error) {
ofp_print_error(string, error);
protocol = ofputil_protocol_set_tid(protocol, true);
ofpbuf_init(&ofpacts, 64);
- error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts,
+ error = ofputil_decode_flow_mod(&fm, oh, protocol, NULL, &ofpacts,
OFPP_MAX, 255);
if (error) {
ofpbuf_uninit(&ofpacts);
struct ofputil_flow_stats_request fsr;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&fsr, oh);
+ error = ofputil_decode_flow_stats_request(&fsr, oh, NULL);
if (error) {
ofp_print_error(string, error);
return;
}
enum ofperr
-ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match,
- uint16_t *padded_match_len)
+ofputil_pull_ofp11_match(struct ofpbuf *buf, const struct tun_table *tun_table,
+ struct match *match, uint16_t *padded_match_len)
{
struct ofp11_match_header *omh = buf->data;
uint16_t match_len;
if (padded_match_len) {
*padded_match_len = ROUND_UP(match_len, 8);
}
- return oxm_pull_match(buf, match);
+ return oxm_pull_match(buf, tun_table, match);
default:
return OFPERR_OFPBMC_BAD_TYPE;
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
enum ofputil_protocol protocol,
+ const struct tun_table *tun_table,
struct ofpbuf *ofpacts,
ofp_port_t max_port, uint8_t max_table)
{
ofm = ofpbuf_pull(&b, sizeof *ofm);
- error = ofputil_pull_ofp11_match(&b, &fm->match, NULL);
+ error = ofputil_pull_ofp11_match(&b, tun_table, &fm->match, NULL);
if (error) {
return error;
}
/* Dissect the message. */
nfm = ofpbuf_pull(&b, sizeof *nfm);
error = nx_pull_match(&b, ntohs(nfm->match_len),
- &fm->match, &fm->cookie, &fm->cookie_mask);
+ &fm->match, &fm->cookie, &fm->cookie_mask,
+ tun_table);
if (error) {
return error;
}
static enum ofperr
ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
- struct ofpbuf *b, bool aggregate)
+ struct ofpbuf *b, bool aggregate,
+ const struct tun_table *tun_table)
{
const struct ofp11_flow_stats_request *ofsr;
enum ofperr error;
fsr->out_group = ntohl(ofsr->out_group);
fsr->cookie = ofsr->cookie;
fsr->cookie_mask = ofsr->cookie_mask;
- error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
+ error = ofputil_pull_ofp11_match(b, tun_table, &fsr->match, NULL);
if (error) {
return error;
}
static enum ofperr
ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
- struct ofpbuf *b, bool aggregate)
+ struct ofpbuf *b, bool aggregate,
+ const struct tun_table *tun_table)
{
const struct nx_flow_stats_request *nfsr;
enum ofperr error;
nfsr = ofpbuf_pull(b, sizeof *nfsr);
error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
- &fsr->cookie, &fsr->cookie_mask);
+ &fsr->cookie, &fsr->cookie_mask, tun_table);
if (error) {
return error;
}
* successful, otherwise an OpenFlow error code. */
enum ofperr
ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
- const struct ofp_header *oh)
+ const struct ofp_header *oh,
+ const struct tun_table *tun_table)
{
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofpraw raw = ofpraw_pull_assert(&b);
return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
case OFPRAW_OFPST11_FLOW_REQUEST:
- return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table);
case OFPRAW_OFPST11_AGGREGATE_REQUEST:
- return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table);
case OFPRAW_NXST_FLOW_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, &b, false);
+ return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table);
case OFPRAW_NXST_AGGREGATE_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, &b, true);
+ return ofputil_decode_nxst_flow_request(fsr, &b, true, tun_table);
default:
/* Hey, the caller lied. */
return EINVAL;
}
- if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) {
+ if (ofputil_pull_ofp11_match(msg, NULL, &fs->match,
+ &padded_match_len)) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
return EINVAL;
}
"claims invalid length %"PRIuSIZE, match_len, length);
return EINVAL;
}
- if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) {
+ if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL, NULL)) {
return EINVAL;
}
instructions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
* have been initialized with ofpmp_init(). */
void
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
- struct ovs_list *replies)
+ struct ovs_list *replies,
+ const struct tun_table *tun_table)
{
+ struct ofputil_flow_stats *fs_ = CONST_CAST(struct ofputil_flow_stats *,
+ fs);
+ const struct tun_table *orig_tun_table;
struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
size_t start_ofs = reply->size;
enum ofp_version version = ofpmp_version(replies);
enum ofpraw raw = ofpmp_decode_raw(replies);
+ orig_tun_table = fs->match.flow.tunnel.metadata.tab;
+ fs_->match.flow.tunnel.metadata.tab = tun_table;
+
if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
struct ofp11_flow_stats *ofs;
}
ofpmp_postappend(replies, start_ofs);
+ fs_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
ofr = ofpbuf_pull(&b, sizeof *ofr);
- error = ofputil_pull_ofp11_match(&b, &fr->match, NULL);
+ error = ofputil_pull_ofp11_match(&b, NULL, &fr->match, NULL);
if (error) {
return error;
}
enum ofperr error;
nfr = ofpbuf_pull(&b, sizeof *nfr);
- error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match,
+ error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, NULL,
NULL, NULL);
if (error) {
return error;
* arguments needs to be initialized. */
static enum ofperr
decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
struct ofputil_packet_in *pin,
size_t *total_len, uint32_t *buffer_id,
struct ofpbuf *continuation)
case NXPINT_METADATA:
error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload),
- &pin->flow_metadata);
+ tun_table, &pin->flow_metadata);
break;
case NXPINT_USERDATA:
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
struct ofputil_packet_in *pin,
size_t *total_lenp, uint32_t *buffer_idp,
struct ofpbuf *continuation)
const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
? ofpbuf_pull(&b, sizeof *cookie)
: NULL);
- enum ofperr error = oxm_pull_match_loose(&b, &pin->flow_metadata);
+ enum ofperr error = oxm_pull_match_loose(&b, tun_table,
+ &pin->flow_metadata);
if (error) {
return error;
}
npi = ofpbuf_pull(&b, sizeof *npi);
error = nx_pull_match_loose(&b, ntohs(npi->match_len),
- &pin->flow_metadata, NULL, NULL);
+ &pin->flow_metadata, NULL, NULL, NULL);
if (error) {
return error;
}
pin->packet = b.data;
pin->packet_len = b.size;
} else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) {
- enum ofperr error = decode_nx_packet_in2(oh, loose, pin, &total_len,
- &buffer_id, continuation);
+ enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table, pin,
+ &total_len, &buffer_id,
+ continuation);
if (error) {
return error;
}
* ofputil_packet_in_private_destroy() to free this data. */
enum ofperr
ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
struct ofputil_packet_in_private *pin,
size_t *total_len, uint32_t *buffer_id)
{
struct ofpbuf continuation;
enum ofperr error;
- error = ofputil_decode_packet_in(oh, loose, &pin->public, total_len,
- buffer_id, &continuation);
+ error = ofputil_decode_packet_in(oh, loose, tun_table, &pin->public,
+ total_len, buffer_id, &continuation);
if (error) {
return error;
}
rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
rq->table_id = nfmr->table_id;
- return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL);
+ return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
+ NULL, NULL);
}
void
update->cookie = nfuf->cookie;
update->priority = ntohs(nfuf->priority);
- error = nx_pull_match(msg, match_len, update->match, NULL, NULL);
+ error = nx_pull_match(msg, match_len, update->match, NULL, NULL, NULL);
if (error) {
return error;
}
#include "nx-match.h"
#include "odp-netlink.h"
#include "openvswitch/ofp-util.h"
-#include "ovs-thread.h"
#include "ovs-rcu.h"
#include "packets.h"
#include "tun-metadata.h"
};
BUILD_ASSERT_DECL(TUN_METADATA_TOT_OPT_SIZE % 4 == 0);
-static struct ovs_mutex tab_mutex = OVS_MUTEX_INITIALIZER;
-static OVSRCU_TYPE(struct tun_table *) metadata_tab;
-
static enum ofperr tun_metadata_add_entry(struct tun_table *map, uint8_t idx,
uint16_t opt_class, uint8_t type,
- uint8_t len) OVS_REQUIRES(tab_mutex);
-static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx)
- OVS_REQUIRES(tab_mutex);
+ uint8_t len);
+static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx);
static void memcpy_to_metadata(struct tun_metadata *dst, const void *src,
const struct tun_metadata_loc *,
unsigned int idx);
/* Returns a newly allocated tun_table. If 'old_map' is nonnull then the new
* tun_table is a deep copy of the old one. */
-static struct tun_table *
-table_alloc(const struct tun_table *old_map) OVS_REQUIRES(tab_mutex)
+struct tun_table *
+tun_metadata_alloc(const struct tun_table *old_map)
{
struct tun_table *new_map;
}
/* Frees 'map' and all the memory it owns. */
-static void
-table_free(struct tun_table *map) OVS_REQUIRES(tab_mutex)
+void
+tun_metadata_free(struct tun_table *map)
{
struct tun_meta_entry *entry;
free(map);
}
-/* Creates a global tunnel metadata mapping table, if none already exists. */
void
-tun_metadata_init(void)
+tun_metadata_postpone_free(struct tun_table *tab)
{
- ovs_mutex_lock(&tab_mutex);
-
- if (!ovsrcu_get_protected(struct tun_table *, &metadata_tab)) {
- ovsrcu_set(&metadata_tab, table_alloc(NULL));
- }
-
- ovs_mutex_unlock(&tab_mutex);
+ ovsrcu_postpone(tun_metadata_free, tab);
}
enum ofperr
-tun_metadata_table_mod(struct ofputil_tlv_table_mod *ttm)
+tun_metadata_table_mod(struct ofputil_tlv_table_mod *ttm,
+ const struct tun_table *old_tab,
+ struct tun_table **new_tab)
{
- struct tun_table *old_map, *new_map;
struct ofputil_tlv_map *ofp_map;
enum ofperr err = 0;
- ovs_mutex_lock(&tab_mutex);
-
- old_map = ovsrcu_get_protected(struct tun_table *, &metadata_tab);
-
switch (ttm->command) {
case NXTTMC_ADD:
- new_map = table_alloc(old_map);
+ *new_tab = tun_metadata_alloc(old_tab);
LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
- err = tun_metadata_add_entry(new_map, ofp_map->index,
+ err = tun_metadata_add_entry(*new_tab, ofp_map->index,
ofp_map->option_class,
ofp_map->option_type,
ofp_map->option_len);
if (err) {
- table_free(new_map);
- goto out;
+ *new_tab = NULL;
+ tun_metadata_free(*new_tab);
+ return err;
}
}
break;
case NXTTMC_DELETE:
- new_map = table_alloc(old_map);
+ *new_tab = tun_metadata_alloc(old_tab);
LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
- tun_metadata_del_entry(new_map, ofp_map->index);
+ tun_metadata_del_entry(*new_tab, ofp_map->index);
}
break;
case NXTTMC_CLEAR:
- new_map = table_alloc(NULL);
+ *new_tab = tun_metadata_alloc(NULL);
break;
default:
OVS_NOT_REACHED();
}
- ovsrcu_set(&metadata_tab, new_map);
- ovsrcu_postpone(table_free, old_map);
-
-out:
- ovs_mutex_unlock(&tab_mutex);
- return err;
+ return 0;
}
void
-tun_metadata_table_request(struct ofputil_tlv_table_reply *ttr)
+tun_metadata_table_request(const struct tun_table *tun_table,
+ struct ofputil_tlv_table_reply *ttr)
{
- struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
int i;
ttr->max_option_space = TUN_METADATA_TOT_OPT_SIZE;
ovs_list_init(&ttr->mappings);
for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) {
- struct tun_meta_entry *entry = &map->entries[i];
+ const struct tun_meta_entry *entry = &tun_table->entries[i];
struct ofputil_tlv_map *map;
if (!entry->valid) {
*
* 'mf' must be an MFF_TUN_METADATA* field.
*
- * This uses the global tunnel metadata mapping table created by
- * tun_metadata_init(). If no such table has been created or if 'mf' hasn't
- * been allocated in it yet, this just zeros 'value'. */
+ * This uses the tunnel metadata mapping table created by tun_metadata_alloc().
+ * If no such table has been created or if 'mf' hasn't been allocated in it yet,
+ * this just zeros 'value'. */
void
tun_metadata_read(const struct flow_tnl *tnl,
const struct mf_field *mf, union mf_value *value)
{
- struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ const struct tun_table *map = tnl->metadata.tab;
unsigned int idx = mf->id - MFF_TUN_METADATA0;
- struct tun_metadata_loc *loc;
+ const struct tun_metadata_loc *loc;
if (!map) {
memset(value->tun_metadata, 0, mf->n_bytes);
*
* 'mf' must be an MFF_TUN_METADATA* field.
*
- * This uses the global tunnel metadata mapping table created by
- * tun_metadata_init(). If no such table has been created or if 'mf' hasn't
- * been allocated in it yet, this function does nothing. */
+ * This uses the tunnel metadata mapping table created by tun_metadata_alloc().
+ * If no such table has been created or if 'mf' hasn't been allocated in it yet,
+ * this function does nothing. */
void
tun_metadata_write(struct flow_tnl *tnl,
const struct mf_field *mf, const union mf_value *value)
{
- struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ const struct tun_table *map = tnl->metadata.tab;
unsigned int idx = mf->id - MFF_TUN_METADATA0;
- struct tun_metadata_loc *loc;
+ const struct tun_metadata_loc *loc;
if (!map || !map->entries[idx].valid) {
return;
}
static const struct tun_metadata_loc *
-metadata_loc_from_match(struct tun_table *map, struct match *match,
+metadata_loc_from_match(const struct tun_table *map, struct match *match,
const char *name, unsigned int idx,
unsigned int field_len, bool masked, char **err_str)
{
*
* 'mf' must be an MFF_TUN_METADATA* field. 'match' must be in non-UDPIF format.
*
- * If there is global tunnel metadata matching table, this function is
- * effective only if there is already a mapping for 'mf'. Otherwise, the
- * metadata mapping table integrated into 'match' is used, adding 'mf' to its
- * mapping table if it isn't already mapped (and if there is room). If 'mf'
- * isn't or can't be mapped, this function returns without modifying 'match'.
+ * If there is a tunnel metadata mapping table associated with the switch,
+ * this function is effective only if there is already a mapping for 'mf'.
+ * Otherwise, the metadata mapping table integrated into 'match' is used,
+ * adding 'mf' to its mapping table if it isn't already mapped (and if there
+ * is room). If 'mf' isn't or can't be mapped, this function returns without
+ * modifying 'match'.
*
* 'value' may be NULL; if so, then 'mf' is made to match on an all-zeros
* value.
const union mf_value *mask, struct match *match,
char **err_str)
{
- struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ const struct tun_table *map = match->flow.tunnel.metadata.tab;
const struct tun_metadata_loc *loc;
unsigned int idx = mf->id - MFF_TUN_METADATA0;
unsigned int field_len;
unsigned int data_offset;
union mf_value data;
- ovs_assert(!(match->flow.tunnel.flags & FLOW_TNL_F_UDPIF));
-
field_len = mf_field_len(mf, value, mask, &is_masked);
loc = metadata_loc_from_match(map, match, mf->name, idx, field_len,
is_masked, err_str);
loc, idx);
}
-static bool
-udpif_to_parsed(const struct flow_tnl *flow, const struct flow_tnl *mask,
- struct flow_tnl *flow_xlate, struct flow_tnl *mask_xlate)
-{
- if (flow->flags & FLOW_TNL_F_UDPIF) {
- int err;
-
- err = tun_metadata_from_geneve_udpif(flow, flow, flow_xlate);
- if (err) {
- return false;
- }
-
- if (mask) {
- tun_metadata_from_geneve_udpif(flow, mask, mask_xlate);
- if (err) {
- return false;
- }
- }
- } else {
- if (flow->metadata.present.map == 0) {
- /* There is no tunnel metadata, don't bother copying. */
- return false;
- }
-
- memcpy(flow_xlate, flow, sizeof *flow_xlate);
- if (mask) {
- memcpy(mask_xlate, mask, sizeof *mask_xlate);
- }
-
- if (!flow_xlate->metadata.tab) {
- flow_xlate->metadata.tab = ovsrcu_get(struct tun_table *,
- &metadata_tab);
- }
- }
-
- return true;
-}
-
-/* Copies all MFF_TUN_METADATA* fields from 'tnl' to 'flow_metadata'. */
+/* Copies all MFF_TUN_METADATA* fields from 'tnl' to 'flow_metadata'. This
+ * is called during action translation and therefore 'tnl' must be in
+ * non-udpif format. */
void
tun_metadata_get_fmd(const struct flow_tnl *tnl, struct match *flow_metadata)
{
- struct flow_tnl flow;
int i;
- if (!udpif_to_parsed(tnl, NULL, &flow, NULL)) {
- return;
- }
-
- ULLONG_FOR_EACH_1 (i, flow.metadata.present.map) {
+ ULLONG_FOR_EACH_1 (i, tnl->metadata.present.map) {
union mf_value opts;
- const struct tun_metadata_loc *old_loc = &flow.metadata.tab->entries[i].loc;
+ const struct tun_metadata_loc *old_loc = &tnl->metadata.tab->entries[i].loc;
const struct tun_metadata_loc *new_loc;
new_loc = metadata_loc_from_match(NULL, flow_metadata, NULL, i,
old_loc->len, false, NULL);
- memcpy_from_metadata(opts.tun_metadata, &flow.metadata, old_loc);
+ memcpy_from_metadata(opts.tun_metadata, &tnl->metadata, old_loc);
memcpy_to_metadata(&flow_metadata->flow.tunnel.metadata,
opts.tun_metadata, new_loc, i);
static int
tun_metadata_alloc_chain(struct tun_table *map, uint8_t len,
struct tun_metadata_loc_chain *loc)
- OVS_REQUIRES(tab_mutex)
{
int alloc_len = len / 4;
int scan_start = 0;
static enum ofperr
tun_metadata_add_entry(struct tun_table *map, uint8_t idx, uint16_t opt_class,
- uint8_t type, uint8_t len) OVS_REQUIRES(tab_mutex)
+ uint8_t type, uint8_t len)
{
struct tun_meta_entry *entry;
struct tun_metadata_loc_chain *cur_chain, *prev_chain;
static void
tun_metadata_del_entry(struct tun_table *map, uint8_t idx)
- OVS_REQUIRES(tab_mutex)
{
struct tun_meta_entry *entry;
struct tun_metadata_loc_chain *chain;
memset(&entry->loc, 0, sizeof entry->loc);
}
-static int
-tun_metadata_from_geneve__(const struct tun_metadata *flow_metadata,
- const struct geneve_opt *opt,
- const struct geneve_opt *flow_opt, int opts_len,
- struct tun_metadata *metadata)
+/* Converts from Geneve netlink attributes in 'attr' to tunnel metadata
+ * in 'tun'. In reality, there is very little conversion done since we are
+ * just copying over the tunnel options in the form that they were received
+ * on the wire. By always using UDPIF format, this allows us to process the
+ * flow key without any knowledge of the mapping table. We can do the
+ * conversion later if necessary. */
+void
+tun_metadata_from_geneve_nlattr(const struct nlattr *attr, bool is_mask,
+ struct flow_tnl *tun)
{
- struct tun_table *map;
- bool is_mask = flow_opt != opt;
+ int attr_len = nl_attr_get_size(attr);
+
+ memcpy(tun->metadata.opts.gnv, nl_attr_get(attr), attr_len);
+ tun->flags |= FLOW_TNL_F_UDPIF;
if (!is_mask) {
- map = ovsrcu_get(struct tun_table *, &metadata_tab);
- metadata->tab = map;
+ tun->metadata.present.len = attr_len;
} else {
- map = flow_metadata->tab;
+ /* We need to exact match on the length so we don't
+ * accidentally match on sets of options that are the same
+ * at the beginning but with additional options after. */
+ tun->metadata.present.len = 0xff;
}
+}
- if (!map) {
- return 0;
- }
+/* Converts from the flat Geneve options representation extracted directly
+ * from the tunnel header to the representation that maps options to
+ * pre-allocated locations. The original version (in UDPIF form) is passed
+ * in 'src' and the translated form in stored in 'dst'. To handle masks, the
+ * flow must also be passed in through 'flow' (in the original, raw form). */
+int
+tun_metadata_from_geneve_udpif(const struct tun_table *tun_tab,
+ const struct flow_tnl *flow,
+ const struct flow_tnl *src,
+ struct flow_tnl *dst)
+{
+ const struct geneve_opt *opt = src->metadata.opts.gnv;
+ const struct geneve_opt *flow_opt = flow->metadata.opts.gnv;
+ int opts_len = flow->metadata.present.len;
+
+ dst->metadata.tab = tun_tab;
+ dst->flags = src->flags & ~FLOW_TNL_F_UDPIF;
+ dst->metadata.present.map = 0;
while (opts_len > 0) {
int len;
return EINVAL;
}
- entry = tun_meta_find_key(&map->key_hmap,
+ entry = tun_meta_find_key(&tun_tab->key_hmap,
tun_meta_key(flow_opt->opt_class,
flow_opt->type));
if (entry) {
if (entry->loc.len == flow_opt->length * 4) {
- memcpy_to_metadata(metadata, opt + 1, &entry->loc,
- entry - map->entries);
+ memcpy_to_metadata(&dst->metadata, opt + 1, &entry->loc,
+ entry - tun_tab->entries);
} else {
return EINVAL;
}
return 0;
}
-static const struct nlattr *
-tun_metadata_find_geneve_key(const struct nlattr *key, uint32_t key_len)
-{
- const struct nlattr *tnl_key;
-
- tnl_key = nl_attr_find__(key, key_len, OVS_KEY_ATTR_TUNNEL);
- if (!tnl_key) {
- return NULL;
- }
-
- return nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
-}
-
-/* Converts from Geneve netlink attributes in 'attr' to tunnel metadata
- * in 'tun'. The result may either in be UDPIF format or not, as determined
- * by 'udpif'.
- *
- * In the event that a mask is being converted, it is also necessary to
- * pass in flow information. This includes the full set of netlink attributes
- * (i.e. not just the Geneve attribute) in 'flow_attrs'/'flow_attr_len' and
- * the previously converted tunnel metadata 'flow_tun'.
- *
- * If a flow rather than mask is being converted, 'flow_attrs' must be NULL. */
-int
-tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
- const struct nlattr *flow_attrs,
- size_t flow_attr_len,
- const struct flow_tnl *flow_tun, bool udpif,
- struct flow_tnl *tun)
-{
- bool is_mask = !!flow_attrs;
- int attr_len = nl_attr_get_size(attr);
- const struct nlattr *flow;
-
- /* No need for real translation, just copy things over. */
- if (udpif) {
- memcpy(tun->metadata.opts.gnv, nl_attr_get(attr), attr_len);
-
- if (!is_mask) {
- tun->metadata.present.len = attr_len;
- tun->flags |= FLOW_TNL_F_UDPIF;
- } else {
- /* We need to exact match on the length so we don't
- * accidentally match on sets of options that are the same
- * at the beginning but with additional options after. */
- tun->metadata.present.len = 0xff;
- }
-
- return 0;
- }
-
- if (is_mask) {
- flow = tun_metadata_find_geneve_key(flow_attrs, flow_attr_len);
- if (!flow) {
- return attr_len ? EINVAL : 0;
- }
-
- if (attr_len != nl_attr_get_size(flow)) {
- return EINVAL;
- }
- } else {
- flow = attr;
- }
-
- return tun_metadata_from_geneve__(&flow_tun->metadata, nl_attr_get(attr),
- nl_attr_get(flow), nl_attr_get_size(flow),
- &tun->metadata);
-}
-
-/* Converts from the flat Geneve options representation extracted directly
- * from the tunnel header to the representation that maps options to
- * pre-allocated locations. The original version (in UDPIF form) is passed
- * in 'src' and the translated form in stored in 'dst'. To handle masks, the
- * flow must also be passed in through 'flow' (in the original, raw form). */
-int
-tun_metadata_from_geneve_udpif(const struct flow_tnl *flow,
- const struct flow_tnl *src,
- struct flow_tnl *dst)
-{
- ovs_assert(flow->flags & FLOW_TNL_F_UDPIF);
-
- if (flow == src) {
- dst->flags = flow->flags & ~FLOW_TNL_F_UDPIF;
- } else {
- dst->metadata.tab = NULL;
- }
- dst->metadata.present.map = 0;
- return tun_metadata_from_geneve__(&flow->metadata, src->metadata.opts.gnv,
- flow->metadata.opts.gnv,
- flow->metadata.present.len,
- &dst->metadata);
-}
-
static void
tun_metadata_to_geneve__(const struct tun_metadata *flow, struct ofpbuf *b,
bool *crit_opt)
{
- struct tun_table *map;
int i;
- map = flow->tab;
- if (!map) {
- map = ovsrcu_get(struct tun_table *, &metadata_tab);
- }
-
*crit_opt = false;
ULLONG_FOR_EACH_1 (i, flow->present.map) {
- struct tun_meta_entry *entry = &map->entries[i];
+ const struct tun_meta_entry *entry = &flow->tab->entries[i];
struct geneve_opt *opt;
opt = ofpbuf_put_uninit(b, sizeof *opt + entry->loc.len);
{
struct ofpbuf b;
- ovs_assert(!(flow->flags & FLOW_TNL_F_UDPIF));
-
ofpbuf_use_stack(&b, opts, TLV_TOT_OPT_SIZE);
tun_metadata_to_geneve__(&flow->metadata, &b, crit_opt);
const struct tun_metadata *mask,
struct geneve_opt *opt, int opts_len)
{
- struct tun_table *map = flow->tab;
-
- if (!map) {
- return;
- }
-
/* All of these options have already been validated, so no need
* for sanity checking. */
while (opts_len > 0) {
struct tun_meta_entry *entry;
int len = sizeof(*opt) + opt->length * 4;
- entry = tun_meta_find_key(&map->key_hmap,
+ entry = tun_meta_find_key(&flow->tab->key_hmap,
tun_meta_key(opt->opt_class, opt->type));
if (entry) {
memcpy_from_metadata(opt + 1, mask, &entry->loc);
const struct flow_tnl *flow,
struct ofpbuf *b)
{
- const struct nlattr *geneve_key;
+ const struct nlattr *tnl_key, *geneve_key;
struct nlattr *geneve_mask;
struct geneve_opt *opt;
int opts_len;
return;
}
- geneve_key = tun_metadata_find_geneve_key(key->data, key->size);
+ tnl_key = nl_attr_find__(key->data, key->size, OVS_KEY_ATTR_TUNNEL);
+ if (!tnl_key) {
+ return;
+ }
+
+ geneve_key = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
if (!geneve_key) {
return;
}
const struct geneve_opt *flow_src_opt,
int opts_len, struct geneve_opt *dst)
{
- ovs_assert(!(flow_src->flags & FLOW_TNL_F_UDPIF));
-
memcpy(dst, flow_src_opt, opts_len);
tun_metadata_to_geneve_mask__(&flow_src->metadata,
&mask_src->metadata, dst, opts_len);
}
static const struct tun_metadata_loc *
-metadata_loc_from_match_read(struct tun_table *map, const struct match *match,
- unsigned int idx, struct flow_tnl *mask,
- bool *is_masked)
+metadata_loc_from_match_read(const struct tun_table *map,
+ const struct match *match, unsigned int idx,
+ const struct flow_tnl *mask, bool *is_masked)
{
union mf_value mask_opts;
return &map->entries[idx].loc;
}
+/* Generates NXM formatted matches in 'b' based on the contents of 'match'.
+ * 'match' must be in non-udpif format. */
void
tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm,
const struct match *match)
{
- struct flow_tnl flow, mask;
int i;
- if (!udpif_to_parsed(&match->flow.tunnel, &match->wc.masks.tunnel,
- &flow, &mask)) {
- return;
- }
-
- ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) {
+ ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) {
const struct tun_metadata_loc *loc;
bool is_masked;
union mf_value opts;
union mf_value mask_opts;
- loc = metadata_loc_from_match_read(flow.metadata.tab, match, i,
- &mask, &is_masked);
- memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc);
- memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc);
+ loc = metadata_loc_from_match_read(match->flow.tunnel.metadata.tab,
+ match, i, &match->wc.masks.tunnel,
+ &is_masked);
+ memcpy_from_metadata(opts.tun_metadata, &match->flow.tunnel.metadata,
+ loc);
+ memcpy_from_metadata(mask_opts.tun_metadata,
+ &match->wc.masks.tunnel.metadata, loc);
nxm_put__(b, MFF_TUN_METADATA0 + i, oxm, opts.tun_metadata,
is_masked ? mask_opts.tun_metadata : NULL, loc->len);
}
}
+/* Formatted matches in 's' based on the contents of 'match'. 'match' must be
+ * in non-udpif format. */
void
tun_metadata_match_format(struct ds *s, const struct match *match)
{
- struct flow_tnl flow, mask;
- unsigned int i;
+ int i;
- if (!udpif_to_parsed(&match->flow.tunnel, &match->wc.masks.tunnel,
- &flow, &mask)) {
+ if (match->flow.tunnel.flags & FLOW_TNL_F_UDPIF ||
+ (!match->flow.tunnel.metadata.tab && !match->tun_md.valid)) {
return;
}
- ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) {
+ ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) {
const struct tun_metadata_loc *loc;
bool is_masked;
union mf_value opts, mask_opts;
- loc = metadata_loc_from_match_read(flow.metadata.tab, match, i,
- &mask, &is_masked);
+ loc = metadata_loc_from_match_read(match->flow.tunnel.metadata.tab,
+ match, i, &match->wc.masks.tunnel,
+ &is_masked);
ds_put_format(s, "tun_metadata%u", i);
- memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc);
+ memcpy_from_metadata(mask_opts.tun_metadata,
+ &match->wc.masks.tunnel.metadata, loc);
- if (!ULLONG_GET(flow.metadata.present.map, i)) {
+ if (!ULLONG_GET(match->flow.tunnel.metadata.present.map, i)) {
/* Indicate that we are matching on the field being not present. */
ds_put_cstr(s, "=NP");
} else if (!(is_masked &&
is_all_zeros(mask_opts.tun_metadata, loc->len))) {
ds_put_char(s, '=');
- memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc);
+ memcpy_from_metadata(opts.tun_metadata,
+ &match->flow.tunnel.metadata, loc);
ds_put_hex(s, opts.tun_metadata, loc->len);
if (!is_all_ones(mask_opts.tun_metadata, loc->len)) {
struct ofputil_tlv_table_reply;
struct tun_table;
-void tun_metadata_init(void);
+struct tun_table *tun_metadata_alloc(const struct tun_table *old_map);
+void tun_metadata_free(struct tun_table *);
+void tun_metadata_postpone_free(struct tun_table *);
-enum ofperr tun_metadata_table_mod(struct ofputil_tlv_table_mod *);
-void tun_metadata_table_request(struct ofputil_tlv_table_reply *);
+enum ofperr tun_metadata_table_mod(struct ofputil_tlv_table_mod *,
+ const struct tun_table *old_tab,
+ struct tun_table **new_tab);
+void tun_metadata_table_request(const struct tun_table *,
+ struct ofputil_tlv_table_reply *);
void tun_metadata_read(const struct flow_tnl *,
const struct mf_field *, union mf_value *);
char **err_str);
void tun_metadata_get_fmd(const struct flow_tnl *, struct match *flow_metadata);
-int tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
- const struct nlattr *flow_attrs,
- size_t flow_attr_len,
- const struct flow_tnl *flow_tun,
- bool udpif, struct flow_tnl *tun);
+void tun_metadata_from_geneve_nlattr(const struct nlattr *attr, bool is_mask,
+ struct flow_tnl *tun);
void tun_metadata_to_geneve_nlattr(const struct flow_tnl *tun,
const struct flow_tnl *flow,
const struct ofpbuf *key,
struct ofpbuf *);
-int tun_metadata_from_geneve_udpif(const struct flow_tnl *flow,
+int tun_metadata_from_geneve_udpif(const struct tun_table *,
+ const struct flow_tnl *flow,
const struct flow_tnl *src,
struct flow_tnl *dst);
void tun_metadata_to_geneve_udpif_mask(const struct flow_tnl *flow_src,
/* Do not handle multi-encap for now. */
sflow_actions->tunnel_err = true;
} else {
- if (odp_tun_key_from_attr(attr, false, &sflow_actions->tunnel)
+ if (odp_tun_key_from_attr(attr, &sflow_actions->tunnel)
== ODP_FIT_ERROR) {
/* Tunnel parsing error. */
sflow_actions->tunnel_err = true;
memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.ipfix);
if (upcall->out_tun_key) {
- odp_tun_key_from_attr(upcall->out_tun_key, false,
- &output_tunnel_key);
+ odp_tun_key_from_attr(upcall->out_tun_key, &output_tunnel_key);
}
dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow,
flow->in_port.odp_port,
memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.flow_sample);
if (upcall->out_tun_key) {
- odp_tun_key_from_attr(upcall->out_tun_key, false,
- &output_tunnel_key);
+ odp_tun_key_from_attr(upcall->out_tun_key, &output_tunnel_key);
}
/* The flow reflects exactly the contents of the packet.
odp_actions);
}
- if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, ukey->key,
- ukey->key_len, &dp_mask, &flow)
+ if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, &dp_mask, &flow)
== ODP_FIT_ERROR) {
goto exit;
}
return "Recirculation conflict";
case XLATE_TOO_MANY_MPLS_LABELS:
return "Too many MPLS labels";
+ case XLATE_INVALID_TUNNEL_METADATA:
+ return "Invalid tunnel metadata";
}
return "Unknown error";
}
if (xport->peer) {
const struct xport *peer = xport->peer;
struct flow old_flow = ctx->xin->flow;
+ struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
bool old_conntrack = ctx->conntracked;
bool old_was_mpls = ctx->was_mpls;
ovs_version_t old_version = ctx->xin->tables_version;
flow->in_port.ofp_port = peer->ofp_port;
flow->metadata = htonll(0);
memset(&flow->tunnel, 0, sizeof flow->tunnel);
+ flow->tunnel.metadata.tab = ofproto_dpif_get_tun_tab(peer->xbridge->ofproto);
+ ctx->wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
memset(flow->regs, 0, sizeof flow->regs);
flow->actset_output = OFPP_UNSET;
ctx->conntracked = false;
/* Restore calling bridge's lookup version. */
ctx->xin->tables_version = old_version;
+ /* Since this packet came in on a patch port (from the perspective of
+ * the peer bridge), it cannot have useful tunnel information. As a
+ * result, any wildcards generated on that tunnel also cannot be valid.
+ * The tunnel wildcards must be restored to their original version since
+ * the peer bridge uses a separate tunnel metadata table and therefore
+ * any generated wildcards will be garbage in the context of our
+ * metadata table. */
+ ctx->wc->masks.tunnel = old_flow_tnl_wc;
+
/* The peer bridge popping MPLS should have no effect on the original
* bridge. */
ctx->was_mpls = old_was_mpls;
xin->ofproto = ofproto;
xin->tables_version = version;
xin->flow = *flow;
+ xin->upcall_flow = flow;
xin->flow.in_port.ofp_port = in_port;
xin->flow.actset_output = OFPP_UNSET;
xin->packet = packet;
goto exit;
}
+ /* Tunnel metadata in udpif format must be normalized before translation. */
+ if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
+ const struct tun_table *tun_tab = ofproto_dpif_get_tun_tab(xin->ofproto);
+ int err;
+
+ err = tun_metadata_from_geneve_udpif(tun_tab, &xin->upcall_flow->tunnel,
+ &xin->upcall_flow->tunnel,
+ &flow->tunnel);
+ if (err) {
+ XLATE_REPORT_ERROR(&ctx, "Invalid Geneve tunnel metadata");
+ ctx.error = XLATE_INVALID_TUNNEL_METADATA;
+ goto exit;
+ }
+ } else if (!flow->tunnel.metadata.tab) {
+ /* If the original flow did not come in on a tunnel, then it won't have
+ * FLOW_TNL_F_UDPIF set. However, we still need to have a metadata
+ * table in case we generate tunnel actions. */
+ flow->tunnel.metadata.tab = ofproto_dpif_get_tun_tab(xin->ofproto);
+ }
+ ctx.wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
+
if (!xin->ofpacts && !ctx.rule) {
ctx.rule = rule_dpif_lookup_from_table(
ctx.xbridge->ofproto, ctx.xin->tables_version, flow, ctx.wc,
}
}
+ /* Translate tunnel metadata masks to udpif format if necessary. */
+ if (xin->upcall_flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
+ if (ctx.wc->masks.tunnel.metadata.present.map) {
+ const struct flow_tnl *upcall_tnl = &xin->upcall_flow->tunnel;
+ struct geneve_opt opts[TLV_TOT_OPT_SIZE /
+ sizeof(struct geneve_opt)];
+
+ tun_metadata_to_geneve_udpif_mask(&flow->tunnel,
+ &ctx.wc->masks.tunnel,
+ upcall_tnl->metadata.opts.gnv,
+ upcall_tnl->metadata.present.len,
+ opts);
+ memset(&ctx.wc->masks.tunnel.metadata, 0,
+ sizeof ctx.wc->masks.tunnel.metadata);
+ memcpy(&ctx.wc->masks.tunnel.metadata.opts.gnv, opts,
+ upcall_tnl->metadata.present.len);
+ }
+ ctx.wc->masks.tunnel.metadata.present.len = 0xff;
+ ctx.wc->masks.tunnel.metadata.tab = NULL;
+ ctx.wc->masks.tunnel.flags |= FLOW_TNL_F_UDPIF;
+ } else if (!xin->upcall_flow->tunnel.metadata.tab) {
+ /* If we didn't have options in UDPIF format and didn't have an existing
+ * metadata table, then it means that there were no options at all when
+ * we started processing and any wildcards we picked up were from
+ * action generation. Without options on the incoming packet, wildcards
+ * aren't meaningful. To avoid them possibly getting misinterpreted,
+ * just clear everything. */
+ if (ctx.wc->masks.tunnel.metadata.present.map) {
+ memset(&ctx.wc->masks.tunnel.metadata, 0,
+ sizeof ctx.wc->masks.tunnel.metadata);
+ } else {
+ ctx.wc->masks.tunnel.metadata.tab = NULL;
+ }
+ }
+
xlate_wc_finish(&ctx);
exit:
+ /* Reset the table to what it was when we came in. If we only fetched
+ * it locally, then it has no meaning outside of flow translation. */
+ flow->tunnel.metadata.tab = xin->upcall_flow->tunnel.metadata.tab;
+
ofpbuf_uninit(&ctx.stack);
ofpbuf_uninit(&ctx.action_set);
ofpbuf_uninit(&ctx.frozen_actions);
* this flow when actions change header fields. */
struct flow flow;
+ /* Pointer to the original flow received during the upcall. xlate_actions()
+ * will never modify this flow. */
+ const struct flow *upcall_flow;
+
/* The packet corresponding to 'flow', or a null pointer if we are
* revalidating without a packet to refer to. */
const struct dp_packet *packet;
XLATE_NO_RECIRCULATION_CONTEXT,
XLATE_RECIRCULATION_CONFLICT,
XLATE_TOO_MANY_MPLS_LABELS,
+ XLATE_INVALID_TUNNEL_METADATA,
};
const char *xlate_strerror(enum xlate_error error);
/* Initial mappings of port to bridge mappings. */
static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
+const struct tun_table *
+ofproto_dpif_get_tun_tab(const struct ofproto_dpif *ofproto)
+{
+ return ofproto_get_tun_tab(&ofproto->up);
+}
+
/* Initialize 'ofm' for a learn action. If the rule already existed, reference
* to that rule is taken, otherwise a new rule is created. 'ofm' keeps the
* rule reference in both cases. */
};
static void
-trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
+trace_format_rule(struct ofproto *ofproto, struct ds *result, int level,
+ const struct rule_dpif *rule)
{
const struct rule_actions *actions;
ovs_be64 cookie;
ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
rule ? rule->up.table_id : 0, ntohll(cookie));
- cls_rule_format(&rule->up.cr, result);
+ cls_rule_format(&rule->up.cr, ofproto_get_tun_tab(ofproto), result);
ds_put_char(result, '\n');
actions = rule_dpif_get_actions(rule);
trace_format_megaflow(result, indentation, "Resubmitted megaflow",
trace);
}
- trace_format_rule(result, indentation, rule);
+ trace_format_rule(&xin->ofproto->up, result, indentation, rule);
}
static void
error = "Invalid datapath flow";
goto exit;
}
+
+ flow->tunnel.metadata.tab = ofproto_dpif_get_tun_tab(*ofprotop);
+
+ /* Convert Geneve options to OpenFlow format now. This isn't actually
+ * required in order to get the right results since the ofproto xlate
+ * actions will handle this for us. However, converting now ensures
+ * that our formatting code will always be able to consistently print
+ * in OpenFlow format, which is what we use here. */
+ if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
+ struct flow_tnl tnl;
+ int err;
+
+ memcpy(&tnl, &flow->tunnel, sizeof tnl);
+ err = tun_metadata_from_geneve_udpif(flow->tunnel.metadata.tab,
+ &tnl, &tnl, &flow->tunnel);
+ if (err) {
+ error = "Failed to parse Geneve options";
+ goto exit;
+ }
+ }
} else {
- char *err = parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL);
+ char *err;
+ if (argc != 3) {
+ error = "Must specify bridge name";
+ goto exit;
+ }
+
+ *ofprotop = ofproto_dpif_lookup(argv[1]);
+ if (!*ofprotop) {
+ error = "Unknown bridge name";
+ goto exit;
+ }
+
+ err = parse_ofp_exact_flow(flow, NULL,
+ ofproto_dpif_get_tun_tab(*ofprotop),
+ argv[argc - 1], NULL);
if (err) {
m_err = xasprintf("Bad openflow flow syntax: %s", err);
free(err);
goto exit;
- } else {
- if (argc != 3) {
- error = "Must specify bridge name";
- goto exit;
- }
-
- *ofprotop = ofproto_dpif_lookup(argv[1]);
- if (!*ofprotop) {
- error = "Unknown bridge name";
- goto exit;
- }
}
}
int priority);
const struct uuid *ofproto_dpif_get_uuid(const struct ofproto_dpif *);
+const struct tun_table *ofproto_dpif_get_tun_tab(const struct ofproto_dpif *);
\f
/* struct rule_dpif has struct rule as it's first member. */
#define RULE_CAST(RULE) ((struct rule *)RULE)
#include "openvswitch/shash.h"
#include "simap.h"
#include "timeval.h"
+#include "tun-metadata.h"
#include "versions.h"
struct match;
struct cmap groups; /* Contains "struct ofgroup"s. */
uint32_t n_groups[4] OVS_GUARDED; /* # of existing groups of each type. */
struct ofputil_group_features ogf;
+
+ /* Tunnel TLV mapping table. */
+ OVSRCU_TYPE(struct tun_table *) metadata_tab;
};
void ofproto_init_tables(struct ofproto *, int n_tables);
return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
}
+static inline const struct tun_table *
+ofproto_get_tun_tab(const struct ofproto *ofproto)
+{
+ return ovsrcu_get(struct tun_table *, &ofproto->metadata_tab);
+}
+
#endif /* ofproto/ofproto-provider.h */
ofproto->ogf.max_groups[i] = OFPG_MAX;
ofproto->ogf.ofpacts[i] = (UINT64_C(1) << N_OFPACTS) - 1;
}
- tun_metadata_init();
+ ovsrcu_set(&ofproto->metadata_tab, tun_metadata_alloc(NULL));
error = ofproto->ofproto_class->construct(ofproto);
if (error) {
cmap_destroy(&ofproto->groups);
hmap_remove(&all_ofprotos, &ofproto->hmap_node);
+ tun_metadata_free(ovsrcu_get_protected(struct tun_table *,
+ &ofproto->metadata_tab));
free(ofproto->name);
free(ofproto->type);
free(ofproto->mfr_desc);
struct ofputil_packet_in_private pin;
enum ofperr error;
- error = ofputil_decode_packet_in_private(oh, false, &pin, NULL, NULL);
+ error = ofputil_decode_packet_in_private(oh, false,
+ ofproto_get_tun_tab(ofproto), &pin,
+ NULL, NULL);
if (error) {
return error;
}
struct ovs_list replies;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&fsr, request);
+ error = ofputil_decode_flow_stats_request(&fsr, request,
+ ofproto_get_tun_tab(ofproto));
if (error) {
return error;
}
fs.ofpacts_len = actions->ofpacts_len;
fs.flags = flags;
- ofputil_append_flow_stats_reply(&fs, &replies);
+ ofputil_append_flow_stats_reply(&fs, &replies,
+ ofproto_get_tun_tab(ofproto));
}
rule_collection_unref(&rules);
}
static void
-flow_stats_ds(struct rule *rule, struct ds *results)
+flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results)
{
uint64_t packet_count, byte_count;
const struct rule_actions *actions;
ds_put_format(results, "duration=%llds, ", (time_msec() - created) / 1000);
ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
- cls_rule_format(&rule->cr, results);
+ cls_rule_format(&rule->cr, ofproto_get_tun_tab(ofproto), results);
ds_put_char(results, ',');
ds_put_cstr(results, "actions=");
struct rule *rule;
CLS_FOR_EACH (rule, cr, &table->cls) {
- flow_stats_ds(rule, results);
+ flow_stats_ds(p, rule, results);
}
}
}
struct ofpbuf *reply;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&request, oh);
+ error = ofputil_decode_flow_stats_request(&request, oh,
+ ofproto_get_tun_tab(ofproto));
if (error) {
return error;
}
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
- &ofpacts,
+ ofproto_get_tun_tab(ofproto), &ofpacts,
u16_to_ofp(ofproto->max_ports),
ofproto->n_tables);
if (!error) {
error = ofputil_decode_flow_mod(&fm, badd.msg,
ofconn_get_protocol(ofconn),
+ ofproto_get_tun_tab(ofproto),
&ofpacts,
u16_to_ofp(ofproto->max_ports),
ofproto->n_tables);
static enum ofperr
handle_tlv_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct tun_table *old_tab, *new_tab;
struct ofputil_tlv_table_mod ttm;
enum ofperr error;
return error;
}
- error = tun_metadata_table_mod(&ttm);
+ old_tab = ovsrcu_get_protected(struct tun_table *, &ofproto->metadata_tab);
+ error = tun_metadata_table_mod(&ttm, old_tab, &new_tab);
+ if (!error) {
+ ovsrcu_set(&ofproto->metadata_tab, new_tab);
+ tun_metadata_postpone_free(old_tab);
+ }
ofputil_uninit_tlv_table(&ttm.mappings);
return error;
static enum ofperr
handle_tlv_table_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
+ const struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_tlv_table_reply ttr;
struct ofpbuf *b;
- tun_metadata_table_request(&ttr);
+ tun_metadata_table_request(ofproto_get_tun_tab(ofproto), &ttr);
+
b = ofputil_encode_tlv_table_reply(oh, &ttr);
ofputil_uninit_tlv_table(&ttr.mappings);
struct ofputil_packet_in pin;
struct ofpbuf continuation;
- enum ofperr error = ofputil_decode_packet_in(msg, true, &pin,
+ enum ofperr error = ofputil_decode_packet_in(msg, true, NULL, &pin,
NULL, NULL, &continuation);
if (error) {
memset(&flow_filter, 0, sizeof(flow_filter));
memset(&wc_filter, 0, sizeof(wc_filter));
- error = parse_ofp_exact_flow(&flow_filter, &wc_filter, filter,
+ error = parse_ofp_exact_flow(&flow_filter, &wc_filter, NULL, filter,
NULL);
if (error) {
ovs_fatal(0, "Failed to parse filter (%s)", error);
struct minimatch minimatch;
odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
- odp_flow_key_to_mask(odp_mask.data, odp_mask.size, odp_key.data,
- odp_key.size, &wc, &flow);
+ odp_flow_key_to_mask(odp_mask.data, odp_mask.size, &wc, &flow);
match_init(&match, &flow, &wc);
match_init(&match_filter, &flow_filter, &wc);
A mapping should not be changed while it is in active
use by a flow. The result of doing so is undefined.
-Currently, the TLV mapping table is shared between all OpenFlow
-switches in a given instance of Open vSwitch. This restriction will
-be lifted in the future to allow for easier management.
-
These commands are Nicira extensions to OpenFlow and require Open vSwitch
2.5 or later.
struct ofputil_packet_in pin;
struct ofpbuf continuation;
- error = ofputil_decode_packet_in(b->data, true, &pin,
+ error = ofputil_decode_packet_in(b->data, true, NULL, &pin,
NULL, NULL,
&continuation);
if (error) {
uint8_t table_id;
};
+/* A FTE entry that has been queued for later insertion after all
+ * flows have been scanned to correctly allocation tunnel metadata. */
+struct fte_pending {
+ struct match *match;
+ int priority;
+ struct fte_version *version;
+ int index;
+
+ struct ovs_list list_node;
+};
+
+/* Processing state during two stage processing of flow table entries.
+ * Tracks the maximum size seen for each tunnel metadata entry as well
+ * as a list of the pending FTE entries. */
+struct fte_state {
+ int tun_metadata_size[TUN_METADATA_NUM_OPTS];
+ struct ovs_list fte_pending_list;
+
+ /* The final metadata table that we have constructed. */
+ struct tun_table *tun_tab;
+};
+
/* Frees 'version' and the data that it owns. */
static void
fte_version_free(struct fte_version *version)
/* Clears 's', then if 's' has a version 'index', formats 'fte' and version
* 'index' into 's', followed by a new-line. */
static void
-fte_version_format(const struct fte *fte, int index, struct ds *s)
+fte_version_format(const struct fte_state *fte_state, const struct fte *fte,
+ int index, struct ds *s)
{
const struct fte_version *version = fte->versions[index];
if (version->table_id) {
ds_put_format(s, "table=%"PRIu8" ", version->table_id);
}
- cls_rule_format(&fte->rule, s);
+ cls_rule_format(&fte->rule, fte_state->tun_tab, s);
if (version->cookie != htonll(0)) {
ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
}
}
}
-/* A FTE entry that has been queued for later insertion after all
- * flows have been scanned to correctly allocation tunnel metadata. */
-struct fte_pending {
- struct match *match;
- int priority;
- struct fte_version *version;
- int index;
-
- struct ovs_list list_node;
-};
-
-/* Processing state during two stage processing of flow table entries.
- * Tracks the maximum size seen for each tunnel metadata entry as well
- * as a list of the pending FTE entries. */
-struct fte_state {
- int tun_metadata_size[TUN_METADATA_NUM_OPTS];
- struct ovs_list fte_pending_list;
-};
-
/* Given a list of the field sizes for each tunnel metadata entry, install
* a mapping table for later operations. */
static void
}
}
- tun_metadata_table_mod(&ttm);
+ tun_metadata_table_mod(&ttm, NULL, &state->tun_tab);
ofputil_uninit_tlv_table(&ttm.mappings);
}
* can just read the data from the match and rewrite it. On rewrite, it
* will use the new table. */
static void
-remap_match(struct match *match)
+remap_match(struct fte_state *state, struct match *match)
{
int i;
sizeof match->wc.masks.tunnel.metadata);
match->tun_md.valid = false;
+ match->flow.tunnel.metadata.tab = state->tun_tab;
+ match->wc.masks.tunnel.metadata.tab = match->flow.tunnel.metadata.tab;
+
ULLONG_FOR_EACH_1 (i, flow_mask.present.map) {
const struct mf_field *field = mf_from_id(MFF_TUN_METADATA0 + i);
int offset = match->tun_md.entry[i].loc.c.offset;
}
ovs_list_init(&state->fte_pending_list);
+ state->tun_tab = NULL;
+}
+
+static void
+fte_state_destroy(struct fte_state *state)
+{
+ tun_metadata_free(state->tun_tab);
}
/* The first pass of the processing described in the comment about
flow_tables_defer(tables);
LIST_FOR_EACH_POP(pending, list_node, &state->fte_pending_list) {
- remap_match(pending->match);
+ remap_match(state, pending->match);
fte_insert(tables, pending->match, pending->priority, pending->version,
pending->index);
free(pending->match);
vconn_close(vconn);
fte_free_all(&tables);
+ fte_state_destroy(&fte_state);
}
static void
struct fte_version *b = fte->versions[1];
if (!a || !b || !fte_version_equals(a, b)) {
- fte_version_format(fte, 0, &a_s);
- fte_version_format(fte, 1, &b_s);
+ fte_version_format(&fte_state, fte, 0, &a_s);
+ fte_version_format(&fte_state, fte, 1, &b_s);
if (strcmp(ds_cstr(&a_s), ds_cstr(&b_s))) {
if (a_s.length) {
printf("-%s", ds_cstr(&a_s));
ds_destroy(&b_s);
fte_free_all(&tables);
+ fte_state_destroy(&fte_state);
if (differences) {
exit(2);
/* Convert nx_match to match. */
if (strict) {
if (oxm) {
- error = oxm_pull_match(&nx_match, &match);
+ error = oxm_pull_match(&nx_match, NULL, &match);
} else {
error = nx_pull_match(&nx_match, match_len, &match,
- &cookie, &cookie_mask);
+ &cookie, &cookie_mask, NULL);
}
} else {
if (oxm) {
- error = oxm_pull_match_loose(&nx_match, &match);
+ error = oxm_pull_match_loose(&nx_match, NULL, &match);
} else {
error = nx_pull_match_loose(&nx_match, match_len, &match,
- &cookie, &cookie_mask);
+ &cookie, &cookie_mask, NULL);
}
}
ofpbuf_init(&nxm, 0);
nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0));
nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
- error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL);
+ error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL, NULL);
printf("NXM: %s -> ", nxm_s);
if (error) {
printf("%s\n", ofperr_to_string(error));
ofpbuf_init(&nxm, 0);
nxm_match_len = oxm_put_match(&nxm, &match, OFP12_VERSION);
nxm_s = oxm_match_to_string(&nxm, nxm_match_len);
- error = oxm_pull_match(&nxm, &nxm_match);
+ error = oxm_pull_match(&nxm, NULL, &nxm_match);
printf("OXM: %s -> ", nxm_s);
if (error) {
printf("%s\n", ofperr_to_string(error));