|
|
5f9769 |
From f21c1b7a467a691847b5552d4570af706fcc5bb0 Mon Sep 17 00:00:00 2001
|
|
|
5f9769 |
Message-Id: <f21c1b7a467a691847b5552d4570af706fcc5bb0.1610458802.git.lorenzo.bianconi@redhat.com>
|
|
|
5f9769 |
From: Anton Ivanov <anton.ivanov@cambridgegreys.com>
|
|
|
5f9769 |
Date: Tue, 5 Jan 2021 17:49:28 +0000
|
|
|
5f9769 |
Subject: [PATCH 01/16] ovn-northd: Move lswitch ARP/ND Responder to functions.
|
|
|
5f9769 |
|
|
|
5f9769 |
Move arp/nd responder lflow processing to per-iterable functions.
|
|
|
5f9769 |
|
|
|
5f9769 |
Signed-off-by: Anton Ivanov <anton.ivanov@cambridgegreys.com>
|
|
|
5f9769 |
Signed-off-by: Numan Siddique <numans@ovn.org>
|
|
|
5f9769 |
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
|
|
|
5f9769 |
---
|
|
|
5f9769 |
northd/ovn-northd.c | 496 +++++++++++++++++++++++---------------------
|
|
|
5f9769 |
1 file changed, 260 insertions(+), 236 deletions(-)
|
|
|
5f9769 |
|
|
|
5f9769 |
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
|
|
|
5f9769 |
index b377dffa1..d17cc55ac 100644
|
|
|
5f9769 |
--- a/northd/ovn-northd.c
|
|
|
5f9769 |
+++ b/northd/ovn-northd.c
|
|
|
5f9769 |
@@ -6770,7 +6770,7 @@ is_vlan_transparent(const struct ovn_datapath *od)
|
|
|
5f9769 |
static void
|
|
|
5f9769 |
build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
5f9769 |
struct hmap *lflows, struct hmap *mcgroups,
|
|
|
5f9769 |
- struct hmap *igmp_groups, struct hmap *lbs)
|
|
|
5f9769 |
+ struct hmap *igmp_groups)
|
|
|
5f9769 |
{
|
|
|
5f9769 |
/* This flow table structure is documented in ovn-northd(8), so please
|
|
|
5f9769 |
* update ovn-northd.8.xml if you change anything. */
|
|
|
5f9769 |
@@ -6778,240 +6778,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
5f9769 |
struct ds match = DS_EMPTY_INITIALIZER;
|
|
|
5f9769 |
struct ds actions = DS_EMPTY_INITIALIZER;
|
|
|
5f9769 |
struct ovn_datapath *od;
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Ingress table 13: ARP/ND responder, skip requests coming from localnet
|
|
|
5f9769 |
- * and vtep ports. (priority 100); see ovn-northd.8.xml for the
|
|
|
5f9769 |
- * rationale. */
|
|
|
5f9769 |
struct ovn_port *op;
|
|
|
5f9769 |
- HMAP_FOR_EACH (op, key_node, ports) {
|
|
|
5f9769 |
- if (!op->nbsp) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- if ((!strcmp(op->nbsp->type, "localnet")) ||
|
|
|
5f9769 |
- (!strcmp(op->nbsp->type, "vtep"))) {
|
|
|
5f9769 |
- ds_clear(&match);
|
|
|
5f9769 |
- ds_put_format(&match, "inport == %s", op->json_key);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
|
|
|
5f9769 |
- 100, ds_cstr(&match), "next;",
|
|
|
5f9769 |
- &op->nbsp->header_);
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Ingress table 13: ARP/ND responder, reply for known IPs.
|
|
|
5f9769 |
- * (priority 50). */
|
|
|
5f9769 |
- HMAP_FOR_EACH (op, key_node, ports) {
|
|
|
5f9769 |
- if (!op->nbsp) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- if (!strcmp(op->nbsp->type, "virtual")) {
|
|
|
5f9769 |
- /* Handle
|
|
|
5f9769 |
- * - GARPs for virtual ip which belongs to a logical port
|
|
|
5f9769 |
- * of type 'virtual' and bind that port.
|
|
|
5f9769 |
- *
|
|
|
5f9769 |
- * - ARP reply from the virtual ip which belongs to a logical
|
|
|
5f9769 |
- * port of type 'virtual' and bind that port.
|
|
|
5f9769 |
- * */
|
|
|
5f9769 |
- ovs_be32 ip;
|
|
|
5f9769 |
- const char *virtual_ip = smap_get(&op->nbsp->options,
|
|
|
5f9769 |
- "virtual-ip");
|
|
|
5f9769 |
- const char *virtual_parents = smap_get(&op->nbsp->options,
|
|
|
5f9769 |
- "virtual-parents");
|
|
|
5f9769 |
- if (!virtual_ip || !virtual_parents ||
|
|
|
5f9769 |
- !ip_parse(virtual_ip, &ip)) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- char *tokstr = xstrdup(virtual_parents);
|
|
|
5f9769 |
- char *save_ptr = NULL;
|
|
|
5f9769 |
- char *vparent;
|
|
|
5f9769 |
- for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL;
|
|
|
5f9769 |
- vparent = strtok_r(NULL, ",", &save_ptr)) {
|
|
|
5f9769 |
- struct ovn_port *vp = ovn_port_find(ports, vparent);
|
|
|
5f9769 |
- if (!vp || vp->od != op->od) {
|
|
|
5f9769 |
- /* vparent name should be valid and it should belong
|
|
|
5f9769 |
- * to the same logical switch. */
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- ds_clear(&match);
|
|
|
5f9769 |
- ds_put_format(&match, "inport == \"%s\" && "
|
|
|
5f9769 |
- "((arp.op == 1 && arp.spa == %s && "
|
|
|
5f9769 |
- "arp.tpa == %s) || (arp.op == 2 && "
|
|
|
5f9769 |
- "arp.spa == %s))",
|
|
|
5f9769 |
- vparent, virtual_ip, virtual_ip,
|
|
|
5f9769 |
- virtual_ip);
|
|
|
5f9769 |
- ds_clear(&actions);
|
|
|
5f9769 |
- ds_put_format(&actions,
|
|
|
5f9769 |
- "bind_vport(%s, inport); "
|
|
|
5f9769 |
- "next;",
|
|
|
5f9769 |
- op->json_key);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
- ds_cstr(&match), ds_cstr(&actions),
|
|
|
5f9769 |
- &vp->nbsp->header_);
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- free(tokstr);
|
|
|
5f9769 |
- } else {
|
|
|
5f9769 |
- /*
|
|
|
5f9769 |
- * Add ARP/ND reply flows if either the
|
|
|
5f9769 |
- * - port is up and it doesn't have 'unknown' address defined or
|
|
|
5f9769 |
- * - port type is router or
|
|
|
5f9769 |
- * - port type is localport
|
|
|
5f9769 |
- */
|
|
|
5f9769 |
- if (check_lsp_is_up &&
|
|
|
5f9769 |
- !lsp_is_up(op->nbsp) && !lsp_is_router(op->nbsp) &&
|
|
|
5f9769 |
- strcmp(op->nbsp->type, "localport")) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- if (lsp_is_external(op->nbsp) || op->has_unknown) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- for (size_t i = 0; i < op->n_lsp_addrs; i++) {
|
|
|
5f9769 |
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
|
|
|
5f9769 |
- ds_clear(&match);
|
|
|
5f9769 |
- ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv4_addrs[j].addr_s);
|
|
|
5f9769 |
- ds_clear(&actions);
|
|
|
5f9769 |
- ds_put_format(&actions,
|
|
|
5f9769 |
- "eth.dst = eth.src; "
|
|
|
5f9769 |
- "eth.src = %s; "
|
|
|
5f9769 |
- "arp.op = 2; /* ARP reply */ "
|
|
|
5f9769 |
- "arp.tha = arp.sha; "
|
|
|
5f9769 |
- "arp.sha = %s; "
|
|
|
5f9769 |
- "arp.tpa = arp.spa; "
|
|
|
5f9769 |
- "arp.spa = %s; "
|
|
|
5f9769 |
- "outport = inport; "
|
|
|
5f9769 |
- "flags.loopback = 1; "
|
|
|
5f9769 |
- "output;",
|
|
|
5f9769 |
- op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv4_addrs[j].addr_s);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 50,
|
|
|
5f9769 |
- ds_cstr(&match),
|
|
|
5f9769 |
- ds_cstr(&actions),
|
|
|
5f9769 |
- &op->nbsp->header_);
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Do not reply to an ARP request from the port that owns
|
|
|
5f9769 |
- * the address (otherwise a DHCP client that ARPs to check
|
|
|
5f9769 |
- * for a duplicate address will fail). Instead, forward
|
|
|
5f9769 |
- * it the usual way.
|
|
|
5f9769 |
- *
|
|
|
5f9769 |
- * (Another alternative would be to simply drop the packet.
|
|
|
5f9769 |
- * If everything is working as it is configured, then this
|
|
|
5f9769 |
- * would produce equivalent results, since no one should
|
|
|
5f9769 |
- * reply to the request. But ARPing for one's own IP
|
|
|
5f9769 |
- * address is intended to detect situations where the
|
|
|
5f9769 |
- * network is not working as configured, so dropping the
|
|
|
5f9769 |
- * request would frustrate that intent.) */
|
|
|
5f9769 |
- ds_put_format(&match, " && inport == %s", op->json_key);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
- ds_cstr(&match), "next;",
|
|
|
5f9769 |
- &op->nbsp->header_);
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* For ND solicitations, we need to listen for both the
|
|
|
5f9769 |
- * unicast IPv6 address and its all-nodes multicast address,
|
|
|
5f9769 |
- * but always respond with the unicast IPv6 address. */
|
|
|
5f9769 |
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
|
|
|
5f9769 |
- ds_clear(&match);
|
|
|
5f9769 |
- ds_put_format(&match,
|
|
|
5f9769 |
- "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv6_addrs[j].addr_s);
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- ds_clear(&actions);
|
|
|
5f9769 |
- ds_put_format(&actions,
|
|
|
5f9769 |
- "%s { "
|
|
|
5f9769 |
- "eth.src = %s; "
|
|
|
5f9769 |
- "ip6.src = %s; "
|
|
|
5f9769 |
- "nd.target = %s; "
|
|
|
5f9769 |
- "nd.tll = %s; "
|
|
|
5f9769 |
- "outport = inport; "
|
|
|
5f9769 |
- "flags.loopback = 1; "
|
|
|
5f9769 |
- "output; "
|
|
|
5f9769 |
- "};",
|
|
|
5f9769 |
- lsp_is_router(op->nbsp) ? "nd_na_router" : "nd_na",
|
|
|
5f9769 |
- op->lsp_addrs[i].ea_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
- op->lsp_addrs[i].ea_s);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 50,
|
|
|
5f9769 |
- ds_cstr(&match),
|
|
|
5f9769 |
- ds_cstr(&actions),
|
|
|
5f9769 |
- &op->nbsp->header_);
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Do not reply to a solicitation from the port that owns
|
|
|
5f9769 |
- * the address (otherwise DAD detection will fail). */
|
|
|
5f9769 |
- ds_put_format(&match, " && inport == %s", op->json_key);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
- ds_cstr(&match), "next;",
|
|
|
5f9769 |
- &op->nbsp->header_);
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Ingress table 13: ARP/ND responder, by default goto next.
|
|
|
5f9769 |
- * (priority 0)*/
|
|
|
5f9769 |
- HMAP_FOR_EACH (od, key_node, datapaths) {
|
|
|
5f9769 |
- if (!od->nbs) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- /* Ingress table 13: ARP/ND responder for service monitor source ip.
|
|
|
5f9769 |
- * (priority 110)*/
|
|
|
5f9769 |
- struct ovn_northd_lb *lb;
|
|
|
5f9769 |
- HMAP_FOR_EACH (lb, hmap_node, lbs) {
|
|
|
5f9769 |
- for (size_t i = 0; i < lb->n_vips; i++) {
|
|
|
5f9769 |
- struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i];
|
|
|
5f9769 |
- if (!lb_vip_nb->lb_health_check) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- for (size_t j = 0; j < lb_vip_nb->n_backends; j++) {
|
|
|
5f9769 |
- struct ovn_northd_lb_backend *backend_nb =
|
|
|
5f9769 |
- &lb_vip_nb->backends_nb[j];
|
|
|
5f9769 |
- if (!backend_nb->op || !backend_nb->svc_mon_src_ip) {
|
|
|
5f9769 |
- continue;
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
-
|
|
|
5f9769 |
- ds_clear(&match);
|
|
|
5f9769 |
- ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
|
|
|
5f9769 |
- backend_nb->svc_mon_src_ip);
|
|
|
5f9769 |
- ds_clear(&actions);
|
|
|
5f9769 |
- ds_put_format(&actions,
|
|
|
5f9769 |
- "eth.dst = eth.src; "
|
|
|
5f9769 |
- "eth.src = %s; "
|
|
|
5f9769 |
- "arp.op = 2; /* ARP reply */ "
|
|
|
5f9769 |
- "arp.tha = arp.sha; "
|
|
|
5f9769 |
- "arp.sha = %s; "
|
|
|
5f9769 |
- "arp.tpa = arp.spa; "
|
|
|
5f9769 |
- "arp.spa = %s; "
|
|
|
5f9769 |
- "outport = inport; "
|
|
|
5f9769 |
- "flags.loopback = 1; "
|
|
|
5f9769 |
- "output;",
|
|
|
5f9769 |
- svc_monitor_mac, svc_monitor_mac,
|
|
|
5f9769 |
- backend_nb->svc_mon_src_ip);
|
|
|
5f9769 |
- ovn_lflow_add_with_hint(lflows,
|
|
|
5f9769 |
- backend_nb->op->od,
|
|
|
5f9769 |
- S_SWITCH_IN_ARP_ND_RSP, 110,
|
|
|
5f9769 |
- ds_cstr(&match), ds_cstr(&actions),
|
|
|
5f9769 |
- &lb->nlb->header_);
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
- }
|
|
|
5f9769 |
|
|
|
5f9769 |
|
|
|
5f9769 |
/* Logical switch ingress table 14 and 15: DHCP options and response
|
|
|
5f9769 |
@@ -7471,6 +7238,251 @@ build_lswitch_lflows_admission_control(struct ovn_datapath *od,
|
|
|
5f9769 |
}
|
|
|
5f9769 |
}
|
|
|
5f9769 |
|
|
|
5f9769 |
+/* Ingress table 13: ARP/ND responder, skip requests coming from localnet
|
|
|
5f9769 |
+ * and vtep ports. (priority 100); see ovn-northd.8.xml for the
|
|
|
5f9769 |
+ * rationale. */
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+static void
|
|
|
5f9769 |
+build_lswitch_arp_nd_responder_skip_local(struct ovn_port *op,
|
|
|
5f9769 |
+ struct hmap *lflows,
|
|
|
5f9769 |
+ struct ds *match)
|
|
|
5f9769 |
+{
|
|
|
5f9769 |
+ if (op->nbsp) {
|
|
|
5f9769 |
+ if ((!strcmp(op->nbsp->type, "localnet")) ||
|
|
|
5f9769 |
+ (!strcmp(op->nbsp->type, "vtep"))) {
|
|
|
5f9769 |
+ ds_clear(match);
|
|
|
5f9769 |
+ ds_put_format(match, "inport == %s", op->json_key);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
|
|
|
5f9769 |
+ 100, ds_cstr(match), "next;",
|
|
|
5f9769 |
+ &op->nbsp->header_);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+}
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+/* Ingress table 13: ARP/ND responder, reply for known IPs.
|
|
|
5f9769 |
+ * (priority 50). */
|
|
|
5f9769 |
+static void
|
|
|
5f9769 |
+build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op,
|
|
|
5f9769 |
+ struct hmap *lflows,
|
|
|
5f9769 |
+ struct hmap *ports,
|
|
|
5f9769 |
+ struct ds *actions,
|
|
|
5f9769 |
+ struct ds *match)
|
|
|
5f9769 |
+{
|
|
|
5f9769 |
+ if (op->nbsp) {
|
|
|
5f9769 |
+ if (!strcmp(op->nbsp->type, "virtual")) {
|
|
|
5f9769 |
+ /* Handle
|
|
|
5f9769 |
+ * - GARPs for virtual ip which belongs to a logical port
|
|
|
5f9769 |
+ * of type 'virtual' and bind that port.
|
|
|
5f9769 |
+ *
|
|
|
5f9769 |
+ * - ARP reply from the virtual ip which belongs to a logical
|
|
|
5f9769 |
+ * port of type 'virtual' and bind that port.
|
|
|
5f9769 |
+ * */
|
|
|
5f9769 |
+ ovs_be32 ip;
|
|
|
5f9769 |
+ const char *virtual_ip = smap_get(&op->nbsp->options,
|
|
|
5f9769 |
+ "virtual-ip");
|
|
|
5f9769 |
+ const char *virtual_parents = smap_get(&op->nbsp->options,
|
|
|
5f9769 |
+ "virtual-parents");
|
|
|
5f9769 |
+ if (!virtual_ip || !virtual_parents ||
|
|
|
5f9769 |
+ !ip_parse(virtual_ip, &ip)) {
|
|
|
5f9769 |
+ return;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ char *tokstr = xstrdup(virtual_parents);
|
|
|
5f9769 |
+ char *save_ptr = NULL;
|
|
|
5f9769 |
+ char *vparent;
|
|
|
5f9769 |
+ for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL;
|
|
|
5f9769 |
+ vparent = strtok_r(NULL, ",", &save_ptr)) {
|
|
|
5f9769 |
+ struct ovn_port *vp = ovn_port_find(ports, vparent);
|
|
|
5f9769 |
+ if (!vp || vp->od != op->od) {
|
|
|
5f9769 |
+ /* vparent name should be valid and it should belong
|
|
|
5f9769 |
+ * to the same logical switch. */
|
|
|
5f9769 |
+ continue;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ ds_clear(match);
|
|
|
5f9769 |
+ ds_put_format(match, "inport == \"%s\" && "
|
|
|
5f9769 |
+ "((arp.op == 1 && arp.spa == %s && "
|
|
|
5f9769 |
+ "arp.tpa == %s) || (arp.op == 2 && "
|
|
|
5f9769 |
+ "arp.spa == %s))",
|
|
|
5f9769 |
+ vparent, virtual_ip, virtual_ip,
|
|
|
5f9769 |
+ virtual_ip);
|
|
|
5f9769 |
+ ds_clear(actions);
|
|
|
5f9769 |
+ ds_put_format(actions,
|
|
|
5f9769 |
+ "bind_vport(%s, inport); "
|
|
|
5f9769 |
+ "next;",
|
|
|
5f9769 |
+ op->json_key);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
+ ds_cstr(match), ds_cstr(actions),
|
|
|
5f9769 |
+ &vp->nbsp->header_);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ free(tokstr);
|
|
|
5f9769 |
+ } else {
|
|
|
5f9769 |
+ /*
|
|
|
5f9769 |
+ * Add ARP/ND reply flows if either the
|
|
|
5f9769 |
+ * - port is up and it doesn't have 'unknown' address defined or
|
|
|
5f9769 |
+ * - port type is router or
|
|
|
5f9769 |
+ * - port type is localport
|
|
|
5f9769 |
+ */
|
|
|
5f9769 |
+ if (check_lsp_is_up &&
|
|
|
5f9769 |
+ !lsp_is_up(op->nbsp) && !lsp_is_router(op->nbsp) &&
|
|
|
5f9769 |
+ strcmp(op->nbsp->type, "localport")) {
|
|
|
5f9769 |
+ return;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ if (lsp_is_external(op->nbsp) || op->has_unknown) {
|
|
|
5f9769 |
+ return;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ for (size_t i = 0; i < op->n_lsp_addrs; i++) {
|
|
|
5f9769 |
+ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
|
|
|
5f9769 |
+ ds_clear(match);
|
|
|
5f9769 |
+ ds_put_format(match, "arp.tpa == %s && arp.op == 1",
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv4_addrs[j].addr_s);
|
|
|
5f9769 |
+ ds_clear(actions);
|
|
|
5f9769 |
+ ds_put_format(actions,
|
|
|
5f9769 |
+ "eth.dst = eth.src; "
|
|
|
5f9769 |
+ "eth.src = %s; "
|
|
|
5f9769 |
+ "arp.op = 2; /* ARP reply */ "
|
|
|
5f9769 |
+ "arp.tha = arp.sha; "
|
|
|
5f9769 |
+ "arp.sha = %s; "
|
|
|
5f9769 |
+ "arp.tpa = arp.spa; "
|
|
|
5f9769 |
+ "arp.spa = %s; "
|
|
|
5f9769 |
+ "outport = inport; "
|
|
|
5f9769 |
+ "flags.loopback = 1; "
|
|
|
5f9769 |
+ "output;",
|
|
|
5f9769 |
+ op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv4_addrs[j].addr_s);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 50,
|
|
|
5f9769 |
+ ds_cstr(match),
|
|
|
5f9769 |
+ ds_cstr(actions),
|
|
|
5f9769 |
+ &op->nbsp->header_);
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ /* Do not reply to an ARP request from the port that owns
|
|
|
5f9769 |
+ * the address (otherwise a DHCP client that ARPs to check
|
|
|
5f9769 |
+ * for a duplicate address will fail). Instead, forward
|
|
|
5f9769 |
+ * it the usual way.
|
|
|
5f9769 |
+ *
|
|
|
5f9769 |
+ * (Another alternative would be to simply drop the packet.
|
|
|
5f9769 |
+ * If everything is working as it is configured, then this
|
|
|
5f9769 |
+ * would produce equivalent results, since no one should
|
|
|
5f9769 |
+ * reply to the request. But ARPing for one's own IP
|
|
|
5f9769 |
+ * address is intended to detect situations where the
|
|
|
5f9769 |
+ * network is not working as configured, so dropping the
|
|
|
5f9769 |
+ * request would frustrate that intent.) */
|
|
|
5f9769 |
+ ds_put_format(match, " && inport == %s", op->json_key);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
+ ds_cstr(match), "next;",
|
|
|
5f9769 |
+ &op->nbsp->header_);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ /* For ND solicitations, we need to listen for both the
|
|
|
5f9769 |
+ * unicast IPv6 address and its all-nodes multicast address,
|
|
|
5f9769 |
+ * but always respond with the unicast IPv6 address. */
|
|
|
5f9769 |
+ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
|
|
|
5f9769 |
+ ds_clear(match);
|
|
|
5f9769 |
+ ds_put_format(match,
|
|
|
5f9769 |
+ "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv6_addrs[j].addr_s);
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ ds_clear(actions);
|
|
|
5f9769 |
+ ds_put_format(actions,
|
|
|
5f9769 |
+ "%s { "
|
|
|
5f9769 |
+ "eth.src = %s; "
|
|
|
5f9769 |
+ "ip6.src = %s; "
|
|
|
5f9769 |
+ "nd.target = %s; "
|
|
|
5f9769 |
+ "nd.tll = %s; "
|
|
|
5f9769 |
+ "outport = inport; "
|
|
|
5f9769 |
+ "flags.loopback = 1; "
|
|
|
5f9769 |
+ "output; "
|
|
|
5f9769 |
+ "};",
|
|
|
5f9769 |
+ lsp_is_router(op->nbsp) ? "nd_na_router" : "nd_na",
|
|
|
5f9769 |
+ op->lsp_addrs[i].ea_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ipv6_addrs[j].addr_s,
|
|
|
5f9769 |
+ op->lsp_addrs[i].ea_s);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 50,
|
|
|
5f9769 |
+ ds_cstr(match),
|
|
|
5f9769 |
+ ds_cstr(actions),
|
|
|
5f9769 |
+ &op->nbsp->header_);
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ /* Do not reply to a solicitation from the port that owns
|
|
|
5f9769 |
+ * the address (otherwise DAD detection will fail). */
|
|
|
5f9769 |
+ ds_put_format(match, " && inport == %s", op->json_key);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows, op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 100,
|
|
|
5f9769 |
+ ds_cstr(match), "next;",
|
|
|
5f9769 |
+ &op->nbsp->header_);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+}
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+/* Ingress table 13: ARP/ND responder, by default goto next.
|
|
|
5f9769 |
+ * (priority 0)*/
|
|
|
5f9769 |
+static void
|
|
|
5f9769 |
+build_lswitch_arp_nd_responder_default(struct ovn_datapath *od,
|
|
|
5f9769 |
+ struct hmap *lflows)
|
|
|
5f9769 |
+{
|
|
|
5f9769 |
+ if (od->nbs) {
|
|
|
5f9769 |
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+}
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+/* Ingress table 13: ARP/ND responder for service monitor source ip.
|
|
|
5f9769 |
+ * (priority 110)*/
|
|
|
5f9769 |
+static void
|
|
|
5f9769 |
+build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb,
|
|
|
5f9769 |
+ struct hmap *lflows,
|
|
|
5f9769 |
+ struct ds *actions,
|
|
|
5f9769 |
+ struct ds *match)
|
|
|
5f9769 |
+{
|
|
|
5f9769 |
+ for (size_t i = 0; i < lb->n_vips; i++) {
|
|
|
5f9769 |
+ struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i];
|
|
|
5f9769 |
+ if (!lb_vip_nb->lb_health_check) {
|
|
|
5f9769 |
+ continue;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ for (size_t j = 0; j < lb_vip_nb->n_backends; j++) {
|
|
|
5f9769 |
+ struct ovn_northd_lb_backend *backend_nb =
|
|
|
5f9769 |
+ &lb_vip_nb->backends_nb[j];
|
|
|
5f9769 |
+ if (!backend_nb->op || !backend_nb->svc_mon_src_ip) {
|
|
|
5f9769 |
+ continue;
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+
|
|
|
5f9769 |
+ ds_clear(match);
|
|
|
5f9769 |
+ ds_put_format(match, "arp.tpa == %s && arp.op == 1",
|
|
|
5f9769 |
+ backend_nb->svc_mon_src_ip);
|
|
|
5f9769 |
+ ds_clear(actions);
|
|
|
5f9769 |
+ ds_put_format(actions,
|
|
|
5f9769 |
+ "eth.dst = eth.src; "
|
|
|
5f9769 |
+ "eth.src = %s; "
|
|
|
5f9769 |
+ "arp.op = 2; /* ARP reply */ "
|
|
|
5f9769 |
+ "arp.tha = arp.sha; "
|
|
|
5f9769 |
+ "arp.sha = %s; "
|
|
|
5f9769 |
+ "arp.tpa = arp.spa; "
|
|
|
5f9769 |
+ "arp.spa = %s; "
|
|
|
5f9769 |
+ "outport = inport; "
|
|
|
5f9769 |
+ "flags.loopback = 1; "
|
|
|
5f9769 |
+ "output;",
|
|
|
5f9769 |
+ svc_monitor_mac, svc_monitor_mac,
|
|
|
5f9769 |
+ backend_nb->svc_mon_src_ip);
|
|
|
5f9769 |
+ ovn_lflow_add_with_hint(lflows,
|
|
|
5f9769 |
+ backend_nb->op->od,
|
|
|
5f9769 |
+ S_SWITCH_IN_ARP_ND_RSP, 110,
|
|
|
5f9769 |
+ ds_cstr(match), ds_cstr(actions),
|
|
|
5f9769 |
+ &lb->nlb->header_);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
+}
|
|
|
5f9769 |
+
|
|
|
5f9769 |
|
|
|
5f9769 |
/* Returns a string of the IP address of the router port 'op' that
|
|
|
5f9769 |
* overlaps with 'ip_s". If one is not found, returns NULL.
|
|
|
5f9769 |
@@ -11322,6 +11334,7 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od,
|
|
|
5f9769 |
build_fwd_group_lflows(od, lsi->lflows);
|
|
|
5f9769 |
build_lswitch_lflows_admission_control(od, lsi->lflows);
|
|
|
5f9769 |
build_lswitch_input_port_sec_od(od, lsi->lflows);
|
|
|
5f9769 |
+ build_lswitch_arp_nd_responder_default(od, lsi->lflows);
|
|
|
5f9769 |
|
|
|
5f9769 |
/* Build Logical Router Flows. */
|
|
|
5f9769 |
build_adm_ctrl_flows_for_lrouter(od, lsi->lflows);
|
|
|
5f9769 |
@@ -11352,7 +11365,12 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
|
|
|
5f9769 |
/* Build Logical Switch Flows. */
|
|
|
5f9769 |
build_lswitch_input_port_sec_op(op, lsi->lflows, &lsi->actions,
|
|
|
5f9769 |
&lsi->match);
|
|
|
5f9769 |
-
|
|
|
5f9769 |
+ build_lswitch_arp_nd_responder_skip_local(op, lsi->lflows,
|
|
|
5f9769 |
+ &lsi->match);
|
|
|
5f9769 |
+ build_lswitch_arp_nd_responder_known_ips(op, lsi->lflows,
|
|
|
5f9769 |
+ lsi->ports,
|
|
|
5f9769 |
+ &lsi->actions,
|
|
|
5f9769 |
+ &lsi->match);
|
|
|
5f9769 |
/* Build Logical Router Flows. */
|
|
|
5f9769 |
build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
|
|
|
5f9769 |
&lsi->actions);
|
|
|
5f9769 |
@@ -11379,6 +11397,7 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
5f9769 |
{
|
|
|
5f9769 |
struct ovn_datapath *od;
|
|
|
5f9769 |
struct ovn_port *op;
|
|
|
5f9769 |
+ struct ovn_northd_lb *lb;
|
|
|
5f9769 |
|
|
|
5f9769 |
char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
|
|
|
5f9769 |
|
|
|
5f9769 |
@@ -11405,6 +11424,11 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
5f9769 |
HMAP_FOR_EACH (op, key_node, ports) {
|
|
|
5f9769 |
build_lswitch_and_lrouter_iterate_by_op(op, &lsi);
|
|
|
5f9769 |
}
|
|
|
5f9769 |
+ HMAP_FOR_EACH (lb, hmap_node, lbs) {
|
|
|
5f9769 |
+ build_lswitch_arp_nd_service_monitor(lb, lsi.lflows,
|
|
|
5f9769 |
+ &lsi.actions,
|
|
|
5f9769 |
+ &lsi.match);
|
|
|
5f9769 |
+ }
|
|
|
5f9769 |
free(svc_check_match);
|
|
|
5f9769 |
|
|
|
5f9769 |
ds_destroy(&lsi.match);
|
|
|
5f9769 |
@@ -11412,7 +11436,7 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
5f9769 |
|
|
|
5f9769 |
/* Legacy lswitch build - to be migrated. */
|
|
|
5f9769 |
build_lswitch_flows(datapaths, ports, lflows, mcgroups,
|
|
|
5f9769 |
- igmp_groups, lbs);
|
|
|
5f9769 |
+ igmp_groups);
|
|
|
5f9769 |
|
|
|
5f9769 |
/* Legacy lrouter build - to be migrated. */
|
|
|
5f9769 |
build_lrouter_flows(datapaths, ports, lflows, meter_groups, lbs);
|
|
|
5f9769 |
--
|
|
|
5f9769 |
2.29.2
|
|
|
5f9769 |
|