Blob Blame History Raw
From 643d4be1b3f40c3075af584d0cbc83e34a5e51ca Mon Sep 17 00:00:00 2001
Message-Id: <643d4be1b3f40c3075af584d0cbc83e34a5e51ca.1586727203.git.lorenzo.bianconi@redhat.com>
In-Reply-To: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
References: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Date: Wed, 1 Apr 2020 18:37:31 +0200
Subject: [PATCH 3/3] northd: Add logical flows for dhcpv6 pfd parsing

Introduce logical flows in ovn router pipeline in order to parse dhcpv6
advertise/reply from IPv6 prefix delegation router.
Do not overwrite ipv6_ra_pd_list info in options column of SB port_binding
table written by ovn-controller
Introduce ipv6_prefix column in NB Logical_router_port table to report
IPv6 prefix received from delegation router to the CMS

Change-Id: Ibc1bd83bf3d9d4671f70610df5635e6266580a18
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
---
 NEWS                    |   1 +
 northd/ovn-northd.8.xml |   8 +++
 northd/ovn-northd.c     |  95 ++++++++++++++++++++++++--
 ovn-nb.ovsschema        |   7 +-
 ovn-nb.xml              |  21 ++++++
 tests/atlocal.in        |   5 +-
 tests/system-ovn.at     | 143 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 272 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index 9b36bfd17..21c80f0dc 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ OVN v20.03.0 - 28 Feb 2020
    - Added Forwarding Group support in OVN.
    - Added support for MLD Snooping and MLD Querier.
    - Added support for ECMP routes in OVN router.
+   - Added IPv6 Prefix Delegation support in OVN.
 
    - OVN Interconnection:
      * Support for L3 interconnection of multiple OVN deployments with tunnels
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index b5e4d6d84..82c86f636 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1660,6 +1660,14 @@ next;
         </ul>
       </li>
 
+      <li>
+          A priority-100 flow parses DHCPv6 replies from IPv6 prefix
+          delegation routers (<code>udp.src == 547 &amp;&amp;
+          udp.dst == 546</code>). The <code>handle_dhcpv6_reply</code>
+          is used to send IPv6 prefix delegation messages to the delegation
+          router.
+      </li>
+
       <li>
         <p>
           A priority-87 flow explicitly allows IPv6 multicast traffic that is
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index bb68b8fe9..fd1be5b27 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -2688,6 +2688,33 @@ op_get_name(const struct ovn_port *op)
     return name;
 }
 
+static void
+ovn_update_ipv6_prefix(struct hmap *ports)
+{
+    const struct ovn_port *op;
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbrp) {
+            continue;
+        }
+
+        char prefix[IPV6_SCAN_LEN + 6];
+        unsigned aid;
+        const char *ipv6_pd_list = smap_get(&op->sb->options,
+                                            "ipv6_ra_pd_list");
+        if (!ipv6_pd_list ||
+            !ovs_scan(ipv6_pd_list, "%u:%s", &aid, prefix)) {
+            continue;
+        }
+
+        struct sset ipv6_prefix_set = SSET_INITIALIZER(&ipv6_prefix_set);
+        sset_add(&ipv6_prefix_set, prefix);
+        nbrec_logical_router_port_set_ipv6_prefix(op->nbrp,
+                                            sset_array(&ipv6_prefix_set),
+                                            sset_count(&ipv6_prefix_set));
+        sset_destroy(&ipv6_prefix_set);
+    }
+}
+
 static void
 ovn_port_update_sbrec(struct northd_context *ctx,
                       struct ovsdb_idl_index *sbrec_chassis_by_name,
@@ -2818,6 +2845,13 @@ ovn_port_update_sbrec(struct northd_context *ctx,
                 smap_add(&new, "l3gateway-chassis", chassis_name);
             }
         }
+
+        const char *ipv6_pd_list = smap_get(&op->sb->options,
+                                            "ipv6_ra_pd_list");
+        if (ipv6_pd_list) {
+            smap_add(&new, "ipv6_ra_pd_list", ipv6_pd_list);
+        }
+
         sbrec_port_binding_set_options(op->sb, &new);
         smap_destroy(&new);
 
@@ -4683,12 +4717,12 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
          * unreachable packets. */
         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
-                      "icmp6.type == 1 || (tcp && tcp.flags == 20)",
-                      "next;");
+                      "icmp6.type == 1 || (tcp && tcp.flags == 20) || "
+                      "(udp && udp.src == 546 && udp.dst == 547)", "next;");
         ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
-                      "icmp6.type == 1 || (tcp && tcp.flags == 20)",
-                      "next;");
+                      "icmp6.type == 1 || (tcp && tcp.flags == 20) ||"
+                      "(udp && udp.src == 546 && udp.dst == 547)", "next;");
 
         /* Ingress and Egress Pre-ACL Table (Priority 100).
          *
@@ -7744,6 +7778,11 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
         }
         ds_put_format(&s, "%s/%u ", addrs->network_s, addrs->plen);
     }
+
+    const char *ra_pd_list = smap_get(&op->sb->options, "ipv6_ra_pd_list");
+    if (ra_pd_list) {
+        ds_put_cstr(&s, ra_pd_list);
+    }
     /* Remove trailing space */
     ds_chomp(&s, ' ');
     smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s));
@@ -8488,7 +8527,34 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
         free(snat_ips);
     }
 
-    /* Logical router ingress table 3: IP Input for IPv6. */
+    /* DHCPv6 reply handling */
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbrp) {
+            continue;
+        }
+
+        if (op->derived) {
+            continue;
+        }
+
+        struct lport_addresses lrp_networks;
+        if (!extract_lrp_networks(op->nbrp, &lrp_networks)) {
+            continue;
+        }
+
+        for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) {
+            ds_clear(&actions);
+            ds_clear(&match);
+            ds_put_format(&match, "ip6.dst == %s && udp.src == 547 &&"
+                          " udp.dst == 546",
+                          lrp_networks.ipv6_addrs[i].addr_s);
+            ds_put_format(&actions, "reg0 = 0; handle_dhcpv6_reply;");
+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
+                          ds_cstr(&match), ds_cstr(&actions));
+        }
+    }
+
+    /* Logical router ingress table 1: IP Input for IPv6. */
     HMAP_FOR_EACH (op, key_node, ports) {
         if (!op->nbrp) {
             continue;
@@ -9250,6 +9316,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
+        struct smap options;
+        /* enable IPv6 prefix delegation */
+        bool prefix_delegation = smap_get_bool(&op->nbrp->options,
+                                               "prefix_delegation", false);
+        if (prefix_delegation) {
+            smap_clone(&options, &op->sb->options);
+            smap_add(&options, "ipv6_prefix_delegation", "true");
+            sbrec_port_binding_set_options(op->sb, &options);
+            smap_destroy(&options);
+        }
+
+        if (smap_get_bool(&op->nbrp->options, "prefix", false)) {
+            smap_clone(&options, &op->sb->options);
+            smap_add(&options, "ipv6_prefix", "true");
+            sbrec_port_binding_set_options(op->sb, &options);
+            smap_destroy(&options);
+        }
+
         const char *address_mode = smap_get(
             &op->nbrp->ipv6_ra_configs, "address_mode");
 
@@ -10941,6 +11025,7 @@ ovnnb_db_run(struct northd_context *ctx,
     build_meter_groups(ctx, &meter_groups);
     build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
                  &igmp_groups, &meter_groups, &lbs);
+    ovn_update_ipv6_prefix(ports);
 
     sync_address_sets(ctx);
     sync_port_groups(ctx);
diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index ea6f4e354..949f6258b 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
-    "version": "5.20.1",
-    "cksum": "721375950 25251",
+    "version": "5.22.0",
+    "cksum": "170077561 25417",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -342,6 +342,9 @@
                 "ipv6_ra_configs": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}},
+                "ipv6_prefix": {"type": {"key": "string",
+                                      "min": 0,
+                                      "max": "unlimited"}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
diff --git a/ovn-nb.xml b/ovn-nb.xml
index f7ba9c334..045c63fb0 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2065,6 +2065,11 @@
       port has all ingress and egress traffic dropped.
     </column>
 
+    <column name="ipv6_prefix">
+       This column contains IPv6 prefix obtained by prefix delegation
+       router according to RFC 3633
+    </column>
+
     <group title="ipv6_ra_configs">
       <p>
         This column defines the IPv6 ND RA address mode and ND MTU Option to be
@@ -2320,6 +2325,22 @@
         <code>ovn-northd</code> honors the configured value.
       </column>
 
+      <column name="options" key="prefix_delegation"
+              type='{"type": "boolean"}'>
+        <p>
+          If set to <code>true</code>, enable IPv6 prefix delegation state
+          machine on this logical router port (RFC3633). IPv6 prefix
+          delegation is available just on a gateway router or on a gateway
+          router port.
+        </p>
+      </column>
+
+      <column name="options" key="prefix" type='{"type": "boolean"}'>
+        <p>
+          If set to <code>true</code>, this interface will receive an IPv6
+          prefix according to RFC3663
+        </p>
+      </column>
     </group>
 
     <group title="Attachment">
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 5f14c3da0..8f3ff03b9 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -157,7 +157,7 @@ find_command()
 {
     which $1 > /dev/null 2>&1
     status=$?
-    var=HAVE_`echo "$1" | tr '[a-z]' '[A-Z]'`
+    var=HAVE_`echo "$1" | tr '-' '_' | tr '[a-z]' '[A-Z]'`
     if test "$status" = "0"; then
         eval ${var}="yes"
     else
@@ -192,6 +192,9 @@ else
     DIFF_SUPPORTS_NORMAL_FORMAT=no
 fi
 
+# Set HAVE_DIBBLER-SERVER
+find_command dibbler-server
+
 # Turn off proxies.
 unset http_proxy
 unset https_proxy
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index f1ae69b20..000b3f13b 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -3793,3 +3793,146 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
 /connection dropped.*/d"])
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- IPv6 prefix delegation])
+AT_SKIP_IF([test $HAVE_DIBBLER_SERVER = no])
+AT_SKIP_IF([test $HAVE_TCPDUMP = no])
+AT_KEYWORDS([ovn-ipv6-prefix_d])
+
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+
+ADD_BR([br-int])
+ADD_BR([br-ext])
+
+ovs-ofctl add-flow br-ext action=normal
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+        -- set Open_vSwitch . external-ids:system-id=hv1 \
+        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-nbctl lr-add R1
+
+ovn-nbctl ls-add sw0
+ovn-nbctl ls-add sw1
+ovn-nbctl ls-add public
+
+ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
+ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24
+ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 \
+    -- set Logical_Router_Port rp-public options:redirect-chassis=hv1
+
+ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+    type=router options:router-port=rp-sw0 \
+    -- lsp-set-addresses sw0-rp router
+ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \
+    type=router options:router-port=rp-sw1 \
+    -- lsp-set-addresses sw1-rp router
+
+ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+    type=router options:router-port=rp-public \
+    -- lsp-set-addresses public-rp router
+
+ADD_NAMESPACES(sw01)
+ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
+         "192.168.1.1")
+ovn-nbctl lsp-add sw0 sw01 \
+    -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2"
+
+ADD_NAMESPACES(sw11)
+ADD_VETH(sw11, sw11, br-int, "192.168.2.2/24", "f0:00:00:02:02:03", \
+         "192.168.2.1")
+ovn-nbctl lsp-add sw1 sw11 \
+    -- lsp-set-addresses sw11 "f0:00:00:02:02:03 192.168.2.2"
+
+ADD_NAMESPACES(server)
+ADD_VETH(s1, server, br-ext, "2001:1db8:3333::2/64", "f0:00:00:01:02:05", \
+         "2001:1db8:3333::1")
+
+OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep 2001:1db8:3333::2 | grep tentative)" = ""])
+OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep fe80 | grep tentative)" = ""])
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext])
+ovn-nbctl lsp-add public public1 \
+        -- lsp-set-addresses public1 unknown \
+        -- lsp-set-type public1 localnet \
+        -- lsp-set-options public1 network_name=phynet
+
+ovn-nbctl set logical_router_port rp-public options:prefix_delegation=true
+ovn-nbctl set logical_router_port rp-public options:prefix=true
+ovn-nbctl set logical_router_port rp-sw0 options:prefix=true
+ovn-nbctl set logical_router_port rp-sw1 options:prefix=true
+
+# reset dibbler state
+sed s/^iface.*/"iface \"s1\" {"/g -i /etc/dibbler/server.conf
+sed s/pd-pool.*/"pd-pool 2001:1db8:3333::\/80"/g -i /etc/dibbler/server.conf
+sed s/t1.*/"t1 10"/g -i /etc/dibbler/server.conf
+sed s/t2.*/"t2 15"/g -i /etc/dibbler/server.conf
+cat > /var/lib/dibbler/server-AddrMgr.xml <<EOF
+<AddrMgr>
+  <timestamp>1575481348</timestamp>
+  <replayDetection>0</replayDetection>
+</AddrMgr>
+EOF
+cat > /var/lib/dibbler/server-CfgMgr.xml <<EOF
+<SrvCfgMgr>
+  <workDir>/var/lib/dibbler</workDir>
+  <LogName>Server</LogName>
+  <LogLevel>8</LogLevel>
+  <InactiveMode>0</InactiveMode>
+  <GuessMode>0</GuessMode>
+</SrvCfgMgr>
+EOF
+
+NS_CHECK_EXEC([server], [dibbler-server run > dibbler.log &])
+ovn-nbctl --wait=hv sync
+
+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c4-15)" = ""])
+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c4-15)" = ""])
+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c4-15)" = ""])
+
+AT_CHECK([ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c3-16], [0], [dnl
+[2001:1db8:3333]
+])
+AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0], [dnl
+[2001:1db8:3333]
+])
+AT_CHECK([ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c3-16], [0], [dnl
+[2001:1db8:3333]
+])
+
+kill $(pidof dibbler-server)
+
+prefix=$(ovn-nbctl list logical_router_port rp-public | awk -F/ '/ipv6_prefix/{print substr($1,25,9)}' | sed 's/://g')
+ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
+ovn-nbctl set logical_router_port rp-sw1 options:prefix=false
+
+NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[95:4]]=0x${prefix} > public.pcap &])
+
+OVS_WAIT_UNTIL([
+    total_pkts=$(cat public.pcap | wc -l)
+    test "${total_pkts}" = "1"
+])
+
+kill $(pidof tcpdump)
+kill $(pidof ovn-controller)
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
+/.*terminating with signal 15.*/d"])
+AT_CLEANUP
-- 
2.25.2