From patchwork Mon Jul 29 05:05:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Karthik Chandrashekar X-Patchwork-Id: 1965824 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=proofpoint20171006 header.b=JtK0yyth; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=selector1 header.b=DI2BsMyH; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXRCd21tYz1yYq for ; Mon, 29 Jul 2024 15:06:19 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 535374042F; Mon, 29 Jul 2024 05:06:17 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id csiG6gKqQPbe; Mon, 29 Jul 2024 05:06:14 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 8E162403F9 Authentication-Results: smtp4.osuosl.org; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=proofpoint20171006 header.b=JtK0yyth; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=selector1 header.b=DI2BsMyH Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp4.osuosl.org (Postfix) with ESMTPS id 8E162403F9; Mon, 29 Jul 2024 05:06:14 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9C22BC002B; Mon, 29 Jul 2024 05:06:13 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id D356DC002A for ; Mon, 29 Jul 2024 05:06:11 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id AB0DC80E4D for ; Mon, 29 Jul 2024 05:06:11 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id gMyZ-3lXQXIM for ; Mon, 29 Jul 2024 05:06:09 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=148.163.155.12; helo=mx0b-002c1b01.pphosted.com; envelope-from=karthik.c@nutanix.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 0254480E45 Authentication-Results: smtp1.osuosl.org; dmarc=pass (p=none dis=none) header.from=nutanix.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 0254480E45 Authentication-Results: smtp1.osuosl.org; dkim=pass (2048-bit key, unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=proofpoint20171006 header.b=JtK0yyth; dkim=pass (2048-bit key, unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=selector1 header.b=DI2BsMyH Received: from mx0b-002c1b01.pphosted.com (mx0b-002c1b01.pphosted.com [148.163.155.12]) by smtp1.osuosl.org (Postfix) with ESMTPS id 0254480E45 for ; Mon, 29 Jul 2024 05:06:08 +0000 (UTC) Received: from pps.filterd (m0127842.ppops.net [127.0.0.1]) by mx0b-002c1b01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 46SCOuQl016086 for ; Sun, 28 Jul 2024 22:06:07 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nutanix.com; h= cc:content-transfer-encoding:content-type:date:from:message-id :mime-version:subject:to; s=proofpoint20171006; bh=gwEVJhxOwtWiU RYWW40x0DAulQj4fWHHomX5WaWyLpk=; b=JtK0yythOOPQCmnlSlVibqknxyoA9 8VGDZVVZ0fkidHjD6Aq/Hi7zxIICey5+Xr0reNO6OEyZp3Tubds+23cyeYd/Y8zC yY62gp08zuIVzdFpQ+NttUo+7r6UGYRwyEF0/BjEwtqrah7gURAkSgOZ52ae4ycx gfJnCS+NVW35vpVTLT4pvZVAegxjM+2jso4WEnr+hRhBSB8JfxlZc538HuqPuWSi GdSBwJDPS+TfdEHOtc7QbTA7ML8XIpZzAYHe6nZLHmofKogYqqJOoj69exevhRqE 1Mw8E9W89z15RnClfdRO5/0YAK/mLWD/Dp5eqIwy1enb5nqtEGGCZ1UDg== Received: from bl2pr02cu003.outbound.protection.outlook.com (mail-eastusazlp17010004.outbound.protection.outlook.com [40.93.11.4]) by mx0b-002c1b01.pphosted.com (PPS) with ESMTPS id 40n07c27fv-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Sun, 28 Jul 2024 22:06:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=dMGWmmdm2ZHW3/SIRyE2zgk6s5dciR5vH/nUPnFsjZkVLsx4kxE/OJj2cd938oZtefQk8PpulnXWxgycPJGng+V0qxFrsLAmsKLv/N8stgAkPdDf/AGgbtMcE3GhSkLAM0O5oGpRZrgvNb9zvtyNzLYW/QPbovROzSsLE4tW/oVzJzuJmT44BBky1p6GpRoIHSIVfth8Yn2ArFnksgHVVyiR5M6Fl4MYygwTDa8DLpC95stYQsJDdiuGUcvzon7vv/gHmfQriLetqp4NBcoCtwWfuO4W4S8wmsCCyHkAG0MgRujpTW525ai2/zZTqTmuRawiR43NeM+mFG2I0MIbMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=gwEVJhxOwtWiURYWW40x0DAulQj4fWHHomX5WaWyLpk=; b=FvdfBHL2FXi4+WOMUPYLdE4MGrt+S04EKNU8VH5NV+0w49mlrjS+Z7wGzg4uMkJoJTZKBlEjAwX2Mh07yp3IkAmTiZuOXm6vDOAh/EOCbAsqvGoyJv3uNlxTjZ/ooYHEANIBvtjR3wF3mMnU9UsbqMyzQY/hjSoiBJ43teN4D2kY+hRjlJrCaaGv2nG09i9ewG86DVX/36rMvVsUT7xHFoZcwgaD6ITSTtrG82XNxeUJIpYFPCwLReOB7cqYzUw0eLtWPVjBRxZi1xUzoM5wbDE5dRyEqglQaEeO67wqXve57cugcgI4rY3l0QJa6o9o2Q07yZQI4T1ec76dKy8MUA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nutanix.com; dmarc=pass action=none header.from=nutanix.com; dkim=pass header.d=nutanix.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nutanix.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=gwEVJhxOwtWiURYWW40x0DAulQj4fWHHomX5WaWyLpk=; b=DI2BsMyHYwkQvYKVaGWVPv61wGqqjv3Rl5kwwp38mucyaLaEjwH9rWS/Kb03QdDbQaoTcQzlC6oNdDWjkzCPnAejadYbv3cWXAjMUNfq0o4GjuTG6zKbz67mbPiitmyXMf99O8wsQkwKHyb+f6Tl3Eyuk11imA714VIiaAoH9TQFd9p5AeHJUbNdh2II1IbNp3xH3QsyaEDXBB1j+INNocgg6/kOr4s25cmyfI9SHceFpG2dHF1D1QFItsjm8HaA8t7/rdZlxaLA3RLkpLAJXAQyG9Au/LMRH0QVmLBZikSG/A//zPcXvKfw0A2WvGaWx1kmDfrvsfYYHRJVyKEl0g== Received: from CH3PR02MB10217.namprd02.prod.outlook.com (2603:10b6:610:1be::10) by MN2PR02MB6767.namprd02.prod.outlook.com (2603:10b6:208:1d2::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7807.27; Mon, 29 Jul 2024 05:06:00 +0000 Received: from CH3PR02MB10217.namprd02.prod.outlook.com ([fe80::1b78:29fa:4471:f1b4]) by CH3PR02MB10217.namprd02.prod.outlook.com ([fe80::1b78:29fa:4471:f1b4%5]) with mapi id 15.20.7784.020; Mon, 29 Jul 2024 05:05:59 +0000 From: karthik.c@nutanix.com To: ovs-dev@openvswitch.org Date: Mon, 29 Jul 2024 05:05:54 +0000 Message-Id: <20240729050554.202163-1-karthik.c@nutanix.com> X-Mailer: git-send-email 2.22.3 X-ClientProxiedBy: BYAPR08CA0024.namprd08.prod.outlook.com (2603:10b6:a03:100::37) To CH3PR02MB10217.namprd02.prod.outlook.com (2603:10b6:610:1be::10) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH3PR02MB10217:EE_|MN2PR02MB6767:EE_ X-MS-Office365-Filtering-Correlation-Id: ae54dce1-1314-429f-b574-08dcaf8c22df x-proofpoint-crosstenant: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|52116014|376014|1800799024|366016|38350700014; X-Microsoft-Antispam-Message-Info: QyNVrv73u4qh+gNb/K3Ui2mfN578EHzPQ91laD79f2Pqg7IqO4sWMFmZgj5nOcw7pF7+UYhmC8swz5XrOOOApSY+R6AEp317Sb23QmEQ4QKFwD8gg5miH7cVN5VsBHx/QZQiNLFJlcty11UUqTX+9tPSU6SFgdzr8a+pt4ggyIpimj0vCRvnapO1VcP0Q3Zc8/k0gwK6SgihpdUXSgavG1UL1aRovRqyOAgeryLPYTPOS8IXEdee92CXt5qKQ4LkhRMHKqHecaBLNbbjkuPru9nzFKu8hgkecj1cjCmLYWmJUxB0YRiBh8rRwUxqeVodoNtcamhDWjhKFaNej6IvC1btLdge7f0gKaR9zN17PXI3p59FAUY+1m91jCM23+SqS5SYWaPhzghVoRFVQnYHTyGOXogkmRspmB/LBm6e16Vxfl4K+NUIO61glSIfULWLHd4axlJA+yFy6e2+k5B36roYSXPNYbCir5O5lD/JYr70KDg+wQP6RKHY61/ST/elzaYARdxkQSBYXIF/f8WOA8CpQxFdOYR6HfiXpoOe3ItD8LhMxcxh2TAIqXj4fTOwMVN7NtXHIzoy1glTHidH9C4T/i4dVQiKEj9EDW5RLVuobIEnikZIJQYC/qHGiccVYZrSEkg1wCBIA1YaV4uXUL7T5trQHbsv0wudUF3fqvUlPGYroo061aumlu0DywhPzwlXv3TdKISIG+MZ19rZLclVDv8sT21qTklXe2phQD3O1z0ObkY4DOOnsOT6HkX2hDkCa+DM8CRT4A9PPn/8t3yaVGlhl08tKNv2mdbQN2kzV4y8yO7P/XSlRIbXBdCX4lIjkI409g9Cxt3oo0FB9KBnOq2WztR1C4FZ+bIVLEZdpKJu9fAbuuIFlEjA0YbvH2avplRpZQ7XBV1gjZbw/rBlkU/+0IA7aVIp53wlXf55TyHKwhNxVfuOZYYETSme4YfR+Nt8lR5q3+weKhqN22h/ELEHW+YxjGX8DeNTVWvAvUozFGFI/26y5gekcIjJ2DUBeeBwAEh4xfrHs253cEHA2h4RI14Zeb4LktrCiMU20uxqNkiictDSNAiP5348zMrGnrpeDaUXr3OxSXlzEcamwfx2sjMrk+gN6ioacNETByGj29GUjoW5eiksrqHjEHRywdP4dRXnX8pXvKCjoef7xpcfKVQS7KJTdR5j+gJKDWoiiGN3DXeGYKFvKXDrq6oLuzi7rz2ENRKa/czStUC7bku7wYaDrCmA+DiwdPzgFJIrYZQyeoZ9F+K9mvTVE2REcOB586tMadzoJ1UImHWO84kHj3fgZ7/FGVLwwTAsy8s1tx4hQLaXhaawv01Cm/RYtomYM/YQqvC77HQKdxbIqdNvNTI3xC1SZOZM9QIFAScSdQyXwC3rlW33CBhg1BO7hLrjJsmdqmCHu6/Jbw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:CH3PR02MB10217.namprd02.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(52116014)(376014)(1800799024)(366016)(38350700014); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: nz4EHfMIJPo4kxzzOREAbj41Ow0/jCigyCm4f9dmUglUYFxoSJ0rkWH3LjR2NRRPKFbL7tz90nK4PTuvwj89I8Wkv8wYnWxMiu7bC8vGGsqx8FIzKZX8kYsqUnNgJFI2BGn30cRiHPE1Fn/XLQLIUyLy1yhtZraOgJIgsvGDoSQg8yUUoKOP9nin8sT85c87GzKcBcqdlo7+z6/tSTc2tNe839o0qRA/rkIHZPtSYcPUHcNrImIT08TIoICw6k+H7fjuaDXSrftuE0pn/u4S5Fd2IsdXudtqepOvsO+Wd2qgXZceXYzoQ80J6FY3nQg6FA635vs6xDS72HFbJtp9gnZCrF5qJBYdq5wpNJwSMqcUG0synkwQhQHD+bBZ8P8qRoskukM+42ssJ6rPPc9HOikVpMuAlzP0s4TEn7Na1yMIQwSVYv0AR5qp3/Gs50JGaNZqdVeSNKRWjEfZz+MXbwxlCMxJ0qdfNtZwMyu+BFHwamCytEYSMuQHvHWsOdTgJ7nVC+XIkqURZotVjNHgBSMwXXSN1MnQL2CX0pd0ST7DRjHjzij6sZ6ES1Oyro+Kc7joWcRjggeG75ZQiNAW12XVROmcS5Ra4NC/cG7NNSJotQKcVYOctrOghyiiNURVRatLQHCWUYDU4AqSxa0OTt0D/Lb2YAeaBLEoQ5G/0smX+ilHjFMz9HRbR5Uu6byvRK7/mfH/Ius5Qp6iG1sMkpoXjwkO93FUFzx9m9WueokPvJl0rjUvUON3xOc74kfgBok+x6eQabL/XtPUvXzqMfOmTnoTmd6xSgDO3y4ie88sQxFES1bf6lUG5z+6VsF5bj67lYuSvRoEV+YzlHdn8ovymFSgmZuT4RuvmWGW6kLddlxUtsUaKj/5/6pBNXML8S3mfYlRMZv9quzhqVevq0UuZ6G6KbyoVo+q0mLOFPjNEWnPxevoUwksUbppAvheGm23FyjyjQkwK50RnZZVZy4tq74ANoA0DOITK1pAFAIGDhom0hajwb9x4DrjMHFYx8plSMzr+yNzaT/YjEwIRF151eiblLesR1tWbGLB9WDFIDH/fSvSKYZ9g8ZOupFOSMrK9GwZQ8EwlV7Fy+aoWlv0gwL4HpTdrv9obBXk9gM/jGjUrMV4Qxm8EADz8ECUk0VRQHTYh7v7ddSjQ/ODhqc/MHeTMesNNVtpnhZ0D3/ZIDqKy+jch9HXdAH3qsAfuI4iHvXcsB/OmBThV6VnBrYcjEjWXHGv+d/45fpHUasBh9/1pM+MHjsfyXEI8NaBVwRbKHB+YxoEzdtcKE6C2CdGGI40JP9qvYLUS23Y+J5me1o5yDNRE0q61AOW1218qCCDJaRIN9gf06OwBRx3ae51mitgsvRWXGXJQtyl6orU4Bv/1XFidBWU4+2BkxhSTHpZ1R9B/+80znDAQBC4rI+OkJoPSjk85hSqKGrA2BLXXR8gZV/Ux6baK1gAs8nGeJb9V9dIJjWBpjJnDN1aDqGqruDmNHVCE85ftBBcUm6zJypNQgjYRarsXHlRffUp/LIE6nlBP9+6arTkwjNKqYKjUziXmB063N2UKrrVgDyInK5H8ybwmR+lRGj9NpzC9Z811zvz3bvEjEQ8VYaDVg== X-OriginatorOrg: nutanix.com X-MS-Exchange-CrossTenant-Network-Message-Id: ae54dce1-1314-429f-b574-08dcaf8c22df X-MS-Exchange-CrossTenant-AuthSource: CH3PR02MB10217.namprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Jul 2024 05:05:59.6679 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: bb047546-786f-4de1-bd75-24e5b6f79043 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: e/3m3yIlPr3JxkVkIZDEzdkxNYzXQFKufKGpsfFtZm+Vu675pCZ1tnv3F+Lp29KKtAst/iwVtvQv+E4zr8GQ+Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR02MB6767 X-Proofpoint-GUID: VhltaBv329_3v0XPpUV0kxJQg6sRjOMP X-Proofpoint-ORIG-GUID: VhltaBv329_3v0XPpUV0kxJQg6sRjOMP X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.28.16 definitions=2024-07-29_03,2024-07-26_01,2024-05-17_01 X-Proofpoint-Spam-Reason: safe Subject: [ovs-dev] [PATCH ovn v1] Support selection fields for ECMP routes X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Karthik Chandrashekar This patch adds the ability to specify a custom set of packet headers for hash computation for ECMP routes similar to the support that was added for LB in 5af304e7478adcf5ac50ed41e96a55bebebff3e8 ECMP routes by default use dp_hash as a selection_method for OVS flows. When ecmp_selection_fields is specified, the selection_method will be hash with the specified list of fields used for computing the hash. For simplicity, list of fields that are used in the select action is a union of all the fields specified in each Logical_Route_Static_Route that is part of a given ECMP route. Signed-off-by: Karthik Chandrashekar --- include/ovn/actions.h | 1 + lib/actions.c | 55 +++++++++++++++- northd/northd.c | 60 ++++++++++++++++-- ovn-nb.xml | 17 +++++ tests/ovn.at | 144 ++++++++++++++++++++++++++++++++++++++++-- utilities/ovn-nbctl.c | 17 +++-- 6 files changed, 273 insertions(+), 21 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 88cf4de79..a9af1e38e 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -338,6 +338,7 @@ struct ovnact_select { struct ovnact_select_dst *dsts; size_t n_dsts; uint8_t ltable; /* Logical table ID of next table. */ + char *hash_fields; struct expr_field res_field; }; diff --git a/lib/actions.c b/lib/actions.c index e8cc0994d..11d4998f2 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -1534,11 +1534,19 @@ parse_select_action(struct action_context *ctx, struct expr_field *res_field) struct ovnact_select_dst *dsts = NULL; size_t allocated_dsts = 0; size_t n_dsts = 0; + bool requires_hash_fields = false; + char *hash_fields = NULL; lexer_get(ctx->lexer); /* Skip "select". */ lexer_get(ctx->lexer); /* Skip '('. */ - while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { + if (lexer_match_id(ctx->lexer, "values")) { + lexer_force_match(ctx->lexer, LEX_T_EQUALS); + requires_hash_fields = true; + } + + while (!lexer_match(ctx->lexer, LEX_T_SEMICOLON) && + !lexer_match(ctx->lexer, LEX_T_RPAREN)) { struct ovnact_select_dst dst; if (!action_parse_uint16(ctx, &dst.id, "id")) { free(dsts); @@ -1574,11 +1582,39 @@ parse_select_action(struct action_context *ctx, struct expr_field *res_field) return; } + if (requires_hash_fields) { + if (!lexer_match_id(ctx->lexer, "hash_fields")) { + lexer_syntax_error(ctx->lexer, "expecting hash_fields"); + free(dsts); + return; + } + if (!lexer_match(ctx->lexer, LEX_T_EQUALS) || + ctx->lexer->token.type != LEX_T_STRING || + lexer_lookahead(ctx->lexer) != LEX_T_RPAREN) { + lexer_syntax_error(ctx->lexer, "invalid hash_fields"); + free(dsts); + return; + } + + hash_fields = xstrdup(ctx->lexer->token.s); + lexer_get(ctx->lexer); + if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) { + lexer_get(ctx->lexer); + } + } else { + if (lexer_match_id(ctx->lexer, "hash_fields")) { + lexer_syntax_error(ctx->lexer, "hash_fields unexpected"); + free(dsts); + return; + } + } + struct ovnact_select *select = ovnact_put_SELECT(ctx->ovnacts); select->ltable = ctx->pp->cur_ltable + 1; select->dsts = dsts; select->n_dsts = n_dsts; select->res_field = *res_field; + select->hash_fields = hash_fields; } static void @@ -1588,6 +1624,9 @@ format_SELECT(const struct ovnact_select *select, struct ds *s) ds_put_cstr(s, " = "); ds_put_cstr(s, "select"); ds_put_char(s, '('); + if (select->hash_fields) { + ds_put_format(s, "values="); + } for (size_t i = 0; i < select->n_dsts; i++) { if (i) { ds_put_cstr(s, ", "); @@ -1598,6 +1637,10 @@ format_SELECT(const struct ovnact_select *select, struct ds *s) ds_put_format(s, "=%"PRIu16, dst->weight); } ds_put_char(s, ')'); + if (select->hash_fields) { + ds_chomp(s, ')'); + ds_put_format(s, "; hash_fields=\"%s\")", select->hash_fields); + } ds_put_char(s, ';'); } @@ -1612,9 +1655,14 @@ encode_SELECT(const struct ovnact_select *select, struct ofpact_group *og; struct ds ds = DS_EMPTY_INITIALIZER; - ds_put_format(&ds, "type=select,selection_method=dp_hash"); + ds_put_format(&ds, "type=select,selection_method=%s", + select->hash_fields ? "hash": "dp_hash"); + if (select->hash_fields) { + ds_put_format(&ds, ",fields(%s)", select->hash_fields); + } - if (ovs_feature_is_supported(OVS_DP_HASH_L4_SYM_SUPPORT)) { + if (ovs_feature_is_supported(OVS_DP_HASH_L4_SYM_SUPPORT) && + !select->hash_fields) { /* Select dp-hash l4_symmetric by setting the upper 32bits of * selection_method_param to value 1 (1 << 32): */ ds_put_cstr(&ds, ",selection_method_param=0x100000000"); @@ -1647,6 +1695,7 @@ static void ovnact_select_free(struct ovnact_select *select) { free(select->dsts); + free(select->hash_fields); } static void diff --git a/northd/northd.c b/northd/northd.c index 5b50ea191..70bd384c3 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -10242,6 +10242,7 @@ struct parsed_route { uint32_t route_table_id; uint32_t hash; const struct nbrec_logical_router_static_route *route; + const char *ecmp_selection_fields; bool ecmp_symmetric_reply; bool is_discard_route; }; @@ -10352,6 +10353,12 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports, pr->ecmp_symmetric_reply = smap_get_bool(&route->options, "ecmp_symmetric_reply", false); pr->is_discard_route = is_discard_route; + pr->ecmp_selection_fields = NULL; + const char *ecmp_selection_fields = smap_get(&route->options, + "ecmp_selection_fields"); + if (ecmp_selection_fields) { + pr->ecmp_selection_fields = ecmp_selection_fields; + } ovs_list_insert(routes, &pr->list_node); return pr; } @@ -10381,6 +10388,7 @@ struct ecmp_groups_node { const char *origin; uint32_t route_table_id; uint16_t route_count; + char *selection_fields; struct ovs_list route_list; /* Contains ecmp_route_list_node */ }; @@ -10397,6 +10405,34 @@ ecmp_groups_add_route(struct ecmp_groups_node *group, struct ecmp_route_list_node *er = xmalloc(sizeof *er); er->route = route; er->id = ++group->route_count; + + if (route->ecmp_selection_fields) { + if (group->selection_fields) { + struct sset current_field_set; + struct sset field_set; + + sset_from_delimited_string(¤t_field_set, + group->selection_fields, ","); + sset_from_delimited_string(&field_set, + route->ecmp_selection_fields, ","); + + const char *field; + SSET_FOR_EACH(field, &field_set) { + sset_add(¤t_field_set, field); + } + + group->selection_fields = xasprintf("%s", + sset_join(¤t_field_set, + ",", "")); + + sset_destroy(&field_set); + sset_destroy(¤t_field_set); + } else { + group->selection_fields = xasprintf("%s", + route->ecmp_selection_fields); + } + } + ovs_list_insert(&group->route_list, &er->list_node); } @@ -10419,6 +10455,7 @@ ecmp_groups_add(struct hmap *ecmp_groups, eg->is_src_route = route->is_src_route; eg->origin = smap_get_def(&route->route->options, "origin", ""); eg->route_table_id = route->route_table_id; + eg->selection_fields = NULL; ovs_list_init(&eg->route_list); ecmp_groups_add_route(eg, route); @@ -10734,19 +10771,28 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, eg->is_src_route, is_ipv4, &route_match, &priority, ofs); free(prefix_s); - struct ds actions = DS_EMPTY_INITIALIZER; - ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; %s = %"PRIu16 - "; %s = select(", REG_ECMP_GROUP_ID, eg->id, - REG_ECMP_MEMBER_ID); - + struct ds values = DS_EMPTY_INITIALIZER; bool is_first = true; LIST_FOR_EACH (er, list_node, &eg->route_list) { if (is_first) { is_first = false; } else { - ds_put_cstr(&actions, ", "); + ds_put_cstr(&values, ", "); } - ds_put_format(&actions, "%"PRIu16, er->id); + ds_put_format(&values, "%"PRIu16, er->id); + } + + struct ds actions = DS_EMPTY_INITIALIZER; + if (eg->selection_fields) { + ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; %s = %"PRIu16 + "; %s = select(values=%s", REG_ECMP_GROUP_ID, eg->id, + REG_ECMP_MEMBER_ID, ds_cstr(&values)); + + ds_put_format(&actions, "; hash_fields=\"%s\"", eg->selection_fields); + } else { + ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; %s = %"PRIu16 + "; %s = select(%s", REG_ECMP_GROUP_ID, eg->id, + REG_ECMP_MEMBER_ID, ds_cstr(&values)); } ds_put_cstr(&actions, ");"); diff --git a/ovn-nb.xml b/ovn-nb.xml index 0f9a1005a..e362609ab 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -3662,6 +3662,23 @@ or set). + +

+ ECMP routes use OpenFlow groups of type select to + pick a nexthop among the list of avaible nexthops. + OVS supports two selection methods: dp_hash and + hash for hash computatiion and selecting + the buckets of a group. Please see the OVS documentation + (man ovs-ofctl) for more details on the selection methods. +

+ +

+ OVN by default uses dp_hash. In order to use the + hash selection method, specify comma-separated + list of selectoin fields. +

+
+ In case ovn-interconnection has been learned this route, it will have its origin set: either "connected" or "static". This key is supposed diff --git a/tests/ovn.at b/tests/ovn.at index 2ced7c0b2..d43668af8 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -2086,6 +2086,16 @@ reg0 = select(1, 2); encodes as group:20 uses group: id(20), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[[96..127]],resubmit(,oflow_in_table),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[[96..127]],resubmit(,oflow_in_table)) +reg9[[16..31]] = select(values=1=50, 2=100, 3; hash_fields="ip_src,ip_dst" ); + formats as reg9[[16..31]] = select(values=1=50, 2=100, 3=100; hash_fields="ip_src,ip_dst"); + encodes as group:21 + uses group: id(21), name(type=select,selection_method=hash,fields(ip_src,ip_dst),bucket=bucket_id=0,weight:50,actions=load:1->xreg4[[16..31]],resubmit(,oflow_in_table),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[[16..31]],resubmit(,oflow_in_table),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[[16..31]],resubmit(,oflow_in_table)) + +reg0 = select(values=1, 2; hash_fields="ip_dst,ip_src"); + formats as reg0 = select(values=1=100, 2=100; hash_fields="ip_dst,ip_src"); + encodes as group:22 + uses group: id(22), name(type=select,selection_method=hash,fields(ip_dst,ip_src),bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[[96..127]],resubmit(,oflow_in_table),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[[96..127]],resubmit(,oflow_in_table)) + reg0 = select(1=, 2); Syntax error at `,' expecting weight. reg0 = select(1=0, 2); @@ -2094,6 +2104,14 @@ reg0 = select(1=123456, 2); Syntax error at `123456' expecting weight. reg0 = select(123); Syntax error at `;' expecting at least 2 group members. +reg0 = select(values=1, 2); + Syntax error at `;' expecting hash_fields. +reg0 = select(values=1, 2; hash_fields); + Syntax error at `)' invalid hash_fields. +reg0 = select(values=1, 2; hash_fields=); + Syntax error at `)' invalid hash_fields. +reg0 = select(1, 2; hash_fields="ip_src"); + Syntax error at `=' hash_fields unexpected. ip.proto = select(1, 2, 3); Field ip.proto is not modifiable. reg0[[0..14]] = select(1, 2, 3); @@ -2101,12 +2119,12 @@ reg0[[0..14]] = select(1, 2, 3); fwd_group(liveness=true, childports="eth0", "lsp1"); formats as fwd_group(liveness="true", childports="eth0", "lsp1"); - encodes as group:21 - uses group: id(21), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=watch_port:17,load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) + encodes as group:23 + uses group: id(23), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=watch_port:17,load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) fwd_group(childports="eth0", "lsp1"); - encodes as group:22 - uses group: id(22), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) + encodes as group:24 + uses group: id(24), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) fwd_group(childports=eth0); Syntax error at `eth0' expecting logical switch port. @@ -2115,8 +2133,8 @@ fwd_group(); Syntax error at `)' expecting `;'. fwd_group(childports="eth0", "lsp1"); - encodes as group:22 - uses group: id(22), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) + encodes as group:24 + uses group: id(24), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT),bucket=load=0x17->NXM_NX_REG15[[0..15]],resubmit(,OFTABLE_SAVE_INPORT)) fwd_group(liveness=xyzzy, childports="eth0", "lsp1"); Syntax error at `xyzzy' expecting true or false. @@ -26459,6 +26477,120 @@ OVN_CLEANUP([hv1]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ECMP static routes - custom hash]) +ovn_start + +# Logical network: +# ls1 (192.168.1.0/24) - lr1 - ls2 (192.168.2.0/24) +# lsl has lsp11 (192.168.1.11) and ls2 has lsp21 (192.168.2.21) and lsp22 +# (192.168.2.22) +# +# Static routes on lr1: +# 10.0.0.0/24 nexthop 192.168.2.21 +# 10.0.0.0/24 nexthop 192.168.2.22 +# +# ECMP hash on eth_src,eth_dst,src_ip,dst_ip +# +# Test: +# lsp11 send packets to 10.0.0.100 with different source ports +# +# Expected result: +# All packets should go out of a either lsp21 or lsp22 + +ovn-nbctl lr-add lr1 + +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +for i in 1 2; do + ovn-nbctl lrp-add lr1 lrp-lr1-ls${i} 00:00:00:01:0${i}:01 192.168.${i}.1/24 + ovn-nbctl lsp-add ls${i} lsp-ls${i}-lr1 -- lsp-set-type lsp-ls${i}-lr1 router \ + -- lsp-set-options lsp-ls${i}-lr1 router-port=lrp-lr1-ls${i} \ + -- lsp-set-addresses lsp-ls${i}-lr1 router +done + +#install static routes +ovn-nbctl --ecmp-selection-fields="ip_src,eth_src" lr-route-add lr1 10.0.0.0/24 192.168.2.21 +ovn-nbctl --ecmp --ecmp-selection-fields="ip_src,ip_dst,eth_dst" lr-route-add lr1 10.0.0.0/24 192.168.2.22 + +# Create logical ports +ovn-nbctl lsp-add ls1 lsp11 -- \ + lsp-set-addresses lsp11 "f0:00:00:00:01:11 192.168.1.11" +ovn-nbctl lsp-add ls2 lsp21 -- \ + lsp-set-addresses lsp21 "f0:00:00:00:02:21 192.168.2.21" +ovn-nbctl lsp-add ls2 lsp22 -- \ + lsp-set-addresses lsp22 "f0:00:00:00:02:22 192.168.2.22" + +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=lsp11 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +ovs-vsctl -- add-port br-int hv1-vif2 -- \ + set interface hv1-vif2 external-ids:iface-id=lsp21 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=2 + +ovs-vsctl -- add-port br-int hv1-vif3 -- \ + set interface hv1-vif3 external-ids:iface-id=lsp22 \ + options:tx_pcap=hv1/vif3-tx.pcap \ + options:rxq_pcap=hv1/vif3-rx.pcap \ + ofport-request=3 + +# wait for earlier changes to take effect +check ovn-nbctl --wait=hv sync +wait_for_ports_up + +ovn-sbctl dump-flows > sbflows +AT_CAPTURE_FILE([sbflows]) + +as hv1 ovs-ofctl dump-groups br-int > ofgroups +AT_CAPTURE_FILE([ofgroups]) +OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-groups br-int > ofgroups + grep "selection_method=hash,fields" ofgroups | \ + grep "eth_src" | grep "eth_dst" | grep "ip_src" | grep "ip_dst" | wc -l], [0], [1 +]) + +for i in $(seq 5001 5010); do + packet="inport==\"lsp11\" && eth.src==f0:00:00:00:01:11 && eth.dst==00:00:00:01:01:01 && + ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==10.0.0.100 && + tcp && tcp.src==$i && tcp.dst==80" + OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"]) + + for j in 1 2; do + # Assume all packets go to lsp2${j}. + exp_packet="eth.src==00:00:00:01:02:01 && eth.dst==f0:00:00:00:02:2${j} && + ip4 && ip.ttl==63 && ip4.src==192.168.1.11 && ip4.dst==10.0.0.100 && + tcp && tcp.src==$i && tcp.dst==80" + echo $exp_packet | ovstest test-ovn expr-to-packets >> expected_lsp2${j} + done +done + +# All packets should go out of a single port given the hashing is based on eth_src,eth_dst,ip_src,ip_dst which is fixed +OVS_WAIT_UNTIL([ + rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l` + rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > lsp22.packets && cat lsp22.packets | wc -l` + echo $rcv_n1 $rcv_n2 + test $(($rcv_n1 + $rcv_n2)) -ge 10]) + +if test $rcv_n1 = 0; then + test $rcv_n2 -ge 10 +else + test $rcv_n1 -ge 10 +fi + +OVN_CLEANUP([hv1]) + +AT_CLEANUP +]) OVN_FOR_EACH_NORTHD([ AT_SETUP([route tables --
route table routes]) diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index 679d3f2d9..70e8fb239 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -4720,11 +4720,18 @@ nbctl_lr_route_add(struct ctl_context *ctx) nbrec_logical_router_static_route_set_route_table(route, route_table); } - if (ecmp_symmetric_reply) { - const struct smap options = SMAP_CONST1(&options, - "ecmp_symmetric_reply", - "true"); + const char *ecmp_selection_fields = shash_find_data(&ctx->options, + "--ecmp-selection-fields"); + if (ecmp_symmetric_reply || ecmp_selection_fields) { + struct smap options = SMAP_INITIALIZER(&options); + if (ecmp_symmetric_reply) { + smap_add(&options, "ecmp_symmetric_reply", "true"); + } + if (ecmp_selection_fields) { + smap_add(&options, "ecmp_selection_fields", ecmp_selection_fields); + } nbrec_logical_router_static_route_set_options(route, &options); + smap_destroy(&options); } nbrec_logical_router_update_static_routes_addvalue(lr, route); @@ -8057,7 +8064,7 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", nbctl_pre_lr_route_add, nbctl_lr_route_add, NULL, "--may-exist,--ecmp,--ecmp-symmetric-reply,--policy=," - "--route-table=,--bfd?", RW }, + "--route-table=,--bfd?,--ecmp-selection-fields=", RW }, { "lr-route-del", 1, 4, "ROUTER [PREFIX [NEXTHOP [PORT]]]", nbctl_pre_lr_route_del, nbctl_lr_route_del, NULL, "--if-exists,--policy=,--route-table=", RW },