This patchs add a new action 'nd_ra' against current 'nd_na' action, to work as a Router Solicitation packet responder. The inner actions to set fields, flags, options in RA packet will be introduced in the following patch.
v1 -> v2 rebased --- include/ovn/actions.h | 7 ++++++ ovn/controller/pinctrl.c | 61 +++++++++++++++++++++++++++++++---------------- ovn/lib/actions.c | 28 ++++++++++++++++++++++ ovn/ovn-sb.xml | 41 +++++++++++++++++++++++++++++++ ovn/utilities/ovn-trace.c | 19 ++++++++------- tests/ovn.at | 6 +++++ 6 files changed, 133 insertions(+), 29 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index d1942b3..a776a26 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -61,6 +61,7 @@ struct simap; OVNACT(CT_LB, ovnact_ct_lb) \ OVNACT(ARP, ovnact_nest) \ OVNACT(ND_NA, ovnact_nest) \ + OVNACT(ND_RA, ovnact_nest) \ OVNACT(GET_ARP, ovnact_get_mac_bind) \ OVNACT(PUT_ARP, ovnact_put_mac_bind) \ OVNACT(GET_ND, ovnact_get_mac_bind) \ @@ -344,6 +345,12 @@ enum action_opcode { * - Any number of DHCPv6 options. */ ACTION_OPCODE_PUT_DHCPV6_OPTS, + + /* "nd_ra { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_ND_RA, }; /* Header. */ diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 4ef2366..ae3a58b 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -71,9 +71,9 @@ static void send_garp_run(const struct ovsrec_bridge *, const char *chassis_id, const struct lport_index *lports, struct hmap *local_datapaths); -static void pinctrl_handle_nd_na(const struct flow *ip_flow, - const struct match *md, - struct ofpbuf *userdata); +static void pinctrl_handle_nd(const struct flow *ip_flow, + const struct match *md, + struct ofpbuf *userdata); static void reload_metadata(struct ofpbuf *ofpacts, const struct match *md); @@ -698,7 +698,8 @@ process_packet_in(const struct ofp_header *msg) break; case ACTION_OPCODE_ND_NA: - pinctrl_handle_nd_na(&headers, &pin.flow_metadata, &userdata); + case ACTION_OPCODE_ND_RA: + pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata); break; case ACTION_OPCODE_PUT_ND: @@ -1350,33 +1351,51 @@ reload_metadata(struct ofpbuf *ofpacts, const struct match *md) } static void -pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md, - struct ofpbuf *userdata) +pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md, + struct ofpbuf *userdata) { - /* This action only works for IPv6 ND Neighbor Solicitation packets, - * and the switch should only send us such packets this way, but check + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + /* This action only works for IPv6 ND packets, and only Neighbor + * Solicitation and Router Solicitation packets are supported. + * The switch should only send us ND packets this way, but check * here just to be sure. */ - if (!is_nd_neighbor(ip_flow, NULL) - || ip_flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "NA action on non-ND-NS packet"); + if (!is_nd(ip_flow, NULL) + || (ip_flow->tp_src != htons(ND_NEIGHBOR_SOLICIT) + && ip_flow->tp_src != htons(ND_ROUTER_SOLICIT))) { + VLOG_WARN_RL(&rl, "ND action on invalid or unsupported packet"); return; } enum ofp_version version = rconn_get_version(swconn); enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); - uint64_t packet_stub[128 / 8]; + bool as_nd_na = ip_flow->tp_src == htons(ND_NEIGHBOR_SOLICIT); + uint64_t packet_stub[256 / 8]; struct dp_packet packet; dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); - /* xxx These flags are not exactly correct. Look at section 7.2.4 - * xxx of RFC 4861. For example, we need to set ND_RSO_ROUTER for - * xxx router's interfaces and ND_RSO_SOLICITED only if it was - * xxx requested. */ - compose_nd_na(&packet, ip_flow->dl_dst, ip_flow->dl_src, - &ip_flow->nd_target, &ip_flow->ipv6_src, - htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE)); + if (as_nd_na) { + /* xxx These flags are not exactly correct. Look at section 7.2.4 + * xxx of RFC 4861. For example, we need to set ND_RSO_ROUTER for + * xxx router's interfaces and ND_RSO_SOLICITED only if it was + * xxx requested. */ + compose_nd_na(&packet, ip_flow->dl_dst, ip_flow->dl_src, + &ip_flow->nd_target, &ip_flow->ipv6_src, + htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE)); + } else { + /* xxx Using hardcode data to compose RA packet. + * xxx The following patch should fix this. */ + uint8_t cur_hop_limit = 64; + uint8_t mo_flags = 0; + ovs_be16 router_lifetime = htons(0x2a30); /* 3 hours. */ + ovs_be32 reachable_time = 0; + ovs_be32 retrans_timer = 0; + + compose_nd_ra(&packet, ip_flow->dl_dst, ip_flow->dl_src, + &ip_flow->ipv6_dst, &ip_flow->ipv6_src, + cur_hop_limit, mo_flags, + router_lifetime, reachable_time, retrans_timer); + } /* Reload previous packet metadata. */ uint64_t ofpacts_stub[4096 / 8]; @@ -1387,7 +1406,7 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md, version, &ofpacts); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "failed to parse actions for 'na' (%s)", + VLOG_WARN_RL(&rl, "failed to parse actions for 'nd' (%s)", ofperr_to_string(error)); goto exit; } diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index 28b66ed..5568475 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1066,6 +1066,12 @@ parse_ND_NA(struct action_context *ctx) } static void +parse_ND_RA(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_ND_RA, "nd_rs"); +} + +static void format_nested_action(const struct ovnact_nest *on, const char *name, struct ds *s) { @@ -1087,6 +1093,12 @@ format_ND_NA(const struct ovnact_nest *nest, struct ds *s) } static void +format_ND_RA(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "nd_ra", s); +} + +static void encode_nested_actions(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, enum action_opcode opcode, @@ -1127,6 +1139,14 @@ encode_ND_NA(const struct ovnact_nest *on, } static void +encode_ND_RA(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_actions(on, ep, ACTION_OPCODE_ND_RA, ofpacts); +} + +static void free_nested_actions(struct ovnact_nest *on) { ovnacts_free(on->nested, on->nested_len); @@ -1144,6 +1164,12 @@ free_ND_NA(struct ovnact_nest *nest) { free_nested_actions(nest); } + +static void +free_ND_RA(struct ovnact_nest *nest) +{ + free_nested_actions(nest); +} static void parse_get_mac_bind(struct action_context *ctx, int width, @@ -1669,6 +1695,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_ra")) { + parse_ND_RA(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/ovn-sb.xml b/ovn/ovn-sb.xml index af48daa..7b17d9d 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1210,6 +1210,47 @@ </p> </dd> + <dt> + <code>nd_ra { <var>action</var>; </code>...<code> };</code> + </dt> + + <dd> + <p> + Temporarily replaces the IPv6 router solicitation (RS) packet + being processed by an IPv6 router advertisement (RA) + packet and executes each nested <var>action</var> on the RA + packet. Actions following the <code>nd_ra</code> action, + if any, apply to the original, unmodified packet. + </p> + + <p> + The RA 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.dst</code> copied from <code>eth.src</code></li> + <li><code>eth.src</code> replaced by new value</li> + <li><code>eth.type = 0x86dd</code></li> + <li><code>ip6.dst</code> copied from <code>ip6.src</code></li> + <li><code>ip6.src</code> replaced by new value</li> + <li><code>icmp6.type = 134</code> (Router Advertisement)</li> + <li><code>nd.target</code> unchanged</li> + <li><code>nd.tll = 00:00:00:00:00:00</code></li> + <li><code>nd.sll</code> replaced by new value</li> + </ul> + + <p> + The ND packet has the same VLAN header, if any, as the IPv6 packet + it replaces. + </p> + + <p> + <b>Prerequisite:</b> <code>nd_rs</code> + </p> + </dd> + <dt><code>get_nd(<var>P</var>, <var>A</var>);</code></dt> <dd> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 487ae52..fabecc6 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -1128,23 +1128,25 @@ execute_arp(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, } static void -execute_nd_na(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, - const struct flow *uflow, uint8_t table_id, - enum ovntrace_pipeline pipeline, struct ovs_list *super) +execute_nd_adv(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + enum ovntrace_pipeline pipeline, struct ovs_list *super, + bool is_neighbor_adv) { struct flow na_flow = *uflow; - /* Update fields for NA. */ + /* Update fields for ND Router/Neighbor Advertisement. */ na_flow.dl_src = uflow->dl_dst; na_flow.dl_dst = uflow->dl_src; na_flow.ipv6_dst = uflow->ipv6_src; na_flow.ipv6_src = uflow->nd_target; - na_flow.tp_src = htons(136); + na_flow.tp_src = is_neighbor_adv ? htons(136) : htons(134); 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_na"); + super, OVNTRACE_NODE_TRANSFORMATION, + (is_neighbor_adv ? "nd_na" : "nd_ra")); trace_actions(on->nested, on->nested_len, dp, &na_flow, table_id, pipeline, &node->subs); @@ -1264,8 +1266,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, break; case OVNACT_ND_NA: - execute_nd_na(ovnact_get_ND_NA(a), dp, uflow, table_id, pipeline, - super); + case OVNACT_ND_RA: + execute_nd_adv(ovnact_get_ND_NA(a), dp, uflow, table_id, pipeline, + super, (a->type == OVNACT_ND_NA)); break; case OVNACT_GET_ARP: diff --git a/tests/ovn.at b/tests/ovn.at index 77ce244..ec16d8e 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -885,6 +885,12 @@ nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inpor encodes as controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) has prereqs nd_ns +# nd_ra +nd_ra{eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output;}; + formats as nd_ra { eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output; }; + encodes as controller(userdata=00.00.00.06.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.18.80.00.34.10.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.01.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) + has prereqs nd_rs + # get_nd get_nd(outport, ip6.dst); encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_XXREG0[] -- 2.7.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev