summaryrefslogtreecommitdiff
path: root/ovn
diff options
context:
space:
mode:
authorNuman Siddique <nusiddiq@redhat.com>2017-11-02 06:20:02 +0530
committerBen Pfaff <blp@ovn.org>2017-11-02 13:39:14 -0700
commitb1a3a6a405781423b993d97157a0af82c1a5e15d (patch)
treeb5ca00fe055da332d2163cd533983041d64e3612 /ovn
parent4364646c6e9898ee74a1e53d36a80a5bb67fc9fb (diff)
downloadopenvswitch-b1a3a6a405781423b993d97157a0af82c1a5e15d.tar.gz
ovn: Generate Neighbor Solicitation packet for unknown MAC IPv6 packets
In the router ingress pipeline, if the destination mac is unresolved by the time the packet reaches the ARP_REQUEST stage, OVN should generate an IPv6 Neighbor Solicitation packet to learn the MAC address. This feature is presently missing. This patch adds this feature. A new action "nd_ns" is added which replaces an IPv6 packet being processed with an IPv6 Neighbor Solicitation packet. ovn-northd adds a flow in the ARP_REQUEST router ingress pipeline stage if the eth.dst is zero which applies this action. This action is similar to the IPv4 counterpart "arp" action. OVN already has the support to learn the MAC from the IPv6 Neighbor Advertisement packets and storing in the south bound MAC_Binding table. Signed-off-by: Numan Siddique <nusiddiq@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'ovn')
-rw-r--r--ovn/controller/pinctrl.c122
-rw-r--r--ovn/lib/actions.c22
-rw-r--r--ovn/northd/ovn-northd.8.xml24
-rw-r--r--ovn/northd/ovn-northd.c8
-rw-r--r--ovn/ovn-sb.xml37
-rw-r--r--ovn/utilities/ovn-trace.c30
6 files changed, 178 insertions, 65 deletions
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 3e470717c..29b2dde0c 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -85,6 +85,9 @@ static void pinctrl_handle_put_nd_ra_opts(
const struct flow *ip_flow, struct dp_packet *pkt_in,
struct ofputil_packet_in *pin, struct ofpbuf *userdata,
struct ofpbuf *continuation);
+static void pinctrl_handle_nd_ns(const struct flow *ip_flow,
+ const struct match *md,
+ struct ofpbuf *userdata);
COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
@@ -132,6 +135,43 @@ set_switch_config(struct rconn *swconn,
}
static void
+set_actions_and_enqueue_msg(const struct dp_packet *packet,
+ const struct match *md,
+ struct ofpbuf *userdata)
+{
+ /* Copy metadata from 'md' into the packet-out via "set_field"
+ * actions, then add actions from 'userdata'.
+ */
+ uint64_t ofpacts_stub[4096 / 8];
+ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+ enum ofp_version version = rconn_get_version(swconn);
+
+ reload_metadata(&ofpacts, md);
+ enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+ version, NULL, NULL,
+ &ofpacts);
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "failed to parse actions from userdata (%s)",
+ ofperr_to_string(error));
+ ofpbuf_uninit(&ofpacts);
+ return;
+ }
+
+ struct ofputil_packet_out po = {
+ .packet = dp_packet_data(packet),
+ .packet_len = dp_packet_size(packet),
+ .buffer_id = UINT32_MAX,
+ .ofpacts = ofpacts.data,
+ .ofpacts_len = ofpacts.size,
+ };
+ match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
+ enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+ queue_msg(ofputil_encode_packet_out(&po, proto));
+ ofpbuf_uninit(&ofpacts);
+}
+
+static void
pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
struct ofpbuf *userdata)
{
@@ -166,40 +206,8 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
ip_flow->vlans[0].tci);
}
- /* Compose actions.
- *
- * First, copy metadata from 'md' into the packet-out via "set_field"
- * actions, then add actions from 'userdata'.
- */
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- enum ofp_version version = rconn_get_version(swconn);
-
- reload_metadata(&ofpacts, md);
- enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
- version, NULL, NULL,
- &ofpacts);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "failed to parse arp actions (%s)",
- ofperr_to_string(error));
- goto exit;
- }
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(ofputil_encode_packet_out(&po, proto));
-
-exit:
+ set_actions_and_enqueue_msg(&packet, md, userdata);
dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
}
static void
@@ -994,6 +1002,10 @@ process_packet_in(const struct ofp_header *msg, struct controller_ctx *ctx)
&continuation);
break;
+ case ACTION_OPCODE_ND_NS:
+ pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata);
+ break;
+
default:
VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
ntohl(ah->opcode));
@@ -1812,9 +1824,6 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md,
return;
}
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-
uint64_t packet_stub[128 / 8];
struct dp_packet packet;
dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
@@ -1827,35 +1836,32 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md,
&ip_flow->nd_target, &ip_flow->ipv6_src,
htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE));
- /* Reload previous packet metadata. */
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- reload_metadata(&ofpacts, md);
+ /* Reload previous packet metadata and set actions from userdata. */
+ set_actions_and_enqueue_msg(&packet, md, userdata);
+ dp_packet_uninit(&packet);
+}
- enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
- version, NULL, NULL,
- &ofpacts);
- if (error) {
+static void
+pinctrl_handle_nd_ns(const struct flow *ip_flow, const struct match *md,
+ struct ofpbuf *userdata)
+{
+ /* This action only works for IPv6 packets. */
+ if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "failed to parse actions for 'na' (%s)",
- ofperr_to_string(error));
- goto exit;
+ VLOG_WARN_RL(&rl, "NS action on non-IPv6 packet");
+ return;
}
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
+ uint64_t packet_stub[128 / 8];
+ struct dp_packet packet;
+ dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- queue_msg(ofputil_encode_packet_out(&po, proto));
+ compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,
+ &ip_flow->ipv6_dst);
-exit:
+ /* Reload previous packet metadata and set actions from userdata. */
+ set_actions_and_enqueue_msg(&packet, md, userdata);
dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
}
static void
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 0df5edbaa..69a2172a8 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1192,6 +1192,12 @@ parse_ND_NA(struct action_context *ctx)
}
static void
+parse_ND_NS(struct action_context *ctx)
+{
+ parse_nested_action(ctx, OVNACT_ND_NS, "ip6");
+}
+
+static void
parse_CLONE(struct action_context *ctx)
{
parse_nested_action(ctx, OVNACT_CLONE, NULL);
@@ -1219,6 +1225,12 @@ format_ND_NA(const struct ovnact_nest *nest, struct ds *s)
}
static void
+format_ND_NS(const struct ovnact_nest *nest, struct ds *s)
+{
+ format_nested_action(nest, "nd_ns", s);
+}
+
+static void
format_CLONE(const struct ovnact_nest *nest, struct ds *s)
{
format_nested_action(nest, "clone", s);
@@ -1265,6 +1277,14 @@ encode_ND_NA(const struct ovnact_nest *on,
}
static void
+encode_ND_NS(const struct ovnact_nest *on,
+ const struct ovnact_encode_params *ep,
+ struct ofpbuf *ofpacts)
+{
+ encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts);
+}
+
+static void
encode_CLONE(const struct ovnact_nest *on,
const struct ovnact_encode_params *ep,
struct ofpbuf *ofpacts)
@@ -2192,6 +2212,8 @@ parse_action(struct action_context *ctx)
parse_ARP(ctx);
} else if (lexer_match_id(ctx->lexer, "nd_na")) {
parse_ND_NA(ctx);
+ } else if (lexer_match_id(ctx->lexer, "nd_ns")) {
+ parse_ND_NS(ctx);
} else if (lexer_match_id(ctx->lexer, "get_arp")) {
parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));
} else if (lexer_match_id(ctx->lexer, "put_arp")) {
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index b3e8b0806..5c277e3d2 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -1936,15 +1936,15 @@ next;
<p>
In the common case where the Ethernet destination has been resolved, this
- table outputs the packet. Otherwise, it composes and sends an ARP
- request. It holds the following flows:
+ table outputs the packet. Otherwise, it composes and sends an ARP or
+ IPv6 Neighbor Solicitation request. It holds the following flows:
</p>
<ul>
<li>
<p>
- Unknown MAC address. A priority-100 flow with match <code>eth.dst ==
- 00:00:00:00:00:00</code> has the following actions:
+ Unknown MAC address. A priority-100 flow for IPv4 packets with match
+ <code>eth.dst == 00:00:00:00:00:00</code> has the following actions:
</p>
<pre>
@@ -1958,13 +1958,25 @@ arp {
</pre>
<p>
+ Unknown MAC address. A priority-100 flow for IPv6 packets with match
+ <code>eth.dst == 00:00:00:00:00:00</code> has the following actions:
+ </p>
+
+ <pre>
+nd_ns {
+ nd.target = xxreg0;
+ output;
+};
+ </pre>
+
+ <p>
(Ingress table <code>IP Routing</code> initialized <code>reg1</code>
with the IP address owned by <code>outport</code> and
- <code>reg0</code> with the next-hop IP address)
+ <code>(xx)reg0</code> with the next-hop IP address)
</p>
<p>
- The IP packet that triggers the ARP request is dropped.
+ The IP packet that triggers the ARP/IPv6 NS request is dropped.
</p>
</li>
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 3478ec54a..a00a86143 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -5819,7 +5819,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
*
* In the common case where the Ethernet destination has been resolved,
* this table outputs the packet (priority 0). Otherwise, it composes
- * and sends an ARP request (priority 100). */
+ * and sends an ARP/IPv6 NA request (priority 100). */
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbr) {
continue;
@@ -5834,6 +5834,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
"arp.op = 1; " /* ARP request */
"output; "
"};");
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
+ "eth.dst == 00:00:00:00:00:00",
+ "nd_ns { "
+ "nd.target = xxreg0; "
+ "output; "
+ "};");
ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;");
}
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 2e4f28b96..ca8cbecdd 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1258,6 +1258,43 @@
<p><b>Example:</b> <code>put_arp(inport, arp.spa, arp.sha);</code></p>
</dd>
+ <dt><code>nd_ns { <var>action</var>; </code>...<code> };</code></dt>
+ <dd>
+ <p>
+ Temporarily replaces the IPv6 packet being processed by an IPv6
+ Neighbor Solicitation packet and executes each nested
+ <var>action</var> on the IPv6 NS packet. Actions following the
+ <var>nd_ns</var> action, if any, apply to the original, unmodified
+ packet.
+ </p>
+
+ <p>
+ The IPv6 NS packet that this action operates on is initialized
+ based on the IPv6 packet being processed, as follows. These are
+ default values that the nested actions will probably want to
+ change:
+ </p>
+
+ <ul>
+ <li><code>eth.src</code> unchanged</li>
+ <li><code>eth.dst</code> set to IPv6 multicast MAC address</li>
+ <li><code>eth.type = 0x86dd</code></li>
+ <li><code>ip6.src</code> copied from <code>ip6.src</code></li>
+ <li>
+ <code>ip6.dst</code> set to IPv6 Solicited-Node multicast address
+ </li>
+ <li><code>icmp6.type = 135</code> (Neighbor Solicitation)</li>
+ <li><code>nd.target</code> copied from <code>ip6.dst</code></li>
+ </ul>
+
+ <p>
+ The IPv6 NS packet has the same VLAN header, if any, as the IP
+ packet it replaces.
+ </p>
+
+ <p><b>Prerequisite:</b> <code>ip6</code></p>
+ </dd>
+
<dt>
<code>nd_na { <var>action</var>; </code>...<code> };</code>
</dt>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 211148b8b..e457284fc 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1510,6 +1510,31 @@ execute_nd_na(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
}
static void
+execute_nd_ns(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
+ const struct flow *uflow, uint8_t table_id,
+ enum ovnact_pipeline pipeline, struct ovs_list *super)
+{
+ struct flow na_flow = *uflow;
+
+ /* Update fields for NA. */
+ na_flow.dl_src = uflow->dl_src;
+ na_flow.ipv6_src = uflow->ipv6_src;
+ na_flow.ipv6_dst = uflow->ipv6_dst;
+ struct in6_addr sn_addr;
+ in6_addr_solicited_node(&sn_addr, &uflow->ipv6_dst);
+ ipv6_multicast_to_ethernet(&na_flow.dl_dst, &sn_addr);
+ na_flow.tp_src = htons(135);
+ na_flow.arp_sha = eth_addr_zero;
+ na_flow.arp_tha = uflow->dl_dst;
+
+ struct ovntrace_node *node = ovntrace_node_append(
+ super, OVNTRACE_NODE_TRANSFORMATION, "nd_ns");
+
+ trace_actions(on->nested, on->nested_len, dp, &na_flow,
+ table_id, pipeline, &node->subs);
+}
+
+static void
execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
const struct ovntrace_datapath *dp,
struct flow *uflow, struct ovs_list *super)
@@ -1811,6 +1836,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
super);
break;
+ case OVNACT_ND_NS:
+ execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id, pipeline,
+ super);
+ break;
+
case OVNACT_GET_ARP:
execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super);
break;