unsigned long *allocated_ipv4s; /* A bitmap of allocated IPv4s */
bool ipv6_prefix_set;
struct in6_addr ipv6_prefix;
+ bool mac_only;
};
/* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
}
if (!subnet_str) {
+ if (!ipv6_prefix) {
+ od->ipam_info.mac_only = smap_get_bool(&od->nbs->other_config,
+ "mac_only", false);
+ }
return;
}
}
static uint64_t
-ipam_get_unused_mac(void)
+ipam_get_unused_mac(ovs_be32 ip)
{
- /* Stores the suffix of the most recently ipam-allocated MAC address. */
- static uint32_t last_mac;
-
- uint64_t mac64;
+ uint32_t mac_addr_suffix, i, base_addr = ntohl(ip) & MAC_ADDR_SPACE;
struct eth_addr mac;
- uint32_t mac_addr_suffix, i;
+ uint64_t mac64;
+
for (i = 0; i < MAC_ADDR_SPACE - 1; i++) {
/* The tentative MAC's suffix will be in the interval (1, 0xfffffe). */
- mac_addr_suffix = ((last_mac + i) % (MAC_ADDR_SPACE - 1)) + 1;
+ mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1;
if (!eth_addr_is_zero(mac_prefix)) {
mac64 = eth_addr_to_uint64(mac_prefix) | mac_addr_suffix;
} else {
mac64 = MAC_ADDR_PREFIX | mac_addr_suffix;
}
eth_addr_from_uint64(mac64, &mac);
- if (!ipam_is_duplicate_mac(&mac, mac64, false)) {
- last_mac = mac_addr_suffix;
+ if (!ipam_is_duplicate_mac(&mac, mac64, true)) {
break;
}
}
struct lport_addresses current_addresses;
struct eth_addr static_mac;
+ ovs_be32 static_ip;
enum dynamic_update_type mac;
enum dynamic_update_type ipv4;
enum dynamic_update_type ipv6;
}
static enum dynamic_update_type
-dynamic_ip4_changed(struct dynamic_address_update *update)
+dynamic_ip4_changed(const char *lsp_addrs,
+ struct dynamic_address_update *update)
{
const struct ipam_info *ipam = &update->op->od->ipam_info;
const struct lport_addresses *cur_addresses = &update->current_addresses;
*/
return DYNAMIC;
} else {
+ ovs_be32 new_ip;
+ int n = 0;
+
+ if (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"%n",
+ IP_SCAN_ARGS(&new_ip), &n)
+ && lsp_addrs[n] == '\0') {
+
+ index = ntohl(new_ip) - ipam->start_ipv4;
+ if (ntohl(new_ip) < ipam->start_ipv4 ||
+ index > ipam->total_ipv4s ||
+ bitmap_is_set(ipam->allocated_ipv4s, index)) {
+ /* new static ip is not valid */
+ return DYNAMIC;
+ } else if (cur_addresses->ipv4_addrs[0].addr != new_ip) {
+ update->ipv4 = STATIC;
+ update->static_ip = new_ip;
+ return STATIC;
+ }
+ }
return NONE;
}
}
struct dynamic_address_update *update)
{
update->mac = dynamic_mac_changed(lsp_addrs, update);
- update->ipv4 = dynamic_ip4_changed(update);
+ update->ipv4 = dynamic_ip4_changed(lsp_addrs, update);
update->ipv6 = dynamic_ip6_changed(update);
if (update->mac == NONE &&
update->ipv4 == NONE &&
struct dynamic_address_update *update)
{
struct eth_addr mac;
+ ovs_be32 ip;
int n = 0;
if (ovs_scan(addrspec, ETH_ADDR_SCAN_FMT" dynamic%n",
ETH_ADDR_SCAN_ARGS(mac), &n)
} else {
update->mac = DYNAMIC;
}
- if (update->op->od->ipam_info.allocated_ipv4s) {
+
+ if (ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"%n",
+ IP_SCAN_ARGS(&ip), &n)
+ && addrspec[n] == '\0') {
+ update->ipv4 = STATIC;
+ update->static_ip = ip;
+ } else if (update->op->od->ipam_info.allocated_ipv4s) {
update->ipv4 = DYNAMIC;
} else {
update->ipv4 = NONE;
static void
update_dynamic_addresses(struct dynamic_address_update *update)
{
- struct eth_addr mac;
- switch (update->mac) {
+ ovs_be32 ip4 = 0;
+ switch (update->ipv4) {
case NONE:
- mac = update->current_addresses.ea;
+ if (update->current_addresses.n_ipv4_addrs) {
+ ip4 = update->current_addresses.ipv4_addrs[0].addr;
+ }
break;
case REMOVE:
- OVS_NOT_REACHED();
+ break;
case STATIC:
- mac = update->static_mac;
+ ip4 = update->static_ip;
break;
case DYNAMIC:
- eth_addr_from_uint64(ipam_get_unused_mac(), &mac);
- break;
+ ip4 = htonl(ipam_get_unused_ip(update->od));
}
- ovs_be32 ip4 = 0;
- switch (update->ipv4) {
+ struct eth_addr mac;
+ switch (update->mac) {
case NONE:
- if (update->current_addresses.n_ipv4_addrs) {
- ip4 = update->current_addresses.ipv4_addrs[0].addr;
- }
+ mac = update->current_addresses.ea;
break;
case REMOVE:
- break;
- case STATIC:
OVS_NOT_REACHED();
+ case STATIC:
+ mac = update->static_mac;
+ break;
case DYNAMIC:
- ip4 = htonl(ipam_get_unused_ip(update->od));
+ eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac);
+ break;
}
struct in6_addr ip6 = in6addr_any;
struct ds new_addr = DS_EMPTY_INITIALIZER;
ds_put_format(&new_addr, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
+ ipam_insert_mac(&mac, true);
+
if (ip4) {
ipam_insert_ip(update->od, ntohl(ip4));
ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(ip4));
const struct nbrec_logical_switch_port *nbsp = od->nbs->ports[i];
if (!od->ipam_info.allocated_ipv4s &&
- !od->ipam_info.ipv6_prefix_set) {
+ !od->ipam_info.ipv6_prefix_set &&
+ !od->ipam_info.mac_only) {
if (nbsp->dynamic_addresses) {
nbrec_logical_switch_port_set_dynamic_addresses(nbsp,
NULL);
}
}
- if (!nbsp->n_addresses && nbsp->dynamic_addresses) {
+ if (!num_dynamic_addresses && nbsp->dynamic_addresses) {
nbrec_logical_switch_port_set_dynamic_addresses(nbsp, NULL);
}
}
}
op->od = od;
- ipam_add_port_addresses(od, op);
tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
}
} else {
op->lrp_networks = lrp_networks;
op->od = od;
- ipam_add_port_addresses(op->od, op);
const char *redirect_chassis = smap_get(&op->nbrp->options,
"redirect-chassis");
}
}
}
+
+ ipam_add_port_addresses(op->od, op);
}
}
ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
ETH_ADDR_ARGS(mac));
if (op->peer->od->l3dgw_port
- && op->peer == op->peer->od->l3dgw_port
- && op->peer->od->l3redirect_port) {
- /* The destination lookup flow for the router's
- * distributed gateway port MAC address should only be
- * programmed on the "redirect-chassis". */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->peer->od->l3redirect_port->json_key);
+ && op->peer->od->l3redirect_port
+ && op->od->localnet_port) {
+ bool add_chassis_resident_check = false;
+ if (op->peer == op->peer->od->l3dgw_port) {
+ /* The peer of this port represents a distributed
+ * gateway port. The destination lookup flow for the
+ * router's distributed gateway port MAC address should
+ * only be programmed on the "redirect-chassis". */
+ add_chassis_resident_check = true;
+ } else {
+ /* Check if the option 'reside-on-redirect-chassis'
+ * is set to true on the peer port. If set to true
+ * and if the logical switch has a localnet port, it
+ * means the router pipeline for the packets from
+ * this logical switch should be run on the chassis
+ * hosting the gateway port.
+ */
+ add_chassis_resident_check = smap_get_bool(
+ &op->peer->nbrp->options,
+ "reside-on-redirect-chassis", false);
+ }
+
+ if (add_chassis_resident_check) {
+ ds_put_format(&match, " && is_chassis_resident(%s)",
+ op->peer->od->l3redirect_port->json_key);
+ }
}
ds_clear(&actions);
op->lrp_networks.ipv4_addrs[i].network_s,
op->lrp_networks.ipv4_addrs[i].plen,
op->lrp_networks.ipv4_addrs[i].addr_s);
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
- && op->od->l3redirect_port) {
- /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
- * should only be sent from the "redirect-chassis", so that
- * upstream MAC learning points to the "redirect-chassis".
- * Also need to avoid generation of multiple ARP responses
- * from different chassis. */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
+
+ if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer
+ && op->peer->od->localnet_port) {
+ bool add_chassis_resident_check = false;
+ if (op == op->od->l3dgw_port) {
+ /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
+ * should only be sent from the "redirect-chassis", so that
+ * upstream MAC learning points to the "redirect-chassis".
+ * Also need to avoid generation of multiple ARP responses
+ * from different chassis. */
+ add_chassis_resident_check = true;
+ } else {
+ /* Check if the option 'reside-on-redirect-chassis'
+ * is set to true on the router port. If set to true
+ * and if peer's logical switch has a localnet port, it
+ * means the router pipeline for the packets from
+ * peer's logical switch is be run on the chassis
+ * hosting the gateway port and it should reply to the
+ * ARP requests for the router port IPs.
+ */
+ add_chassis_resident_check = smap_get_bool(
+ &op->nbrp->options,
+ "reside-on-redirect-chassis", false);
+ }
+
+ if (add_chassis_resident_check) {
+ ds_put_format(&match, " && is_chassis_resident(%s)",
+ op->od->l3redirect_port->json_key);
+ }
}
ds_clear(&actions);
continue;
}
+ for (int i = 0; i < od->nbr->n_static_routes; i++) {
+ const struct nbrec_logical_router_static_route *route;
+
+ route = od->nbr->static_routes[i];
+ struct in6_addr gw_ip6;
+ unsigned int plen;
+ char *error = ipv6_parse_cidr(route->nexthop, &gw_ip6, &plen);
+ if (error || plen != 128) {
+ free(error);
+ continue;
+ }
+
+ ds_clear(&match);
+ ds_put_format(&match, "eth.dst == 00:00:00:00:00:00 && "
+ "ip6 && xxreg0 == %s", route->nexthop);
+ struct in6_addr sn_addr;
+ struct eth_addr eth_dst;
+ in6_addr_solicited_node(&sn_addr, &gw_ip6);
+ ipv6_multicast_to_ethernet(ð_dst, &sn_addr);
+
+ char sn_addr_s[INET6_ADDRSTRLEN + 1];
+ ipv6_string_mapped(sn_addr_s, &sn_addr);
+
+ ds_clear(&actions);
+ ds_put_format(&actions,
+ "nd_ns { "
+ "eth.dst = "ETH_ADDR_FMT"; "
+ "ip6.dst = %s; "
+ "nd.target = %s; "
+ "output; "
+ "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
+ route->nexthop);
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
+ ds_cstr(&match), ds_cstr(&actions));
+ }
+
ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
"eth.dst == 00:00:00:00:00:00",
"arp { "
for (size_t i = 0; i < nb_meter->n_bands; i++) {
if (nb_bands[i].rate != sb_bands[i].rate
|| nb_bands[i].burst_size != sb_bands[i].burst_size
- || strcmp(nb_bands[i].action, nb_bands[i].action)) {
+ || strcmp(nb_bands[i].action, sb_bands[i].action)) {
need_update = true;
goto done;
}
}
hmap_destroy(&ports);
- /* Copy nb_cfg from northbound to southbound database.
- *
+ /* Sync ipsec configuration.
+ * Copy nb_cfg from northbound to southbound database.
* Also set up to update sb_cfg once our southbound transaction commits. */
const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl);
if (!nb) {
if (!sb) {
sb = sbrec_sb_global_insert(ctx->ovnsb_txn);
}
+ if (nb->ipsec != sb->ipsec) {
+ sbrec_sb_global_set_ipsec(sb, nb->ipsec);
+ }
sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg);
sbrec_sb_global_set_options(sb, &nb->options);
sb_loop->next_cfg = nb->nb_cfg;
ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);
ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
add_column_noalert(ovnsb_idl_loop.idl,