This prevents using an older datapath from breaking forwarding. CC: Romain Lenglet <rleng...@vmware.com> Signed-off-by: Ben Pfaff <b...@nicira.com> --- ofproto/ofproto-dpif-ipfix.c | 28 ++++++++++--- ofproto/ofproto-dpif-ipfix.h | 5 ++- ofproto/ofproto-dpif.c | 91 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 8 deletions(-)
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index a9cc73e..1a0ebae 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Nicira, Inc. + * Copyright (c) 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,6 +71,7 @@ struct dpif_ipfix { struct dpif_ipfix_bridge_exporter bridge_exporter; struct hmap flow_exporter_map; /* dpif_ipfix_flow_exporter_map_node. */ atomic_int ref_cnt; + bool variable_length_userdata; }; #define IPFIX_VERSION 0x000a @@ -632,14 +633,21 @@ dpif_ipfix_set_options( ovs_mutex_unlock(&mutex); } +/* Creates and returns a new dpif_ipfix structure. + * + * 'variable_length_userdata' specifies whether the datapath supports + * variable-length OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE + * actions. A datapath that doesn't support such actions cannot support flow + * sampling, so the module will suppress its use. */ struct dpif_ipfix * -dpif_ipfix_create(void) +dpif_ipfix_create(bool variable_length_userdata) { struct dpif_ipfix *di; di = xzalloc(sizeof *di); dpif_ipfix_bridge_exporter_init(&di->bridge_exporter); hmap_init(&di->flow_exporter_map); atomic_init(&di->ref_cnt, 1); + di->variable_length_userdata = variable_length_userdata; return di; } @@ -1239,13 +1247,23 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, } static void -dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter, +dpif_ipfix_sample(struct dpif_ipfix *di, + struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet, const struct flow *flow, uint64_t packet_delta_count, uint32_t obs_domain_id, uint32_t obs_point_id) { struct ipfix_flow_cache_entry *entry; + if (!di->variable_length_userdata) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_ERR_RL(&rl, "ignoring NXAST_SAMPLE action because datapath " + "lacks support (needs Linux 3.10+ or kernel module from " + "OVS 1.11+)"); + return; + } + /* Create a flow cache entry from the sample. */ entry = xmalloc(sizeof *entry); ipfix_cache_entry_init(entry, packet, flow, packet_delta_count, @@ -1263,7 +1281,7 @@ dpif_ipfix_bridge_sample(struct dpif_ipfix *di, struct ofpbuf *packet, /* Use the sampling probability as an approximation of the number * of matched packets. */ packet_delta_count = UINT32_MAX / di->bridge_exporter.probability; - dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, flow, + dpif_ipfix_sample(di, &di->bridge_exporter.exporter, packet, flow, packet_delta_count, di->bridge_exporter.options->obs_domain_id, di->bridge_exporter.options->obs_point_id); @@ -1284,7 +1302,7 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, struct ofpbuf *packet, ovs_mutex_lock(&mutex); node = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id); if (node) { - dpif_ipfix_sample(&node->exporter.exporter, packet, flow, + dpif_ipfix_sample(di, &node->exporter.exporter, packet, flow, packet_delta_count, obs_domain_id, obs_point_id); } ovs_mutex_unlock(&mutex); diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h index 6ebf8b0..a81976c 100644 --- a/ofproto/ofproto-dpif-ipfix.h +++ b/ofproto/ofproto-dpif-ipfix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Nicira, Inc. + * Copyright (c) 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ #ifndef OFPROTO_DPIF_IPFIX_H #define OFPROTO_DPIF_IPFIX_H 1 +#include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -25,7 +26,7 @@ struct ofpbuf; struct ofproto_ipfix_bridge_exporter_options; struct ofproto_ipfix_flow_exporter_options; -struct dpif_ipfix *dpif_ipfix_create(void); +struct dpif_ipfix *dpif_ipfix_create(bool variable_length_userdata); struct dpif_ipfix *dpif_ipfix_ref(const struct dpif_ipfix *); void dpif_ipfix_unref(struct dpif_ipfix *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 80e97e0..e1a690b 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -41,6 +41,7 @@ #include "netdev-vport.h" #include "netdev.h" #include "netlink.h" +#include "netlink-socket.h" #include "nx-match.h" #include "odp-util.h" #include "odp-execute.h" @@ -440,6 +441,10 @@ struct dpif_backer { /* Number of upcall handling threads. */ unsigned int n_handler_threads; + + /* True if the datapath supports variable-length + * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. */ + bool variable_length_userdata; }; /* All existing ofproto_backer instances, indexed by ofproto->up.type. */ @@ -1095,6 +1100,8 @@ struct odp_garbage { odp_port_t odp_port; }; +static bool check_variable_length_userdata(struct dpif_backer *backer); + static int open_dpif_backer(const char *type, struct dpif_backer **backerp) { @@ -1197,6 +1204,7 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) close_dpif_backer(backer); return error; } + backer->variable_length_userdata = check_variable_length_userdata(backer); udpif_recv_set(backer->udpif, n_handler_threads, backer->recv_set_enable); backer->n_handler_threads = n_handler_threads; @@ -1209,6 +1217,86 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) return error; } +/* Tests whether 'backer''s datapath supports variable-length + * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. We need + * to disable some features on older datapaths that don't support this + * feature. + * + * Returns false if 'backer' definitely does not support variable-length + * userdata, true if it seems to support them or if at least the error we get + * is ambiguous. */ +static bool +check_variable_length_userdata(struct dpif_backer *backer) +{ + struct eth_header *eth; + struct ofpbuf actions; + struct ofpbuf key; + struct ofpbuf packet; + size_t start; + int error; + + /* Compose a userspace action that will cause an ERANGE error on older + * datapaths that don't support variable-length userdata. + * + * We really test for using userdata longer than 8 bytes, but older + * datapaths accepted these, silently truncating the userdata to 8 bytes. + * The same older datapaths rejected userdata shorter than 8 bytes, so we + * test for that instead as a proxy for longer userdata support. */ + ofpbuf_init(&actions, 64); + start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_USERSPACE); + nl_msg_put_u32(&actions, OVS_USERSPACE_ATTR_PID, + dpif_port_get_pid(backer->dpif, ODPP_NONE)); + nl_msg_put_unspec_zero(&actions, OVS_USERSPACE_ATTR_USERDATA, 4); + nl_msg_end_nested(&actions, start); + + /* Compose an ODP flow key. The key is arbitrary but it must match the + * packet that we compose later. */ + ofpbuf_init(&key, 64); + nl_msg_put_u32(&key, OVS_KEY_ATTR_IN_PORT, 0); + nl_msg_put_unspec_zero(&key, OVS_KEY_ATTR_ETHERNET, + sizeof(struct ovs_key_ethernet)); + nl_msg_put_be16(&key, OVS_KEY_ATTR_ETHERTYPE, htons(0x1234)); + + /* Compose a packet that matches the key. */ + ofpbuf_init(&packet, ETH_HEADER_LEN); + eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN); + eth->eth_type = htons(0x1234); + + /* Execute the actions. On older datapaths this fails with -ERANGE, on + * newer datapaths it succeeds. */ + error = dpif_execute(backer->dpif, key.data, key.size, + actions.data, actions.size, &packet); + + ofpbuf_uninit(&packet); + ofpbuf_uninit(&key); + ofpbuf_uninit(&actions); + + switch (error) { + case 0: + /* Variable-length userdata is supported. + * + * Purge received packets to avoid processing the nonsense packet we + * sent to userspace, then report success. */ + dpif_recv_purge(backer->dpif); + return true; + + case ERANGE: + /* Variable-length userdata is not supported. */ + VLOG_WARN("%s: datapath does not support variable-length userdata " + "feature (needs Linux 3.10+ or kernel module from OVS " + "1..11+). The NXAST_SAMPLE action will be ignored.", + dpif_name(backer->dpif)); + return false; + + default: + /* Something odd happened. We're not sure whether variable-length + * userdata is supported. Definitely to "yes". */ + VLOG_WARN("%s: variable-length userdata feature probe failed (%s)", + dpif_name(backer->dpif), ovs_strerror(error)); + return true; + } +} + static int construct(struct ofproto *ofproto_) { @@ -1881,7 +1969,8 @@ set_ipfix( bool has_options = bridge_exporter_options || flow_exporters_options; if (has_options && !di) { - di = ofproto->ipfix = dpif_ipfix_create(); + di = ofproto->ipfix = dpif_ipfix_create( + ofproto->backer->variable_length_userdata); } if (di) { -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev