2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

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>
This commit is contained in:
ldejing 2022-08-18 19:11:00 +08:00 committed by Alin-Gabriel Serdean
parent 7af5c33c16
commit b26015c33f
10 changed files with 684 additions and 1 deletions

View File

@ -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 \

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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'

View File

@ -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" />

View File

@ -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;