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; } +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; + + NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, FALSE, NULL); + + return NDIS_STATUS_SUCCESS; +} + +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 + */ + +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 http://openvswitch.org/mailman/listinfo/dev