Skip to content

Commit

Permalink
datapath: Add support for IPv6 tunnels.
Browse files Browse the repository at this point in the history
Mostly backports upstream commit along with other pieces
to make IPv6 tunneling work.

    commit 6b26ba3a7d952e611dcde1f3f77ce63bcc70540a
    Author: Jiri Benc <[email protected]>

    openvswitch: netlink attributes for IPv6 tunneling

    Add netlink attributes for IPv6 tunnel addresses. This enables IPv6 support
    for tunnels.

    Signed-off-by: Jiri Benc <[email protected]>
    Acked-by: Pravin B Shelar <[email protected]>
    Acked-by: Thomas Graf <[email protected]>
    Signed-off-by: David S. Miller <[email protected]>

Signed-off-by: Pravin B Shelar <[email protected]>
Acked-by: Jesse Gross <[email protected]>
  • Loading branch information
pshelar committed Jul 9, 2016
1 parent f2252c6 commit 8a2d490
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 45 deletions.
3 changes: 3 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ Tunnel - STT | NO | YES | NO | YES |
Tunnel - GRE | 3.11 | YES | YES | YES |
Tunnel - VXLAN | 3.12 | YES | YES | YES |
Tunnel - Geneve | 3.18 | YES | YES | YES |
Tunnel - GRE-IPv6 | NO | NO | YES | NO |
Tunnel - VXLAN-IPv6 | 4.3 | YES | YES | NO |
Tunnel - Geneve-IPv6 | 4.4 | YES | YES | NO |
QoS - Policing | YES | YES | NO | NO |
QoS - Shaping | YES | YES | NO | NO |
sFlow | YES | YES | YES | NO |
Expand Down
2 changes: 1 addition & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Post-v2.5.0
- Tunnels:
* Flow based tunnel match and action can be used for IPv6 address using
tun_ipv6_src, tun_ipv6_dst fields.
* Added support for IPv6 tunnels to native tunneling.
* Added support for IPv6 tunnels, for details checkout FAQ.
- A wrapper script, 'ovs-tcpdump', to easily port-mirror an OVS port and
watch with tcpdump
- Introduce --no-self-confinement flag that allows daemons to work with
Expand Down
7 changes: 3 additions & 4 deletions datapath/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,9 +689,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
{
/* Extract metadata from packet. */
if (tun_info) {
if (ip_tunnel_info_af(tun_info) != AF_INET)
return -EINVAL;

key->tun_proto = ip_tunnel_info_af(tun_info);
memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key));
BUILD_BUG_ON(((1 << (sizeof(tun_info->options_len) * 8)) - 1) >
sizeof(key->tun_opts));
Expand All @@ -703,7 +701,8 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
} else {
key->tun_opts_len = 0;
}
} else {
} else {
key->tun_proto = 0;
key->tun_opts_len = 0;
memset(&key->tun_key, 0, sizeof(key->tun_key));
}
Expand Down
1 change: 1 addition & 0 deletions datapath/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct sw_flow_key {
u32 skb_mark; /* SKB mark. */
u16 in_port; /* Input switch port (or DP_MAX_PORTS). */
} __packed phy; /* Safe when right after 'tun_key'. */
u8 tun_proto; /* Protocol of encapsulating tunnel. */
u32 ovs_flow_hash; /* Datapath computed hash value. */
u32 recirc_id; /* Recirculation ID. */
struct {
Expand Down
130 changes: 90 additions & 40 deletions datapath/flow_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,9 @@ size_t ovs_tun_key_attr_size(void)
/* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider
* updating this function.
*/
return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
+ nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+ nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
+ nla_total_size(16) /* OVS_TUNNEL_KEY_ATTR_IPV[46]_SRC */
+ nla_total_size(16) /* OVS_TUNNEL_KEY_ATTR_IPV[46]_DST */
+ nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
+ nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
Expand Down Expand Up @@ -325,6 +325,8 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = OVS_ATTR_VARIABLE },
[OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = OVS_ATTR_NESTED,
.next = ovs_vxlan_ext_key_lens },
[OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) },
[OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) },
};

/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
Expand Down Expand Up @@ -544,15 +546,15 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
return 0;
}

static int ipv4_tun_from_nlattr(const struct nlattr *attr,
struct sw_flow_match *match, bool is_mask,
bool log)
static int ip_tun_from_nlattr(const struct nlattr *attr,
struct sw_flow_match *match, bool is_mask,
bool log)
{
struct nlattr *a;
int rem;
bool ttl = false;
bool ttl = false, ipv4 = false, ipv6 = false;
__be16 tun_flags = 0;
int opts_type = 0;
struct nlattr *a;
int rem;

nla_for_each_nested(a, attr, rem) {
int type = nla_type(a);
Expand Down Expand Up @@ -580,10 +582,22 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.src,
nla_get_in_addr(a), is_mask);
ipv4 = true;
break;
case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.dst,
nla_get_in_addr(a), is_mask);
ipv4 = true;
break;
case OVS_TUNNEL_KEY_ATTR_IPV6_SRC:
SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst,
nla_get_in6_addr(a), is_mask);
ipv6 = true;
break;
case OVS_TUNNEL_KEY_ATTR_IPV6_DST:
SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst,
nla_get_in6_addr(a), is_mask);
ipv6 = true;
break;
case OVS_TUNNEL_KEY_ATTR_TOS:
SW_FLOW_KEY_PUT(match, tun_key.tos,
Expand Down Expand Up @@ -638,28 +652,46 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
opts_type = type;
break;
default:
OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d",
OVS_NLERR(log, "Unknown IP tunnel attribute %d",
type);
return -EINVAL;
}
}

SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
if (is_mask)
SW_FLOW_KEY_MEMSET_FIELD(match, tun_proto, 0xff, true);
else
SW_FLOW_KEY_PUT(match, tun_proto, ipv6 ? AF_INET6 : AF_INET,
false);

if (rem > 0) {
OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.",
OVS_NLERR(log, "IP tunnel attribute has %d unknown bytes.",
rem);
return -EINVAL;
}

if (ipv4 && ipv6) {
OVS_NLERR(log, "Mixed IPv4 and IPv6 tunnel attributes");
return -EINVAL;
}

if (!is_mask) {
if (!match->key->tun_key.u.ipv4.dst) {
if (!ipv4 && !ipv6) {
OVS_NLERR(log, "IP tunnel dst address not specified");
return -EINVAL;
}
if (ipv4 && !match->key->tun_key.u.ipv4.dst) {
OVS_NLERR(log, "IPv4 tunnel dst address is zero");
return -EINVAL;
}
if (ipv6 && ipv6_addr_any(&match->key->tun_key.u.ipv6.dst)) {
OVS_NLERR(log, "IPv6 tunnel dst address is zero");
return -EINVAL;
}

if (!ttl) {
OVS_NLERR(log, "IPv4 tunnel TTL not specified.");
OVS_NLERR(log, "IP tunnel TTL not specified.");
return -EINVAL;
}
}
Expand All @@ -684,21 +716,36 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
return 0;
}

static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
const struct ip_tunnel_key *output,
const void *tun_opts, int swkey_tun_opts_len)
static int __ip_tun_to_nlattr(struct sk_buff *skb,
const struct ip_tunnel_key *output,
const void *tun_opts, int swkey_tun_opts_len,
unsigned short tun_proto)
{
if (output->tun_flags & TUNNEL_KEY &&
nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
return -EMSGSIZE;
if (output->u.ipv4.src &&
nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
output->u.ipv4.src))
return -EMSGSIZE;
if (output->u.ipv4.dst &&
nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
output->u.ipv4.dst))
return -EMSGSIZE;
switch (tun_proto) {
case AF_INET:
if (output->u.ipv4.src &&
nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
output->u.ipv4.src))
return -EMSGSIZE;
if (output->u.ipv4.dst &&
nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
output->u.ipv4.dst))
return -EMSGSIZE;
break;
case AF_INET6:
if (!ipv6_addr_any(&output->u.ipv6.src) &&
nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_SRC,
&output->u.ipv6.src))
return -EMSGSIZE;
if (!ipv6_addr_any(&output->u.ipv6.dst) &&
nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_DST,
&output->u.ipv6.dst))
return -EMSGSIZE;
break;
}
if (output->tos &&
nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->tos))
return -EMSGSIZE;
Expand Down Expand Up @@ -732,9 +779,10 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
return 0;
}

static int ipv4_tun_to_nlattr(struct sk_buff *skb,
const struct ip_tunnel_key *output,
const void *tun_opts, int swkey_tun_opts_len)
static int ip_tun_to_nlattr(struct sk_buff *skb,
const struct ip_tunnel_key *output,
const void *tun_opts, int swkey_tun_opts_len,
unsigned short tun_proto)
{
struct nlattr *nla;
int err;
Expand All @@ -743,7 +791,8 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
if (!nla)
return -EMSGSIZE;

err = __ipv4_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len);
err = __ip_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len,
tun_proto);
if (err)
return err;

Expand All @@ -754,9 +803,10 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
int ovs_nla_put_tunnel_info(struct sk_buff *skb,
struct ip_tunnel_info *tun_info)
{
return __ipv4_tun_to_nlattr(skb, &tun_info->key,
ip_tunnel_info_opts(tun_info),
tun_info->options_len);
return __ip_tun_to_nlattr(skb, &tun_info->key,
ip_tunnel_info_opts(tun_info),
tun_info->options_len,
ip_tunnel_info_af(tun_info));
}

static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
Expand Down Expand Up @@ -807,7 +857,7 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
*attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK);
}
if (*attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
if (ip_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
is_mask, log) < 0)
return -EINVAL;
*attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
Expand Down Expand Up @@ -944,8 +994,7 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
}

if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) {
OVS_NLERR(log,
"Invalid IPv6 flow label value (value=%x, max=%x).",
OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x).\n",
ntohl(ipv6_key->ipv6_label), (1 << 20) - 1);
return -EINVAL;
}
Expand Down Expand Up @@ -1379,14 +1428,14 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
goto nla_put_failure;

if ((swkey->tun_key.u.ipv4.dst || is_mask)) {
if ((swkey->tun_proto || is_mask)) {
const void *opts = NULL;

if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT)
opts = TUN_METADATA_OPTS(output, swkey->tun_opts_len);

if (ipv4_tun_to_nlattr(skb, &output->tun_key, opts,
swkey->tun_opts_len))
if (ip_tun_to_nlattr(skb, &output->tun_key, opts,
swkey->tun_opts_len, swkey->tun_proto))
goto nla_put_failure;
}

Expand Down Expand Up @@ -1684,8 +1733,7 @@ static void __ovs_nla_free_flow_actions(struct rcu_head *head)
}

/* Schedules 'sf_acts' to be freed after the next RCU grace period.
* The caller must hold rcu_read_lock for this to be sensible.
*/
* The caller must hold rcu_read_lock for this to be sensible. */
void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *sf_acts)
{
call_rcu(&sf_acts->rcu, __ovs_nla_free_flow_actions);
Expand Down Expand Up @@ -1892,7 +1940,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
int err = 0, start, opts_type;

ovs_match_init(&match, &key, NULL);
opts_type = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log);
opts_type = ip_tun_from_nlattr(nla_data(attr), &match, false, log);
if (opts_type < 0)
return opts_type;

Expand Down Expand Up @@ -1928,6 +1976,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,

tun_info = &tun_dst->u.tun_info;
tun_info->mode = IP_TUNNEL_INFO_TX;
if (key.tun_proto == AF_INET6)
tun_info->mode |= IP_TUNNEL_INFO_IPV6;
tun_info->key = key.tun_key;

/* We need to store the options in the action itself since
Expand Down

0 comments on commit 8a2d490

Please sign in to comment.