Message ID | 20220712020254.79404-1-svc.ovs-community@vmware.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [ovs-dev,v1,1/1] datapath-windows: support meter action initial version | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
ovsrobot/intel-ovs-compilation | success | test: success |
Again patch looks good I only have one question. Please see bellow. > This patch implemented meter action, currently, meter only support drop > method and only support one band. The overall implementation is, when a > packet comes in, it will first lookup meter according to the meter id, > then get the band->rates and delta time since last access the same meter > from the meter struct. Add the multiply result(band->rates * delta_time) > to bucket, finally bucket minus the packet size, if the result larger > than zero, allow the packet go through, otherwise deny the packet go > through. > > Test case: > 1. Setting the size meter size 3M, then the bandwidth was limit > around 3M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\ > band=type=drop,rate=3000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip \ > actions=meter:2,normal" -O OpenFlow13 > 2. Setting the meter size 8M, then the bandwidth was limit > around 8M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\ > kbps,band=type=drop,rate=8000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip\ > actions=meter:2,normal" -O OpenFlow13 > > Signed-off-by: ldejing <ldejing@vmware.com> > --- > datapath-windows/automake.mk | 2 + > datapath-windows/include/OvsDpInterfaceExt.h | 3 + > datapath-windows/ovsext/Actions.c | 10 + > datapath-windows/ovsext/Datapath.c | 46 +- > datapath-windows/ovsext/Meter.c | 538 +++++++++++++++++++ > datapath-windows/ovsext/Meter.h | 68 +++ > datapath-windows/ovsext/Switch.c | 8 + > datapath-windows/ovsext/Util.h | 1 + > datapath-windows/ovsext/ovsext.vcxproj | 2 + > lib/netlink-socket.c | 5 + > 10 files changed, 682 insertions(+), 1 deletion(-) > create mode 100644 datapath-windows/ovsext/Meter.c > create mode 100644 datapath-windows/ovsext/Meter.h > > diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk > index 60b3d6033..2d9f9a40d 100644 > --- a/datapath-windows/automake.mk > +++ b/datapath-windows/automake.mk > @@ -42,6 +42,8 @@ EXTRA_DIST += \ > datapath-windows/ovsext/Jhash.c \ > datapath-windows/ovsext/Jhash.h \ > datapath-windows/ovsext/Mpls.h \ > + datapath-windows/ovsext/Meter.c \ > + datapath-windows/ovsext/Meter.h \ > datapath-windows/ovsext/NetProto.h \ > datapath-windows/ovsext/Netlink/Netlink.c \ > datapath-windows/ovsext/Netlink/Netlink.h \ > diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h > index 5fd8000d1..045e4cbd6 100644 > --- a/datapath-windows/include/OvsDpInterfaceExt.h > +++ b/datapath-windows/include/OvsDpInterfaceExt.h > @@ -74,6 +74,9 @@ > #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) > #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) > > +/* Meter Family */ > +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) > + > #define OVS_WIN_NL_INVALID_MCGRP_ID 0 > #define OVS_WIN_NL_MCGRP_START_ID 100 > #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) > diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c > index 20de4db4c..63ee574c5 100644 > --- a/datapath-windows/ovsext/Actions.c > +++ b/datapath-windows/ovsext/Actions.c > @@ -23,6 +23,7 @@ > #include "Flow.h" > #include "Gre.h" > #include "Jhash.h" > +#include "Meter.h" > #include "Mpls.h" > #include "NetProto.h" > #include "Offload.h" > @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, > } > break; > } > + case OVS_ACTION_ATTR_METER: { > + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { > + OVS_LOG_INFO("Drop packet"); > + dropReason = L"Ovs-meter exceed max rate"; > + goto dropit; > + } > + > + break; > + } > case OVS_ACTION_ATTR_SAMPLE: > { > if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL > diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c > index fa994840a..37db5bd17 100644 > --- a/datapath-windows/ovsext/Datapath.c > +++ b/datapath-windows/ovsext/Datapath.c > @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, > OvsReadPacketCmdHandler, > OvsCtDeleteCmdHandler, > OvsCtDumpCmdHandler, > - OvsCtLimitHandler; > + OvsCtLimitHandler, > + OvsMeterFeatureProbe, > + OvsNewMeterCmdHandler, > + OvsMeterDestroy, > + OvsMeterGet; > > static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > UINT32 *replyLen); > @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { > .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) > }; > > +/* Netlink Meter family */ > +NETLINK_CMD nlMeterFamilyCmdOps[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .handler = OvsMeterFeatureProbe, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_SET, > + .handler = OvsNewMeterCmdHandler, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_GET, > + .handler = OvsMeterGet, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .handler = OvsMeterDestroy, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > +}; > + > +NETLINK_FAMILY nlMeterFamilyOps = { > + .name = OVS_METER_FAMILY, > + .id = OVS_WIN_NL_METER_FAMILY_ID, > + .version = OVS_METER_VERSION, > + .maxAttr = __OVS_METER_ATTR_MAX, > + .cmds = nlMeterFamilyCmdOps, > + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) > +}; > + > /* Netlink netdev family. */ > NETLINK_CMD nlNetdevFamilyCmdOps[] = { > { .cmd = OVS_WIN_NETDEV_CMD_GET, > @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, > case NFNL_TYPE_CT_DEL: > nlFamilyOps = &nlCtFamilyOps; > break; > + case OVS_WIN_NL_METER_FAMILY_ID: > + nlFamilyOps = &nlMeterFamilyOps; > + break; > case OVS_WIN_NL_CTRL_FAMILY_ID: > nlFamilyOps = &nlControlFamilyOps; > break; > diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c > new file mode 100644 > index 000000000..62f5f36c9 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.c > @@ -0,0 +1,538 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include "Meter.h" > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Flow.h" > +#include "PacketParser.h" > +#include "util.h" > + > +static PNDIS_RW_LOCK_EX meterGlobalTableLock; > +PLIST_ENTRY meterGlobalTable; > + > +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, > + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, > + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, > +}; > + > +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, > + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > +}; > + > +NTSTATUS > +OvsInitMeter(POVS_SWITCH_CONTEXT context) > +{ > + UINT32 maxEntry = METER_HASH_BUCKET_MAX; > + > + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); > + if (meterGlobalTableLock == NULL) { > + return STATUS_INSUFFICIENT_RESOURCES; > + } > + > + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, > + OVS_METER_TAG); > + if (!meterGlobalTable) { > + NdisFreeRWLock(meterGlobalTableLock); > + return NDIS_STATUS_RESOURCES; > + } > + > + for (UINT32 index = 0; index < maxEntry; index++) { > + InitializeListHead(&meterGlobalTable[index]); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) > +{ > + PNL_ATTR a = NULL; > + INT rem = 0; > + UINT32 bandMaxDelta = 0; > + UINT32 attrOffset = 0; > + DpMeterBand *band = NULL; > + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; > + UINT16 nBands = 0; > + > + band = meter->bands; > + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), > + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { > + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); > + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); > + if (!NlAttrParse(nlMsgHdr, > + attrOffset, > + NlAttrGetSize(a), > + bandPolicy, ARRAY_SIZE(bandPolicy), > + bandAttrs, ARRAY_SIZE(bandAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { > + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_RATE]) { > + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_BURST]) { > + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); > + } > + > + band->bucket = (band->burst_size + band->rate) * 1000; > + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); > + if (bandMaxDelta > meter->maxDelta) { > + meter->maxDelta = bandMaxDelta; > + } > + > + nBands++; > + band++; > + } > + > + meter->nBands = nBands; > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + DpMeter *meter = NULL; > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + LOCK_STATE_EX lockState; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + ASSERT(usrParamsCtx->inputBuffer != NULL); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); > + if (!meter) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + RtlZeroMemory(meter, sizeof(*meter)); > + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; > + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *)NlAttrData( > + meterAttrs[OVS_METER_ATTR_STATS]); > + } > + > + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { > + nlError = NL_ERROR_NOMSG; > + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], > + &(meter->link)); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { > + nlError = NL_ERROR_NOMEM; > + 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; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + UINT32 bandsOffset = 0; > + UINT32 bandAttrOffset = 0; > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); > + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); > + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, > + OVS_METER_BAND_TYPE_DROP)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + > + } > + NlMsgEndNested(&nlBuf, bandAttrOffset); > + NlMsgEndNested(&nlBuf, bandsOffset); > + > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return STATUS_SUCCESS; > +} > + > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) > +{ > + BOOLEAN ok = FALSE; > + UINT32 bandAttrOffset; > + UINT32 bandsOffset; > + > + /* Add meter element. */ > + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); > + if (!ok) { > + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); > + return ok; > + } > + > + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, > + (PCHAR)&(dpMeter->stats), > + sizeof(dpMeter->stats)); > + if (!ok) { > + OVS_LOG_ERROR("Could not add ovs meter stats."); > + return ok; > + } > + > + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); > + for (int index = 0; index < dpMeter->nBands; index++) { > + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); > + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, > + (PCHAR)&(dpMeter->bands[index].stats), > + sizeof(dpMeter->bands[index].stats)); > + NlMsgEndNested(nlBuf, bandAttrOffset); > + if (!ok) { > + break; > + } > + } > + > + NlMsgEndNested(nlBuf, bandsOffset); > + return ok; > +} > + > +NDIS_STATUS > +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + UINT32 meterId = 0; > + DpMeter *dpMeter = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + LOCK_STATE_EX lockState; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + > + /* Reply message header */ > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_WARN("Has not find %d associated meter", meterId); > + nlError = NL_ERROR_EXIST; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { > + nlError = NL_ERROR_NOMEM; > + OVS_LOG_ERROR("Could not build ovs meter reply msg."); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + LOCK_STATE_EX lockState; > + UINT32 meterId = 0; > + BOOLEAN ok; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_DEL, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { > + nlError = NL_ERROR_NOMEM; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + RemoveEntryList(&entry->link); > + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); > + break; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > + > + > +DpMeter* > +OvsMeterLookup(UINT32 meterId) > +{ > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + return entry; > + } > + } > + > + return NULL; > +} > + > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) > +{ > + DpMeter *dpMeter; > + DpMeterBand *band; > + UINT32 longDeltaMs; > + UINT32 deltaMs; > + UINT64 currentTime; > + LOCK_STATE_EX lockState; > + UINT32 cost; > + UINT32 bandExceededRate = 0; > + INT32 bandExceedIndex = -1; > + UINT64 maxBucketSize = 0; > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > + } > + > + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); > + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); [AS] Why 10000 here? Can you please add a comment for it? > + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : > + longDeltaMs; > + dpMeter->used = currentTime; > + dpMeter->stats.n_packets += 1; > + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; > + for (int index = 0; index < dpMeter->nBands; index++) { > + band = &(dpMeter->bands[index]); > + maxBucketSize = (band->burst_size + band->rate) * 1000LL; > + band->bucket += deltaMs * band->rate; > + if (band->bucket > maxBucketSize) { > + band->bucket = maxBucketSize; > + } > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + } else if (band->rate > bandExceededRate) { > + bandExceededRate = band->rate; > + bandExceedIndex = index; > + } > + } > + > + if (bandExceedIndex >= 0) { > + band = &(dpMeter->bands[bandExceedIndex]); > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return TRUE; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > +} > \ No newline at end of file [AS] Nit. Please add new line. > diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h > new file mode 100644 > index 000000000..bc94489a5 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.h > @@ -0,0 +1,68 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVS_METER_H > +#define OVS_METER_H > + > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Netlink/Netlink.h" > +#include "Flow.h" > + > +#define OVS_MAX_BANDS 1 > +#define OVS_MAX_METERS 32 > +#define METER_HASH_BUCKET_MAX 1024 > + > +typedef struct _DpMeterBand { > + UINT32 type; > + UINT32 rate; > + UINT32 burst_size; > + UINT64 bucket; > + struct ovs_flow_stats stats; > +} DpMeterBand; > + > +typedef struct _DpMeter { > + LIST_ENTRY link; > + UINT32 id; > + UINT16 kbps:1; > + UINT16 keepStatus:1; > + UINT16 nBands; > + UINT32 maxDelta; > + UINT64 used; > + struct ovs_flow_stats stats; > + DpMeterBand bands[OVS_MAX_BANDS]; > +} DpMeter; > + > +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); > +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > +UINT32 *replyLen); > + > +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +DpMeter* OvsMeterLookup(UINT32 meterId); > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); > + > + > +#endif //OVS_METER_H > diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c > index 1ac4fa77c..088aee21a 100644 > --- a/datapath-windows/ovsext/Switch.c > +++ b/datapath-windows/ovsext/Switch.c > @@ -24,6 +24,7 @@ > #include "Switch.h" > #include "Vport.h" > #include "Event.h" > +#include "Meter.h" > #include "Flow.h" > #include "IpHelper.h" > #include "Oid.h" > @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, > goto create_switch_done; > } > > + status = OvsInitMeter(switchContext); > + if (status != STATUS_SUCCESS) { > + OvsUninitSwitchContext(switchContext); > + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); > + goto create_switch_done; > + } > + > *switchContextOut = switchContext; > > create_switch_done: > diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h > index f63a885a9..16903ed46 100644 > --- a/datapath-windows/ovsext/Util.h > +++ b/datapath-windows/ovsext/Util.h > @@ -38,6 +38,7 @@ > #define OVS_TUNFLT_POOL_TAG 'WSVO' > #define OVS_RECIRC_POOL_TAG 'CSVO' > #define OVS_CT_POOL_TAG 'CTVO' > +#define OVS_METER_TAG 'MEVO' > #define OVS_GENEVE_POOL_TAG 'GNVO' > #define OVS_IPFRAG_POOL_TAG 'FGVO' > > diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj > index 7a2cbd2de..6e54b71f9 100644 > --- a/datapath-windows/ovsext/ovsext.vcxproj > +++ b/datapath-windows/ovsext/ovsext.vcxproj > @@ -164,6 +164,7 @@ > <ClInclude Include="IpFragment.h" /> > <ClInclude Include="IpHelper.h" /> > <ClInclude Include="Jhash.h" /> > + <ClInclude Include="Meter.h" /> > <ClInclude Include="Mpls.h" /> > <ClInclude Include="Netlink/Netlink.h" /> > <ClInclude Include="Netlink/NetlinkBuf.h" /> > @@ -408,6 +409,7 @@ > <ClCompile Include="IpFragment.c" /> > <ClCompile Include="IpHelper.c" /> > <ClCompile Include="Jhash.c" /> > + <ClCompile Include="Meter.c" /> > <ClCompile Include="Netlink/Netlink.c" /> > <ClCompile Include="Netlink/NetlinkBuf.c" /> > <ClCompile Include="Datapath.c" /> > diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c > index 93c1fa561..80da20d9f 100644 > --- a/lib/netlink-socket.c > +++ b/lib/netlink-socket.c > @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, > family_name = OVS_FLOW_FAMILY; > family_version = OVS_FLOW_VERSION; > family_attrmax = OVS_FLOW_ATTR_MAX; > + } else if (!strcmp(name, OVS_METER_FAMILY)) { > + family_id = OVS_WIN_NL_METER_FAMILY_ID; > + family_name = OVS_METER_FAMILY; > + family_version = OVS_METER_VERSION; > + family_attrmax = __OVS_METER_ATTR_MAX; > } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { > family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; > family_name = OVS_WIN_NETDEV_FAMILY;
Hi Alin, I have made some modification according to your suggestion and new patch will come out soon. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org> Date: Tuesday, August 9, 2022 at 9:22 PM To: dev@openvswitch.org <dev@openvswitch.org> Cc: Dejing Liu <ldejing@vmware.com> Subject: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Again patch looks good I only have one question. Please see bellow. > This patch implemented meter action, currently, meter only support drop > method and only support one band. The overall implementation is, when a > packet comes in, it will first lookup meter according to the meter id, > then get the band->rates and delta time since last access the same meter > from the meter struct. Add the multiply result(band->rates * delta_time) > to bucket, finally bucket minus the packet size, if the result larger > than zero, allow the packet go through, otherwise deny the packet go > through. > > Test case: > 1. Setting the size meter size 3M, then the bandwidth was limit > around 3M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\ > band=type=drop,rate=3000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip \ > actions=meter:2,normal" -O OpenFlow13 > 2. Setting the meter size 8M, then the bandwidth was limit > around 8M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\ > kbps,band=type=drop,rate=8000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip\ > actions=meter:2,normal" -O OpenFlow13 > > Signed-off-by: ldejing <ldejing@vmware.com> > --- > datapath-windows/automake.mk | 2 + > datapath-windows/include/OvsDpInterfaceExt.h | 3 + > datapath-windows/ovsext/Actions.c | 10 + > datapath-windows/ovsext/Datapath.c | 46 +- > datapath-windows/ovsext/Meter.c | 538 +++++++++++++++++++ > datapath-windows/ovsext/Meter.h | 68 +++ > datapath-windows/ovsext/Switch.c | 8 + > datapath-windows/ovsext/Util.h | 1 + > datapath-windows/ovsext/ovsext.vcxproj | 2 + > lib/netlink-socket.c | 5 + > 10 files changed, 682 insertions(+), 1 deletion(-) > create mode 100644 datapath-windows/ovsext/Meter.c > create mode 100644 datapath-windows/ovsext/Meter.h > > diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk > index 60b3d6033..2d9f9a40d 100644 > --- a/datapath-windows/automake.mk > +++ b/datapath-windows/automake.mk > @@ -42,6 +42,8 @@ EXTRA_DIST += \ > datapath-windows/ovsext/Jhash.c \ > datapath-windows/ovsext/Jhash.h \ > datapath-windows/ovsext/Mpls.h \ > + datapath-windows/ovsext/Meter.c \ > + datapath-windows/ovsext/Meter.h \ > datapath-windows/ovsext/NetProto.h \ > datapath-windows/ovsext/Netlink/Netlink.c \ > datapath-windows/ovsext/Netlink/Netlink.h \ > diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h > index 5fd8000d1..045e4cbd6 100644 > --- a/datapath-windows/include/OvsDpInterfaceExt.h > +++ b/datapath-windows/include/OvsDpInterfaceExt.h > @@ -74,6 +74,9 @@ > #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) > #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) > > +/* Meter Family */ > +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) > + > #define OVS_WIN_NL_INVALID_MCGRP_ID 0 > #define OVS_WIN_NL_MCGRP_START_ID 100 > #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) > diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c > index 20de4db4c..63ee574c5 100644 > --- a/datapath-windows/ovsext/Actions.c > +++ b/datapath-windows/ovsext/Actions.c > @@ -23,6 +23,7 @@ > #include "Flow.h" > #include "Gre.h" > #include "Jhash.h" > +#include "Meter.h" > #include "Mpls.h" > #include "NetProto.h" > #include "Offload.h" > @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, > } > break; > } > + case OVS_ACTION_ATTR_METER: { > + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { > + OVS_LOG_INFO("Drop packet"); > + dropReason = L"Ovs-meter exceed max rate"; > + goto dropit; > + } > + > + break; > + } > case OVS_ACTION_ATTR_SAMPLE: > { > if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL > diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c > index fa994840a..37db5bd17 100644 > --- a/datapath-windows/ovsext/Datapath.c > +++ b/datapath-windows/ovsext/Datapath.c > @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, > OvsReadPacketCmdHandler, > OvsCtDeleteCmdHandler, > OvsCtDumpCmdHandler, > - OvsCtLimitHandler; > + OvsCtLimitHandler, > + OvsMeterFeatureProbe, > + OvsNewMeterCmdHandler, > + OvsMeterDestroy, > + OvsMeterGet; > > static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > UINT32 *replyLen); > @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { > .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) > }; > > +/* Netlink Meter family */ > +NETLINK_CMD nlMeterFamilyCmdOps[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .handler = OvsMeterFeatureProbe, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_SET, > + .handler = OvsNewMeterCmdHandler, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_GET, > + .handler = OvsMeterGet, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .handler = OvsMeterDestroy, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > +}; > + > +NETLINK_FAMILY nlMeterFamilyOps = { > + .name = OVS_METER_FAMILY, > + .id = OVS_WIN_NL_METER_FAMILY_ID, > + .version = OVS_METER_VERSION, > + .maxAttr = __OVS_METER_ATTR_MAX, > + .cmds = nlMeterFamilyCmdOps, > + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) > +}; > + > /* Netlink netdev family. */ > NETLINK_CMD nlNetdevFamilyCmdOps[] = { > { .cmd = OVS_WIN_NETDEV_CMD_GET, > @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, > case NFNL_TYPE_CT_DEL: > nlFamilyOps = &nlCtFamilyOps; > break; > + case OVS_WIN_NL_METER_FAMILY_ID: > + nlFamilyOps = &nlMeterFamilyOps; > + break; > case OVS_WIN_NL_CTRL_FAMILY_ID: > nlFamilyOps = &nlControlFamilyOps; > break; > diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c > new file mode 100644 > index 000000000..62f5f36c9 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.c > @@ -0,0 +1,538 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&reserved=0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include "Meter.h" > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Flow.h" > +#include "PacketParser.h" > +#include "util.h" > + > +static PNDIS_RW_LOCK_EX meterGlobalTableLock; > +PLIST_ENTRY meterGlobalTable; > + > +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, > + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, > + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, > +}; > + > +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, > + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > +}; > + > +NTSTATUS > +OvsInitMeter(POVS_SWITCH_CONTEXT context) > +{ > + UINT32 maxEntry = METER_HASH_BUCKET_MAX; > + > + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); > + if (meterGlobalTableLock == NULL) { > + return STATUS_INSUFFICIENT_RESOURCES; > + } > + > + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, > + OVS_METER_TAG); > + if (!meterGlobalTable) { > + NdisFreeRWLock(meterGlobalTableLock); > + return NDIS_STATUS_RESOURCES; > + } > + > + for (UINT32 index = 0; index < maxEntry; index++) { > + InitializeListHead(&meterGlobalTable[index]); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) > +{ > + PNL_ATTR a = NULL; > + INT rem = 0; > + UINT32 bandMaxDelta = 0; > + UINT32 attrOffset = 0; > + DpMeterBand *band = NULL; > + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; > + UINT16 nBands = 0; > + > + band = meter->bands; > + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), > + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { > + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); > + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); > + if (!NlAttrParse(nlMsgHdr, > + attrOffset, > + NlAttrGetSize(a), > + bandPolicy, ARRAY_SIZE(bandPolicy), > + bandAttrs, ARRAY_SIZE(bandAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { > + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_RATE]) { > + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_BURST]) { > + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); > + } > + > + band->bucket = (band->burst_size + band->rate) * 1000; > + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); > + if (bandMaxDelta > meter->maxDelta) { > + meter->maxDelta = bandMaxDelta; > + } > + > + nBands++; > + band++; > + } > + > + meter->nBands = nBands; > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + DpMeter *meter = NULL; > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + LOCK_STATE_EX lockState; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + ASSERT(usrParamsCtx->inputBuffer != NULL); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); > + if (!meter) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + RtlZeroMemory(meter, sizeof(*meter)); > + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; > + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *)NlAttrData( > + meterAttrs[OVS_METER_ATTR_STATS]); > + } > + > + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { > + nlError = NL_ERROR_NOMSG; > + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], > + &(meter->link)); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { > + nlError = NL_ERROR_NOMEM; > + 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; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + UINT32 bandsOffset = 0; > + UINT32 bandAttrOffset = 0; > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); > + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); > + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, > + OVS_METER_BAND_TYPE_DROP)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + > + } > + NlMsgEndNested(&nlBuf, bandAttrOffset); > + NlMsgEndNested(&nlBuf, bandsOffset); > + > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return STATUS_SUCCESS; > +} > + > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) > +{ > + BOOLEAN ok = FALSE; > + UINT32 bandAttrOffset; > + UINT32 bandsOffset; > + > + /* Add meter element. */ > + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); > + if (!ok) { > + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); > + return ok; > + } > + > + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, > + (PCHAR)&(dpMeter->stats), > + sizeof(dpMeter->stats)); > + if (!ok) { > + OVS_LOG_ERROR("Could not add ovs meter stats."); > + return ok; > + } > + > + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); > + for (int index = 0; index < dpMeter->nBands; index++) { > + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); > + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, > + (PCHAR)&(dpMeter->bands[index].stats), > + sizeof(dpMeter->bands[index].stats)); > + NlMsgEndNested(nlBuf, bandAttrOffset); > + if (!ok) { > + break; > + } > + } > + > + NlMsgEndNested(nlBuf, bandsOffset); > + return ok; > +} > + > +NDIS_STATUS > +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + UINT32 meterId = 0; > + DpMeter *dpMeter = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + LOCK_STATE_EX lockState; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + > + /* Reply message header */ > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_WARN("Has not find %d associated meter", meterId); > + nlError = NL_ERROR_EXIST; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { > + nlError = NL_ERROR_NOMEM; > + OVS_LOG_ERROR("Could not build ovs meter reply msg."); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + LOCK_STATE_EX lockState; > + UINT32 meterId = 0; > + BOOLEAN ok; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_DEL, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { > + nlError = NL_ERROR_NOMEM; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + RemoveEntryList(&entry->link); > + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); > + break; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > + > + > +DpMeter* > +OvsMeterLookup(UINT32 meterId) > +{ > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + return entry; > + } > + } > + > + return NULL; > +} > + > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) > +{ > + DpMeter *dpMeter; > + DpMeterBand *band; > + UINT32 longDeltaMs; > + UINT32 deltaMs; > + UINT64 currentTime; > + LOCK_STATE_EX lockState; > + UINT32 cost; > + UINT32 bandExceededRate = 0; > + INT32 bandExceedIndex = -1; > + UINT64 maxBucketSize = 0; > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > + } > + > + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); > + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); [AS] Why 10000 here? Can you please add a comment for it? Because "currentTime" here represent count of 100-nanosecond intervals, thus divide 10000 to convert the unit to ms. > + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : > + longDeltaMs; > + dpMeter->used = currentTime; > + dpMeter->stats.n_packets += 1; > + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; > + for (int index = 0; index < dpMeter->nBands; index++) { > + band = &(dpMeter->bands[index]); > + maxBucketSize = (band->burst_size + band->rate) * 1000LL; > + band->bucket += deltaMs * band->rate; > + if (band->bucket > maxBucketSize) { > + band->bucket = maxBucketSize; > + } > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + } else if (band->rate > bandExceededRate) { > + bandExceededRate = band->rate; > + bandExceedIndex = index; > + } > + } > + > + if (bandExceedIndex >= 0) { > + band = &(dpMeter->bands[bandExceedIndex]); > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return TRUE; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > +} > \ No newline at end of file [AS] Nit. Please add new line. Will fix it. > diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h > new file mode 100644 > index 000000000..bc94489a5 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.h > @@ -0,0 +1,68 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&reserved=0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVS_METER_H > +#define OVS_METER_H > + > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Netlink/Netlink.h" > +#include "Flow.h" > + > +#define OVS_MAX_BANDS 1 > +#define OVS_MAX_METERS 32 > +#define METER_HASH_BUCKET_MAX 1024 > + > +typedef struct _DpMeterBand { > + UINT32 type; > + UINT32 rate; > + UINT32 burst_size; > + UINT64 bucket; > + struct ovs_flow_stats stats; > +} DpMeterBand; > + > +typedef struct _DpMeter { > + LIST_ENTRY link; > + UINT32 id; > + UINT16 kbps:1; > + UINT16 keepStatus:1; > + UINT16 nBands; > + UINT32 maxDelta; > + UINT64 used; > + struct ovs_flow_stats stats; > + DpMeterBand bands[OVS_MAX_BANDS]; > +} DpMeter; > + > +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); > +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > +UINT32 *replyLen); > + > +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +DpMeter* OvsMeterLookup(UINT32 meterId); > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); > + > + > +#endif //OVS_METER_H > diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c > index 1ac4fa77c..088aee21a 100644 > --- a/datapath-windows/ovsext/Switch.c > +++ b/datapath-windows/ovsext/Switch.c > @@ -24,6 +24,7 @@ > #include "Switch.h" > #include "Vport.h" > #include "Event.h" > +#include "Meter.h" > #include "Flow.h" > #include "IpHelper.h" > #include "Oid.h" > @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, > goto create_switch_done; > } > > + status = OvsInitMeter(switchContext); > + if (status != STATUS_SUCCESS) { > + OvsUninitSwitchContext(switchContext); > + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); > + goto create_switch_done; > + } > + > *switchContextOut = switchContext; > > create_switch_done: > diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h > index f63a885a9..16903ed46 100644 > --- a/datapath-windows/ovsext/Util.h > +++ b/datapath-windows/ovsext/Util.h > @@ -38,6 +38,7 @@ > #define OVS_TUNFLT_POOL_TAG 'WSVO' > #define OVS_RECIRC_POOL_TAG 'CSVO' > #define OVS_CT_POOL_TAG 'CTVO' > +#define OVS_METER_TAG 'MEVO' > #define OVS_GENEVE_POOL_TAG 'GNVO' > #define OVS_IPFRAG_POOL_TAG 'FGVO' > > diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj > index 7a2cbd2de..6e54b71f9 100644 > --- a/datapath-windows/ovsext/ovsext.vcxproj > +++ b/datapath-windows/ovsext/ovsext.vcxproj > @@ -164,6 +164,7 @@ > <ClInclude Include="IpFragment.h" /> > <ClInclude Include="IpHelper.h" /> > <ClInclude Include="Jhash.h" /> > + <ClInclude Include="Meter.h" /> > <ClInclude Include="Mpls.h" /> > <ClInclude Include="Netlink/Netlink.h" /> > <ClInclude Include="Netlink/NetlinkBuf.h" /> > @@ -408,6 +409,7 @@ > <ClCompile Include="IpFragment.c" /> > <ClCompile Include="IpHelper.c" /> > <ClCompile Include="Jhash.c" /> > + <ClCompile Include="Meter.c" /> > <ClCompile Include="Netlink/Netlink.c" /> > <ClCompile Include="Netlink/NetlinkBuf.c" /> > <ClCompile Include="Datapath.c" /> > diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c > index 93c1fa561..80da20d9f 100644 > --- a/lib/netlink-socket.c > +++ b/lib/netlink-socket.c > @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, > family_name = OVS_FLOW_FAMILY; > family_version = OVS_FLOW_VERSION; > family_attrmax = OVS_FLOW_ATTR_MAX; > + } else if (!strcmp(name, OVS_METER_FAMILY)) { > + family_id = OVS_WIN_NL_METER_FAMILY_ID; > + family_name = OVS_METER_FAMILY; > + family_version = OVS_METER_VERSION; > + family_attrmax = __OVS_METER_ATTR_MAX; > } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { > family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; > family_name = OVS_WIN_NETDEV_FAMILY;
Dejing, Looking forward to it. Alin. From: Dejing Liu <ldejing@vmware.com> Sent: Tuesday, August 16, 2022 1:00 PM To: Alin-Gabriel Serdean <aserdean@ovn.org>; dev@openvswitch.org Cc: Frank Guo <frankg@vmware.com>; Wilson Peng <pweisong@vmware.com>; Lina Li <linali@vmware.com> Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Hi Alin, I have made some modification according to your suggestion and new patch will come out soon. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org <mailto:aserdean@ovn.org> > Date: Tuesday, August 9, 2022 at 9:22 PM To: dev@openvswitch.org <mailto:dev@openvswitch.org> <dev@openvswitch.org <mailto:dev@openvswitch.org> > Cc: Dejing Liu <ldejing@vmware.com <mailto:ldejing@vmware.com> > Subject: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Again patch looks good I only have one question. Please see bellow. > This patch implemented meter action, currently, meter only support drop > method and only support one band. The overall implementation is, when a > packet comes in, it will first lookup meter according to the meter id, > then get the band->rates and delta time since last access the same meter > from the meter struct. Add the multiply result(band->rates * delta_time) > to bucket, finally bucket minus the packet size, if the result larger > than zero, allow the packet go through, otherwise deny the packet go > through. > > Test case: > 1. Setting the size meter size 3M, then the bandwidth was limit > around 3M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\ > band=type=drop,rate=3000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip \ > actions=meter:2,normal" -O OpenFlow13 > 2. Setting the meter size 8M, then the bandwidth was limit > around 8M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\ > kbps,band=type=drop,rate=8000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip\ > actions=meter:2,normal" -O OpenFlow13 > > Signed-off-by: ldejing <ldejing@vmware.com <mailto:ldejing@vmware.com> > > --- > datapath-windows/automake.mk | 2 + > datapath-windows/include/OvsDpInterfaceExt.h | 3 + > datapath-windows/ovsext/Actions.c | 10 + > datapath-windows/ovsext/Datapath.c | 46 +- > datapath-windows/ovsext/Meter.c | 538 +++++++++++++++++++ > datapath-windows/ovsext/Meter.h | 68 +++ > datapath-windows/ovsext/Switch.c | 8 + > datapath-windows/ovsext/Util.h | 1 + > datapath-windows/ovsext/ovsext.vcxproj | 2 + > lib/netlink-socket.c | 5 + > 10 files changed, 682 insertions(+), 1 deletion(-) > create mode 100644 datapath-windows/ovsext/Meter.c > create mode 100644 datapath-windows/ovsext/Meter.h > > diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk > index 60b3d6033..2d9f9a40d 100644 > --- a/datapath-windows/automake.mk > +++ b/datapath-windows/automake.mk > @@ -42,6 +42,8 @@ EXTRA_DIST += \ > datapath-windows/ovsext/Jhash.c \ > datapath-windows/ovsext/Jhash.h \ > datapath-windows/ovsext/Mpls.h \ > + datapath-windows/ovsext/Meter.c \ > + datapath-windows/ovsext/Meter.h \ > datapath-windows/ovsext/NetProto.h \ > datapath-windows/ovsext/Netlink/Netlink.c \ > datapath-windows/ovsext/Netlink/Netlink.h \ > diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h > index 5fd8000d1..045e4cbd6 100644 > --- a/datapath-windows/include/OvsDpInterfaceExt.h > +++ b/datapath-windows/include/OvsDpInterfaceExt.h > @@ -74,6 +74,9 @@ > #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) > #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) > > +/* Meter Family */ > +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) > + > #define OVS_WIN_NL_INVALID_MCGRP_ID 0 > #define OVS_WIN_NL_MCGRP_START_ID 100 > #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) > diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c > index 20de4db4c..63ee574c5 100644 > --- a/datapath-windows/ovsext/Actions.c > +++ b/datapath-windows/ovsext/Actions.c > @@ -23,6 +23,7 @@ > #include "Flow.h" > #include "Gre.h" > #include "Jhash.h" > +#include "Meter.h" > #include "Mpls.h" > #include "NetProto.h" > #include "Offload.h" > @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, > } > break; > } > + case OVS_ACTION_ATTR_METER: { > + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { > + OVS_LOG_INFO("Drop packet"); > + dropReason = L"Ovs-meter exceed max rate"; > + goto dropit; > + } > + > + break; > + } > case OVS_ACTION_ATTR_SAMPLE: > { > if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL > diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c > index fa994840a..37db5bd17 100644 > --- a/datapath-windows/ovsext/Datapath.c > +++ b/datapath-windows/ovsext/Datapath.c > @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, > OvsReadPacketCmdHandler, > OvsCtDeleteCmdHandler, > OvsCtDumpCmdHandler, > - OvsCtLimitHandler; > + OvsCtLimitHandler, > + OvsMeterFeatureProbe, > + OvsNewMeterCmdHandler, > + OvsMeterDestroy, > + OvsMeterGet; > > static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > UINT32 *replyLen); > @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { > .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) > }; > > +/* Netlink Meter family */ > +NETLINK_CMD nlMeterFamilyCmdOps[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .handler = OvsMeterFeatureProbe, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_SET, > + .handler = OvsNewMeterCmdHandler, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_GET, > + .handler = OvsMeterGet, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .handler = OvsMeterDestroy, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > +}; > + > +NETLINK_FAMILY nlMeterFamilyOps = { > + .name = OVS_METER_FAMILY, > + .id = OVS_WIN_NL_METER_FAMILY_ID, > + .version = OVS_METER_VERSION, > + .maxAttr = __OVS_METER_ATTR_MAX, > + .cmds = nlMeterFamilyCmdOps, > + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) > +}; > + > /* Netlink netdev family. */ > NETLINK_CMD nlNetdevFamilyCmdOps[] = { > { .cmd = OVS_WIN_NETDEV_CMD_GET, > @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, > case NFNL_TYPE_CT_DEL: > nlFamilyOps = &nlCtFamilyOps; > break; > + case OVS_WIN_NL_METER_FAMILY_ID: > + nlFamilyOps = &nlMeterFamilyOps; > + break; > case OVS_WIN_NL_CTRL_FAMILY_ID: > nlFamilyOps = &nlControlFamilyOps; > break; > diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c > new file mode 100644 > index 000000000..62f5f36c9 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.c > @@ -0,0 +1,538 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache. org%2Flicenses%2FLICENSE-2.0 <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache .org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf363 7760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C63 7956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzI iLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=HBLCMJ8wxRzzMsHhdi m8OPYjsYQ7c5X58qarQJlEoQk%3D&reserved=0> &data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46% 7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7C TWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0% 3D%7C3000%7C%7C%7C&sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D& amp;reserved=0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include "Meter.h" > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Flow.h" > +#include "PacketParser.h" > +#include "util.h" > + > +static PNDIS_RW_LOCK_EX meterGlobalTableLock; > +PLIST_ENTRY meterGlobalTable; > + > +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, > + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, > + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, > +}; > + > +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, > + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > +}; > + > +NTSTATUS > +OvsInitMeter(POVS_SWITCH_CONTEXT context) > +{ > + UINT32 maxEntry = METER_HASH_BUCKET_MAX; > + > + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); > + if (meterGlobalTableLock == NULL) { > + return STATUS_INSUFFICIENT_RESOURCES; > + } > + > + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, > + OVS_METER_TAG); > + if (!meterGlobalTable) { > + NdisFreeRWLock(meterGlobalTableLock); > + return NDIS_STATUS_RESOURCES; > + } > + > + for (UINT32 index = 0; index < maxEntry; index++) { > + InitializeListHead(&meterGlobalTable[index]); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) > +{ > + PNL_ATTR a = NULL; > + INT rem = 0; > + UINT32 bandMaxDelta = 0; > + UINT32 attrOffset = 0; > + DpMeterBand *band = NULL; > + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; > + UINT16 nBands = 0; > + > + band = meter->bands; > + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), > + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { > + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); > + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); > + if (!NlAttrParse(nlMsgHdr, > + attrOffset, > + NlAttrGetSize(a), > + bandPolicy, ARRAY_SIZE(bandPolicy), > + bandAttrs, ARRAY_SIZE(bandAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { > + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_RATE]) { > + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_BURST]) { > + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); > + } > + > + band->bucket = (band->burst_size + band->rate) * 1000; > + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); > + if (bandMaxDelta > meter->maxDelta) { > + meter->maxDelta = bandMaxDelta; > + } > + > + nBands++; > + band++; > + } > + > + meter->nBands = nBands; > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + DpMeter *meter = NULL; > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + LOCK_STATE_EX lockState; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + ASSERT(usrParamsCtx->inputBuffer != NULL); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); > + if (!meter) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + RtlZeroMemory(meter, sizeof(*meter)); > + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; > + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *)NlAttrData( > + meterAttrs[OVS_METER_ATTR_STATS]); > + } > + > + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { > + nlError = NL_ERROR_NOMSG; > + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], > + &(meter->link)); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { > + nlError = NL_ERROR_NOMEM; > + 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; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + UINT32 bandsOffset = 0; > + UINT32 bandAttrOffset = 0; > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); > + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); > + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, > + OVS_METER_BAND_TYPE_DROP)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + > + } > + NlMsgEndNested(&nlBuf, bandAttrOffset); > + NlMsgEndNested(&nlBuf, bandsOffset); > + > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return STATUS_SUCCESS; > +} > + > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) > +{ > + BOOLEAN ok = FALSE; > + UINT32 bandAttrOffset; > + UINT32 bandsOffset; > + > + /* Add meter element. */ > + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); > + if (!ok) { > + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); > + return ok; > + } > + > + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, > + (PCHAR)&(dpMeter->stats), > + sizeof(dpMeter->stats)); > + if (!ok) { > + OVS_LOG_ERROR("Could not add ovs meter stats."); > + return ok; > + } > + > + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); > + for (int index = 0; index < dpMeter->nBands; index++) { > + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); > + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, > + (PCHAR)&(dpMeter->bands[index].stats), > + sizeof(dpMeter->bands[index].stats)); > + NlMsgEndNested(nlBuf, bandAttrOffset); > + if (!ok) { > + break; > + } > + } > + > + NlMsgEndNested(nlBuf, bandsOffset); > + return ok; > +} > + > +NDIS_STATUS > +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + UINT32 meterId = 0; > + DpMeter *dpMeter = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + LOCK_STATE_EX lockState; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + > + /* Reply message header */ > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_WARN("Has not find %d associated meter", meterId); > + nlError = NL_ERROR_EXIST; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { > + nlError = NL_ERROR_NOMEM; > + OVS_LOG_ERROR("Could not build ovs meter reply msg."); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + LOCK_STATE_EX lockState; > + UINT32 meterId = 0; > + BOOLEAN ok; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_DEL, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { > + nlError = NL_ERROR_NOMEM; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + RemoveEntryList(&entry->link); > + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); > + break; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > + > + > +DpMeter* > +OvsMeterLookup(UINT32 meterId) > +{ > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + return entry; > + } > + } > + > + return NULL; > +} > + > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) > +{ > + DpMeter *dpMeter; > + DpMeterBand *band; > + UINT32 longDeltaMs; > + UINT32 deltaMs; > + UINT64 currentTime; > + LOCK_STATE_EX lockState; > + UINT32 cost; > + UINT32 bandExceededRate = 0; > + INT32 bandExceedIndex = -1; > + UINT64 maxBucketSize = 0; > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > + } > + > + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); > + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); [AS] Why 10000 here? Can you please add a comment for it? Because "currentTime" here represent count of 100-nanosecond intervals, thus divide 10000 to convert the unit to ms. > + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : > + longDeltaMs; > + dpMeter->used = currentTime; > + dpMeter->stats.n_packets += 1; > + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; > + for (int index = 0; index < dpMeter->nBands; index++) { > + band = &(dpMeter->bands[index]); > + maxBucketSize = (band->burst_size + band->rate) * 1000LL; > + band->bucket += deltaMs * band->rate; > + if (band->bucket > maxBucketSize) { > + band->bucket = maxBucketSize; > + } > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + } else if (band->rate > bandExceededRate) { > + bandExceededRate = band->rate; > + bandExceedIndex = index; > + } > + } > + > + if (bandExceedIndex >= 0) { > + band = &(dpMeter->bands[bandExceedIndex]); > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return TRUE; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > +} > \ No newline at end of file [AS] Nit. Please add new line. Will fix it. > diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h > new file mode 100644 > index 000000000..bc94489a5 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.h > @@ -0,0 +1,68 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache. org%2Flicenses%2FLICENSE-2.0 <https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache .org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf363 7760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C63 7956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzI iLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=X1o%2BHH8z7OH95mtH RLPW0ZilompVkeMxx1hSemmumbs%3D&reserved=0> &data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46% 7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7C TWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0% 3D%7C3000%7C%7C%7C&sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3 D&reserved=0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVS_METER_H > +#define OVS_METER_H > + > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Netlink/Netlink.h" > +#include "Flow.h" > + > +#define OVS_MAX_BANDS 1 > +#define OVS_MAX_METERS 32 > +#define METER_HASH_BUCKET_MAX 1024 > + > +typedef struct _DpMeterBand { > + UINT32 type; > + UINT32 rate; > + UINT32 burst_size; > + UINT64 bucket; > + struct ovs_flow_stats stats; > +} DpMeterBand; > + > +typedef struct _DpMeter { > + LIST_ENTRY link; > + UINT32 id; > + UINT16 kbps:1; > + UINT16 keepStatus:1; > + UINT16 nBands; > + UINT32 maxDelta; > + UINT64 used; > + struct ovs_flow_stats stats; > + DpMeterBand bands[OVS_MAX_BANDS]; > +} DpMeter; > + > +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); > +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > +UINT32 *replyLen); > + > +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +DpMeter* OvsMeterLookup(UINT32 meterId); > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); > + > + > +#endif //OVS_METER_H > diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c > index 1ac4fa77c..088aee21a 100644 > --- a/datapath-windows/ovsext/Switch.c > +++ b/datapath-windows/ovsext/Switch.c > @@ -24,6 +24,7 @@ > #include "Switch.h" > #include "Vport.h" > #include "Event.h" > +#include "Meter.h" > #include "Flow.h" > #include "IpHelper.h" > #include "Oid.h" > @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, > goto create_switch_done; > } > > + status = OvsInitMeter(switchContext); > + if (status != STATUS_SUCCESS) { > + OvsUninitSwitchContext(switchContext); > + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); > + goto create_switch_done; > + } > + > *switchContextOut = switchContext; > > create_switch_done: > diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h > index f63a885a9..16903ed46 100644 > --- a/datapath-windows/ovsext/Util.h > +++ b/datapath-windows/ovsext/Util.h > @@ -38,6 +38,7 @@ > #define OVS_TUNFLT_POOL_TAG 'WSVO' > #define OVS_RECIRC_POOL_TAG 'CSVO' > #define OVS_CT_POOL_TAG 'CTVO' > +#define OVS_METER_TAG 'MEVO' > #define OVS_GENEVE_POOL_TAG 'GNVO' > #define OVS_IPFRAG_POOL_TAG 'FGVO' > > diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj > index 7a2cbd2de..6e54b71f9 100644 > --- a/datapath-windows/ovsext/ovsext.vcxproj > +++ b/datapath-windows/ovsext/ovsext.vcxproj > @@ -164,6 +164,7 @@ > <ClInclude Include="IpFragment.h" /> > <ClInclude Include="IpHelper.h" /> > <ClInclude Include="Jhash.h" /> > + <ClInclude Include="Meter.h" /> > <ClInclude Include="Mpls.h" /> > <ClInclude Include="Netlink/Netlink.h" /> > <ClInclude Include="Netlink/NetlinkBuf.h" /> > @@ -408,6 +409,7 @@ > <ClCompile Include="IpFragment.c" /> > <ClCompile Include="IpHelper.c" /> > <ClCompile Include="Jhash.c" /> > + <ClCompile Include="Meter.c" /> > <ClCompile Include="Netlink/Netlink.c" /> > <ClCompile Include="Netlink/NetlinkBuf.c" /> > <ClCompile Include="Datapath.c" /> > diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c > index 93c1fa561..80da20d9f 100644 > --- a/lib/netlink-socket.c > +++ b/lib/netlink-socket.c > @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, > family_name = OVS_FLOW_FAMILY; > family_version = OVS_FLOW_VERSION; > family_attrmax = OVS_FLOW_ATTR_MAX; > + } else if (!strcmp(name, OVS_METER_FAMILY)) { > + family_id = OVS_WIN_NL_METER_FAMILY_ID; > + family_name = OVS_METER_FAMILY; > + family_version = OVS_METER_VERSION; > + family_attrmax = __OVS_METER_ATTR_MAX; > } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { > family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; > family_name = OVS_WIN_NETDEV_FAMILY;
Hi Alin, I have sent all the latest patch to patchlist, hope you noticed it. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org> Date: Tuesday, August 16, 2022 at 6:19 PM To: Dejing Liu <ldejing@vmware.com>, 'Alin-Gabriel Serdean' <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org> Cc: Frank Guo <frankg@vmware.com>, Wilson Peng <pweisong@vmware.com>, Lina Li <linali@vmware.com> Subject: RE: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version ⚠ External Email Dejing, Looking forward to it. Alin. From: Dejing Liu <ldejing@vmware.com> Sent: Tuesday, August 16, 2022 1:00 PM To: Alin-Gabriel Serdean <aserdean@ovn.org>; dev@openvswitch.org Cc: Frank Guo <frankg@vmware.com>; Wilson Peng <pweisong@vmware.com>; Lina Li <linali@vmware.com> Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Hi Alin, I have made some modification according to your suggestion and new patch will come out soon. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org<mailto:aserdean@ovn.org>> Date: Tuesday, August 9, 2022 at 9:22 PM To: dev@openvswitch.org<mailto:dev@openvswitch.org> <dev@openvswitch.org<mailto:dev@openvswitch.org>> Cc: Dejing Liu <ldejing@vmware.com<mailto:ldejing@vmware.com>> Subject: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Again patch looks good I only have one question. Please see bellow. > This patch implemented meter action, currently, meter only support drop > method and only support one band. The overall implementation is, when a > packet comes in, it will first lookup meter according to the meter id, > then get the band->rates and delta time since last access the same meter > from the meter struct. Add the multiply result(band->rates * delta_time) > to bucket, finally bucket minus the packet size, if the result larger > than zero, allow the packet go through, otherwise deny the packet go > through. > > Test case: > 1. Setting the size meter size 3M, then the bandwidth was limit > around 3M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\ > band=type=drop,rate=3000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip \ > actions=meter:2,normal" -O OpenFlow13 > 2. Setting the meter size 8M, then the bandwidth was limit > around 8M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\ > kbps,band=type=drop,rate=8000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip\ > actions=meter:2,normal" -O OpenFlow13 > > Signed-off-by: ldejing <ldejing@vmware.com<mailto:ldejing@vmware.com>> > --- > datapath-windows/automake.mk | 2 + > datapath-windows/include/OvsDpInterfaceExt.h | 3 + > datapath-windows/ovsext/Actions.c | 10 + > datapath-windows/ovsext/Datapath.c | 46 +- > datapath-windows/ovsext/Meter.c | 538 +++++++++++++++++++ > datapath-windows/ovsext/Meter.h | 68 +++ > datapath-windows/ovsext/Switch.c | 8 + > datapath-windows/ovsext/Util.h | 1 + > datapath-windows/ovsext/ovsext.vcxproj | 2 + > lib/netlink-socket.c | 5 + > 10 files changed, 682 insertions(+), 1 deletion(-) > create mode 100644 datapath-windows/ovsext/Meter.c > create mode 100644 datapath-windows/ovsext/Meter.h > > diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk > index 60b3d6033..2d9f9a40d 100644 > --- a/datapath-windows/automake.mk > +++ b/datapath-windows/automake.mk > @@ -42,6 +42,8 @@ EXTRA_DIST += \ > datapath-windows/ovsext/Jhash.c \ > datapath-windows/ovsext/Jhash.h \ > datapath-windows/ovsext/Mpls.h \ > + datapath-windows/ovsext/Meter.c \ > + datapath-windows/ovsext/Meter.h \ > datapath-windows/ovsext/NetProto.h \ > datapath-windows/ovsext/Netlink/Netlink.c \ > datapath-windows/ovsext/Netlink/Netlink.h \ > diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h > index 5fd8000d1..045e4cbd6 100644 > --- a/datapath-windows/include/OvsDpInterfaceExt.h > +++ b/datapath-windows/include/OvsDpInterfaceExt.h > @@ -74,6 +74,9 @@ > #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) > #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) > > +/* Meter Family */ > +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) > + > #define OVS_WIN_NL_INVALID_MCGRP_ID 0 > #define OVS_WIN_NL_MCGRP_START_ID 100 > #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) > diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c > index 20de4db4c..63ee574c5 100644 > --- a/datapath-windows/ovsext/Actions.c > +++ b/datapath-windows/ovsext/Actions.c > @@ -23,6 +23,7 @@ > #include "Flow.h" > #include "Gre.h" > #include "Jhash.h" > +#include "Meter.h" > #include "Mpls.h" > #include "NetProto.h" > #include "Offload.h" > @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, > } > break; > } > + case OVS_ACTION_ATTR_METER: { > + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { > + OVS_LOG_INFO("Drop packet"); > + dropReason = L"Ovs-meter exceed max rate"; > + goto dropit; > + } > + > + break; > + } > case OVS_ACTION_ATTR_SAMPLE: > { > if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL > diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c > index fa994840a..37db5bd17 100644 > --- a/datapath-windows/ovsext/Datapath.c > +++ b/datapath-windows/ovsext/Datapath.c > @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, > OvsReadPacketCmdHandler, > OvsCtDeleteCmdHandler, > OvsCtDumpCmdHandler, > - OvsCtLimitHandler; > + OvsCtLimitHandler, > + OvsMeterFeatureProbe, > + OvsNewMeterCmdHandler, > + OvsMeterDestroy, > + OvsMeterGet; > > static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > UINT32 *replyLen); > @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { > .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) > }; > > +/* Netlink Meter family */ > +NETLINK_CMD nlMeterFamilyCmdOps[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .handler = OvsMeterFeatureProbe, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_SET, > + .handler = OvsNewMeterCmdHandler, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_GET, > + .handler = OvsMeterGet, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .handler = OvsMeterDestroy, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > +}; > + > +NETLINK_FAMILY nlMeterFamilyOps = { > + .name = OVS_METER_FAMILY, > + .id = OVS_WIN_NL_METER_FAMILY_ID, > + .version = OVS_METER_VERSION, > + .maxAttr = __OVS_METER_ATTR_MAX, > + .cmds = nlMeterFamilyCmdOps, > + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) > +}; > + > /* Netlink netdev family. */ > NETLINK_CMD nlNetdevFamilyCmdOps[] = { > { .cmd = OVS_WIN_NETDEV_CMD_GET, > @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, > case NFNL_TYPE_CT_DEL: > nlFamilyOps = &nlCtFamilyOps; > break; > + case OVS_WIN_NL_METER_FAMILY_ID: > + nlFamilyOps = &nlMeterFamilyOps; > + break; > case OVS_WIN_NL_CTRL_FAMILY_ID: > nlFamilyOps = &nlControlFamilyOps; > break; > diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c > new file mode 100644 > index 000000000..62f5f36c9 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.c > @@ -0,0 +1,538 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0> > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include "Meter.h" > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Flow.h" > +#include "PacketParser.h" > +#include "util.h" > + > +static PNDIS_RW_LOCK_EX meterGlobalTableLock; > +PLIST_ENTRY meterGlobalTable; > + > +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, > + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, > + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, > +}; > + > +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, > + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > +}; > + > +NTSTATUS > +OvsInitMeter(POVS_SWITCH_CONTEXT context) > +{ > + UINT32 maxEntry = METER_HASH_BUCKET_MAX; > + > + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); > + if (meterGlobalTableLock == NULL) { > + return STATUS_INSUFFICIENT_RESOURCES; > + } > + > + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, > + OVS_METER_TAG); > + if (!meterGlobalTable) { > + NdisFreeRWLock(meterGlobalTableLock); > + return NDIS_STATUS_RESOURCES; > + } > + > + for (UINT32 index = 0; index < maxEntry; index++) { > + InitializeListHead(&meterGlobalTable[index]); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) > +{ > + PNL_ATTR a = NULL; > + INT rem = 0; > + UINT32 bandMaxDelta = 0; > + UINT32 attrOffset = 0; > + DpMeterBand *band = NULL; > + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; > + UINT16 nBands = 0; > + > + band = meter->bands; > + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), > + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { > + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); > + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); > + if (!NlAttrParse(nlMsgHdr, > + attrOffset, > + NlAttrGetSize(a), > + bandPolicy, ARRAY_SIZE(bandPolicy), > + bandAttrs, ARRAY_SIZE(bandAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { > + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_RATE]) { > + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_BURST]) { > + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); > + } > + > + band->bucket = (band->burst_size + band->rate) * 1000; > + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); > + if (bandMaxDelta > meter->maxDelta) { > + meter->maxDelta = bandMaxDelta; > + } > + > + nBands++; > + band++; > + } > + > + meter->nBands = nBands; > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + DpMeter *meter = NULL; > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + LOCK_STATE_EX lockState; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + ASSERT(usrParamsCtx->inputBuffer != NULL); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); > + if (!meter) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + RtlZeroMemory(meter, sizeof(*meter)); > + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; > + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *)NlAttrData( > + meterAttrs[OVS_METER_ATTR_STATS]); > + } > + > + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { > + nlError = NL_ERROR_NOMSG; > + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], > + &(meter->link)); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { > + nlError = NL_ERROR_NOMEM; > + 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; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + UINT32 bandsOffset = 0; > + UINT32 bandAttrOffset = 0; > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); > + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); > + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, > + OVS_METER_BAND_TYPE_DROP)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + > + } > + NlMsgEndNested(&nlBuf, bandAttrOffset); > + NlMsgEndNested(&nlBuf, bandsOffset); > + > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return STATUS_SUCCESS; > +} > + > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) > +{ > + BOOLEAN ok = FALSE; > + UINT32 bandAttrOffset; > + UINT32 bandsOffset; > + > + /* Add meter element. */ > + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); > + if (!ok) { > + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); > + return ok; > + } > + > + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, > + (PCHAR)&(dpMeter->stats), > + sizeof(dpMeter->stats)); > + if (!ok) { > + OVS_LOG_ERROR("Could not add ovs meter stats."); > + return ok; > + } > + > + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); > + for (int index = 0; index < dpMeter->nBands; index++) { > + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); > + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, > + (PCHAR)&(dpMeter->bands[index].stats), > + sizeof(dpMeter->bands[index].stats)); > + NlMsgEndNested(nlBuf, bandAttrOffset); > + if (!ok) { > + break; > + } > + } > + > + NlMsgEndNested(nlBuf, bandsOffset); > + return ok; > +} > + > +NDIS_STATUS > +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + UINT32 meterId = 0; > + DpMeter *dpMeter = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + LOCK_STATE_EX lockState; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + > + /* Reply message header */ > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_WARN("Has not find %d associated meter", meterId); > + nlError = NL_ERROR_EXIST; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { > + nlError = NL_ERROR_NOMEM; > + OVS_LOG_ERROR("Could not build ovs meter reply msg."); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + LOCK_STATE_EX lockState; > + UINT32 meterId = 0; > + BOOLEAN ok; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_DEL, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { > + nlError = NL_ERROR_NOMEM; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + RemoveEntryList(&entry->link); > + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); > + break; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > + > + > +DpMeter* > +OvsMeterLookup(UINT32 meterId) > +{ > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + return entry; > + } > + } > + > + return NULL; > +} > + > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) > +{ > + DpMeter *dpMeter; > + DpMeterBand *band; > + UINT32 longDeltaMs; > + UINT32 deltaMs; > + UINT64 currentTime; > + LOCK_STATE_EX lockState; > + UINT32 cost; > + UINT32 bandExceededRate = 0; > + INT32 bandExceedIndex = -1; > + UINT64 maxBucketSize = 0; > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > + } > + > + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); > + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); [AS] Why 10000 here? Can you please add a comment for it? Because “currentTime” here represent count of 100-nanosecond intervals, thus divide 10000 to convert the unit to ms. > + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : > + longDeltaMs; > + dpMeter->used = currentTime; > + dpMeter->stats.n_packets += 1; > + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; > + for (int index = 0; index < dpMeter->nBands; index++) { > + band = &(dpMeter->bands[index]); > + maxBucketSize = (band->burst_size + band->rate) * 1000LL; > + band->bucket += deltaMs * band->rate; > + if (band->bucket > maxBucketSize) { > + band->bucket = maxBucketSize; > + } > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + } else if (band->rate > bandExceededRate) { > + bandExceededRate = band->rate; > + bandExceedIndex = index; > + } > + } > + > + if (bandExceedIndex >= 0) { > + band = &(dpMeter->bands[bandExceedIndex]); > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return TRUE; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > +} > \ No newline at end of file [AS] Nit. Please add new line. Will fix it. > diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h > new file mode 100644 > index 000000000..bc94489a5 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.h > @@ -0,0 +1,68 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0> > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVS_METER_H > +#define OVS_METER_H > + > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Netlink/Netlink.h" > +#include "Flow.h" > + > +#define OVS_MAX_BANDS 1 > +#define OVS_MAX_METERS 32 > +#define METER_HASH_BUCKET_MAX 1024 > + > +typedef struct _DpMeterBand { > + UINT32 type; > + UINT32 rate; > + UINT32 burst_size; > + UINT64 bucket; > + struct ovs_flow_stats stats; > +} DpMeterBand; > + > +typedef struct _DpMeter { > + LIST_ENTRY link; > + UINT32 id; > + UINT16 kbps:1; > + UINT16 keepStatus:1; > + UINT16 nBands; > + UINT32 maxDelta; > + UINT64 used; > + struct ovs_flow_stats stats; > + DpMeterBand bands[OVS_MAX_BANDS]; > +} DpMeter; > + > +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); > +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > +UINT32 *replyLen); > + > +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +DpMeter* OvsMeterLookup(UINT32 meterId); > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); > + > + > +#endif //OVS_METER_H > diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c > index 1ac4fa77c..088aee21a 100644 > --- a/datapath-windows/ovsext/Switch.c > +++ b/datapath-windows/ovsext/Switch.c > @@ -24,6 +24,7 @@ > #include "Switch.h" > #include "Vport.h" > #include "Event.h" > +#include "Meter.h" > #include "Flow.h" > #include "IpHelper.h" > #include "Oid.h" > @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, > goto create_switch_done; > } > > + status = OvsInitMeter(switchContext); > + if (status != STATUS_SUCCESS) { > + OvsUninitSwitchContext(switchContext); > + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); > + goto create_switch_done; > + } > + > *switchContextOut = switchContext; > > create_switch_done: > diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h > index f63a885a9..16903ed46 100644 > --- a/datapath-windows/ovsext/Util.h > +++ b/datapath-windows/ovsext/Util.h > @@ -38,6 +38,7 @@ > #define OVS_TUNFLT_POOL_TAG 'WSVO' > #define OVS_RECIRC_POOL_TAG 'CSVO' > #define OVS_CT_POOL_TAG 'CTVO' > +#define OVS_METER_TAG 'MEVO' > #define OVS_GENEVE_POOL_TAG 'GNVO' > #define OVS_IPFRAG_POOL_TAG 'FGVO' > > diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj > index 7a2cbd2de..6e54b71f9 100644 > --- a/datapath-windows/ovsext/ovsext.vcxproj > +++ b/datapath-windows/ovsext/ovsext.vcxproj > @@ -164,6 +164,7 @@ > <ClInclude Include="IpFragment.h" /> > <ClInclude Include="IpHelper.h" /> > <ClInclude Include="Jhash.h" /> > + <ClInclude Include="Meter.h" /> > <ClInclude Include="Mpls.h" /> > <ClInclude Include="Netlink/Netlink.h" /> > <ClInclude Include="Netlink/NetlinkBuf.h" /> > @@ -408,6 +409,7 @@ > <ClCompile Include="IpFragment.c" /> > <ClCompile Include="IpHelper.c" /> > <ClCompile Include="Jhash.c" /> > + <ClCompile Include="Meter.c" /> > <ClCompile Include="Netlink/Netlink.c" /> > <ClCompile Include="Netlink/NetlinkBuf.c" /> > <ClCompile Include="Datapath.c" /> > diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c > index 93c1fa561..80da20d9f 100644 > --- a/lib/netlink-socket.c > +++ b/lib/netlink-socket.c > @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, > family_name = OVS_FLOW_FAMILY; > family_version = OVS_FLOW_VERSION; > family_attrmax = OVS_FLOW_ATTR_MAX; > + } else if (!strcmp(name, OVS_METER_FAMILY)) { > + family_id = OVS_WIN_NL_METER_FAMILY_ID; > + family_name = OVS_METER_FAMILY; > + family_version = OVS_METER_VERSION; > + family_attrmax = __OVS_METER_ATTR_MAX; > } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { > family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; > family_name = OVS_WIN_NETDEV_FAMILY;
Hi Alin, How about the status of latest patch, please feel free to give some suggestions. Thanks Dejing From: Dejing Liu <ldejing@vmware.com> Date: Thursday, August 18, 2022 at 7:21 PM To: Alin-Gabriel Serdean <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org> Cc: Frank Guo <frankg@vmware.com>, Wilson Peng <pweisong@vmware.com>, Lina Li <linali@vmware.com> Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Hi Alin, I have sent all the latest patch to patchlist, hope you noticed it. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org> Date: Tuesday, August 16, 2022 at 6:19 PM To: Dejing Liu <ldejing@vmware.com>, 'Alin-Gabriel Serdean' <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org> Cc: Frank Guo <frankg@vmware.com>, Wilson Peng <pweisong@vmware.com>, Lina Li <linali@vmware.com> Subject: RE: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version ⚠ External Email Dejing, Looking forward to it. Alin. From: Dejing Liu <ldejing@vmware.com> Sent: Tuesday, August 16, 2022 1:00 PM To: Alin-Gabriel Serdean <aserdean@ovn.org>; dev@openvswitch.org Cc: Frank Guo <frankg@vmware.com>; Wilson Peng <pweisong@vmware.com>; Lina Li <linali@vmware.com> Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Hi Alin, I have made some modification according to your suggestion and new patch will come out soon. Regards Dejing From: Alin-Gabriel Serdean <aserdean@ovn.org<mailto:aserdean@ovn.org>> Date: Tuesday, August 9, 2022 at 9:22 PM To: dev@openvswitch.org<mailto:dev@openvswitch.org> <dev@openvswitch.org<mailto:dev@openvswitch.org>> Cc: Dejing Liu <ldejing@vmware.com<mailto:ldejing@vmware.com>> Subject: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action initial version Again patch looks good I only have one question. Please see bellow. > This patch implemented meter action, currently, meter only support drop > method and only support one band. The overall implementation is, when a > packet comes in, it will first lookup meter according to the meter id, > then get the band->rates and delta time since last access the same meter > from the meter struct. Add the multiply result(band->rates * delta_time) > to bucket, finally bucket minus the packet size, if the result larger > than zero, allow the packet go through, otherwise deny the packet go > through. > > Test case: > 1. Setting the size meter size 3M, then the bandwidth was limit > around 3M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\ > band=type=drop,rate=3000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip \ > actions=meter:2,normal" -O OpenFlow13 > 2. Setting the meter size 8M, then the bandwidth was limit > around 8M; > ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\ > kbps,band=type=drop,rate=8000 > ovs-ofctl add-flow br-test "table=0,priority=1,ip\ > actions=meter:2,normal" -O OpenFlow13 > > Signed-off-by: ldejing <ldejing@vmware.com<mailto:ldejing@vmware.com>> > --- > datapath-windows/automake.mk | 2 + > datapath-windows/include/OvsDpInterfaceExt.h | 3 + > datapath-windows/ovsext/Actions.c | 10 + > datapath-windows/ovsext/Datapath.c | 46 +- > datapath-windows/ovsext/Meter.c | 538 +++++++++++++++++++ > datapath-windows/ovsext/Meter.h | 68 +++ > datapath-windows/ovsext/Switch.c | 8 + > datapath-windows/ovsext/Util.h | 1 + > datapath-windows/ovsext/ovsext.vcxproj | 2 + > lib/netlink-socket.c | 5 + > 10 files changed, 682 insertions(+), 1 deletion(-) > create mode 100644 datapath-windows/ovsext/Meter.c > create mode 100644 datapath-windows/ovsext/Meter.h > > diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk > index 60b3d6033..2d9f9a40d 100644 > --- a/datapath-windows/automake.mk > +++ b/datapath-windows/automake.mk > @@ -42,6 +42,8 @@ EXTRA_DIST += \ > datapath-windows/ovsext/Jhash.c \ > datapath-windows/ovsext/Jhash.h \ > datapath-windows/ovsext/Mpls.h \ > + datapath-windows/ovsext/Meter.c \ > + datapath-windows/ovsext/Meter.h \ > datapath-windows/ovsext/NetProto.h \ > datapath-windows/ovsext/Netlink/Netlink.c \ > datapath-windows/ovsext/Netlink/Netlink.h \ > diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h > index 5fd8000d1..045e4cbd6 100644 > --- a/datapath-windows/include/OvsDpInterfaceExt.h > +++ b/datapath-windows/include/OvsDpInterfaceExt.h > @@ -74,6 +74,9 @@ > #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) > #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) > > +/* Meter Family */ > +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) > + > #define OVS_WIN_NL_INVALID_MCGRP_ID 0 > #define OVS_WIN_NL_MCGRP_START_ID 100 > #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) > diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c > index 20de4db4c..63ee574c5 100644 > --- a/datapath-windows/ovsext/Actions.c > +++ b/datapath-windows/ovsext/Actions.c > @@ -23,6 +23,7 @@ > #include "Flow.h" > #include "Gre.h" > #include "Jhash.h" > +#include "Meter.h" > #include "Mpls.h" > #include "NetProto.h" > #include "Offload.h" > @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, > } > break; > } > + case OVS_ACTION_ATTR_METER: { > + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { > + OVS_LOG_INFO("Drop packet"); > + dropReason = L"Ovs-meter exceed max rate"; > + goto dropit; > + } > + > + break; > + } > case OVS_ACTION_ATTR_SAMPLE: > { > if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL > diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c > index fa994840a..37db5bd17 100644 > --- a/datapath-windows/ovsext/Datapath.c > +++ b/datapath-windows/ovsext/Datapath.c > @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, > OvsReadPacketCmdHandler, > OvsCtDeleteCmdHandler, > OvsCtDumpCmdHandler, > - OvsCtLimitHandler; > + OvsCtLimitHandler, > + OvsMeterFeatureProbe, > + OvsNewMeterCmdHandler, > + OvsMeterDestroy, > + OvsMeterGet; > > static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > UINT32 *replyLen); > @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { > .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) > }; > > +/* Netlink Meter family */ > +NETLINK_CMD nlMeterFamilyCmdOps[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .handler = OvsMeterFeatureProbe, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_SET, > + .handler = OvsNewMeterCmdHandler, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_GET, > + .handler = OvsMeterGet, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .handler = OvsMeterDestroy, > + .supportedDevOp = OVS_TRANSACTION_DEV_OP | > + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, > + .validateDpIndex = FALSE > + }, > +}; > + > +NETLINK_FAMILY nlMeterFamilyOps = { > + .name = OVS_METER_FAMILY, > + .id = OVS_WIN_NL_METER_FAMILY_ID, > + .version = OVS_METER_VERSION, > + .maxAttr = __OVS_METER_ATTR_MAX, > + .cmds = nlMeterFamilyCmdOps, > + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) > +}; > + > /* Netlink netdev family. */ > NETLINK_CMD nlNetdevFamilyCmdOps[] = { > { .cmd = OVS_WIN_NETDEV_CMD_GET, > @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, > case NFNL_TYPE_CT_DEL: > nlFamilyOps = &nlCtFamilyOps; > break; > + case OVS_WIN_NL_METER_FAMILY_ID: > + nlFamilyOps = &nlMeterFamilyOps; > + break; > case OVS_WIN_NL_CTRL_FAMILY_ID: > nlFamilyOps = &nlControlFamilyOps; > break; > diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c > new file mode 100644 > index 000000000..62f5f36c9 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.c > @@ -0,0 +1,538 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0> > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include "Meter.h" > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Flow.h" > +#include "PacketParser.h" > +#include "util.h" > + > +static PNDIS_RW_LOCK_EX meterGlobalTableLock; > +PLIST_ENTRY meterGlobalTable; > + > +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, > + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, > + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, > +}; > + > +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, > + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, > + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), > + .maxLen = sizeof(struct ovs_flow_stats), > + .optional = TRUE }, > +}; > + > +NTSTATUS > +OvsInitMeter(POVS_SWITCH_CONTEXT context) > +{ > + UINT32 maxEntry = METER_HASH_BUCKET_MAX; > + > + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); > + if (meterGlobalTableLock == NULL) { > + return STATUS_INSUFFICIENT_RESOURCES; > + } > + > + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, > + OVS_METER_TAG); > + if (!meterGlobalTable) { > + NdisFreeRWLock(meterGlobalTableLock); > + return NDIS_STATUS_RESOURCES; > + } > + > + for (UINT32 index = 0; index < maxEntry; index++) { > + InitializeListHead(&meterGlobalTable[index]); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) > +{ > + PNL_ATTR a = NULL; > + INT rem = 0; > + UINT32 bandMaxDelta = 0; > + UINT32 attrOffset = 0; > + DpMeterBand *band = NULL; > + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; > + UINT16 nBands = 0; > + > + band = meter->bands; > + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), > + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { > + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); > + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); > + if (!NlAttrParse(nlMsgHdr, > + attrOffset, > + NlAttrGetSize(a), > + bandPolicy, ARRAY_SIZE(bandPolicy), > + bandAttrs, ARRAY_SIZE(bandAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { > + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_RATE]) { > + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); > + } > + > + if (bandAttrs[OVS_BAND_ATTR_BURST]) { > + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); > + } > + > + band->bucket = (band->burst_size + band->rate) * 1000; > + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); > + if (bandMaxDelta > meter->maxDelta) { > + meter->maxDelta = bandMaxDelta; > + } > + > + nBands++; > + band++; > + } > + > + meter->nBands = nBands; > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + DpMeter *meter = NULL; > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + LOCK_STATE_EX lockState; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + ASSERT(usrParamsCtx->inputBuffer != NULL); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); > + if (!meter) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + RtlZeroMemory(meter, sizeof(*meter)); > + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; > + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *)NlAttrData( > + meterAttrs[OVS_METER_ATTR_STATS]); > + } > + > + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { > + nlError = NL_ERROR_NOMSG; > + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], > + &(meter->link)); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { > + nlError = NL_ERROR_NOMEM; > + 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; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + UINT32 bandsOffset = 0; > + UINT32 bandAttrOffset = 0; > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); > + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); > + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, > + OVS_METER_BAND_TYPE_DROP)) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + > + } > + NlMsgEndNested(&nlBuf, bandAttrOffset); > + NlMsgEndNested(&nlBuf, bandsOffset); > + > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return STATUS_SUCCESS; > +} > + > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) > +{ > + BOOLEAN ok = FALSE; > + UINT32 bandAttrOffset; > + UINT32 bandsOffset; > + > + /* Add meter element. */ > + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); > + if (!ok) { > + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); > + return ok; > + } > + > + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, > + (PCHAR)&(dpMeter->stats), > + sizeof(dpMeter->stats)); > + if (!ok) { > + OVS_LOG_ERROR("Could not add ovs meter stats."); > + return ok; > + } > + > + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); > + for (int index = 0; index < dpMeter->nBands; index++) { > + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); > + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, > + (PCHAR)&(dpMeter->bands[index].stats), > + sizeof(dpMeter->bands[index].stats)); > + NlMsgEndNested(nlBuf, bandAttrOffset); > + if (!ok) { > + break; > + } > + } > + > + NlMsgEndNested(nlBuf, bandsOffset); > + return ok; > +} > + > +NDIS_STATUS > +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + UINT32 meterId = 0; > + DpMeter *dpMeter = NULL; > + BOOLEAN ok = FALSE; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + LOCK_STATE_EX lockState; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + > + /* Reply message header */ > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_GET, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMSG; > + goto Done; > + } > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_WARN("Has not find %d associated meter", meterId); > + nlError = NL_ERROR_EXIST; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { > + nlError = NL_ERROR_NOMEM; > + OVS_LOG_ERROR("Could not build ovs meter reply msg."); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > +NDIS_STATUS > +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen) > +{ > + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; > + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); > + POVS_HDR ovsHdr = &(msgIn->ovsHdr); > + PNL_MSG_HDR nlMsgOutHdr = NULL; > + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; > + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); > + LOCK_STATE_EX lockState; > + UINT32 meterId = 0; > + BOOLEAN ok; > + NL_BUFFER nlBuf; > + NL_ERROR nlError = NL_ERROR_SUCCESS; > + > + if (!NlAttrParse((PNL_MSG_HDR)msgIn, > + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, > + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), > + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), > + meterAttrs, ARRAY_SIZE(meterAttrs))) { > + return STATUS_INVALID_PARAMETER; > + } > + > + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); > + > + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); > + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); > + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, > + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, > + genlMsgHdr->cmd, OVS_METER_CMD_DEL, > + ovsHdr->dp_ifindex); > + if (!ok) { > + nlError = NL_ERROR_NOMEM; > + goto Done; > + } > + > + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { > + nlError = NL_ERROR_NOMEM; > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + goto Done; > + } > + RemoveEntryList(&entry->link); > + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); > + break; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); > + NlMsgAlignSize(nlMsgOutHdr); > + *replyLen += NlMsgSize(nlMsgOutHdr); > + > +Done: > + if (nlError != NL_ERROR_SUCCESS) { > + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) > + usrParamsCtx->outputBuffer; > + > + ASSERT(msgError); > + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); > + ASSERT(*replyLen != 0); > + } > + > + return NDIS_STATUS_SUCCESS; > +} > + > + > + > +DpMeter* > +OvsMeterLookup(UINT32 meterId) > +{ > + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; > + PLIST_ENTRY link, next; > + DpMeter *entry = NULL; > + > + LIST_FORALL_SAFE(head, link, next) { > + entry = CONTAINING_RECORD(link, DpMeter, link); > + if (entry->id == meterId) { > + return entry; > + } > + } > + > + return NULL; > +} > + > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) > +{ > + DpMeter *dpMeter; > + DpMeterBand *band; > + UINT32 longDeltaMs; > + UINT32 deltaMs; > + UINT64 currentTime; > + LOCK_STATE_EX lockState; > + UINT32 cost; > + UINT32 bandExceededRate = 0; > + INT32 bandExceedIndex = -1; > + UINT64 maxBucketSize = 0; > + > + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); > + dpMeter = OvsMeterLookup(meterId); > + if (!dpMeter) { > + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > + } > + > + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); > + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); [AS] Why 10000 here? Can you please add a comment for it? Because “currentTime” here represent count of 100-nanosecond intervals, thus divide 10000 to convert the unit to ms. > + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : > + longDeltaMs; > + dpMeter->used = currentTime; > + dpMeter->stats.n_packets += 1; > + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; > + for (int index = 0; index < dpMeter->nBands; index++) { > + band = &(dpMeter->bands[index]); > + maxBucketSize = (band->burst_size + band->rate) * 1000LL; > + band->bucket += deltaMs * band->rate; > + if (band->bucket > maxBucketSize) { > + band->bucket = maxBucketSize; > + } > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + } else if (band->rate > bandExceededRate) { > + bandExceededRate = band->rate; > + bandExceedIndex = index; > + } > + } > + > + if (bandExceedIndex >= 0) { > + band = &(dpMeter->bands[bandExceedIndex]); > + band->stats.n_packets += 1; > + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return TRUE; > + } > + } > + > + NdisReleaseRWLock(meterGlobalTableLock, &lockState); > + return FALSE; > +} > \ No newline at end of file [AS] Nit. Please add new line. Will fix it. > diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h > new file mode 100644 > index 000000000..bc94489a5 > --- /dev/null > +++ b/datapath-windows/ovsext/Meter.h > @@ -0,0 +1,68 @@ > +/* > + * Copyright (c) 2022 VMware, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0> > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVS_METER_H > +#define OVS_METER_H > + > +#include "precomp.h" > +#include "Switch.h" > +#include "User.h" > +#include "Datapath.h" > +#include "Event.h" > +#include "NetProto.h" > +#include "Netlink/Netlink.h" > +#include "Flow.h" > + > +#define OVS_MAX_BANDS 1 > +#define OVS_MAX_METERS 32 > +#define METER_HASH_BUCKET_MAX 1024 > + > +typedef struct _DpMeterBand { > + UINT32 type; > + UINT32 rate; > + UINT32 burst_size; > + UINT64 bucket; > + struct ovs_flow_stats stats; > +} DpMeterBand; > + > +typedef struct _DpMeter { > + LIST_ENTRY link; > + UINT32 id; > + UINT16 kbps:1; > + UINT16 keepStatus:1; > + UINT16 nBands; > + UINT32 maxDelta; > + UINT64 used; > + struct ovs_flow_stats stats; > + DpMeterBand bands[OVS_MAX_BANDS]; > +} DpMeter; > + > +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); > +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > +UINT32 *replyLen); > + > +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, > + UINT32 *replyLen); > +DpMeter* OvsMeterLookup(UINT32 meterId); > +BOOLEAN > +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); > +BOOLEAN > +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); > + > + > +#endif //OVS_METER_H > diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c > index 1ac4fa77c..088aee21a 100644 > --- a/datapath-windows/ovsext/Switch.c > +++ b/datapath-windows/ovsext/Switch.c > @@ -24,6 +24,7 @@ > #include "Switch.h" > #include "Vport.h" > #include "Event.h" > +#include "Meter.h" > #include "Flow.h" > #include "IpHelper.h" > #include "Oid.h" > @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, > goto create_switch_done; > } > > + status = OvsInitMeter(switchContext); > + if (status != STATUS_SUCCESS) { > + OvsUninitSwitchContext(switchContext); > + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); > + goto create_switch_done; > + } > + > *switchContextOut = switchContext; > > create_switch_done: > diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h > index f63a885a9..16903ed46 100644 > --- a/datapath-windows/ovsext/Util.h > +++ b/datapath-windows/ovsext/Util.h > @@ -38,6 +38,7 @@ > #define OVS_TUNFLT_POOL_TAG 'WSVO' > #define OVS_RECIRC_POOL_TAG 'CSVO' > #define OVS_CT_POOL_TAG 'CTVO' > +#define OVS_METER_TAG 'MEVO' > #define OVS_GENEVE_POOL_TAG 'GNVO' > #define OVS_IPFRAG_POOL_TAG 'FGVO' > > diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj > index 7a2cbd2de..6e54b71f9 100644 > --- a/datapath-windows/ovsext/ovsext.vcxproj > +++ b/datapath-windows/ovsext/ovsext.vcxproj > @@ -164,6 +164,7 @@ > <ClInclude Include="IpFragment.h" /> > <ClInclude Include="IpHelper.h" /> > <ClInclude Include="Jhash.h" /> > + <ClInclude Include="Meter.h" /> > <ClInclude Include="Mpls.h" /> > <ClInclude Include="Netlink/Netlink.h" /> > <ClInclude Include="Netlink/NetlinkBuf.h" /> > @@ -408,6 +409,7 @@ > <ClCompile Include="IpFragment.c" /> > <ClCompile Include="IpHelper.c" /> > <ClCompile Include="Jhash.c" /> > + <ClCompile Include="Meter.c" /> > <ClCompile Include="Netlink/Netlink.c" /> > <ClCompile Include="Netlink/NetlinkBuf.c" /> > <ClCompile Include="Datapath.c" /> > diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c > index 93c1fa561..80da20d9f 100644 > --- a/lib/netlink-socket.c > +++ b/lib/netlink-socket.c > @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, > family_name = OVS_FLOW_FAMILY; > family_version = OVS_FLOW_VERSION; > family_attrmax = OVS_FLOW_ATTR_MAX; > + } else if (!strcmp(name, OVS_METER_FAMILY)) { > + family_id = OVS_WIN_NL_METER_FAMILY_ID; > + family_name = OVS_METER_FAMILY; > + family_version = OVS_METER_VERSION; > + family_attrmax = __OVS_METER_ATTR_MAX; > } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { > family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; > family_name = OVS_WIN_NETDEV_FAMILY;
diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk index 60b3d6033..2d9f9a40d 100644 --- a/datapath-windows/automake.mk +++ b/datapath-windows/automake.mk @@ -42,6 +42,8 @@ EXTRA_DIST += \ datapath-windows/ovsext/Jhash.c \ datapath-windows/ovsext/Jhash.h \ datapath-windows/ovsext/Mpls.h \ + datapath-windows/ovsext/Meter.c \ + datapath-windows/ovsext/Meter.h \ datapath-windows/ovsext/NetProto.h \ datapath-windows/ovsext/Netlink/Netlink.c \ datapath-windows/ovsext/Netlink/Netlink.h \ diff --git a/datapath-windows/include/OvsDpInterfaceExt.h b/datapath-windows/include/OvsDpInterfaceExt.h index 5fd8000d1..045e4cbd6 100644 --- a/datapath-windows/include/OvsDpInterfaceExt.h +++ b/datapath-windows/include/OvsDpInterfaceExt.h @@ -74,6 +74,9 @@ #define OVS_WIN_NL_CT_FAMILY_ID (NLMSG_MIN_TYPE + 7) #define OVS_WIN_NL_CTLIMIT_FAMILY_ID (NLMSG_MIN_TYPE + 8) +/* Meter Family */ +#define OVS_WIN_NL_METER_FAMILY_ID (NLMSG_MIN_TYPE + 9) + #define OVS_WIN_NL_INVALID_MCGRP_ID 0 #define OVS_WIN_NL_MCGRP_START_ID 100 #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c index 20de4db4c..63ee574c5 100644 --- a/datapath-windows/ovsext/Actions.c +++ b/datapath-windows/ovsext/Actions.c @@ -23,6 +23,7 @@ #include "Flow.h" #include "Gre.h" #include "Jhash.h" +#include "Meter.h" #include "Mpls.h" #include "NetProto.h" #include "Offload.h" @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, } break; } + case OVS_ACTION_ATTR_METER: { + if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) { + OVS_LOG_INFO("Drop packet"); + dropReason = L"Ovs-meter exceed max rate"; + goto dropit; + } + + break; + } case OVS_ACTION_ATTR_SAMPLE: { if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c index fa994840a..37db5bd17 100644 --- a/datapath-windows/ovsext/Datapath.c +++ b/datapath-windows/ovsext/Datapath.c @@ -100,7 +100,11 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, OvsReadPacketCmdHandler, OvsCtDeleteCmdHandler, OvsCtDumpCmdHandler, - OvsCtLimitHandler; + OvsCtLimitHandler, + OvsMeterFeatureProbe, + OvsNewMeterCmdHandler, + OvsMeterDestroy, + OvsMeterGet; static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = { .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps) }; +/* Netlink Meter family */ +NETLINK_CMD nlMeterFamilyCmdOps[] = { + { .cmd = OVS_METER_CMD_FEATURES, + .handler = OvsMeterFeatureProbe, + .supportedDevOp = OVS_TRANSACTION_DEV_OP | + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, + .validateDpIndex = FALSE + }, + { .cmd = OVS_METER_CMD_SET, + .handler = OvsNewMeterCmdHandler, + .supportedDevOp = OVS_TRANSACTION_DEV_OP | + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, + .validateDpIndex = FALSE + }, + { .cmd = OVS_METER_CMD_GET, + .handler = OvsMeterGet, + .supportedDevOp = OVS_TRANSACTION_DEV_OP | + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, + .validateDpIndex = FALSE + }, + { .cmd = OVS_METER_CMD_DEL, + .handler = OvsMeterDestroy, + .supportedDevOp = OVS_TRANSACTION_DEV_OP | + OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, + .validateDpIndex = FALSE + }, +}; + +NETLINK_FAMILY nlMeterFamilyOps = { + .name = OVS_METER_FAMILY, + .id = OVS_WIN_NL_METER_FAMILY_ID, + .version = OVS_METER_VERSION, + .maxAttr = __OVS_METER_ATTR_MAX, + .cmds = nlMeterFamilyCmdOps, + .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps) +}; + /* Netlink netdev family. */ NETLINK_CMD nlNetdevFamilyCmdOps[] = { { .cmd = OVS_WIN_NETDEV_CMD_GET, @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, case NFNL_TYPE_CT_DEL: nlFamilyOps = &nlCtFamilyOps; break; + case OVS_WIN_NL_METER_FAMILY_ID: + nlFamilyOps = &nlMeterFamilyOps; + break; case OVS_WIN_NL_CTRL_FAMILY_ID: nlFamilyOps = &nlControlFamilyOps; break; diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c new file mode 100644 index 000000000..62f5f36c9 --- /dev/null +++ b/datapath-windows/ovsext/Meter.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2022 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Meter.h" +#include "precomp.h" +#include "Switch.h" +#include "User.h" +#include "Datapath.h" +#include "Event.h" +#include "NetProto.h" +#include "Flow.h" +#include "PacketParser.h" +#include "util.h" + +static PNDIS_RW_LOCK_EX meterGlobalTableLock; +PLIST_ENTRY meterGlobalTable; + +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = { + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, }, + [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE }, + [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), + .maxLen = sizeof(struct ovs_flow_stats), + .optional = TRUE }, + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE }, + [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE }, + [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE }, + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE }, + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32, .optional = TRUE }, +}; + +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = { + [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, + [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE }, + [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32, .optional = TRUE }, + [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats), + .maxLen = sizeof(struct ovs_flow_stats), + .optional = TRUE }, +}; + +NTSTATUS +OvsInitMeter(POVS_SWITCH_CONTEXT context) +{ + UINT32 maxEntry = METER_HASH_BUCKET_MAX; + + meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle); + if (meterGlobalTableLock == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * maxEntry, + OVS_METER_TAG); + if (!meterGlobalTable) { + NdisFreeRWLock(meterGlobalTableLock); + return NDIS_STATUS_RESOURCES; + } + + for (UINT32 index = 0; index < maxEntry; index++) { + InitializeListHead(&meterGlobalTable[index]); + } + + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR nlMsgHdr) +{ + PNL_ATTR a = NULL; + INT rem = 0; + UINT32 bandMaxDelta = 0; + UINT32 attrOffset = 0; + DpMeterBand *band = NULL; + PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1]; + UINT16 nBands = 0; + + band = meter->bands; + NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]), + NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) { + RtlZeroMemory(bandAttrs, sizeof(bandAttrs)); + attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr); + if (!NlAttrParse(nlMsgHdr, + attrOffset, + NlAttrGetSize(a), + bandPolicy, ARRAY_SIZE(bandPolicy), + bandAttrs, ARRAY_SIZE(bandAttrs))) { + return STATUS_INVALID_PARAMETER; + } + + if (bandAttrs[OVS_BAND_ATTR_TYPE]) { + band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]); + } + + if (bandAttrs[OVS_BAND_ATTR_RATE]) { + band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]); + } + + if (bandAttrs[OVS_BAND_ATTR_BURST]) { + band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]); + } + + band->bucket = (band->burst_size + band->rate) * 1000; + bandMaxDelta = (UINT32)((band->bucket / band->rate) / 10); + if (bandMaxDelta > meter->maxDelta) { + meter->maxDelta = bandMaxDelta; + } + + nBands++; + band++; + } + + meter->nBands = nBands; + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen) +{ + DpMeter *meter = NULL; + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + POVS_HDR ovsHdr = &(msgIn->ovsHdr); + LOCK_STATE_EX lockState; + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; + ASSERT(usrParamsCtx->inputBuffer != NULL); + PNL_MSG_HDR nlMsgOutHdr = NULL; + NL_BUFFER nlBuf; + NL_ERROR nlError = NL_ERROR_SUCCESS; + + if (!NlAttrParse((PNL_MSG_HDR)msgIn, + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), + meterAttrs, ARRAY_SIZE(meterAttrs))) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG); + if (!meter) { + nlError = NL_ERROR_NOMEM; + goto Done; + } + + RtlZeroMemory(meter, sizeof(*meter)); + meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); + meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0; + meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0; + if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) { + meter->stats = *(struct ovs_flow_stats *)NlAttrData( + meterAttrs[OVS_METER_ATTR_STATS]); + } + + if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != NDIS_STATUS_SUCCESS) { + nlError = NL_ERROR_NOMSG; + OvsFreeMemoryWithTag(meter, OVS_METER_TAG); + goto Done; + } + + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); + InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 1)], + &(meter->link)); + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); + if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, + genlMsgHdr->cmd, OVS_METER_CMD_GET, + ovsHdr->dp_ifindex)) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + if (!buildOvsMeterReplyMsg(&nlBuf, meter)) { + nlError = NL_ERROR_NOMEM; + 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; + + ASSERT(msgError); + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); + ASSERT(*replyLen != 0); + } + + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen) +{ + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); + POVS_HDR ovsHdr = &(msgIn->ovsHdr); + PNL_MSG_HDR nlMsgOutHdr = NULL; + BOOLEAN ok = FALSE; + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + NL_BUFFER nlBuf; + NL_ERROR nlError = NL_ERROR_SUCCESS; + UINT32 bandsOffset = 0; + UINT32 bandAttrOffset = 0; + + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, + genlMsgHdr->cmd, OVS_METER_CMD_FEATURES, + ovsHdr->dp_ifindex); + if (!ok) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS); + bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC); + if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE, + OVS_METER_BAND_TYPE_DROP)) { + nlError = NL_ERROR_NOMSG; + goto Done; + + } + NlMsgEndNested(&nlBuf, bandAttrOffset); + NlMsgEndNested(&nlBuf, bandsOffset); + + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); + NlMsgAlignSize(nlMsgOutHdr); + *replyLen += NlMsgSize(nlMsgOutHdr); + +Done: + if (nlError != NL_ERROR_SUCCESS) { + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) + usrParamsCtx->outputBuffer; + + ASSERT(msgError); + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); + ASSERT(*replyLen != 0); + } + + return STATUS_SUCCESS; +} + +BOOLEAN +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter) +{ + BOOLEAN ok = FALSE; + UINT32 bandAttrOffset; + UINT32 bandsOffset; + + /* Add meter element. */ + ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id); + if (!ok) { + OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id); + return ok; + } + + ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS, + (PCHAR)&(dpMeter->stats), + sizeof(dpMeter->stats)); + if (!ok) { + OVS_LOG_ERROR("Could not add ovs meter stats."); + return ok; + } + + bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS); + for (int index = 0; index < dpMeter->nBands; index++) { + bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC); + ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS, + (PCHAR)&(dpMeter->bands[index].stats), + sizeof(dpMeter->bands[index].stats)); + NlMsgEndNested(nlBuf, bandAttrOffset); + if (!ok) { + break; + } + } + + NlMsgEndNested(nlBuf, bandsOffset); + return ok; +} + +NDIS_STATUS +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen) +{ + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); + POVS_HDR ovsHdr = &(msgIn->ovsHdr); + PNL_MSG_HDR nlMsgOutHdr = NULL; + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; + UINT32 meterId = 0; + DpMeter *dpMeter = NULL; + BOOLEAN ok = FALSE; + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + NL_BUFFER nlBuf; + NL_ERROR nlError = NL_ERROR_SUCCESS; + LOCK_STATE_EX lockState; + + if (!NlAttrParse((PNL_MSG_HDR)msgIn, + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), + meterAttrs, ARRAY_SIZE(meterAttrs))) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); + + /* Reply message header */ + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, + genlMsgHdr->cmd, OVS_METER_CMD_GET, + ovsHdr->dp_ifindex); + if (!ok) { + nlError = NL_ERROR_NOMSG; + goto Done; + } + + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); + dpMeter = OvsMeterLookup(meterId); + if (!dpMeter) { + OVS_LOG_WARN("Has not find %d associated meter", meterId); + nlError = NL_ERROR_EXIST; + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + goto Done; + } + + if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) { + nlError = NL_ERROR_NOMEM; + OVS_LOG_ERROR("Could not build ovs meter reply msg."); + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + goto Done; + } + + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); + NlMsgAlignSize(nlMsgOutHdr); + *replyLen += NlMsgSize(nlMsgOutHdr); + +Done: + if (nlError != NL_ERROR_SUCCESS) { + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) + usrParamsCtx->outputBuffer; + + ASSERT(msgError); + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); + ASSERT(*replyLen != 0); + } + + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen) +{ + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); + POVS_HDR ovsHdr = &(msgIn->ovsHdr); + PNL_MSG_HDR nlMsgOutHdr = NULL; + PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)]; + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + LOCK_STATE_EX lockState; + UINT32 meterId = 0; + BOOLEAN ok; + NL_BUFFER nlBuf; + NL_ERROR nlError = NL_ERROR_SUCCESS; + + if (!NlAttrParse((PNL_MSG_HDR)msgIn, + NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, + NlMsgAttrsLen((PNL_MSG_HDR)msgIn), + nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy), + meterAttrs, ARRAY_SIZE(meterAttrs))) { + return STATUS_INVALID_PARAMETER; + } + + NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); + + meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]); + nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); + ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, + nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, + genlMsgHdr->cmd, OVS_METER_CMD_DEL, + ovsHdr->dp_ifindex); + if (!ok) { + nlError = NL_ERROR_NOMEM; + goto Done; + } + + NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0); + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; + PLIST_ENTRY link, next; + DpMeter *entry = NULL; + + LIST_FORALL_SAFE(head, link, next) { + entry = CONTAINING_RECORD(link, DpMeter, link); + if (entry->id == meterId) { + if (!buildOvsMeterReplyMsg(&nlBuf, entry)) { + nlError = NL_ERROR_NOMEM; + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + goto Done; + } + RemoveEntryList(&entry->link); + OvsFreeMemoryWithTag(entry, OVS_METER_TAG); + break; + } + } + + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); + NlMsgAlignSize(nlMsgOutHdr); + *replyLen += NlMsgSize(nlMsgOutHdr); + +Done: + if (nlError != NL_ERROR_SUCCESS) { + POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) + usrParamsCtx->outputBuffer; + + ASSERT(msgError); + NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); + ASSERT(*replyLen != 0); + } + + return NDIS_STATUS_SUCCESS; +} + + + +DpMeter* +OvsMeterLookup(UINT32 meterId) +{ + PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 1)]; + PLIST_ENTRY link, next; + DpMeter *entry = NULL; + + LIST_FORALL_SAFE(head, link, next) { + entry = CONTAINING_RECORD(link, DpMeter, link); + if (entry->id == meterId) { + return entry; + } + } + + return NULL; +} + +BOOLEAN +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId) +{ + DpMeter *dpMeter; + DpMeterBand *band; + UINT32 longDeltaMs; + UINT32 deltaMs; + UINT64 currentTime; + LOCK_STATE_EX lockState; + UINT32 cost; + UINT32 bandExceededRate = 0; + INT32 bandExceedIndex = -1; + UINT64 maxBucketSize = 0; + + NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0); + dpMeter = OvsMeterLookup(meterId); + if (!dpMeter) { + OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId); + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + return FALSE; + } + + NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); + longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000); + deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta : + longDeltaMs; + dpMeter->used = currentTime; + dpMeter->stats.n_packets += 1; + dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); + cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000; + for (int index = 0; index < dpMeter->nBands; index++) { + band = &(dpMeter->bands[index]); + maxBucketSize = (band->burst_size + band->rate) * 1000LL; + band->bucket += deltaMs * band->rate; + if (band->bucket > maxBucketSize) { + band->bucket = maxBucketSize; + } + + if (band->bucket >= cost) { + band->bucket -= cost; + band->stats.n_packets += 1; + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); + } else if (band->rate > bandExceededRate) { + bandExceededRate = band->rate; + bandExceedIndex = index; + } + } + + if (bandExceedIndex >= 0) { + band = &(dpMeter->bands[bandExceedIndex]); + band->stats.n_packets += 1; + band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl); + if (band->type == OVS_METER_BAND_TYPE_DROP) { + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + return TRUE; + } + } + + NdisReleaseRWLock(meterGlobalTableLock, &lockState); + return FALSE; +} \ No newline at end of file diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h new file mode 100644 index 000000000..bc94489a5 --- /dev/null +++ b/datapath-windows/ovsext/Meter.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVS_METER_H +#define OVS_METER_H + +#include "precomp.h" +#include "Switch.h" +#include "User.h" +#include "Datapath.h" +#include "Event.h" +#include "NetProto.h" +#include "Netlink/Netlink.h" +#include "Flow.h" + +#define OVS_MAX_BANDS 1 +#define OVS_MAX_METERS 32 +#define METER_HASH_BUCKET_MAX 1024 + +typedef struct _DpMeterBand { + UINT32 type; + UINT32 rate; + UINT32 burst_size; + UINT64 bucket; + struct ovs_flow_stats stats; +} DpMeterBand; + +typedef struct _DpMeter { + LIST_ENTRY link; + UINT32 id; + UINT16 kbps:1; + UINT16 keepStatus:1; + UINT16 nBands; + UINT32 maxDelta; + UINT64 used; + struct ovs_flow_stats stats; + DpMeterBand bands[OVS_MAX_BANDS]; +} DpMeter; + +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context); +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen); +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx, +UINT32 *replyLen); + +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen); +DpMeter* OvsMeterLookup(UINT32 meterId); +BOOLEAN +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId); +BOOLEAN +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter); + + +#endif //OVS_METER_H diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c index 1ac4fa77c..088aee21a 100644 --- a/datapath-windows/ovsext/Switch.c +++ b/datapath-windows/ovsext/Switch.c @@ -24,6 +24,7 @@ #include "Switch.h" #include "Vport.h" #include "Event.h" +#include "Meter.h" #include "Flow.h" #include "IpHelper.h" #include "Oid.h" @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, goto create_switch_done; } + status = OvsInitMeter(switchContext); + if (status != STATUS_SUCCESS) { + OvsUninitSwitchContext(switchContext); + OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter."); + goto create_switch_done; + } + *switchContextOut = switchContext; create_switch_done: diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h index f63a885a9..16903ed46 100644 --- a/datapath-windows/ovsext/Util.h +++ b/datapath-windows/ovsext/Util.h @@ -38,6 +38,7 @@ #define OVS_TUNFLT_POOL_TAG 'WSVO' #define OVS_RECIRC_POOL_TAG 'CSVO' #define OVS_CT_POOL_TAG 'CTVO' +#define OVS_METER_TAG 'MEVO' #define OVS_GENEVE_POOL_TAG 'GNVO' #define OVS_IPFRAG_POOL_TAG 'FGVO' diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj index 7a2cbd2de..6e54b71f9 100644 --- a/datapath-windows/ovsext/ovsext.vcxproj +++ b/datapath-windows/ovsext/ovsext.vcxproj @@ -164,6 +164,7 @@ <ClInclude Include="IpFragment.h" /> <ClInclude Include="IpHelper.h" /> <ClInclude Include="Jhash.h" /> + <ClInclude Include="Meter.h" /> <ClInclude Include="Mpls.h" /> <ClInclude Include="Netlink/Netlink.h" /> <ClInclude Include="Netlink/NetlinkBuf.h" /> @@ -408,6 +409,7 @@ <ClCompile Include="IpFragment.c" /> <ClCompile Include="IpHelper.c" /> <ClCompile Include="Jhash.c" /> + <ClCompile Include="Meter.c" /> <ClCompile Include="Netlink/Netlink.c" /> <ClCompile Include="Netlink/NetlinkBuf.c" /> <ClCompile Include="Datapath.c" /> diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c index 93c1fa561..80da20d9f 100644 --- a/lib/netlink-socket.c +++ b/lib/netlink-socket.c @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr **attrs, family_name = OVS_FLOW_FAMILY; family_version = OVS_FLOW_VERSION; family_attrmax = OVS_FLOW_ATTR_MAX; + } else if (!strcmp(name, OVS_METER_FAMILY)) { + family_id = OVS_WIN_NL_METER_FAMILY_ID; + family_name = OVS_METER_FAMILY; + family_version = OVS_METER_VERSION; + family_attrmax = __OVS_METER_ATTR_MAX; } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; family_name = OVS_WIN_NETDEV_FAMILY;