From: Philippe Jung <phil.j...@free.fr> Significant updates by Ben Pfaff, including:
* Comment, coding style, indentation updates. * Documentation improved. * Added tests. * Dropped PORT_VLAN_EMPTY. --- NEWS | 3 + ofproto/ofproto-dpif.c | 123 ++++++++++++++++++++++++++++++++------ ofproto/ofproto.h | 26 ++++++++- tests/automake.mk | 1 + tests/compare-odp-actions.pl | 66 ++++++++++++++++++++ tests/ofproto-dpif.at | 135 +++++++++++++++++++++++++---------------- vswitchd/bridge.c | 33 +++++++++-- vswitchd/vswitch.ovsschema | 8 ++- vswitchd/vswitch.xml | 110 ++++++++++++++++++++++++---------- 9 files changed, 392 insertions(+), 113 deletions(-) create mode 100644 tests/compare-odp-actions.pl diff --git a/NEWS b/NEWS index ae6f55e..12b67d6 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,9 @@ Post-v1.2.0 new NXAST_RESUBMIT_TABLE action can look up in additional tables. Tables 128 and above are reserved for use by the switch itself; please use only tables 0 through 127. + - Added support for native VLAN tagging. A new "vlan_mode" + parameter can be set for "port". Possible values: "access", + "trunk", "native-tagged" and "native-untagged". v1.2.0 - 03 Aug 2011 ------------------------ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index c05ca9e..bcb6968 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -131,6 +131,7 @@ struct ofbundle { /* Configuration. */ struct list ports; /* Contains "struct ofport"s. */ + enum port_vlan_mode vlan_mode; /* VLAN mode */ 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. */ @@ -966,9 +967,10 @@ bundle_set(struct ofproto *ofproto_, void *aux, { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); bool need_flush = false; - const unsigned long *trunks; struct ofport_dpif *port; struct ofbundle *bundle; + unsigned long *trunks; + int vlan; size_t i; bool ok; @@ -991,6 +993,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, bundle->name = NULL; list_init(&bundle->ports); + bundle->vlan_mode = PORT_VLAN_TRUNK; bundle->vlan = -1; bundle->trunks = NULL; bundle->lacp = NULL; @@ -1049,19 +1052,65 @@ bundle_set(struct ofproto *ofproto_, void *aux, return EINVAL; } + /* Set VLAN tagging mode */ + if (s->vlan_mode != bundle->vlan_mode) { + bundle->vlan_mode = s->vlan_mode; + need_flush = true; + } + /* Set VLAN tag. */ - if (s->vlan != bundle->vlan) { - bundle->vlan = s->vlan; + vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1 + : s->vlan >= 0 && s->vlan <= 4095 ? s->vlan + : 0); + if (vlan != bundle->vlan) { + bundle->vlan = vlan; need_flush = true; } /* Get trunked VLANs. */ - trunks = s->vlan == -1 ? s->trunks : NULL; + switch (s->vlan_mode) { + case PORT_VLAN_ACCESS: + trunks = NULL; + break; + + case PORT_VLAN_TRUNK: + trunks = (unsigned long *) s->trunks; + break; + + case PORT_VLAN_NATIVE_UNTAGGED: + case PORT_VLAN_NATIVE_TAGGED: + if (vlan != 0 && (!s->trunks + || !bitmap_is_set(s->trunks, vlan) + || bitmap_is_set(s->trunks, 0))) { + /* Force trunking the native VLAN and prohibit trunking VLAN 0. */ + if (s->trunks) { + trunks = bitmap_clone(s->trunks, 4096); + } else { + trunks = bitmap_allocate1(4096); + } + bitmap_set1(trunks, vlan); + bitmap_set0(trunks, 0); + } else { + trunks = (unsigned long *) s->trunks; + } + break; + + default: + NOT_REACHED(); + } if (!vlan_bitmap_equal(trunks, bundle->trunks)) { free(bundle->trunks); - bundle->trunks = vlan_bitmap_clone(trunks); + if (trunks == s->trunks) { + bundle->trunks = vlan_bitmap_clone(trunks); + } else { + bundle->trunks = trunks; + trunks = NULL; + } need_flush = true; } + if (trunks != s->trunks) { + free(trunks); + } /* Bonding. */ if (!list_is_short(&bundle->ports)) { @@ -3376,16 +3425,47 @@ static bool set_dst(struct action_xlate_ctx *ctx, struct dst *dst, const struct ofbundle *in_bundle, const struct ofbundle *out_bundle) { - dst->vlan = (out_bundle->vlan >= 0 ? OFP_VLAN_NONE - : in_bundle->vlan >= 0 ? in_bundle->vlan - : ctx->flow.vlan_tci == 0 ? OFP_VLAN_NONE - : vlan_tci_to_vid(ctx->flow.vlan_tci)); + /* + * Following rules apply for traffic going out of the bundle: + * - if output is access (bundle mode empty with tag>=0 or access), untag + * - else if output bundle mode is native-untagged and current + * tag = output bundle native vlan, untag + * - else use vlan from input flow + */ + + if (out_bundle->vlan_mode == PORT_VLAN_ACCESS) { + dst->vlan = OFP_VLAN_NONE; + } else { + /* Get the original flow VLAN + * If port mode is empty with tag>=0, access, native, and the flow is + * untagged then use port native tag + */ + int flow_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci); + if (in_bundle->vlan_mode == PORT_VLAN_ACCESS + || (in_bundle->vlan_mode == PORT_VLAN_NATIVE_UNTAGGED + && flow_vlan == 0) + || (in_bundle->vlan_mode == PORT_VLAN_NATIVE_TAGGED + && flow_vlan == 0)) { + flow_vlan = in_bundle->vlan; + } + + /* + * post process the original flow VLAN to take into account + * out_bundle configuration : untag if and only if port mode + * is native-untagged mode and vlan=tag + */ + if (out_bundle->vlan_mode == PORT_VLAN_NATIVE_UNTAGGED + && flow_vlan == out_bundle->vlan) { + dst->vlan = OFP_VLAN_NONE; + } else { + dst->vlan = flow_vlan == 0 ? OFP_VLAN_NONE : flow_vlan; + } + } dst->port = (!out_bundle->bond ? ofbundle_get_a_port(out_bundle) : bond_choose_output_slave(out_bundle->bond, &ctx->flow, dst->vlan, &ctx->tags)); - return dst->port != NULL; } @@ -3447,7 +3527,7 @@ dst_is_duplicate(const struct dst_set *set, const struct dst *test) static bool ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan) { - return (bundle->vlan < 0 + return (bundle->vlan_mode != PORT_VLAN_ACCESS && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan))); } @@ -3665,8 +3745,9 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, struct ofbundle *in_bundle, bool have_packet) { int vlan = vlan_tci_to_vid(flow->vlan_tci); - if (in_bundle->vlan >= 0) { - if (vlan) { + if (vlan) { + if (in_bundle->vlan_mode == PORT_VLAN_ACCESS) { + /* Drop tagged packet on access port */ if (have_packet) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " @@ -3676,10 +3757,10 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, in_bundle->name, in_bundle->vlan); } return -1; - } - vlan = in_bundle->vlan; - } else { - if (!ofbundle_includes_vlan(in_bundle, vlan)) { + } else if (ofbundle_includes_vlan(in_bundle, vlan)) { + return vlan; + } else { + /* Drop packets from a VLAN not member of the trunk */ if (have_packet) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " @@ -3689,9 +3770,13 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, } return -1; } + } else { + if (in_bundle->vlan_mode != PORT_VLAN_TRUNK) { + return in_bundle->vlan; + } else { + return ofbundle_includes_vlan(in_bundle, 0) ? 0 : -1; + } } - - return vlan; } /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index e0c99ea..6f88fb4 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -179,6 +179,27 @@ void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port, const struct cfm_settings *); int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port); +/* The behaviour of the port regarding VLAN handling */ +enum port_vlan_mode { + /* This port is an access port. 'vlan' is the VLAN ID. 'trunks' is + * ignored. */ + PORT_VLAN_ACCESS, + + /* This port is a trunk. 'trunks' is the set of trunks. 'vlan' is + * ignored. */ + PORT_VLAN_TRUNK, + + /* Untagged incoming packets are part of 'vlan', as are incoming packets + * tagged with 'vlan'. Outgoing packets tagged with 'vlan' stay tagged. + * Other VLANs in 'trunks' are trunked. */ + PORT_VLAN_NATIVE_TAGGED, + + /* 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 +}; + /* Configuration of bundles. */ struct ofproto_bundle_settings { char *name; /* For use in log messages. */ @@ -186,8 +207,9 @@ struct ofproto_bundle_settings { uint16_t *slaves; /* OpenFlow port numbers for slaves. */ size_t n_slaves; - int vlan; /* VLAN if access port, -1 if trunk port. */ - unsigned long *trunks; /* vlan_bitmap, NULL to trunk all VLANs. */ + enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */ + int vlan; /* VLAN VID, except for PORT_VLAN_TRUNK. */ + unsigned long *trunks; /* vlan_bitmap, except for PORT_VLAN_ACCESS. */ struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */ uint32_t *bond_stable_ids; /* Array of n_slaves elements. */ diff --git a/tests/automake.mk b/tests/automake.mk index 28b4e1d..ad4cafd 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -54,6 +54,7 @@ TESTSUITE_AT = \ tests/interface-reconfigure.at TESTSUITE = $(srcdir)/tests/testsuite DISTCLEANFILES += tests/atconfig tests/atlocal +EXTRA_DIST += tests/compare-odp-actions.pl AUTOTEST_PATH = utilities:vswitchd:ovsdb:tests diff --git a/tests/compare-odp-actions.pl b/tests/compare-odp-actions.pl new file mode 100644 index 0000000..b18a593 --- /dev/null +++ b/tests/compare-odp-actions.pl @@ -0,0 +1,66 @@ +# -*- perl -*- + +use strict; +use warnings; + +if (@ARGV < 2) { + print <<EOF; +$0: to check ODP sets of actions for equivalence +usage: $0 ACTIONS1 ACTIONS2 [NAME=NUMBER]... +where ACTIONS1 and ACTIONS2 are sets of ODP actions as output by, e.g. + "ovs-dpctl dump-flows" and each NAME=NUMBER pair identifies an ODP + port's name-to-number mapping. + +Exits with status 0 if ACTIONS1 and ACTIONS2 are equivalent, with +status 1 if they differ. +EOF + exit 1; +} + +# Construct mappings between port numbers and names. +our (%name_to_number); +our (%number_to_name); +for (@ARGV[2..$#ARGV]) { + my ($name, $number) = /([^=]+)=([0-9]+)/ + or die "$_: bad syntax (use --help for help)\n"; + $number_to_name{$number} = $name; + $name_to_number{$name} = $number; +} + +my $n1 = normalize_odp_actions($ARGV[0]); +my $n2 = normalize_odp_actions($ARGV[1]); +print "Normalized action set 1: $n1\n"; +print "Normalized action set 2: $n2\n"; +exit($n1 ne $n2); + +sub normalize_odp_actions { + my ($actions) = @_; + + # Transliterate all commas inside parentheses into semicolons. + undef while $actions =~ s/(\([^),]*),([^)]*\))/$1;$2/g; + + # Split on commas. + my (@actions) = split(',', $actions); + + # Map port numbers into port names. + foreach my $s (@actions) { + $s = $number_to_name{$s} if exists($number_to_name{$s}); + } + + # Sort sequential groups of port names into alphabetical order. + for (my $i = 0; $i <= $#actions; ) { + my $j = $i + 1; + if (exists($name_to_number{$actions[$i]})) { + for (; $j <= $#actions; $j++) { + last if !exists($name_to_number{$actions[$j]}); + } + } + @actions[$i..($j - 1)] = sort(@actions[$i..($j - 1)]); + $i = $j; + } + + # Now compose a string again and transliterate semicolons back to commas. + $actions = join(',', @actions); + $actions =~ tr/;/,/; + return $actions; +} diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 6753055..93eed03 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -62,80 +62,109 @@ AT_CHECK([tail -1 stdout], [0], OFPROTO_STOP AT_CLEANUP -AT_SETUP([ofproto-dpif - trunks]) +AT_SETUP([ofproto-dpif - VLAN handling]) OVS_VSWITCHD_START( - [add-port br0 p1 trunks=10,12 -- set Interface p1 type=dummy -- \ - add-port br0 p2 tag=10 -- set Interface p2 type=dummy -- \ - add-port br0 p3 tag=12 -- set Interface p3 type=dummy -- \ - add-port br0 p4 tag=12 -- set Interface p4 type=dummy]) + [add-port br0 p1 trunks=10,12 -- \ + add-port br0 p2 tag=10 -- \ + add-port br0 p3 tag=12 -- \ + add-port br0 p4 tag=12 -- \ + add-port br0 p5 vlan_mode=native-tagged tag=10 -- \ + add-port br0 p6 vlan_mode=native-tagged tag=10 trunks=10,12 -- \ + add-port br0 p7 vlan_mode=native-untagged tag=12 -- \ + add-port br0 p8 vlan_mode=native-untagged tag=12 trunks=10,12 -- \ + set Interface p1 type=dummy -- \ + set Interface p2 type=dummy -- \ + set Interface p3 type=dummy -- \ + set Interface p4 type=dummy -- \ + set Interface p5 type=dummy -- \ + set Interface p6 type=dummy -- \ + set Interface p7 type=dummy -- \ + set Interface p8 type=dummy --]) AT_CHECK( [ovs-vsctl \ -- get Interface p1 ofport \ -- get Interface p2 ofport \ -- get Interface p3 ofport \ - -- get Interface p4 ofport], + -- get Interface p4 ofport \ + -- get Interface p5 ofport \ + -- get Interface p6 ofport \ + -- get Interface p7 ofport \ + -- get Interface p8 ofport], [0], [stdout]) set `cat stdout` -br0=0 p1=$1 p2=$2 p3=$3 p4=$4 +br0=0 p1=$1 p2=$2 p3=$3 p4=$4 p5=$5 p6=$6 p7=$7 p8=$8 -dnl Each of these specifies an in_port, a VLAN VID (or "none"), and one -dnl or more sets of valid datapath actions. (The order of datapath -dnl actions is somewhat unpredictable, hence the ability to specify more -dnl than one set.) +dnl Each of these specifies an in_port, a VLAN VID (or "none"), and the +dnl expected set of datapath actions. +dnl +dnl XXX Some of these actually output an 802.1Q header to an access port +dnl (see for example the actions for in_port=p3, vlan=0) to qualify the +dnl packet with a priority. We should fix that, it's not correct behavior. for tuple in \ - "$br0 none drop" \ - "$br0 0 drop" \ - "$br0 10 $p1,strip_vlan,$p2" \ - "$br0 11 drop" \ - "$br0 12 $p1,strip_vlan,$p3,$p4 $p1,strip_vlan,$p4,$p3" \ - "$p1 none drop" \ - "$p1 0 drop" \ - "$p1 10 $br0,strip_vlan,$p2" \ - "$p1 11 drop" \ - "$p1 12 $br0,strip_vlan,$p4,$p3 $br0,strip_vlan,$p3,$p4" \ - "$p2 none set_tci(vid=10,pcp=0),$br0,$p1 set_tci(vid=10,pcp=0),$p1,$br0" \ - "$p2 0 set_tci(vid=10,pcp=1),$br0,$p1 set_tci(vid=10,pcp=1),$p1,$br0" \ - "$p2 10 drop" \ - "$p2 11 drop" \ - "$p2 12 drop" \ - "$p3 none $p4,set_tci(vid=12,pcp=0),$br0,$p1 $p4,set_tci(vid=12,pcp=0),$p1,$br0" \ - "$p3 0 $p4,set_tci(vid=12,pcp=1),$br0,$p1 $p4,set_tci(vid=12,pcp=1),$p1,$br0" \ - "$p3 10 drop" \ - "$p3 11 drop" \ - "$p3 12 drop" \ - "$p4 none $p3,set_tci(vid=12,pcp=0),$br0,$p1 $p3,set_tci(vid=12,pcp=0),$p1,$br0" \ - "$p4 0 $p3,set_tci(vid=12,pcp=1),$br0,$p1 $p3,set_tci(vid=12,pcp=1),$p1,$br0" \ - "$p4 10 drop" \ - "$p4 11 drop" \ - "$p4 12 drop" + "br0 none drop" \ + "br0 0 drop" \ + "br0 10 p1,p5,p6,p7,p8,strip_vlan,p2" \ + "br0 11 p5,p7" \ + "br0 12 p1,p5,p6,strip_vlan,p3,p4,p7,p8" \ + "p1 none drop" \ + "p1 0 drop" \ + "p1 10 br0,p5,p6,p7,p8,strip_vlan,p2" \ + "p1 11 drop" \ + "p1 12 br0,p5,p6,strip_vlan,p3,p4,p7,p8" \ + "p2 none set_tci(vid=10,pcp=0),br0,p1,p5,p6,p7,p8" \ + "p2 0 set_tci(vid=10,pcp=1),br0,p1,p5,p6,p7,p8" \ + "p2 10 drop" \ + "p2 11 drop" \ + "p2 12 drop" \ + "p3 none p4,p7,p8,set_tci(vid=12,pcp=0),br0,p1,p5,p6" \ + "p3 0 p4,p7,p8,set_tci(vid=12,pcp=1),br0,p1,p5,p6" \ + "p3 10 drop" \ + "p3 11 drop" \ + "p3 12 drop" \ + "p4 none p3,p7,p8,set_tci(vid=12,pcp=0),br0,p1,p5,p6" \ + "p4 0 p3,p7,p8,set_tci(vid=12,pcp=1),br0,p1,p5,p6" \ + "p4 10 drop" \ + "p4 11 drop" \ + "p4 12 drop" \ + "p5 none p2,set_tci(vid=10,pcp=0),br0,p1,p6,p7,p8" \ + "p5 0 p2,set_tci(vid=10,pcp=1),br0,p1,p6,p7,p8" \ + "p5 10 br0,p1,p6,p7,p8,strip_vlan,p2" \ + "p5 11 br0,p7" \ + "p5 12 br0,p1,p6,strip_vlan,p3,p4,p7,p8" \ + "p6 none p2,set_tci(vid=10,pcp=0),br0,p1,p5,p7,p8" \ + "p6 0 p2,set_tci(vid=10,pcp=1),br0,p1,p5,p7,p8" \ + "p6 10 br0,p1,p5,p7,p8,strip_vlan,p2" \ + "p6 11 drop" \ + "p6 12 br0,p1,p5,strip_vlan,p3,p4,p7,p8" \ + "p7 none p3,p4,p8,set_tci(vid=12,pcp=0),br0,p1,p5,p6" \ + "p7 0 p3,p4,p8,set_tci(vid=12,pcp=1),br0,p1,p5,p6" \ + "p7 10 br0,p1,p5,p6,p8,strip_vlan,p2" \ + "p7 11 br0,p5" \ + "p7 12 br0,p1,p5,p6,strip_vlan,p3,p4,p8" \ + "p8 none p3,p4,p7,set_tci(vid=12,pcp=0),br0,p1,p5,p6" \ + "p8 0 p3,p4,p7,set_tci(vid=12,pcp=1),br0,p1,p5,p6" \ + "p8 10 br0,p1,p5,p6,p7,strip_vlan,p2" \ + "p8 11 drop" \ + "p8 12 br0,p1,p5,p6,strip_vlan,p3,p4,p7" do set $tuple in_port=$1 vlan=$2 - shift; shift + expected=$3 + eval n_in_port=\$$in_port if test $vlan = none; then - flow="in_port($in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0xabcd)" + flow="in_port($n_in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0xabcd)" else - flow="in_port($in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),vlan(vid=$vlan,pcp=1),eth_type(0xabcd)" + flow="in_port($n_in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),vlan(vid=$vlan,pcp=1),eth_type(0xabcd)" fi - AT_CHECK( - [echo "-- $tuple" - echo "-- br0=$br0 p1=$p1 p2=$p2 p3=$p3 p4=$p4" - ovs-appctl ofproto/trace br0 "$flow"], - [0], [stdout]) + AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) + actual=`tail -1 stdout | sed 's/Datapath actions: //'` - actions=`tail -1 stdout | sed 's/Datapath actions: //'` - no_match=: - for pattern - do - if test X"$actions" = X"$pattern"; then - no_match=false - fi - done - AT_FAIL_IF([$no_match]) + AT_CHECK([echo "in_port=$in_port vlan=$vlan" + $PERL $srcdir/compare-odp-actions.pl "$expected" "$actual" br0=$br0 p1=$p1 p2=$p2 p3=$p3 p4=$p4 p5=$p5 p6=$p6 p7=$p7 p8=$p8], [0], [ignore]) done OVS_VSWITCHD_STOP diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index d99d6a1..600c163 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -490,7 +490,6 @@ port_configure(struct port *port) if (list_is_short(&port->ifaces)) { if (*cfg->tag >= 0 && *cfg->tag <= 4095) { s.vlan = *cfg->tag; - VLOG_DBG("port %s: assigning VLAN tag %d", port->name, s.vlan); } } else { /* It's possible that bonded, VLAN-tagged ports make sense. Maybe @@ -502,11 +501,35 @@ port_configure(struct port *port) /* Get VLAN trunks. */ s.trunks = NULL; - if (s.vlan < 0 && cfg->n_trunks) { + if (cfg->n_trunks) { s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks); - } else if (s.vlan >= 0 && cfg->n_trunks) { - VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan", - port->name); + } + + /* Get VLAN mode. */ + if (cfg->vlan_mode) { + if (!strcmp(cfg->vlan_mode, "access")) { + s.vlan_mode = PORT_VLAN_ACCESS; + } else if (!strcmp(cfg->vlan_mode, "trunk")) { + s.vlan_mode = PORT_VLAN_TRUNK; + } else if (!strcmp(cfg->vlan_mode, "native-tagged")) { + s.vlan_mode = PORT_VLAN_NATIVE_TAGGED; + } else if (!strcmp(cfg->vlan_mode, "native-untagged")) { + s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED; + } else { + /* This "can't happen" because ovsdb-server should prevent it. */ + VLOG_ERR("unknown VLAN mode %s", cfg->vlan_mode); + s.vlan_mode = PORT_VLAN_TRUNK; + } + } else { + if (s.vlan >= 0) { + s.vlan_mode = PORT_VLAN_ACCESS; + if (cfg->n_trunks) { + VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan", + port->name); + } + } else { + s.vlan_mode = PORT_VLAN_TRUNK; + } } /* Get LACP settings. */ diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index ca61a2c..138569f 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "5.2.0", - "cksum": "434778864 14545", + "version": "5.3.0", + "cksum": "418437550 14728", "tables": { "Open_vSwitch": { "columns": { @@ -115,6 +115,10 @@ "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 1}}, + "vlan_mode": { + "type": {"key": {"type": "string", + "enum": ["set", ["trunk", "access", "native-tagged", "native-untagged"]]}, + "min": 0, "max": 1}}, "qos": { "type": {"key": {"type": "uuid", "refTable": "QoS"}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 6082256..0c40dd2 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -499,50 +499,96 @@ </column> <group title="VLAN Configuration"> - <p>A bridge port must be configured for VLANs in one of two - mutually exclusive ways: - <ul> - <li>A ``trunk port'' has an empty value for <ref - column="tag"/>. Its <ref column="trunks"/> value may be - empty or non-empty.</li> - <li>An ``implicitly tagged VLAN port'' or ``access port'' - has an nonempty value for <ref column="tag"/>. Its - <ref column="trunks"/> value must be empty.</li> - </ul> - If <ref column="trunks"/> and <ref column="tag"/> are both - nonempty, the configuration is ill-formed. + <p>Bridge ports support the following types of VLAN configuration:</p> + <dl> + <dt>trunk</dt> + <dd> + <p> + A trunk port carries packets on one or more specified VLANs + specified in the <ref column="trunks"/> column (often, on every + VLAN). A packet ingressing on a trunk port is in the VLAN + specified in its 802.1Q header, or VLAN 0 if the packet has no + 802.1Q header. A packet that egresses on a trunk port, it will + have a 802.1Q header if it has a nonzero VLAN ID (or a nonzero + 802.1Q priority). + </p> + + <p> + Any packet that ingresses on a trunk port tagged with a VLAN that + the port does not trunk is dropped. + </p> + </dd> + + <dt>access</dt> + <dd> + <p> + An access port carries packets on exactly one VLAN specified in the + <ref column="tag"/> column. Packets ingressing and egressing on an + access port have no 802.1Q header. + </p> + + <p> + Any packet with an 802.1Q header that ingresses on an access port + is dropped, regardless of whether the VLAN ID in the header is the + access port's VLAN ID. + </p> + </dd> + + <dt>native-tagged</dt> + <dd> + A native-tagged port resembles a trunk port, with the exception that + a packet without an 802.1Q header that ingresses on a native-tagged + port is in the ``native VLAN'' (specified in the <ref column="tag"/> + column). + </dd> + + <dt>native-untagged</dt> + <dd> + A native-untagged port resembles a native-tagged port, with the + exception that a packet that egresses on a native-untagged port in + the native VLAN not have an 802.1Q header. + </dd> + </dl> + <p> + A packet will only egress through bridge ports that carry the VLAN of + the packet, as described by the rules above. </p> - <column name="tag"> - <p> - If this is an access port (see above), the port's implicitly - tagged VLAN. Must be empty if this is a trunk port. - </p> + <column name="vlan_mode"> <p> - Frames arriving on trunk ports will be forwarded to this - port only if they are tagged with the given VLAN (or, if - <ref column="tag"/> is 0, then if they lack a VLAN header). - Frames arriving on other access ports will be forwarded to - this port only if they have the same <ref column="tag"/> - value. Frames forwarded to this port will not have an - 802.1Q header. + The VLAN mode of the port, as described above. When this column is + empty, a default mode is selected as follows: </p> + <ul> + <li> + If <ref column="tag"/> contains a value, the port is an access + port. The <ref column="trunks"/> column should be empty. + </li> + <li> + Otherwise, the port is a trunk port. The <ref column="trunks"/> + column value is honored if it is present. + </li> + </ul> + </column> + + <column name="tag"> <p> - When a frame with a 802.1Q header that indicates a nonzero - VLAN is received on an access port, it is discarded. + For an access port, the port's implicitly tagged VLAN. For a + native-tagged or native-untagged port, the port's native VLAN. Must + be empty if this is a trunk port. </p> </column> <column name="trunks"> <p> - If this is a trunk port (see above), the 802.1Q VLAN(s) that - this port trunks; if it is empty, then the port trunks all - VLANs. Must be empty if this is an access port. + For a trunk, native-tagged, or native-untagged port, the 802.1Q VLAN + or VLANs that this port trunks; if it is empty, then the port trunks + all VLANs. Must be empty if this is an access port. </p> <p> - Frames arriving on trunk ports are dropped if they are not - in one of the specified VLANs. For this purpose, packets - that have no VLAN header are treated as part of VLAN 0. + A native-tagged or native-untagged port always trunks its native + VLAN, regardless of whether <ref column="trunks"/> includes that + VLAN. </p> </column> </group> -- 1.7.4.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev