Skip to content

Commit

Permalink
Add new port VLAN mode "dot1q-tunnel"
Browse files Browse the repository at this point in the history
 - Example:
     ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100
   Pushes another VLAN 100 header on packets (tagged and untagged) on
   ingress, and pops it on egress.
 - Customer VLAN check:
     ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100 cvlans=10,20
   Only customer VLAN of 10 and 20 are allowed.

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
2 people authored and blp committed Mar 17, 2017
1 parent cc3ef00 commit fed8962
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 68 deletions.
3 changes: 2 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ 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").
- New support for multiple VLANs (802.1ad or "QinQ"), including a new
"dot1q-tunnel" port VLAN mode.

v2.7.0 - 21 Feb 2017
---------------------
Expand Down
76 changes: 67 additions & 9 deletions ofproto/ofproto-dpif-xlate.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,13 @@ struct xbundle {
struct lacp *lacp; /* LACP handle or null. */

enum port_vlan_mode vlan_mode; /* VLAN mode. */
uint16_t qinq_ethtype; /* Ethertype of dot1q-tunnel interface
* either 0x8100 or 0x88a8. */
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
unsigned long *cvlans; /* Bitmap of allowed customer vlans,
* NULL if all VLANs are allowed */
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
bool floodable; /* No port has OFPUTIL_PC_NO_FLOOD set? */
bool protected; /* Protected port mode */
Expand Down Expand Up @@ -499,6 +503,7 @@ static bool input_vid_is_valid(const struct xlate_ctx *,
uint16_t vid, struct xbundle *);
static void xvlan_copy(struct xvlan *dst, const struct xvlan *src);
static void xvlan_pop(struct xvlan *src);
static void xvlan_push_uninit(struct xvlan *src);
static void xvlan_extract(const struct flow *, struct xvlan *);
static void xvlan_put(struct flow *, const struct xvlan *);
static void xvlan_input_translate(const struct xbundle *,
Expand Down Expand Up @@ -550,8 +555,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
const struct dpif_backer_support *);
static void xlate_xbundle_set(struct xbundle *xbundle,
enum port_vlan_mode vlan_mode,
int vlan,
unsigned long *trunks,
uint16_t qinq_ethtype, int vlan,
unsigned long *trunks, unsigned long *cvlans,
bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected);
Expand Down Expand Up @@ -857,17 +862,19 @@ xlate_xbridge_set(struct xbridge *xbridge,

static void
xlate_xbundle_set(struct xbundle *xbundle,
enum port_vlan_mode vlan_mode,
int vlan, unsigned long *trunks,
enum port_vlan_mode vlan_mode, uint16_t qinq_ethtype,
int vlan, unsigned long *trunks, unsigned long *cvlans,
bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected)
{
ovs_assert(xbundle->xbridge);

xbundle->vlan_mode = vlan_mode;
xbundle->qinq_ethtype = qinq_ethtype;
xbundle->vlan = vlan;
xbundle->trunks = trunks;
xbundle->cvlans = cvlans;
xbundle->use_priority_tags = use_priority_tags;
xbundle->floodable = floodable;
xbundle->protected = protected;
Expand Down Expand Up @@ -962,8 +969,8 @@ xlate_xbundle_copy(struct xbridge *xbridge, struct xbundle *xbundle)
new_xbundle->name = xstrdup(xbundle->name);
xlate_xbundle_init(new_xcfg, new_xbundle);

xlate_xbundle_set(new_xbundle, xbundle->vlan_mode,
xbundle->vlan, xbundle->trunks,
xlate_xbundle_set(new_xbundle, xbundle->vlan_mode, xbundle->qinq_ethtype,
xbundle->vlan, xbundle->trunks, xbundle->cvlans,
xbundle->use_priority_tags, xbundle->bond, xbundle->lacp,
xbundle->floodable, xbundle->protected);
LIST_FOR_EACH (xport, bundle_node, &xbundle->xports) {
Expand Down Expand Up @@ -1157,8 +1164,8 @@ xlate_remove_ofproto(struct ofproto_dpif *ofproto)
void
xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
const char *name, enum port_vlan_mode vlan_mode,
int vlan,
unsigned long *trunks,
uint16_t qinq_ethtype, int vlan,
unsigned long *trunks, unsigned long *cvlans,
bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected)
Expand All @@ -1179,7 +1186,7 @@ xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
free(xbundle->name);
xbundle->name = xstrdup(name);

xlate_xbundle_set(xbundle, vlan_mode, vlan, trunks,
xlate_xbundle_set(xbundle, vlan_mode, qinq_ethtype, vlan, trunks, cvlans,
use_priority_tags, bond, lacp, floodable, protected);
}

Expand Down Expand Up @@ -1700,6 +1707,12 @@ xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
&& (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)));
}

static bool
xbundle_allows_cvlan(const struct xbundle *bundle, uint16_t vlan)
{
return (!bundle->cvlans || bitmap_is_set(bundle->cvlans, vlan));
}

static bool
xbundle_includes_vlan(const struct xbundle *xbundle, const struct xvlan *xvlan)
{
Expand All @@ -1712,6 +1725,10 @@ xbundle_includes_vlan(const struct xbundle *xbundle, const struct xvlan *xvlan)
case PORT_VLAN_NATIVE_TAGGED:
return xbundle_trunks_vlan(xbundle, xvlan->v[0].vid);

case PORT_VLAN_DOT1Q_TUNNEL:
return xvlan->v[0].vid == xbundle->vlan &&
xbundle_allows_cvlan(xbundle, xvlan->v[1].vid);

default:
OVS_NOT_REACHED();
}
Expand Down Expand Up @@ -1948,6 +1965,15 @@ input_vid_is_valid(const struct xlate_ctx *ctx,
}
return true;

case PORT_VLAN_DOT1Q_TUNNEL:
if (!xbundle_allows_cvlan(in_xbundle, vid)) {
xlate_report_error(ctx, "dropping VLAN %"PRIu16" packet received "
"on dot1q-tunnel port %s that excludes this "
"VLAN", vid, in_xbundle->name);
return false;
}
return true;

default:
OVS_NOT_REACHED();
}
Expand All @@ -1968,6 +1994,13 @@ xvlan_pop(struct xvlan *src)
sizeof(src->v[FLOW_MAX_VLAN_HEADERS - 1]));
}

static void
xvlan_push_uninit(struct xvlan *src)
{
memmove(&src->v[1], &src->v[0], sizeof(src->v) - sizeof(src->v[0]));
memset(&src->v[0], 0, sizeof(src->v[0]));
}

/* Extract VLAN information (headers) from flow */
static void
xvlan_extract(const struct flow *flow, struct xvlan *xvlan)
Expand Down Expand Up @@ -2035,6 +2068,14 @@ xvlan_input_translate(const struct xbundle *in_xbundle,
}
break;

case PORT_VLAN_DOT1Q_TUNNEL:
xvlan_copy(xvlan, in_xvlan);
xvlan_push_uninit(xvlan);
xvlan->v[0].tpid = in_xbundle->qinq_ethtype;
xvlan->v[0].vid = in_xbundle->vlan;
xvlan->v[0].pcp = 0;
break;

default:
OVS_NOT_REACHED();
}
Expand Down Expand Up @@ -2064,11 +2105,26 @@ xvlan_output_translate(const struct xbundle *out_xbundle,
}
break;

case PORT_VLAN_DOT1Q_TUNNEL:
xvlan_copy(out_xvlan, xvlan);
xvlan_pop(out_xvlan);
break;

default:
OVS_NOT_REACHED();
}
}

/* If output xbundle is dot1q-tunnel, set mask bits of cvlan */
static void
check_and_set_cvlan_mask(struct flow_wildcards *wc,
const struct xbundle *xbundle)
{
if (xbundle->vlan_mode == PORT_VLAN_DOT1Q_TUNNEL && xbundle->cvlans) {
wc->masks.vlans[1].tci = htons(0xffff);
}
}

static void
output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
const struct xvlan *xvlan)
Expand All @@ -2080,6 +2136,8 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
bool use_recirc = false;
struct xvlan out_xvlan;

check_and_set_cvlan_mask(ctx->wc, out_xbundle);

xvlan_output_translate(out_xbundle, xvlan, &out_xvlan);
if (out_xbundle->use_priority_tags) {
out_xvlan.v[0].pcp = ntohs(ctx->xin->flow.vlans[0].tci) &
Expand Down
4 changes: 2 additions & 2 deletions ofproto/ofproto-dpif-xlate.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ void xlate_remove_ofproto(struct ofproto_dpif *);

void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
const char *name, enum port_vlan_mode,
int vlan,
unsigned long *trunks,
uint16_t qinq_ethtype, int vlan,
unsigned long *trunks, unsigned long *cvlans,
bool use_priority_tags,
const struct bond *, const struct lacp *,
bool floodable, bool protected);
Expand Down
35 changes: 32 additions & 3 deletions ofproto/ofproto-dpif.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -94,9 +94,11 @@ struct ofbundle {
/* Configuration. */
struct ovs_list ports; /* Contains "struct ofport"s. */
enum port_vlan_mode vlan_mode; /* VLAN mode */
uint16_t qinq_ethtype;
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
unsigned long *cvlans;
struct lacp *lacp; /* LACP if LACP is enabled, otherwise NULL. */
struct bond *bond; /* Nonnull iff more than one port. */
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
Expand Down Expand Up @@ -449,8 +451,8 @@ type_run(const char *type)

HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
xlate_bundle_set(ofproto, bundle, bundle->name,
bundle->vlan_mode,
bundle->vlan, bundle->trunks,
bundle->vlan_mode, bundle->qinq_ethtype,
bundle->vlan, bundle->trunks, bundle->cvlans,
bundle->use_priority_tags,
bundle->bond, bundle->lacp,
bundle->floodable, bundle->protected);
Expand Down Expand Up @@ -2857,6 +2859,7 @@ bundle_destroy(struct ofbundle *bundle)
hmap_remove(&ofproto->bundles, &bundle->hmap_node);
free(bundle->name);
free(bundle->trunks);
free(bundle->cvlans);
lacp_unref(bundle->lacp);
bond_unref(bundle->bond);
free(bundle);
Expand All @@ -2871,6 +2874,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
struct ofport_dpif *port;
struct ofbundle *bundle;
unsigned long *trunks = NULL;
unsigned long *cvlans = NULL;
int vlan;
size_t i;
bool ok;
Expand All @@ -2895,8 +2899,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,

ovs_list_init(&bundle->ports);
bundle->vlan_mode = PORT_VLAN_TRUNK;
bundle->qinq_ethtype = ETH_TYPE_VLAN_8021AD;
bundle->vlan = -1;
bundle->trunks = NULL;
bundle->cvlans = NULL;
bundle->use_priority_tags = s->use_priority_tags;
bundle->lacp = NULL;
bundle->bond = NULL;
Expand Down Expand Up @@ -2961,6 +2967,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
need_flush = true;
}

if (s->qinq_ethtype != bundle->qinq_ethtype) {
bundle->qinq_ethtype = s->qinq_ethtype;
need_flush = true;
}

/* Set VLAN tag. */
vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1
: s->vlan >= 0 && s->vlan <= 4095 ? s->vlan
Expand Down Expand Up @@ -2998,6 +3009,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,
}
break;

case PORT_VLAN_DOT1Q_TUNNEL:
cvlans = CONST_CAST(unsigned long *, s->cvlans);
break;

default:
OVS_NOT_REACHED();
}
Expand All @@ -3015,6 +3030,20 @@ bundle_set(struct ofproto *ofproto_, void *aux,
free(trunks);
}

if (!vlan_bitmap_equal(cvlans, bundle->cvlans)) {
free(bundle->cvlans);
if (cvlans == s->cvlans) {
bundle->cvlans = vlan_bitmap_clone(cvlans);
} else {
bundle->cvlans = cvlans;
cvlans = NULL;
}
need_flush = true;
}
if (cvlans != s->cvlans) {
free(cvlans);
}

/* Bonding. */
if (!ovs_list_is_short(&bundle->ports)) {
bundle->ofproto->has_bonded_bundles = true;
Expand Down
8 changes: 7 additions & 1 deletion ofproto/ofproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,11 @@ enum port_vlan_mode {
/* Untagged incoming packets are part of 'vlan', as are incoming packets
* tagged with 'vlan'. Outgoing packets tagged with 'vlan' are untagged.
* Other VLANs in 'trunks' are trunked. */
PORT_VLAN_NATIVE_UNTAGGED
PORT_VLAN_NATIVE_UNTAGGED,

/* 802.1q tunnel port. Incoming packets are added an outer vlan tag
* 'vlan'. If 'cvlans' is set, only allows VLANs in 'cvlans'. */
PORT_VLAN_DOT1Q_TUNNEL
};

/* Configuration of bundles. */
Expand All @@ -399,8 +403,10 @@ struct ofproto_bundle_settings {
size_t n_slaves;

enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */
uint16_t qinq_ethtype;
int vlan; /* VLAN VID, except for PORT_VLAN_TRUNK. */
unsigned long *trunks; /* vlan_bitmap, except for PORT_VLAN_ACCESS. */
unsigned long *cvlans;
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */

struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
Expand Down
Loading

0 comments on commit fed8962

Please sign in to comment.