|
|
bbaaef |
From 036599bdfa4c10d69c1bcae407fb75ea841fa834 Mon Sep 17 00:00:00 2001
|
|
|
bbaaef |
From: Numan Siddique <nusiddiq@redhat.com>
|
|
|
bbaaef |
Date: Thu, 5 Sep 2019 00:15:38 +0530
|
|
|
bbaaef |
Subject: [PATCH 3/4] Learn the mac binding only if required
|
|
|
bbaaef |
|
|
|
bbaaef |
OVN has the actions - put_arp and put_nd to learn the mac bindings from the
|
|
|
bbaaef |
ARP/ND packets. These actions update the Southbound MAC_Binding table.
|
|
|
bbaaef |
These actions translates to controller actions. Whenever pinctrl thread
|
|
|
bbaaef |
receives such packets, it wakes up the main ovn-controller thread.
|
|
|
bbaaef |
If the MAC_Binding table is already upto date, this results
|
|
|
bbaaef |
in unnecessary CPU cyles. There are some security implications as well.
|
|
|
bbaaef |
A rogue VM can flood broadcast ARP request/reply packets and this
|
|
|
bbaaef |
could cause DoS issues. A physical switch may send periodic GARPs
|
|
|
bbaaef |
and these packets hit ovn-controllers.
|
|
|
bbaaef |
|
|
|
bbaaef |
This patch solves these problems by learning the mac bindings only if
|
|
|
bbaaef |
required. There is no need to apply the put_arp/put_nd action if the
|
|
|
bbaaef |
Southbound MAC_Binding row is upto date.
|
|
|
bbaaef |
|
|
|
bbaaef |
New actions - lookup_arp and lookup_nd are added which looks up the
|
|
|
bbaaef |
IP, MAC pair in the mac_binding table and stores the result in a
|
|
|
bbaaef |
register. 1 if lookup is successful, 0 otherwise.
|
|
|
bbaaef |
|
|
|
bbaaef |
ovn-northd adds 2 new stages - LOOKUP_NEIGHBOR and LEARN_NEIGHBOR before
|
|
|
bbaaef |
IP_INPUT in the router ingress pipeline.c. The LOOKUP_NEIGHBOR stage
|
|
|
bbaaef |
adds flows to do the lookup in the mac_binding table and the LEARN_NEIGHBOR
|
|
|
bbaaef |
adds flows to learn the neighbors only if require.
|
|
|
bbaaef |
|
|
|
bbaaef |
The lflow module of ovn-controller adds OF flows in table 67 (OFTABLE_MAC_LOOKUP)
|
|
|
bbaaef |
for each mac_binding entry with the match reg0 = ip && eth.src = mac with
|
|
|
bbaaef |
the action - load:1->reg10[6]
|
|
|
bbaaef |
|
|
|
bbaaef |
Eg:
|
|
|
bbaaef |
table=31, priority=100,arp,reg0=0xaca8006f,reg14=0x3,metadata=0x3,dl_src=00:44:00:00:00:04
|
|
|
bbaaef |
actions=load:1->NXM_NX_REG10[6]
|
|
|
bbaaef |
|
|
|
bbaaef |
This patch should also address the issue reported in 'Reported-at'
|
|
|
bbaaef |
|
|
|
bbaaef |
Reported-at: https://bugzilla.redhat.com/1729846
|
|
|
bbaaef |
Reported-by: Haidong Li <haili@redhat.com>
|
|
|
bbaaef |
CC: Han ZHou <hzhou8@ebay.com>
|
|
|
bbaaef |
CC: Dumitru Ceara <dceara@redhat.com>
|
|
|
bbaaef |
Acked-by: Han ZHou <hzhou8@ebay.com>
|
|
|
bbaaef |
Tested-by: Dumitru Ceara <dceara@redhat.com>
|
|
|
bbaaef |
Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
|
|
|
bbaaef |
---
|
|
|
bbaaef |
include/ovn/actions.h | 13 ++
|
|
|
bbaaef |
include/ovn/logical-fields.h | 4 +
|
|
|
bbaaef |
ovn/controller/lflow.c | 37 ++++-
|
|
|
bbaaef |
ovn/controller/lflow.h | 1 +
|
|
|
bbaaef |
ovn/lib/actions.c | 114 ++++++++++++++
|
|
|
bbaaef |
ovn/northd/ovn-northd.8.xml | 212 ++++++++++++++++---------
|
|
|
bbaaef |
ovn/northd/ovn-northd.c | 205 ++++++++++++++-----------
|
|
|
bbaaef |
ovn/ovn-architecture.7.xml | 18 +++
|
|
|
bbaaef |
ovn/ovn-sb.xml | 57 +++++++
|
|
|
bbaaef |
ovn/utilities/ovn-trace.c | 69 +++++++++
|
|
|
bbaaef |
tests/ovn.at | 290 ++++++++++++++++++++++++++++++++++-
|
|
|
bbaaef |
tests/test-ovn.c | 1 +
|
|
|
bbaaef |
12 files changed, 844 insertions(+), 177 deletions(-)
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
|
|
|
bbaaef |
index 145f27f25..4e2f4d28d 100644
|
|
|
bbaaef |
--- a/include/ovn/actions.h
|
|
|
bbaaef |
+++ b/include/ovn/actions.h
|
|
|
bbaaef |
@@ -73,8 +73,10 @@ struct ovn_extend_table;
|
|
|
bbaaef |
OVNACT(ND_NA_ROUTER, ovnact_nest) \
|
|
|
bbaaef |
OVNACT(GET_ARP, ovnact_get_mac_bind) \
|
|
|
bbaaef |
OVNACT(PUT_ARP, ovnact_put_mac_bind) \
|
|
|
bbaaef |
+ OVNACT(LOOKUP_ARP, ovnact_lookup_mac_bind) \
|
|
|
bbaaef |
OVNACT(GET_ND, ovnact_get_mac_bind) \
|
|
|
bbaaef |
OVNACT(PUT_ND, ovnact_put_mac_bind) \
|
|
|
bbaaef |
+ OVNACT(LOOKUP_ND, ovnact_lookup_mac_bind) \
|
|
|
bbaaef |
OVNACT(PUT_DHCPV4_OPTS, ovnact_put_opts) \
|
|
|
bbaaef |
OVNACT(PUT_DHCPV6_OPTS, ovnact_put_opts) \
|
|
|
bbaaef |
OVNACT(SET_QUEUE, ovnact_set_queue) \
|
|
|
bbaaef |
@@ -266,6 +268,15 @@ struct ovnact_put_mac_bind {
|
|
|
bbaaef |
struct expr_field mac; /* 48-bit Ethernet address. */
|
|
|
bbaaef |
};
|
|
|
bbaaef |
|
|
|
bbaaef |
+/* OVNACT_LOOKUP_ARP, OVNACT_LOOKUP_ND. */
|
|
|
bbaaef |
+struct ovnact_lookup_mac_bind {
|
|
|
bbaaef |
+ struct ovnact ovnact;
|
|
|
bbaaef |
+ struct expr_field dst; /* 1-bit destination field. */
|
|
|
bbaaef |
+ struct expr_field port; /* Logical port name. */
|
|
|
bbaaef |
+ struct expr_field ip; /* 32-bit or 128-bit IP address. */
|
|
|
bbaaef |
+ struct expr_field mac; /* 48-bit Ethernet address. */
|
|
|
bbaaef |
+};
|
|
|
bbaaef |
+
|
|
|
bbaaef |
struct ovnact_gen_option {
|
|
|
bbaaef |
const struct gen_opts_map *option;
|
|
|
bbaaef |
struct expr_constant_set value;
|
|
|
bbaaef |
@@ -628,6 +639,8 @@ struct ovnact_encode_params {
|
|
|
bbaaef |
uint8_t output_ptable; /* OpenFlow table for 'output' to resubmit. */
|
|
|
bbaaef |
uint8_t mac_bind_ptable; /* OpenFlow table for 'get_arp'/'get_nd' to
|
|
|
bbaaef |
resubmit. */
|
|
|
bbaaef |
+ uint8_t mac_lookup_ptable; /* OpenFlow table for
|
|
|
bbaaef |
+ 'lookup_arp'/'lookup_nd' to resubmit. */
|
|
|
bbaaef |
};
|
|
|
bbaaef |
|
|
|
bbaaef |
void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
|
|
|
bbaaef |
diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
|
|
|
bbaaef |
index 9bac8e027..9b7c34fb7 100644
|
|
|
bbaaef |
--- a/include/ovn/logical-fields.h
|
|
|
bbaaef |
+++ b/include/ovn/logical-fields.h
|
|
|
bbaaef |
@@ -56,6 +56,7 @@ enum mff_log_flags_bits {
|
|
|
bbaaef |
MLF_FORCE_SNAT_FOR_LB_BIT = 3,
|
|
|
bbaaef |
MLF_LOCAL_ONLY_BIT = 4,
|
|
|
bbaaef |
MLF_NESTED_CONTAINER_BIT = 5,
|
|
|
bbaaef |
+ MLF_LOOKUP_MAC_BIT = 6,
|
|
|
bbaaef |
};
|
|
|
bbaaef |
|
|
|
bbaaef |
/* MFF_LOG_FLAGS_REG flag assignments */
|
|
|
bbaaef |
@@ -84,6 +85,9 @@ enum mff_log_flags {
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Indicate that a packet was received from a nested container. */
|
|
|
bbaaef |
MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT),
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Indicate that the lookup in the mac binding table was successful. */
|
|
|
bbaaef |
+ MLF_LOOKUP_MAC = (1 << MLF_LOOKUP_MAC_BIT),
|
|
|
bbaaef |
};
|
|
|
bbaaef |
|
|
|
bbaaef |
/* OVN logical fields
|
|
|
bbaaef |
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
|
|
|
bbaaef |
index 1aafafb33..733564fd1 100644
|
|
|
bbaaef |
--- a/ovn/controller/lflow.c
|
|
|
bbaaef |
+++ b/ovn/controller/lflow.c
|
|
|
bbaaef |
@@ -687,6 +687,7 @@ consider_logical_flow(
|
|
|
bbaaef |
.egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
|
|
|
bbaaef |
.output_ptable = output_ptable,
|
|
|
bbaaef |
.mac_bind_ptable = OFTABLE_MAC_BINDING,
|
|
|
bbaaef |
+ .mac_lookup_ptable = OFTABLE_MAC_LOOKUP,
|
|
|
bbaaef |
};
|
|
|
bbaaef |
ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
|
|
|
bbaaef |
ovnacts_free(ovnacts.data, ovnacts.size);
|
|
|
bbaaef |
@@ -777,7 +778,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
|
|
|
bbaaef |
return;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- struct match match = MATCH_CATCHALL_INITIALIZER;
|
|
|
bbaaef |
+ struct match get_arp_match = MATCH_CATCHALL_INITIALIZER;
|
|
|
bbaaef |
+ struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
if (strchr(b->ip, '.')) {
|
|
|
bbaaef |
ovs_be32 ip;
|
|
|
bbaaef |
if (!ip_parse(b->ip, &ip)) {
|
|
|
bbaaef |
@@ -785,7 +788,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
|
|
|
bbaaef |
VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
|
|
|
bbaaef |
return;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
- match_set_reg(&match, 0, ntohl(ip));
|
|
|
bbaaef |
+ match_set_reg(&get_arp_match, 0, ntohl(ip));
|
|
|
bbaaef |
+ match_set_reg(&lookup_arp_match, 0, ntohl(ip));
|
|
|
bbaaef |
+ match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP));
|
|
|
bbaaef |
} else {
|
|
|
bbaaef |
struct in6_addr ip6;
|
|
|
bbaaef |
if (!ipv6_parse(b->ip, &ip6)) {
|
|
|
bbaaef |
@@ -795,17 +800,35 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
|
|
|
bbaaef |
}
|
|
|
bbaaef |
ovs_be128 value;
|
|
|
bbaaef |
memcpy(&value, &ip6, sizeof(value));
|
|
|
bbaaef |
- match_set_xxreg(&match, 0, ntoh128(value));
|
|
|
bbaaef |
+ match_set_xxreg(&get_arp_match, 0, ntoh128(value));
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ match_set_xxreg(&lookup_arp_match, 0, ntoh128(value));
|
|
|
bbaaef |
+ match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6));
|
|
|
bbaaef |
+ match_set_nw_proto(&lookup_arp_match, 58);
|
|
|
bbaaef |
+ match_set_icmp_code(&lookup_arp_match, 0);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
|
|
|
bbaaef |
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
|
|
|
bbaaef |
+ match_set_metadata(&get_arp_match, htonll(pb->datapath->tunnel_key));
|
|
|
bbaaef |
+ match_set_reg(&get_arp_match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ match_set_metadata(&lookup_arp_match, htonll(pb->datapath->tunnel_key));
|
|
|
bbaaef |
+ match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - MFF_REG0,
|
|
|
bbaaef |
+ pb->tunnel_key);
|
|
|
bbaaef |
|
|
|
bbaaef |
uint64_t stub[1024 / 8];
|
|
|
bbaaef |
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
|
|
|
bbaaef |
put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
|
|
|
bbaaef |
- ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts,
|
|
|
bbaaef |
- &b->header_.uuid);
|
|
|
bbaaef |
+ ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &get_arp_match,
|
|
|
bbaaef |
+ &ofpacts, &b->header_.uuid);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ofpbuf_clear(&ofpacts);
|
|
|
bbaaef |
+ uint8_t value = 1;
|
|
|
bbaaef |
+ put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
|
|
|
bbaaef |
+ &ofpacts);
|
|
|
bbaaef |
+ match_set_dl_src(&lookup_arp_match, mac);
|
|
|
bbaaef |
+ ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, 100, 0, &lookup_arp_match,
|
|
|
bbaaef |
+ &ofpacts, &b->header_.uuid);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
ofpbuf_uninit(&ofpacts);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
|
|
|
bbaaef |
index 4e1086eb6..9cb2afb86 100644
|
|
|
bbaaef |
--- a/ovn/controller/lflow.h
|
|
|
bbaaef |
+++ b/ovn/controller/lflow.h
|
|
|
bbaaef |
@@ -65,6 +65,7 @@ struct uuid;
|
|
|
bbaaef |
#define OFTABLE_SAVE_INPORT 64
|
|
|
bbaaef |
#define OFTABLE_LOG_TO_PHY 65
|
|
|
bbaaef |
#define OFTABLE_MAC_BINDING 66
|
|
|
bbaaef |
+#define OFTABLE_MAC_LOOKUP 67
|
|
|
bbaaef |
|
|
|
bbaaef |
/* The number of tables for the ingress and egress pipelines. */
|
|
|
bbaaef |
#define LOG_PIPELINE_LEN 24
|
|
|
bbaaef |
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
|
|
|
bbaaef |
index 0bb76cb27..45f8715cf 100644
|
|
|
bbaaef |
--- a/ovn/lib/actions.c
|
|
|
bbaaef |
+++ b/ovn/lib/actions.c
|
|
|
bbaaef |
@@ -1607,6 +1607,112 @@ ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED)
|
|
|
bbaaef |
{
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
+static void format_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ struct ds *s, const char *name)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ expr_field_format(&lookup_mac->dst, s);
|
|
|
bbaaef |
+ ds_put_format(s, " = %s(", name);
|
|
|
bbaaef |
+ expr_field_format(&lookup_mac->port, s);
|
|
|
bbaaef |
+ ds_put_cstr(s, ", ");
|
|
|
bbaaef |
+ expr_field_format(&lookup_mac->ip, s);
|
|
|
bbaaef |
+ ds_put_cstr(s, ", ");
|
|
|
bbaaef |
+ expr_field_format(&lookup_mac->mac, s);
|
|
|
bbaaef |
+ ds_put_cstr(s, ");");
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+format_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ struct ds *s)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ format_lookup_mac(lookup_mac, s, "lookup_arp");
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+format_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ struct ds *s)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ format_lookup_mac(lookup_mac, s, "lookup_nd");
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+encode_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ enum mf_field_id ip_field,
|
|
|
bbaaef |
+ const struct ovnact_encode_params *ep,
|
|
|
bbaaef |
+ struct ofpbuf *ofpacts)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ const struct arg args[] = {
|
|
|
bbaaef |
+ { expr_resolve_field(&lookup_mac->port), MFF_LOG_INPORT },
|
|
|
bbaaef |
+ { expr_resolve_field(&lookup_mac->ip), ip_field },
|
|
|
bbaaef |
+ { expr_resolve_field(&lookup_mac->mac), MFF_ETH_SRC},
|
|
|
bbaaef |
+ };
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst);
|
|
|
bbaaef |
+ ovs_assert(dst.field);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts);
|
|
|
bbaaef |
+ emit_resubmit(ofpacts, ep->mac_lookup_ptable);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
|
|
|
bbaaef |
+ orm->dst = dst;
|
|
|
bbaaef |
+ orm->src.field = mf_from_id(MFF_LOG_FLAGS);
|
|
|
bbaaef |
+ orm->src.ofs = MLF_LOOKUP_MAC_BIT;
|
|
|
bbaaef |
+ orm->src.n_bits = 1;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+encode_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ const struct ovnact_encode_params *ep,
|
|
|
bbaaef |
+ struct ofpbuf *ofpacts)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ encode_lookup_mac(lookup_mac, MFF_REG0, ep, ofpacts);
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+encode_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac,
|
|
|
bbaaef |
+ const struct ovnact_encode_params *ep,
|
|
|
bbaaef |
+ struct ofpbuf *ofpacts)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ encode_lookup_mac(lookup_mac, MFF_XXREG0, ep, ofpacts);
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+parse_lookup_mac_bind(struct action_context *ctx,
|
|
|
bbaaef |
+ const struct expr_field *dst,
|
|
|
bbaaef |
+ int width,
|
|
|
bbaaef |
+ struct ovnact_lookup_mac_bind *lookup_mac)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ /* Validate that the destination is a 1-bit, modifiable field. */
|
|
|
bbaaef |
+ char *error = expr_type_check(dst, 1, true);
|
|
|
bbaaef |
+ if (error) {
|
|
|
bbaaef |
+ lexer_error(ctx->lexer, "%s", error);
|
|
|
bbaaef |
+ free(error);
|
|
|
bbaaef |
+ return;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */
|
|
|
bbaaef |
+ lexer_get(ctx->lexer); /* Skip '('. * */
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ action_parse_field(ctx, 0, false, &lookup_mac->port);
|
|
|
bbaaef |
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
|
|
|
bbaaef |
+ action_parse_field(ctx, width, false, &lookup_mac->ip);
|
|
|
bbaaef |
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
|
|
|
bbaaef |
+ action_parse_field(ctx, 48, false, &lookup_mac->mac);
|
|
|
bbaaef |
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
|
|
|
bbaaef |
+ lookup_mac->dst = *dst;
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+ovnact_lookup_mac_bind_free(
|
|
|
bbaaef |
+ struct ovnact_lookup_mac_bind *lookup_mac OVS_UNUSED)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
static void
|
|
|
bbaaef |
parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
|
|
|
bbaaef |
const struct hmap *gen_opts, const char *opts_type)
|
|
|
bbaaef |
@@ -2722,6 +2828,14 @@ parse_set_action(struct action_context *ctx)
|
|
|
bbaaef |
&& lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
|
|
|
bbaaef |
parse_check_pkt_larger(ctx, &lhs,
|
|
|
bbaaef |
ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts));
|
|
|
bbaaef |
+ } else if (!strcmp(ctx->lexer->token.s, "lookup_arp")
|
|
|
bbaaef |
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
|
|
|
bbaaef |
+ parse_lookup_mac_bind(ctx, &lhs, 32,
|
|
|
bbaaef |
+ ovnact_put_LOOKUP_ARP(ctx->ovnacts));
|
|
|
bbaaef |
+ } else if (!strcmp(ctx->lexer->token.s, "lookup_nd")
|
|
|
bbaaef |
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
|
|
|
bbaaef |
+ parse_lookup_mac_bind(ctx, &lhs, 128,
|
|
|
bbaaef |
+ ovnact_put_LOOKUP_ND(ctx->ovnacts));
|
|
|
bbaaef |
} else {
|
|
|
bbaaef |
parse_assignment_action(ctx, false, &lhs;;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
index ec2b6454c..ec9020d2a 100644
|
|
|
bbaaef |
--- a/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
+++ b/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
@@ -1223,7 +1223,126 @@ output;
|
|
|
bbaaef |
Other packets are implicitly dropped.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 1: IP Input
|
|
|
bbaaef |
+ Ingress Table 1: Neighbor lookup
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ For ARP and IPv6 Neighbor Discovery packets, this table looks into the
|
|
|
bbaaef |
+ <ref db="OVN_Southbound" table="MAC_Binding"/> records to determine
|
|
|
bbaaef |
+ if OVN needs to learn the mac bindings. Following flows are added:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ For each router port P that owns IP address A,
|
|
|
bbaaef |
+ which belongs to subnet S with prefix length L,
|
|
|
bbaaef |
+ a priority-100 flow is added which matches
|
|
|
bbaaef |
+ inport == P &&
|
|
|
bbaaef |
+ arp.spa == S/L && arp.op == 1
|
|
|
bbaaef |
+ (ARP request) with the
|
|
|
bbaaef |
+ following actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
+next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ If the logical router port P is a distributed gateway
|
|
|
bbaaef |
+ router port, additional match
|
|
|
bbaaef |
+ is_chassis_resident(cr-P) is added so that
|
|
|
bbaaef |
+ the resident gateway chassis handles the neighbor lookup.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-100 flow which matches on ARP reply packets and applies
|
|
|
bbaaef |
+ the actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
+next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-100 flow which matches on IPv6 Neighbor Discovery
|
|
|
bbaaef |
+ advertisement packet and applies the actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+reg9[4] = lookup_nd(inport, nd.target, nd.tll);
|
|
|
bbaaef |
+next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-100 flow which matches on IPv6 Neighbor Discovery
|
|
|
bbaaef |
+ solicitation packet and applies the actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+reg9[4] = lookup_nd(inport, ip6.src, nd.sll);
|
|
|
bbaaef |
+next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-0 fallback flow that matches all packets and applies
|
|
|
bbaaef |
+ the action reg9[5] = 1; next;
|
|
|
bbaaef |
+ advancing the packet to the next table.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Ingress Table 2: Neighbor learning
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ This table adds flows to learn the mac bindings from the ARP and
|
|
|
bbaaef |
+ IPv6 Neighbor Solicitation/Advertisement packets if ARP/ND lookup
|
|
|
bbaaef |
+ failed in the previous table.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ reg9[4] will be 1 if the lookup_arp/lookup_nd
|
|
|
bbaaef |
+ in the previous table was successful.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ reg9[5] will be 1 if there was no need to do the lookup.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-100 flow with the match
|
|
|
bbaaef |
+ reg9[4] == 1 || reg9[5] == 1 and advances the packet
|
|
|
bbaaef |
+ to the next table as there is no need to learn the neighbor.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-90 flow with the match arp and
|
|
|
bbaaef |
+ applies the action
|
|
|
bbaaef |
+ put_arp(inport, arp.spa, arp.sha); next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-90 flow with the match nd_na and
|
|
|
bbaaef |
+ applies the action
|
|
|
bbaaef |
+ put_nd(inport, nd.target, nd.tll); next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ A priority-90 flow with the match nd_ns and
|
|
|
bbaaef |
+ applies the action
|
|
|
bbaaef |
+ put_nd(inport, ip6.src, nd.sll); next;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Ingress Table 3: IP Input
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
This table is the core of the logical router datapath functionality. It
|
|
|
bbaaef |
@@ -1320,8 +1439,7 @@ next;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- These flows reply to ARP requests for the router's own IP address
|
|
|
bbaaef |
- and populates mac binding table of the logical router port.
|
|
|
bbaaef |
+ These flows reply to ARP requests for the router's own IP address.
|
|
|
bbaaef |
The ARP requests are handled only if the requestor's IP belongs
|
|
|
bbaaef |
to the same subnets of the logical router port.
|
|
|
bbaaef |
For each router port P that owns IP address A,
|
|
|
bbaaef |
@@ -1334,7 +1452,6 @@ next;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-put_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
eth.dst = eth.src;
|
|
|
bbaaef |
eth.src = E;
|
|
|
bbaaef |
arp.op = 2; /* ARP reply. */
|
|
|
bbaaef |
@@ -1370,17 +1487,6 @@ output;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- These flows handles ARP requests not for router's own IP address.
|
|
|
bbaaef |
- They use the SPA and SHA to populate the logical router port's
|
|
|
bbaaef |
- mac binding table, with priority 80. The typical use case of
|
|
|
bbaaef |
- these flows are GARP requests handling. For the gateway port
|
|
|
bbaaef |
- on a distributed logical router, these flows are only programmed
|
|
|
bbaaef |
- on the gateway port instance on the redirect-chassis .
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
These flows reply to ARP requests for the virtual IP addresses
|
|
|
bbaaef |
@@ -1451,36 +1557,6 @@ arp.sha = external_mac;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- ARP reply handling. Following flows are added to handle ARP replies.
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- For each distributed gateway logical router port a priority-92 flow
|
|
|
bbaaef |
- with match inport == P &&
|
|
|
bbaaef |
- is_chassis_resident(cr-P) && eth.bcast &&
|
|
|
bbaaef |
- arp.op == 2 && arp.spa == I with the
|
|
|
bbaaef |
- action put_arp(inport, arp.spa, arp.sha); so that the
|
|
|
bbaaef |
- resident gateway chassis can learn the GARP reply, where
|
|
|
bbaaef |
- P is the distributed gateway router port name,
|
|
|
bbaaef |
- I is the logical router port's network address.
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- For each distributed gateway logical router port a priority-92 flow
|
|
|
bbaaef |
- with match inport == P &&
|
|
|
bbaaef |
- !is_chassis_resident(cr-P) && eth.bcast &&
|
|
|
bbaaef |
- arp.op == 2 && arp.spa == I with the action
|
|
|
bbaaef |
- drop; so that other chassis drop this packet.
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- A priority-90 flow with match arp.op == 2 has actions
|
|
|
bbaaef |
- put_arp(inport, arp.spa, arp.sha); .
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
Reply to IPv6 Neighbor Solicitations. These flows reply to
|
|
|
bbaaef |
@@ -1499,7 +1575,6 @@ arp.sha = external_mac;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-put_nd(inport, ip6.src, nd.sll);
|
|
|
bbaaef |
nd_na_router {
|
|
|
bbaaef |
eth.src = E;
|
|
|
bbaaef |
ip6.src = A;
|
|
|
bbaaef |
@@ -1521,7 +1596,6 @@ nd_na_router {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-put_nd(inport, ip6.src, nd.sll);
|
|
|
bbaaef |
nd_na {
|
|
|
bbaaef |
eth.src = E;
|
|
|
bbaaef |
ip6.src = A;
|
|
|
bbaaef |
@@ -1546,20 +1620,8 @@ nd_na {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- IPv6 neighbor advertisement handling. This flow uses neighbor
|
|
|
bbaaef |
- advertisements to populate the logical router's mac binding
|
|
|
bbaaef |
- table. A priority-90 flow with match nd_na
|
|
|
bbaaef |
- has actions put_nd(inport, nd.target, nd.tll); .
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- IPv6 neighbor solicitation for non-hosted addresses handling.
|
|
|
bbaaef |
- This flow uses neighbor solicitations to populate the logical
|
|
|
bbaaef |
- router's mac binding table (ones that were directed at the
|
|
|
bbaaef |
- logical router would have matched the priority-90 neighbor
|
|
|
bbaaef |
- solicitation flow already). A priority-80 flow with match
|
|
|
bbaaef |
- nd_ns has actions
|
|
|
bbaaef |
- put_nd(inport, ip6.src, nd.sll); .
|
|
|
bbaaef |
+ Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery
|
|
|
bbaaef |
+ packets.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1675,7 +1737,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 2: DEFRAG
|
|
|
bbaaef |
+ Ingress Table 4: DEFRAG
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
This is to send packets to connection tracker for tracking and
|
|
|
bbaaef |
@@ -1733,7 +1795,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 3: UNSNAT on Distributed Routers
|
|
|
bbaaef |
+ Ingress Table 5: UNSNAT on Distributed Routers
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1772,7 +1834,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 4: DNAT
|
|
|
bbaaef |
+ Ingress Table 6: DNAT
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
Packets enter the pipeline with destination IP address that needs to
|
|
|
bbaaef |
@@ -1780,7 +1842,7 @@ icmp6 {
|
|
|
bbaaef |
in the reverse direction needs to be unDNATed.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 4: Load balancing DNAT rules
|
|
|
bbaaef |
+ Ingress Table 6: Load balancing DNAT rules
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
Following load balancing DNAT flows are added for Gateway router or
|
|
|
bbaaef |
@@ -1851,7 +1913,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 4: DNAT on Gateway Routers
|
|
|
bbaaef |
+ Ingress Table 6: DNAT on Gateway Routers
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1877,7 +1939,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 4: DNAT on Distributed Routers
|
|
|
bbaaef |
+ Ingress Table 6: DNAT on Distributed Routers
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
On distributed routers, the DNAT table only handles packets
|
|
|
bbaaef |
@@ -1924,7 +1986,7 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 5: IPv6 ND RA option processing
|
|
|
bbaaef |
+ Ingress Table 7: IPv6 ND RA option processing
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1954,7 +2016,7 @@ reg0[5] = put_nd_ra_opts(options);next;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 6: IPv6 ND RA responder
|
|
|
bbaaef |
+ Ingress Table 8: IPv6 ND RA responder
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
This table implements IPv6 ND RA responder for the IPv6 ND RA replies
|
|
|
bbaaef |
@@ -1999,7 +2061,7 @@ output;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 7: IP Routing
|
|
|
bbaaef |
+ Ingress Table 9: IP Routing
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
A packet that arrives at this table is an IP packet that should be
|
|
|
bbaaef |
@@ -2149,7 +2211,7 @@ next;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 8: ARP/ND Resolution
|
|
|
bbaaef |
+ Ingress Table 10: ARP/ND Resolution
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
Any packet that reaches this table is an IP packet whose next-hop
|
|
|
bbaaef |
@@ -2284,7 +2346,7 @@ next;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 9: Check packet length
|
|
|
bbaaef |
+ Ingress Table 11: Check packet length
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
For distributed logical routers with distributed gateway port configured
|
|
|
bbaaef |
@@ -2314,7 +2376,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(L); next;
|
|
|
bbaaef |
and advances to the next table.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 10: Handle larger packets
|
|
|
bbaaef |
+ Ingress Table 12: Handle larger packets
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
For distributed logical routers with distributed gateway port configured
|
|
|
bbaaef |
@@ -2363,7 +2425,7 @@ icmp4 {
|
|
|
bbaaef |
and advances to the next table.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 11: Gateway Redirect
|
|
|
bbaaef |
+ Ingress Table 13: Gateway Redirect
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
For distributed logical routers where one of the logical router
|
|
|
bbaaef |
@@ -2425,7 +2487,7 @@ icmp4 {
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- Ingress Table 12: ARP Request
|
|
|
bbaaef |
+ Ingress Table 14: ARP Request
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
In the common case where the Ethernet destination has been resolved, this
|
|
|
bbaaef |
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
index 83a7ec14f..2f6826f17 100644
|
|
|
bbaaef |
--- a/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
+++ b/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
@@ -144,20 +144,22 @@ enum ovn_stage {
|
|
|
bbaaef |
PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 9, "ls_out_port_sec_l2") \
|
|
|
bbaaef |
\
|
|
|
bbaaef |
/* Logical router ingress stages. */ \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, DEFRAG, 2, "lr_in_defrag") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, UNSNAT, 3, "lr_in_unsnat") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, DNAT, 4, "lr_in_dnat") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 5, "lr_in_nd_ra_options") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 6, "lr_in_nd_ra_response") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 7, "lr_in_ip_routing") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, POLICY, 8, "lr_in_policy") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 9, "lr_in_arp_resolve") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 10, "lr_in_chk_pkt_len") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 11,"lr_in_larger_pkts") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 12, "lr_in_gw_redirect") \
|
|
|
bbaaef |
- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 13, "lr_in_arp_request") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, DEFRAG, 4, "lr_in_defrag") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, DNAT, 6, "lr_in_dnat") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 7, "lr_in_nd_ra_options") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 8, "lr_in_nd_ra_response") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 9, "lr_in_ip_routing") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, POLICY, 10, "lr_in_policy") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 11, "lr_in_arp_resolve") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 12, "lr_in_chk_pkt_len") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 13,"lr_in_larger_pkts") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 14, "lr_in_gw_redirect") \
|
|
|
bbaaef |
+ PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 15, "lr_in_arp_request") \
|
|
|
bbaaef |
\
|
|
|
bbaaef |
/* Logical router egress stages. */ \
|
|
|
bbaaef |
PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \
|
|
|
bbaaef |
@@ -196,6 +198,8 @@ enum ovn_stage {
|
|
|
bbaaef |
#define REGBIT_DISTRIBUTED_NAT "reg9[2]"
|
|
|
bbaaef |
/* Register to store the result of check_pkt_larger action. */
|
|
|
bbaaef |
#define REGBIT_PKT_LARGER "reg9[3]"
|
|
|
bbaaef |
+#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]"
|
|
|
bbaaef |
+#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]"
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Returns an "enum ovn_stage" built from the arguments. */
|
|
|
bbaaef |
static enum ovn_stage
|
|
|
bbaaef |
@@ -6420,7 +6424,96 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
ds_cstr(&match), "next;");
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* Logical router ingress table 1: IP Input. */
|
|
|
bbaaef |
+ /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
|
|
|
bbaaef |
+ * table 2: LEARN_NEIGHBOR. */
|
|
|
bbaaef |
+ HMAP_FOR_EACH (od, key_node, datapaths) {
|
|
|
bbaaef |
+ if (!od->nbr) {
|
|
|
bbaaef |
+ continue;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Learn MAC bindings from ARP/IPv6 ND.
|
|
|
bbaaef |
+ *
|
|
|
bbaaef |
+ * For ARP packets, table LOOKUP_NEIGHBOR does a lookup for the
|
|
|
bbaaef |
+ * (arp.spa, arp.sha) in the mac binding table using the 'lookup_arp'
|
|
|
bbaaef |
+ * action and stores the result in REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
|
|
|
bbaaef |
+ *
|
|
|
bbaaef |
+ * For IPv6 ND NA packets, table LOOKUP_NEIGHBOR does a lookup
|
|
|
bbaaef |
+ * for the (nd.target, nd.tll) in the mac binding table using the
|
|
|
bbaaef |
+ * 'lookup_nd' action and stores the result in
|
|
|
bbaaef |
+ * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
|
|
|
bbaaef |
+ *
|
|
|
bbaaef |
+ * For IPv6 ND NS packets, table LOOKUP_NEIGHBOR does a lookup
|
|
|
bbaaef |
+ * for the (ip6.src, nd.sll) in the mac binding table using the
|
|
|
bbaaef |
+ * 'lookup_nd' action and stores the result in
|
|
|
bbaaef |
+ * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
|
|
|
bbaaef |
+ *
|
|
|
bbaaef |
+ * Table LEARN_NEIGHBOR learns the mac-binding using the action
|
|
|
bbaaef |
+ * - 'put_arp/put_nd' only if REGBIT_LOOKUP_NEIGHBOR_RESULT bit
|
|
|
bbaaef |
+ * is not set.
|
|
|
bbaaef |
+ *
|
|
|
bbaaef |
+ * */
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Flows for LOOKUP_NEIGHBOR. */
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
|
|
|
bbaaef |
+ "arp.op == 2",
|
|
|
bbaaef |
+ REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
|
|
|
bbaaef |
+ "lookup_arp(inport, arp.spa, arp.sha); next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_na",
|
|
|
bbaaef |
+ REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
|
|
|
bbaaef |
+ "lookup_nd(inport, nd.target, nd.tll); next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_ns",
|
|
|
bbaaef |
+ REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
|
|
|
bbaaef |
+ "lookup_nd(inport, ip6.src, nd.sll); next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* For other packet types, we can skip neighbor learning.
|
|
|
bbaaef |
+ * So set REGBIT_SKIP_LOOKUP_NEIGHBOR to 1. */
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 0, "1",
|
|
|
bbaaef |
+ REGBIT_SKIP_LOOKUP_NEIGHBOR" = 1; next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Flows for LEARN_NEIGHBOR. */
|
|
|
bbaaef |
+ /* Skip Neighbor learning if not required. */
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
|
|
|
bbaaef |
+ REGBIT_SKIP_LOOKUP_NEIGHBOR" == 1 || "
|
|
|
bbaaef |
+ REGBIT_LOOKUP_NEIGHBOR_RESULT" == 1", "next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
|
|
|
bbaaef |
+ "arp", "put_arp(inport, arp.spa, arp.sha); next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
|
|
|
bbaaef |
+ "nd_na", "put_nd(inport, nd.target, nd.tll); next;");
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
|
|
|
bbaaef |
+ "nd_ns", "put_nd(inport, ip6.src, nd.sll); next;");
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ HMAP_FOR_EACH (op, key_node, ports) {
|
|
|
bbaaef |
+ if (!op->nbrp) {
|
|
|
bbaaef |
+ continue;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Check if we need to learn mac-binding from ARP requests. */
|
|
|
bbaaef |
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
|
|
|
bbaaef |
+ ds_clear(&match);
|
|
|
bbaaef |
+ ds_put_format(&match,
|
|
|
bbaaef |
+ "inport == %s && arp.spa == %s/%u && arp.op == 1",
|
|
|
bbaaef |
+ op->json_key,
|
|
|
bbaaef |
+ op->lrp_networks.ipv4_addrs[i].network_s,
|
|
|
bbaaef |
+ op->lrp_networks.ipv4_addrs[i].plen);
|
|
|
bbaaef |
+ if (op->od->l3dgw_port && op == op->od->l3dgw_port
|
|
|
bbaaef |
+ && op->od->l3redirect_port) {
|
|
|
bbaaef |
+ ds_put_format(&match, " && is_chassis_resident(%s)",
|
|
|
bbaaef |
+ op->od->l3redirect_port->json_key);
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
|
|
|
bbaaef |
+ ds_cstr(&match),
|
|
|
bbaaef |
+ REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
|
|
|
bbaaef |
+ "lookup_arp(inport, arp.spa, arp.sha); next;");
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Logical router ingress table 3: IP Input. */
|
|
|
bbaaef |
HMAP_FOR_EACH (od, key_node, datapaths) {
|
|
|
bbaaef |
if (!od->nbr) {
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
@@ -6442,10 +6535,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 95, "ip4.mcast",
|
|
|
bbaaef |
od->mcast_info.rtr.relay ? "next;" : "drop;");
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* ARP reply handling. Use ARP replies to populate the logical
|
|
|
bbaaef |
- * router's ARP table. */
|
|
|
bbaaef |
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2",
|
|
|
bbaaef |
- "put_arp(inport, arp.spa, arp.sha);");
|
|
|
bbaaef |
+ /* Drop ARP packets (priority 85). ARP request packets for router's own
|
|
|
bbaaef |
+ * IPs are handled with priority-90 flows.
|
|
|
bbaaef |
+ * Drop IPv6 ND packets (priority 85). ND NA packets for router's own
|
|
|
bbaaef |
+ * IPs are handled with priority-90 flows.
|
|
|
bbaaef |
+ */
|
|
|
bbaaef |
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 85,
|
|
|
bbaaef |
+ "arp || nd", "drop;");
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Drop Ethernet local broadcast. By definition this traffic should
|
|
|
bbaaef |
* not be forwarded.*/
|
|
|
bbaaef |
@@ -6458,23 +6554,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
|
|
|
bbaaef |
ds_cstr(&match), "drop;");
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* ND advertisement handling. Use advertisements to populate
|
|
|
bbaaef |
- * the logical router's ARP/ND table. */
|
|
|
bbaaef |
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na",
|
|
|
bbaaef |
- "put_nd(inport, nd.target, nd.tll);");
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- /* Lean from neighbor solicitations that were not directed at
|
|
|
bbaaef |
- * us. (A priority-90 flow will respond to requests to us and
|
|
|
bbaaef |
- * learn the sender's mac address. */
|
|
|
bbaaef |
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns",
|
|
|
bbaaef |
- "put_nd(inport, ip6.src, nd.sll);");
|
|
|
bbaaef |
-
|
|
|
bbaaef |
/* Pass other traffic not already handled to the next table for
|
|
|
bbaaef |
* routing. */
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* Logical router ingress table 1: IP Input for IPv4. */
|
|
|
bbaaef |
+ /* Logical router ingress table 3: IP Input for IPv4. */
|
|
|
bbaaef |
HMAP_FOR_EACH (op, key_node, ports) {
|
|
|
bbaaef |
if (!op->nbrp) {
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
@@ -6584,7 +6669,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
ds_put_format(&actions,
|
|
|
bbaaef |
- "put_arp(inport, arp.spa, arp.sha); "
|
|
|
bbaaef |
"eth.dst = eth.src; "
|
|
|
bbaaef |
"eth.src = %s; "
|
|
|
bbaaef |
"arp.op = 2; /* ARP reply */ "
|
|
|
bbaaef |
@@ -6603,62 +6687,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
ds_cstr(&match), ds_cstr(&actions));
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* Learn from ARP requests that were not directed at us. A typical
|
|
|
bbaaef |
- * use case is GARP request handling. (A priority-90 flow will
|
|
|
bbaaef |
- * respond to request to us and learn the sender's mac address.) */
|
|
|
bbaaef |
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
|
|
|
bbaaef |
- ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match,
|
|
|
bbaaef |
- "inport == %s && arp.spa == %s/%u && arp.op == 1",
|
|
|
bbaaef |
- op->json_key,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].network_s,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].plen);
|
|
|
bbaaef |
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
|
|
|
bbaaef |
- && op->od->l3redirect_port) {
|
|
|
bbaaef |
- ds_put_format(&match, " && is_chassis_resident(%s)",
|
|
|
bbaaef |
- op->od->l3redirect_port->json_key);
|
|
|
bbaaef |
- }
|
|
|
bbaaef |
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
|
|
|
bbaaef |
- ds_cstr(&match),
|
|
|
bbaaef |
- "put_arp(inport, arp.spa, arp.sha);");
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- }
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- /* Handle GARP reply packets received on a distributed router gateway
|
|
|
bbaaef |
- * port. GARP reply broadcast packets could be sent by external
|
|
|
bbaaef |
- * switches. We don't want them to be handled by all the
|
|
|
bbaaef |
- * ovn-controllers if they receive it. So add a priority-92 flow to
|
|
|
bbaaef |
- * apply the put_arp action on a redirect chassis and drop it on
|
|
|
bbaaef |
- * other chassis.
|
|
|
bbaaef |
- * Note that we are already adding a priority-90 logical flow in the
|
|
|
bbaaef |
- * table S_ROUTER_IN_IP_INPUT to apply the put_arp action if
|
|
|
bbaaef |
- * arp.op == 2.
|
|
|
bbaaef |
- * */
|
|
|
bbaaef |
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
|
|
|
bbaaef |
- && op->od->l3redirect_port) {
|
|
|
bbaaef |
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
|
|
|
bbaaef |
- ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match,
|
|
|
bbaaef |
- "inport == %s && is_chassis_resident(%s) && "
|
|
|
bbaaef |
- "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
|
|
|
bbaaef |
- op->json_key, op->od->l3redirect_port->json_key,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].network_s,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].plen);
|
|
|
bbaaef |
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
|
|
|
bbaaef |
- ds_cstr(&match),
|
|
|
bbaaef |
- "put_arp(inport, arp.spa, arp.sha);");
|
|
|
bbaaef |
- ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match,
|
|
|
bbaaef |
- "inport == %s && !is_chassis_resident(%s) && "
|
|
|
bbaaef |
- "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
|
|
|
bbaaef |
- op->json_key, op->od->l3redirect_port->json_key,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].network_s,
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].plen);
|
|
|
bbaaef |
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
|
|
|
bbaaef |
- ds_cstr(&match), "drop;");
|
|
|
bbaaef |
- }
|
|
|
bbaaef |
- }
|
|
|
bbaaef |
-
|
|
|
bbaaef |
/* A set to hold all load-balancer vips that need ARP responses. */
|
|
|
bbaaef |
struct sset all_ips = SSET_INITIALIZER(&all_ips);
|
|
|
bbaaef |
int addr_family;
|
|
|
bbaaef |
@@ -6969,7 +6997,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
ds_put_format(&actions,
|
|
|
bbaaef |
- "put_nd(inport, ip6.src, nd.sll); "
|
|
|
bbaaef |
"nd_na_router { "
|
|
|
bbaaef |
"eth.src = %s; "
|
|
|
bbaaef |
"ip6.src = %s; "
|
|
|
bbaaef |
diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
|
|
|
bbaaef |
index c4099f25a..2a375c8b3 100644
|
|
|
bbaaef |
--- a/ovn/ovn-architecture.7.xml
|
|
|
bbaaef |
+++ b/ovn/ovn-architecture.7.xml
|
|
|
bbaaef |
@@ -970,6 +970,24 @@
|
|
|
bbaaef |
this temporary use.)
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ R = lookup_arp(P, A, M);
|
|
|
bbaaef |
+ R = lookup_nd(P, A, M);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Implemented by storing arguments into OpenFlow fields, then
|
|
|
bbaaef |
+ resubmitting to table 67, which ovn-controller
|
|
|
bbaaef |
+ populates with flows generated from the MAC_Binding
|
|
|
bbaaef |
+ table in the OVN Southbound database. If there is a match in table
|
|
|
bbaaef |
+ 67, then its actions set the logical flow flag MLF_LOOKUP_MAC .
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ (The OpenFlow actions save and restore the OpenFlow fields used for
|
|
|
bbaaef |
+ the arguments, so that the OVN actions do not have to be aware of
|
|
|
bbaaef |
+ this temporary use.)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
|
|
|
bbaaef |
index 477e7bc7a..e5fb51a9d 100644
|
|
|
bbaaef |
--- a/ovn/ovn-sb.xml
|
|
|
bbaaef |
+++ b/ovn/ovn-sb.xml
|
|
|
bbaaef |
@@ -1397,6 +1397,35 @@
|
|
|
bbaaef |
Example: put_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ R = lookup_arp(P, A, M);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Parameters: logical port string field P, 32-bit
|
|
|
bbaaef |
+ IP address field A, 48-bit MAC address field
|
|
|
bbaaef |
+ M.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Result: stored to a 1-bit subfield R.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Looks up A and M in P's mac
|
|
|
bbaaef |
+ binding table. If an entry is found, stores 1 in
|
|
|
bbaaef |
+ the 1-bit subfield R, else 0.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Example:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ reg0[0] = lookup_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
nd_ns { action; ... };
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1553,6 +1582,34 @@
|
|
|
bbaaef |
Example: put_nd(inport, nd.target, nd.tll);
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
+ R = lookup_nd(P, A, M);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Parameters: logical port string field P, 128-bit
|
|
|
bbaaef |
+ IP address field A, 48-bit MAC address field
|
|
|
bbaaef |
+ M.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Result: stored to a 1-bit subfield R.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Looks up A and M in P's mac
|
|
|
bbaaef |
+ binding table. If an entry is found, stores 1 in
|
|
|
bbaaef |
+ the 1-bit subfield R, else 0.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ Example:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ reg0[0] = lookup_nd(inport, ip6.src, eth.src);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
|
|
|
bbaaef |
R = put_dhcp_opts(D1 = V1, D2 = V2, ..., Dn = Vn);
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
|
|
|
bbaaef |
index b532b8eaf..103b25891 100644
|
|
|
bbaaef |
--- a/ovn/utilities/ovn-trace.c
|
|
|
bbaaef |
+++ b/ovn/utilities/ovn-trace.c
|
|
|
bbaaef |
@@ -556,6 +556,22 @@ ovntrace_mac_binding_find(const struct ovntrace_datapath *dp,
|
|
|
bbaaef |
return NULL;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
+static const struct ovntrace_mac_binding *
|
|
|
bbaaef |
+ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
|
|
|
bbaaef |
+ uint16_t port_key, const struct in6_addr *ip,
|
|
|
bbaaef |
+ struct eth_addr mac)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ const struct ovntrace_mac_binding *bind;
|
|
|
bbaaef |
+ HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
|
|
|
bbaaef |
+ &dp->mac_bindings) {
|
|
|
bbaaef |
+ if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)
|
|
|
bbaaef |
+ && eth_addr_equals(bind->mac, mac)) {
|
|
|
bbaaef |
+ return bind;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ return NULL;
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
/* If 's' ends with a UUID, returns a copy of it with the UUID truncated to
|
|
|
bbaaef |
* just the first 6 characters; otherwise, returns a copy of 's'. */
|
|
|
bbaaef |
static char *
|
|
|
bbaaef |
@@ -1704,6 +1720,51 @@ execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
|
|
|
bbaaef |
ETH_ADDR_ARGS(uflow->dl_dst));
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
+static void
|
|
|
bbaaef |
+execute_lookup_mac(const struct ovnact_lookup_mac_bind *bind OVS_UNUSED,
|
|
|
bbaaef |
+ const struct ovntrace_datapath *dp OVS_UNUSED,
|
|
|
bbaaef |
+ struct flow *uflow OVS_UNUSED,
|
|
|
bbaaef |
+ struct ovs_list *super OVS_UNUSED)
|
|
|
bbaaef |
+{
|
|
|
bbaaef |
+ /* Get logical port number.*/
|
|
|
bbaaef |
+ struct mf_subfield port_sf = expr_resolve_field(&bind->port);
|
|
|
bbaaef |
+ ovs_assert(port_sf.n_bits == 32);
|
|
|
bbaaef |
+ uint32_t port_key = mf_get_subfield(&port_sf, uflow);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Get IP address. */
|
|
|
bbaaef |
+ struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
|
|
|
bbaaef |
+ ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
|
|
|
bbaaef |
+ union mf_subvalue ip_sv;
|
|
|
bbaaef |
+ mf_read_subfield(&ip_sf, uflow, &ip_sv);
|
|
|
bbaaef |
+ struct in6_addr ip = (ip_sf.n_bits == 32
|
|
|
bbaaef |
+ ? in6_addr_mapped_ipv4(ip_sv.ipv4)
|
|
|
bbaaef |
+ : ip_sv.ipv6);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ /* Get MAC. */
|
|
|
bbaaef |
+ struct mf_subfield mac_sf = expr_resolve_field(&bind->mac);
|
|
|
bbaaef |
+ ovs_assert(mac_sf.n_bits == 48);
|
|
|
bbaaef |
+ union mf_subvalue mac_sv;
|
|
|
bbaaef |
+ mf_read_subfield(&mac_sf, uflow, &mac_sv);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ const struct ovntrace_mac_binding *binding
|
|
|
bbaaef |
+ = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, mac_sv.mac);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ struct mf_subfield dst = expr_resolve_field(&bind->dst);
|
|
|
bbaaef |
+ uint8_t val = 0;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ if (binding) {
|
|
|
bbaaef |
+ val = 1;
|
|
|
bbaaef |
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
|
|
|
bbaaef |
+ "/* MAC binding to "ETH_ADDR_FMT" found. */",
|
|
|
bbaaef |
+ ETH_ADDR_ARGS(uflow->dl_dst));
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
|
|
|
bbaaef |
+ "/* lookup failed - No MAC binding. */");
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ union mf_subvalue sv = { .u8_val = val };
|
|
|
bbaaef |
+ mf_write_subfield_flow(&dst, &sv, uflow);
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
static void
|
|
|
bbaaef |
execute_put_opts(const struct ovnact_put_opts *po,
|
|
|
bbaaef |
const char *name, struct flow *uflow,
|
|
|
bbaaef |
@@ -2072,6 +2133,14 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
|
|
|
bbaaef |
/* Nothing to do for tracing. */
|
|
|
bbaaef |
break;
|
|
|
bbaaef |
|
|
|
bbaaef |
+ case OVNACT_LOOKUP_ARP:
|
|
|
bbaaef |
+ execute_lookup_mac(ovnact_get_LOOKUP_ARP(a), dp, uflow, super);
|
|
|
bbaaef |
+ break;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ case OVNACT_LOOKUP_ND:
|
|
|
bbaaef |
+ execute_lookup_mac(ovnact_get_LOOKUP_ND(a), dp, uflow, super);
|
|
|
bbaaef |
+ break;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
case OVNACT_PUT_DHCPV4_OPTS:
|
|
|
bbaaef |
execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
|
|
|
bbaaef |
"put_dhcp_opts", uflow, super);
|
|
|
bbaaef |
diff --git a/tests/ovn.at b/tests/ovn.at
|
|
|
bbaaef |
index df41a7549..d52c97541 100644
|
|
|
bbaaef |
--- a/tests/ovn.at
|
|
|
bbaaef |
+++ b/tests/ovn.at
|
|
|
bbaaef |
@@ -2231,6 +2231,33 @@ put_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[]
|
|
|
bbaaef |
has prereqs eth.type == 0x806 && eth.type == 0x806
|
|
|
bbaaef |
|
|
|
bbaaef |
+# lookup_arp
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport, ip4.dst, eth.src);
|
|
|
bbaaef |
+ encodes as push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],pop:NXM_NX_REG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_NX_REG0[]
|
|
|
bbaaef |
+ has prereqs eth.type == 0x800
|
|
|
bbaaef |
+reg1[1] = lookup_arp(inport, arp.spa, arp.sha);
|
|
|
bbaaef |
+ encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[]
|
|
|
bbaaef |
+ has prereqs eth.type == 0x806 && eth.type == 0x806
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+lookup_arp;
|
|
|
bbaaef |
+ Syntax error at `lookup_arp' expecting action.
|
|
|
bbaaef |
+reg0[0] = lookup_arp;
|
|
|
bbaaef |
+ Syntax error at `lookup_arp' expecting field name.
|
|
|
bbaaef |
+reg0[0] = lookup_arp();
|
|
|
bbaaef |
+ Syntax error at `)' expecting field name.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport);
|
|
|
bbaaef |
+ Syntax error at `)' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport ip4.dst);
|
|
|
bbaaef |
+ Syntax error at `ip4.dst' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport, ip4.dst;
|
|
|
bbaaef |
+ Syntax error at `;' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport, ip4.dst, eth.src;
|
|
|
bbaaef |
+ Syntax error at `;' expecting `)'.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport, eth.dst);
|
|
|
bbaaef |
+ Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required.
|
|
|
bbaaef |
+reg0[0] = lookup_arp(inport, ip4.src, ip4.dst);
|
|
|
bbaaef |
+ Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
# put_dhcp_opts
|
|
|
bbaaef |
reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
|
|
|
bbaaef |
encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
|
|
|
bbaaef |
@@ -2331,6 +2358,35 @@ reg1[0] = put_dhcpv6_opts(ia_addr="ae70::4");
|
|
|
bbaaef |
reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1);
|
|
|
bbaaef |
DHCPv6 option domain_search requires string value.
|
|
|
bbaaef |
|
|
|
bbaaef |
+# lookup_nd
|
|
|
bbaaef |
+reg2[0] = lookup_nd(inport, ip6.dst, eth.src);
|
|
|
bbaaef |
+ encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_NX_XXREG0[]
|
|
|
bbaaef |
+ has prereqs eth.type == 0x86dd
|
|
|
bbaaef |
+reg3[0] = lookup_nd(inport, nd.target, nd.tll);
|
|
|
bbaaef |
+ encodes as push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_TLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[]
|
|
|
bbaaef |
+ has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+lookup_nd;
|
|
|
bbaaef |
+ Syntax error at `lookup_nd' expecting action.
|
|
|
bbaaef |
+reg0[0] = lookup_nd;
|
|
|
bbaaef |
+ Syntax error at `lookup_nd' expecting field name.
|
|
|
bbaaef |
+reg0[0] = lookup_nd();
|
|
|
bbaaef |
+ Syntax error at `)' expecting field name.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport);
|
|
|
bbaaef |
+ Syntax error at `)' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport ip6.dst);
|
|
|
bbaaef |
+ Syntax error at `ip6.dst' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport, ip6.dst;
|
|
|
bbaaef |
+ Syntax error at `;' expecting `,'.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport, ip6.dst, eth.src;
|
|
|
bbaaef |
+ Syntax error at `;' expecting `)'.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport, eth.dst);
|
|
|
bbaaef |
+ Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport, ip4.src, ip4.dst);
|
|
|
bbaaef |
+ Cannot use 32-bit field ip4.src[0..31] where 128-bit field is required.
|
|
|
bbaaef |
+reg0[0] = lookup_nd(inport, ip6.src, ip6.dst);
|
|
|
bbaaef |
+ Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is required.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
# set_queue
|
|
|
bbaaef |
set_queue(0);
|
|
|
bbaaef |
encodes as set_queue:0
|
|
|
bbaaef |
@@ -15608,7 +15664,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
# Since the sw0-vir is not claimed by any chassis, eth.dst should be set to
|
|
|
bbaaef |
# zero if the ip4.dst is the virtual ip in the router pipeline.
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
ip_to_hex() {
|
|
|
bbaaef |
@@ -15644,7 +15700,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
# There should be an arp resolve flow to resolve the virtual_ip with the
|
|
|
bbaaef |
# sw0-p1's MAC.
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
# send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir
|
|
|
bbaaef |
@@ -15667,7 +15723,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
# There should be an arp resolve flow to resolve the virtual_ip with the
|
|
|
bbaaef |
# sw0-p2's MAC.
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
# Now send arp reply from sw0-p1. hv1 should claim sw0-vir
|
|
|
bbaaef |
@@ -15688,7 +15744,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
> lflows.txt
|
|
|
bbaaef |
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
# Delete hv1-vif1 port. hv1 should release sw0-vir
|
|
|
bbaaef |
@@ -15706,7 +15762,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
> lflows.txt
|
|
|
bbaaef |
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
# Now send arp reply from sw0-p2. hv2 should claim sw0-vir
|
|
|
bbaaef |
@@ -15727,7 +15783,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
|
|
|
bbaaef |
> lflows.txt
|
|
|
bbaaef |
|
|
|
bbaaef |
AT_CHECK([cat lflows.txt], [0], [dnl
|
|
|
bbaaef |
- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
|
|
|
bbaaef |
+ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
|
|
|
bbaaef |
])
|
|
|
bbaaef |
|
|
|
bbaaef |
# Delete sw0-p2 logical port
|
|
|
bbaaef |
@@ -16332,3 +16388,225 @@ OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
|
|
|
bbaaef |
|
|
|
bbaaef |
OVN_CLEANUP([hv1], [hv2])
|
|
|
bbaaef |
AT_CLEANUP
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_SETUP([ovn -- ARP lookup before learning])
|
|
|
bbaaef |
+AT_KEYWORDS([virtual ports])
|
|
|
bbaaef |
+AT_SKIP_IF([test $HAVE_PYTHON = no])
|
|
|
bbaaef |
+ovn_start
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+send_garp() {
|
|
|
bbaaef |
+ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
|
|
|
bbaaef |
+ local request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa}
|
|
|
bbaaef |
+ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+send_arp_reply() {
|
|
|
bbaaef |
+ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
|
|
|
bbaaef |
+ local request=${eth_dst}${eth_src}08060001080006040002${eth_src}${spa}${eth_dst}${tpa}
|
|
|
bbaaef |
+ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+net_add n1
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+sim_add hv1
|
|
|
bbaaef |
+as hv1
|
|
|
bbaaef |
+ovs-vsctl add-br br-phys
|
|
|
bbaaef |
+ovn_attach n1 br-phys 192.168.0.1
|
|
|
bbaaef |
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
|
|
|
bbaaef |
+ set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
|
|
|
bbaaef |
+ options:tx_pcap=hv1/vif1-tx.pcap \
|
|
|
bbaaef |
+ options:rxq_pcap=hv1/vif1-rx.pcap \
|
|
|
bbaaef |
+ ofport-request=1
|
|
|
bbaaef |
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
|
|
|
bbaaef |
+ set interface hv1-vif2 external-ids:iface-id=sw0-p3 \
|
|
|
bbaaef |
+ options:tx_pcap=hv1/vif2-tx.pcap \
|
|
|
bbaaef |
+ options:rxq_pcap=hv1/vif2-rx.pcap \
|
|
|
bbaaef |
+ ofport-request=2
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+sim_add hv2
|
|
|
bbaaef |
+as hv2
|
|
|
bbaaef |
+ovs-vsctl add-br br-phys
|
|
|
bbaaef |
+ovn_attach n1 br-phys 192.168.0.2
|
|
|
bbaaef |
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
|
|
|
bbaaef |
+ set interface hv2-vif1 external-ids:iface-id=sw1-p1 \
|
|
|
bbaaef |
+ options:tx_pcap=hv2/vif1-tx.pcap \
|
|
|
bbaaef |
+ options:rxq_pcap=hv2/vif1-rx.pcap \
|
|
|
bbaaef |
+ ofport-request=1
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ovn-nbctl ls-add sw0
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ovn-nbctl lsp-add sw0 sw0-p1
|
|
|
bbaaef |
+ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03"
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Create the second logical switch with one port
|
|
|
bbaaef |
+ovn-nbctl ls-add sw1
|
|
|
bbaaef |
+ovn-nbctl lsp-add sw1 sw1-p1
|
|
|
bbaaef |
+ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
|
|
|
bbaaef |
+ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Create a logical router and attach both logical switches
|
|
|
bbaaef |
+ovn-nbctl lr-add lr0
|
|
|
bbaaef |
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
|
|
|
bbaaef |
+ovn-nbctl lsp-add sw0 sw0-lr0
|
|
|
bbaaef |
+ovn-nbctl lsp-set-type sw0-lr0 router
|
|
|
bbaaef |
+ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
|
|
|
bbaaef |
+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
|
|
|
bbaaef |
+ovn-nbctl lsp-add sw1 sw1-lr0
|
|
|
bbaaef |
+ovn-nbctl lsp-set-type sw1-lr0 router
|
|
|
bbaaef |
+ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02
|
|
|
bbaaef |
+ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVN_POPULATE_ARP
|
|
|
bbaaef |
+ovn-nbctl --wait=hv sync
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+as hv1 ovs-appctl -t ovn-controller vlog/set dbg
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ip_to_hex() {
|
|
|
bbaaef |
+ printf "%02x%02x%02x%02x" "$@"
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# From sw0-p1 send GARP for 10.0.0.30.
|
|
|
bbaaef |
+# ovn-controller should learn the
|
|
|
bbaaef |
+# mac_binding entry
|
|
|
bbaaef |
+# port - lr0-sw0
|
|
|
bbaaef |
+# ip - 10.0.0.30
|
|
|
bbaaef |
+# mac - 50:54:00:00:00:03
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 0 = `ovn-sbctl list mac_binding | wc -l`])
|
|
|
bbaaef |
+eth_src=505400000003
|
|
|
bbaaef |
+eth_dst=ffffffffffff
|
|
|
bbaaef |
+spa=$(ip_to_hex 10 0 0 30)
|
|
|
bbaaef |
+tpa=$(ip_to_hex 10 0 0 30)
|
|
|
bbaaef |
+send_garp 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
|
|
|
bbaaef |
+list mac_binding], [0], [lr0-sw0
|
|
|
bbaaef |
+10.0.0.30
|
|
|
bbaaef |
+50:54:00:00:00:03
|
|
|
bbaaef |
+])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
|
|
|
bbaaef |
+grep controller | grep -v n_packets=0 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Wait for an entry in table=67
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=0 \
|
|
|
bbaaef |
+| wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Send garp again. This time the packet should not be sent to ovn-controller.
|
|
|
bbaaef |
+send_garp 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+# Wait for an entry in table=67
|
|
|
bbaaef |
+OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=1 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# The packet should not be sent to ovn-controller. The packet
|
|
|
bbaaef |
+count should be 1 only.
|
|
|
bbaaef |
+AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
|
|
|
bbaaef |
+grep controller | grep -v n_packets=0 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Now send garp packet with different mac.
|
|
|
bbaaef |
+eth_src=505400000013
|
|
|
bbaaef |
+eth_dst=ffffffffffff
|
|
|
bbaaef |
+spa=$(ip_to_hex 10 0 0 30)
|
|
|
bbaaef |
+tpa=$(ip_to_hex 10 0 0 30)
|
|
|
bbaaef |
+send_garp 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# The garp packet should be sent to ovn-controller and the mac_binding entry
|
|
|
bbaaef |
+# should be updated.
|
|
|
bbaaef |
+OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
|
|
|
bbaaef |
+list mac_binding], [0], [lr0-sw0
|
|
|
bbaaef |
+10.0.0.30
|
|
|
bbaaef |
+50:54:00:00:00:13
|
|
|
bbaaef |
+])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Send ARP request to lrp - lr0-sw1 (20.0.0.1) using src mac 50:54:00:00:00:33
|
|
|
bbaaef |
+# and src ip - 10.0.0.50.from sw0-p1.
|
|
|
bbaaef |
+# ovn-controller should add the mac_binding entry
|
|
|
bbaaef |
+# logical_port - lr0
|
|
|
bbaaef |
+# IP - 10.0.0.50
|
|
|
bbaaef |
+# MAC - 50:54:00:00:00:33
|
|
|
bbaaef |
+eth_src=505400000033
|
|
|
bbaaef |
+eth_dst=ffffffffffff
|
|
|
bbaaef |
+spa=$(ip_to_hex 10 0 0 50)
|
|
|
bbaaef |
+tpa=$(ip_to_hex 20 0 0 1)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+send_garp 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# The garp packet should be sent to ovn-controller and the mac_binding entry
|
|
|
bbaaef |
+# should be updated.
|
|
|
bbaaef |
+OVS_WAIT_UNTIL([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \
|
|
|
bbaaef |
+| wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
|
|
|
bbaaef |
+find mac_binding ip=10.0.0.50], [0], [lr0-sw0
|
|
|
bbaaef |
+10.0.0.50
|
|
|
bbaaef |
+50:54:00:00:00:33
|
|
|
bbaaef |
+])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Send the same packet again.
|
|
|
bbaaef |
+send_garp 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \
|
|
|
bbaaef |
+| grep n_packets=1 | wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Now send ARP reply packet with IP - 10.0.0.40 and mac 505400000023
|
|
|
bbaaef |
+eth_src=505400000023
|
|
|
bbaaef |
+eth_dst=ffffffffffff
|
|
|
bbaaef |
+spa=$(ip_to_hex 10 0 0 40)
|
|
|
bbaaef |
+tpa=$(ip_to_hex 10 0 0 50)
|
|
|
bbaaef |
+send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# ovn-controller should add the
|
|
|
bbaaef |
+# mac_binding entry
|
|
|
bbaaef |
+# port - lr0-sw0
|
|
|
bbaaef |
+# ip - 10.0.0.40
|
|
|
bbaaef |
+# mac - 50:54:00:00:00:23
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# The garp packet should be sent to ovn-controller and the mac_binding entry
|
|
|
bbaaef |
+# should be updated.
|
|
|
bbaaef |
+OVS_WAIT_UNTIL([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Wait for an entry in table=67 for the learnt mac_binding entry.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
|
|
|
bbaaef |
+| wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+# Send the same garp reply. This time it should not be sent to ovn-controller.
|
|
|
bbaaef |
+send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
|
|
|
bbaaef |
+| grep n_packets=1 | wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
|
|
|
bbaaef |
+OVS_WAIT_UNTIL(
|
|
|
bbaaef |
+ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
|
|
|
bbaaef |
+| grep n_packets=2 | wc -l`]
|
|
|
bbaaef |
+)
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+OVN_CLEANUP([hv1], [hv2])
|
|
|
bbaaef |
+AT_CLEANUP
|
|
|
bbaaef |
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
|
|
|
bbaaef |
index cf1bc5432..a7adc1c9a 100644
|
|
|
bbaaef |
--- a/tests/test-ovn.c
|
|
|
bbaaef |
+++ b/tests/test-ovn.c
|
|
|
bbaaef |
@@ -1297,6 +1297,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
|
bbaaef |
.egress_ptable = 40,
|
|
|
bbaaef |
.output_ptable = 64,
|
|
|
bbaaef |
.mac_bind_ptable = 65,
|
|
|
bbaaef |
+ .mac_lookup_ptable = 67,
|
|
|
bbaaef |
};
|
|
|
bbaaef |
struct ofpbuf ofpacts;
|
|
|
bbaaef |
ofpbuf_init(&ofpacts, 0);
|
|
|
bbaaef |
--
|
|
|
bbaaef |
2.21.0
|
|
|
bbaaef |
|