From a1d4207e2c8a751d03b45fa21e5d8370713b9047 Mon Sep 17 00:00:00 2001 From: Anand Kumar Date: Wed, 19 Sep 2018 11:39:21 -0700 Subject: datapath-windows: Add support to configure ct zone limits This patch implements limiting conntrack entries per zone using dpctl commands. Example: ovs-appctl dpctl/ct-set-limits default=5 zone=1,limit=2 zone=1,limit=3 ovs-appctl dpct/ct-del-limits zone=4 ovs-appctl dpct/ct-get-limits zone=1,2,3 - Also update the netlink-socket.c to support netlink family 'OVS_WIN_NL_CTLIMIT_FAMILY_ID' for conntrack zone limit. Signed-off-by: Anand Kumar Acked-by: Alin Gabriel Serdean Signed-off-by: Alin Gabriel Serdean --- datapath-windows/ovsext/Conntrack.c | 167 +++++++++++++++++++++++++++++++++++- datapath-windows/ovsext/Conntrack.h | 12 +++ datapath-windows/ovsext/Datapath.c | 34 +++++++- 3 files changed, 210 insertions(+), 3 deletions(-) (limited to 'datapath-windows/ovsext') diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c index dd1660218..5be8e4d66 100644 --- a/datapath-windows/ovsext/Conntrack.c +++ b/datapath-windows/ovsext/Conntrack.c @@ -27,13 +27,17 @@ #define WINDOWS_TICK 10000000 #define SEC_TO_UNIX_EPOCH 11644473600LL #define SEC_TO_NANOSEC 1000000000LL +#define CT_MAX_ZONE (UINT16_MAX + 1) KSTART_ROUTINE OvsConntrackEntryCleaner; static PLIST_ENTRY ovsConntrackTable; static OVS_CT_THREAD_CTX ctThreadCtx; static PNDIS_RW_LOCK_EX *ovsCtBucketLock = NULL; +static NDIS_SPIN_LOCK ovsCtZoneLock; +static POVS_CT_ZONE_INFO zoneInfo = NULL; extern POVS_SWITCH_CONTEXT gOvsSwitchContext; static ULONG ctTotalEntries; +static ULONG defaultCtLimit; static __inline OvsCtFlush(UINT16 zone, struct ovs_key_ct_tuple_ipv4 *tuple); static __inline NDIS_STATUS @@ -94,6 +98,20 @@ OvsInitConntrack(POVS_SWITCH_CONTEXT context) ZwClose(threadHandle); threadHandle = NULL; + zoneInfo = OvsAllocateMemoryWithTag(sizeof(OVS_CT_ZONE_INFO) * + CT_MAX_ZONE, OVS_CT_POOL_TAG); + if (zoneInfo == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto freeBucketLock; + } + + NdisAllocateSpinLock(&ovsCtZoneLock); + defaultCtLimit = CT_MAX_ENTRIES; + for (UINT32 i = 0; i < CT_MAX_ZONE; i++) { + zoneInfo[i].entries = 0; + zoneInfo[i].limit = defaultCtLimit; + } + status = OvsNatInit(); if (status != STATUS_SUCCESS) { @@ -149,6 +167,25 @@ OvsCleanupConntrack(VOID) OvsFreeMemoryWithTag(ovsCtBucketLock, OVS_CT_POOL_TAG); ovsCtBucketLock = NULL; OvsNatCleanup(); + NdisFreeSpinLock(&ovsCtZoneLock); + if (zoneInfo) { + OvsFreeMemoryWithTag(zoneInfo, OVS_CT_POOL_TAG); + } +} + +VOID +OvsCtSetZoneLimit(int zone, ULONG value) { + NdisAcquireSpinLock(&ovsCtZoneLock); + if (zone == -1) { + /* Set default limit for all zones. */ + defaultCtLimit = value; + for (UINT32 i = 0; i < CT_MAX_ZONE; i++) { + zoneInfo[i].limit = value; + } + } else { + zoneInfo[(UINT16)zone].limit = value; + } + NdisReleaseSpinLock(&ovsCtZoneLock); } /* @@ -263,6 +300,7 @@ OvsCtAddEntry(POVS_CT_ENTRY entry, &entry->link); NdisInterlockedIncrement((PLONG)&ctTotalEntries); + NdisInterlockedIncrement((PLONG)&zoneInfo[ctx->key.zone].entries); NdisReleaseRWLock(ovsCtBucketLock[bucketIdx], &lockState); return TRUE; } @@ -437,6 +475,7 @@ OvsCtEntryDelete(POVS_CT_ENTRY entry, BOOLEAN forceDelete) if (entry->natInfo.natAction) { OvsNatDeleteKey(&entry->key); } + NdisInterlockedDecrement((PLONG)&zoneInfo[entry->key.zone].entries); OvsPostCtEventEntry(entry, OVS_EVENT_CT_DELETE); RemoveEntryList(&entry->link); OVS_RELEASE_SPIN_LOCK(&(entry->lock), irql); @@ -877,12 +916,16 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx, &entryCreated); } else { - if (commit && ctTotalEntries >= CT_MAX_ENTRIES) { + if (commit && (ctTotalEntries >= CT_MAX_ENTRIES || + zoneInfo[ctx.key.zone].entries >= zoneInfo[ctx.key.zone].limit)) { /* Don't proceed with processing if the max limit has been hit. * This blocks only new entries from being created and doesn't * affect existing connections. */ - OVS_LOG_ERROR("Conntrack Limit hit: %lu", ctTotalEntries); + OVS_LOG_ERROR("Conntrack Limit hit: zone(%u), zoneLimit(%lu)," + "zoneEntries(%lu), ctTotalEntries(%lu)", + zone, zoneInfo[ctx.key.zone].limit, + zoneInfo[ctx.key.zone].entries, ctTotalEntries); return NDIS_STATUS_RESOURCES; } /* If no matching entry was found, create one and add New state */ @@ -1783,4 +1826,124 @@ OvsCtDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, return STATUS_SUCCESS; } +static NTSTATUS +OvsCreateNlMsgFromCtLimit(POVS_MESSAGE msgIn, + PVOID outBuffer, + UINT32 outBufLen, + PCHAR attr, + UINT32 numAttrs, + int dpIfIndex) +{ + NTSTATUS status = STATUS_SUCCESS; + NL_BUFFER nlBuffer; + PNL_MSG_HDR nlMsg; + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + + NlBufInit(&nlBuffer, outBuffer, outBufLen); + + if (!NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI, + msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid, + msgIn->genlMsg.cmd, msgIn->genlMsg.version, + dpIfIndex)) { + return STATUS_INVALID_BUFFER_SIZE; + } + + if (genlMsgHdr->cmd == OVS_CT_LIMIT_CMD_GET && numAttrs) { + POVS_CT_ZONE_LIMIT zoneLimitAttr = (POVS_CT_ZONE_LIMIT) attr; + UINT32 offset = NlMsgStartNested(&nlBuffer, OVS_CT_LIMIT_ATTR_ZONE_LIMIT); + if (!offset) { + /* Starting the nested attribute failed. */ + status = STATUS_INVALID_BUFFER_SIZE; + goto done; + } + + /* Insert OVS_CT_ZONE_LIMIT attributes.*/ + for (UINT32 i = 0; i < numAttrs; i++) { + if (zoneLimitAttr) { + zoneLimitAttr->limit = zoneInfo[zoneLimitAttr->zone_id].limit; + zoneLimitAttr->count = zoneInfo[zoneLimitAttr->zone_id].entries; + if (zoneLimitAttr->zone_id == -1) { + zoneLimitAttr->limit = defaultCtLimit; + } + NlMsgPutTail(&nlBuffer, (const PCHAR)zoneLimitAttr, + sizeof(OVS_CT_ZONE_LIMIT)); + } else { + status = STATUS_INVALID_PARAMETER; + break; + } + zoneLimitAttr = (POVS_CT_ZONE_LIMIT)((PCHAR) zoneLimitAttr + + sizeof(OVS_CT_ZONE_LIMIT)); + } + NlMsgEndNested(&nlBuffer, offset); + } + +done: + nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0); + nlMsg->nlmsgLen = NlBufSize(&nlBuffer); + + return status; +} + +NTSTATUS +OvsCtLimitHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, + UINT32 *replyLen) +{ + NTSTATUS status; + POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; + POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; + PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); + PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); + POVS_HDR ovsHdr = &(msgIn->ovsHdr); + PCHAR attr = NULL; + UINT32 numAttrs = 0; + UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN; + + static const NL_POLICY ovsCtLimitPolicy[] = { + [OVS_CT_LIMIT_ATTR_ZONE_LIMIT] = { .type = NL_A_NESTED, .optional = TRUE } + }; + PNL_ATTR nlAttrs[ARRAY_SIZE(ovsCtLimitPolicy)]; + + if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr), + ovsCtLimitPolicy, ARRAY_SIZE(ovsCtLimitPolicy), + nlAttrs, ARRAY_SIZE(nlAttrs))) + != TRUE) { + OVS_LOG_ERROR("Attr Parsing failed for msg: %p", nlMsgHdr); + return STATUS_INVALID_PARAMETER; + } + + if (nlAttrs[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) { + numAttrs = NlAttrGetSize(nlAttrs[OVS_CT_LIMIT_ATTR_ZONE_LIMIT])/sizeof(OVS_CT_ZONE_LIMIT); + attr = NlAttrGet(nlAttrs[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]); + } + + if (genlMsgHdr->cmd == OVS_CT_LIMIT_CMD_SET || + genlMsgHdr->cmd == OVS_CT_LIMIT_CMD_DEL) { + POVS_CT_ZONE_LIMIT zoneLimitAttr = (POVS_CT_ZONE_LIMIT)attr; + for (UINT32 i = 0; i < numAttrs; i++) { + /* Parse zone limit attributes. */ + if (zoneLimitAttr) { + if (genlMsgHdr->cmd == OVS_CT_LIMIT_CMD_DEL) { + zoneLimitAttr->limit = CT_MAX_ENTRIES; + } + OvsCtSetZoneLimit(zoneLimitAttr->zone_id, zoneLimitAttr->limit); + } else { + OVS_LOG_ERROR("Failed to get zone limit attribute at index(%u)," + " numAttrs(%u)", i, numAttrs); + return STATUS_INVALID_PARAMETER; + } + zoneLimitAttr = (POVS_CT_ZONE_LIMIT)((PCHAR) zoneLimitAttr + + sizeof(OVS_CT_ZONE_LIMIT)); + } + } + + /* Output buffer has been validated while validating transact dev op. */ + ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); + status = OvsCreateNlMsgFromCtLimit(msgIn, msgOut, + usrParamsCtx->outputLength, + attr, numAttrs, ovsHdr->dp_ifindex); + *replyLen = msgOut->nlMsg.nlmsgLen; + + return status; +} + #pragma warning(pop) diff --git a/datapath-windows/ovsext/Conntrack.h b/datapath-windows/ovsext/Conntrack.h index d4152b33a..4678ed028 100644 --- a/datapath-windows/ovsext/Conntrack.h +++ b/datapath-windows/ovsext/Conntrack.h @@ -132,6 +132,18 @@ typedef struct OvsConntrackKeyLookupCtx { BOOLEAN related; } OvsConntrackKeyLookupCtx; +/* Per zone strucuture. */ +typedef struct _OVS_CT_ZONE_INFO { + ULONG limit; + ULONG entries; +} OVS_CT_ZONE_INFO, *POVS_CT_ZONE_INFO; + +typedef struct _OVS_CT_ZONE_LIMIT { + int zone_id; + ULONG limit; + ULONG count; +} OVS_CT_ZONE_LIMIT, *POVS_CT_ZONE_LIMIT; + #define CT_MAX_ENTRIES 1 << 21 #define CT_HASH_TABLE_SIZE ((UINT32)1 << 10) #define CT_HASH_TABLE_MASK (CT_HASH_TABLE_SIZE - 1) diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c index 34ef2b40a..fa994840a 100644 --- a/datapath-windows/ovsext/Datapath.c +++ b/datapath-windows/ovsext/Datapath.c @@ -99,7 +99,8 @@ NetlinkCmdHandler OvsGetNetdevCmdHandler, OvsSubscribePacketCmdHandler, OvsReadPacketCmdHandler, OvsCtDeleteCmdHandler, - OvsCtDumpCmdHandler; + OvsCtDumpCmdHandler, + OvsCtLimitHandler; static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); @@ -324,6 +325,34 @@ NETLINK_FAMILY nlNetdevFamilyOps = { .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps) }; + +/* Netlink conntrack limit family. */ +NETLINK_CMD nlCtLimitFamilyCmdOps[] = { + { .cmd = OVS_CT_LIMIT_CMD_GET, + .handler = OvsCtLimitHandler, + .supportedDevOp = OVS_TRANSACTION_DEV_OP, + .validateDpIndex = FALSE + }, + { .cmd = OVS_CT_LIMIT_CMD_SET, + .handler = OvsCtLimitHandler, + .supportedDevOp = OVS_TRANSACTION_DEV_OP, + .validateDpIndex = FALSE + }, + { .cmd = OVS_CT_LIMIT_CMD_DEL, + .handler = OvsCtLimitHandler, + .supportedDevOp = OVS_TRANSACTION_DEV_OP, + .validateDpIndex = FALSE + }, +}; + +NETLINK_FAMILY nlCtLimitFamilyOps = { + .name = OVS_CT_LIMIT_FAMILY, + .id = OVS_WIN_NL_CTLIMIT_FAMILY_ID, + .version = OVS_CT_LIMIT_VERSION, + .maxAttr = OVS_CT_LIMIT_ATTR_MAX, + .cmds = nlCtLimitFamilyCmdOps, + .opsCount = ARRAY_SIZE(nlCtLimitFamilyCmdOps) +}; static NTSTATUS MapIrpOutputBuffer(PIRP irp, UINT32 bufferLength, UINT32 requiredLength, @@ -941,6 +970,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject, case OVS_WIN_NL_NETDEV_FAMILY_ID: nlFamilyOps = &nlNetdevFamilyOps; break; + case OVS_WIN_NL_CTLIMIT_FAMILY_ID: + nlFamilyOps = &nlCtLimitFamilyOps; + break; default: status = STATUS_INVALID_PARAMETER; goto done; -- cgit v1.2.1