A few comments inline, otherwise LGTM. Thanks Numan
On Mon, Nov 13, 2017 at 8:50 AM, Mark Michelson <mmich...@redhat.com> wrote: > This change adds three new options to the Northbound > Logical_Router_Port's ipv6_ra_configs option: > > * send_periodic: If set to "true", then OVN will send periodic router > advertisements out of this router port. > * max_interval: The maximum amount of time to wait between sending > periodic router advertisements. > * min_interval: The minimum amount of time to wait between sending > periodic router advertisements. > > When send_periodic is true, then IPv6 RA configs, as well as some layer > 2 and layer 3 information about the router port, are copied to the > southbound database. From there, ovn-controller can use this information > to know when to send periodic RAs and what to send in them. > > Because periodic RAs originate from each ovn-controller, the new > keep-local flag is set on the packet so that ports don't receive an > overabundance of RAs. > > Signed-off-by: Mark Michelson <mmich...@redhat.com> > --- > lib/packets.h | 7 + > ovn/controller/pinctrl.c | 343 ++++++++++++++++++++++++++++++ > +++++++++++++++++ > ovn/northd/ovn-northd.c | 66 +++++++++ > ovn/ovn-nb.xml | 19 +++ > tests/ovn-northd.at | 110 +++++++++++++++ > tests/ovn.at | 150 +++++++++++++++++++++ > 6 files changed, 695 insertions(+) > > diff --git a/lib/packets.h b/lib/packets.h > index 057935cbf..b8249047f 100644 > --- a/lib/packets.h > +++ b/lib/packets.h > @@ -976,6 +976,7 @@ BUILD_ASSERT_DECL(ND_PREFIX_OPT_LEN == sizeof(struct > ovs_nd_prefix_opt)); > > /* Neighbor Discovery option: MTU. */ > #define ND_MTU_OPT_LEN 8 > +#define ND_MTU_DEFAULT 0 > struct ovs_nd_mtu_opt { > uint8_t type; /* ND_OPT_MTU */ > uint8_t len; /* Always 1. */ > @@ -1015,6 +1016,12 @@ BUILD_ASSERT_DECL(RA_MSG_LEN == sizeof(struct > ovs_ra_msg)); > #define ND_RA_MANAGED_ADDRESS 0x80 > #define ND_RA_OTHER_CONFIG 0x40 > > +/* Defaults based on MaxRtrInterval and MinRtrInterval from RFC 4861 > section > + * 6.2.1 > + */ > +#define ND_RA_MAX_INTERVAL_DEFAULT 600 > +#define ND_RA_MIN_INTERVAL_DEFAULT(max) ((max) >= 9 ? (max) / 3 : (max) > * 3 / 4) > + > /* > * Use the same struct for MLD and MLD2, naming members as the defined > fields in > * in the corresponding version of the protocol, though they are reserved > in the > diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c > index 29b2dde0c..4e6a289b4 100644 > --- a/ovn/controller/pinctrl.c > +++ b/ovn/controller/pinctrl.c > @@ -48,6 +48,7 @@ > #include "socket-util.h" > #include "timeval.h" > #include "vswitch-idl.h" > +#include "lflow.h" > > VLOG_DEFINE_THIS_MODULE(pinctrl); > > @@ -88,6 +89,11 @@ static void pinctrl_handle_put_nd_ra_opts( > static void pinctrl_handle_nd_ns(const struct flow *ip_flow, > const struct match *md, > struct ofpbuf *userdata); > +static void init_ipv6_ras(void); > +static void destroy_ipv6_ras(void); > +static void ipv6_ra_wait(void); > +static void send_ipv6_ras(const struct controller_ctx *ctx, > + struct hmap *local_datapaths); > > COVERAGE_DEFINE(pinctrl_drop_put_mac_binding); > > @@ -98,6 +104,7 @@ pinctrl_init(void) > conn_seq_no = 0; > init_put_mac_bindings(); > init_send_garps(); > + init_ipv6_ras(); > } > > static ovs_be32 > @@ -1083,8 +1090,342 @@ pinctrl_run(struct controller_ctx *ctx, > run_put_mac_bindings(ctx); > send_garp_run(ctx, br_int, chassis, chassis_index, local_datapaths, > active_tunnels); > + send_ipv6_ras(ctx, local_datapaths); > } > > +/* Table of ipv6_ra_state structures, keyed on logical port name */ > +static struct shash ipv6_ras; > + > +/* Next IPV6 RA in seconds. */ > +static long long int send_ipv6_ra_time; > + > +struct ipv6_network_prefix { > + struct in6_addr addr; > + unsigned int prefix_len; > +}; > We probably don't need this structure if we reuse the function parse_and_store_addresses from ovn-util.c Please see below for more comments on this. + > +struct ipv6_ra_config { > + time_t min_interval; > + time_t max_interval; > + struct eth_addr eth_src; > + struct eth_addr eth_dst; > + struct in6_addr ipv6_src; > + struct in6_addr ipv6_dst; > + ovs_be32 mtu; > + uint8_t mo_flags; /* Managed/Other flags for RAs */ > + uint8_t la_flags; /* On-link/autonomous flags for address prefixes */ > + struct ipv6_network_prefix *prefixes; > + int n_prefixes; > +}; > + > +struct ipv6_ra_state { > + long long int next_announce; > + struct ipv6_ra_config *config; > + int64_t port_key; > + int64_t metadata; > + bool deleteme; > small nit: can be renamed to delete_me. > +}; > + > +static void > +init_ipv6_ras(void) > +{ > + shash_init(&ipv6_ras); > + send_ipv6_ra_time = LLONG_MAX; > +} > + > +static void > +ipv6_ra_config_delete(struct ipv6_ra_config *config) > +{ > + if (config) { > + free(config->prefixes); > + } > + free(config); > +} > + > +static void > +ipv6_ra_delete(struct ipv6_ra_state *ra) > +{ > + if (ra) { > + ipv6_ra_config_delete(ra->config); > + } > + free(ra); > +} > + > +static void > +destroy_ipv6_ras(void) > +{ > + struct shash_node *iter, *next; > + SHASH_FOR_EACH_SAFE (iter, next, &ipv6_ras) { > + struct ipv6_ra_state *ra = iter->data; > + ipv6_ra_delete(ra); > + shash_delete(&ipv6_ras, iter); > + } > + shash_destroy(&ipv6_ras); > +} > + > +static struct ipv6_ra_config * > +ipv6_ra_update_config(const struct sbrec_port_binding *pb) > +{ > + struct ipv6_ra_config *config; > + > + config = xzalloc(sizeof *config); > + > + config->max_interval = smap_get_int(&pb->options, > "ipv6_ra_max_interval", > + ND_RA_MAX_INTERVAL_DEFAULT); > + config->min_interval = smap_get_int(&pb->options, > "ipv6_ra_min_interval", > + ND_RA_MIN_INTERVAL_DEFAULT(config->max_interval)); > + config->mtu = htonl(smap_get_int(&pb->options, "ipv6_ra_mtu", > + ND_MTU_DEFAULT)); > + config->la_flags = ND_PREFIX_ON_LINK; > + > + const char *address_mode = smap_get(&pb->options, > "ipv6_ra_address_mode"); > + if (!address_mode) { > + VLOG_WARN("No address mode specified"); > + goto fail; > + } > + if (!strcmp(address_mode, "dhcpv6_stateless")) { > + config->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; > + } else if (!strcmp(address_mode, "dhcpv6_stateful")) { > + config->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; > + } else if (!strcmp(address_mode, "slaac")) { > + config->la_flags |= ND_PREFIX_AUTONOMOUS_ADDRESS; > + } else { > + VLOG_WARN("Invalid address mode %s", address_mode); > + goto fail; > + } > + > + const char *prefixes = smap_get(&pb->options, "ipv6_ra_prefixes"); > + if (prefixes) { > + char *prefixes_copy = xstrdup(prefixes); > + char *prefix; > + > + /* Prefixes are in the format: > + * addr/len, where > + * addr is the network prefix > + * and len is the prefix length > + */ > + while ((prefix = strsep(&prefixes_copy, " "))) { > Instead of parsing the IPv6 addresses here, is it possible to reuse the function - parse_and_store_addresses (present in ovn/lib/ovn-util.c) ? Presently this function is static, we can make it public if we can reuse it here. > + unsigned int prefix_len; > + struct in6_addr prefix_addr; > + char *error; > + > + error = ipv6_parse_cidr(prefix, &prefix_addr, &prefix_len); > + if (error) { > + VLOG_WARN("Error parsing IPv6 prefix: %s", error); > + continue; > + } > + > + config->n_prefixes++; > + config->prefixes = xrealloc(config->prefixes, > + config->n_prefixes * sizeof *config->prefixes); > + > + struct ipv6_network_prefix *network; > + network = &config->prefixes[config->n_prefixes - 1]; > + network->addr = prefix_addr; > + network->prefix_len = prefix_len; > + } > + free (prefixes_copy); > + } > + > + /* All nodes multicast addresses */ > + ovs_assert(eth_addr_from_string("33:33:00:00:00:01", > &config->eth_dst)); > + ovs_assert(ipv6_parse("ff02::1", &config->ipv6_dst)); > + > + const char *eth_addr = smap_get(&pb->options, "ipv6_ra_src_eth"); > + if (!eth_addr || !eth_addr_from_string(eth_addr, &config->eth_src)) { > + VLOG_WARN("Invalid ethernet source %s", eth_addr); > + goto fail; > + } > + const char *ip_addr = smap_get(&pb->options, "ipv6_ra_src_addr"); > + if (!ip_addr || !ipv6_parse(ip_addr, &config->ipv6_src)) { > + VLOG_WARN("Invalid IP source %s", ip_addr); > + goto fail; > + } > + > + return config; > + > +fail: > + ipv6_ra_config_delete(config); > + return NULL; > +} > + > +static long long int > +ipv6_ra_calc_next_announce(time_t min_interval, time_t max_interval) > +{ > + long long int min_interval_ms = min_interval * 1000; > + long long int max_interval_ms = max_interval * 1000; > + > + return time_msec() + min_interval_ms + > + random_range(max_interval_ms - min_interval_ms); > +} > + > +static void > +put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, > + struct ofpbuf *ofpacts) > +{ > + struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, > + mf_from_id(dst), > NULL, > + NULL); > + ovs_be64 n_value = htonll(value); > + bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, > n_bits); > + bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, > n_bits); > +} > + > +/* RFC 4861 default AdvValidLifetime (30 days) */ > +#define RA_DEFAULT_VALID_LIFETIME 2592000 > +/* RFC 4861 default AdvPreferredLifetime (7 days) */ > +#define RA_DEFAULT_PREFERRED_LIFETIME 604800 > These values are already defined in ovn/lib/ovn-l7.h and they are set to INFINITY (0xffffffff). I had set the value to infinity thinking it is not going to change for virtual router environment. I think we can reuse these values from here. If you think its good to set the default RFC 4861 values instead, please change the values there. > +static time_t > +ipv6_ra_send(struct ipv6_ra_state *ra) > +{ > + if (time_msec() < ra->next_announce) { > + return ra->next_announce; > + } > + > + uint64_t packet_stub[128 / 8]; > + struct dp_packet packet; > + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); > + compose_nd_ra(&packet, ra->config->eth_src, ra->config->eth_dst, > + &ra->config->ipv6_src, &ra->config->ipv6_dst, > + 255, ra->config->mo_flags, 0, 0, 0, ra->config->mtu); > + > + for (int i = 0; i < ra->config->n_prefixes; ++i) { > + ovs_be128 addr; > + memcpy(&addr, &ra->config->prefixes[i].addr, sizeof addr); > + packet_put_ra_prefix_opt(&packet, ra->config->prefixes[i].prefix > _len, > + ra->config->la_flags, htonl(RA_DEFAULT_VALID_LIFETIME), > + htonl(RA_DEFAULT_PREFERRED_LIFETIME), addr); > + } > + > + uint64_t ofpacts_stub[4096 / 8]; > + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); > + enum ofp_version version = rconn_get_version(swconn); > + > + /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ > + uint32_t dp_key = ra->metadata; > + uint32_t port_key = ra->port_key; > + put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); > + put_load(port_key, MFF_LOG_INPORT, 0, 32, &ofpacts); > + put_load(1, MFF_LOG_FLAGS, MLF_KEEP_LOCAL_BIT, 1, &ofpacts); > + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); > + resubmit->in_port = OFPP_CONTROLLER; > + resubmit->table_id = OFTABLE_LOG_INGRESS_PIPELINE; > + > + 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_vers > ion(version); > + queue_msg(ofputil_encode_packet_out(&po, proto)); > + dp_packet_uninit(&packet); > + ofpbuf_uninit(&ofpacts); > + > + ra->next_announce = ipv6_ra_calc_next_announce(ra- > >config->min_interval, > + ra->config->max_interval); > + > + return ra->next_announce; > +} > + > +static void > +ipv6_ra_wait(void) > +{ > + poll_timer_wait_until(send_ipv6_ra_time); > +} > + > +static void > +send_ipv6_ras(const struct controller_ctx *ctx OVS_UNUSED, > + struct hmap *local_datapaths) > +{ > + struct shash_node *iter, *iter_next; > + > + send_ipv6_ra_time = LLONG_MAX; > + > + SHASH_FOR_EACH (iter, &ipv6_ras) { > + struct ipv6_ra_state *ra = iter->data; > + ra->deleteme = true; > + } > + > + const struct local_datapath *ld; > + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { > + > + struct sbrec_port_binding *lpval; > + const struct sbrec_port_binding *pb; > + struct ovsdb_idl_index_cursor cursor; > + > + lpval = sbrec_port_binding_index_init_row(ctx->ovnsb_idl, > + > &sbrec_table_port_binding); > + sbrec_port_binding_index_set_datapath(lpval, ld->datapath); > + ovsdb_idl_initialize_cursor(ctx->ovnsb_idl, > &sbrec_table_port_binding, > + "lport-by-datapath", &cursor); > + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) { > + if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", > false)) { > + continue; > + } > + > + const char *peer_s; > + peer_s = smap_get(&pb->options, "peer"); > + if (!peer_s) { > + continue; > + } > + > + const struct sbrec_port_binding *peer; > + peer = lport_lookup_by_name(ctx->ovnsb_idl, peer_s); > + if (!peer) { > + continue; > + } > + > + struct ipv6_ra_config *config; > + config = ipv6_ra_update_config(pb); > + if (!config) { > + continue; > + } > + > + struct ipv6_ra_state *ra; > + ra = shash_find_data(&ipv6_ras, pb->logical_port); > + if (!ra) { > + ra = xzalloc(sizeof *ra); > + ra->config = config; > + ra->next_announce = ipv6_ra_calc_next_announce( > + ra->config->min_interval, > + ra->config->max_interval); > + shash_add(&ipv6_ras, pb->logical_port, ra); > + } else { > + ipv6_ra_config_delete(ra->config); > + ra->config = config; > + } > + > + /* Peer is the logical switch port that the logical > + * router port is connected to. The RA is injected > + * into that logical switch port. > + */ > + ra->port_key = peer->tunnel_key; > + ra->metadata = peer->datapath->tunnel_key; > + ra->deleteme = false; > + > + long long int next_ra; > + next_ra = ipv6_ra_send(ra); > + if (send_ipv6_ra_time > next_ra) { > + send_ipv6_ra_time = next_ra; > + } > + } > + } > + > + /* Remove those that are no longer in the SB database */ > + SHASH_FOR_EACH_SAFE (iter, iter_next, &ipv6_ras) { > + struct ipv6_ra_state *ra = iter->data; > + if (ra->deleteme) { > + shash_delete(&ipv6_ras, iter); > + ipv6_ra_delete(ra); > + } > + } > +} > + > + > void > pinctrl_wait(struct controller_ctx *ctx) > { > @@ -1092,6 +1433,7 @@ pinctrl_wait(struct controller_ctx *ctx) > rconn_run_wait(swconn); > rconn_recv_wait(swconn); > send_garp_wait(); > + ipv6_ra_wait(); > } > > void > @@ -1100,6 +1442,7 @@ pinctrl_destroy(void) > rconn_destroy(swconn); > destroy_put_mac_bindings(); > destroy_send_garps(); > + destroy_ipv6_ras(); > } > > /* Implementation of the "put_arp" and "put_nd" OVN actions. These > diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c > index b4c971320..9123e9097 100644 > --- a/ovn/northd/ovn-northd.c > +++ b/ovn/northd/ovn-northd.c > @@ -4445,6 +4445,67 @@ add_router_lb_flow(struct hmap *lflows, struct > ovn_datapath *od, > ds_destroy(&undnat_match); > } > > +#define ND_RA_MAX_INTERVAL_MAX 1800 > +#define ND_RA_MAX_INTERVAL_MIN 4 > + > +#define ND_RA_MIN_INTERVAL_MAX(max) ((max) * 3 / 4) > +#define ND_RA_MIN_INTERVAL_MIN 3 > + > +static void > +copy_ra_to_sb(struct ovn_port *op, const char *address_mode) > +{ > + struct smap options; > + smap_clone(&options, &op->sb->options); > + > + smap_add(&options, "ipv6_ra_send_periodic", "true"); > + smap_add(&options, "ipv6_ra_address_mode", address_mode); > + > + int max_interval = smap_get_int(&op->nbrp->ipv6_ra_configs, > + "max_interval", ND_RA_MAX_INTERVAL_DEFAULT); > + if (max_interval > ND_RA_MAX_INTERVAL_MAX) { > + max_interval = ND_RA_MAX_INTERVAL_MAX; > + } > + if (max_interval < ND_RA_MAX_INTERVAL_MIN) { > + max_interval = ND_RA_MAX_INTERVAL_MIN; > + } > + smap_add_format(&options, "ipv6_ra_max_interval", "%d", max_interval); > + > + int min_interval = smap_get_int(&op->nbrp->ipv6_ra_configs, > + "min_interval", ND_RA_MIN_INTERVAL_DEFAULT(max_interval)); > + if (min_interval > ND_RA_MIN_INTERVAL_MAX(max_interval)) { > + min_interval = ND_RA_MIN_INTERVAL_MAX(max_interval); > + } > + if (min_interval < ND_RA_MIN_INTERVAL_MIN) { > + min_interval = ND_RA_MIN_INTERVAL_MIN; > + } > + smap_add_format(&options, "ipv6_ra_min_interval", "%d", min_interval); > + > + int mtu = smap_get_int(&op->nbrp->ipv6_ra_configs, "mtu", > ND_MTU_DEFAULT); > + /* RFC 2460 requires the MTU for IPv6 to be at least 1280 */ > + if (mtu && mtu >= 1280) { > + smap_add_format(&options, "ipv6_ra_mtu", "%d", mtu); > + } > + > + struct ds s = DS_EMPTY_INITIALIZER; > + for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; ++i) { > + struct ipv6_netaddr *addrs = &op->lrp_networks.ipv6_addrs[i]; > + if (in6_is_lla(&addrs->network)) { > + smap_add(&options, "ipv6_ra_src_addr", addrs->addr_s); > + continue; > + } > + ds_put_format(&s, "%s/%u ", addrs->network_s, addrs->plen); > + } > + /* Remove trailing space */ > + ds_chomp(&s, ' '); > + smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s)); > + ds_destroy(&s); > Instead of storing ipv6_ra_prefixes as options in port binding table, is it good to set logical_router's networks column directly into port_binding's mac column ? Mac column is a bit misnomer though. Presently we set the logical_switch_port's addresses directly in the 'mac' column of port_binding table. This is just a thought. Not sure if it is appropriate. + > + smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s); > + > + sbrec_port_binding_set_options(op->sb, &options); > + smap_destroy(&options); > +} > + > static void > build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, > struct hmap *lflows) > @@ -5428,6 +5489,11 @@ build_lrouter_flows(struct hmap *datapaths, struct > hmap *ports, > continue; > } > > + if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic", > + false)) { > + copy_ra_to_sb(op, address_mode); > + } > + > ds_clear(&match); > ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && > nd_rs", > op->json_key); > diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml > index 8ad53cd7d..864d99c80 100644 > --- a/ovn/ovn-nb.xml > +++ b/ovn/ovn-nb.xml > @@ -1369,6 +1369,25 @@ > Per RFC 2460, the mtu value is recommended no less than 1280, so > any mtu value less than 1280 will be considered as no MTU Option. > </column> > + > + <column name="ipv6_ra_configs" key="send_periodic"> > + Should the router port send periodic router advertisements? If > set to > + true, then this router interface will send router advertisements > + out periodically. The default is false. > + </column> > + > + <column name="ipv6_ra_configs" key="max_interval"> > + The maximum number of seconds to wait between sending periodic > router > + advertisements. This option has no effect if the "send_periodic" > value > + is set to false. The default is 600. > + </column> > + > + <column name="ipv6_ra_configs" key="min_interval"> > + The minimum number of seconds to wait between sending periodic > router > + advertisements. This option has no effect if the "send_periodic" > value > + is set to "false". The default is 0.33 * max_interval. If > max_interval > + is set to its default, then min_interval will be 200. > + </column> > </group> > > <group title="Options"> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index fc9eda870..91d433dd6 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -83,3 +83,113 @@ ovn-nbctl --wait=sb remove Logical_Router_Port bob > options redirect-chassis > AT_CHECK([ovn-sbctl find Gateway_Chassis name=bob_gw1], [0], []) > > AT_CLEANUP > + > +AT_SETUP([ovn -- check IPv6 RA config propagation to SBDB]) > +ovn_start > + > +ovn-nbctl lr-add ro > +ovn-nbctl lrp-add ro ro-sw 00:00:00:00:00:01 aef0::1/64 > +ovn-nbctl ls-add sw > +ovn-nbctl lsp-add sw sw-ro > +ovn-nbctl lsp-set-type sw-ro router > +ovn-nbctl lsp-set-options sw-ro router-port=ro-sw > +ovn-nbctl lsp-set-addresses sw-ro 00:00:00:00:00:01 > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:send_periodic= > true > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=s > laac > +ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1280 > + > +uuid=$(ovn-sbctl --columns=_uuid --bare find Port_Binding > logical_port=ro-sw) > + > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_send_periodic], > +[0], ["true" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_address_mode], > +[0], [slaac > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[0], ["600" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[0], ["200" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_mtu], > +[0], ["1280" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_eth], > +[0], ["00:00:00:00:00:01" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_addr], > +[0], ["fe80::200:ff:fe00:1" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_prefixes], > +[0], ["aef0::/64" > +]) > + > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=300 > +ovn-nbctl --wait=sb set Logical_Router_Port ro-sw > ipv6_ra_configs:min_interval=600 > + > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[0], ["300" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[0], ["225" > +]) > + > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=300 > +ovn-nbctl --wait=sb set Logical_Router_Port ro-sw > ipv6_ra_configs:min_interval=250 > + > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[0], ["300" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[0], ["225" > +]) > + > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=0 > +ovn-nbctl --wait=sb set Logical_Router_Port ro-sw > ipv6_ra_configs:min_interval=0 > + > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[0], ["4" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[0], ["3" > +]) > + > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=3600 > +ovn-nbctl --wait=sb set Logical_Router_Port ro-sw > ipv6_ra_configs:min_interval=2400 > + > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[0], ["1800" > +]) > +AT_CHECK([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[0], ["1350" > +]) > + > +ovn-nbctl --wait=sb set Logical_Router_port ro-sw > ipv6_ra_configs:send_periodic=false > + > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_send_periodic], > +[1], [], [ovn-sbctl: no key "ipv6_ra_send_periodic" in Port_Binding > record "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_max_interval], > +[1], [], [ovn-sbctl: no key "ipv6_ra_max_interval" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_min_interval], > +[1], [], [ovn-sbctl: no key "ipv6_ra_min_interval" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_mtu], > +[1], [], [ovn-sbctl: no key "ipv6_ra_mtu" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_address_mode], > +[1], [], [ovn-sbctl: no key "ipv6_ra_address_mode" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_src_eth], > +[1], [], [ovn-sbctl: no key "ipv6_ra_src_eth" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_src_addr], > +[1], [], [ovn-sbctl: no key "ipv6_ra_src_addr" in Port_Binding record > "${uuid}" column options > +]) > +AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} > options:ipv6_ra_prefixes], > +[1], [], [ovn-sbctl: no key "ipv6_ra_prefixes" in Port_Binding record > "${uuid}" column options > +]) > + > +AT_CLEANUP > diff --git a/tests/ovn.at b/tests/ovn.at > index a27d5d944..52e5d293c 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -9023,3 +9023,153 @@ AT_CHECK([test x$(ovn-sbctl --bare --columns > chassis find port_binding logical_p > OVN_CLEANUP([hv1]) > > AT_CLEANUP > + > +AT_SETUP([ovn -- IPv6 periodic RA]) > +ovn_start > + > +# This test sets up two hypervisors. > +# hv1 and hv2 run ovn-controllers, and > +# each has a VIF connected to the same > +# logical switch in OVN. The logical > +# switch is connected to a logical > +# router port that is configured to send > +# periodic router advertisements. > +# > +# The reason for having two ovn-controller > +# hypervisors is to ensure that the > +# periodic RAs being sent by each ovn-controller > +# are kept to their local hypervisors. If the > +# packets are not kept local, then each port > +# will receive too many RAs. > + > +net_add n1 > +sim_add hv1 > +sim_add hv2 > +as hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.2 > +as hv2 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.3 > + > +ovn-nbctl lr-add ro > +ovn-nbctl lrp-add ro ro-sw 00:00:00:00:00:01 aef0::1/64 > + > +ovn-nbctl ls-add sw > +ovn-nbctl lsp-add sw sw-ro > +ovn-nbctl lsp-set-type sw-ro router > +ovn-nbctl lsp-set-options sw-ro router-port=ro-sw > +ovn-nbctl lsp-set-addresses sw-ro 00:00:00:00:00:01 > +ovn-nbctl lsp-add sw sw-p1 > +ovn-nbctl lsp-set-addresses sw-p1 "00:00:00:00:00:02 aef0::200:ff:fe00:2" > +ovn-nbctl lsp-add sw sw-p2 > +ovn-nbctl lsp-set-addresses sw-p2 "00:00:00:00:00:03 aef0::200:ff:fe00:3" > + > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:send_periodic= > true > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=s > laac > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=4 > +ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=3 > + > +for i in hv1 hv2 ; do > + as $i > + ovs-vsctl -- add-port br-int $i-vif1 -- \ > + set interface $i-vif1 external-ids:iface-id=sw-p1 \ > + options:tx_pcap=$i/vif1-tx.pcap \ > + options:rxq_pcap=$i/vif1-rx.pcap \ > + ofport-request=1 > +done > + > +# Allow time for ovn-northd and ovn-controller to catch up > +sleep 1 > + > +reset_pcap_file() { > + local iface=$1 > + local pcap_file=$2 > + ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ > +options:rxq_pcap=dummy-rx.pcap > + rm -f ${pcap_file}*.pcap > + ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap > \ > +options:rxq_pcap=${pcap_file}-rx.pcap > + > +} > + > +construct_expected_ra() { > + local src_mac=000000000001 > + local dst_mac=333300000001 > + local src_addr=fe80000000000000020000fffe000001 > + local dst_addr=ff020000000000000000000000000001 > + > + local mtu=$1 > + local ra_mo=$2 > + local ra_prefix_la=$3 > + > + local slla=0101${src_mac} > + local mtu_opt="" > + if test $mtu != 0; then > + mtu_opt=05010000${mtu} > + fi > + shift 3 > + > + local prefix="" > + while [[ $# -gt 0 ]] ; do > + local size=$1 > + local net=$2 > + prefix=${prefix}0304${size}${ra_prefix_la}00278d0000093a8000 > 000000${net} > + shift 2 > + done > + > + local ra=ff${ra_mo}00000000000000000000${slla}${mtu_opt}${prefix} > + local icmp=8600XXXX${ra} > + > + local ip_len=$(expr ${#icmp} / 2) > + ip_len=$(printf "%0.4x" ${ip_len}) > + > + local ip=60000000${ip_len}3aff${src_addr}${dst_addr}${icmp} > + local eth=${dst_mac}${src_mac}86dd${ip} > + local packet=${eth} > + echo $packet >> expected > +} > + > +ra_test() { > + construct_expected_ra $@ > + > + for i in hv1 hv2 ; do > + OVS_WAIT_WHILE([test 24 = $(wc -c $i/vif1-tx.pcap | cut -d " " > -f1)]) > + > + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $i/vif1-tx.pcap > > packets > + > + cat expected | cut -c -112 > expout > + AT_CHECK([cat packets | cut -c -112], [0], [expout]) > + > + # Skip ICMPv6 checksum. > + cat expected | cut -c 117- > expout > + AT_CHECK([cat packets | cut -c 117-], [0], [expout]) > + > + rm -f packets > + as $i reset_pcap_file $i-vif1 $i/vif1 > + done > + > + rm -f expected > +} > + > +# Baseline test with no MTU > +ra_test 0 00 c0 40 aef00000000000000000000000000000 > + > +# Now make sure an MTU option makes it > +ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500 > +ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 > + > +# Now test for multiple network prefixes > +ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 > fd0f\:\:1/48' > +ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 30 > fd0f0000000000000000000000000000 > + > +# Test a different address mode now > +ovn-nbctl --wait=hv set Logical_Router_Port ro-sw > ipv6_ra_configs:address_mode=dhcpv6_stateful > +ra_test 000005dc 80 80 40 aef00000000000000000000000000000 30 > fd0f0000000000000000000000000000 > + > +# And the other address mode > +ovn-nbctl --wait=hv set Logical_Router_Port ro-sw > ipv6_ra_configs:address_mode=dhcpv6_stateless > +ra_test 000005dc 40 80 40 aef00000000000000000000000000000 30 > fd0f0000000000000000000000000000 > + > +OVN_CLEANUP([hv1],[hv2]) > +AT_CLEANUP > -- > 2.13.5 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev