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