+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;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+
+ if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
+ status = _FlowNlGetCmdHandler(usrParamsCtx, replyLen);
+
+ /* No trasanctional errors as of now.
+ * If we have something then we need to convert rc to
+ * nlError. */
+ if ((nlError != NL_ERROR_SUCCESS) &&
+ (usrParamsCtx->outputBuffer)) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ status = STATUS_SUCCESS;
+ goto done;
+ }
+ } else {
+ status = _FlowNlDumpCmdHandler(usrParamsCtx, replyLen);
+ }
+
+done:
+ 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;
+
+ 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, 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, 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,
+ 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.");
+ 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:
+ 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 */
+
+ POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)
+ (usrParamsCtx->ovsInstance);
+
+ if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
+ /* Dump Start */
+ OvsSetupDumpStart(usrParamsCtx);
+ goto done;
+ }
+
+ POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
+ 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);
+ 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:
+ 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;
+ }
+
+ 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;
+ }
+