The the logical routers will populate the logical router's ND table when
receiving the NS/NA packets. If we continue to send ND advertisements or
solicitations to logical router, the MAC_Binding table will continue to
increase. That may reduce system performance and cause instability and
crashes. This patches may fix this bug.

Signed-off-by: nickcooper-zhangtonghao <nickcooper-zhangtong...@opencloud.tech>
---
 include/ovn/actions.h     |  7 ++++
 ovn/controller/pinctrl.c  | 99 +++++++++++++++++++++++++++++++++++++++++++----
 ovn/lib/actions.c         | 29 +++++++++++++-
 ovn/northd/ovn-northd.c   | 49 ++++++++++++++++-------
 ovn/utilities/ovn-trace.c | 25 ++++++++++++
 5 files changed, 186 insertions(+), 23 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index e2a716a..b1f67be 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -61,6 +61,7 @@ struct simap;
     OVNACT(CT_SNAT,       ovnact_ct_nat)            \
     OVNACT(CT_LB,         ovnact_ct_lb)             \
     OVNACT(ARP,           ovnact_nest)              \
+    OVNACT(ND_NS,         ovnact_nest)              \
     OVNACT(ND_NA,         ovnact_nest)              \
     OVNACT(GET_ARP,       ovnact_get_mac_bind)      \
     OVNACT(PUT_ARP,       ovnact_put_mac_bind)      \
@@ -329,6 +330,12 @@ enum action_opcode {
      */
     ACTION_OPCODE_PUT_DHCP_OPTS,
 
+    /* "nd_ns { ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ND_NS,
+
     /* "nd_na { ...actions... }".
      *
      * The actions, in OpenFlow 1.3 format, follow the action_header.
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 177d0d4..9c39681 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -79,6 +79,9 @@ static void send_garp_run(const struct ovsrec_bridge *,
 static void pinctrl_handle_nd_na(const struct flow *ip_flow,
                                  const struct match *md,
                                  struct ofpbuf *userdata);
+static void pinctrl_handle_nd_ns(const struct flow *ip_flow,
+                                 const struct match *md,
+                                 struct ofpbuf *userdata);
 static void reload_metadata(struct ofpbuf *ofpacts,
                             const struct match *md);
 
@@ -717,6 +720,10 @@ process_packet_in(const struct ofp_header *msg)
         pinctrl_handle_nd_na(&headers, &pin.flow_metadata, &userdata);
         break;
 
+    case ACTION_OPCODE_ND_NS:
+        pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     case ACTION_OPCODE_PUT_ND:
         pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
                                        false);
@@ -868,17 +875,29 @@ pinctrl_handle_neighbour_cache(uint32_t dp_key, uint32_t 
port_key,
 static void
 send_solicitation_request(const struct neighbour_cache * nc)
 {
-    /* Compose a ARP request packet. */
+    /* Compose a ARP/NS request packet. */
     uint64_t packet_stub[128 / 8];
     struct dp_packet packet;
-    ovs_be32 nw_dst, nw_src;
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
 
-    inet_pton(AF_INET, nc->nw_dst, &nw_dst);
-    inet_pton(AF_INET, nc->nw_src, &nw_src);
+    if (nc->type == OVN_NEIGHBOUR_TYPE_ARP) {
+        ovs_be32 ip_dst, ip_src;
+        inet_pton(AF_INET, nc->nw_dst, &ip_dst);
+        inet_pton(AF_INET, nc->nw_src, &ip_src);
 
-    compose_arp(&packet, ARP_OP_REQUEST, nc->eth_src, nc->eth_dst,
-             true, nw_src, nw_dst);
+        compose_arp(&packet, ARP_OP_REQUEST, nc->eth_src, nc->eth_dst,
+                 true, ip_src, ip_dst);
+    } else if (nc->type == OVN_NEIGHBOUR_TYPE_ND) {
+        struct in6_addr ip6_dst, ip6_src;
+        inet_pton(AF_INET6, nc->nw_dst, &ip6_dst);
+        inet_pton(AF_INET6, nc->nw_src, &ip6_src);
+
+        compose_nd_ns(&packet, nc->eth_src, &ip6_src, &ip6_dst);
+    } else {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "Unknown ovn neighbour type.");
+        return;
+    }
 
     /* Compose actions. */
     uint64_t ofpacts_stub[4096 / 8];
@@ -975,7 +994,8 @@ pinctrl_handle_put_mac_binding(const struct flow *md,
             inet_ntop(AF_INET, &headers->nw_dst, ip_s, sizeof ip_s);
             ovs_strlcpy(nc->nw_src, ip_s, sizeof nc->nw_src);
         } else {
-            /* ND codes. */
+            inet_ntop(AF_INET6, &headers->ipv6_dst, ip_s, sizeof ip_s);
+            ovs_strlcpy(nc->nw_src, ip_s, sizeof nc->nw_src);
         }
     }
 }
@@ -1433,6 +1453,71 @@ reload_metadata(struct ofpbuf *ofpacts, const struct 
match *md)
 }
 
 static void
+pinctrl_handle_nd_ns(const struct flow *ip_flow, const struct match *md,
+                     struct ofpbuf *userdata)
+{
+    /* This action only works for IPv6 ND packets, and the switch should only
+     * send us ND packets this way, but check here just to be sure. */
+    if (ip_flow->dl_type != htons(ETH_TYPE_IPV6)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "NS action on non-ND packet");
+        return;
+    }
+
+    enum ofp_version version = rconn_get_version(swconn);
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet packet;
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+
+    compose_nd_ns(&packet, ip_flow->dl_src,
+                &ip_flow->ipv6_src, &ip_flow->ipv6_dst);
+
+    /* Reload previous packet metadata. */
+    uint64_t ofpacts_stub[4096 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+    reload_metadata(&ofpacts, md);
+
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+                                                      version, &ofpacts);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "failed to parse actions for 'ns' (%s)",
+                     ofperr_to_string(error));
+        goto exit;
+    }
+
+    struct ofputil_packet_out po = {
+        .packet = dp_packet_data(&packet),
+        .packet_len = dp_packet_size(&packet),
+        .buffer_id = UINT32_MAX,
+        .in_port = OFPP_CONTROLLER,
+        .ofpacts = ofpacts.data,
+        .ofpacts_len = ofpacts.size,
+    };
+
+    queue_msg(ofputil_encode_packet_out(&po, proto));
+
+    /* The logical routers use a neighbour cache to store the neighbor
+     * solicitations. Only when logical routers send a NS packet, and
+     * get a corresponding NA packet, will the 'ovn-controller' update
+     * mac_binding table of SB database. */
+    uint32_t dp_key = ntohll(md->flow.metadata);
+    uint32_t port_key = md->flow.regs[MFF_LOG_OUTPORT - MFF_REG0];
+    char ip_s[INET6_ADDRSTRLEN];
+
+    ovs_be128 ip6 = hton128(flow_get_xxreg(&md->flow, 0));
+    inet_ntop(AF_INET6, &ip6, ip_s, sizeof ip_s);
+
+    pinctrl_handle_neighbour_cache(dp_key, port_key, ip_s, false);
+
+exit:
+    dp_packet_uninit(&packet);
+    ofpbuf_uninit(&ofpacts);
+}
+
+static void
 pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md,
                      struct ofpbuf *userdata)
 {
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 3908e1d..d0c94e7 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1082,6 +1082,12 @@ parse_ND_NA(struct action_context *ctx)
 }
 
 static void
+parse_ND_NS(struct action_context *ctx)
+{
+    parse_nested_action(ctx, OVNACT_ND_NS, "ip6");
+}
+
+static void
 format_nested_action(const struct ovnact_nest *on, const char *name,
                      struct ds *s)
 {
@@ -1103,6 +1109,12 @@ format_ND_NA(const struct ovnact_nest *nest, struct ds 
*s)
 }
 
 static void
+format_ND_NS(const struct ovnact_nest *nest, struct ds *s)
+{
+    format_nested_action(nest, "nd_ns", s);
+}
+
+static void
 encode_nested_actions(const struct ovnact_nest *on,
                       const struct ovnact_encode_params *ep,
                       enum action_opcode opcode,
@@ -1143,6 +1155,14 @@ encode_ND_NA(const struct ovnact_nest *on,
 }
 
 static void
+encode_ND_NS(const struct ovnact_nest *on,
+             const struct ovnact_encode_params *ep,
+             struct ofpbuf *ofpacts)
+{
+    encode_nested_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts);
+}
+
+static void
 free_nested_actions(struct ovnact_nest *on)
 {
     ovnacts_free(on->nested, on->nested_len);
@@ -1160,7 +1180,12 @@ free_ND_NA(struct ovnact_nest *nest)
 {
     free_nested_actions(nest);
 }
-
+
+static void
+free_ND_NS(struct ovnact_nest *nest)
+{
+    free_nested_actions(nest);
+}
 static void
 parse_get_mac_bind(struct action_context *ctx, int width,
                    struct ovnact_get_mac_bind *get_mac)
@@ -1685,6 +1710,8 @@ parse_action(struct action_context *ctx)
         parse_ARP(ctx);
     } else if (lexer_match_id(ctx->lexer, "nd_na")) {
         parse_ND_NA(ctx);
+    } else if (lexer_match_id(ctx->lexer, "nd_ns")) {
+        parse_ND_NS(ctx);
     } else if (lexer_match_id(ctx->lexer, "get_arp")) {
         parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));
     } else if (lexer_match_id(ctx->lexer, "put_arp")) {
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index d139617..1015a1b 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -3180,17 +3180,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
                       ds_cstr(&match), "drop;");
 
-        /* ND advertisement handling.  Use advertisements to populate
-         * the logical router's ARP/ND table. */
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na",
-                      "put_nd(inport, nd.target, nd.tll);");
-
-        /* Lean from neighbor solicitations that were not directed at
-         * us.  (A priority-90 flow will respond to requests to us and
-         * learn the sender's mac address. */
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns",
-                      "put_nd(inport, ip6.src, nd.sll);");
-
         /* Pass other traffic not already handled to the next table for
          * routing. */
         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
@@ -3405,21 +3394,37 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
                           ds_cstr(&match), "drop;");
         }
 
-        /* ND reply.  These flows reply to ND solicitations for the
-         * router's own IP address. */
         for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
+            /* ND reply handling.  Use Use advertisements to populate the 
logical
+             * router's ND table. */
+            ds_clear(&match);
+            ds_put_format(&match,
+                    "inport == %s && nd_na && ip6.dst == {%s, %s} "
+                    "&& ip6.src == %s/%d",
+                    op->json_key,
+                    op->lrp_networks.ipv6_addrs[i].addr_s,
+                    op->lrp_networks.ipv6_addrs[i].sn_addr_s,
+                    op->lrp_networks.ipv6_addrs[i].network_s,
+                    op->lrp_networks.ipv6_addrs[i].plen);
+
+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                          ds_cstr(&match), "put_nd(inport, nd.target, 
nd.tll);");
+
+             /* ND reply.  These flows reply to ND solicitations for the
+             * router's own IP address. */
             ds_clear(&match);
             ds_put_format(&match,
                     "inport == %s && nd_ns && ip6.dst == {%s, %s} "
+                    "&& ip6.src != %s "
                     "&& nd.target == %s",
                     op->json_key,
                     op->lrp_networks.ipv6_addrs[i].addr_s,
                     op->lrp_networks.ipv6_addrs[i].sn_addr_s,
+                    op->lrp_networks.ipv6_addrs[i].addr_s,
                     op->lrp_networks.ipv6_addrs[i].addr_s);
 
             ds_clear(&actions);
             ds_put_format(&actions,
-                          "put_nd(inport, ip6.src, nd.sll); "
                           "nd_na { "
                           "eth.src = %s; "
                           "ip6.src = %s; "
@@ -3815,7 +3820,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
         }
 
         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
-                      "eth.dst == 00:00:00:00:00:00",
+                      "ip && eth.dst == 00:00:00:00:00:00",
                       "arp { "
                       "eth.dst = ff:ff:ff:ff:ff:ff; "
                       "arp.spa = reg1; "
@@ -3823,6 +3828,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
                       "arp.op = 1; " /* ARP request */
                       "output; "
                       "};");
+
+        ds_clear(&actions);
+        ds_put_format(&actions,
+                      "nd_ns { "
+                      "nd.sll = eth.src; "
+                      "nd.target = xxreg0; "
+                      "ip6.src = xxreg1; "
+                      "output; "
+                      "};");
+
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
+                      "ip6 && eth.dst == 00:00:00:00:00:00",
+                      ds_cstr(&actions));
+
         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;");
     }
 
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index df2ff21..8d78d78 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1138,6 +1138,26 @@ execute_nd_na(const struct ovnact_nest *on, const struct 
ovntrace_datapath *dp,
 }
 
 static void
+execute_nd_ns(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
+              const struct flow *uflow, uint8_t table_id,
+              enum ovntrace_pipeline pipeline, struct ovs_list *super)
+{
+    struct flow ns_flow = *uflow;
+
+    /* Update fields for NS. */
+    ns_flow.dl_dst = uflow->arp_sha;
+    ns_flow.ipv6_dst = uflow->ipv6_src;
+    ns_flow.ipv6_src = uflow->nd_target;
+    ns_flow.tp_src = htons(135);
+
+    struct ovntrace_node *node = ovntrace_node_append(
+        super, OVNTRACE_NODE_TRANSFORMATION, "nd_ns");
+
+    trace_actions(on->nested, on->nested_len, dp, &ns_flow,
+                  table_id, pipeline, &node->subs);
+}
+
+static void
 execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
                      const struct ovntrace_datapath *dp,
                      struct flow *uflow, struct ovs_list *super)
@@ -1255,6 +1275,11 @@ trace_actions(const struct ovnact *ovnacts, size_t 
ovnacts_len,
                           super);
             break;
 
+        case OVNACT_ND_NS:
+            execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id, pipeline,
+                          super);
+            break;
+
         case OVNACT_GET_ARP:
             execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super);
             break;
-- 
1.8.3.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to