Skip to content

Commit

Permalink
ovn-northd ipam: Support IPv6 dynamic assignment
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
numansiddique and blp committed Apr 14, 2017
1 parent 161ea2c commit 7cc0741
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 27 deletions.
1 change: 1 addition & 0 deletions NEWS
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions lib/packets.h
Expand Up @@ -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'
Expand Down
69 changes: 49 additions & 20 deletions ovn/northd/ovn-northd.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 =
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down
34 changes: 29 additions & 5 deletions ovn/ovn-nb.xml
Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand Down
56 changes: 54 additions & 2 deletions tests/ovn.at
Expand Up @@ -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 \
Expand Down Expand Up @@ -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])

Expand Down

0 comments on commit 7cc0741

Please sign in to comment.