Le 25/08/2011 01:07, Ben Pfaff a écrit : > > Hi Philippe. Just dropping you a note to let you know that I haven't > forgotten your patch. I spent some time writing a separate commit > that fixes the bug that you reported and posted it in this thread, > along with some unit tests for VLANs: > http://openvswitch.org/pipermail/dev/2011-August/010779.html > > I'll get back to your patch soon.
So, you will have the opportunity to check this new version of the patch :-). I have included the optimisation I spoke about. set_bundle with no vlan_mode defined guesses the vlan_mode. All if(mode=access || mode=empty && tag>=0) are replaced by if(mode=access) Philippe --- 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 f09c230..d6e71b6 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. */ @@ -970,6 +971,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, struct ofport_dpif *port; struct ofbundle *bundle; size_t i; + enum port_vlan_mode vlan_mode; bool ok; if (!s) { @@ -991,6 +993,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, bundle->name = NULL; list_init(&bundle->ports); + bundle->vlan_mode = PORT_VLAN_EMPTY; bundle->vlan = -1; bundle->trunks = NULL; bundle->lacp = NULL; @@ -1049,6 +1052,24 @@ bundle_set(struct ofproto *ofproto_, void *aux, return EINVAL; } + /* + * Set VLAN tagging mode + * First transform PORT_VLAN_EMPTY into ACCESS or TRUNK to avoid + * handling this situation separately + */ + vlan_mode = s->vlan_mode; + if (vlan_mode == PORT_VLAN_EMPTY) { + if (s->vlan >= 0) { + vlan_mode = PORT_VLAN_ACCESS; + } else { + vlan_mode = PORT_VLAN_TRUNK; + } + } + if (vlan_mode != bundle->vlan_mode) { + bundle->vlan_mode = vlan_mode; + need_flush = true; + } + /* Set VLAN tag. */ if (s->vlan != bundle->vlan) { bundle->vlan = s->vlan; @@ -1056,7 +1077,11 @@ bundle_set(struct ofproto *ofproto_, void *aux, } /* Get trunked VLANs. */ - trunks = s->vlan == -1 ? NULL : s->trunks; + if (vlan_mode == PORT_VLAN_ACCESS) { + trunks = NULL; + } else { + trunks = s->trunks; + } if (!vlan_bitmap_equal(trunks, bundle->trunks)) { free(bundle->trunks); bundle->trunks = vlan_bitmap_clone(trunks); @@ -3376,16 +3401,54 @@ 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 + */ + + /* output is access ? */ + if (out_bundle->vlan_mode == PORT_VLAN_ACCESS) { + + /* Untag everything */ + dst->vlan = OFP_VLAN_NONE; + + } else { + + /* + * Get the original flow VLAN + * If port mode is access, native, and the flow is + * unttaged 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,8 +3510,17 @@ 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 - && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan))); + /* + * Returns true if and only if: + * port is working as trunk (not an access port) and + * - trunks is empty + * - or trunks includes vlan + */ + if (bundle->vlan_mode == PORT_VLAN_ACCESS) { + return false; + } else { + return (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)); + } } static bool @@ -3665,8 +3737,19 @@ 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) { + + /* + * First case : vlan != 0 input packet is tagged. Returns: + * - -1 if port is access + * - flow vlan if vlan mode is trunk or native-(un)tagged and flow vlan + * is part of the trunk + * - -1 if vlan mode is trunk or native-(un)tagged and flow vlan is not + * part of the trunk + */ + 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 +3759,16 @@ 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)) { + + /* Accept packet if it is comming from a VLAN + * that is member of the port "trunks" list */ + 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 +3778,23 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, } return -1; } - } - return vlan; + /* + * Second case : vlan=0, input packet is untagged. Returns: + * - bundle native vlan if vlan mode is access or native-(un)tagged + * - 0 if mode is trunk and flow vlan (0) is part of the trunk + * - -1 if mode is trunk and flow vlan (0) is not part of the trunk + */ + } else { + if (in_bundle->vlan_mode == PORT_VLAN_ACCESS + || in_bundle->vlan_mode == PORT_VLAN_NATIVE_TAGGED + || in_bundle->vlan_mode == PORT_VLAN_NATIVE_UNTAGGED) { + + return in_bundle->vlan; + } else { + return ofbundle_includes_vlan(in_bundle, 0) ? 0 : -1; + } + } } /* 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..db8b6c4 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -179,6 +179,41 @@ 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 { + /* + * For compatibility. Access mode if tag is defined or Trunk mode + * if trunk is defined. Native VLAN feature is disabled + */ + PORT_VLAN_EMPTY, + + /* + * This port is an access port. All packets are tagged with "vlan" + * that comes from the "tag" column. The "trunk" column is ignored + */ + PORT_VLAN_ACCESS, + + /* + * This port is a trunk. The "tag" column is ignored. Packets are + * unchanged. + */ + PORT_VLAN_TRUNK, + + /* + * Untagged incoming packets are tagged with "vlan" comming from + * the "tag" column. Outgoing packets tagged with "vlan" stay + * tagged. The "tag" and "trunks" columns are both used. + */ + PORT_VLAN_NATIVE_TAGGED, + + /* + * Untagged incoming packets are tagged with "vlan" comming from + * the "tag" column. Outgoing packets tagged with "vlan" are + * untagged. The "tag" and "trunks" columns are both used. + */ + PORT_VLAN_NATIVE_UNTAGGED, +}; + /* Configuration of bundles. */ struct ofproto_bundle_settings { char *name; /* For use in log messages. */ @@ -186,6 +221,7 @@ struct ofproto_bundle_settings { uint16_t *slaves; /* OpenFlow port numbers for slaves. */ size_t n_slaves; + enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */ int vlan; /* VLAN if access port, -1 if trunk port. */ unsigned long *trunks; /* vlan_bitmap, NULL to trunk all VLANs. */ diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index d99d6a1..65317b8 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -484,6 +484,23 @@ port_configure(struct port *port) s.slaves[s.n_slaves++] = iface->ofp_port; } + /* Get VLAN mode */ + s.vlan_mode = PORT_VLAN_EMPTY; + 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 { + VLOG_WARN("port %s: unknown VLAN mode %s.", + port->name, cfg->vlan_mode); + } + } + /* Get VLAN tag. */ s.vlan = -1; if (cfg->tag) { @@ -504,9 +521,11 @@ port_configure(struct port *port) s.trunks = NULL; if (s.vlan < 0 && 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); + } else if (s.vlan >= 0 && cfg->n_trunks ) { + if (s.vlan_mode == PORT_VLAN_EMPTY) { + VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan", + port->name); + } } /* 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..c78e745 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -499,8 +499,8 @@ </column> <group title="VLAN Configuration"> - <p>A bridge port must be configured for VLANs in one of two - mutually exclusive ways: + <p>A bridge port must be configured for VLANs in one of these + ways: <ul> <li>A ``trunk port'' has an empty value for <ref column="tag"/>. Its <ref column="trunks"/> value may be @@ -508,11 +508,32 @@ <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> + <li>An trunk port for tagged packets mixed with implicitly + tagged VLAN for untagged packets. The implicit tag is + usually called native VLAN, default VLAN or port VLAN identifier + (PVID). The VLAN tag persistence is configurable. When disabled + (native untagged), the VLAN tag is removed from leaving packets + whose VLAN tag matches the port native VLAN.</li> </ul> - If <ref column="trunks"/> and <ref column="tag"/> are both - nonempty, the configuration is ill-formed. </p> + <column name="vlan_mode"> + <p> + This allows to select the working mode of the port. Allowed + values are: + <ul> + <li>empty: the OpenVswitch standard behaviour is used.</li> + <li>access: the port is an access port (see above).</li> + <li>native-untagged: tagged packets are unchanged. Arriving + untagged packets are tagged with native VLAN. Leaving packets + tagged with native VLAN are untagged.</li> + <li>native-tagged: tagged packets are unchanged. Arriving + untagged packets are tagged with native VLAN. Leaving packets + tagged with native VLAN are unchanged.</li> + </ul> + </p> + </column> + <column name="tag"> <p> If this is an access port (see above), the port's implicitly @@ -531,6 +552,27 @@ When a frame with a 802.1Q header that indicates a nonzero VLAN is received on an access port, it is discarded. </p> + <p> + If this is a pvid or untagged-pvid port, this column contains + the PVID value. The <ref column="trunks"/> value must also be + defined. + </p> + <p> + Frames arriving on this port with an 802.1Q header indicating + a nonzero VLAN will be forwarded based on destination + <ref column="trunks"/> value. Frames arriving on this port + without an 802.1Q header or with an 802.1Q header indicating + VLAN 0 will first be tagged with <ref column="tag"/> value + then will be forwarded based on destination + <ref column="trunks"/> value. + </p> + <p> + Frames leaving this port will keep their VLAN header expect + if mode is untagged-pvid. In this case, all frames leaving + this port will keep their VLAN header if it is different + from <ref column="tag"/>. If it is equal to <ref column="tag"/> + the frame is untagged. + </p> </column> <column name="trunks"> _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev