From patchwork Fri May 14 19:33:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vasu Dasari X-Patchwork-Id: 1478662 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=hC0XoJnk; dkim-atps=neutral Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fhdx03QNQz9sWQ for ; Sat, 15 May 2021 05:33:24 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 569874187E; Fri, 14 May 2021 19:33:21 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id JfL1e6R9oWlt; Fri, 14 May 2021 19:33:19 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTP id C149E41874; Fri, 14 May 2021 19:33:18 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 875F9C000D; Fri, 14 May 2021 19:33:18 +0000 (UTC) X-Original-To: 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 41C79C0001 for ; Fri, 14 May 2021 19:33:17 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 277DD40610 for ; Fri, 14 May 2021 19:33:17 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.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 SXeA-w_VuTOk for ; Fri, 14 May 2021 19:33:15 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 Received: from mail-qk1-x72a.google.com (mail-qk1-x72a.google.com [IPv6:2607:f8b0:4864:20::72a]) by smtp2.osuosl.org (Postfix) with ESMTPS id B7C3A4060D for ; Fri, 14 May 2021 19:33:15 +0000 (UTC) Received: by mail-qk1-x72a.google.com with SMTP id a2so58512qkh.11 for ; Fri, 14 May 2021 12:33:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=mtoO+HBFZwnO8vMVW/x96HPk19fZBgBxYRn/7NP1f+c=; b=hC0XoJnkdvdaDQ9dJ/YM1aHdqyJSxmOBo58IpZh2rfPRR3C8yyvOw7d+u0VmA+VTd3 qdTpiXMbEQYGcMjkVWGCXmUOoy5YTy9foaxrf1W1UUBCiJD0gH/B9b3cJrCgPM0QL+av ovnTRyCef20f/lBUgumJLfgIGorWTYUJ71dQSZe3bHZ0R1Zj/MGLeI2AotBmT+/oiyXT Ylt/I2CHkgi01Llo80P1DCnfbSmAokeB/VCvUpYlp/N90KyuUgkiExC5qU+AUBMdbOue 7apj0ToGC1G89IOx9DyX9GXqS1v1wcD0OsZEM2Q7xePvy/bs8aUme0erhhU5pdIczP7F 4JNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=mtoO+HBFZwnO8vMVW/x96HPk19fZBgBxYRn/7NP1f+c=; b=PB+zSysnDCtbSDqXgV+kL6IwUmtD9FF4U5M7UeNYBcghfsV6/8kCq6EOvML2CBoptY qZVnavQCoRkEGgbErlfEj9+74v54VPU4PK45H/lmnS6UzmqM99vQ4me6vOYEOOpbUcz0 GxZ5PwZmUI4xDapr6eKNSy31HWVnK8mpX07LUbOLay0ABWDcjcB7VSd7Xwyx258kL93Q ZWX6dseVImhfomIhdrdN25p0P6TnylO0uTDcucxt0OZSC9UU1Hm8V4g6tgQMzwSFZ2ZL 9ikm2j2F0MGFW6m4i1kb+W6i2rBpatGghKDQlDJ/l/jhVzVKEMh4JFR6dLlH7+ST17X5 rUSQ== X-Gm-Message-State: AOAM532Lmalc/7b/2HvTMvaMPA/d2L/VSIAdWiv6S8PCuZD+zAdf5DSN EN3w0ZM2e/TE+XKJS59y3WBZiLX4Ec1oNqGx X-Google-Smtp-Source: ABdhPJzKUbvtZvLXTMvzbgUxDH36OZnNLXM5wXuuwT/WDDUmSSYRJOBJde3NX6nu9j3GUpfWLeVvWw== X-Received: by 2002:a05:620a:2291:: with SMTP id o17mr24276570qkh.150.1621020793916; Fri, 14 May 2021 12:33:13 -0700 (PDT) Received: from vdasari-mac.cinci.rr.com (cpe-98-28-205-172.cinci.res.rr.com. [98.28.205.172]) by smtp.gmail.com with ESMTPSA id t187sm5442871qkc.56.2021.05.14.12.33.13 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 14 May 2021 12:33:13 -0700 (PDT) From: Vasu Dasari To: dev@openvswitch.org Date: Fri, 14 May 2021 15:33:09 -0400 Message-Id: <20210514193309.80837-1-vdasari@gmail.com> X-Mailer: git-send-email 2.29.2 MIME-Version: 1.0 Cc: fbl@sysclose.org Subject: [ovs-dev] [PATCH v3] ofproto-dpif: APIs and CLI option to add/delete static fdb entry 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" Currently there is an option to add/flush/show ARP/ND neighbor. This covers L3 side. For L2 side, there is only fdb show command. This patch gives an option to add/del an fdb entry via ovs-appctl. ovs-appctl command looks like this: To add: ovs-appctl fdb/add ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05 To del: ovs-appctl fdb/del ovs-appctl fdb/del br0 p1 0 50:54:00:00:00:05 Static entry should not age. To indicate that entry being programmed is a static entry, 'expires' field in 'struct mac_entry' will be set to a MAC_ENTRY_AGE_STATIC_ENTRY. A check for this value is made while deleting mac entry as part of regular aging process. Another check as part of mac-update process, when a packet with same source mac as this entry arrives on the configured port will not modify the expires field Added two new APIs to provide convenient interface to add and delete static-macs. void xlate_add_static_mac_entry(const struct ofproto_dpif *, ofp_port_t in_port, struct eth_addr dl_src, int vlan); void xlate_delete_static_mac_entry(const struct ofproto_dpif *, struct eth_addr dl_src, int vlan); Signed-off-by: Vasu Dasari Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2019-June/048894.html Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1597752 --- v1: - Fixed 0-day robot warnings v2: - Fix valgrind error in the modified code in mac_learning_insert() where a read is is performed on e->expires which is not initialized v3: - Addressed code review comments - Added more documentation - Fixed mac_entry_age() and is_mac_learning_update_needed() to have common understanding of return values when mac_entry is a static one. - Added NEWS item --- NEWS | 2 ++ lib/mac-learning.c | 38 ++++++++++++++++++++--- lib/mac-learning.h | 11 +++++++ ofproto/ofproto-dpif-xlate.c | 26 ++++++++++++++++ ofproto/ofproto-dpif-xlate.h | 5 +++ ofproto/ofproto-dpif.c | 60 +++++++++++++++++++++++++++++++++--- tests/ofproto-dpif.at | 55 +++++++++++++++++++++++++++++++++ 7 files changed, 188 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 402ce5969..61ab61462 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Post-v2.15.0 - Userspace datapath: * Auto load balancing of PMDs now partially supports cross-NUMA polling cases, e.g if all PMD threads are running on the same NUMA node. + * Added ability to add and delete static mac entries using: + 'ovs-appctl fdb/{add,del} ' - ovs-ctl: * New option '--no-record-hostname' to disable hostname configuration in ovsdb on startup. diff --git a/lib/mac-learning.c b/lib/mac-learning.c index 3d5293d3b..f7c6ef538 100644 --- a/lib/mac-learning.c +++ b/lib/mac-learning.c @@ -35,12 +35,23 @@ COVERAGE_DEFINE(mac_learning_expired); COVERAGE_DEFINE(mac_learning_evicted); COVERAGE_DEFINE(mac_learning_moved); -/* Returns the number of seconds since 'e' (within 'ml') was last learned. */ +/* + * This function will return age of mac entry in the fdb. It + * will return either one of the following: + * 1. Number of seconds since 'e' (within 'ml') was last learned. + * 2. If the mac entry is a static entry, it returns + * MAC_ENTRY_AGE_STATIC_ENTRY + */ int mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e) { - time_t remaining = e->expires - time_now(); - return ml->idle_time - remaining; + /* For static fdb entries, expires would be MAC_ENTRY_AGE_STATIC_ENTRY */ + if (MAC_ENTRY_AGE_STATIC_ENTRY == e->expires) { + return e->expires; + } else { + time_t remaining = e->expires - time_now(); + return ml->idle_time - remaining; + } } static uint32_t @@ -282,6 +293,18 @@ mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time) } } +/* Changes the MAC aging timeout of a mac_entry to 'idle_time' seconds. */ +void +mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr mac, + int vlan, unsigned int idle_time) +{ + struct mac_entry *e; + e = mac_entry_lookup(ml, mac, vlan); + if (e) { + e->expires = idle_time; + } +} + /* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it * to be within a reasonable range. */ void @@ -336,6 +359,7 @@ mac_learning_insert(struct mac_learning *ml, e->vlan = vlan; e->grat_arp_lock = TIME_MIN; e->mlport = NULL; + e->expires = 0; COVERAGE_INC(mac_learning_learned); ml->total_learned++; } else { @@ -348,7 +372,10 @@ mac_learning_insert(struct mac_learning *ml, ovs_list_remove(&e->port_lru_node); ovs_list_push_back(&e->mlport->port_lrus, &e->port_lru_node); } - e->expires = time_now() + ml->idle_time; + /* Do not update 'expires' for static mac entry */ + if (e->expires != MAC_ENTRY_AGE_STATIC_ENTRY) { + e->expires = time_now() + ml->idle_time; + } return e; } @@ -378,7 +405,8 @@ is_mac_learning_update_needed(const struct mac_learning *ml, } mac = mac_learning_lookup(ml, src, vlan); - if (!mac || mac_entry_age(ml, mac)) { + /* If mac entry is missing or if it is a static entry, then just return */ + if (!mac || (mac_entry_age(ml, mac) == MAC_ENTRY_AGE_STATIC_ENTRY)) { return true; } diff --git a/lib/mac-learning.h b/lib/mac-learning.h index 0ddab06cb..d8ff3172b 100644 --- a/lib/mac-learning.h +++ b/lib/mac-learning.h @@ -57,6 +57,11 @@ * list starting from the LRU end, deleting each entry that has been idle too * long. * + * Fourth, a mac entry can be configured statically via API or appctl commands. + * Static entries are programmed to have a age of MAC_ENTRY_AGE_STATIC_ENTRY. + * Age of static entries will not be updated by a receiving packet as part of + * regular packet processing. + * * Finally, the number of MAC learning table entries has a configurable maximum * size to prevent memory exhaustion. When a new entry must be inserted but * the table is already full, the implementation uses an eviction strategy @@ -94,6 +99,9 @@ struct mac_learning; /* Time, in seconds, before expiring a mac_entry due to inactivity. */ #define MAC_ENTRY_DEFAULT_IDLE_TIME 300 +/* Age value to represent a static entry */ +#define MAC_ENTRY_AGE_STATIC_ENTRY INT_MAX + /* Time, in seconds, to lock an entry updated by a gratuitous ARP to avoid * relearning based on a reflection from a bond member. */ #define MAC_GRAT_ARP_LOCK_TIME 5 @@ -202,6 +210,9 @@ bool mac_learning_set_flood_vlans(struct mac_learning *ml, void mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time) OVS_REQ_WRLOCK(ml->rwlock); +void mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr src, + int vlan, unsigned int idle_time) + OVS_REQ_WRLOCK(ml->rwlock); void mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries) OVS_REQ_WRLOCK(ml->rwlock); diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 7108c8a30..8580b39f4 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -8011,6 +8011,32 @@ xlate_mac_learning_update(const struct ofproto_dpif *ofproto, update_learning_table__(xbridge, xbundle, dl_src, vlan, is_grat_arp); } +void +xlate_add_static_mac_entry(const struct ofproto_dpif *ofproto, + ofp_port_t in_port, + struct eth_addr dl_src, int vlan) +{ + xlate_mac_learning_update(ofproto, in_port, dl_src, vlan, false); + + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + mac_entry_set_idle_time(ofproto->ml, dl_src, vlan, INT_MAX); + ovs_rwlock_unlock(&ofproto->ml->rwlock); +} + +void +xlate_delete_static_mac_entry(const struct ofproto_dpif *ofproto, + struct eth_addr dl_src, int vlan) +{ + struct mac_entry *e = NULL; + + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + e = mac_learning_lookup(ofproto->ml, dl_src, vlan); + if (e) { + mac_learning_expire(ofproto->ml, e); + } + ovs_rwlock_unlock(&ofproto->ml->rwlock); +} + void xlate_set_support(const struct ofproto_dpif *ofproto, const struct dpif_backer_support *support) diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h index 3426a27b2..9e6e95756 100644 --- a/ofproto/ofproto-dpif-xlate.h +++ b/ofproto/ofproto-dpif-xlate.h @@ -225,6 +225,11 @@ int xlate_send_packet(const struct ofport_dpif *, bool oam, struct dp_packet *); void xlate_mac_learning_update(const struct ofproto_dpif *ofproto, ofp_port_t in_port, struct eth_addr dl_src, int vlan, bool is_grat_arp); +void xlate_add_static_mac_entry(const struct ofproto_dpif *, + ofp_port_t in_port, + struct eth_addr dl_src, int vlan); +void xlate_delete_static_mac_entry(const struct ofproto_dpif *, + struct eth_addr dl_src, int vlan); void xlate_set_support(const struct ofproto_dpif *, const struct dpif_backer_support *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index fd0b2fdea..d0f030e87 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5852,18 +5852,66 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) { struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e); char name[OFP_MAX_PORT_NAME_LEN]; + int age = mac_entry_age(ofproto->ml, e); ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, - NULL, name, sizeof name); - ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" %3d\n", - name, e->vlan, ETH_ADDR_ARGS(e->mac), - mac_entry_age(ofproto->ml, e)); + NULL, name, sizeof name); + ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" ", + name, e->vlan, ETH_ADDR_ARGS(e->mac)); + if (MAC_ENTRY_AGE_STATIC_ENTRY == age) { + ds_put_format(&ds, "static\n"); + } else { + ds_put_format(&ds, "%3d\n", age); + } } ovs_rwlock_unlock(&ofproto->ml->rwlock); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } +static void +ofproto_unixctl_fdb_update(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) +{ + const char *br_name = argv[1]; + const char *name = argv[2]; + struct eth_addr mac; + uint16_t vlan = atoi(argv[3]); + const char *op = (const char *) aux; + const struct ofproto_dpif *ofproto; + ofp_port_t in_port = OFPP_NONE; + struct ofproto_port ofproto_port; + + ofproto = ofproto_dpif_lookup_by_name(br_name); + if (!ofproto) { + unixctl_command_reply_error(conn, "no such bridge"); + return; + } + + if (!eth_addr_from_string(argv[4], &mac)) { + unixctl_command_reply_error(conn, "bad MAC address"); + return; + } + + if (ofproto_port_query_by_name(&ofproto->up, name, &ofproto_port)) { + unixctl_command_reply_error(conn, + "software error, odp port is present but no ofp port"); + return; + } + in_port = ofproto_port.ofp_port; + ofproto_port_destroy(&ofproto_port); + + if (!strcmp(op, "add")) { + xlate_add_static_mac_entry(ofproto, in_port, mac, vlan); + unixctl_command_reply(conn, "OK"); + } else if (!strcmp(op, "del")) { + xlate_delete_static_mac_entry(ofproto, mac, vlan); + unixctl_command_reply(conn, "OK"); + } else { + unixctl_command_reply_error(conn, "software error, unknown op"); + } +} + static void ofproto_unixctl_fdb_stats_clear(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) @@ -6415,6 +6463,10 @@ ofproto_unixctl_init(void) } registered = true; + unixctl_command_register("fdb/add", "[bridge port vlan mac]", 4, 4, + ofproto_unixctl_fdb_update, "add"); + unixctl_command_register("fdb/del", "[bridge port vlan mac]", 4, 4, + ofproto_unixctl_fdb_update, "del"); unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 24bbd884c..fcec75999 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -6753,6 +6753,61 @@ PORTNAME portName=p2 ])]) +AT_SETUP([ofproto-dpif - static MAC programming]) +OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone]) +add_of_ports br0 1 2 + +arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' + +dnl Trace an ARP packet arriving on p1 +OFPROTO_TRACE( + [ovs-dummy], + [in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp], + [-generate], + [2,100]) + +dnl Make sure dynamic learning happens +AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort], + [0], [dnl + 1 0 50:54:00:00:00:05 + port VLAN MAC Age +]) + +dnl Now add convert same entry to a static entry +AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05], [0], [OK +]) + +dnl Check for the static MAC entry +AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl + port VLAN MAC Age + 1 0 50:54:00:00:00:05 static +]) + +dnl Trace an ARP packet arriving on p1 +OFPROTO_TRACE( + [ovs-dummy], + [in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp], + [-generate], + [2,100]) + +dnl Check that age on static MAC entry is not altered because of above learning event +AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl + port VLAN MAC Age + 1 0 50:54:00:00:00:05 static +]) + +dnl Remove static mac entry +AT_CHECK([ovs-appctl fdb/del br0 p1 0 50:54:00:00:00:05], [0], [OK +]) + +dnl Check that entry is removed +AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl + port VLAN MAC Age +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto-dpif - basic truncate action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5