From patchwork Mon Apr 11 02:42:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wilson Peng X-Patchwork-Id: 1615516 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=vmware.com header.i=@vmware.com header.a=rsa-sha256 header.s=selector2 header.b=pCH1KM8m; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4KcCpK2H5Wz9sBJ for ; Mon, 11 Apr 2022 12:43:19 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id E854560E70; Mon, 11 Apr 2022 02:43:15 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bMFHHJ8i5pKT; Mon, 11 Apr 2022 02:43:13 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id B68B860B12; Mon, 11 Apr 2022 02:43:12 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 81DB2C0084; Mon, 11 Apr 2022 02:43:12 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 85845C002C for ; Mon, 11 Apr 2022 02:43:11 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 52E9860B12 for ; Mon, 11 Apr 2022 02:43:11 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fWp-GdrBSXFv for ; Mon, 11 Apr 2022 02:43:08 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12on20627.outbound.protection.outlook.com [IPv6:2a01:111:f400:fe5b::627]) by smtp3.osuosl.org (Postfix) with ESMTPS id 3A61E60B08 for ; Mon, 11 Apr 2022 02:43:08 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=CzpVXMbLbFYBXf4ygpLBJSLLjRnKfb9gnH0Qh55cwvLLGA5jeJwFiWZAGCExqGeRqpwkMAZwqml5uXLnpVcqIflfQQw7Uz65gLcQuuABT8MA5EiXx6xA4zzuOobGnHu9H2KjM+Lt2AG98p2JQVdymdCpISKb/kGlVWyBKYvPeVwwKUrWsh7xhwLIYZHj2fXISGGWkm95zRKfcgs8m436aTnf7RDO8GDEL9r9sA3EW+V+hdke6N8/e8uwR3IFKkn4oJc1rYw8pfBKgwIs2fJPvuT5iiUd7sfGIVKiwDDDBQFcTJPub92tSGvEtGpD/gLCrbP+sjwFGVtaooBjr25aDQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; 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=pSErlkQslTY4lLId7yYFEXBlGvCg1J3FyoCNzTeps4k=; b=RuyykANQlhjQndRzgzi/4V/ZCNGI3QeyUeJBji3QUSJJS5PsOKBgpfNNxQQUTuYOalw9bPe0KkxRfwXaqf14RcNV+sASSpkptHOKA+lLPLq3gePhJc8PFeB3iBHJat5F1dzi/mbf9Ud5V9SQkKEuN9N/LG1n46/kka4AP1cOnfeKNt7+YDM0y0f3t6OaTDJHDmiqDY9vbEK+4yqLtcdUhjDLD4Tacs1G6CZf0G7FK30YnTWt+DYWmFJZGzRS81m4sXncJtRrvycXh/I4EFBS2JGLefYwyuZQxOlhfLYQJIZmXKVqi9cBaT1CdFNyGCXnqN7tDKXRyNIVwjFPlVUfHg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=vmware.com; dmarc=pass action=none header.from=vmware.com; dkim=pass header.d=vmware.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vmware.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=pSErlkQslTY4lLId7yYFEXBlGvCg1J3FyoCNzTeps4k=; b=pCH1KM8m6dku9IgXiqI+oGXcSBWhiatOEk4W8LuAKJlu3UOvl+DH5T/uUUp0LA0/cM7zT0gRmQJpsOu6B20n9NMRLvQEQTxaJUyrYIClZHA+IarypIRlQTVw1B8cRSNtbuvhFz73W0nESxGlXAoAAx2VNlpa55QS/2vpKQ7/UJ0= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=vmware.com; Received: from SN6PR05MB5935.namprd05.prod.outlook.com (2603:10b6:805:100::18) by SN6PR05MB4285.namprd05.prod.outlook.com (2603:10b6:805:2e::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5164.17; Mon, 11 Apr 2022 02:43:00 +0000 Received: from SN6PR05MB5935.namprd05.prod.outlook.com ([fe80::94e7:cabc:36dc:4e37]) by SN6PR05MB5935.namprd05.prod.outlook.com ([fe80::94e7:cabc:36dc:4e37%7]) with mapi id 15.20.5164.017; Mon, 11 Apr 2022 02:43:00 +0000 From: ldejing To: dev@openvswitch.org Date: Mon, 11 Apr 2022 10:42:41 +0800 Message-Id: <20220411024241.81927-1-svc.ovs-community@vmware.com> X-Mailer: git-send-email 2.30.1 (Apple Git-130) X-ClientProxiedBy: TYAPR01CA0177.jpnprd01.prod.outlook.com (2603:1096:404:ba::21) To SN6PR05MB5935.namprd05.prod.outlook.com (2603:10b6:805:100::18) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 766cfc43-68f5-4042-03c9-08da1b64fd51 X-MS-TrafficTypeDiagnostic: SN6PR05MB4285:EE_ X-MS-Exchange-AtpMessageProperties: SA|SL X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5mj8mpgdzu+NMqlGK4+Tv0Tstk6SuZUlPo93Chz5VIpL8UuIwIV2NmDewP4fw+4QSDYphsDAsTl9SIGUQ2VXdll5qk6QI8byJW1EkLS7Fh2pm2707L0dOFZ/j6I1H/n2Ycc/B9RkAn73qp6QnyMu3eKkBZtMNEciF8Qu8gDb9FJ+wmrcEVZCsarZ++0vi1161DVIbtIl3QyowNdalb1mohqbFRvQYcSK6CEeETW9imviYPZLgFKbNOCOUbjn8QM6RQl4/ZhwsVVC3BIqzBTjiqWNTX6HwhU7Tk6TV/04k+nO0yaX6+zLax1koFdlnOcRd75UXfXnOV9xG8GxCXAee7yrEe50zBJUQ+NmDF9aRVdKrGUxXZayP/nJ+b1MHYIwvRcrZg58p95poyQoP1fyE/Q45t1EzkYgCHIAiNwoOlmV4qbCQhZmkjWdU1U1p5oEZHSAByAIr6mkx9TaUpw0b0X/Sx1iz00nA7fbW+1Uq9UCWhirV4zz3tg8bqef1CQ6fSkOwIIJK/kuCgJrlTnGRPOdqDkj7H903C693hbouI+JJ0CmExJ1KxHVpjq/uihox33JILs4J95riWPhQ5Q85rdHMh0Oz1ABVb3H1Vqzvl591u+X9j8We+XEdxM4ZWGwZNSSeZYU6hJLFPkmwI3/sHek9IUBJOAJSnNk5sLTdsg2t2xu7XvXT1nsP0C+TATAMEW9Mz285MoHXfz75uJUbPSQGVP3sd/gcqPua3SyoNnmusttcaeiFV+2dwqAXrFEx3YppNewLitmlk8IWEmc8Q== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SN6PR05MB5935.namprd05.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230001)(4636009)(366004)(66556008)(8676002)(4326008)(66476007)(508600001)(6486002)(2616005)(66946007)(1076003)(107886003)(6666004)(6506007)(86362001)(52116002)(6512007)(316002)(6916009)(19627235002)(83380400001)(186003)(36756003)(2906002)(5660300002)(30864003)(38100700002)(8936002)(43062005)(21314003)(559001)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: 4nIXKQ8VIWiJ/MmndE8wv6U+FzhswK5kR2oK7aGI7Qr/n8r21aTVrbrmGYmSbsFpJ4yKCIoin7jgYnhgPshGYPEGnKx32EeMJper8384oOPE1sFshoHNbAHAYILxKb/RTMLPwtgC4BwmcbE4i+jcCrfhqbHArmOhPbiEhNlieWnQM1/A82xdwd/usoKrF6lAtdoOpTT9ft+PXi+a7qJ1av5HWV2icYSNNRQN87tzwjoy6VQ4M39mgI4wDuI6PRjU/KLq9YOYwgu6r4ObX8h0hrDQfD1mcXQxIuEDRngU7pnet55epcjNnsaSDSqL4TEPB89j0PLaIS8G6QgtipB17gNmWyWzPLA1duWQjisPX1yOuXt3P1rPsmIljCjI1YB4CE3F0SJGGvUHUdr+3Cd1iyn6d69JRnF+l+CcZ7JdelmIhqrNgMxqvBHNC0CtkmZUk7xOLOtYtYsPfeVNcOmK46AEP0+hsOgcZLZRLnK6kr33kJbBiHW9a/icKJu01e9S7lz8AY0fp5m2EBQxjkT2ZsTLURd6nJBCL97kw4nsPsB12HEN9PhySarjsabz+fX9GqS9RXd040Cj2nTTXb7F03SanfrOgVuvliUJ3UydOpK5LibeuPqKymScZ6uM1QZiPKtgwnd6zkU0b8ZLDy+0jhNdQR1QnC34+GJOWg2DZNdT76Is5ZPa+HFHj56kKstxbayL3cCZyabNLH+jstEc0iN/qDtN0iwn3FGhyIsNJVrV+p4L5KqaPo0d/HXT1TzVaJjpf8iQgblHmLyO9NDUmkffwbYaLn7uyCIvcPNC+Mkfoi2LmQ/XlRuKhH14lENLoXLTJ88JkufANe2EQoaX6M8EA1KrzabgbCCGE4vcJ4WMLsjlJoXQEukwpqMNUQIChJ7alCvQ6uVPOl8RNMgjeLVibZGhuc0xipaUWJxjapA9A8GeJOxQg1WCC+03DOE9781nqrObIJo6Jvaa6qgHwK51ywzbthT919nvbz+PfeK0mTTjTn/g1PrxLuvTYFbpkFEY39duf+eu4psMB3zOSNrHnQV+BnujDs2af5Evqq5B9IczrVa/hdg9eosnO9awBrkNixgK+9SF2xzXArofl81DXjwGMpniCP8/0Ymz7EhHvGurfyX/f63C+LVa/sNVRwYVaDbecYbTNwDIanRI1U4JnAXKQVorD4Kx27dUmL/rkDjm+Hwg/4GhFPPPxDPdhqSyHKlycjZTdeVaIYEEkpTI9VTeZbauTfE28qLOFpLEWtv9Jp2MJpOrQIfDswv4BJEUoZ8JAwlk//Dn0KlkIf8MBXghUHgX2ZBFp2f3j46xBGqGgbLWbm5r+Crx42cvBWyEUAF72RNOu4vaeVotuPrPHsIH/qvX8hFgkvl3HVTOc+5xQQYEOUGy+2hOg7/334qzJ/rCD+FgblXM7XEYXNpAqwPOP4gJzsa6kc+Jg4FyZj8LGnvFmGIWn0O+055VbCzaeLMwYmUdbttaQnV5rvl7965J2LYLCPofFKbt1fRW0SlOXbu2IO16nVdq22UTkVuRMy4wkfvSwGOCfKSA2PJ6d0Oh1Fhd0+V5qPJ34YeW0tTWul5NzJ2J7WKQjPIq2BeEl/tmoANzMnTkTlINjJcrs3/DlvwgUCRgVwu/RD9r1KZZthRAgzTYTinETnnNI6c0sbM2rMtXCuuAvGqRnQJAT+c1DtSHE8FDoIV7BhiIuePZyjGKyswgRukQIVCkC94cMDrK01KnX6v9AOOATs5El26QqpaXnkQlfL5mWIKWvBAqGl+ljQWfhnxLtW2j6IQAL8tk X-MS-Exchange-AntiSpam-MessageData-1: ECDubZyH1ec2+bbauP8Q18IWg6b/ytgMLFs= X-OriginatorOrg: vmware.com X-MS-Exchange-CrossTenant-Network-Message-Id: 766cfc43-68f5-4042-03c9-08da1b64fd51 X-MS-Exchange-CrossTenant-AuthSource: SN6PR05MB5935.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Apr 2022 02:43:00.5407 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: b39138ca-3cee-4b4a-a4d6-cd83d9dd62f0 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: N6syo0s7ZqKfUBA+5y69OToaMes/Z39CQJ+44TwX/KoDBSVas1yNzrQPf8J9fiqJ8ZWot9U4iTSHzVXgCo23WBXBxFFYc4cjgoMdbUGQsVg= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR05MB4285 Cc: ldejing Subject: [ovs-dev] [PATCH v2 1/1] datapath-windows: Add IPv6 conntrack support on Windows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: ldejing Implementation on Windows: Currently, IPv4 conntrack was supported on the windows platform. In this patch we have implemented ipv6 conntrack functions according to the current logic of the IPv4 conntrack. This implementation has included TcpV6(nat and normal scenario), UdpV6(nat and normal scenario), IcmpV6 conntrack of echo request/reply packet and FtpV6(nat and normal scenario). Testing Topology: On the Windows VM runs on the ESXi host, two hyper-v ports attached to the ovs bridge; one hyper-v port worked as client and the other port worked as server. Testing Case: 1. TcpV6 a) Tcp request/reply conntrack for normal scenario. In this scenario, 20::1 as client, 20::2 as server, it will generate following conntrack entry: (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=1556), reply(src=20::2,src_port=1556,dst=20::1,dst_port=1555),protocol=tcp) b) Tcp request/reply conntrack for nat scenario. In this scenario, 20::1 as client, 20::10 as floating ip, 21::3 as server, it will generate following conntrack entry: (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=1556), reply(src=21::3, src_port=1556, dst=20::1, dst_port= 1555),protocol=tcp) 2. UdpV6 a) Udp request/reply conntrack for normal scenario. (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=1556), reply(src=20::2,src_port=1556,dst=20::1,dst_port=1555),protocol=udp) b) Udp request/reply conntrack for nat scenario. (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=1556), reply(src=21::3, src_port=1556, dst=20::1, dst_port= 1555),protocol=udp) 3. IcmpV6: a) Icmpv6 request/reply conntrack for normal scenario. Currently Icmpv6 only support to construct conntrack for echo request/reply packet, take (20::1 -> 20::2) for example, it will generate following conntrack entry: (origin(src = 20::1, dst=20::2), reply(src=20::2, dst=20::1), protocol=icmp) b) Icmp request/reply conntrack for dnat scenario, for example (20::1->20::10->21::3), 20::1 is client, 20::10 is floating ip, 21::3 is server ip. It will generate flow like below: (origin(src=20::1, dst=20::10), reply(src=21::3, dst=20::1), protocol=icmp) 4. FtpV6 a) Ftp request/reply conntrack for normal scenario. In this scenario, take 20::1 as client, 20::2 as server, it will generate two conntrack entries: Ftp active mode (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=21), reply(src=20::2, src_port=21, dst=20::1, dst_port=1555), protocol=tcp) (Origin(src=20::2, src_port=20, dst=20::1, dst_port=1556), reply(src=20::1, src_port=1556, dst=20::2, dst_port=20), protocol=tcp) Ftp passive mode (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=21), reply(src=20::2,src_port=21,dst=20::1,dst_port=1555),protocol=tcp) (Origin(src=20::1, src_port=1556, dst=20::2, dst_port=1557), reply(src=20::2,src_port=1557, dst=20::1, dst_port=1556) protocol=tcp) b) Ftp request/reply conntrack for nat scenario. Ftp passive mode, In this secnario, 20::1 as client, 20::10 as floating ip, 21::3 as server ip. It will generate following flow: (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=21), reply(src=21::3, src_port=21, dst=20::1, dst_port= 1555),protocol=tcp) (Origin(src=20::1, src_port=1556, dst=20::10, dst_port=1557), reply(src=21::3, src_port=1557, dst=20::1, dst_port= 1556),protocol=tcp) 5. Regression test for IpV4 in Antrea project (about 60 test case) Future work: 1) IcmpV6 redirect packet conntrack. 2) IpV6 fragment support on Udp. 3) Support napt for IPv6. 4) FtpV6 active mode for nat. Signed-off-by: ldejing --- Documentation/faq/releases.rst | 2 + Documentation/intro/install/windows.rst | 118 ++++++++ NEWS | 5 + datapath-windows/ovsext/Actions.c | 174 ++++++++++++ datapath-windows/ovsext/Actions.h | 5 + datapath-windows/ovsext/Conntrack-ftp.c | 159 +++++++++-- datapath-windows/ovsext/Conntrack-icmp.c | 11 + datapath-windows/ovsext/Conntrack-nat.c | 82 ++++-- datapath-windows/ovsext/Conntrack-related.c | 45 ++- datapath-windows/ovsext/Conntrack.c | 287 ++++++++++++++++++-- datapath-windows/ovsext/Conntrack.h | 34 +-- datapath-windows/ovsext/DpInternal.h | 2 + datapath-windows/ovsext/Flow.c | 41 ++- datapath-windows/ovsext/NetProto.h | 8 +- datapath-windows/ovsext/PacketParser.c | 1 + datapath-windows/ovsext/Util.c | 19 ++ datapath-windows/ovsext/Util.h | 21 ++ datapath-windows/ovsext/precomp.h | 1 + 18 files changed, 918 insertions(+), 97 deletions(-) diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst index 5edc1e204..ec54b8057 100644 --- a/Documentation/faq/releases.rst +++ b/Documentation/faq/releases.rst @@ -132,10 +132,12 @@ Q: Are all features available with all datapaths? Feature Linux upstream Linux OVS tree Userspace Hyper-V ========================== ============== ============== ========= ======= Connection tracking 4.3 2.5 2.6 YES + Connection tracking for IPv6 YES YES YES YES Conntrack Fragment Reass. 4.3 2.6 2.12 YES Conntrack Timeout Policies 5.2 2.12 2.14 NO Conntrack Zone Limit 4.18 2.10 2.13 YES Conntrack NAT 4.6 2.6 2.8 YES + Conntrack NAT6 4.6 2.6 2.8 YES Tunnel - LISP NO 2.11 NO NO Tunnel - STT NO 2.4 NO YES Tunnel - GRE 3.11 1.0 2.4 YES diff --git a/Documentation/intro/install/windows.rst b/Documentation/intro/install/windows.rst index 8e9442efb..a00762a7d 100644 --- a/Documentation/intro/install/windows.rst +++ b/Documentation/intro/install/windows.rst @@ -758,6 +758,124 @@ Add tunnels Till the checksum offload support is complete we recommend disabling TX/RX offloads for IPV6 on Windows VM. +Add conntrack for ipv6 +~~~~~~~~~~~~~~~~~~~~~~ + +The Windows Open vSwitch implementation support conntrack ipv6. To use the +conntrack ipv6. Using the following commands. Take tcp6(replace Protocol to +icmp6, udp6 to other protocol) for example. + + :: + + normal scenario + Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3) + Vif38Name="podvif38" + Vif40Name="podvif40" + Vif38Port=2 + Vif38Address="20::1" + Vif40Port=3 + Vif40Address="20::2" + Vif40MacAddressCli="00-15-5D-F0-01-0C" + Vif38MacAddressCli="00-15-5D-F0-01-0b" + Protocol="tcp6" + > netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli + > netsh int ipv6 set neighbors $Vif40Name $Vif38Address $Vif38MacAddressCli + > ovs-ofctl del-flows --strict br-int "table=0,priority=0" + > ovs-ofctl add-flow br-int "table=0,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=ct(table=1)" + > ovs-ofctl add-flow br-int "table=0,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=ct(table=1)" + > ovs-ofctl add-flow br-int "table=1,priority=1,ip6,ct_state=+new+trk,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=1,priority=2,ip6,ct_state=-new+rpl+trk,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=1,priority=1,ip6,ct_state=+trk+est-new,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port" + > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port" + + nat scenario + Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4) + Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually + Vif38Name="podvif38" + Vif42Name="podvif42" + Vif38Ip="20::1" + Vif38Port=2 + Vif42Port=4 + NatAddress="20::9" + NatMacAddress="aa:bb:cc:dd:ee:ff" + NatMacAddressForCli="aa-bb-cc-dd-ee-ff" + Vif42Ip="21::3" + Vif38MacAddress="00:15:5D:F0:01:0B" + Vif38MacAddressCli="00-15-5D-F0-01-0B" + Vif42MacAddress="00:15:5D:F0:01:0D" + Protocol="tcp6" + > netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli + > netsh int ipv6 set neighbors $Vif42Name $Vif38Ip $Vif38MacAddressCli + > ovs-ofctl del-flows --strict br-int "table=0,priority=0" + > ovs-ofctl add-flow br-int "table=0, priority=2,ipv6,dl_dst=$NatMacAddress,ct_state=-trk,$Protocol actions=ct(table=1,zone=456,nat)" + > ovs-ofctl add-flow br-int "table=0, priority=1,ipv6,ct_state=-trk,ip6,$Protocol actions=ct(nat, zone=456,table=1)" + > ovs-ofctl add-flow br-int "table=1, ipv6,in_port=$Vif38Port,ipv6_dst=$NatAddress,$Protocol,ct_state=+trk+new, actions=ct(commit,nat(dst=$Vif42Ip),zone=456,exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" + > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+dnat,$Protocol,action=resubmit(,2)" + > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+trk+snat,$Protocol,action=resubmit(,2)" + > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif38Port,ipv6_dst=$Vif42Ip,$Protocol, actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" + > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif42Port,ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol,actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress,output:$Vif38Port" + + :: + +Ftp is a specific protocol, it contains an related flow, we need to match is related state. + + :: + + normal scenario + Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3) + Vif38Name="podvif38" + Vif40Name="podvif40" + Vif38Port=2 + Vif38Address="20::1" + Vif38MacAddressCli="00-15-5D-F0-01-0b" + Vif40Port=3 + Vif40Address="20::2" + Vif40MacAddressCli="00-15-5D-F0-01-0C" + Protocol="tcp6" + > netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli + > netsh int ipv6 set neighbors $Vif40Name $Vif38Address $Vif38MacAddressCli + > ovs-ofctl del-flows br-int --strict "table=0,priority=0" + > ovs-ofctl add-flow br-int "table=0,priority=1,$Protocol actions=ct(table=1)" + > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=+new+trk-est,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est-rel,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est+rel,$Protocol,actions=ct(commit,table=2)" + > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port" + > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port" + + nat scenario + Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4) + Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually + Vif38Port=2 + Vif42Port=4 + Vif38Name="podvif38" + Vif42Name="podvif42" + NatAddress="20::9" + NatMacAddress="aa:bb:cc:dd:ee:ff" + NatMacAddressForCli="aa-bb-cc-dd-ee-ff" + Vif42Ip="21::3" + Vif38MacAddress="00:15:5D:F0:01:0B" + Vif42MacAddress="00:15:5D:F0:01:0D" + Protocol="tcp6" + > netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli + > netsh int ipv6 set neighbors $Vif42Name $NatAddress $NatMacAddressForCli + > ovs-ofctl del-flows br-int --strict "table=0,priority=0" + > ovs-ofctl add-flow br-int "table=0,priority=2,ipv6,dl_dst=$NatMacAddress,ct_state=-trk,$Protocol actions=ct(table=1,zone=456,nat)" + > ovs-ofctl add-flow br-int "table=0,priority=1,ipv6,ct_state=-trk,ip6,$Protocol actions=ct(nat, zone=456,table=1)" + > ovs-ofctl add-flow br-int "table=1,ipv6,in_port=$Vif38Port,ipv6_dst=$NatAddress,ct_state=+trk+new,$Protocol actions=ct(commit,nat(dst=$Vif42Ip),zone=456,exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" + > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+dnat,$Protocol,action=resubmit(,2)" + > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+snat,$Protocol,action=resubmit(,2)" + > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+rel,$Protocol,action=resubmit(,2)" + > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif38Port,ipv6_dst=$Vif42Ip,$Protocol, actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" + > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif42Port,ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol,actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress,output:$Vif38Port" + + :: + + note:: + + Till the checksum offload support is complete we recommend + disabling TX/RX offloads for IPV6 on Windows VM. + Windows Services ---------------- diff --git a/NEWS b/NEWS index 5074b97aa..5b97ca1ca 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +v2.17.0 - 27 Mar 2022 +--------------------- + - Tcp6, Udp6, Icmp6, ftp6 conntrack support on Windows ovs. + * In current OVS we could use ct related state and actions for Ftp6, Udp6, + Icmp6, Ftp6 connection. Post-v2.17.0 --------------------- - libopenvswitch API change: diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c index c66ece080..0f7f78932 100644 --- a/datapath-windows/ovsext/Actions.c +++ b/datapath-windows/ovsext/Actions.c @@ -1618,6 +1618,180 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, return NDIS_STATUS_SUCCESS; } +UINT16 +OvsCalculateICMPv6Checksum(struct in6_addr srcAddr, + struct in6_addr dstAddr, + uint16_t totalLength, + uint16_t protocol, + uint16_t *icmpStart, + uint16_t length) +{ + uint32_t checkSum = 0; + uint16_t *srcAddressPtr = (uint16_t *)&srcAddr; + uint16_t *dstAddressPtr = (uint16_t *)&dstAddr; + uint16_t *value = (uint16_t *)icmpStart; + int index = 0; + + checkSum = totalLength + protocol; + + for (int i = 0; i < 8; i++) { + checkSum += ntohs(srcAddressPtr[i]); + checkSum += ntohs(dstAddressPtr[i]); + } + + for (index = length; index > 1; index -= 2) { + checkSum += ntohs(*value); + value++; + } + + if (index > 0) { + checkSum += (uint16_t)(*((uint8_t *)value)); + } + + while ((checkSum >> 16) & 0xffff) { + checkSum = (checkSum & 0xffff) + ((checkSum >> 16) & 0xffff); + } + + return htons(~((uint16_t)checkSum)); +} + +/* + *----------------------------------------------------------------------------- + * + * OvsUpdateAddressAndPortForIpv6-- + * + * Update ipv6 address in ovsFwdCtx.curNbl. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ +NDIS_STATUS +OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx, + struct in6_addr newAddr, UINT16 newPort, + BOOLEAN isSource, BOOLEAN isTx) +{ + PUINT8 bufferStart; + UINT32 hdrSize; + OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers; + IPv6Hdr *ipHdr; + TCPHdr *tcpHdr = NULL; + UDPHdr *udpHdr = NULL; + struct in6_addr *addrField = NULL; + UINT16 *portField = NULL; + UINT16 *checkField = NULL; + BOOLEAN l4Offload = FALSE; + NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; + + ASSERT(layers->value != 0); + + if (layers->isTcp || layers->isUdp) { + hdrSize = layers->l4Offset + + layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr); + } else { + hdrSize = layers->l3Offset + sizeof (*ipHdr); + } + + csumInfo.Value = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl, + TcpIpChecksumNetBufferListInfo); + + bufferStart = OvsGetHeaderBySize(ovsFwdCtx, hdrSize); + if (!bufferStart) { + return NDIS_STATUS_RESOURCES; + } + + ipHdr = (IPv6Hdr *)(bufferStart + layers->l3Offset); + + if (layers->isTcp) { + tcpHdr = (TCPHdr *)(bufferStart + layers->l4Offset); + } else if (layers->isUdp) { + udpHdr = (UDPHdr *)(bufferStart + layers->l4Offset); + } + + if (isSource) { + addrField = &ipHdr->saddr; + if (tcpHdr) { + portField = &tcpHdr->source; + checkField = &tcpHdr->check; + l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum : + ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded || + (BOOLEAN)csumInfo.Receive.TcpChecksumFailed); + } else if (udpHdr) { + portField = &udpHdr->source; + checkField = &udpHdr->check; + l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum : + ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded || + (BOOLEAN)csumInfo.Receive.UdpChecksumFailed); + } + if (isTx && l4Offload) { + *checkField = IPv6PseudoChecksum((UINT32 *)&newAddr, (UINT32 *)&ipHdr->daddr, + tcpHdr ? IPPROTO_TCP : IPPROTO_UDP, + ntohs(ipHdr->payload_len) - + (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset)); + } + } else { + addrField = &ipHdr->daddr; + if (tcpHdr) { + portField = &tcpHdr->dest; + checkField = &tcpHdr->check; + l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum : + ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded || + (BOOLEAN)csumInfo.Receive.TcpChecksumFailed); + } else if (udpHdr) { + portField = &udpHdr->dest; + checkField = &udpHdr->check; + l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum : + ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded || + (BOOLEAN)csumInfo.Receive.UdpChecksumFailed); + } + + if (isTx && l4Offload) { + *checkField = IPv6PseudoChecksum((UINT32 *)&ipHdr->saddr, (UINT32 *)&newAddr, + tcpHdr ? IPPROTO_TCP : IPPROTO_UDP, + ntohs(ipHdr->payload_len) - + (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset)); + } + } + + if (memcmp(addrField, &newAddr, sizeof(struct in6_addr))) { + if ((checkField && *checkField != 0) && (!l4Offload || !isTx)) { + uint32_t *oldField = (uint32_t *)addrField; + uint32_t *newField = (uint32_t *)&newAddr; + *checkField = ChecksumUpdate32(*checkField, oldField[0], newField[0]); + *checkField = ChecksumUpdate32(*checkField, oldField[1], newField[1]); + *checkField = ChecksumUpdate32(*checkField, oldField[2], newField[2]); + *checkField = ChecksumUpdate32(*checkField, oldField[3], newField[3]); + } + + *addrField = newAddr; + + if (layers->isIcmp) { + ICMPHdr *icmp =(ICMPHdr *)(bufferStart + layers->l4Offset); + icmp->checksum = 0x00; + icmp->checksum = OvsCalculateICMPv6Checksum(ipHdr->saddr, ipHdr->daddr, + ntohs(ipHdr->payload_len), + IPPROTO_ICMPV6, + (uint16_t *)icmp, + ntohs(ipHdr->payload_len)); + } + } + + if (portField && *portField != newPort) { + if ((checkField) && (!l4Offload || !isTx)) { + /* Recompute total checksum. */ + *checkField = ChecksumUpdate16(*checkField, *portField, + newPort); + } + *portField = newPort; + } + + return NDIS_STATUS_SUCCESS; +} + /* *---------------------------------------------------------------------------- * OvsUpdateIPv4Header -- diff --git a/datapath-windows/ovsext/Actions.h b/datapath-windows/ovsext/Actions.h index b374c3a18..7329d0ed0 100644 --- a/datapath-windows/ovsext/Actions.h +++ b/datapath-windows/ovsext/Actions.h @@ -133,4 +133,9 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, UINT32 newAddr, UINT16 newPort, BOOLEAN isSource, BOOLEAN isTx); +NDIS_STATUS +OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx, + struct in6_addr newAddr, UINT16 newPort, + BOOLEAN isSource, BOOLEAN isTx); + #endif /* __ACTIONS_H_ */ diff --git a/datapath-windows/ovsext/Conntrack-ftp.c b/datapath-windows/ovsext/Conntrack-ftp.c index ce09a6528..066723685 100644 --- a/datapath-windows/ovsext/Conntrack-ftp.c +++ b/datapath-windows/ovsext/Conntrack-ftp.c @@ -14,15 +14,21 @@ * limitations under the License. */ +#include #include "Conntrack.h" #include "PacketParser.h" +#include "util.h" /* Eg: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)*/ #define FTP_PASV_RSP_PREFIX "227" +#define FTP_EXTEND_PASV_RSP_PREFIX "229" +#define FTP_EXTEND_ACTIVE_RSP_PREFIX "200" typedef enum FTP_TYPE { FTP_TYPE_PASV = 1, - FTP_TYPE_ACTIVE + FTP_TYPE_ACTIVE, + FTP_EXTEND_TYPE_PASV, + FTP_EXTEND_TYPE_ACTIVE } FTP_TYPE; static __inline UINT32 @@ -123,12 +129,11 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, POVS_CT_ENTRY entry, BOOLEAN request) { - NDIS_STATUS status; + NDIS_STATUS status = NDIS_STATUS_SUCCESS; FTP_TYPE ftpType = 0; const char *buf; char temp[256] = { 0 }; char ftpMsg[256] = { 0 }; - UINT32 len; TCPHdr tcpStorage; const TCPHdr *tcp; @@ -156,6 +161,9 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) { ftpType = FTP_TYPE_ACTIVE; req = ftpMsg + 4; + } else if ((len >= 5) && (OvsStrncmp("EPRT", ftpMsg, 4) == 0)) { + ftpType = FTP_EXTEND_TYPE_ACTIVE; + req = ftpMsg + 4; } } else { if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) { @@ -176,6 +184,25 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, /* PASV command without ( */ req = ftpMsg + 3; } + } else if ((len >= 4) && (OvsStrncmp(FTP_EXTEND_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) { + ftpType = FTP_EXTEND_TYPE_PASV; + /* The ftp extended passive mode only contain port info, ip address + * is same with the network protocol used by control connection. + * 229 Entering Extended Passive Mode (|||port|) + * */ + char *paren; + paren = strchr(ftpMsg, '|'); + if (paren) { + req = paren + 3; + } else { + /* Not a valid EPSV packet. */ + return NDIS_STATUS_INVALID_PACKET; + } + + if (!(*req > '0' && * req < '9')) { + /* Not a valid port number. */ + return NDIS_STATUS_INVALID_PACKET; + } } } @@ -184,28 +211,102 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, return NDIS_STATUS_SUCCESS; } - UINT32 arr[6] = {0}; - status = OvsCtExtractNumbers(req, len, arr, 6, ','); + struct ct_addr clientIp = {0}, serverIp = {0}; + UINT16 port = 0; - if (status != NDIS_STATUS_SUCCESS) { - return status; - } + if (ftpType == FTP_TYPE_ACTIVE || ftpType == FTP_TYPE_PASV) { + UINT32 arr[6] = {0}; + status = OvsCtExtractNumbers(req, len, arr, 6, ','); + + if (status != NDIS_STATUS_SUCCESS) { + return status; + } + + UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) | + (arr[2] << 8) | arr[3]); + port = ntohs(((arr[4] << 8) | arr[5])); + + serverIp.ipv4 = ip; + clientIp.ipv4 = key->ipKey.nwDst; + } else { + if (ftpType == FTP_EXTEND_TYPE_ACTIVE) { + /** In ftp active mode, we need to parse string like below: + * " |2|20::1|50778|", 2 represent address is ipv6, 1 represent + * address family is ipv4, "20::1" is ipv6 address, 50779 is port + * client need to listen. + * **/ + char *curHdr = NULL; + char *nextHdr = NULL; + int index = 0; + int isIpv6AddressFamily = 0; + char ftpStr[1024] = {0x00}; + + RtlCopyMemory(ftpStr, req, strlen(req)); + for (curHdr = ftpStr; *curHdr != '|'; curHdr++); + curHdr = curHdr + 1;; + do { + /** index == 0 parse address family, + * index == 1 parse address, + * index == 2 parse port **/ + for (nextHdr = curHdr; *nextHdr != '|'; nextHdr++); + *nextHdr = '\0'; + + if (*curHdr == '0' || !curHdr || index > 2) { + break; + } + + if (index == 0 && *curHdr == '1') { + isIpv6AddressFamily = 0; + } else if (index == 0 && *curHdr == '2') { + isIpv6AddressFamily = 1; + } + + if (index == 1 && isIpv6AddressFamily) { + OvsIpv6StringToAddress(curHdr, &clientIp.ipv6); + } + + if (index == 2) { + for (char *tmp = curHdr; *tmp != '\0'; tmp++) { + port = port * 10 + (*tmp - '0'); + } + port = htons(port); + } + + curHdr = nextHdr + 1; + index++; + } while (1); + + if (index < 2) { /* Not valid packet due to less than three parameter */ + return NDIS_STATUS_SUCCESS; + } + serverIp.ipv6 = key->ipv6Key.ipv6Dst; + } + + if (ftpType == FTP_EXTEND_TYPE_PASV) { + /* Here used to parse the string "229 Entering Extended Passive Mode (|||50522|), + * 50522 is the port we want". */ + char *tmp = req; + while (*tmp != '|' && *tmp != '\0') { + port = port * 10 + (*tmp - '0'); + tmp++; + } - UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) | - (arr[2] << 8) | arr[3]); - UINT16 port = ntohs(((arr[4] << 8) | arr[5])); + port = htons(port); + + serverIp.ipv6 = key->ipv6Key.ipv6Src; + clientIp.ipv6 = key->ipv6Key.ipv6Dst; + } + } switch (ftpType) { case FTP_TYPE_PASV: /* Ensure that the command states Server's IP address */ - ASSERT(ip == key->ipKey.nwSrc); - OvsCtRelatedEntryCreate(key->ipKey.nwProto, key->l2.dlType, /* Server's IP */ - ip, + serverIp, /* Use intended client's IP */ - key->ipKey.nwDst, + clientIp, /* Dynamic port opened on server */ port, /* We don't know the client port */ @@ -217,9 +318,33 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, OvsCtRelatedEntryCreate(key->ipKey.nwProto, key->l2.dlType, /* Server's default IP address */ - key->ipKey.nwDst, + serverIp, + /* Client's IP address */ + clientIp, + /* FTP Data Port is 20 */ + ntohs(IPPORT_FTP_DATA), + /* Port opened up on Client */ + port, + currentTime, + entry); + break; + case FTP_EXTEND_TYPE_PASV: + OvsCtRelatedEntryCreate(key->ipv6Key.nwProto, + key->l2.dlType, + serverIp, + clientIp, + port, + 0, + currentTime, + entry); + break; + case FTP_EXTEND_TYPE_ACTIVE: + OvsCtRelatedEntryCreate(key->ipv6Key.nwProto, + key->l2.dlType, + /* Server's default IP address */ + serverIp, /* Client's IP address */ - ip, + clientIp, /* FTP Data Port is 20 */ ntohs(IPPORT_FTP_DATA), /* Port opened up on Client */ diff --git a/datapath-windows/ovsext/Conntrack-icmp.c b/datapath-windows/ovsext/Conntrack-icmp.c index 28fe2bff8..9221f8518 100644 --- a/datapath-windows/ovsext/Conntrack-icmp.c +++ b/datapath-windows/ovsext/Conntrack-icmp.c @@ -70,6 +70,17 @@ OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp) || icmp->type == ICMP4_TIMESTAMP_REQUEST; } +BOOLEAN +OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp) +{ + if (!icmp) { + OVS_LOG_TRACE("Invalid ICMP packet detected, header cannot be NULL"); + return FALSE; + } + + return icmp->type == ICMP6_ECHO_REQUEST; +} + OVS_CT_ENTRY * OvsConntrackCreateIcmpEntry(UINT64 now) { diff --git a/datapath-windows/ovsext/Conntrack-nat.c b/datapath-windows/ovsext/Conntrack-nat.c index 1607d4c4f..497354ec8 100644 --- a/datapath-windows/ovsext/Conntrack-nat.c +++ b/datapath-windows/ovsext/Conntrack-nat.c @@ -42,12 +42,9 @@ OvsHashNatKey(const OVS_CT_KEY *key) static __inline BOOLEAN OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2) { - // XXX: Compare IPv6 key as well #define FIELD_COMPARE(field) \ if (key1->field != key2->field) return FALSE - FIELD_COMPARE(src.addr.ipv4_aligned); - FIELD_COMPARE(dst.addr.ipv4_aligned); FIELD_COMPARE(src.port); FIELD_COMPARE(dst.port); FIELD_COMPARE(zone); @@ -56,6 +53,15 @@ OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2) FIELD_COMPARE(dst.icmp_type); FIELD_COMPARE(src.icmp_code); FIELD_COMPARE(dst.icmp_code); + + if (memcmp(&(key1->src.addr), &(key2->src.addr), sizeof(struct ct_addr))) { + return FALSE; + } + + if (memcmp(&(key1->dst.addr), &(key2->dst.addr), sizeof(struct ct_addr))) { + return FALSE; + } + return TRUE; #undef FIELD_COMPARE } @@ -215,18 +221,37 @@ OvsNatPacket(OvsForwardingContext *ovsFwdCtx, } else { key->ipKey.nwDst = endpoint->addr.ipv4_aligned; } - } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)){ - // XXX: IPv6 packet not supported yet. - return; + } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) { + OvsUpdateAddressAndPortForIpv6(ovsFwdCtx, + endpoint->addr.ipv6, + endpoint->port, isSrcNat, + !reverse); + if (isSrcNat) { + key->ipv6Key.ipv6Src = endpoint->addr.ipv6; + } else { + key->ipv6Key.ipv6Dst = endpoint->addr.ipv6; + } } if (natAction & (NAT_ACTION_SRC_PORT | NAT_ACTION_DST_PORT)) { - if (isSrcNat) { - if (key->ipKey.l4.tpSrc != 0) { - key->ipKey.l4.tpSrc = endpoint->port; + if (ctKey->dl_type == htons(ETH_TYPE_IPV4)) { + if (isSrcNat) { + if (key->ipKey.l4.tpSrc != 0) { + key->ipKey.l4.tpSrc = endpoint->port; + } + } else { + if (key->ipKey.l4.tpDst != 0) { + key->ipKey.l4.tpDst = endpoint->port; + } } - } else { - if (key->ipKey.l4.tpDst != 0) { - key->ipKey.l4.tpDst = endpoint->port; + } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) { + if (isSrcNat) { + if (key->ipv6Key.l4.tpSrc != 0) { + key->ipv6Key.l4.tpSrc = endpoint->port; + } + } else { + if (key->ipv6Key.l4.tpDst != 0) { + key->ipv6Key.l4.tpDst = endpoint->port; + } } } } @@ -326,8 +351,8 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry) ctAddr.ipv4_aligned = htonl( ntohl(entry->natInfo.minAddr.ipv4_aligned) + addrIndex); } else { - // XXX: IPv6 not supported - return FALSE; + /** Current, only support nat single address**/ + ctAddr.ipv6_aligned = entry->natInfo.minAddr.ipv6_aligned; } port = firstPort; @@ -379,8 +404,33 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry) ctAddr.ipv4_aligned = htonl( ntohl(ctAddr.ipv4_aligned) + 1); } else { - // XXX: IPv6 not supported - return FALSE; + /** When all ports was used, return fails indicate exceed range. **/ + uint32_t addr[8] = {0}; + uint16_t *tmpAddr = (uint16_t *)&(ctAddr.ipv6_aligned); + for (int m = 0; m < 8; m++) { + addr[m] = tmpAddr[m]; + } + + uint16_t carry = 1, i = 8; + while (carry && i) + { + addr[i-1] += carry; + if (addr[i-1] > 0xffff || !addr[i-1]) + { + carry = 1; + addr[i-1] &= 0xffff; + } else carry = 0; + i--; + } + + if (carry) { + OVS_LOG_INFO("Ipv6 address incremented overflow."); + return FALSE; + } + + for (int m = 0; m < 8; m++) { + tmpAddr[m] = (uint16_t)addr[m]; + } } } else { ctAddr = entry->natInfo.minAddr; diff --git a/datapath-windows/ovsext/Conntrack-related.c b/datapath-windows/ovsext/Conntrack-related.c index a5bba5cf8..f985c7631 100644 --- a/datapath-windows/ovsext/Conntrack-related.c +++ b/datapath-windows/ovsext/Conntrack-related.c @@ -38,10 +38,22 @@ static __inline BOOLEAN OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey) { /* FTP PASV - Client initiates the connection from unknown port */ - if ((incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) && + if ((incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) && + (incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) && (incomingKey.dst.port == entryKey.src.port) && (incomingKey.src.addr.ipv4 == entryKey.dst.addr.ipv4) && - (incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.nw_proto == entryKey.nw_proto)) { + return TRUE; + } + + if ((incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) && + !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.src.addr.ipv6), + sizeof(incomingKey.dst.addr.ipv6)) && + (incomingKey.dst.port == entryKey.src.port) && + !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.dst.addr.ipv6), + sizeof(incomingKey.src.addr.ipv6)) && (incomingKey.nw_proto == entryKey.nw_proto)) { return TRUE; } @@ -51,10 +63,22 @@ OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey) * except 20. In this case, the incomingKey's src port is different with * entryKey's src port. */ - if ((incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) && + if ((incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) && + (incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) && (incomingKey.dst.addr.ipv4 == entryKey.dst.addr.ipv4) && (incomingKey.dst.port == entryKey.dst.port) && - (incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.nw_proto == entryKey.nw_proto)) { + return TRUE; + } + + if ((incomingKey.dl_type == entryKey.dl_type) && + (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) && + !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.src.addr.ipv6), + sizeof(incomingKey.src.addr.ipv6)) && + !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.dst.addr.ipv6), + sizeof(incomingKey.src.addr.ipv6)) && + (incomingKey.dst.port == entryKey.dst.port) && (incomingKey.nw_proto == entryKey.nw_proto)) { return TRUE; } @@ -110,8 +134,8 @@ OvsCtRelatedEntryDelete(POVS_CT_REL_ENTRY entry) NDIS_STATUS OvsCtRelatedEntryCreate(UINT8 ipProto, UINT16 dl_type, - UINT32 serverIp, - UINT32 clientIp, + struct ct_addr serverIp, + struct ct_addr clientIp, UINT16 serverPort, UINT16 clientPort, UINT64 currentTime, @@ -127,13 +151,18 @@ OvsCtRelatedEntryCreate(UINT8 ipProto, RtlZeroMemory(entry, sizeof(struct OVS_CT_REL_ENTRY)); entry->expiration = currentTime + (CT_INTERVAL_SEC * 60); - entry->key.src.addr.ipv4 = serverIp; - entry->key.dst.addr.ipv4 = clientIp; entry->key.nw_proto = ipProto; entry->key.dl_type = dl_type; entry->key.src.port = serverPort; entry->key.dst.port = clientPort; entry->parent = parent; + if (dl_type == htons(ETH_TYPE_IPV6)) { + entry->key.src.addr.ipv6 = serverIp.ipv6; + entry->key.dst.addr.ipv6 = clientIp.ipv6; + } else { + entry->key.src.addr.ipv4 = serverIp.ipv4; + entry->key.dst.addr.ipv4 = clientIp.ipv4; + } UINT32 hash = OvsExtractCtRelatedKeyHash(&entry->key); diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c index 237efa9e9..471bf961b 100644 --- a/datapath-windows/ovsext/Conntrack.c +++ b/datapath-windows/ovsext/Conntrack.c @@ -189,6 +189,13 @@ OvsCtSetZoneLimit(int zone, ULONG value) { NdisReleaseSpinLock(&ovsCtZoneLock); } +static uint32_t +OvsCtEndpointHashAdd(uint32_t hash, const struct ct_endpoint *ep) +{ + BUILD_ASSERT_DECL(sizeof *ep % 4 == 0); + return OvsJhashBytes((UINT32 *)ep, sizeof *ep, hash); +} + /* *---------------------------------------------------------------------------- * OvsCtHashKey @@ -199,8 +206,10 @@ UINT32 OvsCtHashKey(const OVS_CT_KEY *key) { UINT32 hsrc, hdst, hash; - hsrc = key->src.addr.ipv4 | ntohl(key->src.port); - hdst = key->dst.addr.ipv4 | ntohl(key->dst.port); + hsrc = ntohl(key->src.port); + hdst = ntohl(key->dst.port); + hsrc = OvsCtEndpointHashAdd(hsrc, &key->src); + hdst = OvsCtEndpointHashAdd(hdst, &key->dst); hash = hsrc ^ hdst; /* TO identify reverse traffic */ hash = hash | (key->zone + key->nw_proto); hash = OvsJhashWords((uint32_t*) &hash, 1, hash); @@ -341,6 +350,26 @@ OvsCtEntryCreate(OvsForwardingContext *fwdCtx, } break; } + case IPPROTO_ICMPV6: + { + ICMPHdr storage; + const ICMPHdr *icmp; + icmp = OvsGetIcmp(curNbl, layers->l4Offset, &storage); + if (!OvsConntrackValidateIcmp6Packet(icmp)) { + if(icmp) { + OVS_LOG_TRACE("Invalid ICMP packet detected, icmp->type %u", + icmp->type); + } + state = OVS_CS_F_INVALID; + break; + } + + if (commit) { + entry = OvsConntrackCreateIcmpEntry(currentTime); + } + break; + } + case IPPROTO_ICMP: { ICMPHdr storage; @@ -435,6 +464,15 @@ OvsCtUpdateEntry(OVS_CT_ENTRY* entry, NdisReleaseSpinLock(&(entry->lock)); break; } + + case IPPROTO_ICMPV6: + { + NdisAcquireSpinLock(&(entry->lock)); + status = OvsConntrackUpdateIcmpEntry(entry, reply, now); + NdisReleaseSpinLock(&(entry->lock)); + break; + } + case IPPROTO_UDP: { NdisAcquireSpinLock(&(entry->lock)); @@ -528,6 +566,23 @@ OvsDetectCtPacket(OvsForwardingContext *fwdCtx, } return NDIS_STATUS_NOT_SUPPORTED; case ETH_TYPE_IPV6: + if (key->ipv6Key.nwProto == IPPROTO_ICMPV6 + || key->ipv6Key.nwProto == IPPROTO_TCP + || key->ipv6Key.nwProto == IPPROTO_UDP) { + /** TODO fragment **/ + + /** Extract flow key from packet and assign it to + * returned parameter. **/ + status = OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo, + &newFlowKey, &fwdCtx->layers, + !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ? &(fwdCtx->tunKey) : NULL); + if (status != NDIS_STATUS_SUCCESS) { + OVS_LOG_ERROR("Extract flow for ipv6 failed Nbl %p", fwdCtx->curNbl); + return status; + } + *key = newFlowKey; + return NDIS_STATUS_SUCCESS; + } return NDIS_STATUS_NOT_SUPPORTED; } @@ -606,6 +661,54 @@ OvsCtLookup(OvsConntrackKeyLookupCtx *ctx) return found; } +const TCPHdr* +OvsGetTcpHeader(PNET_BUFFER_LIST nbl, + OVS_PACKET_HDR_INFO *layers, + VOID *storage, + UINT32 *tcpPayloadLen) +{ + IPHdr *ipHdr; + IPv6Hdr *ipv6Hdr; + TCPHdr *tcp; + VOID *dest = storage; + uint16_t ipv6ExtLength = 0; + + if (layers->isIPv6) { + ipv6Hdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl), + layers->l4Offset + sizeof(TCPHdr), + NULL, 1, 0); + if (ipv6Hdr == NULL) { + return NULL; + } + + tcp = (TCPHdr *)((PCHAR)ipv6Hdr + layers->l4Offset); + ipv6Hdr = (IPv6Hdr *)((PCHAR)ipv6Hdr + layers->l3Offset); + if (tcp->doff * 4 >= sizeof *tcp) { + NdisMoveMemory(dest, tcp, sizeof(TCPHdr)); + ipv6ExtLength = layers->l4Offset - layers->l3Offset - sizeof(IPv6Hdr); + *tcpPayloadLen = (ntohs(ipv6Hdr->payload_len) - ipv6ExtLength - TCP_HDR_LEN(tcp)); + return storage; + } + } else { + ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl), + layers->l4Offset + sizeof(TCPHdr), + NULL, 1 /*no align*/, 0); + if (ipHdr == NULL) { + return NULL; + } + + ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset); + tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4); + + if (tcp->doff * 4 >= sizeof *tcp) { + NdisMoveMemory(dest, tcp, sizeof(TCPHdr)); + *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp); + return storage; + } + } + return NULL; +} + static UINT8 OvsReverseIcmpType(UINT8 type) { @@ -622,6 +725,10 @@ OvsReverseIcmpType(UINT8 type) return ICMP4_INFO_REPLY; case ICMP4_INFO_REPLY: return ICMP4_INFO_REQUEST; + case ICMP6_ECHO_REQUEST: + return ICMP6_ECHO_REPLY; + case ICMP6_ECHO_REPLY: + return ICMP6_ECHO_REQUEST; default: return 0; } @@ -692,8 +799,8 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, return NDIS_STATUS_INVALID_PACKET; } /* Separate ICMP connection: identified using id */ - ctx->key.dst.icmp_id = icmp->fields.echo.id; - ctx->key.src.icmp_id = icmp->fields.echo.id; + ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id); + ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id); ctx->key.src.icmp_type = icmp->type; ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type); break; @@ -702,7 +809,6 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, case ICMP4_PARAM_PROB: case ICMP4_SOURCE_QUENCH: case ICMP4_REDIRECT: { - /* XXX Handle inner packet */ ctx->related = TRUE; break; } @@ -717,7 +823,53 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, ctx->key.src.port = flowKey->ipv6Key.l4.tpSrc; ctx->key.dst.port = flowKey->ipv6Key.l4.tpDst; - /* XXX Handle ICMPv6 errors*/ + if (flowKey->ipv6Key.nwProto == IPPROTO_ICMPV6) { + ICMPHdr icmpStorage; + const ICMPHdr *icmp; + icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage); + ASSERT(icmp); + + switch (icmp->type) { + case ICMP6_ECHO_REQUEST: + case ICMP6_ECHO_REPLY: { + ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id); + ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id); + ctx->key.src.icmp_type = icmp->type; + ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type); + break; + } + case ICMP6_DST_UNREACH: + case ICMP6_TIME_EXCEEDED: + case ICMP6_PARAM_PROB: + case ICMP6_PACKET_TOO_BIG: { + Ipv6Key ipv6Key; + OVS_PACKET_HDR_INFO layers; + OvsExtractLayers(curNbl, &layers); + layers.l3Offset = layers.l7Offset; + NDIS_STATUS status = OvsParseIPv6(curNbl, &ipv6Key, &layers); + if (status != NDIS_STATUS_SUCCESS) { + return NDIS_STATUS_INVALID_PACKET; + } + ctx->key.src.addr.ipv6 = ipv6Key.ipv6Src; + ctx->key.dst.addr.ipv6 = ipv6Key.ipv6Dst; + ctx->key.nw_proto = ipv6Key.nwProto; + if (ipv6Key.nwProto == SOCKET_IPPROTO_TCP) { + OvsParseTcp(curNbl, &(ipv6Key.l4), &layers); + } else if (ipv6Key.nwProto == SOCKET_IPPROTO_UDP) { + OvsParseUdp(curNbl, &(ipv6Key.l4), &layers); + } else if (ipv6Key.nwProto == SOCKET_IPPROTO_SCTP) { + OvsParseSctp(curNbl, &ipv6Key.l4, &layers); + } + ctx->key.src.port = ipv6Key.l4.tpSrc; + ctx->key.dst.port = ipv6Key.l4.tpDst; + OvsCtKeyReverse(&ctx->key); + ctx->related = TRUE; + break; + } + default: + ctx->related = FALSE; + } + } } else { return NDIS_STATUS_INVALID_PACKET; } @@ -744,6 +896,13 @@ OvsDetectFtpPacket(OvsFlowKey *key) { ntohs(key->ipKey.l4.tpSrc) == IPPORT_FTP)); } +static __inline BOOLEAN +OvsDetectFtp6Packet(OvsFlowKey *key) { + return (key->ipv6Key.nwProto == IPPROTO_TCP && + (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP || + ntohs(key->ipv6Key.l4.tpSrc) == IPPORT_FTP)); +} + /* *---------------------------------------------------------------------------- * OvsProcessConntrackEntry @@ -777,11 +936,22 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx, } else { CT_UPDATE_RES result; UINT32 bucketIdx; - result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers, - ctx->reply, currentTime); + + if (layers->isIPv6) { + result = OvsCtUpdateEntry(entry, curNbl, key->ipv6Key.nwProto, layers, + ctx->reply, currentTime); + } else { + result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers, + ctx->reply, currentTime); + } + switch (result) { case CT_UPDATE_VALID: state |= OVS_CS_F_ESTABLISHED; + /** when the ct state is established, at least + * request/reply two packets go through, + * so the ct_state shouldn't contain new.**/ + state &= ~OVS_CS_F_NEW; if (ctx->reply) { state |= OVS_CS_F_REPLY_DIR; } @@ -796,9 +966,17 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx, OvsCtEntryDelete(ctx->entry, TRUE); NdisReleaseRWLock(ovsCtBucketLock[bucketIdx], &lockStateTable); ctx->entry = NULL; - entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers, - ctx, key, natInfo, commit, currentTime, - entryCreated); + + if (layers->isIPv6) { + entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto, layers, + ctx, key, natInfo, commit, currentTime, + entryCreated); + } else { + entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers, + ctx, key, natInfo, commit, currentTime, + entryCreated); + } + if (!entry) { return NULL; } @@ -810,7 +988,8 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx, } if (entry) { NdisAcquireSpinLock(&(entry->lock)); - if (key->ipKey.nwProto == IPPROTO_TCP) { + if ((layers->isIPv6 && key->ipv6Key.nwProto == IPPROTO_TCP) || + (!(layers->isIPv6) && key->ipKey.nwProto == IPPROTO_TCP)) { /* Update the related bit if there is a parent */ if (entry->parent) { state |= OVS_CS_F_RELATED; @@ -938,6 +1117,29 @@ OvsCtUpdateTuple(OvsFlowKey *key, OVS_CT_KEY *ctKey) htons(ctKey->src.icmp_code); } +/* + * -------------------------------------------------------------------------- + * OvsCtUpdateTupleV6 name -- + * Update origin tuple for ipv6 packet. + * -------------------------------------------------------------------------- + */ +static __inline void +OvsCtUpdateTupleV6(OvsFlowKey *key, OVS_CT_KEY *ctKey) +{ + RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_src, &ctKey->src.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_src)); + RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_dst, &ctKey->dst.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_dst)); + key->ct.tuple_ipv6.ipv6_proto = ctKey->nw_proto; + + /* Orig tuple Port is overloaded to take in ICMP-Type & Code */ + /* This mimics the behavior in lib/conntrack.c*/ + key->ct.tuple_ipv6.src_port = ctKey->nw_proto != IPPROTO_ICMPV6 ? + ctKey->src.port : + htons(ctKey->src.icmp_type); + key->ct.tuple_ipv6.dst_port = ctKey->nw_proto != IPPROTO_ICMPV6 ? + ctKey->dst.port : + htons(ctKey->src.icmp_code); +} + static __inline NDIS_STATUS OvsCtExecute_(OvsForwardingContext *fwdCtx, OvsFlowKey *key, @@ -954,6 +1156,8 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx, NDIS_STATUS status = NDIS_STATUS_SUCCESS; BOOLEAN triggerUpdateEvent = FALSE; BOOLEAN entryCreated = FALSE; + BOOLEAN isFtpPacket = FALSE; + BOOLEAN isFtpRequestDirection = FALSE; POVS_CT_ENTRY entry = NULL; POVS_CT_ENTRY parent = NULL; PNET_BUFFER_LIST curNbl = fwdCtx->curNbl; @@ -1004,10 +1208,17 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx, return NDIS_STATUS_RESOURCES; } /* If no matching entry was found, create one and add New state */ - entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, - layers, &ctx, - key, natInfo, commit, currentTime, - &entryCreated); + if (key->l2.dlType == htons(ETH_TYPE_IPV6)) { + entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto, + layers, &ctx, + key, natInfo, commit, currentTime, + &entryCreated); + } else { + entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, + layers, &ctx, + key, natInfo, commit, currentTime, + &entryCreated); + } } if (entry == NULL) { @@ -1030,10 +1241,22 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx, OvsCtSetMarkLabel(key, entry, mark, labels, &triggerUpdateEvent); - if (OvsDetectFtpPacket(key)) { + if (layers->isIPv6) { + isFtpPacket = OvsDetectFtp6Packet(key); + if (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP) { + isFtpRequestDirection = TRUE; + } + } else { + isFtpPacket = OvsDetectFtpPacket(key); + if (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP) { + isFtpRequestDirection = TRUE; + } + } + + if (isFtpPacket) { /* FTP parser will always be loaded */ status = OvsCtHandleFtp(curNbl, key, layers, currentTime, entry, - (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP)); + isFtpRequestDirection); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_ERROR("Error while parsing the FTP packet"); } @@ -1064,6 +1287,14 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx, } else { OvsCtUpdateTuple(key, &entry->key); } + } else if (entry->key.dl_type == ntohs(ETH_TYPE_IPV6)) { + if (parent != NULL) { + OVS_ACQUIRE_SPIN_LOCK(&(parent->lock), irql); + OvsCtUpdateTupleV6(key, &parent->key); + OVS_RELEASE_SPIN_LOCK(&(parent->lock), irql); + } else { + OvsCtUpdateTupleV6(key, &entry->key); + } } if (entryCreated) { @@ -1400,13 +1631,15 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr, PNL_ATTR ctTupleAttrs[__CTA_MAX]; UINT32 attrOffset; static const NL_POLICY ctTuplePolicy[] = { - [CTA_TUPLE_IP] = {.type = NL_A_NESTED, .optional = FALSE }, - [CTA_TUPLE_PROTO] = {.type = NL_A_NESTED, .optional = FALSE}, + [CTA_TUPLE_IP] = { .type = NL_A_NESTED, .optional = FALSE }, + [CTA_TUPLE_PROTO] = { .type = NL_A_NESTED, .optional = FALSE}, }; static const NL_POLICY ctTupleIpPolicy[] = { [CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = TRUE }, [CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = TRUE }, + [CTA_IP_V6_SRC] = { .type = NL_A_BE32,.optional = TRUE }, + [CTA_IP_V6_DST] = { .type = NL_A_BE32,.optional = TRUE }, }; static const NL_POLICY ctTupleProtoPolicy[] = { @@ -1415,6 +1648,9 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr, [CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = TRUE }, [CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = TRUE }, [CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = TRUE }, + [CTA_PROTO_ICMPV6_ID] = { .type = NL_A_BE16,.optional = TRUE }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NL_A_U8,.optional = TRUE }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NL_A_U8,.optional = TRUE }, }; if (!ctAttr) { @@ -1467,8 +1703,11 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr, ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE] ) { ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_TYPE]); ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE]); + } else if (ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE] && + ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE] ) { + ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE]); + ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE]); } - } } @@ -1551,15 +1790,15 @@ MapProtoTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key) goto done; } } else if (key->nw_proto == IPPROTO_ICMPV6) { - if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, 0)) { + if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, htons(key->src.icmp_id))) { status = NDIS_STATUS_FAILURE; goto done; } - if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, 0)) { + if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, key->src.icmp_type)) { status = NDIS_STATUS_FAILURE; goto done; } - if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, 0)) { + if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, key->src.icmp_code)) { status = NDIS_STATUS_FAILURE; goto done; } diff --git a/datapath-windows/ovsext/Conntrack.h b/datapath-windows/ovsext/Conntrack.h index bbbf49c11..b68a54f30 100644 --- a/datapath-windows/ovsext/Conntrack.h +++ b/datapath-windows/ovsext/Conntrack.h @@ -167,33 +167,8 @@ OvsConntrackUpdateExpiration(OVS_CT_ENTRY *ctEntry, ctEntry->expiration = now + interval; } -static const TCPHdr* -OvsGetTcpHeader(PNET_BUFFER_LIST nbl, - OVS_PACKET_HDR_INFO *layers, - VOID *storage, - UINT32 *tcpPayloadLen) -{ - IPHdr *ipHdr; - TCPHdr *tcp; - VOID *dest = storage; - - ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl), - layers->l4Offset + sizeof(TCPHdr), - NULL, 1 /*no align*/, 0); - if (ipHdr == NULL) { - return NULL; - } - - ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset); - tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4); - if (tcp->doff * 4 >= sizeof *tcp) { - NdisMoveMemory(dest, tcp, sizeof(TCPHdr)); - *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp); - return storage; - } - - return NULL; -} +const TCPHdr* OvsGetTcpHeader(PNET_BUFFER_LIST nbl, OVS_PACKET_HDR_INFO *layers, + VOID *storage, UINT32 *tcpPayloadLen); VOID OvsCleanupConntrack(VOID); NTSTATUS OvsInitConntrack(POVS_SWITCH_CONTEXT context); @@ -203,6 +178,7 @@ NDIS_STATUS OvsExecuteConntrackAction(OvsForwardingContext *fwdCtx, const PNL_ATTR a); BOOLEAN OvsConntrackValidateTcpPacket(const TCPHdr *tcp); BOOLEAN OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp); +BOOLEAN OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp); OVS_CT_ENTRY * OvsConntrackCreateTcpEntry(const TCPHdr *tcp, UINT64 now, UINT32 tcpPayloadLen); @@ -235,8 +211,8 @@ NTSTATUS OvsInitCtRelated(POVS_SWITCH_CONTEXT context); VOID OvsCleanupCtRelated(VOID); NDIS_STATUS OvsCtRelatedEntryCreate(UINT8 ipProto, UINT16 dl_type, - UINT32 serverIp, - UINT32 clientIp, + struct ct_addr serverIp, + struct ct_addr clientIp, UINT16 serverPort, UINT16 clientPort, UINT64 currentTime, diff --git a/datapath-windows/ovsext/DpInternal.h b/datapath-windows/ovsext/DpInternal.h index b5027e35e..6af5748e1 100644 --- a/datapath-windows/ovsext/DpInternal.h +++ b/datapath-windows/ovsext/DpInternal.h @@ -207,6 +207,7 @@ typedef __declspec(align(8)) struct OvsFlowKey { UINT32 state; struct ovs_key_ct_labels labels; struct ovs_key_ct_tuple_ipv4 tuple_ipv4; + struct ovs_key_ct_tuple_ipv6 tuple_ipv6; } ct; /* Connection Tracking Flags */ } OvsFlowKey; @@ -217,6 +218,7 @@ typedef __declspec(align(8)) struct OvsFlowKey { #define OVS_ARP_KEY_SIZE (sizeof (ArpKey)) #define OVS_ICMPV6_KEY_SIZE (sizeof (Icmp6Key)) #define OVS_MPLS_KEY_SIZE (sizeof (MplsKey)) +#define OVS_TUPLE_IPV6 (sizeof (struct ovs_key_ct_tuple_ipv6)) typedef struct OvsFlowStats { Ovs64AlignedU64 packetCount; diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c index 0a7b2b1d4..08fba4c4d 100644 --- a/datapath-windows/ovsext/Flow.c +++ b/datapath-windows/ovsext/Flow.c @@ -186,6 +186,10 @@ const NL_POLICY nlFlowKeyPolicy[] = { [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_ct_tuple_ipv4), .maxLen = sizeof(struct ovs_key_ct_tuple_ipv4), + .optional = TRUE}, + [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = {.type = NL_A_UNSPEC, + .minLen = OVS_TUPLE_IPV6, + .maxLen = OVS_TUPLE_IPV6, .optional = TRUE} }; const UINT32 nlFlowKeyPolicyLen = ARRAY_SIZE(nlFlowKeyPolicy); @@ -1580,6 +1584,13 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs, sizeof(struct ovs_key_ct_tuple_ipv4)); } + if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) { + const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6; + tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]); + NdisMoveMemory(&destKey->ct.tuple_ipv6, tuple_ipv6, + sizeof(struct ovs_key_ct_tuple_ipv6)); + } + /* ===== L2 headers ===== */ if (keyAttrs[OVS_KEY_ATTR_IN_PORT]) { destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]); @@ -2170,6 +2181,13 @@ OvsGetFlowMetadata(OvsFlowKey *key, sizeof(struct ovs_key_ct_tuple_ipv4)); } + if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) { + const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6; + tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]); + NdisMoveMemory(&key->ct.tuple_ipv6, tuple_ipv6, + sizeof(struct ovs_key_ct_tuple_ipv6)); + } + return status; } @@ -2292,6 +2310,7 @@ OvsExtractLayers(const NET_BUFFER_LIST *packet, if (icmp) { layers->l7Offset = layers->l4Offset + sizeof *icmp; } + layers->isIcmp = 1; } } } else { @@ -2651,6 +2670,8 @@ FlowEqual(OvsFlow *srcFlow, sizeof(struct ovs_key_ct_labels)) && !memcmp(&srcFlow->key.ct.tuple_ipv4, &dstKey->ct.tuple_ipv4, sizeof(struct ovs_key_ct_tuple_ipv4)) && + !memcmp(&srcFlow->key.ct.tuple_ipv6, &dstKey->ct.tuple_ipv6, + sizeof(struct ovs_key_ct_tuple_ipv6)) && FlowMemoryEqual((UINT64 *)((UINT8 *)&srcFlow->key + offset), (UINT64 *) dstStart, size)); @@ -2763,13 +2784,20 @@ OvsLookupFlow(OVS_DATAPATH *datapath, 0); *hash = OvsJhashWords((UINT32*)hash, 1, lblHash); } - if (key->ct.tuple_ipv4.ipv4_src) { + if (key->ct.tuple_ipv4.ipv4_proto) { UINT32 tupleHash = OvsJhashBytes( &key->ct.tuple_ipv4, sizeof(struct ovs_key_ct_tuple_ipv4), 0); *hash = OvsJhashWords((UINT32*)hash, 1, tupleHash); } + + if (key->ct.tuple_ipv6.ipv6_proto) { + UINT32 tupleHash = OvsJhashBytes(&key->ct.tuple_ipv6, + sizeof(struct ovs_key_ct_tuple_ipv6), + 0); + *hash = OvsJhashWords((UINT32*)hash, 1, tupleHash); + } } head = &datapath->flowTable[HASH_BUCKET(*hash)]; @@ -2945,7 +2973,9 @@ ReportFlowInfo(OvsFlow *flow, NdisMoveMemory(&info->key.ct.tuple_ipv4, &flow->key.ct.tuple_ipv4, sizeof(struct ovs_key_ct_tuple_ipv4)); - + NdisMoveMemory(&info->key.ct.tuple_ipv6, + &flow->key.ct.tuple_ipv6, + sizeof(struct ovs_key_ct_tuple_ipv6)); return status; } @@ -3339,6 +3369,13 @@ OvsProbeSupportedFeature(POVS_MESSAGE msgIn, OVS_LOG_ERROR("Invalid ct_tuple_ipv4."); status = STATUS_INVALID_PARAMETER; } + } else if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) { + const struct ovs_key_ct_tuple_ipv6 *ct_tuple_ipv6; + ct_tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]); + if (!ct_tuple_ipv6) { + OVS_LOG_ERROR("Invalid ct_tuple_ipv6."); + status = STATUS_INVALID_PARAMETER; + } } else { OVS_LOG_ERROR("Feature not supported."); status = STATUS_INVALID_PARAMETER; diff --git a/datapath-windows/ovsext/NetProto.h b/datapath-windows/ovsext/NetProto.h index 9a17deee7..39726c203 100644 --- a/datapath-windows/ovsext/NetProto.h +++ b/datapath-windows/ovsext/NetProto.h @@ -239,7 +239,8 @@ typedef union _OVS_PACKET_HDR_INFO { UINT16 tcpCsumNeeded:1; UINT16 udpCsumNeeded:1; UINT16 udpCsumZero:1; - UINT16 pad:8; + UINT16 isIcmp:1; + UINT16 pad:7; } ; UINT64 value; } OVS_PACKET_HDR_INFO, *POVS_PACKET_HDR_INFO; @@ -300,6 +301,11 @@ typedef struct IPv6FragHdr { UINT32 ident; } IPv6FragHdr; +typedef struct IPv6OptHdr { + UINT8 nextHdr; + UINT8 hdrLen; +} IPv6OptHdr; + typedef struct IPv6NdOptHdr { UINT8 type; UINT8 len; diff --git a/datapath-windows/ovsext/PacketParser.c b/datapath-windows/ovsext/PacketParser.c index f8adcc3c9..aeead5899 100644 --- a/datapath-windows/ovsext/PacketParser.c +++ b/datapath-windows/ovsext/PacketParser.c @@ -318,6 +318,7 @@ OvsParseIcmpV6(const NET_BUFFER_LIST *packet, } layers->l7Offset = ofs; + layers->isIcmp = 1; return NDIS_STATUS_SUCCESS; invalid: diff --git a/datapath-windows/ovsext/Util.c b/datapath-windows/ovsext/Util.c index abd38c2fe..d703b2468 100644 --- a/datapath-windows/ovsext/Util.c +++ b/datapath-windows/ovsext/Util.c @@ -161,3 +161,22 @@ OvsPerCpuDataCleanup() { OvsDeferredActionsCleanup(); } + +NTSTATUS +OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr) +{ + NTSTATUS status = STATUS_SUCCESS; + char *terminator = NULL; + + status = RtlIpv6StringToAddressA(ip6String, &terminator, ipv6Addr); + return status; +} + +char * +OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String) +{ + char *returnedIpv6Str = NULL; + + returnedIpv6Str = RtlIpv6AddressToStringA((&ipv6Addr), ip6String); + return returnedIpv6Str; +} \ No newline at end of file diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h index a9bccf36c..f63a885a9 100644 --- a/datapath-windows/ovsext/Util.h +++ b/datapath-windows/ovsext/Util.h @@ -143,6 +143,27 @@ OvsPerCpuDataInit(); VOID OvsPerCpuDataCleanup(); + +/* + * -------------------------------------------------------------------------- + * OvsIpv6StringToAddress -- + * The function was used to convert ip6string to binary ipv6 address. + * -------------------------------------------------------------------------- + */ +NTSTATUS +OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr); + + +/* + * -------------------------------------------------------------------------- + * OvsIpv6AddressToString -- + * The function convert bindary ipv6Addr to string. + * -------------------------------------------------------------------------- + */ +char * +OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String); + + static LARGE_INTEGER seed; /* diff --git a/datapath-windows/ovsext/precomp.h b/datapath-windows/ovsext/precomp.h index 14f6843d3..e9f62f6ab 100644 --- a/datapath-windows/ovsext/precomp.h +++ b/datapath-windows/ovsext/precomp.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "Types.h"