diff mbox series

[ovs-dev,2/3] nb: Add support for match and priority in NAT.

Message ID 20240503072622.2111265-3-amusil@redhat.com
State New
Headers show
Series Arbitrary match for NAT | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Ales Musil May 3, 2024, 7:26 a.m. UTC
Add support for match and priority in NAT table. This allows to define
NAT that has extra match condition to have more fine-grained control
over the final NAT rule application. At the same time it allows for
NAT rules that would be considered as duplicates otherwise e.g.
multiple SNATs with same logical IP, but different external IP. Also,
when the match is specified allow addition of priority to order the
NAT rule valuation as needed.

Signed-off-by: Ales Musil <amusil@redhat.com>
---
 ovn-nb.ovsschema          |   8 +-
 ovn-nb.xml                |  15 +++
 tests/ovn-nbctl.at        | 220 +++++++++++++++++++++++++++-----------
 utilities/ovn-nbctl.8.xml |  14 ++-
 utilities/ovn-nbctl.c     | 189 ++++++++++++++++++++------------
 5 files changed, 307 insertions(+), 139 deletions(-)
diff mbox series

Patch

diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index 10ce50b25..e3c4aff9d 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@ 
 {
     "name": "OVN_Northbound",
-    "version": "7.3.1",
-    "cksum": "3899022625 35372",
+    "version": "7.4.0",
+    "cksum": "1908497390 35615",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -524,6 +524,10 @@ 
                                      "refType": "weak"},
                              "min": 0,
                              "max": 1}},
+                "priority": {"type": {"key": {"type": "integer",
+                                              "minInteger": 0,
+                                              "maxInteger": 32767}}},
+                "match": {"type": "string"},
                 "options": {"type": {"key": "string", "value": "string",
                                      "min": 0, "max": "unlimited"}},
                 "external_ids": {
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 5cb6ba640..fbad5f124 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3924,6 +3924,21 @@  or
       </p>
     </column>
 
+    <column name="match">
+      The packets that the NAT rules should match, in addition to the match
+      that is created based on the NAT type, in the same expression
+      language used for the <ref column="match" table="Logical_Flow"
+                                 db="OVN_Southbound"/> column in the OVN
+      Southbound database's <ref table="Logical_Flow" db="OVN_Southbound"/>
+      table.  This allows for more fine-grained control over the NAT rule.
+    </column>
+
+    <column name="priority">
+      The NAT rule's priority.  Rules with numerically higher priority
+      take precedence over those with lower.  The priority is taken into
+      account only if the <code>match</code> is defined.
+    </column>
+
     <column name="options" key="stateless">
       Indicates if a dnat_and_snat rule should lead to connection
       tracking state or not.
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
index 5248e6c76..19c83a4a5 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -625,15 +625,15 @@  AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::1 fd11::2])
 AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:01:02:03])
 AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3 lp0 00:00:00:01:02:03])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat                                   30.0.0.1                            192.168.1.2
-dnat                                   fd01::1                             fd11::2
-dnat_and_snat                          30.0.0.1                            192.168.1.2
-dnat_and_snat                          30.0.0.2                            192.168.1.3         00:00:00:01:02:03    lp0
-dnat_and_snat                          fd01::1                             fd11::2
-dnat_and_snat                          fd01::2                             fd11::3             00:00:00:01:02:03    lp0
-snat                                   30.0.0.1                            192.168.1.0/24
-snat                                   fd01::1                             fd11::/64
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         30.0.0.1                            192.168.1.2
+dnat                                                         fd01::1                             fd11::2
+dnat_and_snat                                                30.0.0.1                            192.168.1.2
+dnat_and_snat                                                30.0.0.2                            192.168.1.3         00:00:00:01:02:03    lp0
+dnat_and_snat                                                fd01::1                             fd11::2
+dnat_and_snat                                                fd01::2                             fd11::3             00:00:00:01:02:03    lp0
+snat                                                         30.0.0.1                            192.168.1.0/24
+snat                                                         fd01::1                             fd11::/64
 ])
 AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [],
 [ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists
@@ -661,28 +661,28 @@  AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.3], [1], [],
 ])
 AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:04:05:06])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat                                   30.0.0.1                            192.168.1.2
-dnat                                   fd01::1                             fd11::2
-dnat_and_snat                          30.0.0.1                            192.168.1.2
-dnat_and_snat                          30.0.0.2                            192.168.1.3         00:00:00:04:05:06    lp0
-dnat_and_snat                          fd01::1                             fd11::2
-dnat_and_snat                          fd01::2                             fd11::3             00:00:00:01:02:03    lp0
-snat                                   30.0.0.1                            192.168.1.0/24
-snat                                   fd01::1                             fd11::/64
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         30.0.0.1                            192.168.1.2
+dnat                                                         fd01::1                             fd11::2
+dnat_and_snat                                                30.0.0.1                            192.168.1.2
+dnat_and_snat                                                30.0.0.2                            192.168.1.3         00:00:00:04:05:06    lp0
+dnat_and_snat                                                fd01::1                             fd11::2
+dnat_and_snat                                                fd01::2                             fd11::3             00:00:00:01:02:03    lp0
+snat                                                         30.0.0.1                            192.168.1.0/24
+snat                                                         fd01::1                             fd11::/64
 ])
 AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3])
 AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat                                   30.0.0.1                            192.168.1.2
-dnat                                   fd01::1                             fd11::2
-dnat_and_snat                          30.0.0.1                            192.168.1.2
-dnat_and_snat                          30.0.0.2                            192.168.1.3
-dnat_and_snat                          fd01::1                             fd11::2
-dnat_and_snat                          fd01::2                             fd11::3
-snat                                   30.0.0.1                            192.168.1.0/24
-snat                                   fd01::1                             fd11::/64
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         30.0.0.1                            192.168.1.2
+dnat                                                         fd01::1                             fd11::2
+dnat_and_snat                                                30.0.0.1                            192.168.1.2
+dnat_and_snat                                                30.0.0.2                            192.168.1.3
+dnat_and_snat                                                fd01::1                             fd11::2
+dnat_and_snat                                                fd01::2                             fd11::3
+snat                                                         30.0.0.1                            192.168.1.0/24
+snat                                                         fd01::1                             fd11::/64
 ])
 
 check_row_count nb:NAT 0 options:stateless=true
@@ -720,26 +720,26 @@  AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.3])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd01::1])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat                                   30.0.0.1                            192.168.1.2
-dnat                                   fd01::1                             fd11::2
-dnat_and_snat                          30.0.0.2                            192.168.1.3
-dnat_and_snat                          40.0.0.2                            192.168.1.4
-dnat_and_snat                          fd01::2                             fd11::3
-snat                                   30.0.0.1                            192.168.1.0/24
-snat                                   40.0.0.3                            192.168.1.6
-snat                                   fd01::1                             fd11::/64
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         30.0.0.1                            192.168.1.2
+dnat                                                         fd01::1                             fd11::2
+dnat_and_snat                                                30.0.0.2                            192.168.1.3
+dnat_and_snat                                                40.0.0.2                            192.168.1.4
+dnat_and_snat                                                fd01::2                             fd11::3
+snat                                                         30.0.0.1                            192.168.1.0/24
+snat                                                         40.0.0.3                            192.168.1.6
+snat                                                         fd01::1                             fd11::/64
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat_and_snat                          30.0.0.2                            192.168.1.3
-dnat_and_snat                          40.0.0.2                            192.168.1.4
-dnat_and_snat                          fd01::2                             fd11::3
-snat                                   30.0.0.1                            192.168.1.0/24
-snat                                   40.0.0.3                            192.168.1.6
-snat                                   fd01::1                             fd11::/64
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat_and_snat                                                30.0.0.2                            192.168.1.3
+dnat_and_snat                                                40.0.0.2                            192.168.1.4
+dnat_and_snat                                                fd01::2                             fd11::3
+snat                                                         30.0.0.1                            192.168.1.0/24
+snat                                                         40.0.0.3                            192.168.1.6
+snat                                                         fd01::1                             fd11::/64
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-del lr0])
@@ -810,12 +810,12 @@  AT_CHECK([ovn-nbctl show lr0 | grep -C2 'external port(s): "1"' | uuidfilt], [0]
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat                                   40.0.0.4           1-3000           192.168.1.7
-dnat                                   40.0.0.5           1                192.168.1.10
-dnat_and_snat                          40.0.0.5           1-3000           192.168.1.8
-dnat_and_snat                          40.0.0.6           1-3000           192.168.1.9         00:00:00:04:05:06    lp0
-snat                                   40.0.0.3           21-65535         192.168.1.6
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         40.0.0.4           1-3000           192.168.1.7
+dnat                                                         40.0.0.5           1                192.168.1.10
+dnat_and_snat                                                40.0.0.5           1-3000           192.168.1.8
+dnat_and_snat                                                40.0.0.6           1-3000           192.168.1.9         00:00:00:04:05:06    lp0
+snat                                                         40.0.0.3           21-65535         192.168.1.6
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-del lr0])
@@ -895,39 +895,39 @@  AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 dnat 30.0.0.10 20.0.0.11
 AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 dnat 30.0.0.10 20.0.0.10])
 
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-dnat             lrp00                 30.0.0.10                           20.0.0.10
-dnat             lrp01                 30.0.0.10                           20.0.0.10
-snat                                   172.64.1.10                         20.0.0.10
-snat             lrp00                 172.64.0.10                         20.0.0.10
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat             lrp00                                       30.0.0.10                           20.0.0.10
+dnat             lrp01                                       30.0.0.10                           20.0.0.10
+snat                                                         172.64.1.10                         20.0.0.10
+snat             lrp00                                       172.64.0.10                         20.0.0.10
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 30.0.0.10])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-snat                                   172.64.1.10                         20.0.0.10
-snat             lrp00                 172.64.0.10                         20.0.0.10
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         172.64.1.10                         20.0.0.10
+snat             lrp00                                       172.64.0.10                         20.0.0.10
 ])
 
 AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 snat 30.0.0.10 20.0.0.20])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-snat                                   172.64.1.10                         20.0.0.10
-snat             lrp00                 172.64.0.10                         20.0.0.10
-snat             lrp00                 30.0.0.10                           20.0.0.20
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         172.64.1.10                         20.0.0.10
+snat             lrp00                                       172.64.0.10                         20.0.0.10
+snat             lrp00                                       30.0.0.10                           20.0.0.20
 ])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat lrp11], [1], [],
 [ovn-nbctl: lrp11: port name not found
 ])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 snat lrp00])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-snat                                   172.64.1.10                         20.0.0.10
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         172.64.1.10                         20.0.0.10
 ])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 snat lrp01])
 AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE             GATEWAY_PORT          EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
-snat                                   172.64.1.10                         20.0.0.10
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         172.64.1.10                         20.0.0.10
 ])
 
 AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 20.0.0 lrp01], [1], [],
@@ -938,6 +938,96 @@  AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 20.0.0.10 lrp01], [1], [],
 ])
 AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 20.0.0.10 lrp01])
 AT_CHECK([ovn-nbctl lr-nat-del lr0 snat])
+
+AT_CHECK([ovn-nbctl lr-nat-del lr0])
+AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lrp00 chassis1])
+AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lrp01 chassis2])
+
+AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 10.0.0.1 192.168.0.0/24])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 snat 10.0.0.2 192.168.0.0/24])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         10.0.0.1                            192.168.0.0/24
+snat                                   tcp                   10.0.0.2                            192.168.0.0/24
+])
+
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 snat 10.0.0.3 192.168.0.0/24], [1], [],
+[ovn-nbctl: a NAT with this type (snat), logical_ip (192.168.0.0/24) and match "tcp" already exists
+])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 snat 10.0.0.2 192.168.0.0/24], [1], [],
+[ovn-nbctl: 10.0.0.2, 192.168.0.0/24: a NAT with this external_ip and logical_ip already exists
+])
+AT_CHECK([ovn-nbctl --may-exist --priority=5 --match="udp" lr-nat-add lr0 snat 10.0.0.2 192.168.0.0/24])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         10.0.0.1                            192.168.0.0/24
+snat                                   udp                   10.0.0.2                            192.168.0.0/24
+])
+
+AT_CHECK([ovn-nbctl --match="udp" lr-nat-del lr0 snat 192.168.0.0/24])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+snat                                                         10.0.0.1                            192.168.0.0/24
+])
+
+AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 192.168.0.0/24])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0])
+
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 10.0.0.1 192.168.0.1])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat 10.0.0.1 192.168.0.2])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         10.0.0.1                            192.168.0.1
+dnat                                   tcp                   10.0.0.1                            192.168.0.2
+])
+
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat 10.0.0.1 192.168.0.3], [1], [],
+[ovn-nbctl: a NAT with this type (dnat), external_ip (10.0.0.1) and match "tcp" already exists
+])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat 10.0.0.1 192.168.0.2], [1], [],
+[ovn-nbctl: 10.0.0.1, 192.168.0.2: a NAT with this external_ip and logical_ip already exists
+])
+AT_CHECK([ovn-nbctl --may-exist --priority=10 --match="udp" lr-nat-add lr0 dnat 10.0.0.1 192.168.0.2])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         10.0.0.1                            192.168.0.1
+dnat                                   udp                   10.0.0.1                            192.168.0.2
+])
+
+AT_CHECK([ovn-nbctl --match="udp" lr-nat-del lr0 dnat 10.0.0.1])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat                                                         10.0.0.1                            192.168.0.1
+])
+
+AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 10.0.0.1])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0])
+
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 10.0.0.1 192.168.0.1])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat_and_snat 10.0.0.1 192.168.0.2])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat_and_snat                                                10.0.0.1                            192.168.0.1
+dnat_and_snat                          tcp                   10.0.0.1                            192.168.0.2
+])
+
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat_and_snat 10.0.0.1 192.168.0.3], [1], [],
+[ovn-nbctl: a NAT with this type (dnat_and_snat), external_ip (10.0.0.1) and match "tcp" already exists
+])
+AT_CHECK([ovn-nbctl --match="tcp" lr-nat-add lr0 dnat_and_snat 10.0.0.1 192.168.0.2], [1], [],
+[ovn-nbctl: 10.0.0.1, 192.168.0.2: a NAT with this external_ip and logical_ip already exists
+])
+AT_CHECK([ovn-nbctl --may-exist --priority=10 --match="udp" lr-nat-add lr0 dnat_and_snat 10.0.0.1 192.168.0.2])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE             GATEWAY_PORT          MATCH                 EXTERNAL_IP        EXTERNAL_PORT    LOGICAL_IP          EXTERNAL_MAC         LOGICAL_PORT
+dnat_and_snat                                                10.0.0.1                            192.168.0.1
+dnat_and_snat                          udp                   10.0.0.1                            192.168.0.2
+])
+
+AT_CHECK([ovn-nbctl --priority=5 lr-nat-add lr0 snat 10.0.0.2 192.168.0.0/24], [1], [],
+[ovn-nbctl: priority can be set only with --match option.
+])
+
 ])
 
 dnl ---------------------------------------------------------------------
diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
index ea2b201a5..9e31d1cf0 100644
--- a/utilities/ovn-nbctl.8.xml
+++ b/utilities/ovn-nbctl.8.xml
@@ -1175,7 +1175,7 @@ 
     <h2>NAT Commands</h2>
 
     <dl>
-      <dt>[<code>--may-exist</code>] [<code>--stateless</code>] [<code>--gateway-port</code>=<var>GATEWAY_PORT</var>] [--portrange] <code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var> [<var>logical_port</var> <var>external_mac</var>] [<var>external_port_range</var>]</dt>
+      <dt>[<code>--may-exist</code>] [<code>--stateless</code>] [<code>--gateway-port</code>=<var>GATEWAY_PORT</var>] [--portrange] [<code>--match</code>=<var>MATCH</var>] [<code>--priority</code>=<var>PRIORITY</var>] <code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var> [<var>logical_port</var> <var>external_mac</var>] [<var>external_port_range</var>]</dt>
       <dd>
         <p>
           Adds the specified NAT to <var>router</var>.
@@ -1224,6 +1224,18 @@ 
           <code>external_port_range</code> is <code>1-65535</code>.
         </p>
 
+        <p>
+          The <code>--match</code> allows to specify the extra match condition.
+          The extra match is for more fine-grained control over the NAT rule.
+        </p>
+
+        <p>
+          The <code>--priority</code> option allows to specify order of NAT
+          rule evaluation. Priority must be between <code>0</code> and
+          <code>32767</code>, inclusive and can be only specified together
+          with <code>--match</code>.
+        </p>
+
         <p>
           When <var>type</var> is <code>dnat</code>, the externally
           visible IP address <var>external_ip</var> is DNATted to the
diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
index 618f3a18b..da31c45f1 100644
--- a/utilities/ovn-nbctl.c
+++ b/utilities/ovn-nbctl.c
@@ -4981,6 +4981,8 @@  nbctl_pre_lr_nat_add(struct ctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_logical_port);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_external_mac);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_gateway_port);
+    ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_match);
+    ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_priority);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_options);
 
     ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
@@ -5177,6 +5179,21 @@  nbctl_lr_nat_add(struct ctl_context *ctx)
         }
     }
 
+    int64_t priority = 0;
+    const char *match = shash_find_data(&ctx->options, "--match");
+    const char *priority_opt = shash_find_data(&ctx->options, "--priority");
+
+    if (!match && priority_opt) {
+        ctl_error(ctx, "priority can be set only with --match option.");
+        goto cleanup;
+    } else if (match && priority_opt) {
+        error = parse_priority(priority_opt, &priority);
+        if (error) {
+            ctx->error = error;
+            goto cleanup;
+        }
+    }
+
     for (size_t i = 0; i < lr->n_nat; i++) {
         const struct nbrec_nat *nat = lr->nat[i];
 
@@ -5207,6 +5224,10 @@  nbctl_lr_nat_add(struct ctl_context *ctx)
                             nbrec_nat_verify_external_mac(nat);
                             nbrec_nat_set_logical_port(nat, logical_port);
                             nbrec_nat_set_external_mac(nat, external_mac);
+                            if (match) {
+                                nbrec_nat_set_match(nat, match);
+                                nbrec_nat_set_priority(nat, priority);
+                            }
                             should_return = true;
                         } else {
                             ctl_error(ctx, "%s, %s: a NAT with this "
@@ -5215,12 +5236,19 @@  nbctl_lr_nat_add(struct ctl_context *ctx)
                                       new_logical_ip);
                             should_return = true;
                         }
-                } else {
-                    ctl_error(ctx, "a NAT with this type (%s), %s (%s) "
-                              "already exists",
-                              nat_type,
-                              is_snat ? "logical_ip" : "external_ip",
-                              is_snat ? new_logical_ip : new_external_ip);
+                } else if (!match || !strcmp(nat->match, match)) {
+                    if (match) {
+                        ctl_error(ctx, "a NAT with this type (%s), %s (%s) "
+                                   "and match \"%s\" already exists", nat_type,
+                                  is_snat ? "logical_ip" : "external_ip",
+                                  is_snat ? new_logical_ip : new_external_ip,
+                                  match);
+                    } else {
+                        ctl_error(ctx, "a NAT with this type (%s), %s (%s) "
+                                       "already exists", nat_type,
+                                  is_snat ? "logical_ip" : "external_ip",
+                                  is_snat ? new_logical_ip : new_external_ip);
+                    }
                     should_return = true;
                 }
             }
@@ -5273,6 +5301,11 @@  nbctl_lr_nat_add(struct ctl_context *ctx)
     }
     nbrec_nat_set_options(nat, &nat_options);
 
+    if (match) {
+        nbrec_nat_set_match(nat, match);
+        nbrec_nat_set_priority(nat, priority);
+    }
+
     smap_destroy(&nat_options);
 
     /* Insert the NAT into the logical router. */
@@ -5293,30 +5326,79 @@  nbctl_pre_lr_nat_del(struct ctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_logical_ip);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_external_ip);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_gateway_port);
+    ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_match);
 
     ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
 }
 
+static size_t
+lr_nat_del_matching(const struct nbrec_logical_router *lr, const char *type,
+                    const char *ip,
+                    const struct nbrec_logical_router_port *dgw_port,
+                    const char *match, bool is_snat)
+{
+    size_t n_deleted = 0;
+
+    for (size_t i = 0; i < lr->n_nat; i++) {
+        struct nbrec_nat *nat = lr->nat[i];
+        char *old_ip = normalize_prefix_str(is_snat
+                                            ? nat->logical_ip
+                                            : nat->external_ip);
+        if (!old_ip) {
+            continue;
+        }
+
+        bool delete = true;
+
+        if (type && strcmp(type, nat->type)) {
+            delete = false;
+        }
+
+        if (ip && strcmp(ip, old_ip)) {
+            delete = false;
+        }
+
+        if (dgw_port && nat->gateway_port != dgw_port) {
+            delete = false;
+        }
+
+        if (match && strcmp(match, nat->match)) {
+            delete = false;
+        }
+
+        if (delete) {
+            nbrec_logical_router_update_nat_delvalue(lr, nat);
+            n_deleted++;
+        }
+
+        free(old_ip);
+    }
+
+    return n_deleted;
+}
+
 static void
 nbctl_lr_nat_del(struct ctl_context *ctx)
 {
     const struct nbrec_logical_router *lr = NULL;
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
+    const char *match = shash_find_data(&ctx->options, "--match");
     char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr);
     if (error) {
         ctx->error = error;
         return;
     }
 
-    if (ctx->argc == 2) {
-        /* If type, external_ip and logical_ip are not specified, delete
-         * all NATs. */
+    if (ctx->argc == 2 && !match) {
+        /* If type, external_ip, logical_ip and match are not specified,
+         * delete all NATs. */
         nbrec_logical_router_verify_nat(lr);
         nbrec_logical_router_set_nat(lr, NULL, 0);
         return;
     }
 
     const char *nat_type = ctx->argv[2];
+    int is_snat = !strcmp("snat", nat_type);
     if (strcmp(nat_type, "dnat") && strcmp(nat_type, "snat")
             && strcmp(nat_type, "dnat_and_snat")) {
         ctl_error(ctx, "%s: type must be one of \"dnat\", \"snat\" and "
@@ -5326,16 +5408,11 @@  nbctl_lr_nat_del(struct ctl_context *ctx)
 
     if (ctx->argc == 3) {
         /*Deletes all NATs with the specified type. */
-        for (size_t i = 0; i < lr->n_nat; i++) {
-            if (!strcmp(nat_type, lr->nat[i]->type)) {
-                nbrec_logical_router_update_nat_delvalue(lr, lr->nat[i]);
-            }
-        }
+        lr_nat_del_matching(lr, nat_type, NULL, NULL, match, is_snat);
         return;
     }
 
     char *nat_ip = normalize_prefix_str(ctx->argv[3]);
-    int is_snat = !strcmp("snat", nat_type);
     const struct nbrec_logical_router_port *dgw_port = NULL;
 
     if (ctx->argc == 4) {
@@ -5346,32 +5423,9 @@  nbctl_lr_nat_del(struct ctl_context *ctx)
                 ctx->error = error;
                 return;
             }
-
-            /* Deletes all NATs matching the type and gateway_port
-             * specified. */
-            for (size_t i = 0; i < lr->n_nat; i++) {
-                if (!strcmp(nat_type, lr->nat[i]->type) &&
-                    lr->nat[i]->gateway_port == dgw_port) {
-                    nbrec_logical_router_update_nat_delvalue(lr, lr->nat[i]);
-                }
-            }
-            return;
         }
 
-        /* Remove NAT rules matching the type and IP (based on type). */
-        for (size_t i = 0; i < lr->n_nat; i++) {
-            struct nbrec_nat *nat = lr->nat[i];
-            char *old_ip = normalize_prefix_str(is_snat
-                                                ? nat->logical_ip
-                                                : nat->external_ip);
-            if (!old_ip) {
-                continue;
-            }
-            if (!strcmp(nat_type, nat->type) && !strcmp(nat_ip, old_ip)) {
-                nbrec_logical_router_update_nat_delvalue(lr, nat);
-            }
-            free(old_ip);
-        }
+        lr_nat_del_matching(lr, nat_type, nat_ip, dgw_port, match, is_snat);
         goto cleanup;
     }
 
@@ -5386,32 +5440,22 @@  nbctl_lr_nat_del(struct ctl_context *ctx)
         goto cleanup;
     }
 
-    /* Remove matching NAT. */
-    for (size_t i = 0; i < lr->n_nat; i++) {
-        struct nbrec_nat *nat = lr->nat[i];
-        bool should_return = false;
-        char *old_ip = normalize_prefix_str(is_snat
-                                            ? nat->logical_ip
-                                            : nat->external_ip);
-        if (!old_ip) {
-            continue;
-        }
-        if (!strcmp(nat_type, nat->type) && !strcmp(nat_ip, old_ip) &&
-            nat->gateway_port == dgw_port) {
-            nbrec_logical_router_update_nat_delvalue(lr, nat);
-            should_return = true;
-        }
-        free(old_ip);
-        if (should_return) {
-            goto cleanup;
+    size_t n_deleted = lr_nat_del_matching(lr, nat_type, nat_ip, dgw_port,
+                                           match, is_snat);
+    if (must_exist && !n_deleted) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+        ds_put_format(&ds, "no matching NAT with the type (%s), %s (%s)",
+                      nat_type, is_snat ? "logical_ip" : "external_ip",
+                      nat_ip);
+        if (match) {
+            ds_put_format(&ds, ", gateway_port (%s) and match (%s)",
+                          ctx->argv[4], match);
+        } else {
+            ds_put_format(&ds, " and gateway_port (%s)", ctx->argv[4]);
         }
-    }
 
-    if (must_exist) {
-        ctl_error(ctx, "no matching NAT with the type (%s), %s (%s) and "
-                  "gateway_port (%s)", nat_type,
-                  is_snat ? "logical_ip" : "external_ip", nat_ip,
-                  ctx->argv[4]);
+        ctx->error = xstrdup(ds_cstr_ro(&ds));
+        ds_destroy(&ds);
     }
 
 cleanup:
@@ -5431,6 +5475,7 @@  nbctl_pre_lr_nat_list(struct ctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_external_mac);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_logical_port);
     ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_gateway_port);
+    ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_match);
 
     ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
 }
@@ -5448,11 +5493,12 @@  nbctl_lr_nat_list(struct ctl_context *ctx)
     struct smap lr_nats = SMAP_INITIALIZER(&lr_nats);
     for (size_t i = 0; i < lr->n_nat; i++) {
         const struct nbrec_nat *nat = lr->nat[i];
-        char *key = xasprintf("%-17.13s%-22.18s%s",
+        char *key = xasprintf("%-17.13s%-22.18s%-22.18s%-19.15s",
                               nat->type,
                               nat->gateway_port
                               ? nat->gateway_port->name
                               : "",
+                              nat->match,
                               nat->external_ip);
         if (nat->external_mac && nat->logical_port) {
             smap_add_format(&lr_nats, key,
@@ -5472,13 +5518,13 @@  nbctl_lr_nat_list(struct ctl_context *ctx)
     const struct smap_node **nodes = smap_sort(&lr_nats);
     if (nodes) {
         ds_put_format(&ctx->output,
-                "%-17.13s%-22.18s%-19.15s%-17.13s%-20.16s%-21.17s%s\n",
-                "TYPE", "GATEWAY_PORT", "EXTERNAL_IP", "EXTERNAL_PORT",
-                "LOGICAL_IP", "EXTERNAL_MAC", "LOGICAL_PORT");
+                "%-17.13s%-22.18s%-22.18s%-19.15s%-17.13s%-20.16s%-21.17s%s\n",
+                "TYPE", "GATEWAY_PORT", "MATCH", "EXTERNAL_IP",
+                "EXTERNAL_PORT", "LOGICAL_IP", "EXTERNAL_MAC", "LOGICAL_PORT");
         for (size_t i = 0; i < smap_count(&lr_nats); i++) {
             const struct smap_node *node = nodes[i];
-            ds_put_format(&ctx->output, "%-58.54s%s\n",
-                    node->key, node->value);
+            ds_put_format(&ctx->output, "%-80.86s%s\n",
+                          node->key, node->value);
         }
         free(nodes);
     }
@@ -8022,9 +8068,10 @@  static const struct ctl_command_syntax nbctl_commands[] = {
       "[LOGICAL_PORT EXTERNAL_MAC] [EXTERNAL_PORT_RANGE]",
       nbctl_pre_lr_nat_add, nbctl_lr_nat_add,
       NULL, "--may-exist,--stateless,--portrange,--add-route,"
-      "--gateway-port=", RW },
+      "--gateway-port=,--priority=,--match=", RW },
     { "lr-nat-del", 1, 4, "ROUTER [TYPE [IP] [GATEWAY_PORT]]",
-      nbctl_pre_lr_nat_del, nbctl_lr_nat_del, NULL, "--if-exists", RW },
+      nbctl_pre_lr_nat_del, nbctl_lr_nat_del, NULL,
+      "--if-exists,--match=", RW },
     { "lr-nat-list", 1, 1, "ROUTER", nbctl_pre_lr_nat_list,
       nbctl_lr_nat_list, NULL, "", RO },
     { "lr-nat-update-ext-ip", 4, 4, "ROUTER TYPE IP ADDRESS_SET",