Skip to content

Commit

Permalink
datapath: Add support for Geneve tunneling.
Browse files Browse the repository at this point in the history
This adds support for Geneve - Generic Network Virtualization
Encapsulation. The protocol is documented at
http://tools.ietf.org/html/draft-gross-geneve-00

The kernel implementation is completely agnostic to the options
that are in use and can handle newly defined options without
further work. It does this by simply matching on a byte array
of options and allowing userspace to setup flows on this array.

Userspace currently implements only support for basic version of
Geneve. It can work with the base header (including the VNI) and
is capable of parsing options but does not currently support any
particular option definitions. Over time, the intention is to
allow options to be matched through OpenFlow without requiring
explicit support in OVS userspace.

Signed-off-by: Jesse Gross <[email protected]>
Acked-by: Thomas Graf <[email protected]>
Acked-by: Pravin B Shelar <[email protected]>
  • Loading branch information
jessegross committed Jun 20, 2014
1 parent 1d2a1b5 commit c1fc141
Show file tree
Hide file tree
Showing 25 changed files with 768 additions and 43 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ Post-v2.3.0
- The "learn" action supports a new flag "delete_learned" that causes
the learned flows to be deleted when the flow with the "learn" action
is deleted.
- Basic support for the Geneve tunneling protocol. It is not yet
possible to generate or match options. This is planned for a future
release. The protocol is documented at
http://tools.ietf.org/html/draft-gross-geneve-00


v2.3.0 - xx xxx xxxx
Expand Down
1 change: 1 addition & 0 deletions datapath/Modules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ openvswitch_sources = \
flow_netlink.c \
flow_table.c \
vport.c \
vport-geneve.c \
vport-gre.c \
vport-internal_dev.c \
vport-lisp.c \
Expand Down
32 changes: 19 additions & 13 deletions datapath/datapath.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ static size_t key_attr_size(void)
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
+ nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
+ nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
+ nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */
Expand Down Expand Up @@ -488,7 +489,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
upcall->dp_ifindex = dp_ifindex;

nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
err = ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb);
err = ovs_nla_put_flow(dp, upcall_info->key,
upcall_info->key, user_skb);
BUG_ON(err);
nla_nest_end(user_skb, nla);

Expand Down Expand Up @@ -696,7 +698,8 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
}

/* Called with ovs_mutex or RCU read lock. */
static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
static int ovs_flow_cmd_fill_info(struct datapath *dp,
const struct sw_flow *flow, int dp_ifindex,
struct sk_buff *skb, u32 portid,
u32 seq, u32 flags, u8 cmd)
{
Expand All @@ -720,7 +723,8 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
if (!nla)
goto nla_put_failure;

err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb);
err = ovs_nla_put_flow(dp, &flow->unmasked_key,
&flow->unmasked_key, skb);
if (err)
goto error;
nla_nest_end(skb, nla);
Expand All @@ -729,7 +733,7 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
if (!nla)
goto nla_put_failure;

err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb);
err = ovs_nla_put_flow(dp, &flow->key, &flow->mask->key, skb);
if (err)
goto error;

Expand Down Expand Up @@ -806,7 +810,8 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *act
}

/* Called with ovs_mutex. */
static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
static struct sk_buff *ovs_flow_cmd_build_info(struct datapath *dp,
const struct sw_flow *flow,
int dp_ifindex,
struct genl_info *info, u8 cmd,
bool always)
Expand All @@ -819,7 +824,7 @@ static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
if (!skb || IS_ERR(skb))
return skb;

retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
retval = ovs_flow_cmd_fill_info(dp, flow, dp_ifindex, skb,
info->snd_portid, info->snd_seq, 0,
cmd);
BUG_ON(retval < 0);
Expand Down Expand Up @@ -900,7 +905,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
}

if (unlikely(reply)) {
error = ovs_flow_cmd_fill_info(new_flow,
error = ovs_flow_cmd_fill_info(dp, new_flow,
ovs_header->dp_ifindex,
reply, info->snd_portid,
info->snd_seq, 0,
Expand Down Expand Up @@ -932,7 +937,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
rcu_assign_pointer(flow->sf_acts, acts);

if (unlikely(reply)) {
error = ovs_flow_cmd_fill_info(flow,
error = ovs_flow_cmd_fill_info(dp, flow,
ovs_header->dp_ifindex,
reply, info->snd_portid,
info->snd_seq, 0,
Expand Down Expand Up @@ -1048,7 +1053,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
rcu_assign_pointer(flow->sf_acts, acts);

if (unlikely(reply)) {
error = ovs_flow_cmd_fill_info(flow,
error = ovs_flow_cmd_fill_info(dp, flow,
ovs_header->dp_ifindex,
reply, info->snd_portid,
info->snd_seq, 0,
Expand All @@ -1057,7 +1062,8 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
}
} else {
/* Could not alloc without acts before locking. */
reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
reply = ovs_flow_cmd_build_info(dp, flow,
ovs_header->dp_ifindex,
info, OVS_FLOW_CMD_NEW, false);
if (unlikely(IS_ERR(reply))) {
error = PTR_ERR(reply);
Expand Down Expand Up @@ -1119,7 +1125,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto unlock;
}

reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info,
reply = ovs_flow_cmd_build_info(dp, flow, ovs_header->dp_ifindex, info,
OVS_FLOW_CMD_NEW, true);
if (IS_ERR(reply)) {
err = PTR_ERR(reply);
Expand Down Expand Up @@ -1176,7 +1182,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
if (likely(reply)) {
if (likely(!IS_ERR(reply))) {
rcu_read_lock(); /* Keep RCU checker happy. */
err = ovs_flow_cmd_fill_info(flow,
err = ovs_flow_cmd_fill_info(dp, flow,
ovs_header->dp_ifindex,
reply, info->snd_portid,
info->snd_seq, 0,
Expand Down Expand Up @@ -1222,7 +1228,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
if (!flow)
break;

if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb,
if (ovs_flow_cmd_fill_info(dp, flow, ovs_header->dp_ifindex, skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
OVS_FLOW_CMD_NEW) < 0)
Expand Down
10 changes: 10 additions & 0 deletions datapath/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,17 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
struct ovs_tunnel_info *tun_info = OVS_CB(skb)->tun_info;
memcpy(&key->tun_key, &tun_info->tunnel,
sizeof(key->tun_key));
if (tun_info->options) {
BUILD_BUG_ON((1 << (sizeof(tun_info->options_len) * 8)) - 1
> sizeof(key->tun_opts));
memcpy(GENEVE_OPTS(key, tun_info->options_len),
tun_info->options, tun_info->options_len);
key->tun_opts_len = tun_info->options_len;
} else {
key->tun_opts_len = 0;
}
} else {
key->tun_opts_len = 0;
memset(&key->tun_key, 0, sizeof(key->tun_key));
}

Expand Down
20 changes: 19 additions & 1 deletion datapath/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,24 @@ struct ovs_key_ipv4_tunnel {

struct ovs_tunnel_info {
struct ovs_key_ipv4_tunnel tunnel;
struct geneve_opt *options;
u8 options_len;
};

/* Store options at the end of the array if they are less than the
* maximum size. This allows us to get the benefits of variable length
* matching for small options.
*/
#define GENEVE_OPTS(flow_key, opt_len) (struct geneve_opt *) \
((flow_key)->tun_opts + \
FIELD_SIZEOF(struct sw_flow_key, tun_opts) - \
opt_len)

static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
const struct iphdr *iph, __be64 tun_id,
__be16 tun_flags)
__be16 tun_flags,
struct geneve_opt *opts,
u8 opts_len)
{
tun_info->tunnel.tun_id = tun_id;
tun_info->tunnel.ipv4_src = iph->saddr;
Expand All @@ -69,9 +82,14 @@ static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
/* clear struct padding. */
memset((unsigned char *) &tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0,
sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);

tun_info->options = opts;
tun_info->options_len = opts_len;
}

struct sw_flow_key {
u8 tun_opts[255];
u8 tun_opts_len;
struct ovs_key_ipv4_tunnel tun_key; /* Encapsulating tunnel key. */
struct {
u32 priority; /* Packet QoS priority. */
Expand Down
Loading

0 comments on commit c1fc141

Please sign in to comment.