If a logical port has two ipv4 addresses and one ipv6 address it will be stored as ["MAC IPv41 IPv42 IPv61"] instead of ["MAC IPv41", "MAC IPv42", "MAC IPv61"].
Signed-off-by: Numan Siddique <nusid...@redhat.com> --- lib/packets.c | 102 ++++++++++++++++++++++++++++++++++ lib/packets.h | 10 ++++ ovn/northd/ovn-northd.c | 142 ++++++++++++++++++++++++++++++++++++++++++------ ovn/ovn-nb.xml | 58 +++++++++++++++----- 4 files changed, 280 insertions(+), 32 deletions(-) diff --git a/lib/packets.c b/lib/packets.c index d82341d..b338846 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -455,6 +455,34 @@ ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask) return NULL; } +/* + * This function is similar to ip_parse_masked(), with an extra parameter + * `n` added to return the number of scanned characters. + */ +char * OVS_WARN_UNUSED_RESULT +ip_parse_masked_len(const char *s, int *n, ovs_be32 *ip, + ovs_be32 *mask) +{ + int prefix; + + if (ovs_scan_len(s, n, IP_SCAN_FMT"/"IP_SCAN_FMT, + IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask))) { + /* OK. */ + } else if (ovs_scan_len(s, n, IP_SCAN_FMT"/%d", + IP_SCAN_ARGS(ip), &prefix)) { + if (prefix <= 0 || prefix > 32) { + return xasprintf("%s: network prefix bits not between 0 and " + "32", s); + } + *mask = be32_prefix_mask(prefix); + } else if (ovs_scan_len(s, n, IP_SCAN_FMT, IP_SCAN_ARGS(ip))) { + *mask = OVS_BE32_MAX; + } else { + return xasprintf("%s: invalid IP address", s); + } + return NULL; +} + /* Similar to ip_parse_masked(), but the mask, if present, must be a CIDR mask * and is returned as a prefix length in '*plen'. */ char * OVS_WARN_UNUSED_RESULT @@ -475,6 +503,26 @@ ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen) return NULL; } +/* Similar to ip_parse_cidr(), with an extra parameter `n` added to return + * the number of scanned characters. */ +char * OVS_WARN_UNUSED_RESULT +ip_parse_cidr_len(const char *s, int *n, ovs_be32 *ip, unsigned int *plen) +{ + ovs_be32 mask; + char *error; + + error = ip_parse_masked_len(s, n, ip, &mask); + if (error) { + return error; + } + + if (!ip_is_cidr(mask)) { + return xasprintf("%s: CIDR network required", s); + } + *plen = ip_count_cidr_bits(mask); + return NULL; +} + /* Parses string 's', which must be an IPv6 address. Stores the IPv6 address * into '*ip'. Returns true if successful, otherwise false. */ bool @@ -519,6 +567,39 @@ ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask) return xasprintf("%s: invalid IPv6 address", s); } +/* + * This function is similar to ipv6_parse_masked(), with an extra parameter + * `n` added to return the number of scanned characters. + */ +char * OVS_WARN_UNUSED_RESULT +ipv6_parse_masked_len(const char *s, int *n, struct in6_addr *ip, + struct in6_addr *mask) +{ + char ipv6_s[IPV6_SCAN_LEN + 1]; + int prefix; + + if (ovs_scan_len(s, n, " "IPV6_SCAN_FMT, ipv6_s) + && ipv6_parse(ipv6_s, ip)) { + if (ovs_scan_len(s, n, "/%d", &prefix)) { + if (prefix <= 0 || prefix > 128) { + return xasprintf("%s: IPv6 network prefix bits not between 0 " + "and 128", s); + } + *mask = ipv6_create_mask(prefix); + } else if (ovs_scan_len(s, n, "/"IPV6_SCAN_FMT, ipv6_s)) { + if (!ipv6_parse(ipv6_s, mask)) { + return xasprintf("%s: Invalid IPv6 mask", s); + } + /* OK. */ + } else { + /* OK. No mask */ + *mask = in6addr_exact; + } + return NULL; + } + return xasprintf("%s: invalid IPv6 address", s); +} + /* Similar to ipv6_parse_masked(), but the mask, if present, must be a CIDR * mask and is returned as a prefix length in '*plen'. */ char * OVS_WARN_UNUSED_RESULT @@ -539,6 +620,27 @@ ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen) return NULL; } +/* Similar to ipv6_parse_cidr(), with an extra parameter `n` added to return + * the number of scanned characters. */ +char * OVS_WARN_UNUSED_RESULT +ipv6_parse_cidr_len(const char *s, int *n, struct in6_addr *ip, + unsigned int *plen) +{ + struct in6_addr mask; + char *error; + + error = ipv6_parse_masked_len(s, n, ip, &mask); + if (error) { + return error; + } + + if (!ipv6_is_cidr(&mask)) { + return xasprintf("%s: IPv6 CIDR network required", s); + } + *plen = ipv6_count_cidr_bits(&mask); + return NULL; +} + /* Stores the string representation of the IPv6 address 'addr' into the * character array 'addr_str', which must be at least INET6_ADDRSTRLEN * bytes long. */ diff --git a/lib/packets.h b/lib/packets.h index 834e8a4..31a4e92 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -586,6 +586,11 @@ char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask) OVS_WARN_UNUSED_RESULT; char *ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen) OVS_WARN_UNUSED_RESULT; +char *ip_parse_masked_len(const char *s, int *n, ovs_be32 *ip, ovs_be32 *mask) + OVS_WARN_UNUSED_RESULT; +char *ip_parse_cidr_len(const char *s, int *n, ovs_be32 *ip, + unsigned int *plen) + OVS_WARN_UNUSED_RESULT; #define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4) #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15) @@ -1033,6 +1038,11 @@ char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask); char *ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen) OVS_WARN_UNUSED_RESULT; +char *ipv6_parse_masked_len(const char *s, int *n, struct in6_addr *ipv6, + struct in6_addr *mask); +char *ipv6_parse_cidr_len(const char *s, int *n, struct in6_addr *ip, + unsigned int *plen) + OVS_WARN_UNUSED_RESULT; void *eth_compose(struct dp_packet *, const struct eth_addr eth_dst, const struct eth_addr eth_src, uint16_t eth_type, diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 4f03287..f20f3b1 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -914,6 +914,96 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow) } } +struct ipv4_netaddr { + ovs_be32 addr; + unsigned int plen; +}; + +struct ipv6_netaddr { + struct in6_addr addr; + unsigned int plen; +}; + +struct lport_addresses { + struct eth_addr ea; + size_t n_ipv4_addrs; + struct ipv4_netaddr *ipv4_addrs; + size_t n_ipv6_addrs; + struct ipv6_netaddr *ipv6_addrs; +}; + +/* + * Extracts the mac, ipv4 and ipv6 addresses from the input + * param 'address' which should be of the format + * 'MAC [IPV41 IPV42 ...IPV61 IPV62 .." + */ +static struct lport_addresses * +extract_lport_addresses(char *address) +{ + struct lport_addresses *laddrs; + int n = 0; + char *p = address; + laddrs = xzalloc(sizeof (*laddrs)); + if (!ovs_scan_len(p, &n, ETH_ADDR_SCAN_FMT, + ETH_ADDR_SCAN_ARGS(laddrs->ea))) { + free(laddrs); + return NULL; + } + + ovs_be32 ip4; + unsigned int plen; + int n1 = n; + int i = 0; + char *error; + while (!(error = ip_parse_cidr_len(p, &n1, &ip4, &plen))) { + laddrs->n_ipv4_addrs++; + } + + if (error) { + free(error); + } + + struct in6_addr ip6; + while (!(error = ipv6_parse_cidr_len(p, &n1, &ip6, &plen))) { + laddrs->n_ipv6_addrs++; + } + + if (error) { + free(error); + } + + n1 = n; + if (laddrs->n_ipv4_addrs) { + laddrs->ipv4_addrs = xcalloc(laddrs->n_ipv4_addrs, + sizeof (struct ipv4_netaddr)); + i = 0; + while (!(error = ip_parse_cidr_len(p, &n1, &laddrs->ipv4_addrs[i].addr, + &laddrs->ipv4_addrs[i].plen))) { + i++; + } + + if (error) { + free(error); + } + } + + if (laddrs->n_ipv6_addrs) { + laddrs->ipv6_addrs = xcalloc(laddrs->n_ipv6_addrs, + sizeof (struct ipv6_netaddr)); + i = 0; + while (!(error = ipv6_parse_cidr_len( + p, &n1, &laddrs->ipv6_addrs[i].addr, + &laddrs->ipv6_addrs[i].plen))) { + i++; + } + + if (error) { + free(error); + } + } + return laddrs; +} + /* Appends port security constraints on L2 address field 'eth_addr_field' * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with * 'n_port_security' elements, is the collection of port_security constraints @@ -1194,14 +1284,15 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, } for (size_t i = 0; i < op->nbs->n_addresses; i++) { - struct eth_addr ea; - ovs_be32 ip; - - if (ovs_scan(op->nbs->addresses[i], - ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT, - ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) { + struct lport_addresses *laddrs = extract_lport_addresses( + op->nbs->addresses[i]); + if (!laddrs) { + continue; + } + for (size_t j = 0; j < laddrs->n_ipv4_addrs; j++) { char *match = xasprintf( - "arp.tpa == "IP_FMT" && arp.op == 1", IP_ARGS(ip)); + "arp.tpa == "IP_FMT" && arp.op == 1", + IP_ARGS(laddrs->ipv4_addrs[j].addr)); char *actions = xasprintf( "eth.dst = eth.src; " "eth.src = "ETH_ADDR_FMT"; " @@ -1213,14 +1304,22 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, "outport = inport; " "inport = \"\"; /* Allow sending out inport. */ " "output;", - ETH_ADDR_ARGS(ea), - ETH_ADDR_ARGS(ea), - IP_ARGS(ip)); + ETH_ADDR_ARGS(laddrs->ea), + ETH_ADDR_ARGS(laddrs->ea), + IP_ARGS(laddrs->ipv4_addrs[j].addr)); ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 150, match, actions); free(match); free(actions); } + + if (laddrs->ipv4_addrs) { + free(laddrs->ipv4_addrs); + } + if (laddrs->ipv6_addrs) { + free(laddrs->ipv6_addrs); + } + free(laddrs); } } @@ -1541,12 +1640,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, /* XXX ARP for neighboring router */ } else if (op->od->n_router_ports) { for (size_t i = 0; i < op->nbs->n_addresses; i++) { - struct eth_addr ea; - ovs_be32 ip; - - if (ovs_scan(op->nbs->addresses[i], - ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT, - ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) { + struct lport_addresses *laddrs = extract_lport_addresses( + op->nbs->addresses[i]); + if (!laddrs) { + continue; + } + for (size_t k = 0; k < laddrs->n_ipv4_addrs; k++) { + ovs_be32 ip = laddrs->ipv4_addrs[k].addr; for (size_t j = 0; j < op->od->n_router_ports; j++) { /* Get the Logical_Router_Port that the Logical_Port is * connected to, as 'peer'. */ @@ -1574,7 +1674,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "outport = %s; " "output;", ETH_ADDR_ARGS(peer->mac), - ETH_ADDR_ARGS(ea), + ETH_ADDR_ARGS(laddrs->ea), peer->json_key); ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP, 200, match, actions); @@ -1583,6 +1683,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, break; } } + + if (laddrs->ipv4_addrs) { + free(laddrs->ipv4_addrs); + } + if (laddrs->ipv6_addrs) { + free(laddrs->ipv6_addrs); + } + free(laddrs); } } } diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 4e414ce..b4768d0 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -254,12 +254,12 @@ </p> <dl> - <dt><code><var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var></code></dt> + <dt><code>Ethernet address followed by zero or more IPv4 or IPv6 addresses (or both)</code></dt> <dd> <p> - An Ethernet address owned by the logical port. Like a physical - Ethernet NIC, a logical port ordinarily has a single fixed - Ethernet address. + An Ethernet address defined is owned by the logical port. + Like a physical Ethernet NIC, a logical port ordinarily has + a single fixed Ethernet address. </p> <p> @@ -269,27 +269,55 @@ if a MAC learning process had learned that MAC address on the port. </p> - </dd> - <dt><code><var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var> <var>a</var>.<var>b</var>.<var>c</var>.<var>d</var></code></dt> - <dd> <p> - This form has all the effects of the previous form. It also - indicates that the logical port owns the given IPv4 address. + If IPv4 or IPv6 address(es) (or both) are defined, it indicates + that the logical port owns the given IP addresses. </p> <p> - The OVN logical switch uses this information to synthesize - responses to ARP requests without traversing the physical - network. The OVN logical router connected to the logical switch, - if any, uses this information to avoid issuing ARP requests for - logical switch ports. + If IPv4 address(es) are defined, the OVN logical switch uses this + information to synthesize responses to ARP requests without + traversing the physical network. The OVN logical router connected + to the logical switch, if any, uses this information to avoid + issuing ARP requests for logical switch ports. </p> <p> Note that the order here is important. The Ethernet address must - be listed before the IP address. + be listed before the IP address(es) if defined. If both IPv4 and + IPv6 addresses are defined, then IPv4 addresses should be listed + before IPv6 address(es). + </p> + + <p> + Examples: </p> + + <dl> + <dt><code>80:fa:5b:06:72:b7</code></dt> + <dd> + This indicates that the logical port owns the above mac address. + </dd> + + <dt><code>80:fa:5b:06:72:b7 10.0.0.4 20.0.0.4</code></dt> + <dd> + This indicates that the logical port owns the mac address and two + IPv4 addresses. + </dd> + + <dt><code>80:fa:5b:06:72:b7 fdaa:15f2:72cf:0:f816:3eff:fe20:3f41</code></dt> + <dd> + This indicates that the logical port owns the mac address and + 1IPv6 address. + </dd> + + <dt><code>80:fa:5b:06:72:b7 10.0.0.4 fdaa:15f2:72cf:0:f816:3eff:fe20:3f41</code></dt> + <dd> + This indicates that the logical port owns the mac address and + 1 IPv4 address and 1 IPv6 address. + </dd> + </dl> </dd> <dt><code>unknown</code></dt> -- 2.5.0 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev