return NULL;
}
+static void set_tunnel(struct sk_buff *skb, struct odp_flow_key *key,
+ __be32 tun_id)
+{
+ OVS_CB(skb)->tun_id = key->tun_id = tun_id;
+}
static struct sk_buff *
vlan_pull_tag(struct sk_buff *skb)
}
}
+ OVS_CB(skb)->tun_id = 0;
+
for (; n_actions > 0; a++, n_actions--) {
WARN_ON_ONCE(skb_shared(skb));
if (prev_port != -1) {
}
break;
+ case ODPAT_SET_TUNNEL:
+ set_tunnel(skb, key, a->tunnel.tun_id);
+ break;
+
case ODPAT_SET_VLAN_VID:
case ODPAT_SET_VLAN_PCP:
skb = modify_vlan_tci(dp, skb, key, a, n_actions, gfp);
WARN_ON_ONCE(skb_shared(skb));
compute_ip_summed(skb, false);
+ OVS_CB(skb)->tun_id = 0;
/* BHs are off so we don't have to use get_cpu()/put_cpu() here. */
stats = percpu_ptr(dp->stats_percpu, smp_processor_id());
stats->n_hit++;
} else {
stats->n_missed++;
- dp_output_control(dp, skb, _ODPL_MISS_NR, 0);
+ dp_output_control(dp, skb, _ODPL_MISS_NR, OVS_CB(skb)->tun_id);
}
}
skb = alloc_skb(execute.length, GFP_KERNEL);
if (!skb)
goto error_free_actions;
+
if (execute.in_port < DP_MAX_PORTS) {
struct net_bridge_port *p = dp->ports[execute.in_port];
if (p)
* kernel versions.
*/
struct ovs_skb_cb {
- enum csum_type ip_summed;
+ enum csum_type ip_summed;
+ __be32 tun_id;
};
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
int nh_ofs;
memset(key, 0, sizeof *key);
- key->dl_vlan = htons(ODP_VLAN_NONE);
+ key->tun_id = OVS_CB(skb)->tun_id;
key->in_port = in_port;
+ key->dl_vlan = htons(ODP_VLAN_NONE);
if (skb->len < sizeof *eth)
return 0;
NXT_FLOW_END_CONFIG__OBSOLETE,
NXT_FLOW_END__OBSOLETE,
NXT_MGMT__OBSOLETE,
+
+ /* Use the high 32 bits of the cookie field as the tunnel ID in the flow
+ * match. */
+ NXT_TUN_ID_FROM_COOKIE,
};
struct nicira_header {
};
OFP_ASSERT(sizeof(struct nicira_header) == 16);
+struct nxt_tun_id_cookie {
+ struct ofp_header header;
+ uint32_t vendor; /* NX_VENDOR_ID. */
+ uint32_t subtype; /* NXT_TUN_ID_FROM_COOKIE */
+ uint8_t set; /* Nonzero to enable, zero to disable. */
+ uint8_t pad[7];
+};
+OFP_ASSERT(sizeof(struct nxt_tun_id_cookie) == 24);
enum nx_action_subtype {
NXAST_SNAT__OBSOLETE, /* No longer used. */
*
* NXAST_RESUBMIT may be used any number of times within a set of actions.
*/
- NXAST_RESUBMIT
+ NXAST_RESUBMIT,
+
+ NXAST_SET_TUNNEL /* Set encapsulating tunnel ID. */
};
/* Action structure for NXAST_RESUBMIT. */
struct nx_action_resubmit {
uint16_t type; /* OFPAT_VENDOR. */
- uint16_t len; /* Length is 8. */
+ uint16_t len; /* Length is 16. */
uint32_t vendor; /* NX_VENDOR_ID. */
uint16_t subtype; /* NXAST_RESUBMIT. */
uint16_t in_port; /* New in_port for checking flow table. */
};
OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16);
+/* Action structure for NXAST_SET_TUNNEL. */
+struct nx_action_set_tunnel {
+ uint16_t type; /* OFPAT_VENDOR. */
+ uint16_t len; /* Length is 16. */
+ uint32_t vendor; /* NX_VENDOR_ID. */
+ uint16_t subtype; /* NXAST_SET_TUNNEL. */
+ uint8_t pad[2];
+ uint32_t tun_id; /* Tunnel ID. */
+};
+OFP_ASSERT(sizeof(struct nx_action_set_tunnel) == 16);
+
/* Header for Nicira-defined actions. */
struct nx_action_header {
uint16_t type; /* OFPAT_VENDOR. */
- uint16_t len; /* Length is 8. */
+ uint16_t len; /* Length is 16. */
uint32_t vendor; /* NX_VENDOR_ID. */
uint16_t subtype; /* NXAST_*. */
uint8_t pad[6];
};
OFP_ASSERT(sizeof(struct nx_action_header) == 16);
+/* Wildcard for tunnel ID. */
+#define NXFW_TUN_ID (1 << 25)
+
+#define NXFW_ALL NXFW_TUN_ID
+#define OVSFW_ALL (OFPFW_ALL | NXFW_ALL)
+
#endif /* openflow/nicira-ext.h */
* @arg: Argument value whose meaning depends on @type.
*
* For @type == %_ODPL_MISS_NR, the header is followed by packet data. The
- * @arg member is unused and set to 0.
+ * @arg member is the ID (in network byte order) of the tunnel that
+ * encapsulated this packet. It is 0 if the packet was not received on a tunnel.
*
* For @type == %_ODPL_ACTION_NR, the header is followed by packet data. The
* @arg member is copied from the &struct odp_action_controller that caused
};
struct odp_flow_key {
+ __be32 tun_id; /* Encapsulating tunnel ID. */
__be32 nw_src; /* IP source address. */
__be32 nw_dst; /* IP destination address. */
__u16 in_port; /* Input switch port. */
#define ODPAT_SET_NW_TOS 10 /* IP ToS/DSCP field (6 bits). */
#define ODPAT_SET_TP_SRC 11 /* TCP/UDP source port. */
#define ODPAT_SET_TP_DST 12 /* TCP/UDP destination port. */
-#define ODPAT_N_ACTIONS 13
+#define ODPAT_SET_TUNNEL 13 /* Set the encapsulating tunnel ID. */
+#define ODPAT_N_ACTIONS 14
struct odp_action_output {
__u16 type; /* ODPAT_OUTPUT. */
__u32 arg; /* Copied to struct odp_msg 'arg' member. */
};
+struct odp_action_tunnel {
+ __u16 type; /* ODPAT_SET_TUNNEL. */
+ __u16 reserved;
+ __be32 tun_id; /* Tunnel ID. */
+};
+
/* Action structure for ODPAT_SET_VLAN_VID. */
struct odp_action_vlan_vid {
__u16 type; /* ODPAT_SET_VLAN_VID. */
struct odp_action_output output;
struct odp_action_output_group output_group;
struct odp_action_controller controller;
+ struct odp_action_tunnel tunnel;
struct odp_action_vlan_vid vlan_vid;
struct odp_action_vlan_pcp vlan_pcp;
struct odp_action_dl_addr dl_addr;
/* Converts the flow in 'flow' into a cls_rule in 'rule', with the given
* 'wildcards' and 'priority'.*/
void
-cls_rule_from_flow(struct cls_rule *rule, const flow_t *flow,
- uint32_t wildcards, unsigned int priority)
+cls_rule_from_flow(const flow_t *flow, uint32_t wildcards,
+ unsigned int priority, struct cls_rule *rule)
{
assert(!flow->reserved[0] && !flow->reserved[1] && !flow->reserved[2]);
rule->flow = *flow;
}
/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
- * 'priority'. */
+ * 'priority'. If 'tun_id_from_cookie' is set then the upper 32 bits of
+ * 'cookie' are stored in the rule as the tunnel ID. */
void
-cls_rule_from_match(struct cls_rule *rule, const struct ofp_match *match,
- unsigned int priority)
+cls_rule_from_match(const struct ofp_match *match, unsigned int priority,
+ bool tun_id_from_cookie, uint64_t cookie,
+ struct cls_rule *rule)
{
uint32_t wildcards;
- flow_from_match(&rule->flow, &wildcards, match);
+ flow_from_match(match, tun_id_from_cookie, cookie, &rule->flow, &wildcards);
flow_wildcards_init(&rule->wc, wildcards);
rule->priority = rule->wc.wildcards ? priority : UINT16_MAX;
rule->table_idx = table_idx_from_wildcards(rule->wc.wildcards);
struct cls_rule target;
int i;
- cls_rule_from_flow(&target, flow, 0, 0);
+ cls_rule_from_flow(flow, 0, 0, &target);
for (i = 0; i < CLS_N_FIELDS; i++) {
struct cls_rule *rule = search_table(&cls->tables[i], i, &target);
if (rule && (!best || rule->priority > best->priority)) {
return search_exact_table(cls, flow_hash(target, 0), target);
}
- assert(wildcards == (wildcards & OFPFW_ALL));
+ assert(wildcards == (wildcards & OVSFW_ALL));
table_idx = table_idx_from_wildcards(wildcards);
hash = hash_fields(target, table_idx);
HMAP_FOR_EACH_WITH_HASH (bucket, struct cls_bucket, hmap_node, hash,
true : false;
}
- cls_rule_from_flow(&target_rule, target, wildcards, priority);
+ cls_rule_from_flow(target, wildcards, priority, &target_rule);
for (tbl = &cls->tables[0]; tbl < &cls->tables[CLS_N_FIELDS]; tbl++) {
struct cls_bucket *bucket;
#include "flow.h"
#include "hmap.h"
#include "list.h"
+#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
/* Number of bytes of fields in a rule. */
-#define CLS_N_BYTES 31
+#define CLS_N_BYTES 37
/* Fields in a rule.
*
/* wildcard bit(s) member name name */ \
/* ----------------- ----------- -------- */ \
CLS_FIELD(OFPFW_IN_PORT, in_port, IN_PORT) \
+ CLS_FIELD(NXFW_TUN_ID, tun_id, TUN_ID) \
CLS_FIELD(OFPFW_DL_VLAN, dl_vlan, DL_VLAN) \
CLS_FIELD(OFPFW_DL_VLAN_PCP, dl_vlan_pcp, DL_VLAN_PCP) \
CLS_FIELD(OFPFW_DL_SRC, dl_src, DL_SRC) \
unsigned int table_idx; /* Index into struct classifier 'tables'. */
};
-void cls_rule_from_flow(struct cls_rule *, const flow_t *, uint32_t wildcards,
- unsigned int priority);
-void cls_rule_from_match(struct cls_rule *, const struct ofp_match *,
- unsigned int priority);
+void cls_rule_from_flow(const flow_t *, uint32_t wildcards,
+ unsigned int priority, struct cls_rule *);
+void cls_rule_from_match(const struct ofp_match *, unsigned int priority,
+ bool tun_id_from_cookie, uint64_t cookie,
+ struct cls_rule *);
char *cls_rule_to_string(const struct cls_rule *);
void cls_rule_print(const struct cls_rule *);
void cls_rule_moved(struct classifier *,
goto drained;
}
- flow_extract(&b, 0, &flow);
+ flow_extract(&b, 0, 0, &flow);
if (flow.dl_type != htons(ETH_TYPE_IP)
|| flow.nw_proto != IP_TYPE_UDP
|| flow.tp_dst != htons(DHCP_CLIENT_PORT)
* if we don't. */
copy = *packet;
}
- flow_extract(©, in_port, &flow);
+ flow_extract(©, 0, in_port, &flow);
error = dp_netdev_execute_actions(dp, ©, &flow, actions, n_actions);
if (mutates) {
ofpbuf_uninit(©);
struct dp_netdev_flow *flow;
flow_t key;
- if (flow_extract(packet, port->port_no, &key) && dp->drop_frags) {
+ if (flow_extract(packet, 0, port->port_no, &key) && dp->drop_frags) {
dp->n_frags++;
return;
}
#include "openflow/openflow.h"
#include "openvswitch/datapath-protocol.h"
#include "packets.h"
+#include "xtoxll.h"
#include "vlog.h"
#define THIS_MODULE VLM_flow
return ofpbuf_try_pull(packet, VLAN_HEADER_LEN);
}
-/* Returns 1 if 'packet' is an IP fragment, 0 otherwise. */
+/* Returns 1 if 'packet' is an IP fragment, 0 otherwise.
+ * 'tun_id' is in network byte order, while 'in_port' is in host byte order.
+ * These byte orders are the same as they are in struct odp_flow_key. */
int
-flow_extract(struct ofpbuf *packet, uint16_t in_port, flow_t *flow)
+flow_extract(struct ofpbuf *packet, uint32_t tun_id, uint16_t in_port,
+ flow_t *flow)
{
struct ofpbuf b = *packet;
struct eth_header *eth;
COVERAGE_INC(flow_extract);
memset(flow, 0, sizeof *flow);
- flow->dl_vlan = htons(OFP_VLAN_NONE);
+ flow->tun_id = tun_id;
flow->in_port = in_port;
+ flow->dl_vlan = htons(OFP_VLAN_NONE);
packet->l2 = b.data;
packet->l3 = NULL;
/* Extract 'flow' with 'wildcards' into the OpenFlow match structure
* 'match'. */
void
-flow_to_match(const flow_t *flow, uint32_t wildcards, struct ofp_match *match)
+flow_to_match(const flow_t *flow, uint32_t wildcards, bool tun_id_from_cookie,
+ struct ofp_match *match)
{
+ if (!tun_id_from_cookie) {
+ wildcards &= OFPFW_ALL;
+ }
match->wildcards = htonl(wildcards);
+
match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
: flow->in_port);
match->dl_vlan = flow->dl_vlan;
}
void
-flow_from_match(flow_t *flow, uint32_t *wildcards,
- const struct ofp_match *match)
+flow_from_match(const struct ofp_match *match, bool tun_id_from_cookie,
+ uint64_t cookie, flow_t *flow, uint32_t *wildcards)
{
if (wildcards) {
*wildcards = ntohl(match->wildcards);
+
+ if (!tun_id_from_cookie) {
+ *wildcards |= NXFW_TUN_ID;
+ }
}
flow->nw_src = match->nw_src;
flow->nw_dst = match->nw_dst;
+ if (tun_id_from_cookie) {
+ flow->tun_id = htonl(ntohll(cookie) >> 32);
+ }
flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
: ntohs(match->in_port));
flow->dl_vlan = match->dl_vlan;
void
flow_format(struct ds *ds, const flow_t *flow)
{
- ds_put_format(ds, "in_port%04x:vlan%d:pcp%d mac"ETH_ADDR_FMT
- "->"ETH_ADDR_FMT" type%04x proto%"PRId8" tos%"PRIu8
- " ip"IP_FMT"->"IP_FMT" port%d->%d",
- flow->in_port, ntohs(flow->dl_vlan), flow->dl_vlan_pcp,
- ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
- ntohs(flow->dl_type), flow->nw_proto, flow->nw_tos,
- IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst),
- ntohs(flow->tp_src), ntohs(flow->tp_dst));
+ ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16
+ ":vlan%"PRIu16":pcp%"PRIu8
+ " mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT
+ " type%04"PRIx16
+ " proto%"PRIu8
+ " tos%"PRIu8
+ " ip"IP_FMT"->"IP_FMT
+ " port%"PRIu16"->%"PRIu16,
+ ntohl(flow->tun_id),
+ flow->in_port,
+ ntohs(flow->dl_vlan),
+ flow->dl_vlan_pcp,
+ ETH_ADDR_ARGS(flow->dl_src),
+ ETH_ADDR_ARGS(flow->dl_dst),
+ ntohs(flow->dl_type),
+ flow->nw_proto,
+ flow->nw_tos,
+ IP_ARGS(&flow->nw_src),
+ IP_ARGS(&flow->nw_dst),
+ ntohs(flow->tp_src),
+ ntohs(flow->tp_dst));
}
void
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
#include "hash.h"
-#include "openflow/openflow.h"
#include "openvswitch/datapath-protocol.h"
#include "util.h"
typedef struct odp_flow_key flow_t;
-int flow_extract(struct ofpbuf *, uint16_t in_port, flow_t *);
+int flow_extract(struct ofpbuf *, uint32_t tun_id, uint16_t in_port, flow_t *);
void flow_extract_stats(const flow_t *flow, struct ofpbuf *packet,
struct odp_flow_stats *stats);
-void flow_to_match(const flow_t *, uint32_t wildcards, struct ofp_match *);
-void flow_from_match(flow_t *, uint32_t *wildcards, const struct ofp_match *);
+void flow_to_match(const flow_t *, uint32_t wildcards, bool tun_id_cookie,
+ struct ofp_match *);
+void flow_from_match(const struct ofp_match *, bool tun_id_from_cookie,
+ uint64_t cookie, flow_t *, uint32_t *wildcards);
char *flow_to_string(const flow_t *);
void flow_format(struct ds *, const flow_t *);
void flow_print(FILE *, const flow_t *);
static inline void
flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards)
{
- wc->wildcards = wildcards & OFPFW_ALL;
+ wc->wildcards = wildcards & OVSFW_ALL;
wc->nw_src_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_SRC_SHIFT);
wc->nw_dst_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_DST_SHIFT);
}
pkt_len = ntohs(opi->header.length) - pkt_ofs;
pkt.data = opi->data;
pkt.size = pkt_len;
- flow_extract(&pkt, in_port, &flow);
+ flow_extract(&pkt, 0, in_port, &flow);
if (may_learn(sw, in_port) && sw->ml) {
if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port)) {
case ODPAT_CONTROLLER:
ds_put_format(ds, "ctl(%"PRIu32")", a->controller.arg);
break;
+ case ODPAT_SET_TUNNEL:
+ ds_put_format(ds, "set_tunnel(0x%08"PRIx32")", ntohl(a->tunnel.tun_id));
+ break;
case ODPAT_SET_VLAN_VID:
ds_put_format(ds, "set_vlan(%"PRIu16")", ntohs(a->vlan_vid.vlan_vid));
break;
struct ofp_match match;
packet.data = (void *) op->data;
packet.size = data_len;
- flow_extract(&packet, ntohs(op->in_port), &flow);
- flow_to_match(&flow, 0, &match);
+ flow_extract(&packet, 0, ntohs(op->in_port), &flow);
+ flow_to_match(&flow, 0, false, &match);
ofp_print_match(string, &match, verbosity);
ds_put_char(string, '\n');
}
break;
}
+ case NXAST_SET_TUNNEL: {
+ const struct nx_action_set_tunnel *nast =
+ (struct nx_action_set_tunnel *)nah;
+ ds_put_format(string, "set_tunnel:0x%08"PRIx32, ntohl(nast->tun_id));
+ break;
+ }
+
default:
ds_put_format(string, "***unknown Nicira action:%d***\n",
ntohs(nah->subtype));
skip_type = false;
}
}
+ if (w & NXFW_TUN_ID) {
+ ds_put_cstr(&f, "tun_id_wild,");
+ }
print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
"%d", ntohs(om->in_port));
print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
switch (ntohs(nah->subtype)) {
case NXAST_RESUBMIT:
+ case NXAST_SET_TUNNEL:
return check_action_exact_len(a, len, 16);
default:
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
uint32_t wc;
- wc = ntohl(m->wildcards) & OFPFW_ALL;
+ wc = ntohl(m->wildcards) & OVSFW_ALL;
if (wc & OFPFW_DL_TYPE) {
m->dl_type = 0;
fo->next_bogus_packet_in = LLONG_MAX;
memset(&flow, 0, sizeof flow);
- ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY);
+ ofproto_delete_flow(fo->ofproto, &flow, OVSFW_ALL, FAIL_OPEN_PRIORITY);
}
}
action.output.len = htons(sizeof action);
action.output.port = htons(OFPP_NORMAL);
memset(&flow, 0, sizeof flow);
- ofproto_add_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY,
+ ofproto_add_flow(fo->ofproto, &flow, OVSFW_ALL, FAIL_OPEN_PRIORITY,
&action, 1, 0);
}
}
rule->installed = true;
rule->flow = *flow;
- rule->wildcards = OFPFW_ALL & ~fixed_fields;
+ rule->wildcards = OVSFW_ALL & ~fixed_fields;
rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx);
action.type = htons(OFPAT_OUTPUT);
/* Get packet payload and extract flow. */
payload.data = (union odp_action *) (actions + n_actions);
payload.size = msg->length - min_size;
- flow_extract(&payload, msg->port, &flow);
+ flow_extract(&payload, 0, msg->port, &flow);
/* Build a flow sample */
memset(&fs, 0, sizeof fs);
bool need_revalidate;
long long int next_expiration;
struct tag_set revalidate_set;
+ bool tun_id_from_cookie;
/* OpenFlow connections. */
struct list all_conns;
rule = rule_create(p, NULL, actions, n_actions,
idle_timeout >= 0 ? idle_timeout : 5 /* XXX */,
0, 0, false);
- cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
+ cls_rule_from_flow(flow, wildcards, priority, &rule->cr);
rule_insert(p, rule, NULL, 0);
}
/* Send the packet and credit it to the rule. */
if (packet) {
flow_t flow;
- flow_extract(packet, in_port, &flow);
+ flow_extract(packet, 0, in_port, &flow);
rule_execute(p, rule, packet, &flow);
}
rule->idle_timeout, rule->hard_timeout,
0, false);
COVERAGE_INC(ofproto_subrule_create);
- cls_rule_from_flow(&subrule->cr, flow, 0,
- (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
- : rule->cr.priority));
+ cls_rule_from_flow(flow, 0, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
+ : rule->cr.priority), &subrule->cr);
classifier_insert_exact(&ofproto->cls, &subrule->cr);
return subrule;
const struct nx_action_header *nah)
{
const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ union odp_action *oa;
int subtype = ntohs(nah->subtype);
assert(nah->vendor == htonl(NX_VENDOR_ID));
xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
break;
+ case NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) nah;
+ oa = odp_actions_add(ctx->out, ODPAT_SET_TUNNEL);
+ ctx->flow.tun_id = oa->tunnel.tun_id = nast->tun_id;
+ break;
+
/* If you add a new action here that modifies flow data, don't forget to
* update the flow key in ctx->flow in the same key. */
buffer = NULL;
}
- flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
+ flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
&flow, p, &payload, &actions, NULL, NULL, NULL);
if (error) {
memset(ots, 0, sizeof *ots);
ots->table_id = TABLEID_CLASSIFIER;
strcpy(ots->name, "classifier");
- ots->wildcards = htonl(OFPFW_ALL);
+ ots->wildcards = p->tun_id_from_cookie ? htonl(OVSFW_ALL)
+ : htonl(OFPFW_ALL);
ots->max_entries = htonl(65536);
ots->active_count = htonl(n_wild);
ots->lookup_count = htonll(0); /* XXX */
ofs->length = htons(len);
ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH;
ofs->pad = 0;
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
+ cbdata->ofproto->tun_id_from_cookie, &ofs->match);
ofs->duration_sec = htonl(sec);
ofs->duration_nsec = htonl(msec * 1000000);
ofs->cookie = rule->flow_cookie;
cbdata.ofconn = ofconn;
cbdata.out_port = fsr->out_port;
cbdata.msg = start_stats_reply(osr, 1024);
- cls_rule_from_match(&target, &fsr->match, 0);
+ cls_rule_from_match(&fsr->match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target,
table_id_to_include(fsr->table_id),
flow_stats_cb, &cbdata);
}
query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
+ cbdata->ofproto->tun_id_from_cookie, &match);
ds_put_format(results, "duration=%llds, ",
(time_msec() - rule->created) / 1000);
struct flow_stats_ds_cbdata cbdata;
memset(&match, 0, sizeof match);
- match.wildcards = htonl(OFPFW_ALL);
+ match.wildcards = htonl(OVSFW_ALL);
cbdata.ofproto = p;
cbdata.results = results;
- cls_rule_from_match(&target, &match, 0);
+ cls_rule_from_match(&match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
flow_stats_ds_cb, &cbdata);
}
cbdata.packet_count = 0;
cbdata.byte_count = 0;
cbdata.n_flows = 0;
- cls_rule_from_match(&target, &asr->match, 0);
+ cls_rule_from_match(&asr->match, 0, false, 0, &target);
classifier_for_each_match(&p->cls, &target,
table_id_to_include(asr->table_id),
aggregate_stats_cb, &cbdata);
flow_t flow;
uint32_t wildcards;
- flow_from_match(&flow, &wildcards, &ofm->match);
+ flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+ &flow, &wildcards);
if (classifier_rule_overlaps(&p->cls, &flow, wildcards,
ntohs(ofm->priority))) {
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
n_actions, ntohs(ofm->idle_timeout),
ntohs(ofm->hard_timeout), ofm->cookie,
ofm->flags & htons(OFPFF_SEND_FLOW_REM));
- cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
+ cls_rule_from_match(&ofm->match, ntohs(ofm->priority),
+ p->tun_id_from_cookie, ofm->cookie, &rule->cr);
error = 0;
if (ofm->buffer_id != htonl(UINT32_MAX)) {
uint32_t wildcards;
flow_t flow;
- flow_from_match(&flow, &wildcards, &ofm->match);
+ flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+ &flow, &wildcards);
return rule_from_cls_rule(classifier_find_rule_exactly(
&p->cls, &flow, wildcards,
ntohs(ofm->priority)));
return error;
}
- flow_extract(packet, in_port, &flow);
+ flow_extract(packet, 0, in_port, &flow);
rule_execute(ofproto, rule, packet, &flow);
ofpbuf_delete(packet);
cbdata.n_actions = n_actions;
cbdata.match = NULL;
- cls_rule_from_match(&target, &ofm->match, 0);
+ cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie,
+ &target);
classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
modify_flows_cb, &cbdata);
cbdata.ofproto = p;
cbdata.out_port = ofm->out_port;
- cls_rule_from_match(&target, &ofm->match, 0);
+ cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie,
+ &target);
classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
delete_flows_cb, &cbdata);
}
}
+static int
+handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg)
+{
+ int error;
+
+ error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
+ if (error) {
+ return error;
+ }
+
+ p->tun_id_from_cookie = !!msg->set;
+ return 0;
+}
+
static int
handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
{
struct nicira_header *nh;
if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
+ VLOG_WARN_RL(&rl, "received vendor message of length %zu "
+ "(expected at least %zu)",
+ ntohs(ovh->header.length), sizeof(struct ofp_vendor_header));
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
if (ovh->vendor != htonl(NX_VENDOR_ID)) {
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
}
if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
+ VLOG_WARN_RL(&rl, "received Nicira vendor message of length %zu "
+ "(expected at least %zu)",
+ ntohs(ovh->header.length), sizeof(struct nicira_header));
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
}
case NXT_STATUS_REQUEST:
return switch_status_handle_request(p->switch_status, ofconn->rconn,
msg);
+
+ case NXT_TUN_ID_FROM_COOKIE:
+ return handle_tun_id_from_cookie(p, msg);
}
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
payload.data = msg + 1;
payload.size = msg->length - sizeof *msg;
- flow_extract(&payload, msg->port, &flow);
+ flow_extract(&payload, msg->arg, msg->port, &flow);
/* Check with in-band control to see if this packet should be sent
* to the local port regardless of the flow table. */
}
static struct ofpbuf *
-compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
+compose_flow_removed(struct ofproto *p, const struct rule *rule,
+ long long int now, uint8_t reason)
{
struct ofp_flow_removed *ofr;
struct ofpbuf *buf;
uint32_t msec = tdiff - (sec * 1000);
ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
- flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match);
+ flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, p->tun_id_from_cookie,
+ &ofr->match);
ofr->cookie = rule->flow_cookie;
ofr->priority = htons(rule->cr.priority);
ofr->reason = reason;
if (prev) {
queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
} else {
- buf = compose_flow_removed(rule, now, reason);
+ buf = compose_flow_removed(p, rule, now, reason);
}
prev = ofconn;
}
T_HTONL(0xc0a04455) };
static uint32_t nw_dst_values[] = { T_HTONL(0xc0a80002),
T_HTONL(0xc0a04455) };
+static uint32_t tun_id_values[] = { 0, 0xffff0000 };
static uint16_t in_port_values[] = { T_HTONS(1), T_HTONS(OFPP_LOCAL) };
static uint16_t dl_vlan_values[] = { T_HTONS(101), T_HTONS(0) };
static uint8_t dl_vlan_pcp_values[] = { 7, 0 };
static void
init_values(void)
{
+ values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0];
+ values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1];
+
values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
#define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
#define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
+#define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
#define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
#define N_DL_VLAN_VALUES ARRAY_SIZE(dl_vlan_values)
#define N_DL_VLAN_PCP_VALUES ARRAY_SIZE(dl_vlan_pcp_values)
#define N_FLOW_VALUES (N_NW_SRC_VALUES * \
N_NW_DST_VALUES * \
+ N_TUN_ID_VALUES * \
N_IN_PORT_VALUES * \
N_DL_VLAN_VALUES * \
N_DL_VLAN_PCP_VALUES * \
x = i;
flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
+ flow.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
flow.dl_vlan = dl_vlan_values[get_value(&x, N_DL_VLAN_VALUES)];
flow.dl_vlan_pcp = dl_vlan_pcp_values[get_value(&x,
}
rule = xzalloc(sizeof *rule);
- cls_rule_from_flow(&rule->cls_rule, &flow, wildcards,
- !wildcards ? UINT_MAX : priority);
+ cls_rule_from_flow(&flow, wildcards, !wildcards ? UINT_MAX : priority,
+ &rule->cls_rule);
return rule;
}
ovs_fatal(retval, "error reading pcap file");
}
- flow_extract(packet, 1, &flow);
- flow_to_match(&flow, 0, &extracted_match);
+ flow_extract(packet, 0, 1, &flow);
+ flow_to_match(&flow, 0, false, &extracted_match);
if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
char *exp_s = ofp_match_to_string(&expected_match, 2);
255, inclusive. Note that the two lower reserved bits are never
modified.
.
+.RE
+.IP
+The following actions are Nicira vendor extensions that, as of this writing, are
+only known to be implemented by Open vSwitch:
+.
+.RS
+.
.IP \fBresubmit\fB:\fIport\fR
Re-searches the OpenFlow flow table with the \fBin_port\fR field
replaced by \fIport\fR and executes the actions found, if any, in
addition to any other actions in this flow entry. Recursive
\fBresubmit\fR actions are ignored.
-.IP
-This action is a Nicira vendor extension that, as of this writing, is
-only known to be implemented by Open vSwitch.
+.
+.IP \fBset_tunnel\fB:\fIid\fR
+If outputting to a port that encapsulates the packet in a tunnel and supports
+an identifier (such as GRE), sets the identifier to \fBid\fR.
.
.RE
.
nar->vendor = htonl(NX_VENDOR_ID);
nar->subtype = htons(NXAST_RESUBMIT);
nar->in_port = htons(str_to_u32(arg));
+ } else if (!strcasecmp(act, "set_tunnel")) {
+ struct nx_action_set_tunnel *nast;
+ nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
+ nast->vendor = htonl(NX_VENDOR_ID);
+ nast->subtype = htons(NXAST_SET_TUNNEL);
+ nast->tun_id = htonl(str_to_u32(arg));
} else if (!strcasecmp(act, "output")) {
put_output_action(b, str_to_u32(arg));
} else if (!strcasecmp(act, "drop")) {
parse_field(const char *name, const struct field **f_out)
{
#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
- static const struct field fields[] = {
+ static const struct field fields[] = {
{ "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
{ "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
{ "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 },
*hard_timeout = atoi(value);
} else if (cookie && !strcmp(name, "cookie")) {
*cookie = str_to_u64(value);
+ } else if (!strcmp(name, "tun_id_wild")) {
+ wildcards |= NXFW_TUN_ID;
} else if (parse_field(name, &f)) {
void *data = (char *) match + f->offset;
if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
vconn_close(vconn);
}
+static void
+do_tun_cookie(int argc OVS_UNUSED, char *argv[])
+{
+ struct nxt_tun_id_cookie *tun_id_cookie;
+ struct ofpbuf *buffer;
+ struct vconn *vconn;
+
+ tun_id_cookie = make_openflow(sizeof *tun_id_cookie, OFPT_VENDOR, &buffer);
+
+ tun_id_cookie->vendor = htonl(NX_VENDOR_ID);
+ tun_id_cookie->subtype = htonl(NXT_TUN_ID_FROM_COOKIE);
+ tun_id_cookie->set = !strcmp(argv[2], "true");
+
+ open_vconn(argv[1], &vconn);
+ send_openflow_buffer(vconn, buffer);
+ vconn_close(vconn);
+}
+
static void
do_monitor(int argc OVS_UNUSED, char *argv[])
{
{ "add-flows", 2, 2, do_add_flows },
{ "mod-flows", 2, 2, do_mod_flows },
{ "del-flows", 1, 2, do_del_flows },
+ { "tun-cookie", 2, 2, do_tun_cookie },
{ "dump-ports", 1, 2, do_dump_ports },
{ "mod-port", 3, 3, do_mod_port },
{ "probe", 1, 1, do_probe },
action.output.len = htons(sizeof action);
action.output.port = htons(OFPP_NORMAL);
memset(&flow, 0, sizeof flow);
- ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
- &action, 1, 0);
+ ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
ofproto_set_in_band(br->ofproto, false);
ofproto_set_max_backoff(br->ofproto, 1);
n_packets++;
compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
e->mac);
- flow_extract(&packet, ODPP_NONE, &flow);
+ flow_extract(&packet, 0, ODPP_NONE, &flow);
retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
&packet);
if (retval) {