From patchwork Fri Apr 8 18:27:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Karthik Chandrashekar X-Patchwork-Id: 1615053 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" (2048-bit key; unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=proofpoint20171006 header.b=JFYgUPaz; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (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 4KZmvB5S0lz9sG3 for ; Sat, 9 Apr 2022 04:27:34 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 042AB844B7; Fri, 8 Apr 2022 18:27:29 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6kJrkSjjcusg; Fri, 8 Apr 2022 18:27:27 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 8461E844C3; Fri, 8 Apr 2022 18:27:26 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 4A88EC0033; Fri, 8 Apr 2022 18:27:26 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 63520C002C for ; Fri, 8 Apr 2022 18:27:24 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 40AC040407 for ; Fri, 8 Apr 2022 18:27:24 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=nutanix.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xBxaWe8HT7f8 for ; Fri, 8 Apr 2022 18:27:21 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from mx0b-002c1b01.pphosted.com (mx0b-002c1b01.pphosted.com [148.163.155.12]) by smtp2.osuosl.org (Postfix) with ESMTPS id EB28340108 for ; Fri, 8 Apr 2022 18:27:20 +0000 (UTC) Received: from pps.filterd (m0127843.ppops.net [127.0.0.1]) by mx0b-002c1b01.pphosted.com (8.16.1.2/8.16.1.2) with ESMTP id 238CTR4t009098; Fri, 8 Apr 2022 11:27:19 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nutanix.com; h=from : to : cc : subject : date : message-id : content-transfer-encoding : content-type : mime-version; s=proofpoint20171006; bh=Qme4L6eXFFaO8Fp6FZmngxfKXg7NrHeMdWc7Afnfa6E=; b=JFYgUPazA5Fz2HSxJKI/F8sMoOF7buz24OYSITK+56BIJbteYmbBmnUtQOnEAx2r92nr 1aC2Gaf5umgt512mwu0yM+yAYMjNjvlIv13IvEyKfPsVFe04bRVcWVryZJX5MAny35N8 8SvbM880Mdcdiw+ZM/DfQSZJZJTv6TmLjntF6Ud1XPGbEqv3kV4NJGahViXdJAzLCltl OvM5Vg9+EoEpLD9VTamaPWAy4KqFyvTGPOSfZtzXPaX5sV9elFLAuZ8dmzn32Z37/NEs R8KVnyySsyyNi3ZaNrW5Y0d5ALaAq0JkaeE5h4h6lyKRZByhUi6Unmx8za8sl7p97YDv eQ== Received: from nam04-mw2-obe.outbound.protection.outlook.com (mail-mw2nam08lp2176.outbound.protection.outlook.com [104.47.73.176]) by mx0b-002c1b01.pphosted.com (PPS) with ESMTPS id 3fa4eqthck-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 08 Apr 2022 11:27:19 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=VB8Nv1pgYWMusPYOkM4FlDQSexBhDqyGTJX5yVSf0uTL2+YuLFe4mmd9B/PcPGvwIE5WyUJHSFc7B2Zsx9UCMw5FYuHxta1YnWOsTF9JwgobwNb+OJp02gqgy9dYdDXJJXyMKjDek3zYx8vvHrVQpo24yG5DIKykzHaz4N5q5s6AfQGAKhpwPUeXr9gxwsDDTEks94TBimdmeqvp91eJEdElPXBzIMJdSAL1g9IugaIkGKl00xktzMDNVP32PFpSViS+khuUhp90vD79T0H4ySVCyYwebn08UcmBMR8RmFRnZ0oD+RTXABr2Uso08zkAdHtqVoI2abzuKU1kpJMPGA== 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=Qme4L6eXFFaO8Fp6FZmngxfKXg7NrHeMdWc7Afnfa6E=; b=ezNt61WbukuKt86LbX8pt2uytp/3Lcls+qu9WITms78xe/2wlAuo6J2u5NRTeHtPSg9ggvVytFKKa6DtAXqEKa4UEyCrw0he47vpUgK5Moq/e8+seHw1xSKW0RJXBtaBqaV4nykdUj5K2reKQToNJPs+seHdPTpPxC5XWbLjkedQZvaJidJpw+zuzymQ8TU2bjNZ8IY2GbrMBB+OlUdYntTvRLin0bv/UswP++HieVcV40FbFQDknWxAQ8taDnH1R6TZS6cIfaR++SFHCzumbkM0c4NigXyXMbt8C6OLrHKCWOGdjkuLJcddVfm4cJss9E70Vob+ATtZoaK+oYeY6g== 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 Received: from MWHPR02MB3150.namprd02.prod.outlook.com (2603:10b6:301:61::38) by SN6PR02MB5518.namprd02.prod.outlook.com (2603:10b6:805:eb::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5144.26; Fri, 8 Apr 2022 18:27:16 +0000 Received: from MWHPR02MB3150.namprd02.prod.outlook.com ([fe80::f9fd:11cc:4c8:e44e]) by MWHPR02MB3150.namprd02.prod.outlook.com ([fe80::f9fd:11cc:4c8:e44e%4]) with mapi id 15.20.5144.026; Fri, 8 Apr 2022 18:27:15 +0000 From: karthik.c@nutanix.com To: ovs-dev@openvswitch.org Date: Fri, 8 Apr 2022 18:27:12 +0000 Message-Id: <20220408182712.244931-1-karthik.c@nutanix.com> X-Mailer: git-send-email 2.22.3 X-ClientProxiedBy: BY5PR03CA0001.namprd03.prod.outlook.com (2603:10b6:a03:1e0::11) To MWHPR02MB3150.namprd02.prod.outlook.com (2603:10b6:301:61::38) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 9cd8e581-a797-4937-0da8-08da198d6845 X-MS-TrafficTypeDiagnostic: SN6PR02MB5518:EE_ X-Microsoft-Antispam-PRVS: x-proofpoint-crosstenant: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: gBmADnXbz1tq4cLTb7NuGmmH/U2P1DSs82Vj6+1S9bSJ2wl5wR6cKdkXkEEM1hqCo3SQo+eworRqjGtWcgv4VqfTwxOQiD7NDku9LR5hh80+hkC42RB03BCxAXfNUz7soiJXjjPgr53AZvMedu/Pa67zfzOSw+grlompyp67NZAiqyToE+NVLeTAXdomAH/wWIDogqbFoXx+1XCLqNB3Cwgi7tr+9Rk4qJiq7C/rMyA7jtg4g54s1Gr6vs549dXj1a8mIk6UBMJRKw9Ylra0knzY9pMpW7VqOGeKwTTdRPtlozA9MSi5XCawjfVshzxTpnOqRPgBUjIHBegI5fKNYcefuRCorCI5JUObDXMGST6IADVh72YK1tlsMBPj+l7joxwZrOFBCycXhMhrChfJxP0LdemIw/fpyTLbt6BbDSp/OuO4jD65hD3q+i2+oe351kWP+zKZr9YcJ0p3lhb4PQxmV31V2s2X5CxqypYflmz5bxD6Fr8n3s06Z9iUA1Kj4XJMCVrANIIk9eWY0NBYVdHvOV/uqJNVIJQRY9d2OKFdNdWjhdbLmA3Zq7aYofvUNS9zgtWNcbOaZUhquyOqEh/eP3QR+OUELCecgnQiH+nogyhW/ospRQ44IEnPJA7CbJhKA+WBok2z/EV6SbnVh8AnPaYH5U2vUkWj50P0BV+t4kBdrtMJH4JUs48qvptMp5e4mjt4pKTp2Gpykq0ADRngaW49Vuc9lz7sXGtQDp23BGcWSoZL/X1dUrlYUK/sZV9hr+uVJUdF5qLw8gVs0Q== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:MWHPR02MB3150.namprd02.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230001)(366004)(508600001)(8936002)(8676002)(66946007)(66476007)(66556008)(66574015)(30864003)(6666004)(36756003)(6512007)(9686003)(6506007)(52116002)(4326008)(38100700002)(83380400001)(6486002)(966005)(5660300002)(38350700002)(1076003)(2906002)(54906003)(2616005)(6916009)(316002)(86362001)(26005)(186003)(579004); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: TL2TNB79UO3z6GHhw1GtQWw5pxQVoW3j+qgUGeO9bJ/K8hLn4TT95EyHAAp5a3pOt150fBWisZpoxymUGoI0bXZnb9AM/8ppQ8Q8m6eOR710lDqHxoPbi4jNQTIRbOapC9jXYU8+uT1O4xx+YMT9OQfG57n7bOmXuGNkOQYzCTekP1YwxxBhj1GvW9fSfHoS17zrbk8PETTRQRYyhuVnVrG4Xmp6cE7WY5xjW/xcRVx5Y7xeBnLI3OZhN4JT123zoRF+2aXwld9Fg5CtJXMppkx2+WWgtBq3c9S4gJqb+SijizcbZ5094rhnpyI36Kkwh6C7FngRi6vTZ298DqmDJl2ifaZEb21yfZCPZuZ9S8uJ7Cb+oEUd+boiVF06OA9GV7XKi5vavTPBzYnXjjhZ9MJnga6bBahPnDHPj1vJw4caVuHXnLmO1MnK1sp+dTNHaE38h94F6sKqn7ivRMAxV1Yy2uRA6ZLQWLSnbSViOHLSFFkBfGMJdzyfJGqyvPaAs/SNWeoQlUMAVeVIbFVc0f6TCU5LxXk+BA1fDpJWhLyfDBsWNOOYQf+SRIbm61vE30RbvPFFs1T7GOGSUvsDprG1BaB9cFDsurjrV0el6NMph2C9O57wEupCEzmdWSOUvr2UqjVtWNwMeNHdMklEDEjH4hBuHI/V9d1N4KM5pvLNBH8IP8/y1g7g7fvhKJRPA0WgZ2F5jEFsylhz7gv/SP65cSUySVy7RJfoo7VR+y46rUvNe2H5LIBvW7LkYBjvLgKNsRKZ5/R1x5wVvFojnmNRNyCS86TIjzezxS+c8w56sQPFILplkAan1adid2i7nV9py9gqJ24qu+ijUZIrfUBf8jTUTZi3ZbR0i4MjigAufllowcaL2qyDQ+UuoBIUyI8O4XpSrcCvkK11vR9X5hByglB8eA/1wy+NrhQdfrEeOqUWekRtUcqnCXdfYxABSY/6f2hM67tIvMtf8xqZwoiHNnY7RMsGQ9bSLQcVtGZu1zDeCe4WKOpUUfohP3/CjJS0vJnSaQ0rX/ySIhDREkmljcCC8aO2EWe9U7VfloKJQmkOuT6MtNgIRshKb6syxJpPc+1NqG4pkG6EAKTbYt8WLXZSnPg+UTmKQDI1Exet0Dr++M91SDdLQUa+j5VldVx0/WO84ZY+3tdrkKRO7oCjQHxTGoZe9g793fgVwFF2obAM3lCmyb8+n7dMj5o7gDus8Xv1rUOMg5s3NWSD3bAriLVaveqrSAEYxknZhPOevRiTBFL0no3Ac1/xJDyn09gxF8egB8cSYqof1qbOC8ydX4nxNlODKPoNW7Lhe8lStegPx7v9te53AAgKQ5o/nzeRNYjn2ZkRv8bqfU9z85GY+XKziTHUj3x2/TF1UyT/xjv+G9DDYziufyBcVi9tRq7AjUbOJ1beSyYW8b9K8YJgGgYNFYOpuntFvmSCsjwrMiV4faaQgplxSzUkzOhlWuvKLzsNUfSbkBiZRCL6PZ6Rip8wj10fL2x2LR0TDkVzAnZCv8djEYR5zi+PyaWBVeQU9qwtlF3DUbwNSBREEK8Zk5QEz9/xCFpivA9C/eXVa1UVhjnTHjuT3UMdrY4DOZuPxrO8WzDbT2smeVc8K8UzlrpdSkuhjNMRsOOT11KlCwELah/OjRaI2TroZHEmeBeGAPmGTNbRxUYmZO32yfN2QgDyAeI4sVqeeHHnkKP5QoVdpxXGQmGS6M4Jjo0msSpU/2TtpxrElcov+JvYNE63TjjFeIaw7T4YpyAFMYg= X-OriginatorOrg: nutanix.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9cd8e581-a797-4937-0da8-08da198d6845 X-MS-Exchange-CrossTenant-AuthSource: MWHPR02MB3150.namprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Apr 2022 18:27:15.8972 (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: SI83GjhbJAq8lsDzfUD0RvUAzr5oKaoEt7YbXm5CABr/Q8Sg9ZK0ieEjR6ILhJ6OH+OQ5TJWHLdQhd1FXa9QEQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR02MB5518 X-Proofpoint-GUID: zZNV22U8J9RJyvMn_lQB4xdy4jR900lC X-Proofpoint-ORIG-GUID: zZNV22U8J9RJyvMn_lQB4xdy4jR900lC X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.858,Hydra:6.0.425,FMLib:17.11.64.514 definitions=2022-04-08_05,2022-04-08_01,2022-02-23_01 X-Proofpoint-Spam-Reason: safe Cc: Karthik Chandrashekar Subject: [ovs-dev] [PATCH ovn v7] Add a northbound interface to program MAC_Binding table 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: Karthik Chandrashekar Add a new NB and SB table for managing Static MAC_Binding entries. This table is currently supported for logical routers. OVN northd is responsible for propagating the values from NB to SB. OVN controller is responsible for installation MAC lookup flows. The priority of the installed flows are based on override_dynamic_mac flag. This helps control the precedence of statically programmed vs dynamically learnt MAC Bindings. Signed-off-by: Karthik Chandrashekar Tetsted-by: Lorenzo Bianconi --- controller/lflow.c | 103 ++++++++++++++++----- controller/lflow.h | 10 +- controller/ovn-controller.c | 38 +++++++- lib/automake.mk | 2 + lib/static-mac-binding-index.c | 43 +++++++++ lib/static-mac-binding-index.h | 27 ++++++ northd/en-northd.c | 8 ++ northd/inc-proc-northd.c | 14 ++- northd/northd.c | 76 +++++++++++++++ northd/northd.h | 5 + ovn-nb.ovsschema | 15 ++- ovn-nb.xml | 29 ++++++ ovn-sb.ovsschema | 15 ++- ovn-sb.xml | 27 ++++++ tests/ovn-nbctl.at | 69 ++++++++++++++ tests/ovn-northd.at | 25 ++++- tests/ovn.at | 90 ++++++++++++++++++ utilities/ovn-nbctl.c | 164 ++++++++++++++++++++++++++++++++- 18 files changed, 720 insertions(+), 40 deletions(-) create mode 100644 lib/static-mac-binding-index.c create mode 100644 lib/static-mac-binding-index.h diff --git a/controller/lflow.c b/controller/lflow.c index 5f0374f72..976877a72 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -1622,40 +1622,50 @@ static void consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, const struct hmap *local_datapaths, const struct sbrec_mac_binding *b, - struct ovn_desired_flow_table *flow_table) + const struct sbrec_static_mac_binding *smb, + struct ovn_desired_flow_table *flow_table, + uint16_t priority) { + if (!b && !smb) { + return; + } + + char *logical_port = !b ? smb->logical_port : b->logical_port; + char *ip = !b ? smb->ip : b->ip; + char *mac = !b ? smb->mac : b->mac; + const struct sbrec_port_binding *pb - = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port); + = lport_lookup_by_name(sbrec_port_binding_by_name, logical_port); if (!pb || !get_local_datapath(local_datapaths, pb->datapath->tunnel_key)) { return; } - struct eth_addr mac; - if (!eth_addr_from_string(b->mac, &mac)) { + struct eth_addr mac_addr; + if (!eth_addr_from_string(mac, &mac_addr)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac); + VLOG_WARN_RL(&rl, "bad 'mac' %s", mac); return; } struct match get_arp_match = MATCH_CATCHALL_INITIALIZER; struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER; - if (strchr(b->ip, '.')) { - ovs_be32 ip; - if (!ip_parse(b->ip, &ip)) { + if (strchr(ip, '.')) { + ovs_be32 ip_addr; + if (!ip_parse(ip, &ip_addr)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); + VLOG_WARN_RL(&rl, "bad 'ip' %s", ip); return; } - match_set_reg(&get_arp_match, 0, ntohl(ip)); - match_set_reg(&lookup_arp_match, 0, ntohl(ip)); + match_set_reg(&get_arp_match, 0, ntohl(ip_addr)); + match_set_reg(&lookup_arp_match, 0, ntohl(ip_addr)); match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP)); } else { struct in6_addr ip6; - if (!ipv6_parse(b->ip, &ip6)) { + if (!ipv6_parse(ip, &ip6)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); + VLOG_WARN_RL(&rl, "bad 'ip' %s", ip); return; } ovs_be128 value; @@ -1678,20 +1688,22 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, uint64_t stub[1024 / 8]; struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); uint8_t value = 1; - put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts); + put_load(mac_addr.ea, sizeof mac_addr.ea, MFF_ETH_DST, 0, 48, &ofpacts); put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, &ofpacts); - ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, - b->header_.uuid.parts[0], &get_arp_match, - &ofpacts, &b->header_.uuid); + ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, priority, + !b ? smb->header_.uuid.parts[0] : b->header_.uuid.parts[0], + &get_arp_match, &ofpacts, + !b ? &smb->header_.uuid : &b->header_.uuid); ofpbuf_clear(&ofpacts); put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, &ofpacts); - match_set_dl_src(&lookup_arp_match, mac); - ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, 100, - b->header_.uuid.parts[0], &lookup_arp_match, - &ofpacts, &b->header_.uuid); + match_set_dl_src(&lookup_arp_match, mac_addr); + ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, priority, + !b ? smb->header_.uuid.parts[0] : b->header_.uuid.parts[0], + &lookup_arp_match, &ofpacts, + !b ? &smb->header_.uuid : &b->header_.uuid); ofpbuf_uninit(&ofpacts); } @@ -1701,13 +1713,23 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, static void add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name, const struct sbrec_mac_binding_table *mac_binding_table, + const struct sbrec_static_mac_binding_table *smb_table, const struct hmap *local_datapaths, struct ovn_desired_flow_table *flow_table) { + /* Add flows for learnt MAC bindings */ const struct sbrec_mac_binding *b; SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) { consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, - b, flow_table); + b, NULL, flow_table, 100); + } + + /* Add flows for statically configured MAC bindings */ + const struct sbrec_static_mac_binding *smb; + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH (smb, smb_table) { + consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, + NULL, smb, flow_table, + smb->override_dynamic_mac ? 150 : 50); } } @@ -2366,7 +2388,7 @@ add_lb_hairpin_flows(const struct sbrec_load_balancer_table *lb_table, /* Handles neighbor changes in mac_binding table. */ void -lflow_handle_changed_neighbors( +lflow_handle_changed_mac_bindings( struct ovsdb_idl_index *sbrec_port_binding_by_name, const struct sbrec_mac_binding_table *mac_binding_table, const struct hmap *local_datapaths, @@ -2393,7 +2415,36 @@ lflow_handle_changed_neighbors( VLOG_DBG("handle new mac_binding "UUID_FMT, UUID_ARGS(&mb->header_.uuid)); consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, - mb, flow_table); + mb, NULL, flow_table, 100); + } + } +} + +/* Handles changes to static_mac_binding table. */ +void +lflow_handle_changed_static_mac_bindings( + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_static_mac_binding_table *smb_table, + const struct hmap *local_datapaths, + struct ovn_desired_flow_table *flow_table) +{ + const struct sbrec_static_mac_binding *smb; + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (smb, smb_table) { + if (sbrec_static_mac_binding_is_deleted(smb)) { + VLOG_DBG("handle deleted static_mac_binding "UUID_FMT, + UUID_ARGS(&smb->header_.uuid)); + ofctrl_remove_flows(flow_table, &smb->header_.uuid); + } else { + if (!sbrec_static_mac_binding_is_new(smb)) { + VLOG_DBG("handle updated static_mac_binding "UUID_FMT, + UUID_ARGS(&smb->header_.uuid)); + ofctrl_remove_flows(flow_table, &smb->header_.uuid); + } + VLOG_DBG("handle new static_mac_binding "UUID_FMT, + UUID_ARGS(&smb->header_.uuid)); + consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, + NULL, smb, flow_table, + smb->override_dynamic_mac ? 150 : 50); } } } @@ -2463,7 +2514,9 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out) add_logical_flows(l_ctx_in, l_ctx_out); add_neighbor_flows(l_ctx_in->sbrec_port_binding_by_name, - l_ctx_in->mac_binding_table, l_ctx_in->local_datapaths, + l_ctx_in->mac_binding_table, + l_ctx_in->static_mac_binding_table, + l_ctx_in->local_datapaths, l_ctx_out->flow_table); add_lb_hairpin_flows(l_ctx_in->lb_table, l_ctx_in->local_datapaths, l_ctx_in->check_ct_label_for_lb_hairpin, diff --git a/controller/lflow.h b/controller/lflow.h index 3909605f0..4003a15a5 100644 --- a/controller/lflow.h +++ b/controller/lflow.h @@ -147,6 +147,7 @@ struct lflow_ctx_in { const struct sbrec_fdb_table *fdb_table; const struct sbrec_chassis *chassis; const struct sbrec_load_balancer_table *lb_table; + const struct sbrec_static_mac_binding_table *static_mac_binding_table; const struct hmap *local_datapaths; const struct shash *addr_sets; const struct shash *port_groups; @@ -192,9 +193,14 @@ bool lflow_handle_addr_set_update(const char *as_name, struct addr_set_diff *, struct lflow_ctx_out *, bool *changed); -void lflow_handle_changed_neighbors( +void lflow_handle_changed_mac_bindings( struct ovsdb_idl_index *sbrec_port_binding_by_name, - const struct sbrec_mac_binding_table *, + const struct sbrec_mac_binding_table *mac_binding_table, + const struct hmap *local_datapaths, + struct ovn_desired_flow_table *); +void lflow_handle_changed_static_mac_bindings( + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_static_mac_binding_table *smb_table, const struct hmap *local_datapaths, struct ovn_desired_flow_table *); bool lflow_handle_changed_lbs(struct lflow_ctx_in *, struct lflow_ctx_out *); diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index dd52b45bf..5a215d46d 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -977,7 +977,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) SB_NODE(dns, "dns") \ SB_NODE(load_balancer, "load_balancer") \ SB_NODE(fdb, "fdb") \ - SB_NODE(meter, "meter") + SB_NODE(meter, "meter") \ + SB_NODE(static_mac_binding, "static_mac_binding") enum sb_engine_node { #define SB_NODE(NAME, NAME_STR) SB_##NAME, @@ -2362,6 +2363,10 @@ init_lflow_ctx(struct engine_node *node, (struct sbrec_fdb_table *)EN_OVSDB_GET( engine_get_input("SB_fdb", node)); + struct sbrec_static_mac_binding_table *smb_table = + (struct sbrec_static_mac_binding_table *)EN_OVSDB_GET( + engine_get_input("SB_static_mac_binding", node)); + struct ovsrec_open_vswitch_table *ovs_table = (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( engine_get_input("OVS_open_vswitch", node)); @@ -2414,6 +2419,7 @@ init_lflow_ctx(struct engine_node *node, l_ctx_in->fdb_table = fdb_table, l_ctx_in->chassis = chassis; l_ctx_in->lb_table = lb_table; + l_ctx_in->static_mac_binding_table = smb_table; l_ctx_in->local_datapaths = &rt_data->local_datapaths; l_ctx_in->addr_sets = addr_sets; l_ctx_in->port_groups = port_groups; @@ -2558,13 +2564,39 @@ lflow_output_sb_mac_binding_handler(struct engine_node *node, void *data) struct ed_type_lflow_output *lfo = data; - lflow_handle_changed_neighbors(sbrec_port_binding_by_name, + lflow_handle_changed_mac_bindings(sbrec_port_binding_by_name, mac_binding_table, local_datapaths, &lfo->flow_table); engine_set_node_state(node, EN_UPDATED); return true; } +static bool +lflow_output_sb_static_mac_binding_handler(struct engine_node *node, + void *data) +{ + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); + + struct sbrec_static_mac_binding_table *smb_table = + (struct sbrec_static_mac_binding_table *)EN_OVSDB_GET( + engine_get_input("SB_static_mac_binding", node)); + + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + const struct hmap *local_datapaths = &rt_data->local_datapaths; + + struct ed_type_lflow_output *lfo = data; + + lflow_handle_changed_static_mac_bindings(sbrec_port_binding_by_name, + smb_table, local_datapaths, &lfo->flow_table); + + engine_set_node_state(node, EN_UPDATED); + return true; +} + static bool lflow_output_sb_multicast_group_handler(struct engine_node *node, void *data) { @@ -3401,6 +3433,8 @@ main(int argc, char *argv[]) engine_add_input(&en_lflow_output, &en_sb_mac_binding, lflow_output_sb_mac_binding_handler); + engine_add_input(&en_lflow_output, &en_sb_static_mac_binding, + lflow_output_sb_static_mac_binding_handler); engine_add_input(&en_lflow_output, &en_sb_logical_flow, lflow_output_sb_logical_flow_handler); /* Using a noop handler since we don't really need any data from datapath diff --git a/lib/automake.mk b/lib/automake.mk index 829aedfc5..3a2da1fe4 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -38,6 +38,8 @@ lib_libovn_la_SOURCES = \ lib/inc-proc-eng.h \ lib/lb.c \ lib/lb.h \ + lib/static-mac-binding-index.c \ + lib/static-mac-binding-index.h \ lib/stopwatch-names.h \ lib/vif-plug-provider.h \ lib/vif-plug-provider.c \ diff --git a/lib/static-mac-binding-index.c b/lib/static-mac-binding-index.c new file mode 100644 index 000000000..fecc9b74f --- /dev/null +++ b/lib/static-mac-binding-index.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "lib/static-mac-binding-index.h" +#include "lib/ovn-sb-idl.h" + +struct ovsdb_idl_index * +static_mac_binding_index_create(struct ovsdb_idl *idl) +{ + return ovsdb_idl_index_create2(idl, + &sbrec_static_mac_binding_col_logical_port, + &sbrec_static_mac_binding_col_ip); +} + +const struct sbrec_static_mac_binding * +static_mac_binding_lookup(struct ovsdb_idl_index *smb_index, + const char *logical_port, const char *ip) +{ + struct sbrec_static_mac_binding *target = + sbrec_static_mac_binding_index_init_row(smb_index); + sbrec_static_mac_binding_index_set_logical_port(target, logical_port); + sbrec_static_mac_binding_index_set_ip(target, ip); + + struct sbrec_static_mac_binding *smb = + sbrec_static_mac_binding_index_find(smb_index, target); + sbrec_static_mac_binding_index_destroy_row(target); + + return smb; +} diff --git a/lib/static-mac-binding-index.h b/lib/static-mac-binding-index.h new file mode 100644 index 000000000..3d4ff06a2 --- /dev/null +++ b/lib/static-mac-binding-index.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_STATIC_MAC_BINDING_INDEX_H +#define OVN_STATIC_MAC_BINDING_INDEX_H 1 + +struct ovsdb_idl; + +struct ovsdb_idl_index *static_mac_binding_index_create(struct ovsdb_idl *); +const struct sbrec_static_mac_binding *static_mac_binding_lookup( + struct ovsdb_idl_index *smb_index, + const char *logical_port, + const char *ip); + +#endif /* lib/static-mac-binding-index.h */ diff --git a/northd/en-northd.c b/northd/en-northd.c index 79da7e1c4..4907a1ff2 100644 --- a/northd/en-northd.c +++ b/northd/en-northd.c @@ -55,6 +55,10 @@ void en_northd_run(struct engine_node *node, void *data) engine_ovsdb_node_get_index( engine_get_input("SB_ip_multicast", node), "sbrec_ip_mcast_by_dp"); + input_data.sbrec_static_mac_binding_by_lport_ip = + engine_ovsdb_node_get_index( + engine_get_input("SB_static_mac_binding", node), + "sbrec_static_mac_binding_by_lport_ip"); input_data.nbrec_nb_global_table = EN_OVSDB_GET(engine_get_input("NB_nb_global", node)); @@ -72,6 +76,8 @@ void en_northd_run(struct engine_node *node, void *data) EN_OVSDB_GET(engine_get_input("NB_meter", node)); input_data.nbrec_acl_table = EN_OVSDB_GET(engine_get_input("NB_acl", node)); + input_data.nbrec_static_mac_binding_table = + EN_OVSDB_GET(engine_get_input("NB_static_mac_binding", node)); input_data.sbrec_sb_global_table = EN_OVSDB_GET(engine_get_input("SB_sb_global", node)); @@ -103,6 +109,8 @@ void en_northd_run(struct engine_node *node, void *data) EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node)); input_data.sbrec_chassis_private_table = EN_OVSDB_GET(engine_get_input("SB_chassis_private", node)); + input_data.sbrec_static_mac_binding_table = + EN_OVSDB_GET(engine_get_input("SB_static_mac_binding", node)); northd_run(&input_data, data, eng_ctx->ovnnb_idl_txn, diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index af55221e3..43093cb5a 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -20,6 +20,7 @@ #include "chassis-index.h" #include "ip-mcast-index.h" +#include "static-mac-binding-index.h" #include "lib/inc-proc-eng.h" #include "lib/ovn-nb-idl.h" #include "lib/ovn-sb-idl.h" @@ -60,7 +61,8 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); NB_NODE(gateway_chassis, "gateway_chassis") \ NB_NODE(ha_chassis_group, "ha_chassis_group") \ NB_NODE(ha_chassis, "ha_chassis") \ - NB_NODE(bfd, "bfd") + NB_NODE(bfd, "bfd") \ + NB_NODE(static_mac_binding, "static_mac_binding") enum nb_engine_node { #define NB_NODE(NAME, NAME_STR) NB_##NAME, @@ -109,7 +111,8 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); SB_NODE(service_monitor, "service_monitor") \ SB_NODE(load_balancer, "load_balancer") \ SB_NODE(bfd, "bfd") \ - SB_NODE(fdb, "fdb") + SB_NODE(fdb, "fdb") \ + SB_NODE(static_mac_binding, "static_mac_binding") enum sb_engine_node { #define SB_NODE(NAME, NAME_STR) SB_##NAME, @@ -178,6 +181,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_nb_gateway_chassis, NULL); engine_add_input(&en_northd, &en_nb_ha_chassis_group, NULL); engine_add_input(&en_northd, &en_nb_ha_chassis, NULL); + engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL); engine_add_input(&en_northd, &en_sb_sb_global, NULL); engine_add_input(&en_northd, &en_sb_chassis, NULL); @@ -206,6 +210,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_sb_service_monitor, NULL); engine_add_input(&en_northd, &en_sb_load_balancer, NULL); engine_add_input(&en_northd, &en_sb_fdb, NULL); + engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL); engine_add_input(&en_lflow, &en_nb_bfd, NULL); engine_add_input(&en_lflow, &en_sb_bfd, NULL); engine_add_input(&en_lflow, &en_sb_logical_flow, NULL); @@ -228,6 +233,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, ip_mcast_index_create(sb->idl); struct ovsdb_idl_index *sbrec_chassis_by_hostname = chassis_hostname_index_create(sb->idl); + struct ovsdb_idl_index *sbrec_static_mac_binding_by_lport_ip + = static_mac_binding_index_create(sb->idl); engine_init(&en_lflow, &engine_arg); @@ -246,6 +253,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_ovsdb_node_add_index(&en_sb_ip_multicast, "sbrec_ip_mcast_by_dp", sbrec_ip_mcast_by_dp); + engine_ovsdb_node_add_index(&en_sb_static_mac_binding, + "sbrec_static_mac_binding_by_lport_ip", + sbrec_static_mac_binding_by_lport_ip); } void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn, diff --git a/northd/northd.c b/northd/northd.c index 5551064d7..e5864fe02 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -28,6 +28,7 @@ #include "ovn/lex.h" #include "lib/chassis-index.h" #include "lib/ip-mcast-index.h" +#include "lib/static-mac-binding-index.h" #include "lib/copp.h" #include "lib/mcast-group-index.h" #include "lib/ovn-l7.h" @@ -15054,6 +15055,80 @@ build_meter_groups(struct northd_input *input_data, } } +static const struct nbrec_static_mac_binding * +static_mac_binding_by_port_ip(struct northd_input *input_data, + const char *logical_port, const char *ip) +{ + const struct nbrec_static_mac_binding *nb_smb = NULL; + + NBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH ( + nb_smb, input_data->nbrec_static_mac_binding_table) { + if (!strcmp(nb_smb->logical_port, logical_port) && + !strcmp(nb_smb->ip, ip)) { + break; + } + } + + return nb_smb; +} + +static void +build_static_mac_binding_table(struct northd_input *input_data, + struct ovsdb_idl_txn *ovnsb_txn, + struct hmap *ports) +{ + /* Cleanup SB Static_MAC_Binding entries which do not have corresponding + * NB Static_MAC_Binding entries. */ + const struct nbrec_static_mac_binding *nb_smb; + const struct sbrec_static_mac_binding *sb_smb, *sb_smb_next; + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH_SAFE (sb_smb, sb_smb_next, + input_data->sbrec_static_mac_binding_table) { + nb_smb = static_mac_binding_by_port_ip(input_data, + sb_smb->logical_port, + sb_smb->ip); + if (!nb_smb) { + sbrec_static_mac_binding_delete(sb_smb); + } + } + + /* Create/Update SB Static_MAC_Binding entries with corresponding values + * from NB Static_MAC_Binding entries. */ + NBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH ( + nb_smb, input_data->nbrec_static_mac_binding_table) { + struct ovn_port *op = ovn_port_find(ports, nb_smb->logical_port); + if (op && op->nbrp) { + struct ovn_datapath *od = op->od; + if (od && od->sb) { + const struct sbrec_static_mac_binding *mb = + static_mac_binding_lookup( + input_data->sbrec_static_mac_binding_by_lport_ip, + nb_smb->logical_port, nb_smb->ip); + if (!mb) { + /* Create new entry */ + mb = sbrec_static_mac_binding_insert(ovnsb_txn); + sbrec_static_mac_binding_set_logical_port( + mb, nb_smb->logical_port); + sbrec_static_mac_binding_set_ip(mb, nb_smb->ip); + sbrec_static_mac_binding_set_mac(mb, nb_smb->mac); + sbrec_static_mac_binding_set_override_dynamic_mac(mb, + nb_smb->override_dynamic_mac); + sbrec_static_mac_binding_set_datapath(mb, od->sb); + } else { + /* Update existing entry if there is a change*/ + if (strcmp(mb->mac, nb_smb->mac)) { + sbrec_static_mac_binding_set_mac(mb, nb_smb->mac); + } + if (mb->override_dynamic_mac != + nb_smb->override_dynamic_mac) { + sbrec_static_mac_binding_set_override_dynamic_mac(mb, + nb_smb->override_dynamic_mac); + } + } + } + } + } +} + void northd_init(struct northd_data *data) { @@ -15207,6 +15282,7 @@ ovnnb_db_run(struct northd_input *input_data, build_lrouter_groups(&data->ports, &data->lr_list); build_ip_mcast(input_data, ovnsb_txn, &data->datapaths); build_meter_groups(input_data, &data->meter_groups); + build_static_mac_binding_table(input_data, ovnsb_txn, &data->ports); stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); ovn_update_ipv6_prefix(&data->ports); diff --git a/northd/northd.h b/northd/northd.h index ebcb40de7..2d804a22e 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -28,6 +28,8 @@ struct northd_input { const struct nbrec_address_set_table *nbrec_address_set_table; const struct nbrec_meter_table *nbrec_meter_table; const struct nbrec_acl_table *nbrec_acl_table; + const struct nbrec_static_mac_binding_table + *nbrec_static_mac_binding_table; /* Southbound table references */ const struct sbrec_sb_global_table *sbrec_sb_global_table; @@ -45,12 +47,15 @@ struct northd_input { const struct sbrec_dns_table *sbrec_dns_table; const struct sbrec_ip_multicast_table *sbrec_ip_multicast_table; const struct sbrec_chassis_private_table *sbrec_chassis_private_table; + const struct sbrec_static_mac_binding_table + *sbrec_static_mac_binding_table; /* Indexes */ struct ovsdb_idl_index *sbrec_chassis_by_name; struct ovsdb_idl_index *sbrec_chassis_by_hostname; struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name; struct ovsdb_idl_index *sbrec_ip_mcast_by_dp; + struct ovsdb_idl_index *sbrec_static_mac_binding_by_lport_ip; }; struct northd_data { diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 6c69732e9..174364c8b 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "6.2.0", - "cksum": "2862883635 31533", + "version": "6.3.0", + "cksum": "4042813038 31869", "tables": { "NB_Global": { "columns": { @@ -612,5 +612,14 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["logical_port", "dst_ip"]], - "isRoot": true}} + "isRoot": true}, + "Static_MAC_Binding": { + "columns": { + "logical_port": {"type": "string"}, + "ip": {"type": "string"}, + "mac": {"type": "string"}, + "override_dynamic_mac": {"type": "boolean"}}, + "indexes": [["logical_port", "ip"]], + "isRoot": true} } +} diff --git a/ovn-nb.xml b/ovn-nb.xml index 0e3ad2b9b..547f7f48a 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -4326,4 +4326,33 @@ + + +

+ Each record represents a Static_MAC_Binding entry for a logical router. +

+ + +

+ ovn-northd reads configuration from these columns + and propagates the value to SBDB. +

+ + + The logical router port for the binding. + + + + The bound IP address. + + + + The Ethernet address to which the IP is bound. + + + + Override dynamically learnt MACs. + +
+
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index 122614dd5..66664c840 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.21.0", - "cksum": "2362446865 26963", + "version": "20.22.0", + "cksum": "1686121686 27471", "tables": { "SB_Global": { "columns": { @@ -533,6 +533,17 @@ "minInteger": 1, "maxInteger": 16777215}}}}, "indexes": [["mac", "dp_key"]], + "isRoot": true}, + "Static_MAC_Binding": { + "columns": { + "logical_port": {"type": "string"}, + "ip": {"type": "string"}, + "mac": {"type": "string"}, + "override_dynamic_mac": {"type": "boolean"}, + "datapath": {"type": { + "key": {"type": "uuid", + "refTable": "Datapath_Binding"}}}}, + "indexes": [["logical_port", "ip"]], "isRoot": true} } } diff --git a/ovn-sb.xml b/ovn-sb.xml index 65bfc9a59..1ffb24e7a 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -4563,4 +4563,31 @@ tcp.flags = RST; The key of the port binding on which this FDB was learnt. + + +

+ Each record represents a Static_MAC_Binding entry for a logical router. +

+ + + + The logical router port for the binding. + + + + The bound IP address. + + + + The Ethernet address to which the IP is bound. + + + + Override dynamically learnt MACs. + + + + The logical datapath to which the logical router port belongs. + +
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index db0d23099..f9b9defd0 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -2332,6 +2332,75 @@ AT_CHECK([ovn-nbctl list forwarding_group], [0], []) dnl --------------------------------------------------------------------- +OVN_NBCTL_TEST([ovn_nbctl_static_mac_binding], [lr static_mac_binding], [ + +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lrp-add lr0 lr0-p0 00:00:01:01:02:03 192.168.10.1/24]) +AT_CHECK([ovn-nbctl lrp-add lr0 lr0-p1 00:00:02:02:03:04 192.168.11.1/24]) + +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.10 00:00:11:22:33:44]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:44:55]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 10.0.0.10 00:00:33:44:55:66]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:88]) + +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 foo 00:00:44:55:66:88], [1], [], + [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address. +]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 172.16.0.200 foo], [1], [], + [ovn-nbctl: invalid mac address foo. +]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:77], [1], [], + [ovn-nbctl: lr0-p0, 172.16.0.11: a Static_MAC_Binding with this logical_port and ip already exists +]) + +AT_CHECK([ovn-nbctl --may-exist static-mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:77]) + +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p1 10.0.0.10 00:00:33:44:55:66]) +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p1 172.16.0.11 00:00:44:55:66:88]) + +AT_CHECK([ovn-nbctl static-mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 10.0.0.10 00:00:33:44:55:66 +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p0 192.168.10.100 00:00:22:33:44:55 +lr0-p1 10.0.0.10 00:00:33:44:55:66 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p0 foo], [1], [], + [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address. +]) + +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p1 10.0.0.100], [1], [], + [ovn-nbctl: no matching Static_MAC_Binding with port lr0-p1 and ip 10.0.0.100 +]) + +AT_CHECK([ovn-nbctl --if-exists static-mac-binding-del lr0-p1 10.0.0.100]) + +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p0 10.0.0.10]) +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p0 192.168.10.100]) + +AT_CHECK([ovn-nbctl static-mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p1 10.0.0.10 00:00:33:44:55:66 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p1 10.0.0.10]) +AT_CHECK([ovn-nbctl static-mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +]) + +dnl --------------------------------------------------------------------- + OVN_NBCTL_TEST([ovn_nbctl_negative], [basic negative tests], [ AT_CHECK([ovn-nbctl --id=@ls create logical_switch name=foo -- \ set logical_switch foo1 name=bar], diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index b2afc24c3..adb304385 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -6640,4 +6640,27 @@ AT_CHECK([ovn-sbctl dump-flows DR | grep -e lr_in_unsnat -e lr_out_snat -e lr_in ]) AT_CLEANUP -]) + +AT_SETUP([LR NB Static_MAC_Binding table]) +ovn_start + +# Create logical routers +ovn-nbctl lr-add lr0 +ovn-nbctl lrp-add lr0 lr0-p0 00:00:01:01:02:03 192.168.10.1/24 +ovn-nbctl lrp-add lr0 lr0-p1 00:00:02:02:03:04 192.168.11.1/24 + +ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.10 00:00:11:22:33:44 +ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:44:55 + +wait_row_count nb:Static_MAC_Binding 2 logical_port=lr0-p0 +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.10 mac="00\:00\:11\:22\:33\:44" +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 mac="00\:00\:22\:33\:44\:55" + +ovn-nbctl static-mac-binding-add lr0-p1 10.0.0.10 00:00:33:44:55:66 +wait_row_count nb:Static_MAC_Binding 1 logical_port=lr0-p1 +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p1 ip=10.0.0.10 mac="00\:00\:33\:44\:55\:66" + +ovn-nbctl --may-exist static-mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:55:66 +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 mac="00\:00\:22\:33\:55\:66" + +AT_CLEANUP diff --git a/tests/ovn.at b/tests/ovn.at index b1dbc1f7e..b2c88c470 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -30119,3 +30119,93 @@ AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters br-int | grep -q rate=100], OVN_CLEANUP([hv1]) AT_CLEANUP ]) + +AT_SETUP([ovn -- lr static_mac_binding]) +AT_KEYWORDS([static_mac_binding]) +ovn_start + +# Add chassis +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys +ovn_attach n1 br-phys 192.168.0.1 + +# Create a logical router +ovn-nbctl lr-add lr0 +ovn-nbctl lrp-add lr0 lr0-ls0 00:00:11:11:22:22 20.0.0.1/24 +ovn-nbctl lrp-add lr0 lr0-ext-ls0 00:00:11:11:33:33 172.16.1.10/24 + +# Create logical switch and connect to logical router +ovn-nbctl ls-add ls0 +ovn-nbctl lsp-add ls0 ls0-lr0 +ovn-nbctl lsp-set-type ls0-lr0 router +ovn-nbctl lsp-set-addresses ls0-lr0 router +ovn-nbctl --wait=sb lsp-set-options ls0-lr0 router-port=lr0-ls0 + +# Create external gateway switch and connect to logical router +ovn-nbctl ls-add ext-ls0 +ovn-nbctl lsp-add ext-ls0 ext-ls0-lr0 +ovn-nbctl lsp-set-type ext-ls0-lr0 router +ovn-nbctl lsp-set-addresses ext-ls0-lr0 router +ovn-nbctl --wait=sb lsp-set-options ext-ls0-lr0 router-port=lr0-ext-ls0 + +ovn-nbctl lsp-add ext-ls0 ln0 "" 1000 +ovn-nbctl lsp-set-addresses ln0 unknown +ovn-nbctl lsp-set-type ln0 localnet +ovn-nbctl lsp-set-options ln0 network_name=phys + +# Add the lsp lp11 to ls0. This will map to VIF11. +ovn-nbctl lsp-add ls0 lp11 +ovn-nbctl lsp-set-addresses lp11 "00:00:11:11:44:44 20.0.0.10" + +# Add a vif on HV1 +ovs-vsctl add-port br-int vif11 -- \ + set Interface vif11 external-ids:iface-id=lp11 \ + options:tx_pcap=hv1/vif11-tx.pcap \ + options:rxq_pcap=hv1/vif11-rx.pcap \ + ofport-request=11 +OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up lp11) = xup]) + +ovn-nbctl lrp-set-gateway-chassis lr0-ext-ls0 hv1 + +ovn-nbctl --wait=sb sync +OVN_POPULATE_ARP + +ovn-nbctl --wait=hv lr-nat-add lr0 snat 172.16.1.10 20.0.0.0/24 +ovn-nbctl --wait=hv lr-route-add lr0 0.0.0.0/0 172.16.1.1 + +test_mac_binding_flows() { + local priority=$1 mac=$2 count=$3 + OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int | grep table=66 | grep priority=${priority} | grep actions=mod_dl_dst:${mac} | wc -l) -eq ${count}]) +} +# Create SB MAC_Binding entry on external gateway port +lr0_dp_uuid=$(fetch_column datapath_binding _uuid external_ids:name=lr0) + +ovn-sbctl create mac_binding ip=172.16.1.1 logical_port=lr0-ext-ls0 mac="00\:00\:11\:22\:33\:44" datapath=$lr0_dp_uuid +test_mac_binding_flows 100 00:00:11:22:33:44 1 + +# Create Static_MAC_Binding entry on external gateway port. This should have +# higher priority than MAC_Binding entry +ovn-nbctl static-mac-binding-add lr0-ext-ls0 172.16.1.1 00:00:11:22:33:66 +test_mac_binding_flows 50 00:00:11:22:33:66 1 + +# Update MAC for existing Static_MAC_Binding. Existing flow should be updated. +ovn-nbctl --may-exist static-mac-binding-add lr0-ext-ls0 172.16.1.1 00:00:11:22:33:88 +test_mac_binding_flows 50 00:00:11:22:33:66 0 +test_mac_binding_flows 50 00:00:11:22:33:88 1 + +# Update override_dynamic_mac for existing Static_MAC_Binding. Existing flow should be updated. +smb_uuid=$(fetch_column nb:static_mac_binding _uuid ip=172.16.1.1) + +ovn-nbctl set static_mac_binding $smb_uuid override_dynamic_mac=true +test_mac_binding_flows 50 00:00:11:22:33:88 0 +test_mac_binding_flows 150 00:00:11:22:33:88 1 + +# Delete Static_MAC_Binding. Higher priority flow should get deleted. +ovn-nbctl static-mac-binding-del lr0-ext-ls0 172.16.1.1 +test_mac_binding_flows 150 00:00:11:22:33:88 0 + +OVN_CLEANUP([hv1]) +AT_CLEANUP diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index f8b957792..4ed4d27c6 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -373,7 +373,8 @@ Policy commands:\n\ lr-policy-del ROUTER [{PRIORITY | UUID} [MATCH]]\n\ remove policies from ROUTER\n\ lr-policy-list ROUTER print policies for ROUTER\n\ -\n\ +\n\n",program_name, program_name); + printf("\ NAT commands:\n\ [--stateless]\n\ [--portrange]\n\ @@ -417,8 +418,7 @@ Connection commands:\n\ del-connection delete the connections\n\ [--inactivity-probe=MSECS]\n\ set-connection TARGET... set the list of connections to TARGET...\n\ -\n\n",program_name, program_name); - printf("\ +\n\ SSL commands:\n\ get-ssl print the SSL configuration\n\ del-ssl delete the SSL configuration\n\ @@ -453,6 +453,13 @@ Control Plane Protection Policy commands:\n\ lr-copp-add NAME ROUTER\n\ Add a NAME copp policy on ROUTER logical router.\n\ \n\ +MAC_Binding commands:\n\ + static-mac-binding-add LOGICAL_PORT IP MAC\n\ + Add a Static_MAC_Binding entry\n\ + static-mac-binding-del LOGICAL_PORT IP\n\ + Delete Static_MAC_Binding entry\n\ + static-mac-binding-list List all Static_MAC_Binding entries\n\ +\n\ %s\ %s\ \n\ @@ -5844,6 +5851,146 @@ nbctl_lrp_get_redirect_type(struct ctl_context *ctx) !redirect_type ? "overlay": redirect_type); } +static const struct nbrec_static_mac_binding * +static_mac_binding_by_port_ip(struct ctl_context *ctx, + const char *logical_port, const char *ip) +{ + const struct nbrec_static_mac_binding *nb_smb = NULL; + + NBREC_STATIC_MAC_BINDING_FOR_EACH (nb_smb, ctx->idl) { + if (!strcmp(nb_smb->logical_port, logical_port) && + !strcmp(nb_smb->ip, ip)) { + break; + } + } + + return nb_smb; +} + +static void +nbctl_pre_static_mac_binding(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name); + + ovsdb_idl_add_column(ctx->idl, &nbrec_static_mac_binding_col_logical_port); + ovsdb_idl_add_column(ctx->idl, &nbrec_static_mac_binding_col_ip); + ovsdb_idl_add_column(ctx->idl, &nbrec_static_mac_binding_col_mac); +} + +static void +nbctl_static_mac_binding_add(struct ctl_context *ctx) +{ + const char *logical_port = ctx->argv[1]; + const char *ip = ctx->argv[2]; + const char *mac = ctx->argv[3]; + char *new_ip = NULL; + + const struct nbrec_logical_router_port *lrp; + char *error = lrp_by_name_or_uuid(ctx, logical_port, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + new_ip = normalize_addr_str(ip); + if (!new_ip) { + ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ip); + return; + } + + struct eth_addr ea; + if (!eth_addr_from_string(mac, &ea)) { + ctl_error(ctx, "invalid mac address %s.", mac); + goto cleanup; + } + + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + const struct nbrec_static_mac_binding *nb_smb = + static_mac_binding_by_port_ip(ctx, logical_port, ip); + if (nb_smb) { + if (may_exist) { + if (strcmp(nb_smb->mac, mac)) { + nbrec_static_mac_binding_verify_mac(nb_smb); + nbrec_static_mac_binding_set_mac(nb_smb, mac); + } + } else { + ctl_error(ctx, "%s, %s: a Static_MAC_Binding with this " + "logical_port and ip already exists", + logical_port, new_ip); + } + goto cleanup; + } + + /* Create Static_MAC_Binding entry */ + nb_smb = nbrec_static_mac_binding_insert(ctx->txn); + nbrec_static_mac_binding_set_logical_port(nb_smb, logical_port); + nbrec_static_mac_binding_set_ip(nb_smb, new_ip); + nbrec_static_mac_binding_set_mac(nb_smb, mac); + +cleanup: + free(new_ip); +} + +static void +nbctl_static_mac_binding_del(struct ctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + const char *logical_port = ctx->argv[1]; + const struct nbrec_logical_router_port *lrp; + char *error = lrp_by_name_or_uuid(ctx, logical_port, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + char *ip = normalize_addr_str(ctx->argv[2]); + if (!ip) { + ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ctx->argv[2]); + return; + } + + const struct nbrec_static_mac_binding *nb_smb = + static_mac_binding_by_port_ip(ctx, logical_port, ip); + + if (nb_smb) { + /* Remove the matching Static_MAC_Binding. */ + nbrec_static_mac_binding_delete(nb_smb); + goto cleanup; + } + + if (must_exist) { + ctl_error(ctx, "no matching Static_MAC_Binding with port %s and ip %s", + logical_port, ip); + } + +cleanup: + free(ip); +} + +static void +nbctl_static_mac_binding_list(struct ctl_context *ctx) +{ + struct smap lr_mac_bindings = SMAP_INITIALIZER(&lr_mac_bindings); + const struct nbrec_static_mac_binding *nb_smb = NULL; + NBREC_STATIC_MAC_BINDING_FOR_EACH (nb_smb, ctx->idl) { + char *key = xasprintf("%-25s%-25s", nb_smb->logical_port, nb_smb->ip); + smap_add_format(&lr_mac_bindings, key, "%s", nb_smb->mac); + free(key); + } + + const struct smap_node **nodes = smap_sort(&lr_mac_bindings); + if (nodes) { + ds_put_format(&ctx->output, "%-25s%-25s%s\n", + "LOGICAL_PORT", "IP", "MAC"); + for (size_t i = 0; i < smap_count(&lr_mac_bindings); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%-25s%s\n", node->key, node->value); + } + } + smap_destroy(&lr_mac_bindings); + free(nodes); +} + static const struct nbrec_forwarding_group * fwd_group_by_name_or_uuid(struct ctl_context *ctx, const char *id) { @@ -7321,6 +7468,17 @@ static const struct ctl_command_syntax nbctl_commands[] = { pre_ha_ch_grp_set_chassis_prio, cmd_ha_ch_grp_set_chassis_prio, NULL, "", RW }, + /* Static_MAC_Binding commands */ + { "static-mac-binding-add", 3, 3, "LOGICAL_PORT IP MAC", + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_add, NULL, + "--may-exist", RW }, + { "static-mac-binding-del", 2, 2, "LOGICAL_PORT IP", + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_del, NULL, + "--if-exists", RW }, + { "static-mac-binding-list", 0, 0, "", + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_list, NULL, + "", RO }, + {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, };