diff mbox series

[ovs-dev,v1,1/1] datapath-windows: support meter action initial version

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

Checks

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

Commit Message

Wilson Peng July 12, 2022, 2:02 a.m. UTC
From: ldejing <ldejing@vmware.com>

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

Comments

Alin-Gabriel Serdean Aug. 9, 2022, 1:21 p.m. UTC | #1
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 *)&currentTime);
> +    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;
Dejing Liu Aug. 16, 2022, 9:59 a.m. UTC | #2
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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;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 *)&currentTime);
> +    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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%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.
> + */
> +
> +#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;
Alin-Gabriel Serdean Aug. 16, 2022, 10:19 a.m. UTC | #3
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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf363
7760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C63
7956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzI
iLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=HBLCMJ8wxRzzMsHhdi
m8OPYjsYQ7c5X58qarQJlEoQk%3D&amp;reserved=0>
&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%
7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7C
TWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
3D%7C3000%7C%7C%7C&amp;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 *)&currentTime);
> +    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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf363
7760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C63
7956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzI
iLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtH
RLPW0ZilompVkeMxx1hSemmumbs%3D&amp;reserved=0>
&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%
7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7C
TWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3
D&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.
> + */
> +
> +#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 Liu Aug. 18, 2022, 11:21 a.m. UTC | #4
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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&amp;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 *)&currentTime);
> +    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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&amp;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;
Dejing Liu Aug. 22, 2022, 6:48 a.m. UTC | #5
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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&amp;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 *)&currentTime);
> +    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&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&amp;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 mbox series

Patch

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 *)&currentTime);
+    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;