Hey Sorin, Were you able to test out the changes? Can you clarify how we can reproduce it? It will be nice to have documentation around testing this since it¹s a new feature. I remember you having difficulties with testing this earlier, hopefully that is now resolved.
I took a look at your changes and have mentioned my comments inline. Thanks, Sairam On 11/11/15, 12:11 PM, "Sorin Vinturis" <svintu...@cloudbasesolutions.com> wrote: >This patch adds OVS_KEY_ATTR_MPLS to the OVS flow mechanism. > >Signed-off-by: Sorin Vinturis <svintu...@cloudbasesolutions.com> >--- > datapath-windows/ovsext/Actions.c | 176 >+++++++++++++++++++++++++++++++++ > datapath-windows/ovsext/DpInternal.h | 7 ++ > datapath-windows/ovsext/Ethernet.h | 2 + > datapath-windows/ovsext/Flow.c | 89 ++++++++++++++++- > datapath-windows/ovsext/NetProto.h | 33 +++++++ > datapath-windows/ovsext/PacketParser.c | 12 +-- > datapath-windows/ovsext/PacketParser.h | 7 ++ > 7 files changed, 318 insertions(+), 8 deletions(-) > >diff --git a/datapath-windows/ovsext/Actions.c >b/datapath-windows/ovsext/Actions.c >index ce592b3..9ee1763 100644 >--- a/datapath-windows/ovsext/Actions.c >+++ b/datapath-windows/ovsext/Actions.c >@@ -1074,6 +1074,142 @@ OvsPopVlanInPktBuf(OvsForwardingContext >*ovsFwdCtx) > return NDIS_STATUS_SUCCESS; > } I think we should move the MPLS-Push and POP functions to a separate file mpls.h/mpls.c to keep Actions.c limited to what it does. > >+static __inline NDIS_STATUS >+OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx, >+ const struct ovs_action_push_mpls *mpls) >+{ >+ NDIS_STATUS status; >+ PNET_BUFFER curNb = NULL; >+ PMDL curMdl = NULL; >+ PUINT8 bufferStart = NULL; >+ OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers; >+ EthHdr *ethHdr = NULL; >+ MPLSHdr *mplsHdr = NULL; >+ UINT32 mdlLen = 0, curMdlOffset = 0; >+ UINT32 packetLen = 0; >+ PNET_BUFFER_LIST newNbl; >+ >+ curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); >+ ASSERT(curNb->Next == NULL); >+ packetLen = NET_BUFFER_DATA_LENGTH(curNb); >+ curMdl = NET_BUFFER_CURRENT_MDL(curNb); >+ NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); >+ if (!bufferStart) { >+ ovsActionStats.noResource++; >+ return NDIS_STATUS_RESOURCES; >+ } >+ curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb); >+ mdlLen -= curMdlOffset; >+ mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb); >+ ASSERT(mdlLen > 0); >+ /* Bail out if the L2 header is not in a contiguous buffer. */ >+ if (MIN(packetLen, mdlLen) < sizeof *ethHdr) { >+ ASSERT(FALSE); >+ return NDIS_STATUS_FAILURE; >+ } >+ ASSERT((INT)mdlLen >= 0); >+ >+ newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, >ovsFwdCtx->curNbl, >+ MPLS_HLEN, 0, TRUE /*copy NBL info*/); >+ if (!newNbl) { >+ ovsActionStats.noCopiedNbl++; >+ return NDIS_STATUS_RESOURCES; >+ } >+ OvsCompleteNBLForwardingCtx(ovsFwdCtx, >+ L"Complete after partial copy."); >+ >+ status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, >+ newNbl, ovsFwdCtx->srcVportNo, 0, >+ >NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), >+ NULL, &ovsFwdCtx->layers, FALSE); >+ if (status != NDIS_STATUS_SUCCESS) { >+ OvsCompleteNBLForwardingCtx(ovsFwdCtx, >+ L"OVS-Dropped due to resources"); >+ return NDIS_STATUS_RESOURCES; >+ } >+ >+ curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); >+ ASSERT(curNb->Next == NULL); >+ curMdl = NET_BUFFER_CURRENT_MDL(curNb); >+ NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); >+ if (!curMdl) { >+ ovsActionStats.noResource++; >+ return NDIS_STATUS_RESOURCES; >+ } >+ curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb); >+ mdlLen -= curMdlOffset; >+ ASSERT(mdlLen >= MPLS_HLEN); >+ >+ ethHdr = (EthHdr *)(bufferStart + curMdlOffset); >+ ethHdr->Type = mpls->mpls_ethertype; >+ >+ mplsHdr = (MPLSHdr *)(bufferStart + curMdlOffset + MPLS_HLEN); >+ mplsHdr->mpls_lse = mpls->mpls_lse; >+ >+ layers->l3Offset += MPLS_HLEN; >+ layers->isIPv4 = 0; >+ layers->isIPv6 = 0; >+ Should we introduce an isMpls label in OVS_PACKET_HDR_INFO to identify this? >+ NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, FALSE, NULL); >+ >+ return NDIS_STATUS_SUCCESS; >+} >+ This function seems like a copy of OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx) except for the shiftLength/MPLS_HLEN. You can have 1 function that takes in the length instead. >+static __inline NDIS_STATUS >+OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx) >+{ >+ PNET_BUFFER curNb; >+ PMDL curMdl; >+ PUINT8 bufferStart; >+ ULONG dataLength = sizeof (DL_EUI48) + sizeof (DL_EUI48); >+ UINT32 packetLen, mdlLen; >+ PNET_BUFFER_LIST newNbl; >+ NDIS_STATUS status; >+ >+ PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)]; >+ >+ newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, >ovsFwdCtx->curNbl, >+ 0, 0, TRUE /* copy NBL info */); >+ if (!newNbl) { >+ ovsActionStats.noCopiedNbl++; >+ return NDIS_STATUS_RESOURCES; >+ } >+ >+ /* Complete the original NBL and create a copy to modify. */ >+ OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy"); >+ >+ status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, >+ newNbl, ovsFwdCtx->srcVportNo, 0, >+ >NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), >+ NULL, &ovsFwdCtx->layers, FALSE); >+ if (status != NDIS_STATUS_SUCCESS) { >+ OvsCompleteNBLForwardingCtx(ovsFwdCtx, >+ L"Dropped due to resouces"); >+ return NDIS_STATUS_RESOURCES; >+ } >+ >+ curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); >+ packetLen = NET_BUFFER_DATA_LENGTH(curNb); >+ ASSERT(curNb->Next == NULL); >+ curMdl = NET_BUFFER_CURRENT_MDL(curNb); >+ NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); >+ if (!bufferStart) { >+ return NDIS_STATUS_RESOURCES; >+ } >+ mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb); >+ /* Bail out if L2 + MPLS header is not contiguous in the first >buffer. */ >+ if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + MPLS_HLEN) { >+ ASSERT(FALSE); >+ return NDIS_STATUS_FAILURE; >+ } >+ bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); >+ RtlCopyMemory(tempBuffer, bufferStart, dataLength); >+ RtlCopyMemory(bufferStart + MPLS_HLEN, tempBuffer, dataLength); >+ NdisAdvanceNetBufferDataStart(curNb, MPLS_HLEN, FALSE, NULL); >+ >+ return NDIS_STATUS_SUCCESS; >+} >+ > /* > * >-------------------------------------------------------------------------- > * OvsTunnelAttrToIPv4TunnelKey -- >@@ -1513,6 +1649,46 @@ OvsActionsExecute(POVS_SWITCH_CONTEXT >switchContext, > break; > } > >+ case OVS_ACTION_ATTR_PUSH_MPLS: >+ { >+ if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic >!= NULL >+ || ovsFwdCtx.tunnelRxNic != NULL) { >+ status = OvsOutputBeforeSetAction(&ovsFwdCtx); >+ if (status != NDIS_STATUS_SUCCESS) { >+ dropReason = L"OVS-adding destination failed"; >+ goto dropit; >+ } >+ } >+ >+ status = OvsActionMplsPush(&ovsFwdCtx, >+ (struct ovs_action_push_mpls >*)NlAttrGet >+ ((const PNL_ATTR)a)); >+ if (status != NDIS_STATUS_SUCCESS) { >+ dropReason = L"OVS-set push MPLS failed"; >+ goto dropit; >+ } >+ break; >+ } >+ >+ case OVS_ACTION_ATTR_POP_MPLS: >+ { >+ if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic >!= NULL >+ || ovsFwdCtx.tunnelRxNic != NULL) { >+ status = OvsOutputBeforeSetAction(&ovsFwdCtx); >+ if (status != NDIS_STATUS_SUCCESS) { >+ dropReason = L"OVS-adding destination failed"; >+ goto dropit; >+ } >+ } >+ >+ status = OvsActionMplsPop(&ovsFwdCtx); >+ if (status != NDIS_STATUS_SUCCESS) { >+ dropReason = L"OVS-set pop MPLS failed"; >+ goto dropit; >+ } >+ break; >+ } >+ > case OVS_ACTION_ATTR_USERSPACE: > { > PNL_ATTR userdataAttr; >diff --git a/datapath-windows/ovsext/DpInternal.h >b/datapath-windows/ovsext/DpInternal.h >index 8de48a2..c195494 100644 >--- a/datapath-windows/ovsext/DpInternal.h >+++ b/datapath-windows/ovsext/DpInternal.h >@@ -20,6 +20,7 @@ > #include <netioapi.h> > #define IFNAMSIZ IF_NAMESIZE > #include "../ovsext/Netlink/Netlink.h" >+#include "NetProto.h" > > #define OVS_DP_NUMBER ((uint32_t) 0) > >@@ -149,6 +150,11 @@ typedef union OvsIPv4TunnelKey { > uint64_t attr[NUM_PKT_ATTR_REQUIRED]; > } OvsIPv4TunnelKey; > >+typedef struct MplsKey { >+ ovs_be32 top_lse; /* MPLS topmost label stack entry. */ >+ uint8 pad[4]; >+} MplsKey; /* Size of 8 bytes. */ >+ > typedef __declspec(align(8)) struct OvsFlowKey { > OvsIPv4TunnelKey tunKey; /* 24 bytes */ > L2Key l2; /* 24 bytes */ >@@ -157,6 +163,7 @@ typedef __declspec(align(8)) struct OvsFlowKey { > ArpKey arpKey; /* size 24 */ > Ipv6Key ipv6Key; /* size 48 */ > Icmp6Key icmp6Key; /* size 72 */ >+ MplsKey mplsKey; /* size 8 */ > }; > } OvsFlowKey; > >diff --git a/datapath-windows/ovsext/Ethernet.h >b/datapath-windows/ovsext/Ethernet.h >index 22aa27c..1d69d47 100644 >--- a/datapath-windows/ovsext/Ethernet.h >+++ b/datapath-windows/ovsext/Ethernet.h >@@ -66,6 +66,8 @@ typedef enum { > ETH_TYPE_CDP = 0x2000, > ETH_TYPE_802_1PQ = 0x8100, // not really a DIX type, but used as >such > ETH_TYPE_LLC = 0xFFFF, // 0xFFFF is IANA reserved, used to >mark LLC >+ ETH_TYPE_MPLS = 0x8847, >+ ETH_TYPE_MPLS_MCAST = 0x8848, > } Eth_DixType; > > typedef enum { >diff --git a/datapath-windows/ovsext/Flow.c >b/datapath-windows/ovsext/Flow.c >index b629c93..c989c14 100644 >--- a/datapath-windows/ovsext/Flow.c >+++ b/datapath-windows/ovsext/Flow.c >@@ -80,6 +80,8 @@ static NTSTATUS _MapFlowIpv6KeyToNlKey(PNL_BUFFER nlBuf, > Icmp6Key *ipv6FlowPutIcmpKey); > static NTSTATUS _MapFlowArpKeyToNlKey(PNL_BUFFER nlBuf, > ArpKey *arpFlowPutKey); >+static NTSTATUS _MapFlowMplsKeyToNlKey(PNL_BUFFER nlBuf, >+ MplsKey *mplsFlowPutKey); > > static NTSTATUS OvsDoDumpFlows(OvsFlowDumpInput *dumpInput, > OvsFlowDumpOutput *dumpOutput, >@@ -108,7 +110,7 @@ const NL_POLICY nlFlowPolicy[] = { > > /* For Parsing nested OVS_FLOW_ATTR_KEY attributes. > * Some of the attributes like OVS_KEY_ATTR_RECIRC_ID >- * & OVS_KEY_ATTR_MPLS are not supported yet. */ >+ * are not supported yet. */ > > const NL_POLICY nlFlowKeyPolicy[] = { > [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE}, >@@ -872,6 +874,13 @@ MapFlowKeyToNlKey(PNL_BUFFER nlBuf, > break; > } > >+ case ETH_TYPE_MPLS: >+ case ETH_TYPE_MPLS_MCAST: { >+ MplsKey *mplsFlowPutKey = &(flowKey->mplsKey); >+ rc = _MapFlowMplsKeyToNlKey(nlBuf, mplsFlowPutKey); >+ break; >+ } >+ > default: > break; > } >@@ -1194,6 +1203,31 @@ done: > > /* > >*------------------------------------------------------------------------- >--- >+ * _MapFlowMplsKeyToNlKey -- >+ * Maps _MapFlowMplsKeyToNlKey to OVS_KEY_ATTR_MPLS attribute. >+ >*------------------------------------------------------------------------- >--- >+ */ >+static NTSTATUS >+_MapFlowMplsKeyToNlKey(PNL_BUFFER nlBuf, MplsKey *mplsFlowPutKey) >+{ >+ NTSTATUS rc = STATUS_SUCCESS; >+ struct ovs_key_mpls *mplsKey; >+ >+ mplsKey = (struct ovs_key_mpls *) >+ NlMsgPutTailUnspecUninit(nlBuf, OVS_KEY_ATTR_MPLS, >sizeof(*mplsKey)); >+ if (!mplsKey) { >+ rc = STATUS_UNSUCCESSFUL; >+ goto done; >+ } >+ >+ mplsKey->mpls_lse = mplsFlowPutKey->top_lse; >+ >+done: >+ return rc; >+} >+ >+/* >+ >*------------------------------------------------------------------------- >--- > * _MapNlToFlowPut -- > * Maps input netlink message to OvsFlowPut. > >*------------------------------------------------------------------------- >--- >@@ -1469,8 +1503,28 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs, > arpFlowPutKey->pad[1] = 0; > arpFlowPutKey->pad[2] = 0; > destKey->l2.keyLen += OVS_ARP_KEY_SIZE; >- break; > } >+ break; >+ } >+ case ETH_TYPE_MPLS: >+ case ETH_TYPE_MPLS_MCAST: { >+ >+ if (keyAttrs[OVS_KEY_ATTR_MPLS]) { >+ MplsKey *mplsFlowPutKey = &destKey->mplsKey; >+ const struct ovs_key_mpls *mplsKey; >+ UINT32 size = NlAttrGetSize(keyAttrs[OVS_KEY_ATTR_MPLS]); >+ UINT32 n = size / sizeof(struct ovs_key_mpls); >+ >+ mplsKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_MPLS]); >+ >+ mplsFlowPutKey->top_lse = mplsKey->mpls_lse; >+ mplsFlowPutKey->pad[0] = 0; >+ mplsFlowPutKey->pad[1] = 0; >+ mplsFlowPutKey->pad[2] = 0; >+ mplsFlowPutKey->pad[3] = 0; >+ destKey->l2.keyLen += (UINT16)n * sizeof(MplsKey); >+ } >+ break; > } > } > } >@@ -1864,6 +1918,37 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet, > memcpy(arpKey->arpTha, arp->arp_tha, ETH_ADDR_LENGTH); > } > } >+ } else if (flow->l2.dlType == htons(ETH_TYPE_MPLS) || >+ flow->l2.dlType == htons(ETH_TYPE_MPLS_MCAST)) { >+ MPLSHdr mplsStorage; >+ const MPLSHdr *mpls; >+ MplsKey *mplsKey = &flow->mplsKey; >+ ((UINT64 *)mplsKey)[0] = 0; >+ >+ /* In the presence of an MPLS label stack the end of the L2 >+ * header and the beginning of the L3 header differ. >+ * >+ * A network packet may contain multiple MPLS labels, but we >+ * are only interested in the topmost label stack entry. >+ * >+ * Advance network header to the beginning of the L3 header. >+ * layers->l3Offset corresponds to the end of the L2 header. >+ */ >+ for (UINT32 i = 0; i < FLOW_MAX_MPLS_LABELS; i++) { >+ mpls = OvsGetMpls(packet, layers->l3Offset, &mplsStorage); >+ if (mpls) { >+ >+ /* Keep only the topmost MPLS label stack entry. */ >+ if (i == 0) { >+ mplsKey->top_lse = mpls->mpls_lse; >+ } >+ >+ layers->l3Offset += sizeof(MPLSHdr); >+ >+ if (mpls->mpls_lse & htonl(MPLS_LS_S_MASK)) >+ break; >+ } >+ } > } > > return NDIS_STATUS_SUCCESS; >diff --git a/datapath-windows/ovsext/NetProto.h >b/datapath-windows/ovsext/NetProto.h >index a364869..12e8bd9 100644 >--- a/datapath-windows/ovsext/NetProto.h >+++ b/datapath-windows/ovsext/NetProto.h >@@ -366,4 +366,37 @@ typedef struct IPOpt { > #define SOCKET_IPPROTO_UDP 17 > #define SOCKET_IPPROTO_GRE 47 > >+/* Reference: RFC 5462, RFC 3032 >+ * >+ * 0 1 2 3 >+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 >+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ >+ * | Label | TC |S| TTL | >+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ >+ * >+ * Label: Label Value, 20 bits >+ * TC: Traffic Class field, 3 bits >+ * S: Bottom of Stack, 1 bit >+ * TTL: Time to Live, 8 bits >+ */ >+ Can¹t you simply define the structure with the respective bits that are used instead of the mask and shift? You can name it lse instead of redundant mpls_lse >+typedef struct MPLSHdr { >+ ovs_be32 mpls_lse; >+} MPLSHdr; >+ >+/* >+ * MPLS definitions >+ */ >+#define MPLS_HLEN 4 >+#define FLOW_MAX_MPLS_LABELS 3 >+#define MPLS_LS_LABEL_MASK 0xFFFFF000 >+#define MPLS_LS_LABEL_SHIFT 12 >+#define MPLS_LS_TC_MASK 0x00000E00 >+#define MPLS_LS_TC_SHIFT 9 >+#define MPLS_LS_S_MASK 0x00000100 >+#define MPLS_LS_S_SHIFT 8 >+#define MPLS_LS_TTL_MASK 0x000000FF >+#define MPLS_LS_TTL_SHIFT 0 >+ >+ > #endif /* __NET_PROTO_H_ */ >diff --git a/datapath-windows/ovsext/PacketParser.c >b/datapath-windows/ovsext/PacketParser.c >index e01be17..246c603 100644 >--- a/datapath-windows/ovsext/PacketParser.c >+++ b/datapath-windows/ovsext/PacketParser.c >@@ -84,8 +84,8 @@ OvsGetPacketBytes(const NET_BUFFER_LIST *nbl, > > NDIS_STATUS > OvsParseIPv6(const NET_BUFFER_LIST *packet, >- OvsFlowKey *key, >- POVS_PACKET_HDR_INFO layers) >+ OvsFlowKey *key, >+ POVS_PACKET_HDR_INFO layers) > { > UINT16 ofs = layers->l3Offset; > IPv6Hdr ipv6HdrStorage; >@@ -178,8 +178,8 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet, > > VOID > OvsParseTcp(const NET_BUFFER_LIST *packet, >- L4Key *flow, >- POVS_PACKET_HDR_INFO layers) >+ L4Key *flow, >+ POVS_PACKET_HDR_INFO layers) > { > TCPHdr tcpStorage; > const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage); >@@ -193,8 +193,8 @@ OvsParseTcp(const NET_BUFFER_LIST *packet, > > VOID > OvsParseUdp(const NET_BUFFER_LIST *packet, >- L4Key *flow, >- POVS_PACKET_HDR_INFO layers) >+ L4Key *flow, >+ POVS_PACKET_HDR_INFO layers) > { > UDPHdr udpStorage; > const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage); >diff --git a/datapath-windows/ovsext/PacketParser.h >b/datapath-windows/ovsext/PacketParser.h >index 55d110f..96136b7 100644 >--- a/datapath-windows/ovsext/PacketParser.h >+++ b/datapath-windows/ovsext/PacketParser.h >@@ -141,4 +141,11 @@ OvsGetIcmp(const NET_BUFFER_LIST *packet, > return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); > } > >+static const MPLSHdr * >+OvsGetMpls(const NET_BUFFER_LIST *packet, >+ UINT32 ofs, >+ MPLSHdr *storage) >+{ >+ return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); >+} > #endif /* __PACKET_PARSER_H_ */ >-- >1.9.0.msysgit.0 >_______________________________________________ >dev mailing list >dev@openvswitch.org >https://urldefense.proofpoint.com/v2/url?u=http-3A__openvswitch.org_mailma >n_listinfo_dev&d=BQIGaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=Dc >ruz40PROJ40ROzSpxyQSLw6fcrOWpJgEcEmNR3JEQ&m=4jij1GpaBzb8M2GdcLn47dOarUO1na >f-2Is_kWuRby4&s=XYH34q49hUVRrnPrL3HqLDdPj3AJPM0N_ab6JxhriJk&e= _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev