summaryrefslogtreecommitdiff
path: root/datapath-windows
diff options
context:
space:
mode:
authorldejing <ldejing@vmware.com>2022-08-18 19:11:00 +0800
committerAlin-Gabriel Serdean <aserdean@ovn.org>2022-09-20 02:48:44 +0300
commitb26015c33fe420399fff1c9f35d1e3204c441954 (patch)
tree7f5ea882a645a452a41da75e4834caf7bfe1fbd0 /datapath-windows
parent7af5c33c1629b309cbcbe3b6c9c3bd6d3b4c0abf (diff)
downloadopenvswitch-b26015c33fe420399fff1c9f35d1e3204c441954.tar.gz
datapath-windows: support meter action initial version
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> Signed-off-by: Alin-Gabriel Serdean <aserdean@ovn.org>
Diffstat (limited to 'datapath-windows')
-rw-r--r--datapath-windows/automake.mk2
-rw-r--r--datapath-windows/include/OvsDpInterfaceExt.h3
-rw-r--r--datapath-windows/ovsext/Actions.c10
-rw-r--r--datapath-windows/ovsext/Datapath.c46
-rw-r--r--datapath-windows/ovsext/Meter.c540
-rw-r--r--datapath-windows/ovsext/Meter.h68
-rw-r--r--datapath-windows/ovsext/Switch.c8
-rw-r--r--datapath-windows/ovsext/Util.h1
-rw-r--r--datapath-windows/ovsext/ovsext.vcxproj2
9 files changed, 679 insertions, 1 deletions
diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
index a3fe909a4..ea320e732 100644
--- a/datapath-windows/automake.mk
+++ b/datapath-windows/automake.mk
@@ -44,6 +44,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 884fce4e3..2f44086b4 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"
@@ -2503,6 +2504,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..d99631ba0
--- /dev/null
+++ b/datapath-windows/ovsext/Meter.c
@@ -0,0 +1,540 @@
+/*
+ * 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 *)&currentTime);
+ /* currentTime represent count of 100-nanosecond intervals, to convert it to
+ * ms, we need to divide 10000. */
+ 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;
+}
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 a40624bab..6aa5abc28 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"
@@ -246,6 +247,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
OVS_LOG_ERROR("Exit: Failed to initialize Ip6 Fragment");
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;
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index 4d9ce4210..3670e2e4b 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'
#define OVS_IP6FRAG_POOL_TAG 'F6VO'
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index 691a05706..8ee56aa00 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -165,6 +165,7 @@
<ClInclude Include="Ip6Fragment.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" />
@@ -410,6 +411,7 @@
<ClCompile Include="Ip6Fragment.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" />