Solved access violation when trying to acces netling message - obtained with forged IOCTLs
Signed-off-by: Paul-Daniel Boca <pb...@cloudbasesolutions.com> --- datapath-windows/ovsext/Datapath.c | 45 +++++++++++++++++--- datapath-windows/ovsext/Flow.c | 42 ++++++++++--------- datapath-windows/ovsext/Netlink/Netlink.c | 68 ++++++++++++++++++++++++++----- datapath-windows/ovsext/Netlink/Netlink.h | 15 +++++-- datapath-windows/ovsext/User.c | 5 ++- datapath-windows/ovsext/Vport.c | 34 ++++++++-------- lib/netlink-socket.c | 2 + 7 files changed, 154 insertions(+), 57 deletions(-) diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c index 0a25af0..6f97693 100644 --- a/datapath-windows/ovsext/Datapath.c +++ b/datapath-windows/ovsext/Datapath.c @@ -307,6 +307,7 @@ static NTSTATUS MapIrpOutputBuffer(PIRP irp, static NTSTATUS ValidateNetlinkCmd(UINT32 devOp, POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg, + UINT32 ovsMgsLength, NETLINK_FAMILY *nlFamilyOps); static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, NETLINK_FAMILY *nlFamilyOps, @@ -693,6 +694,7 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, UINT32 devOp; OVS_MESSAGE ovsMsgReadOp; POVS_MESSAGE ovsMsg; + UINT32 ovsMsgLength = 0; NETLINK_FAMILY *nlFamilyOps; OVS_USER_PARAMS_CONTEXT usrParamsCtx; @@ -772,6 +774,7 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, } ovsMsg = inputBuffer; + ovsMsgLength = inputBufferLen; devOp = OVS_TRANSACTION_DEV_OP; break; @@ -806,6 +809,7 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, OVS_CTRL_CMD_EVENT_NOTIFY : OVS_CTRL_CMD_READ_NOTIFY; ovsMsg->genlMsg.version = nlControlFamilyOps.version; + ovsMsgLength = outputBufferLen; devOp = OVS_READ_DEV_OP; break; @@ -851,6 +855,7 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, /* Create an NL message for consumption. */ ovsMsg = &ovsMsgReadOp; + ovsMsgLength = sizeof (ovsMsgReadOp); devOp = OVS_READ_DEV_OP; break; @@ -862,7 +867,21 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, goto done; } + /* + * Output buffer not mandatory but map it in case we have something + * to return to requester. + */ + if (outputBufferLen != 0) { + status = MapIrpOutputBuffer(irp, outputBufferLen, + sizeof *ovsMsg, &outputBuffer); + if (status != STATUS_SUCCESS) { + goto done; + } + ASSERT(outputBuffer); + } + ovsMsg = inputBuffer; + ovsMsgLength = inputBufferLen; devOp = OVS_WRITE_DEV_OP; break; @@ -901,7 +920,8 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, * "artificial" or was copied from a previously validated 'ovsMsg'. */ if (devOp != OVS_READ_DEV_OP) { - status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps); + status = ValidateNetlinkCmd(devOp, instance, ovsMsg, + ovsMsgLength, nlFamilyOps); if (status != STATUS_SUCCESS) { goto done; } @@ -936,11 +956,18 @@ static NTSTATUS ValidateNetlinkCmd(UINT32 devOp, POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg, + UINT32 ovsMsgLength, NETLINK_FAMILY *nlFamilyOps) { NTSTATUS status = STATUS_INVALID_PARAMETER; UINT16 i; + // We need to ensure we have enough data to process + if (NlMsgSize(&ovsMsg->nlMsg) > ovsMsgLength) { + status = STATUS_INVALID_PARAMETER; + goto done; + } + for (i = 0; i < nlFamilyOps->opsCount; i++) { if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) { /* Validate if the command is valid for the device operation. */ @@ -975,6 +1002,14 @@ ValidateNetlinkCmd(UINT32 devOp, } } + // validate all NlAttrs + if (!NlValidateAllAttrs(&ovsMsg->nlMsg, sizeof(*ovsMsg), + NlMsgAttrsLen((PNL_MSG_HDR)ovsMsg), + NULL, 0)) { + status = STATUS_INVALID_PARAMETER; + goto done; + } + done: return status; } @@ -1026,6 +1061,7 @@ InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, POVS_MESSAGE msgIn = NULL; POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_CTRL_CMD_EVENT_NOTIFY || usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_CTRL_CMD_READ_NOTIFY) { @@ -1041,8 +1077,7 @@ InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, ASSERT(msgIn); ASSERT(msgError); - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } if (*replyLen != 0) { @@ -1414,9 +1449,9 @@ cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return STATUS_SUCCESS; diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c index a7e9bd2..2a3bf73 100644 --- a/datapath-windows/ovsext/Flow.c +++ b/datapath-windows/ovsext/Flow.c @@ -285,20 +285,10 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, 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. */ + /* FLOW_DEL command w/o any key input is a flush case. + If we don't have any attr, we treat this as a flush command*/ if ((genlMsgHdr->cmd == OVS_FLOW_CMD_DEL) && - (!(flowAttrs[OVS_FLOW_ATTR_KEY]))) { + (!NlMsgAttrsLen(nlMsgHdr))) { rc = OvsFlushFlowIoctl(ovsHdr->dp_ifindex); @@ -323,6 +313,17 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, 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; + } + if (flowAttrs[OVS_FLOW_ATTR_PROBE]) { rc = OvsProbeSupportedFeature(msgIn, flowAttrs[OVS_FLOW_ATTR_KEY]); if (rc != STATUS_SUCCESS) { @@ -399,8 +400,9 @@ done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + UINT32 msgErrorLen = usrParamsCtx->outputLength; + + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); rc = STATUS_SUCCESS; } @@ -568,8 +570,9 @@ done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + UINT32 msgErrorLen = usrParamsCtx->outputLength; + + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); rc = STATUS_SUCCESS; } @@ -706,8 +709,9 @@ done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + UINT32 msgErrorLen = usrParamsCtx->outputLength; + + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); rc = STATUS_SUCCESS; } diff --git a/datapath-windows/ovsext/Netlink/Netlink.c b/datapath-windows/ovsext/Netlink/Netlink.c index e2312da..b5a97b9 100644 --- a/datapath-windows/ovsext/Netlink/Netlink.c +++ b/datapath-windows/ovsext/Netlink/Netlink.c @@ -108,12 +108,18 @@ NlFillNlHdr(PNL_BUFFER nlBuf, UINT16 nlmsgType, * --------------------------------------------------------------------------- */ VOID -NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, UINT errorCode) +NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, + UINT32 msgErrorLen, + UINT errorCode, UINT32 *msgLen) { NL_BUFFER nlBuffer; ASSERT(errorCode != NL_ERROR_PENDING); + if ((msgError == NULL) || (msgErrorLen < sizeof *msgError)) { + return; + } + NlBufInit(&nlBuffer, (PCHAR)msgError, sizeof *msgError); NlFillNlHdr(&nlBuffer, NLMSG_ERROR, 0, msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid); @@ -121,6 +127,10 @@ NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, UINT errorCode) msgError->errorMsg.error = errorCode; msgError->errorMsg.nlMsg = msgIn->nlMsg; msgError->nlMsg.nlmsgLen = sizeof(OVS_MESSAGE_ERROR); + + if (NULL != msgLen) { + *msgLen = msgError->nlMsg.nlmsgLen; + } } /* @@ -1006,7 +1016,7 @@ NlAttrFind__(const PNL_ATTR attrs, UINT32 size, UINT16 type) { PNL_ATTR iter = NULL; PNL_ATTR ret = NULL; - UINT32 left; + INT left; NL_ATTR_FOR_EACH (iter, left, attrs, size) { if (NlAttrType(iter) == type) { @@ -1036,6 +1046,49 @@ NlAttrFindNested(const PNL_ATTR nla, UINT16 type) /* *---------------------------------------------------------------------------- + * Traverses all attributes in received buffer in order to insure all are valid + *---------------------------------------------------------------------------- + */ +BOOLEAN NlValidateAllAttrs(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, + UINT32 totalAttrLen, + const NL_POLICY policy[], const UINT32 numPolicy) +{ + PNL_ATTR nla; + INT left; + BOOLEAN ret = TRUE; + + if ((NlMsgSize(nlMsg) < attrOffset)) { + OVS_LOG_WARN("No attributes in nlMsg: %p at offset: %d", + nlMsg, attrOffset); + ret = FALSE; + goto done; + } + + NL_ATTR_FOR_EACH_UNSAFE(nla, left, NlMsgAt(nlMsg, attrOffset), + totalAttrLen) + { + if (!NlAttrIsValid(nla, left)) { + ret = FALSE; + goto done; + } + + UINT16 type = NlAttrType(nla); + if (type < numPolicy && policy[type].type != NL_A_NO_ATTR) { + /* Typecasting to keep the compiler happy */ + const PNL_POLICY e = (const PNL_POLICY)(&policy[type]); + if (!NlAttrValidate(nla, e)) { + ret = FALSE; + goto done; + } + } + } + +done: + return ret; +} + +/* + *---------------------------------------------------------------------------- * Parses the netlink message at a given offset (attrOffset) * as a series of attributes. A pointer to the attribute with type * 'type' is stored in attrs at index 'type'. policy is used to define the @@ -1052,20 +1105,13 @@ NlAttrParse(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, PNL_ATTR attrs[], UINT32 numAttrs) { PNL_ATTR nla; - UINT32 left; + INT left; UINT32 iter; BOOLEAN ret = FALSE; UINT32 numPolicyAttr = MIN(numPolicy, numAttrs); RtlZeroMemory(attrs, numAttrs * sizeof *attrs); - - /* There is nothing to parse */ - if (!(NlMsgAttrsLen(nlMsg))) { - ret = TRUE; - goto done; - } - if ((NlMsgSize(nlMsg) < attrOffset)) { OVS_LOG_WARN("No attributes in nlMsg: %p at offset: %d", nlMsg, attrOffset); @@ -1100,7 +1146,7 @@ NlAttrParse(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, for (iter = 0; iter < numPolicyAttr; iter++) { const PNL_POLICY e = (const PNL_POLICY)(&policy[iter]); if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[iter]) { - OVS_LOG_ERROR("Required attr:%d missing", iter); + OVS_LOG_WARN("Required attr:%d missing", iter); goto done; } } diff --git a/datapath-windows/ovsext/Netlink/Netlink.h b/datapath-windows/ovsext/Netlink/Netlink.h index 8f6a5be..a37ea57 100644 --- a/datapath-windows/ovsext/Netlink/Netlink.h +++ b/datapath-windows/ovsext/Netlink/Netlink.h @@ -72,7 +72,8 @@ typedef struct _NL_POLICY /* This macro is careful to check for attributes with bad lengths. */ #define NL_ATTR_FOR_EACH(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ - NlAttrIsValid(ITER, LEFT); \ + ((INT)LEFT) >= (INT)NLA_ALIGN(sizeof(NL_ATTR)) && \ + NlAttrIsValid(ITER, LEFT); \ (LEFT) -= NlAttrLenPad(ITER, LEFT), (ITER) = NlAttrNext(ITER)) /* This macro does not check for attributes with bad lengths. It should only @@ -80,7 +81,7 @@ typedef struct _NL_POLICY * already been validated (e.g. with NL_ATTR_FOR_EACH). */ #define NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ - (LEFT) > 0; \ + ((INT)LEFT) >= (INT)NLA_ALIGN(sizeof(NL_ATTR)); \ (LEFT) -= NLA_ALIGN((ITER)->nlaLen), (ITER) = NlAttrNext(ITER)) #define NL_ATTR_GET_AS(NLA, TYPE) \ @@ -94,8 +95,9 @@ BOOLEAN NlFillNlHdr(PNL_BUFFER nlBuf, UINT16 nlmsgType, UINT16 nlmsgFlags, UINT32 nlmsgSeq, UINT32 nlmsgPid); -VOID NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, - UINT errorCode); +VOID NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, + UINT32 msgErrorLen, + UINT errorCode, UINT32 *msgLen); /* Netlink message accessing the payload */ PVOID NlMsgAt(const PNL_MSG_HDR nlh, UINT32 offset); @@ -187,6 +189,11 @@ static __inline NlAttrIsLast(const PNL_ATTR nla, int rem) /* Netlink attribute validation */ BOOLEAN NlAttrValidate(const PNL_ATTR, const PNL_POLICY); +/* Netlink attribute stream validation */ +BOOLEAN NlValidateAllAttrs(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, + UINT32 totalAttrLen, + const NL_POLICY policy[], const UINT32 numPolicy); + /* Put APis */ BOOLEAN NlMsgPutNlHdr(PNL_BUFFER buf, PNL_MSG_HDR nlMsg); BOOLEAN NlMsgPutGenlHdr(PNL_BUFFER buf, PGENL_MSG_HDR genlMsg); diff --git a/datapath-windows/ovsext/User.c b/datapath-windows/ovsext/User.c index 6b2d94a..909e945 100644 --- a/datapath-windows/ovsext/User.c +++ b/datapath-windows/ovsext/User.c @@ -355,8 +355,9 @@ OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + UINT32 msgErrorLen = usrParamsCtx->outputLength; + + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); status = STATUS_SUCCESS; goto done; } diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c index 882b41f..2825a22 100644 --- a/datapath-windows/ovsext/Vport.c +++ b/datapath-windows/ovsext/Vport.c @@ -1729,9 +1729,9 @@ cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return STATUS_SUCCESS; @@ -2088,9 +2088,9 @@ Cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return STATUS_SUCCESS; @@ -2324,6 +2324,7 @@ Cleanup: if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; if (vport && vportAllocated == TRUE) { if (vportInitialized == TRUE) { @@ -2343,8 +2344,7 @@ Cleanup: OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG); } - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS; @@ -2452,9 +2452,9 @@ Cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return STATUS_SUCCESS; @@ -2544,9 +2544,9 @@ Cleanup: if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; + UINT32 msgErrorLen = usrParamsCtx->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS; @@ -2579,10 +2579,11 @@ OvsTunnelVportPendingRemove(PVOID context, *replyLen = msgOut->nlMsg.nlmsgLen; } else { - POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut; + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) + tunnelContext->outputBuffer; + UINT32 msgErrorLen = tunnelContext->outputLength; - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } } @@ -2721,12 +2722,13 @@ OvsTunnelVportPendingInit(PVOID context, } while (error); if (error) { - POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut; + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) + tunnelContext->outputBuffer; + UINT32 msgErrorLen = tunnelContext->outputLength; OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL); OvsFreeMemory(vport); - NlBuildErrorMsg(msgIn, msgError, nlError); - *replyLen = msgError->nlMsg.nlmsgLen; + NlBuildErrorMsg(msgIn, msgError, msgErrorLen, nlError, replyLen); } } diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c index cad9490..32b0cc3 100644 --- a/lib/netlink-socket.c +++ b/lib/netlink-socket.c @@ -127,6 +127,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp) sock = xmalloc(sizeof *sock); #ifdef _WIN32 + sock->overlapped.hEvent = NULL; sock->handle = CreateFile(OVS_DEVICE_NAME_USER, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -1191,6 +1192,7 @@ pend_io_request(struct nl_sock *sock) ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header); ovs_header->dp_ifindex = 0; + nlmsg->nlmsg_len = request.size; if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE, request.data, request.size, -- 2.7.2.windows.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev