Skip to content

Commit

Permalink
datapath-windows: Support for OVS_KEY_ATTR_MPLS attribute
Browse files Browse the repository at this point in the history
This patch adds OVS_KEY_ATTR_MPLS to the OVS flow mechanism.

Tested using ping.
Tested using iperf (TCP and UDP).
Tested using DriverVerifier.

Signed-off-by: Sorin Vinturis <[email protected]>
Acked-by: Nithin Raju <[email protected]>
Signed-off-by: Gurucharan Shetty <[email protected]>
  • Loading branch information
svinturis authored and shettyg committed Feb 2, 2016
1 parent fa47c11 commit 5874d57
Show file tree
Hide file tree
Showing 11 changed files with 371 additions and 31 deletions.
1 change: 1 addition & 0 deletions datapath-windows/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ EXTRA_DIST += \
datapath-windows/ovsext/IpHelper.h \
datapath-windows/ovsext/Jhash.c \
datapath-windows/ovsext/Jhash.h \
datapath-windows/ovsext/Mpls.h \
datapath-windows/ovsext/NetProto.h \
datapath-windows/ovsext/Netlink/Netlink.c \
datapath-windows/ovsext/Netlink/Netlink.h \
Expand Down
206 changes: 189 additions & 17 deletions datapath-windows/ovsext/Actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Event.h"
#include "Flow.h"
#include "Gre.h"
#include "Mpls.h"
#include "NetProto.h"
#include "PacketIO.h"
#include "Stt.h"
Expand Down Expand Up @@ -1031,28 +1032,29 @@ OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)

/*
* --------------------------------------------------------------------------
* OvsPopVlanInPktBuf --
* Function to pop a VLAN tag when the tag is in the packet buffer.
* OvsPopFieldInPacketBuf --
* Function to pop a specified field of length 'shiftLength' located at
* 'shiftOffset' from the ethernet header. The data on the left of the
* 'shiftOffset' is right shifted.
*
* Returns a pointer to the new start in 'bufferData'.
* --------------------------------------------------------------------------
*/
static __inline NDIS_STATUS
OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
UINT32 shiftOffset,
UINT32 shiftLength,
PUINT8 *bufferData)
{
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[ETH_HEADER_LENGTH];

/*
* Declare a dummy vlanTag structure since we need to compute the size
* of shiftLength. The NDIS one is a unionized structure.
*/
NDIS_PACKET_8021Q_INFO vlanTag = {0};
ULONG shiftLength = sizeof (vlanTag.TagHeader);
PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)];
ASSERT(shiftOffset > ETH_ADDR_LENGTH);

newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
0, 0, TRUE /* copy NBL info */);
Expand All @@ -1064,8 +1066,8 @@ OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
/* 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,
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) {
Expand All @@ -1083,16 +1085,142 @@ OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
return NDIS_STATUS_RESOURCES;
}
mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
/* Bail out if L2 + VLAN header is not contiguous in the first buffer. */
if (MIN(packetLen, mdlLen) < sizeof (EthHdr) + shiftLength) {
/* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + shiftLength) {
ASSERT(FALSE);
return NDIS_STATUS_FAILURE;
}
bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
RtlCopyMemory(tempBuffer, bufferStart, dataLength);
RtlCopyMemory(bufferStart + shiftLength, tempBuffer, dataLength);
RtlCopyMemory(tempBuffer, bufferStart, shiftOffset);
RtlCopyMemory(bufferStart + shiftLength, tempBuffer, shiftOffset);
NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);

if (bufferData) {
*bufferData = bufferStart + shiftLength;
}

return NDIS_STATUS_SUCCESS;
}


/*
* --------------------------------------------------------------------------
* OvsPopVlanInPktBuf --
* Function to pop a VLAN tag when the tag is in the packet buffer.
* --------------------------------------------------------------------------
*/
static __inline NDIS_STATUS
OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
{
/*
* Declare a dummy vlanTag structure since we need to compute the size
* of shiftLength. The NDIS one is a unionized structure.
*/
NDIS_PACKET_8021Q_INFO vlanTag = {0};
UINT32 shiftLength = sizeof(vlanTag.TagHeader);
UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);

return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
}


/*
* --------------------------------------------------------------------------
* OvsActionMplsPop --
* Function to pop the first MPLS label from the current packet.
* --------------------------------------------------------------------------
*/
static __inline NDIS_STATUS
OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx,
ovs_be16 ethertype)
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
EthHdr *ethHdr = NULL;

status = OvsPopFieldInPacketBuf(ovsFwdCtx, sizeof(*ethHdr),
MPLS_HLEN, (PUINT8*)&ethHdr);
if (status == NDIS_STATUS_SUCCESS) {
if (ethHdr && OvsEthertypeIsMpls(ethHdr->Type)) {
ethHdr->Type = ethertype;
}

layers->l3Offset -= MPLS_HLEN;
layers->l4Offset -= MPLS_HLEN;
}

return status;
}


/*
* --------------------------------------------------------------------------
* OvsActionMplsPush --
* Function to push the MPLS label into the current packet.
* --------------------------------------------------------------------------
*/
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;
PNET_BUFFER_LIST newNbl;

newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
layers->l3Offset, MPLS_HLEN, TRUE);
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);

status = NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, 0, NULL);
if (status != NDIS_STATUS_SUCCESS) {
return status;
}

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);
RtlMoveMemory(ethHdr, (UINT8*)ethHdr + MPLS_HLEN, sizeof(*ethHdr));
ethHdr->Type = mpls->mpls_ethertype;

mplsHdr = (MPLSHdr *)(ethHdr + 1);
mplsHdr->lse = mpls->mpls_lse;

layers->l3Offset += MPLS_HLEN;
layers->l4Offset += MPLS_HLEN;

return NDIS_STATUS_SUCCESS;
}

Expand Down Expand Up @@ -1523,6 +1651,50 @@ 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-push MPLS action failed";
goto dropit;
}
layers->l3Offset += MPLS_HLEN;
layers->l4Offset += MPLS_HLEN;
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, NlAttrGetBe16(a));
if (status != NDIS_STATUS_SUCCESS) {
dropReason = L"OVS-pop MPLS action failed";
goto dropit;
}
layers->l3Offset -= MPLS_HLEN;
layers->l4Offset -= MPLS_HLEN;
break;
}

case OVS_ACTION_ATTR_USERSPACE:
{
PNL_ATTR userdataAttr;
Expand Down
12 changes: 10 additions & 2 deletions datapath-windows/ovsext/DpInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <netioapi.h>
#define IFNAMSIZ IF_NAMESIZE
#include "../ovsext/Netlink/Netlink.h"
#include "Mpls.h"

#define OVS_DP_NUMBER ((uint32_t) 0)

Expand Down Expand Up @@ -125,7 +126,7 @@ typedef struct L2Key {
uint8_t dlDst[6]; /* Ethernet destination address. */
ovs_be16 vlanTci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be16 dlType; /* Ethernet frame type. */
} L2Key; /* Size of 24 byte. */
} L2Key; /* Size of 24 byte. */

/* Number of packet attributes required to store OVS tunnel key. */
#define NUM_PKT_ATTR_REQUIRED 3
Expand All @@ -147,16 +148,23 @@ typedef union OvsIPv4TunnelKey {
};
};
uint64_t attr[NUM_PKT_ATTR_REQUIRED];
} OvsIPv4TunnelKey;
} OvsIPv4TunnelKey; /* Size of 24 byte. */

typedef struct MplsKey {
ovs_be32 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 */
union {
/* These headers are mutually exclusive. */
IpKey ipKey; /* size 16 */
ArpKey arpKey; /* size 24 */
Ipv6Key ipv6Key; /* size 48 */
Icmp6Key icmp6Key; /* size 72 */
MplsKey mplsKey; /* size 8 */
};
} OvsFlowKey;

Expand Down
2 changes: 2 additions & 0 deletions datapath-windows/ovsext/Ethernet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 5874d57

Please sign in to comment.