#include "Jhash.h"
#include "Flow.h"
#include "PacketParser.h"
+#include "Datapath.h"
#ifdef OVS_DBG_MOD
#undef OVS_DBG_MOD
#pragma warning( push )
#pragma warning( disable:4127 )
-extern PNDIS_SPIN_LOCK gOvsCtrlLock;
extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
extern UINT64 ovsTimeIncrementPerTick;
static NTSTATUS ReportFlowInfo(OvsFlow *flow, UINT32 getFlags,
- UINT32 getActionsLen, OvsFlowInfo *info);
+ OvsFlowInfo *info);
static NTSTATUS HandleFlowPut(OvsFlowPut *put,
OVS_DATAPATH *datapath,
struct OvsFlowStats *stats);
static NTSTATUS AddFlow(OVS_DATAPATH *datapath, OvsFlow *flow);
static VOID FreeFlow(OvsFlow *flow);
static VOID __inline *GetStartAddrNBL(const NET_BUFFER_LIST *_pNB);
+static NTSTATUS _MapNlToFlowPut(POVS_MESSAGE msgIn, PNL_ATTR keyAttr,
+ PNL_ATTR actionAttr,
+ PNL_ATTR flowAttrClear,
+ OvsFlowPut *mappedFlow);
+static VOID _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
+ PNL_ATTR *tunnelAttrs,
+ OvsFlowKey *destKey);
+
+static VOID _MapTunAttrToFlowPut(PNL_ATTR *keyAttrs,
+ PNL_ATTR *tunnelAttrs,
+ OvsFlowKey *destKey);
+static VOID _MapNlToFlowPutFlags(PGENL_MSG_HDR genlMsgHdr,
+ PNL_ATTR flowAttrClear,
+ OvsFlowPut *mappedFlow);
+
+static NTSTATUS _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen);
+static NTSTATUS _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen);
+static NTSTATUS _MapFlowInfoToNl(PNL_BUFFER nlBuf,
+ OvsFlowInfo *flowInfo);
+static NTSTATUS _MapFlowStatsToNlStats(PNL_BUFFER nlBuf,
+ OvsFlowStats *flowStats);
+static NTSTATUS _MapFlowActionToNlAction(PNL_BUFFER nlBuf,
+ uint32_t actionsLen,
+ PNL_ATTR actions);
+
+static NTSTATUS _MapFlowIpv4KeyToNlKey(PNL_BUFFER nlBuf,
+ IpKey *ipv4FlowPutKey);
+static NTSTATUS _MapFlowIpv6KeyToNlKey(PNL_BUFFER nlBuf,
+ Ipv6Key *ipv6FlowPutKey,
+ 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,
+ UINT32 *replyLen);
+static NTSTATUS OvsProbeSupportedFeature(POVS_MESSAGE msgIn,
+ PNL_ATTR keyAttr);
+
#define OVS_FLOW_TABLE_SIZE 2048
#define OVS_FLOW_TABLE_MASK (OVS_FLOW_TABLE_SIZE -1)
#define HASH_BUCKET(hash) ((hash) & OVS_FLOW_TABLE_MASK)
-/* Flow family related netlink policies */
+/* Flow family related netlink policies */
+
+/* For Parsing attributes in FLOW_* commands */
+const NL_POLICY nlFlowPolicy[] = {
+ [OVS_FLOW_ATTR_KEY] = {.type = NL_A_NESTED, .optional = FALSE},
+ [OVS_FLOW_ATTR_MASK] = {.type = NL_A_NESTED, .optional = TRUE},
+ [OVS_FLOW_ATTR_ACTIONS] = {.type = NL_A_NESTED, .optional = TRUE},
+ [OVS_FLOW_ATTR_STATS] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_flow_stats),
+ .maxLen = sizeof(struct ovs_flow_stats),
+ .optional = TRUE},
+ [OVS_FLOW_ATTR_TCP_FLAGS] = {NL_A_U8, .optional = TRUE},
+ [OVS_FLOW_ATTR_USED] = {NL_A_U64, .optional = TRUE},
+ [OVS_FLOW_ATTR_PROBE] = {.type = NL_A_FLAG, .optional = TRUE}
+};
+
+/* For Parsing nested OVS_FLOW_ATTR_KEY attributes.
+ * Some of the attributes like OVS_KEY_ATTR_RECIRC_ID
+ * are not supported yet. */
+
+const NL_POLICY nlFlowKeyPolicy[] = {
+ [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE},
+ [OVS_KEY_ATTR_PRIORITY] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = TRUE},
+ [OVS_KEY_ATTR_IN_PORT] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = FALSE},
+ [OVS_KEY_ATTR_ETHERNET] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_ethernet),
+ .maxLen = sizeof(struct ovs_key_ethernet),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_VLAN] = {.type = NL_A_UNSPEC, .minLen = 2,
+ .maxLen = 2, .optional = TRUE},
+ [OVS_KEY_ATTR_ETHERTYPE] = {.type = NL_A_UNSPEC, .minLen = 2,
+ .maxLen = 2, .optional = TRUE},
+ [OVS_KEY_ATTR_IPV4] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_ipv4),
+ .maxLen = sizeof(struct ovs_key_ipv4),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_IPV6] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_ipv6),
+ .maxLen = sizeof(struct ovs_key_ipv6),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_TCP] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_tcp),
+ .maxLen = sizeof(struct ovs_key_tcp),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_UDP] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_udp),
+ .maxLen = sizeof(struct ovs_key_udp),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_ICMP] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_icmp),
+ .maxLen = sizeof(struct ovs_key_icmp),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_ICMPV6] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_icmpv6),
+ .maxLen = sizeof(struct ovs_key_icmpv6),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_ARP] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_arp),
+ .maxLen = sizeof(struct ovs_key_arp),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_ND] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_nd),
+ .maxLen = sizeof(struct ovs_key_nd),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_SKB_MARK] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = TRUE},
+ [OVS_KEY_ATTR_TUNNEL] = {.type = NL_A_VAR_LEN, .optional = TRUE},
+ [OVS_KEY_ATTR_SCTP] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_key_sctp),
+ .maxLen = sizeof(struct ovs_key_sctp),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_TCP_FLAGS] = {.type = NL_A_UNSPEC,
+ .minLen = 2, .maxLen = 2,
+ .optional = TRUE},
+ [OVS_KEY_ATTR_DP_HASH] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = TRUE},
+ [OVS_KEY_ATTR_RECIRC_ID] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = TRUE},
+ [OVS_KEY_ATTR_MPLS] = {.type = NL_A_VAR_LEN, .optional = TRUE}
+};
+const UINT32 nlFlowKeyPolicyLen = ARRAY_SIZE(nlFlowKeyPolicy);
+
+/* For Parsing nested OVS_KEY_ATTR_TUNNEL attributes */
+const NL_POLICY nlFlowTunnelKeyPolicy[] = {
+ [OVS_TUNNEL_KEY_ATTR_ID] = {.type = NL_A_UNSPEC, .minLen = 8,
+ .maxLen = 8, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = {.type = NL_A_UNSPEC, .minLen = 4,
+ .maxLen = 4, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = {.type = NL_A_UNSPEC, .minLen = 4 ,
+ .maxLen = 4, .optional = FALSE},
+ [OVS_TUNNEL_KEY_ATTR_TOS] = {.type = NL_A_UNSPEC, .minLen = 1,
+ .maxLen = 1, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_TTL] = {.type = NL_A_UNSPEC, .minLen = 1,
+ .maxLen = 1, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = {.type = NL_A_UNSPEC, .minLen = 0,
+ .maxLen = 0, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_CSUM] = {.type = NL_A_UNSPEC, .minLen = 0,
+ .maxLen = 0, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_OAM] = {.type = NL_A_UNSPEC, .minLen = 0,
+ .maxLen = 0, .optional = TRUE},
+ [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = {.type = NL_A_VAR_LEN,
+ .optional = TRUE}
+};
+
+/* For Parsing nested OVS_FLOW_ATTR_ACTIONS attributes */
+const NL_POLICY nlFlowActionPolicy[] = {
+ [OVS_ACTION_ATTR_OUTPUT] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT32),
+ .maxLen = sizeof(UINT32), .optional = TRUE},
+ [OVS_ACTION_ATTR_USERSPACE] = {.type = NL_A_VAR_LEN, .optional = TRUE},
+ [OVS_ACTION_ATTR_PUSH_VLAN] = {.type = NL_A_UNSPEC,
+ .minLen =
+ sizeof(struct ovs_action_push_vlan),
+ .maxLen =
+ sizeof(struct ovs_action_push_vlan),
+ .optional = TRUE},
+ [OVS_ACTION_ATTR_POP_VLAN] = {.type = NL_A_UNSPEC, .optional = TRUE},
+ [OVS_ACTION_ATTR_PUSH_MPLS] = {.type = NL_A_UNSPEC,
+ .minLen =
+ sizeof(struct ovs_action_push_mpls),
+ .maxLen =
+ sizeof(struct ovs_action_push_mpls),
+ .optional = TRUE},
+ [OVS_ACTION_ATTR_POP_MPLS] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(UINT16),
+ .maxLen = sizeof(UINT16),
+ .optional = TRUE},
+ [OVS_ACTION_ATTR_RECIRC] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(UINT32),
+ .maxLen = sizeof(UINT32),
+ .optional = TRUE},
+ [OVS_ACTION_ATTR_HASH] = {.type = NL_A_UNSPEC,
+ .minLen = sizeof(struct ovs_action_hash),
+ .maxLen = sizeof(struct ovs_action_hash),
+ .optional = TRUE},
+ [OVS_ACTION_ATTR_SET] = {.type = NL_A_VAR_LEN, .optional = TRUE},
+ [OVS_ACTION_ATTR_SAMPLE] = {.type = NL_A_VAR_LEN, .optional = TRUE}
+};
+
+/*
+ *----------------------------------------------------------------------------
+ * Netlink interface for flow commands.
+ *----------------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsFlowNewCmdHandler --
+ * Handler for OVS_FLOW_CMD_NEW/SET/DEL command.
+ * It also handles FLUSH case (DEL w/o any key in input)
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ BOOLEAN ok;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
+ PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
+ POVS_HDR ovsHdr = &(msgIn->ovsHdr);
+ PNL_ATTR flowAttrs[__OVS_FLOW_ATTR_MAX];
+ UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN;
+ OvsFlowPut mappedFlow;
+ OvsFlowStats stats;
+ struct ovs_flow_stats replyStats;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ NL_BUFFER nlBuf;
+
+ RtlZeroMemory(&mappedFlow, sizeof(OvsFlowPut));
+ RtlZeroMemory(&stats, sizeof(stats));
+ RtlZeroMemory(&replyStats, sizeof(replyStats));
+
+ if (!(usrParamsCtx->outputBuffer)) {
+ /* No output buffer */
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+ /* Get all the top level Flow attributes */
+ if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr),
+ nlFlowPolicy, ARRAY_SIZE(nlFlowPolicy),
+ flowAttrs, ARRAY_SIZE(flowAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* FLOW_DEL command w/o any key input is a flush case. */
+ if ((genlMsgHdr->cmd == OVS_FLOW_CMD_DEL) &&
+ (!(flowAttrs[OVS_FLOW_ATTR_KEY]))) {
+
+ rc = OvsFlushFlowIoctl(ovsHdr->dp_ifindex);
+
+ if (rc == STATUS_SUCCESS) {
+ /* XXX: refactor this code. */
+ /* So far so good. Prepare the reply for userspace */
+ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength);
+
+ /* Prepare nl Msg headers */
+ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
+ nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
+ genlMsgHdr->cmd, OVS_FLOW_VERSION,
+ ovsHdr->dp_ifindex);
+ if (ok) {
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+ } else {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ }
+ }
+
+ goto done;
+ }
+
+ if (flowAttrs[OVS_FLOW_ATTR_PROBE]) {
+ rc = OvsProbeSupportedFeature(msgIn, flowAttrs[OVS_FLOW_ATTR_KEY]);
+ if (rc != STATUS_SUCCESS) {
+ nlError = NlMapStatusToNlErr(rc);
+ goto done;
+ }
+ }
+
+ if ((rc = _MapNlToFlowPut(msgIn, flowAttrs[OVS_FLOW_ATTR_KEY],
+ flowAttrs[OVS_FLOW_ATTR_ACTIONS],
+ flowAttrs[OVS_FLOW_ATTR_CLEAR],
+ &mappedFlow))
+ != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("Conversion to OvsFlowPut failed");
+ goto done;
+ }
+
+ rc = OvsPutFlowIoctl(&mappedFlow, sizeof (struct OvsFlowPut),
+ &stats);
+ if (rc != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("OvsPutFlowIoctl failed.");
+ /*
+ * Report back to the userspace the flow could not be modified,
+ * created or deleted
+ */
+ nlError = NL_ERROR_NOENT;
+ goto done;
+ }
+
+ replyStats.n_packets = stats.packetCount;
+ replyStats.n_bytes = stats.byteCount;
+
+ /* So far so good. Prepare the reply for userspace */
+ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength);
+
+ /* Prepare nl Msg headers */
+ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
+ nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
+ genlMsgHdr->cmd, OVS_FLOW_VERSION,
+ ovsHdr->dp_ifindex);
+ if (!ok) {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ } else {
+ rc = STATUS_SUCCESS;
+ }
+
+ /* Append OVS_FLOW_ATTR_KEY attribute. This is need i.e. for flow delete*/
+ if (!NlMsgPutNested(&nlBuf, OVS_FLOW_ATTR_KEY,
+ NlAttrData(flowAttrs[OVS_FLOW_ATTR_KEY]),
+ NlAttrGetSize(flowAttrs[OVS_FLOW_ATTR_KEY]))) {
+ OVS_LOG_ERROR("Adding OVS_FLOW_ATTR_KEY attribute failed.");
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+ /* Append OVS_FLOW_ATTR_STATS attribute */
+ if (!NlMsgPutTailUnspec(&nlBuf, OVS_FLOW_ATTR_STATS,
+ (PCHAR)(&replyStats), sizeof(replyStats))) {
+ OVS_LOG_ERROR("Adding OVS_FLOW_ATTR_STATS attribute failed.");
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+ msgOut->nlMsg.nlmsgLen = NLMSG_ALIGN(NlBufSize(&nlBuf));
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+done:
+
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ rc = STATUS_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsFlowNlGetCmdHandler --
+ * Handler for OVS_FLOW_CMD_GET/DUMP commands.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsFlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
+ status = _FlowNlGetCmdHandler(usrParamsCtx, replyLen);
+ } else {
+ status = _FlowNlDumpCmdHandler(usrParamsCtx, replyLen);
+ }
+
+ return status;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _FlowNlGetCmdHandler --
+ * Handler for OVS_FLOW_CMD_GET command.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+_FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
+ POVS_HDR ovsHdr = &(msgIn->ovsHdr);
+ PNL_MSG_HDR nlMsgOutHdr = NULL;
+ UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN;
+ PNL_ATTR nlAttrs[__OVS_FLOW_ATTR_MAX];
+
+ OvsFlowGetInput getInput;
+ OvsFlowGetOutput getOutput;
+ NL_BUFFER nlBuf;
+ PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX];
+ PNL_ATTR tunnelAttrs[__OVS_TUNNEL_KEY_ATTR_MAX];
+
+ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength);
+ RtlZeroMemory(&getInput, sizeof(OvsFlowGetInput));
+ RtlZeroMemory(&getOutput, sizeof(OvsFlowGetOutput));
+ UINT32 keyAttrOffset = 0;
+ UINT32 tunnelKeyAttrOffset = 0;
+ BOOLEAN ok;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+
+ if (usrParamsCtx->inputLength > usrParamsCtx->outputLength) {
+ /* Should not be the case.
+ * We'll be copying the flow keys back from
+ * input buffer to output buffer. */
+ rc = STATUS_INVALID_PARAMETER;
+ OVS_LOG_ERROR("inputLength: %d GREATER THEN outputLength: %d",
+ usrParamsCtx->inputLength, usrParamsCtx->outputLength);
+ goto done;
+ }
+
+ /* Get all the top level Flow attributes */
+ if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr),
+ nlFlowPolicy, ARRAY_SIZE(nlFlowPolicy),
+ nlAttrs, ARRAY_SIZE(nlAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ keyAttrOffset = (UINT32)((PCHAR) nlAttrs[OVS_FLOW_ATTR_KEY] -
+ (PCHAR)nlMsgHdr);
+
+ /* Get flow keys attributes */
+ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset,
+ NlAttrLen(nlAttrs[OVS_FLOW_ATTR_KEY]),
+ nlFlowKeyPolicy, ARRAY_SIZE(nlFlowKeyPolicy),
+ keyAttrs, ARRAY_SIZE(keyAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) {
+ tunnelKeyAttrOffset = (UINT32)((PCHAR)
+ (keyAttrs[OVS_KEY_ATTR_TUNNEL])
+ - (PCHAR)nlMsgHdr);
+
+ /* Get tunnel keys attributes */
+ if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset,
+ NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]),
+ nlFlowTunnelKeyPolicy,
+ ARRAY_SIZE(nlFlowTunnelKeyPolicy),
+ tunnelAttrs, ARRAY_SIZE(tunnelAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Tunnel key Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ }
+
+ _MapKeyAttrToFlowPut(keyAttrs, tunnelAttrs,
+ &(getInput.key));
+
+ getInput.dpNo = ovsHdr->dp_ifindex;
+ getInput.getFlags = FLOW_GET_STATS | FLOW_GET_ACTIONS;
+
+ /* 4th argument is a no op.
+ * We are keeping this argument to be compatible
+ * with our dpif-windows based interface. */
+ rc = OvsGetFlowIoctl(&getInput, &getOutput);
+ if (rc != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("OvsGetFlowIoctl failed.");
+ /*
+ * Report back to the userspace the flow could not be found
+ */
+ nlError = NL_ERROR_NOENT;
+ goto done;
+ }
+
+ /* Lets prepare the reply. */
+ nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0));
+
+ /* Input already has all the attributes for the flow key.
+ * Lets copy the values back. */
+ ok = NlMsgPutTail(&nlBuf, (PCHAR)(usrParamsCtx->inputBuffer),
+ usrParamsCtx->inputLength);
+ if (!ok) {
+ OVS_LOG_ERROR("Could not copy the data to the buffer tail");
+ goto done;
+ }
+
+ rc = _MapFlowStatsToNlStats(&nlBuf, &((getOutput.info).stats));
+ if (rc != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("_OvsFlowMapFlowKeyToNlStats failed.");
+ goto done;
+ }
+
+ rc = _MapFlowActionToNlAction(&nlBuf, ((getOutput.info).actionsLen),
+ getOutput.info.actions);
+ if (rc != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("_MapFlowActionToNlAction failed.");
+ goto done;
+ }
+
+ NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf));
+ NlMsgAlignSize(nlMsgOutHdr);
+ *replyLen += NlMsgSize(nlMsgOutHdr);
+
+done:
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ rc = STATUS_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _FlowNlDumpCmdHandler --
+ * Handler for OVS_FLOW_CMD_DUMP command.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+_FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ UINT32 temp = 0; /* To keep compiler happy for calling OvsDoDumpFlows */
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)
+ (usrParamsCtx->ovsInstance);
+ POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
+
+ if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
+ /* Dump Start */
+ OvsSetupDumpStart(usrParamsCtx);
+ goto done;
+ }
+
+ PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
+ PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
+ POVS_HDR ovsHdr = &(msgIn->ovsHdr);
+ PNL_MSG_HDR nlMsgOutHdr = NULL;
+ UINT32 hdrOffset = 0;
+
+ /* Get Next */
+ OvsFlowDumpOutput dumpOutput;
+ OvsFlowDumpInput dumpInput;
+ NL_BUFFER nlBuf;
+
+ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength);
+
+ ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+ ASSERT(usrParamsCtx->outputLength);
+
+ RtlZeroMemory(&dumpInput, sizeof(OvsFlowDumpInput));
+ RtlZeroMemory(&dumpOutput, sizeof(OvsFlowDumpOutput));
+
+ dumpInput.dpNo = ovsHdr->dp_ifindex;
+ dumpInput.getFlags = FLOW_GET_KEY | FLOW_GET_STATS | FLOW_GET_ACTIONS;
+
+ /* Lets provide as many flows to userspace as possible. */
+ do {
+ dumpInput.position[0] = instance->dumpState.index[0];
+ dumpInput.position[1] = instance->dumpState.index[1];
+
+ rc = OvsDoDumpFlows(&dumpInput, &dumpOutput, &temp);
+ if (rc != STATUS_SUCCESS) {
+ OVS_LOG_ERROR("OvsDoDumpFlows failed with rc: %d", rc);
+ /*
+ * Report back to the userspace the flows could not be found
+ */
+ nlError = NL_ERROR_NOENT;
+ break;
+ }
+
+ /* Done with Dump, send NLMSG_DONE */
+ if (!(dumpOutput.n)) {
+ BOOLEAN ok;
+
+ OVS_LOG_INFO("Dump Done");
+
+ nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, NlBufSize(&nlBuf), 0));
+ ok = NlFillNlHdr(&nlBuf, NLMSG_DONE, NLM_F_MULTI,
+ nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid);
+
+ if (!ok) {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ OVS_LOG_ERROR("Unable to prepare DUMP_DONE reply.");
+ break;
+ } else {
+ rc = STATUS_SUCCESS;
+ }
+
+ NlMsgAlignSize(nlMsgOutHdr);
+ *replyLen += NlMsgSize(nlMsgOutHdr);
+
+ FreeUserDumpState(instance);
+ break;
+ } else {
+ BOOLEAN ok;
+
+ hdrOffset = NlBufSize(&nlBuf);
+ nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, hdrOffset, 0));
+
+ /* Netlink header */
+ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, NLM_F_MULTI,
+ nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
+ genlMsgHdr->cmd, genlMsgHdr->version,
+ ovsHdr->dp_ifindex);
+
+ if (!ok) {
+ /* Reset rc to success so that we can
+ * send already added messages to user space. */
+ rc = STATUS_SUCCESS;
+ break;
+ }
+
+ /* Time to add attributes */
+ rc = _MapFlowInfoToNl(&nlBuf, &(dumpOutput.flow));
+ if (rc != STATUS_SUCCESS) {
+ /* Adding the attribute failed, we are out of
+ space in the buffer, remove the appended OVS header */
+ NlMsgSetSize(nlMsgOutHdr,
+ NlMsgSize(nlMsgOutHdr) -
+ sizeof(struct _OVS_MESSAGE));
+
+ /* Reset rc to success so that we can
+ * send already added messages to user space. */
+ rc = STATUS_SUCCESS;
+ break;
+ }
+
+ NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf) - hdrOffset);
+ NlMsgAlignSize(nlMsgOutHdr);
+ *replyLen += NlMsgSize(nlMsgOutHdr);
+ instance->dumpState.index[0] = dumpOutput.position[0];
+ instance->dumpState.index[1] = dumpOutput.position[1];
+ }
+ } while(TRUE);
+
+done:
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ rc = STATUS_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowInfoToNl --
+ * Maps OvsFlowInfo to Netlink attributes.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowInfoToNl(PNL_BUFFER nlBuf, OvsFlowInfo *flowInfo)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+
+ rc = MapFlowKeyToNlKey(nlBuf, &(flowInfo->key), OVS_FLOW_ATTR_KEY,
+ OVS_KEY_ATTR_TUNNEL);
+ if (rc != STATUS_SUCCESS) {
+ goto done;
+ }
+
+ rc = _MapFlowStatsToNlStats(nlBuf, &(flowInfo->stats));
+ if (rc != STATUS_SUCCESS) {
+ goto done;
+ }
+
+ rc = _MapFlowActionToNlAction(nlBuf, flowInfo->actionsLen,
+ flowInfo->actions);
+ if (rc != STATUS_SUCCESS) {
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowStatsToNlStats --
+ * Maps OvsFlowStats to OVS_FLOW_ATTR_STATS attribute.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowStatsToNlStats(PNL_BUFFER nlBuf, OvsFlowStats *flowStats)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ struct ovs_flow_stats replyStats;
+
+ replyStats.n_packets = flowStats->packetCount;
+ replyStats.n_bytes = flowStats->byteCount;
+
+ if (!NlMsgPutTailU64(nlBuf, OVS_FLOW_ATTR_USED, flowStats->used)) {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_FLOW_ATTR_STATS,
+ (PCHAR)(&replyStats),
+ sizeof(struct ovs_flow_stats))) {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU8(nlBuf, OVS_FLOW_ATTR_TCP_FLAGS, flowStats->tcpFlags)) {
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowActionToNlAction --
+ * Maps flow actions to OVS_FLOW_ATTR_ACTION attribute.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowActionToNlAction(PNL_BUFFER nlBuf, uint32_t actionsLen,
+ PNL_ATTR actions)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ UINT32 offset = 0;
+
+ offset = NlMsgStartNested(nlBuf, OVS_FLOW_ATTR_ACTIONS);
+ if (!offset) {
+ /* Starting the nested attribute failed. */
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto error_nested_start;
+ }
+
+ if (!NlBufCopyAtTail(nlBuf, (PCHAR)actions, actionsLen)) {
+ /* Adding a nested attribute failed. */
+ rc = STATUS_INVALID_BUFFER_SIZE;
+ goto done;
+ }
+
+done:
+ NlMsgEndNested(nlBuf, offset);
+error_nested_start:
+ return rc;
+
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * MapFlowKeyToNlKey --
+ * Maps OvsFlowKey to OVS_FLOW_ATTR_KEY attribute.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+MapFlowKeyToNlKey(PNL_BUFFER nlBuf,
+ OvsFlowKey *flowKey,
+ UINT16 keyType,
+ UINT16 tunKeyType)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ struct ovs_key_ethernet ethKey;
+ UINT32 offset = 0;
+
+ offset = NlMsgStartNested(nlBuf, keyType);
+ if (!offset) {
+ /* Starting the nested attribute failed. */
+ rc = STATUS_UNSUCCESSFUL;
+ goto error_nested_start;
+ }
+
+ /* Ethernet header */
+ RtlCopyMemory(&(ethKey.eth_src), flowKey->l2.dlSrc, ETH_ADDR_LEN);
+ RtlCopyMemory(&(ethKey.eth_dst), flowKey->l2.dlDst, ETH_ADDR_LEN);
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ETHERNET,
+ (PCHAR)(ðKey),
+ sizeof(struct ovs_key_ethernet))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU32(nlBuf, OVS_KEY_ATTR_IN_PORT,
+ flowKey->l2.inPort)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU16(nlBuf, OVS_KEY_ATTR_ETHERTYPE,
+ flowKey->l2.dlType)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (flowKey->l2.vlanTci) {
+ if (!NlMsgPutTailU16(nlBuf, OVS_KEY_ATTR_VLAN,
+ flowKey->l2.vlanTci)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ }
+
+ /* ==== L3 + L4 ==== */
+ switch (ntohs(flowKey->l2.dlType)) {
+ case ETH_TYPE_IPV4: {
+ IpKey *ipv4FlowPutKey = &(flowKey->ipKey);
+ rc = _MapFlowIpv4KeyToNlKey(nlBuf, ipv4FlowPutKey);
+ break;
+ }
+
+ case ETH_TYPE_IPV6: {
+ Ipv6Key *ipv6FlowPutKey = &(flowKey->ipv6Key);
+ Icmp6Key *icmpv6FlowPutKey = &(flowKey->icmp6Key);
+ rc = _MapFlowIpv6KeyToNlKey(nlBuf, ipv6FlowPutKey,
+ icmpv6FlowPutKey);
+ break;
+ }
+
+ case ETH_TYPE_ARP:
+ case ETH_TYPE_RARP: {
+ ArpKey *arpFlowPutKey = &(flowKey->arpKey);
+ rc = _MapFlowArpKeyToNlKey(nlBuf, arpFlowPutKey);
+ break;
+ }
+
+ case ETH_TYPE_MPLS:
+ case ETH_TYPE_MPLS_MCAST: {
+ MplsKey *mplsFlowPutKey = &(flowKey->mplsKey);
+ rc = _MapFlowMplsKeyToNlKey(nlBuf, mplsFlowPutKey);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (rc != STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if (flowKey->tunKey.dst) {
+ rc = MapFlowTunKeyToNlKey(nlBuf, &(flowKey->tunKey),
+ tunKeyType);
+ if (rc != STATUS_SUCCESS) {
+ goto done;
+ }
+ }
+
+done:
+ NlMsgEndNested(nlBuf, offset);
+error_nested_start:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * MapFlowTunKeyToNlKey --
+ * Maps OvsIPv4TunnelKey to OVS_TUNNEL_KEY_ATTR_ID attribute.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf,
+ OvsIPv4TunnelKey *tunKey,
+ UINT16 tunKeyType)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ UINT32 offset = 0;
+
+ offset = NlMsgStartNested(nlBuf, tunKeyType);
+ if (!offset) {
+ /* Starting the nested attribute failed. */
+ rc = STATUS_UNSUCCESSFUL;
+ goto error_nested_start;
+ }
+
+ if (!NlMsgPutTailU64(nlBuf, OVS_TUNNEL_KEY_ATTR_ID,
+ tunKey->tunnelId)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU32(nlBuf, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
+ tunKey->dst)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU32(nlBuf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
+ tunKey->src)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU8(nlBuf, OVS_TUNNEL_KEY_ATTR_TOS,
+ tunKey->tos)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NlMsgPutTailU8(nlBuf, OVS_TUNNEL_KEY_ATTR_TTL,
+ tunKey->ttl)) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+ NlMsgEndNested(nlBuf, offset);
+error_nested_start:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowTunKeyToNlKey --
+ * Maps OvsIPv4FlowPutKey to OVS_KEY_ATTR_IPV4 attribute.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowIpv4KeyToNlKey(PNL_BUFFER nlBuf, IpKey *ipv4FlowPutKey)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ struct ovs_key_ipv4 ipv4Key;
+
+ ipv4Key.ipv4_src = ipv4FlowPutKey->nwSrc;
+ ipv4Key.ipv4_dst = ipv4FlowPutKey->nwDst;
+ ipv4Key.ipv4_proto = ipv4FlowPutKey->nwProto;
+ ipv4Key.ipv4_tos = ipv4FlowPutKey->nwTos;
+ ipv4Key.ipv4_ttl = ipv4FlowPutKey->nwTtl;
+ ipv4Key.ipv4_frag = ipv4FlowPutKey->nwFrag;
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_IPV4,
+ (PCHAR)(&ipv4Key),
+ sizeof(struct ovs_key_ipv4))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ switch (ipv4Key.ipv4_proto) {
+ case IPPROTO_TCP: {
+ struct ovs_key_tcp tcpKey;
+ tcpKey.tcp_src = ipv4FlowPutKey->l4.tpSrc;
+ tcpKey.tcp_dst = ipv4FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_TCP,
+ (PCHAR)(&tcpKey),
+ sizeof(tcpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct ovs_key_udp udpKey;
+ udpKey.udp_src = ipv4FlowPutKey->l4.tpSrc;
+ udpKey.udp_dst = ipv4FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_UDP,
+ (PCHAR)(&udpKey),
+ sizeof(udpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_SCTP: {
+ struct ovs_key_sctp sctpKey;
+ sctpKey.sctp_src = ipv4FlowPutKey->l4.tpSrc;
+ sctpKey.sctp_dst = ipv4FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_SCTP,
+ (PCHAR)(&sctpKey),
+ sizeof(sctpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_ICMP: {
+ struct ovs_key_icmp icmpKey;
+ /* XXX: revisit to see if htons is needed */
+ icmpKey.icmp_type = (__u8)(ipv4FlowPutKey->l4.tpSrc);
+ icmpKey.icmp_code = (__u8)(ipv4FlowPutKey->l4.tpDst);
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ICMP,
+ (PCHAR)(&icmpKey),
+ sizeof(icmpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowIpv6KeyToNlKey --
+ * Maps _MapFlowIpv6KeyToNlKey to OVS_KEY_ATTR_IPV6 attribute.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowIpv6KeyToNlKey(PNL_BUFFER nlBuf, Ipv6Key *ipv6FlowPutKey,
+ Icmp6Key *icmpv6FlowPutKey)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ struct ovs_key_ipv6 ipv6Key;
+
+ RtlCopyMemory(&(ipv6Key.ipv6_src), &ipv6FlowPutKey->ipv6Src,
+ sizeof ipv6Key.ipv6_src);
+ RtlCopyMemory(&(ipv6Key.ipv6_dst), &ipv6FlowPutKey->ipv6Dst,
+ sizeof ipv6Key.ipv6_dst);
+
+ ipv6Key.ipv6_label = ipv6FlowPutKey->ipv6Label;
+ ipv6Key.ipv6_proto = ipv6FlowPutKey->nwProto;
+ ipv6Key.ipv6_tclass = ipv6FlowPutKey->nwTos;
+ ipv6Key.ipv6_hlimit = ipv6FlowPutKey->nwTtl;
+ ipv6Key.ipv6_frag = ipv6FlowPutKey->nwFrag;
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_IPV6,
+ (PCHAR)(&ipv6Key),
+ sizeof(ipv6Key))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ switch (ipv6Key.ipv6_proto) {
+ case IPPROTO_TCP: {
+ struct ovs_key_tcp tcpKey;
+ tcpKey.tcp_src = ipv6FlowPutKey->l4.tpSrc;
+ tcpKey.tcp_dst = ipv6FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_TCP,
+ (PCHAR)(&tcpKey),
+ sizeof(tcpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct ovs_key_udp udpKey;
+ udpKey.udp_src = ipv6FlowPutKey->l4.tpSrc;
+ udpKey.udp_dst = ipv6FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_UDP,
+ (PCHAR)(&udpKey),
+ sizeof(udpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_SCTP: {
+ struct ovs_key_sctp sctpKey;
+ sctpKey.sctp_src = ipv6FlowPutKey->l4.tpSrc;
+ sctpKey.sctp_dst = ipv6FlowPutKey->l4.tpDst;
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_SCTP,
+ (PCHAR)(&sctpKey),
+ sizeof(sctpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ break;
+ }
+
+ case IPPROTO_ICMPV6: {
+ struct ovs_key_icmpv6 icmpV6Key;
+ struct ovs_key_nd ndKey;
+
+ /* XXX: revisit to see if htons is needed */
+ icmpV6Key.icmpv6_type = (__u8)(icmpv6FlowPutKey->l4.tpSrc);
+ icmpV6Key.icmpv6_code = (__u8)(icmpv6FlowPutKey->l4.tpDst);
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ICMPV6,
+ (PCHAR)(&icmpV6Key),
+ sizeof(icmpV6Key))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ RtlCopyMemory(&(ndKey.nd_target), &icmpv6FlowPutKey->ndTarget,
+ sizeof(icmpv6FlowPutKey->ndTarget));
+ RtlCopyMemory(&(ndKey.nd_sll), &icmpv6FlowPutKey->arpSha,
+ ETH_ADDR_LEN);
+ RtlCopyMemory(&(ndKey.nd_tll), &icmpv6FlowPutKey->arpTha,
+ ETH_ADDR_LEN);
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ND,
+ (PCHAR)(&ndKey),
+ sizeof(ndKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapFlowArpKeyToNlKey --
+ * Maps _MapFlowArpKeyToNlKey to OVS_KEY_ATTR_ARP attribute.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapFlowArpKeyToNlKey(PNL_BUFFER nlBuf, ArpKey *arpFlowPutKey)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ struct ovs_key_arp arpKey;
+
+ arpKey.arp_sip = arpFlowPutKey->nwSrc;
+ arpKey.arp_tip = arpFlowPutKey->nwDst;
+
+ RtlCopyMemory(&(arpKey.arp_sha), arpFlowPutKey->arpSha, ETH_ADDR_LEN);
+ RtlCopyMemory(&(arpKey.arp_tha), arpFlowPutKey->arpTha, ETH_ADDR_LEN);
+
+ /*
+ * Flow_Extract() stores 'nwProto' in host order for ARP since 'nwProto' is
+ * 1 byte field and the ARP opcode is 2 bytes, and all of the kernel code
+ * understand this while looking at an ARP key.
+ * While we pass up the ARP key to userspace, convert from host order to
+ * network order. Likewise, when processing an ARP key from userspace,
+ * convert from network order to host order.
+ *
+ * It is important to note that the flow table stores the ARP opcode field
+ * in host order.
+ */
+ arpKey.arp_op = htons(arpFlowPutKey->nwProto);
+
+ if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ARP,
+ (PCHAR)(&arpKey),
+ sizeof(arpKey))) {
+ rc = STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _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->lse;
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapNlToFlowPut --
+ * Maps input netlink message to OvsFlowPut.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+_MapNlToFlowPut(POVS_MESSAGE msgIn, PNL_ATTR keyAttr,
+ PNL_ATTR actionAttr, PNL_ATTR flowAttrClear,
+ OvsFlowPut *mappedFlow)
+{
+ NTSTATUS rc = STATUS_SUCCESS;
+ PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
+ PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
+ POVS_HDR ovsHdr = &(msgIn->ovsHdr);
+
+ UINT32 keyAttrOffset = (UINT32)((PCHAR)keyAttr - (PCHAR)nlMsgHdr);
+ UINT32 tunnelKeyAttrOffset;
+
+ PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = {NULL};
+ PNL_ATTR tunnelAttrs[__OVS_TUNNEL_KEY_ATTR_MAX] = {NULL};
+
+ /* Get flow keys attributes */
+ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, NlAttrLen(keyAttr),
+ nlFlowKeyPolicy, ARRAY_SIZE(nlFlowKeyPolicy),
+ keyAttrs, ARRAY_SIZE(keyAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) {
+ tunnelKeyAttrOffset = (UINT32)((PCHAR)
+ (keyAttrs[OVS_KEY_ATTR_TUNNEL])
+ - (PCHAR)nlMsgHdr);
+
+ /* Get tunnel keys attributes */
+ if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset,
+ NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]),
+ nlFlowTunnelKeyPolicy,
+ ARRAY_SIZE(nlFlowTunnelKeyPolicy),
+ tunnelAttrs, ARRAY_SIZE(tunnelAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Tunnel key Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ rc = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ }
+
+ _MapKeyAttrToFlowPut(keyAttrs, tunnelAttrs,
+ &(mappedFlow->key));
+
+ /* Map the action */
+ if (actionAttr) {
+ mappedFlow->actionsLen = NlAttrGetSize(actionAttr);
+ mappedFlow->actions = NlAttrGet(actionAttr);
+ }
+
+ mappedFlow->dpNo = ovsHdr->dp_ifindex;
+
+ _MapNlToFlowPutFlags(genlMsgHdr, flowAttrClear,
+ mappedFlow);
+
+done:
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapNlToFlowPutFlags --
+ * Maps netlink message to OvsFlowPut->flags.
+ *----------------------------------------------------------------------------
+ */
+static VOID
+_MapNlToFlowPutFlags(PGENL_MSG_HDR genlMsgHdr,
+ PNL_ATTR flowAttrClear, OvsFlowPut *mappedFlow)
+{
+ uint32_t flags = 0;
+
+ switch (genlMsgHdr->cmd) {
+ case OVS_FLOW_CMD_NEW:
+ flags |= OVSWIN_FLOW_PUT_CREATE;
+ break;
+ case OVS_FLOW_CMD_DEL:
+ flags |= OVSWIN_FLOW_PUT_DELETE;
+ break;
+ case OVS_FLOW_CMD_SET:
+ flags |= OVSWIN_FLOW_PUT_MODIFY;
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ if (flowAttrClear) {
+ flags |= OVSWIN_FLOW_PUT_CLEAR;
+ }
+
+ mappedFlow->flags = flags;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * _MapKeyAttrToFlowPut --
+ * Converts FLOW_KEY attribute to OvsFlowPut->key.
+ *----------------------------------------------------------------------------
+ */
+static VOID
+_MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
+ PNL_ATTR *tunnelAttrs,
+ OvsFlowKey *destKey)
+{
+ _MapTunAttrToFlowPut(keyAttrs, tunnelAttrs, destKey);
+
+ /* ===== L2 headers ===== */
+ destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
+
+ if (keyAttrs[OVS_KEY_ATTR_ETHERNET]) {
+ const struct ovs_key_ethernet *eth_key;
+ eth_key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ETHERNET]);
+ RtlCopyMemory(destKey->l2.dlSrc, eth_key->eth_src, ETH_ADDR_LEN);
+ RtlCopyMemory(destKey->l2.dlDst, eth_key->eth_dst, ETH_ADDR_LEN);
+ }
+
+ /* TODO: Ideally ETHERTYPE should not be optional.
+ * But during vswitchd bootup we are seeing FLOW_ADD
+ * requests with no ETHERTYPE attributes.
+ * Need to verify this. */
+ if (keyAttrs[OVS_KEY_ATTR_ETHERTYPE]) {
+ destKey->l2.dlType = (NlAttrGetU16(keyAttrs
+ [OVS_KEY_ATTR_ETHERTYPE]));
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_VLAN]) {
+ destKey->l2.vlanTci = NlAttrGetU16(keyAttrs[OVS_KEY_ATTR_VLAN]);
+ }
-/* For Parsing attributes in FLOW_* commands */
-static const NL_POLICY nlFlowPolicy[] = {
- [OVS_FLOW_ATTR_KEY] = {.type = NL_A_NESTED, .optional = FALSE},
- [OVS_FLOW_ATTR_MASK] = {.type = NL_A_NESTED, .optional = TRUE},
- [OVS_FLOW_ATTR_ACTIONS] = {.type = NL_A_NESTED, .optional = TRUE},
- [OVS_FLOW_ATTR_STATS] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_flow_stats),
- .maxLen = sizeof(struct ovs_flow_stats),
- .optional = TRUE},
- [OVS_FLOW_ATTR_TCP_FLAGS] = {NL_A_U8, .optional = TRUE},
- [OVS_FLOW_ATTR_USED] = {NL_A_U64, .optional = TRUE}
-};
+ /* ==== L3 + L4. ==== */
+ destKey->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + OVS_L2_KEY_SIZE
+ - destKey->l2.offset;
+
+ switch (ntohs(destKey->l2.dlType)) {
+ case ETH_TYPE_IPV4: {
+
+ if (keyAttrs[OVS_KEY_ATTR_IPV4]) {
+ const struct ovs_key_ipv4 *ipv4Key;
+
+ ipv4Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_IPV4]);
+ IpKey *ipv4FlowPutKey = &(destKey->ipKey);
+ ipv4FlowPutKey->nwSrc = ipv4Key->ipv4_src;
+ ipv4FlowPutKey->nwDst = ipv4Key->ipv4_dst;
+ ipv4FlowPutKey->nwProto = ipv4Key->ipv4_proto;
+ ipv4FlowPutKey->nwTos = ipv4Key->ipv4_tos;
+ ipv4FlowPutKey->nwTtl = ipv4Key->ipv4_ttl;
+ ipv4FlowPutKey->nwFrag = ipv4Key->ipv4_frag;
+
+ if (keyAttrs[OVS_KEY_ATTR_TCP]) {
+ const struct ovs_key_tcp *tcpKey;
+ tcpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_TCP]);
+ ipv4FlowPutKey->l4.tpSrc = tcpKey->tcp_src;
+ ipv4FlowPutKey->l4.tpDst = tcpKey->tcp_dst;
+ }
-/* 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. */
+ if (keyAttrs[OVS_KEY_ATTR_UDP]) {
+ const struct ovs_key_udp *udpKey;
+ udpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_UDP]);
+ ipv4FlowPutKey->l4.tpSrc = udpKey->udp_src;
+ ipv4FlowPutKey->l4.tpDst = udpKey->udp_dst;
+ }
-static const NL_POLICY nlFlowKeyPolicy[] = {
- [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE},
- [OVS_KEY_ATTR_PRIORITY] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = TRUE},
- [OVS_KEY_ATTR_IN_PORT] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = FALSE},
- [OVS_KEY_ATTR_ETHERNET] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_ethernet),
- .maxLen = sizeof(struct ovs_key_ethernet),
- .optional = FALSE},
- [OVS_KEY_ATTR_VLAN] = {.type = NL_A_UNSPEC, .minLen = 2,
- .maxLen = 2, .optional = TRUE},
- [OVS_KEY_ATTR_ETHERTYPE] = {.type = NL_A_UNSPEC, .minLen = 2,
- .maxLen = 2, .optional = TRUE},
- [OVS_KEY_ATTR_IPV4] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_ipv4),
- .maxLen = sizeof(struct ovs_key_ipv4),
- .optional = TRUE},
- [OVS_KEY_ATTR_IPV6] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_ipv6),
- .maxLen = sizeof(struct ovs_key_ipv6),
- .optional = TRUE},
- [OVS_KEY_ATTR_TCP] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_tcp),
- .maxLen = sizeof(struct ovs_key_tcp),
- .optional = TRUE},
- [OVS_KEY_ATTR_UDP] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_udp),
- .maxLen = sizeof(struct ovs_key_udp),
- .optional = TRUE},
- [OVS_KEY_ATTR_ICMP] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_icmp),
- .maxLen = sizeof(struct ovs_key_icmp),
- .optional = TRUE},
- [OVS_KEY_ATTR_ICMPV6] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_icmpv6),
- .maxLen = sizeof(struct ovs_key_icmpv6),
- .optional = TRUE},
- [OVS_KEY_ATTR_ARP] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_arp),
- .maxLen = sizeof(struct ovs_key_arp),
- .optional = TRUE},
- [OVS_KEY_ATTR_ND] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_nd),
- .maxLen = sizeof(struct ovs_key_nd),
- .optional = TRUE},
- [OVS_KEY_ATTR_SKB_MARK] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = TRUE},
- [OVS_KEY_ATTR_TUNNEL] = {.type = NL_A_VAR_LEN, .optional = TRUE},
- [OVS_KEY_ATTR_SCTP] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_key_sctp),
- .maxLen = sizeof(struct ovs_key_sctp),
- .optional = TRUE},
- [OVS_KEY_ATTR_TCP_FLAGS] = {.type = NL_A_UNSPEC,
- .minLen = 2, .maxLen = 2,
- .optional = TRUE},
- [OVS_KEY_ATTR_DP_HASH] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = TRUE},
- [OVS_KEY_ATTR_RECIRC_ID] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = TRUE},
- [OVS_KEY_ATTR_MPLS] = {.type = NL_A_VAR_LEN, .optional = TRUE}
-};
+ if (keyAttrs[OVS_KEY_ATTR_SCTP]) {
+ const struct ovs_key_sctp *sctpKey;
+ sctpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_SCTP]);
+ ipv4FlowPutKey->l4.tpSrc = sctpKey->sctp_src;
+ ipv4FlowPutKey->l4.tpDst = sctpKey->sctp_dst;
+ }
-/* For Parsing nested OVS_KEY_ATTR_TUNNEL attributes */
-static const NL_POLICY nlFlowTunnelKeyPolicy[] = {
- [OVS_TUNNEL_KEY_ATTR_ID] = {.type = NL_A_UNSPEC, .minLen = 8,
- .maxLen = 8, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = {.type = NL_A_UNSPEC, .minLen = 4,
- .maxLen = 4, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = {.type = NL_A_UNSPEC, .minLen = 4 ,
- .maxLen = 4, .optional = FALSE},
- [OVS_TUNNEL_KEY_ATTR_TOS] = {.type = NL_A_UNSPEC, .minLen = 1,
- .maxLen = 1, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_TTL] = {.type = NL_A_UNSPEC, .minLen = 1,
- .maxLen = 1, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = {.type = NL_A_UNSPEC, .minLen = 0,
- .maxLen = 0, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_CSUM] = {.type = NL_A_UNSPEC, .minLen = 0,
- .maxLen = 0, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_OAM] = {.type = NL_A_UNSPEC, .minLen = 0,
- .maxLen = 0, .optional = TRUE},
- [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = {.type = NL_A_VAR_LEN,
- .optional = TRUE}
-};
+ destKey->l2.keyLen += OVS_IP_KEY_SIZE;
+ }
+ break;
+ }
+ case ETH_TYPE_IPV6: {
+
+ if (keyAttrs[OVS_KEY_ATTR_IPV6]) {
+ const struct ovs_key_ipv6 *ipv6Key;
+
+ ipv6Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_IPV6]);
+ Ipv6Key *ipv6FlowPutKey = &(destKey->ipv6Key);
+
+ RtlCopyMemory(&ipv6FlowPutKey->ipv6Src, ipv6Key->ipv6_src,
+ sizeof ipv6Key->ipv6_src);
+ RtlCopyMemory(&ipv6FlowPutKey->ipv6Dst, ipv6Key->ipv6_dst,
+ sizeof ipv6Key->ipv6_dst);
+
+ ipv6FlowPutKey->ipv6Label = ipv6Key->ipv6_label;
+ ipv6FlowPutKey->nwProto = ipv6Key->ipv6_proto;
+ ipv6FlowPutKey->nwTos = ipv6Key->ipv6_tclass;
+ ipv6FlowPutKey->nwTtl = ipv6Key->ipv6_hlimit;
+ ipv6FlowPutKey->nwFrag = ipv6Key->ipv6_frag;
+
+ if (keyAttrs[OVS_KEY_ATTR_TCP]) {
+ const struct ovs_key_tcp *tcpKey;
+ tcpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_TCP]);
+ ipv6FlowPutKey->l4.tpSrc = tcpKey->tcp_src;
+ ipv6FlowPutKey->l4.tpDst = tcpKey->tcp_dst;
+ }
-/* For Parsing nested OVS_FLOW_ATTR_ACTIONS attributes */
-static const NL_POLICY nlFlowActionPolicy[] = {
- [OVS_ACTION_ATTR_OUTPUT] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT32),
- .maxLen = sizeof(UINT32), .optional = TRUE},
- [OVS_ACTION_ATTR_USERSPACE] = {.type = NL_A_VAR_LEN, .optional = TRUE},
- [OVS_ACTION_ATTR_PUSH_VLAN] = {.type = NL_A_UNSPEC,
- .minLen =
- sizeof(struct ovs_action_push_vlan),
- .maxLen =
- sizeof(struct ovs_action_push_vlan),
- .optional = TRUE},
- [OVS_ACTION_ATTR_POP_VLAN] = {.type = NL_A_UNSPEC, .optional = TRUE},
- [OVS_ACTION_ATTR_PUSH_MPLS] = {.type = NL_A_UNSPEC,
- .minLen =
- sizeof(struct ovs_action_push_mpls),
- .maxLen =
- sizeof(struct ovs_action_push_mpls),
- .optional = TRUE},
- [OVS_ACTION_ATTR_POP_MPLS] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(UINT16),
- .maxLen = sizeof(UINT16),
- .optional = TRUE},
- [OVS_ACTION_ATTR_RECIRC] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(UINT32),
- .maxLen = sizeof(UINT32),
- .optional = TRUE},
- [OVS_ACTION_ATTR_HASH] = {.type = NL_A_UNSPEC,
- .minLen = sizeof(struct ovs_action_hash),
- .maxLen = sizeof(struct ovs_action_hash),
- .optional = TRUE},
- [OVS_ACTION_ATTR_SET] = {.type = NL_A_VAR_LEN, .optional = TRUE},
- [OVS_ACTION_ATTR_SAMPLE] = {.type = NL_A_VAR_LEN, .optional = TRUE}
-};
+ if (keyAttrs[OVS_KEY_ATTR_UDP]) {
+ const struct ovs_key_udp *udpKey;
+ udpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_UDP]);
+ ipv6FlowPutKey->l4.tpSrc = udpKey->udp_src;
+ ipv6FlowPutKey->l4.tpDst = udpKey->udp_dst;
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_SCTP]) {
+ const struct ovs_key_sctp *sctpKey;
+ sctpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_SCTP]);
+ ipv6FlowPutKey->l4.tpSrc = sctpKey->sctp_src;
+ ipv6FlowPutKey->l4.tpDst = sctpKey->sctp_dst;
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_ICMPV6]) {
+ const struct ovs_key_icmpv6 *icmpv6Key;
+
+ Icmp6Key *icmp6FlowPutKey= &(destKey->icmp6Key);
+
+ icmpv6Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ICMPV6]);
+
+ icmp6FlowPutKey->l4.tpSrc = icmpv6Key->icmpv6_type;
+ icmp6FlowPutKey->l4.tpDst = icmpv6Key->icmpv6_code;
+
+ if (keyAttrs[OVS_KEY_ATTR_ND]) {
+ const struct ovs_key_nd *ndKey;
+
+ ndKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ND]);
+ RtlCopyMemory(&icmp6FlowPutKey->ndTarget,
+ ndKey->nd_target, sizeof (icmp6FlowPutKey->ndTarget));
+ RtlCopyMemory(icmp6FlowPutKey->arpSha,
+ ndKey->nd_sll, ETH_ADDR_LEN);
+ RtlCopyMemory(icmp6FlowPutKey->arpTha,
+ ndKey->nd_tll, ETH_ADDR_LEN);
+ }
+
+ destKey->l2.keyLen += OVS_ICMPV6_KEY_SIZE;
+
+ } else {
+
+ destKey->l2.keyLen += OVS_IPV6_KEY_SIZE;
+ }
+
+ ipv6FlowPutKey->pad = 0;
+ }
+ break;
+ }
+ case ETH_TYPE_ARP:
+ case ETH_TYPE_RARP: {
+
+ if (keyAttrs[OVS_KEY_ATTR_ARP]) {
+ ArpKey *arpFlowPutKey = &destKey->arpKey;
+ const struct ovs_key_arp *arpKey;
+
+ arpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ARP]);
+
+ arpFlowPutKey->nwSrc = arpKey->arp_sip;
+ arpFlowPutKey->nwDst = arpKey->arp_tip;
+
+ RtlCopyMemory(arpFlowPutKey->arpSha, arpKey->arp_sha, ETH_ADDR_LEN);
+ RtlCopyMemory(arpFlowPutKey->arpTha, arpKey->arp_tha, ETH_ADDR_LEN);
+ /* Kernel datapath assumes 'arpFlowPutKey->nwProto' to be in host
+ * order. */
+ arpFlowPutKey->nwProto = (UINT8)ntohs((arpKey->arp_op));
+ arpFlowPutKey->pad[0] = 0;
+ arpFlowPutKey->pad[1] = 0;
+ arpFlowPutKey->pad[2] = 0;
+ destKey->l2.keyLen += OVS_ARP_KEY_SIZE;
+ }
+ 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;
+
+ mplsKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_MPLS]);
+
+ mplsFlowPutKey->lse = mplsKey->mpls_lse;
+ mplsFlowPutKey->pad[0] = 0;
+ mplsFlowPutKey->pad[1] = 0;
+ mplsFlowPutKey->pad[2] = 0;
+ mplsFlowPutKey->pad[3] = 0;
+ destKey->l2.keyLen += sizeof(MplsKey);
+ }
+ break;
+ }
+ }
+}
/*
*----------------------------------------------------------------------------
- * Netlink interface for flow commands.
+ * _MapTunAttrToFlowPut --
+ * Converts FLOW_TUNNEL_KEY attribute to OvsFlowKey->tunKey.
*----------------------------------------------------------------------------
*/
-NTSTATUS
-OvsFlowNlNewCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
- UINT32 *replyLen)
+static VOID
+_MapTunAttrToFlowPut(PNL_ATTR *keyAttrs,
+ PNL_ATTR *tunAttrs,
+ OvsFlowKey *destKey)
{
- NTSTATUS rc = STATUS_SUCCESS;
+ if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) {
+
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]) {
+ destKey->tunKey.tunnelId = NlAttrGetU64
+ (tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]);
+ destKey->tunKey.flags |= OVS_TNL_F_KEY;
+ }
- UNREFERENCED_PARAMETER(usrParamsCtx);
- UNREFERENCED_PARAMETER(replyLen);
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_DST]) {
+ destKey->tunKey.dst = NlAttrGetU32
+ (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_DST]);
+ }
- return rc;
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_SRC]) {
+ destKey->tunKey.src = NlAttrGetU32
+ (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_SRC]);
+ }
+
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT]) {
+ destKey->tunKey.flags |= OVS_TNL_F_DONT_FRAGMENT;
+ }
+
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_CSUM]) {
+ destKey->tunKey.flags |= OVS_TNL_F_CSUM;
+ }
+
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_TOS]) {
+ destKey->tunKey.tos = NlAttrGetU8
+ (tunAttrs[OVS_TUNNEL_KEY_ATTR_TOS]);
+ }
+
+ if (tunAttrs[OVS_TUNNEL_KEY_ATTR_TTL]) {
+ destKey->tunKey.ttl = NlAttrGetU8
+ (tunAttrs[OVS_TUNNEL_KEY_ATTR_TTL]);
+ }
+
+ destKey->tunKey.pad = 0;
+ destKey->l2.offset = 0;
+ } else {
+ destKey->tunKey.attr[0] = 0;
+ destKey->tunKey.attr[1] = 0;
+ destKey->tunKey.attr[2] = 0;
+ destKey->l2.offset = sizeof destKey->tunKey;
+ }
}
/*
}
DeleteAllFlows(datapath);
- OvsFreeMemory(datapath->flowTable);
+ OvsFreeMemoryWithTag(datapath->flowTable, OVS_FLOW_POOL_TAG);
datapath->flowTable = NULL;
+
+ if (datapath->lock == NULL) {
+ return NDIS_STATUS_SUCCESS;
+ }
+
NdisFreeRWLock(datapath->lock);
return NDIS_STATUS_SUCCESS;
PLIST_ENTRY bucket;
int i;
- datapath->flowTable = OvsAllocateMemory(OVS_FLOW_TABLE_SIZE *
- sizeof (LIST_ENTRY));
+ datapath->flowTable = OvsAllocateMemoryWithTag(
+ OVS_FLOW_TABLE_SIZE * sizeof(LIST_ENTRY), OVS_FLOW_POOL_TAG);
if (!datapath->flowTable) {
return NDIS_STATUS_RESOURCES;
}
}
datapath->lock = NdisAllocateRWLock(switchContext->NdisFilterHandle);
+ if (!datapath->lock) {
+ return NDIS_STATUS_RESOURCES;
+ }
+
return NDIS_STATUS_SUCCESS;
}
* - packet->l4 to just past the IPv4 header, if one is present and has a
* correct length, and otherwise NULL.
*
- * - packet->l7 to just past the TCP or UDP or ICMP header, if one is
+ * - packet->l7 to just past the TCP, UDP, SCTP or ICMP header, if one is
* present and has a correct length, and otherwise NULL.
*
* Returns NDIS_STATUS_SUCCESS normally. Fails only if packet data cannot be accessed
flow->l2.vlanTci = 0;
}
/*
- * XXX
- * Please note after this point, src mac and dst mac should
- * not be accessed through eth
- */
+ * XXX
+ * Please note after this point, src mac and dst mac should
+ * not be accessed through eth
+ */
eth = (Eth_Header *)((UINT8 *)eth + offset);
}
ipKey->nwTos = nh->tos;
if (nh->frag_off & htons(IP_MF | IP_OFFSET)) {
- ipKey->nwFrag = OVSWIN_NW_FRAG_ANY;
+ ipKey->nwFrag = OVS_FRAG_TYPE_FIRST;
if (nh->frag_off & htons(IP_OFFSET)) {
- ipKey->nwFrag |= OVSWIN_NW_FRAG_LATER;
+ ipKey->nwFrag = OVS_FRAG_TYPE_LATER;
}
} else {
- ipKey->nwFrag = 0;
+ ipKey->nwFrag = OVS_FRAG_TYPE_NONE;
}
ipKey->nwTtl = nh->ttl;
OvsParseTcp(packet, &ipKey->l4, layers);
} else if (ipKey->nwProto == SOCKET_IPPROTO_UDP) {
OvsParseUdp(packet, &ipKey->l4, layers);
+ } else if (ipKey->nwProto == SOCKET_IPPROTO_SCTP) {
+ OvsParseSctp(packet, &ipKey->l4, layers);
} else if (ipKey->nwProto == SOCKET_IPPROTO_ICMP) {
ICMPHdr icmpStorage;
const ICMPHdr *icmp;
OvsParseTcp(packet, &(flow->ipv6Key.l4), layers);
} else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_UDP) {
OvsParseUdp(packet, &(flow->ipv6Key.l4), layers);
+ } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_SCTP) {
+ OvsParseSctp(packet, &flow->ipv6Key.l4, layers);
} else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_ICMPV6) {
OvsParseIcmpV6(packet, flow, layers);
flow->l2.keyLen += (OVS_ICMPV6_KEY_SIZE - OVS_IPV6_KEY_SIZE);
memcpy(arpKey->arpTha, arp->arp_tha, ETH_ADDR_LENGTH);
}
}
+ } else if (OvsEthertypeIsMpls(flow->l2.dlType)) {
+ MPLSHdr mplsStorage;
+ const MPLSHdr *mpls;
+ MplsKey *mplsKey = &flow->mplsKey;
+ ((UINT64 *)mplsKey)[0] = 0;
+ flow->l2.keyLen += OVS_MPLS_KEY_SIZE;
+
+ /*
+ * 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) {
+ break;
+ }
+
+ /* Keep only the topmost MPLS label stack entry. */
+ if (i == 0) {
+ mplsKey->lse = mpls->lse;
+ }
+
+ layers->l3Offset += MPLS_HLEN;
+ layers->l4Offset += MPLS_HLEN;
+
+ if (mpls->lse & htonl(MPLS_BOS_MASK)) {
+ /*
+ * Bottom of Stack bit is set, which means there are no
+ * remaining MPLS labels in the packet.
+ */
+ break;
+ }
+ }
}
return NDIS_STATUS_SUCCESS;
{
OvsFlow *f = *flow;
*flow = NULL;
- UNREFERENCED_PARAMETER(datapath);
ASSERT(datapath->nFlows);
datapath->nFlows--;
FreeFlow(OvsFlow *flow)
{
ASSERT(flow);
- OvsFreeMemory(flow);
+ OvsFreeMemoryWithTag(flow, OVS_FLOW_POOL_TAG);
}
NTSTATUS
BOOLEAN findNextNonEmpty = FALSE;
dpNo = dumpInput->dpNo;
- NdisAcquireSpinLock(gOvsCtrlLock);
- if (gOvsSwitchContext == NULL ||
- gOvsSwitchContext->dpNo != dpNo) {
+ if (gOvsSwitchContext->dpNo != dpNo) {
status = STATUS_INVALID_PARAMETER;
- goto unlock;
+ goto exit;
}
rowIndex = dumpInput->position[0];
if (rowIndex >= OVS_FLOW_TABLE_SIZE) {
dumpOutput->n = 0;
*replyLen = sizeof(*dumpOutput);
- goto unlock;
+ goto exit;
}
columnIndex = dumpInput->position[1];
ASSERT(rowIndex < OVS_FLOW_TABLE_SIZE);
flow = CONTAINING_RECORD(node, OvsFlow, ListEntry);
- status = ReportFlowInfo(flow, dumpInput->getFlags, dumpInput->actionsLen,
- &dumpOutput->flow);
+ status = ReportFlowInfo(flow, dumpInput->getFlags, &dumpOutput->flow);
if (status == STATUS_BUFFER_TOO_SMALL) {
dumpOutput->n = sizeof(OvsFlowDumpOutput) + flow->actionsLen;
dp_unlock:
OvsReleaseDatapath(datapath, &dpLockState);
-unlock:
- NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
return status;
}
-NTSTATUS
-OvsDumpFlowIoctl(PVOID inputBuffer,
- UINT32 inputLength,
- PVOID outputBuffer,
- UINT32 outputLength,
- UINT32 *replyLen)
-{
- OvsFlowDumpOutput *dumpOutput = (OvsFlowDumpOutput *)outputBuffer;
- OvsFlowDumpInput *dumpInput = (OvsFlowDumpInput *)inputBuffer;
-
- if (inputBuffer == NULL || outputBuffer == NULL) {
- return STATUS_INVALID_PARAMETER;
- }
-
- if ((inputLength != sizeof(OvsFlowDumpInput))
- || (outputLength != sizeof *dumpOutput + dumpInput->actionsLen)) {
- return STATUS_INFO_LENGTH_MISMATCH;
- }
-
- return OvsDoDumpFlows(dumpInput, dumpOutput, replyLen);
-}
-
static NTSTATUS
ReportFlowInfo(OvsFlow *flow,
UINT32 getFlags,
- UINT32 getActionsLen,
OvsFlowInfo *info)
{
NTSTATUS status = STATUS_SUCCESS;
if (getFlags & FLOW_GET_ACTIONS) {
if (flow->actionsLen == 0) {
info->actionsLen = 0;
- } else if (flow->actionsLen > getActionsLen) {
- info->actionsLen = 0;
- status = STATUS_BUFFER_TOO_SMALL;
} else {
- RtlCopyMemory(info->actions, flow->actions, flow->actionsLen);
+ info->actions = flow->actions;
info->actionsLen = flow->actionsLen;
}
}
NTSTATUS
OvsPutFlowIoctl(PVOID inputBuffer,
UINT32 inputLength,
- PVOID outputBuffer,
- UINT32 outputLength,
- UINT32 *replyLen)
+ struct OvsFlowStats *stats)
{
NTSTATUS status = STATUS_SUCCESS;
OVS_DATAPATH *datapath = NULL;
- struct OvsFlowStats stats;
ULONG actionsLen;
OvsFlowPut *put;
UINT32 dpNo;
return STATUS_INFO_LENGTH_MISMATCH;
}
- if ((outputLength != sizeof(stats)) || (outputBuffer == NULL)) {
- return STATUS_INFO_LENGTH_MISMATCH;
- }
-
put = (OvsFlowPut *)inputBuffer;
if (put->actionsLen > 0) {
actionsLen = put->actionsLen;
} else {
actionsLen = 0;
}
- if (inputLength != actionsLen + sizeof(*put)) {
- return STATUS_INFO_LENGTH_MISMATCH;
- }
dpNo = put->dpNo;
- NdisAcquireSpinLock(gOvsCtrlLock);
- if (gOvsSwitchContext == NULL ||
- gOvsSwitchContext->dpNo != dpNo) {
+ if (gOvsSwitchContext->dpNo != dpNo) {
status = STATUS_INVALID_PARAMETER;
- goto unlock;
+ goto exit;
}
datapath = &gOvsSwitchContext->datapath;
ASSERT(datapath);
- RtlZeroMemory(&stats, sizeof(stats));
OvsAcquireDatapathWrite(datapath, &dpLockState, FALSE);
- status = HandleFlowPut(put, datapath, &stats);
+ status = HandleFlowPut(put, datapath, stats);
OvsReleaseDatapath(datapath, &dpLockState);
- if (status == STATUS_SUCCESS) {
- // Copy stats to User mode app
- NdisMoveMemory(outputBuffer, (PVOID)&stats, sizeof(stats));
- *replyLen = sizeof stats;
- }
-
-unlock:
- NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
return status;
}
status = OvsPrepareFlow(&KernelFlow, put, hash);
if (status != STATUS_SUCCESS) {
- FreeFlow(KernelFlow);
return STATUS_UNSUCCESSFUL;
}
RemoveFlow(datapath, &KernelFlow);
}
} else {
- return STATUS_UNSUCCESSFUL;
+ /* Return success if an identical flow already exists. */
+ /* XXX: should we return EEXIST in a netlink error? */
+ return STATUS_SUCCESS;
}
}
}
do {
*flow = localFlow =
- OvsAllocateMemory(sizeof(OvsFlow) + put->actionsLen);
+ OvsAllocateMemoryWithTag(sizeof(OvsFlow) + put->actionsLen,
+ OVS_FLOW_POOL_TAG);
if (localFlow == NULL) {
status = STATUS_NO_MEMORY;
break;
NTSTATUS
OvsGetFlowIoctl(PVOID inputBuffer,
- UINT32 inputLength,
- PVOID outputBuffer,
- UINT32 outputLength,
- UINT32 *replyLen)
+ PVOID outputBuffer)
{
NTSTATUS status = STATUS_SUCCESS;
OVS_DATAPATH *datapath = NULL;
UINT32 dpNo;
LOCK_STATE_EX dpLockState;
- if (inputLength != sizeof(OvsFlowGetInput)
- || inputBuffer == NULL) {
- return STATUS_INFO_LENGTH_MISMATCH;
- }
-
getInput = (OvsFlowGetInput *) inputBuffer;
getFlags = getInput->getFlags;
getActionsLen = getInput->actionsLen;
- if (getInput->getFlags & FLOW_GET_KEY) {
- return STATUS_INVALID_PARAMETER;
- }
- if (outputBuffer == NULL
- || outputLength != (sizeof *getOutput +
- getInput->actionsLen)) {
+ if (outputBuffer == NULL) {
return STATUS_INFO_LENGTH_MISMATCH;
}
dpNo = getInput->dpNo;
- NdisAcquireSpinLock(gOvsCtrlLock);
- if (gOvsSwitchContext == NULL ||
- gOvsSwitchContext->dpNo != dpNo) {
+ if (gOvsSwitchContext->dpNo != dpNo) {
status = STATUS_INVALID_PARAMETER;
- goto unlock;
+ goto exit;
}
datapath = &gOvsSwitchContext->datapath;
goto dp_unlock;
}
- // XXX: can be optimized to return only how much is written out
- *replyLen = outputLength;
getOutput = (OvsFlowGetOutput *)outputBuffer;
- ReportFlowInfo(flow, getFlags, getActionsLen, &getOutput->info);
+ ReportFlowInfo(flow, getFlags, &getOutput->info);
dp_unlock:
OvsReleaseDatapath(datapath, &dpLockState);
-unlock:
- NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
return status;
}
NTSTATUS
-OvsFlushFlowIoctl(PVOID inputBuffer,
- UINT32 inputLength)
+OvsFlushFlowIoctl(UINT32 dpNo)
{
NTSTATUS status = STATUS_SUCCESS;
OVS_DATAPATH *datapath = NULL;
- UINT32 dpNo;
LOCK_STATE_EX dpLockState;
- if (inputLength != sizeof(UINT32) || inputBuffer == NULL) {
- return STATUS_INFO_LENGTH_MISMATCH;
- }
-
- dpNo = *(UINT32 *)inputBuffer;
- NdisAcquireSpinLock(gOvsCtrlLock);
- if (gOvsSwitchContext == NULL ||
- gOvsSwitchContext->dpNo != dpNo) {
+ if (gOvsSwitchContext->dpNo != dpNo) {
status = STATUS_INVALID_PARAMETER;
- goto unlock;
+ goto exit;
}
datapath = &gOvsSwitchContext->datapath;
DeleteAllFlows(datapath);
OvsReleaseDatapath(datapath, &dpLockState);
-unlock:
- NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
+ return status;
+}
+
+UINT32
+OvsFlowKeyAttrSize(void)
+{
+ return NlAttrTotalSize(4) /* OVS_KEY_ATTR_PRIORITY */
+ + NlAttrTotalSize(0) /* OVS_KEY_ATTR_TUNNEL */
+ + OvsTunKeyAttrSize()
+ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_IN_PORT */
+ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_SKB_MARK */
+ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_DP_HASH */
+ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_RECIRC_ID */
+ + NlAttrTotalSize(12) /* OVS_KEY_ATTR_ETHERNET */
+ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_VLAN */
+ + NlAttrTotalSize(0) /* OVS_KEY_ATTR_ENCAP */
+ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + NlAttrTotalSize(40) /* OVS_KEY_ATTR_IPV6 */
+ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ICMPV6 */
+ + NlAttrTotalSize(28); /* OVS_KEY_ATTR_ND */
+}
+
+UINT32
+OvsTunKeyAttrSize(void)
+{
+ /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider
+ * updating this function.
+ */
+ return NlAttrTotalSize(8) /* OVS_TUNNEL_KEY_ATTR_ID */
+ + NlAttrTotalSize(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+ + NlAttrTotalSize(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+ + NlAttrTotalSize(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
+ + NlAttrTotalSize(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
+ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
+ + NlAttrTotalSize(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
+ + NlAttrTotalSize(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
+ + NlAttrTotalSize(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsProbeSupportedFeature --
+ * Verifies if the probed feature is supported.
+ *
+ * Results:
+ * STATUS_SUCCESS if the probed feature is supported.
+ *----------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsProbeSupportedFeature(POVS_MESSAGE msgIn,
+ PNL_ATTR keyAttr)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
+
+ UINT32 keyAttrOffset = (UINT32)((PCHAR)keyAttr - (PCHAR)nlMsgHdr);
+ PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = { NULL };
+
+ /* Get flow keys attributes */
+ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, NlAttrLen(keyAttr),
+ nlFlowKeyPolicy, ARRAY_SIZE(nlFlowKeyPolicy),
+ keyAttrs, ARRAY_SIZE(keyAttrs)))
+ != TRUE) {
+ OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p",
+ nlMsgHdr);
+ status = STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (keyAttrs[OVS_KEY_ATTR_MPLS] &&
+ keyAttrs[OVS_KEY_ATTR_ETHERTYPE]) {
+ ovs_be16 ethType = NlAttrGetU16(keyAttrs[OVS_KEY_ATTR_ETHERTYPE]);
+
+ if (OvsEthertypeIsMpls(ethType)) {
+ if (!OvsCountMplsLabels(keyAttrs[OVS_KEY_ATTR_MPLS])) {
+ OVS_LOG_ERROR("Maximum supported MPLS labels exceeded.");
+ status = STATUS_INVALID_MESSAGE;
+ }
+ } else {
+ OVS_LOG_ERROR("Wrong ethertype for MPLS attribute.");
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ OVS_LOG_ERROR("Probed feature not supported.");
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+done:
return status;
}