2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

ovn-northd ipam: Support IPv6 dynamic assignment

OVN will generate the IPv6 address for a logical port if requested
using the IPv6 prefix and the MAC address (as IEEE EUI64 identifier).
To generate the IPv6 address, CMS should define the IPv6 prefix in the
'Logical_switch.other_config:ipv6_prefix' column.

Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
Co-authored-by: Ben Pfaff <blp@ovn.org>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Numan Siddique 2017-03-10 07:47:20 +05:30 committed by Ben Pfaff
parent 161ea2c871
commit 7cc0741ea2
5 changed files with 153 additions and 27 deletions

1
NEWS
View File

@ -14,6 +14,7 @@ Post-v2.7.0
"dot1q-tunnel" port VLAN mode.
- OVN:
* IPAM for IPv4 can now exclude user-defined addresses from assignment.
* IPAM can now assign IPv6 addresses.
* Make the DHCPv4 router setting optional.
* Gratuitous ARP for NAT addresses on a distributed logical router.
* Allow ovn-controller SSL configuration to be obtained from vswitchd

View File

@ -1038,6 +1038,26 @@ in6_addr_solicited_node(struct in6_addr *addr, const struct in6_addr *ip6)
memcpy(&addr->s6_addr[13], &ip6->s6_addr[13], 3);
}
/*
* Generates ipv6 EUI64 address from the given eth addr
* and prefix and stores it in 'lla'
*/
static inline void
in6_generate_eui64(struct eth_addr ea, struct in6_addr *prefix,
struct in6_addr *lla)
{
union ovs_16aligned_in6_addr *taddr = (void *) lla;
union ovs_16aligned_in6_addr *prefix_taddr = (void *) prefix;
taddr->be16[0] = prefix_taddr->be16[0];
taddr->be16[1] = prefix_taddr->be16[1];
taddr->be16[2] = prefix_taddr->be16[2];
taddr->be16[3] = prefix_taddr->be16[3];
taddr->be16[4] = htons(((ea.ea[0] ^ 0x02) << 8) | ea.ea[1]);
taddr->be16[5] = htons(ea.ea[2] << 8 | 0x00ff);
taddr->be16[6] = htons(0xfe << 8 | ea.ea[3]);
taddr->be16[7] = ea.be16[2];
}
/*
* Generates ipv6 link local address from the given eth addr
* with prefix 'fe80::/64' and stores it in 'lla'

View File

@ -377,6 +377,8 @@ struct ipam_info {
uint32_t start_ipv4;
size_t total_ipv4s;
unsigned long *allocated_ipv4s; /* A bitmap of allocated IPv4s */
bool ipv6_prefix_set;
struct in6_addr ipv6_prefix;
};
/* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
@ -509,6 +511,14 @@ init_ipam_info_for_datapath(struct ovn_datapath *od)
}
const char *subnet_str = smap_get(&od->nbs->other_config, "subnet");
const char *ipv6_prefix = smap_get(&od->nbs->other_config, "ipv6_prefix");
if (ipv6_prefix) {
od->ipam_info = xzalloc(sizeof *od->ipam_info);
od->ipam_info->ipv6_prefix_set = ipv6_parse(
ipv6_prefix, &od->ipam_info->ipv6_prefix);
}
if (!subnet_str) {
return;
}
@ -523,7 +533,9 @@ init_ipam_info_for_datapath(struct ovn_datapath *od)
return;
}
od->ipam_info = xzalloc(sizeof *od->ipam_info);
if (!od->ipam_info) {
od->ipam_info = xzalloc(sizeof *od->ipam_info);
}
od->ipam_info->start_ipv4 = ntohl(subnet) + 1;
od->ipam_info->total_ipv4s = ~ntohl(mask);
od->ipam_info->allocated_ipv4s =
@ -1022,42 +1034,59 @@ static bool
ipam_allocate_addresses(struct ovn_datapath *od, struct ovn_port *op,
const char *addrspec)
{
if (!od || !op || !op->nbsp) {
return false;
}
uint32_t ip = ipam_get_unused_ip(od);
if (!ip) {
if (!op->nbsp || !od->ipam_info) {
return false;
}
/* Get or generate MAC address. */
struct eth_addr mac;
bool check_mac;
bool dynamic_mac;
int n = 0;
if (ovs_scan(addrspec, ETH_ADDR_SCAN_FMT" dynamic%n",
ETH_ADDR_SCAN_ARGS(mac), &n)
&& addrspec[n] == '\0') {
check_mac = true;
dynamic_mac = false;
} else {
uint64_t mac64 = ipam_get_unused_mac();
if (!mac64) {
return false;
}
eth_addr_from_uint64(mac64, &mac);
check_mac = false;
dynamic_mac = true;
}
/* Add MAC to MACAM and IP to IPAM bitmap if both addresses were allocated
* successfully. */
ipam_insert_ip(od, ip);
ipam_insert_mac(&mac, check_mac);
/* Generate IPv4 address, if desirable. */
bool dynamic_ip4 = od->ipam_info->allocated_ipv4s != NULL;
uint32_t ip4 = dynamic_ip4 ? ipam_get_unused_ip(od) : 0;
char *new_addr = xasprintf(ETH_ADDR_FMT" "IP_FMT,
ETH_ADDR_ARGS(mac), IP_ARGS(htonl(ip)));
nbrec_logical_switch_port_set_dynamic_addresses(op->nbsp, new_addr);
free(new_addr);
/* Generate IPv6 address, if desirable. */
bool dynamic_ip6 = od->ipam_info->ipv6_prefix_set;
struct in6_addr ip6;
if (dynamic_ip6) {
in6_generate_eui64(mac, &od->ipam_info->ipv6_prefix, &ip6);
}
/* If we didn't generate anything, bail out. */
if (!dynamic_ip4 && !dynamic_ip6) {
return false;
}
/* Save the dynamic addresses. */
struct ds new_addr = DS_EMPTY_INITIALIZER;
ds_put_format(&new_addr, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
if (dynamic_ip4 && ip4) {
ipam_insert_ip(od, ip4);
ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(htonl(ip4)));
}
if (dynamic_ip6) {
char ip6_s[INET6_ADDRSTRLEN + 1];
ipv6_string_mapped(ip6_s, &ip6);
ds_put_format(&new_addr, " %s", ip6_s);
}
ipam_insert_mac(&mac, !dynamic_mac);
nbrec_logical_switch_port_set_dynamic_addresses(op->nbsp,
ds_cstr(&new_addr));
ds_destroy(&new_addr);
return true;
}
@ -1074,7 +1103,7 @@ build_ipam(struct hmap *datapaths, struct hmap *ports)
* ports that have the "dynamic" keyword in their addresses column. */
struct ovn_datapath *od;
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbs || !od->ipam_info || !od->ipam_info->allocated_ipv4s) {
if (!od->nbs || !od->ipam_info) {
continue;
}

View File

@ -137,12 +137,19 @@
<group title="IP Address Assignment">
<p>
These options control automatic IP address management (IPAM) for ports
attached to the logical switch. To enable IPAM, set <ref
attached to the logical switch. To enable IPAM for IPv4, set <ref
column="other_config" key="subnet"/> and optionally <ref
column="other_config:exclude_ips"/>. Then, to request dynamic address
assignment for a particular port, use the <code>dynamic</code> keyword
in the <ref table="Logical_Switch_Port" column="addresses"/> column of
the port's <ref table="Logical_Switch_Port"/> row.
column="other_config:exclude_ips"/>. To enable IPAM for IPv6, set
<ref column="other_config" key="ipv6_prefix"/>. IPv4 and IPv6 may
be enabled together or separately.
</p>
<p>
To request dynamic address assignment for a particular port, use the
<code>dynamic</code> keyword in the <ref table="Logical_Switch_Port"
column="addresses"/> column of the port's <ref
table="Logical_Switch_Port"/> row. This requests both an IPv4 and an
IPv6 address, if IPAM for IPv4 and IPv6 are both enabled.
</p>
<column name="other_config" key="subnet">
@ -172,6 +179,23 @@
<li><code>192.168.0.110..192.168.0.120 192.168.0.25..192.168.0.30 192.168.0.144</code></li>
</ul>
</column>
<column name="other_config" key="ipv6_prefix">
Set this to an IPv6 prefix to enable <code>ovn-northd</code> to
automatically assign IPv6 addresses using this prefix. The assigned
IPv6 address will be generated using the IPv6 prefix and the MAC
address (converted to an IEEE EUI64 identifier) of the port. The IPv6
prefix defined here should be a valid IPv6 address ending with
<code>::</code>.
<p>
Examples:
</p>
<ul>
<li><code>aef0::</code></li>
<li><code>bef0:1234:a890:5678::</code></li>
<li><code>8230:5678::</code></li>
</ul>
</column>
</group>
<group title="Common Columns">

View File

@ -4869,14 +4869,14 @@ AT_CHECK([ovn-nbctl get Logical-Switch-Port p27 dynamic_addresses], [0],
ovn-nbctl --wait=sb lsp-add sw2 p28 -- lsp-set-addresses p28 dynamic
AT_CHECK([ovn-nbctl get Logical-Switch-Port p28 dynamic_addresses], [0],
[[[]]
["0a:00:00:00:00:1e"
])
# Test that address management does not add duplicate MAC for lsp/lrp peers.
ovn-nbctl create Logical_Router name=R2
ovn-nbctl ls-add sw3
ovn-nbctl lsp-add sw3 p29 -- lsp-set-addresses p29 \
"0a:00:00:00:00:1e"
"0a:00:00:00:00:1f"
ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw3 \
network="192.168.2.1/24" mac=\"0a:00:00:00:00:1f\" \
-- add Logical_Router R2 ports @lrp -- lsp-add sw3 rp-sw3 \
@ -4961,6 +4961,58 @@ AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0],
[[[]]
])
# Set IPv6 prefix
ovn-nbctl --wait=sb set Logical-switch sw0 other_config:ipv6_prefix="aef0::"
ovn-nbctl --wait=sb lsp-add sw0 p37 -- lsp-set-addresses p37 \
"dynamic"
# With prefix aef0 and mac 0a:00:00:00:00:26, the dynamic IPv6 should be
# - aef0::800:ff:fe00:26 (EUI64)
AT_CHECK([ovn-nbctl get Logical-Switch-Port p37 dynamic_addresses], [0],
["0a:00:00:00:00:26 192.168.1.21 aef0::800:ff:fe00:26"
])
ovn-nbctl --wait=sb ls-add sw4
ovn-nbctl --wait=sb set Logical-switch sw4 other_config:ipv6_prefix="bef0::"
ovn-nbctl --wait=sb lsp-add sw4 p38 -- lsp-set-addresses p38 \
"dynamic"
AT_CHECK([ovn-nbctl get Logical-Switch-Port p38 dynamic_addresses], [0],
["0a:00:00:00:00:27 bef0::800:ff:fe00:27"
])
ovn-nbctl --wait=sb lsp-add sw4 p39 -- lsp-set-addresses p39 \
"f0:00:00:00:10:12 dynamic"
AT_CHECK([ovn-nbctl get Logical-Switch-Port p39 dynamic_addresses], [0],
["f0:00:00:00:10:12 bef0::f200:ff:fe00:1012"
])
# Clear the other_config for sw4. No dynamic ip should be assigned.
ovn-nbctl --wait=sb clear Logical-switch sw4 other_config
ovn-nbctl --wait=sb lsp-add sw4 p40 -- lsp-set-addresses p40 \
"dynamic"
AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0],
[[[]]
])
# Test the case where IPv4 addresses are exhausted and IPv6 prefix is set
ovn-nbctl --wait=sb set Logical-switch sw4 other_config:subnet=192.168.2.0/30 \
-- set Logical-switch sw4 other_config:ipv6_prefix="bef0::"
# Now p40 should be assigned with dynamic addresses.
AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0],
["0a:00:00:00:00:28 192.168.2.2 bef0::800:ff:fe00:28"
])
ovn-nbctl --wait=sb lsp-add sw4 p41 -- lsp-set-addresses p41 \
"dynamic"
# p41 should not have IPv4 address (as the pool is exhausted).
AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
["0a:00:00:00:00:29 bef0::800:ff:fe00:29"
])
as ovn-sb
OVS_APP_EXIT_AND_WAIT([ovsdb-server])