#include "packets.h"
#include "simap.h"
#include "timeval.h"
+#include "tun-metadata.h"
#include "unaligned.h"
#include "util.h"
+#include "uuid.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(odp_util);
* from another. */
static const char *delimiters = ", \t\r\n";
-static const char *hex_chars = "0123456789abcdefABCDEF";
-
struct attr_len_tbl {
int len;
const struct attr_len_tbl *next;
}
}
-#define GENEVE_OPT(class, type) ((OVS_FORCE uint32_t)(class) << 8 | (type))
-static int
-parse_geneve_opts(const struct nlattr *attr)
-{
- int opts_len = nl_attr_get_size(attr);
- const struct geneve_opt *opt = nl_attr_get(attr);
-
- while (opts_len > 0) {
- int len;
-
- if (opts_len < sizeof(*opt)) {
- return -EINVAL;
- }
-
- len = sizeof(*opt) + opt->length * 4;
- if (len > opts_len) {
- return -EINVAL;
- }
-
- switch (GENEVE_OPT(opt->opt_class, opt->type)) {
- default:
- if (opt->type & GENEVE_CRIT_OPT_TYPE) {
- return -EINVAL;
- }
- };
-
- opt = opt + len / sizeof(*opt);
- opts_len -= len;
- };
-
- return 0;
-}
-
-enum odp_key_fitness
-odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+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)
{
unsigned int left;
const struct nlattr *a;
break;
}
- case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
- if (parse_geneve_opts(a)) {
+ case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+ if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len,
+ &src_tun->metadata,
+ &tun->metadata)) {
return ODP_FIT_ERROR;
}
- /* It is necessary to reproduce options exactly (including order)
- * so it's easiest to just echo them back. */
- unknown = true;
break;
- }
+
default:
/* Allow this to show up as unexpected, if there are unknown
* tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
return ODP_FIT_PERFECT;
}
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+ return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun);
+}
+
static void
-tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
+ const struct flow_tnl *tun_flow_key,
+ const struct ofpbuf *key_buf)
{
size_t tun_key_ofs;
nl_msg_end_nested(a, vxlan_opts_ofs);
}
+ if (tun_key == tun_flow_key) {
+ tun_metadata_to_geneve_nlattr_flow(&tun_key->metadata, a);
+ } else {
+ tun_metadata_to_geneve_nlattr_mask(key_buf, &tun_key->metadata,
+ &tun_flow_key->metadata, a);
+ }
+
nl_msg_end_nested(a, tun_key_ofs);
}
&& ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_dst);
}
if (attr == OVS_KEY_ATTR_TUNNEL) {
- const struct flow_tnl *tun_mask = mask;
-
- return tun_mask->flags == FLOW_TNL_F_MASK
- && tun_mask->tun_id == OVS_BE64_MAX
- && tun_mask->ip_src == OVS_BE32_MAX
- && tun_mask->ip_dst == OVS_BE32_MAX
- && tun_mask->ip_tos == UINT8_MAX
- && tun_mask->ip_ttl == UINT8_MAX
- && tun_mask->tp_src == OVS_BE16_MAX
- && tun_mask->tp_dst == OVS_BE16_MAX
- && tun_mask->gbp_id == OVS_BE16_MAX
- && tun_mask->gbp_flags == UINT8_MAX;
+ return false;
}
if (attr == OVS_KEY_ATTR_ARP) {
static bool
odp_mask_attr_is_exact(const struct nlattr *ma)
{
- struct flow_tnl tun_mask;
enum ovs_key_attr attr = nl_attr_type(ma);
const void *mask;
size_t size;
if (attr == OVS_KEY_ATTR_TUNNEL) {
- memset(&tun_mask, 0, sizeof tun_mask);
- odp_tun_key_from_attr(ma, &tun_mask);
- mask = &tun_mask;
- size = sizeof tun_mask;
+ return false;
} else {
mask = nl_attr_get(ma);
size = nl_attr_get_size(ma);
const char *s = s_;
if (ovs_scan(s, "ufid:")) {
- size_t n;
-
s += 5;
- if (ovs_scan(s, "0x")) {
- s += 2;
- }
- n = strspn(s, hex_chars);
- if (n != 32) {
+ if (!uuid_from_string_prefix((struct uuid *)ufid, s)) {
return -EINVAL;
}
-
- if (!ovs_scan(s, "%16"SCNx64"%16"SCNx64, &ufid->u64.hi,
- &ufid->u64.lo)) {
- return -EINVAL;
- }
- s += n;
- s += strspn(s, delimiters);
+ s += UUID_LEN;
return s - s_;
}
void
odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
{
- ds_put_format(ds, "ufid:%016"PRIx64"%016"PRIx64, ufid->u64.hi,
- ufid->u64.lo);
+ ds_put_format(ds, "ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid));
}
/* Appends to 'ds' a string representation of the 'key_len' bytes of
scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
{
const char *s_base = s;
- ovs_be16 id, id_mask;
- uint8_t flags, flags_mask;
+ ovs_be16 id = 0, id_mask = 0;
+ uint8_t flags = 0, flags_mask = 0;
if (!strncmp(s, "id=", 3)) {
s += 3;
s += scan_be16(s, &id, mask ? &id_mask : NULL);
- } else if (mask) {
- memset(&id_mask, 0, sizeof id_mask);
}
if (s[0] == ',') {
if (!strncmp(s, "flags=", 6)) {
s += 6;
s += scan_u8(s, &flags, mask ? &flags_mask : NULL);
- } else if (mask) {
- memset(&flags_mask, 0, sizeof flags_mask);
}
if (!strncmp(s, "))", 2)) {
}
struct geneve_scan {
- uint8_t d[252];
+ struct geneve_opt d[63];
int len;
};
scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
{
const char *s_base = s;
- struct geneve_opt *opt = (struct geneve_opt *)key->d;
- struct geneve_opt *opt_mask = (struct geneve_opt *)(mask ? mask->d : NULL);
+ struct geneve_opt *opt = key->d;
+ struct geneve_opt *opt_mask = mask ? mask->d : NULL;
int len_remain = sizeof key->d;
while (s[0] == '{' && len_remain >= sizeof *opt) {
parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
struct ofpbuf *key, struct ofpbuf *mask)
{
- if (!strncmp(s, "ufid:", 5)) {
- const char *start = s;
-
- /* Skip UFID. */
- s += 5;
- s += strspn(s, hex_chars);
- s += strspn(s, delimiters);
+ ovs_u128 ufid;
+ int len;
- return s - start;
+ /* Skip UFID. */
+ len = odp_ufid_from_string(s, &ufid);
+ if (len) {
+ return len;
}
SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY);
static void put_tp_key(const union ovs_key_tp *, struct flow *);
static void
-odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
- const struct flow *mask, odp_port_t odp_in_port,
- size_t max_mpls_depth, bool recirc, bool export_mask)
+odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+ bool export_mask, struct ofpbuf *buf)
{
struct ovs_key_ethernet *eth_key;
size_t encap;
- const struct flow *data = export_mask ? mask : flow;
+ const struct flow *flow = parms->flow;
+ const struct flow *data = export_mask ? parms->mask : parms->flow;
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
if (flow->tunnel.ip_dst || export_mask) {
- tun_key_to_attr(buf, &data->tunnel);
+ tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
+ parms->key_buf);
}
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
- if (recirc) {
+ if (parms->recirc) {
nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
}
/* Add an ingress port attribute if this is a mask or 'odp_in_port'
* is not the magical value "ODPP_NONE". */
- if (export_mask || odp_in_port != ODPP_NONE) {
- nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
+ if (export_mask || parms->odp_in_port != ODPP_NONE) {
+ nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_port);
}
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
int i, n;
n = flow_count_mpls_labels(flow, NULL);
- n = MIN(n, max_mpls_depth);
+ if (export_mask) {
+ n = MIN(n, parms->max_mpls_depth);
+ }
mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
n * sizeof *mpls_key);
for (i = 0; i < n; i++) {
}
/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
- * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
- * number rather than a datapath port number). Instead, if 'odp_in_port'
- * is anything other than ODPP_NONE, it is included in 'buf' as the input
- * port.
*
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
void
-odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
- const struct flow *mask, odp_port_t odp_in_port,
- bool recirc)
+odp_flow_key_from_flow(const struct odp_flow_key_parms *parms,
+ struct ofpbuf *buf)
{
- odp_flow_key_from_flow__(buf, flow, mask, odp_in_port, SIZE_MAX, recirc,
- false);
+ odp_flow_key_from_flow__(parms, false, buf);
}
/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
- * 'buf'. 'flow' is used as a template to determine how to interpret
- * 'mask'. For example, the 'dl_type' of 'mask' describes the mask, but
- * it doesn't indicate whether the other fields should be interpreted as
- * ARP, IPv4, IPv6, etc.
+ * 'buf'.
*
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
void
-odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
- const struct flow *flow, uint32_t odp_in_port_mask,
- size_t max_mpls_depth, bool recirc)
+odp_flow_key_from_mask(const struct odp_flow_key_parms *parms,
+ struct ofpbuf *buf)
{
- odp_flow_key_from_flow__(buf, flow, mask, u32_to_odp(odp_in_port_mask),
- max_mpls_depth, recirc, true);
+ odp_flow_key_from_flow__(parms, true, buf);
}
/* Generate ODP flow key from the given packet metadata */
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
if (md->tunnel.ip_dst) {
- tun_key_to_attr(buf, &md->tunnel);
+ tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL);
}
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
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)
{
const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
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], &flow->tunnel);
+ res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], src_key,
+ src_key_len, &src_flow->tunnel,
+ &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, flow, flow);
+ return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow);
}
-/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
- * structure in 'mask'. 'flow' must be a previously translated flow
- * corresponding to 'mask'. Returns an ODP_FIT_* value that indicates how well
- * 'key' fits our expectations for what a flow key should contain. */
+/* 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 *key, size_t key_len,
+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 *mask, const struct flow *flow)
{
- return odp_flow_key_to_flow__(key, key_len, mask, flow);
+ return odp_flow_key_to_flow__(mask_key, mask_key_len, flow_key, flow_key_len,
+ mask, flow);
}
/* Returns 'fitness' as a string, for use in debug messages. */
struct ofpbuf *odp_actions)
{
size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
- tun_key_to_attr(odp_actions, tunnel);
+ tun_key_to_attr(odp_actions, tunnel, tunnel, NULL);
nl_msg_end_nested(odp_actions, offset);
}