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:
parent
7af5c33c16
commit
b26015c33f
@ -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 \
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
540
datapath-windows/ovsext/Meter.c
Normal file
540
datapath-windows/ovsext/Meter.c
Normal 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 *)¤tTime);
|
||||
/* 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;
|
||||
}
|
68
datapath-windows/ovsext/Meter.h
Normal file
68
datapath-windows/ovsext/Meter.h
Normal 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
|
@ -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;
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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" />
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user