Skip to content

Commit

Permalink
Add support for 802.1ad (QinQ tunneling)
Browse files Browse the repository at this point in the history
Flow key handling changes:
 - Add VLAN header array in struct flow, to record multiple 802.1q VLAN
   headers.
 - Add dpif multi-VLAN capability probing. If datapath supports
   multi-VLAN, increase the maximum depth of nested OVS_KEY_ATTR_ENCAP.

Refactor VLAN handling in dpif-xlate:
 - Introduce 'xvlan' to track VLAN stack during flow processing.
 - Input and output VLAN translation according to the xbundle type.

Push VLAN action support:
 - Allow ethertype 0x88a8 in VLAN headers and push_vlan action.
 - Support push_vlan on dot1q packets.

Use other_config:vlan-limit in table Open_vSwitch to limit maximum VLANs
that can be matched. This allows us to preserve backwards compatibility.

Add test cases for VLAN depth limit, Multi-VLAN actions and QinQ VLAN
handling

Co-authored-by: Thomas F Herbert <[email protected]>
Signed-off-by: Thomas F Herbert <[email protected]>
Co-authored-by: Xiao Liang <[email protected]>
Signed-off-by: Xiao Liang <[email protected]>
Signed-off-by: Eric Garver <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
3 people authored and blp committed Mar 16, 2017
1 parent 4c71600 commit f0fb825
Show file tree
Hide file tree
Showing 33 changed files with 1,110 additions and 578 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Post-v2.7.0
'ovs-appctl vlog' commands for 'dpdk' module. Lower bound
still can be configured via extra arguments for DPDK EAL.
- The "learn" action now supports a "limit" option (see ovs-ofctl(8)).
- New support for multiple VLANs (802.1ad or "QinQ").

v2.7.0 - 21 Feb 2017
---------------------
Expand Down
19 changes: 15 additions & 4 deletions include/openvswitch/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
#define FLOW_WC_SEQ 37
#define FLOW_WC_SEQ 38

/* Number of Open vSwitch extension 32-bit registers. */
#define FLOW_N_REGS 16
Expand Down Expand Up @@ -64,6 +64,16 @@ const char *flow_tun_flag_to_string(uint32_t flags);
/* Maximum number of supported SAMPLE action nesting. */
#define FLOW_MAX_SAMPLE_NESTING 10

/* Maximum number of supported VLAN headers.
*
* We require this to be a multiple of 2 so that vlans[] in struct flow is a
* multiple of 64 bits. */
#define FLOW_MAX_VLAN_HEADERS 2
BUILD_ASSERT_DECL(FLOW_MAX_VLAN_HEADERS % 2 == 0);

/* Legacy maximum VLAN headers */
#define LEGACY_MAX_VLAN_HEADERS 1

/*
* A flow in the network.
*
Expand Down Expand Up @@ -107,7 +117,8 @@ struct flow {
struct eth_addr dl_dst; /* Ethernet destination address. */
struct eth_addr dl_src; /* Ethernet source address. */
ovs_be16 dl_type; /* Ethernet frame type. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
uint8_t pad2[2]; /* Pad to 64 bits. */
union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS]; /* VLANs */
ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack
(with padding). */
/* L3 (64-bit aligned) */
Expand Down Expand Up @@ -146,8 +157,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);

/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
== sizeof(struct flow_tnl) + 292
&& FLOW_WC_SEQ == 37);
== sizeof(struct flow_tnl) + 300
&& FLOW_WC_SEQ == 38);

/* Incremental points at which flow classification may be performed in
* segments.
Expand Down
10 changes: 9 additions & 1 deletion include/openvswitch/ofp-actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ struct vl_mff_map;
OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact, "set_vlan_vid") \
OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact, "set_vlan_pcp") \
OFPACT(STRIP_VLAN, ofpact_null, ofpact, "strip_vlan") \
OFPACT(PUSH_VLAN, ofpact_null, ofpact, "push_vlan") \
OFPACT(PUSH_VLAN, ofpact_push_vlan, ofpact, "push_vlan") \
OFPACT(SET_ETH_SRC, ofpact_mac, ofpact, "mod_dl_src") \
OFPACT(SET_ETH_DST, ofpact_mac, ofpact, "mod_dl_dst") \
OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact, "mod_nw_src") \
Expand Down Expand Up @@ -390,6 +390,14 @@ struct ofpact_vlan_pcp {
bool flow_has_vlan; /* VLAN present at action validation time? */
};

/* OFPACT_PUSH_VLAN.
*
* Used for OFPAT11_PUSH_VLAN. */
struct ofpact_push_vlan {
struct ofpact ofpact;
ovs_be16 ethertype;
};

/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
*
* Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
Expand Down
8 changes: 8 additions & 0 deletions include/openvswitch/packets.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,12 @@ union flow_in_port {
ofp_port_t ofp_port;
};

union flow_vlan_hdr {
ovs_be32 qtag;
struct {
ovs_be16 tpid; /* ETH_TYPE_VLAN_DOT1Q or ETH_TYPE_DOT1AD */
ovs_be16 tci;
};
};

#endif /* packets.h */
29 changes: 21 additions & 8 deletions lib/dpctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,7 @@ dpctl_normalize_actions(int argc, const char *argv[],
struct ds s;
int left;
int i, error;
int encaps = 0;

ds_init(&s);

Expand Down Expand Up @@ -1464,12 +1465,14 @@ dpctl_normalize_actions(int argc, const char *argv[],
const struct ovs_action_push_vlan *push;
switch(nl_attr_type(a)) {
case OVS_ACTION_ATTR_POP_VLAN:
flow.vlan_tci = htons(0);
flow_pop_vlan(&flow, NULL);
continue;

case OVS_ACTION_ATTR_PUSH_VLAN:
flow_push_vlan_uninit(&flow, NULL);
push = nl_attr_get_unspec(a, sizeof *push);
flow.vlan_tci = push->vlan_tci;
flow.vlans[0].tpid = push->vlan_tpid;
flow.vlans[0].tci = push->vlan_tci;
continue;
}

Expand All @@ -1495,12 +1498,22 @@ dpctl_normalize_actions(int argc, const char *argv[],

sort_output_actions(af->actions.data, af->actions.size);

if (af->flow.vlan_tci != htons(0)) {
dpctl_print(dpctl_p, "vlan(vid=%"PRIu16",pcp=%d): ",
vlan_tci_to_vid(af->flow.vlan_tci),
vlan_tci_to_pcp(af->flow.vlan_tci));
} else {
dpctl_print(dpctl_p, "no vlan: ");
for (encaps = 0; encaps < FLOW_MAX_VLAN_HEADERS; encaps ++) {
union flow_vlan_hdr *vlan = &af->flow.vlans[encaps];
if (vlan->tci != htons(0)) {
dpctl_print(dpctl_p, "vlan(");
if (vlan->tpid != htons(ETH_TYPE_VLAN)) {
dpctl_print(dpctl_p, "tpid=0x%04"PRIx16",", vlan->tpid);
}
dpctl_print(dpctl_p, "vid=%"PRIu16",pcp=%d): ",
vlan_tci_to_vid(vlan->tci),
vlan_tci_to_pcp(vlan->tci));
} else {
if (encaps == 0) {
dpctl_print(dpctl_p, "no vlan: ");
}
break;
}
}

if (eth_type_mpls(af->flow.dl_type)) {
Expand Down
79 changes: 43 additions & 36 deletions lib/dpif-netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static struct vlog_rate_limit upcall_rl = VLOG_RATE_LIMIT_INIT(600, 600);
#define DP_NETDEV_CS_UNSUPPORTED_MASK (~(uint32_t)DP_NETDEV_CS_SUPPORTED_MASK)

static struct odp_support dp_netdev_support = {
.max_vlan_headers = SIZE_MAX,
.max_mpls_depth = SIZE_MAX,
.recirc = true,
.ct_state = true,
Expand Down Expand Up @@ -441,7 +442,7 @@ struct dp_netdev_flow {
static void dp_netdev_flow_unref(struct dp_netdev_flow *);
static bool dp_netdev_flow_ref(struct dp_netdev_flow *);
static int dpif_netdev_flow_from_nlattrs(const struct nlattr *, uint32_t,
struct flow *);
struct flow *, bool);

/* A set of datapath actions within a "struct dp_netdev_flow".
*
Expand Down Expand Up @@ -2093,7 +2094,7 @@ dp_netdev_pmd_find_flow(const struct dp_netdev_pmd_thread *pmd,

/* If a UFID is not provided, determine one based on the key. */
if (!ufidp && key && key_len
&& !dpif_netdev_flow_from_nlattrs(key, key_len, &flow)) {
&& !dpif_netdev_flow_from_nlattrs(key, key_len, &flow, false)) {
dpif_flow_hash(pmd->dp->dpif, &flow, sizeof flow, &ufid);
ufidp = &ufid;
}
Expand Down Expand Up @@ -2186,27 +2187,29 @@ static int
dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
const struct nlattr *mask_key,
uint32_t mask_key_len, const struct flow *flow,
struct flow_wildcards *wc)
struct flow_wildcards *wc, bool probe)
{
enum odp_key_fitness fitness;

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()
* disagree on the acceptable form of a mask. Log the problem
* as an error, with enough details to enable debugging. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);

if (!VLOG_DROP_ERR(&rl)) {
struct ds s;

ds_init(&s);
odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
true);
VLOG_ERR("internal error parsing flow mask %s (%s)",
ds_cstr(&s), odp_key_fitness_to_string(fitness));
ds_destroy(&s);
if (!probe) {
/* This should not happen: it indicates that
* odp_flow_key_from_mask() and odp_flow_key_to_mask()
* disagree on the acceptable form of a mask. Log the problem
* as an error, with enough details to enable debugging. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);

if (!VLOG_DROP_ERR(&rl)) {
struct ds s;

ds_init(&s);
odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
true);
VLOG_ERR("internal error parsing flow mask %s (%s)",
ds_cstr(&s), odp_key_fitness_to_string(fitness));
ds_destroy(&s);
}
}

return EINVAL;
Expand All @@ -2217,24 +2220,26 @@ dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,

static int
dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
struct flow *flow)
struct flow *flow, bool probe)
{
odp_port_t in_port;

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
* debugging. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);

if (!VLOG_DROP_ERR(&rl)) {
struct ds s;

ds_init(&s);
odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
ds_destroy(&s);
if (!probe) {
/* 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 debugging. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);

if (!VLOG_DROP_ERR(&rl)) {
struct ds s;

ds_init(&s);
odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
ds_destroy(&s);
}
}

return EINVAL;
Expand Down Expand Up @@ -2466,17 +2471,19 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
struct match match;
ovs_u128 ufid;
int error;
bool probe = put->flags & DPIF_FP_PROBE;

if (put->stats) {
memset(put->stats, 0, sizeof *put->stats);
}
error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow);
error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow,
probe);
if (error) {
return error;
}
error = dpif_netdev_mask_from_nlattrs(put->key, put->key_len,
put->mask, put->mask_len,
&match.flow, &match.wc);
&match.flow, &match.wc, probe);
if (error) {
return error;
}
Expand Down Expand Up @@ -4596,8 +4603,8 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet,
* VLAN. Unless we refactor a lot of code that translates between
* Netlink and struct flow representations, we have to do the same
* here. */
if (!match.wc.masks.vlan_tci) {
match.wc.masks.vlan_tci = htons(0xffff);
if (!match.wc.masks.vlans[0].tci) {
match.wc.masks.vlans[0].tci = htons(0xffff);
}

/* We can't allow the packet batching in the next loop to execute
Expand Down
Loading

0 comments on commit f0fb825

Please sign in to comment.