This is actually intended for use by the VXLAN tunnel implementation, which
should use a source port range, but it is fairly generic infrastructure so
this implements it independent of VXLAN and applies it to CAPWAP.
Not yet tested.
---
datapath/tunnel.c | 184 ++++++++++++++++++++++++++++++++++++++----
datapath/tunnel.h | 27 ++++++-
datapath/vport-capwap.c | 57 ++------------
include/openvswitch/tunnel.h | 7 ++-
lib/netdev-vport.c | 117 ++++++++++++++++++++++++---
vswitchd/vswitch.xml | 47 ++++++++++-
6 files changed, 355 insertions(+), 84 deletions(-)
diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index 372d90e..047961f 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -71,8 +71,24 @@
#define CACHE_CLEANER_INTERVAL (5 * HZ)
#define CACHE_DATA_ALIGN 16
-#define PORT_TABLE_SIZE 1024
+/* Sockets for UDP-based tunnels.
+ *
+ * It's uncommon to have more than one socket per tunnel protocol, and the
+ * table is not traversed on any fast path, so we just use a linked list.
+ *
+ * Only accessed under RTNL.
+ */
+struct tnl_sock {
+ struct list_head list;
+ struct socket *socket;
+ __be16 port;
+ unsigned int refcnt;
+};
+static LIST_HEAD(tnl_socks);
+
+/* Table of struct tnl_vport. */
+#define PORT_TABLE_SIZE 1024
static struct hlist_head *port_table __read_mostly;
static int port_table_count;
@@ -155,6 +171,8 @@ static void free_cache_rcu(struct rcu_head *rcu)
*/
static void free_mutable_rtnl(struct tnl_mutable_config *mutable)
{
+ struct tnl_sock *ts;
+
ASSERT_RTNL();
if (ipv4_is_multicast(mutable->key.daddr) && mutable->mlink) {
struct in_device *in_dev;
@@ -162,6 +180,13 @@ static void free_mutable_rtnl(struct tnl_mutable_config
*mutable)
if (in_dev)
ip_mc_dec_group(in_dev, mutable->key.daddr);
}
+
+ ts = mutable->tnl_sock;
+ if (ts && !--ts->refcnt) {
+ sock_release(ts->socket);
+ list_del(&ts->list);
+ kfree(ts);
+ }
}
static void assign_config_rcu(struct vport *vport,
@@ -1327,10 +1352,76 @@ out:
return sent_len;
}
+static int tnl_sock_bind(const struct tnl_ops *tnl_ops, struct
tnl_mutable_config *mutable)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ struct sockaddr_in sin;
+ struct tnl_sock *ts;
+ int err;
+
+ if (tnl_ops->ipproto != IPPROTO_UDP)
+ return 0;
+
+ list_for_each_entry (ts, &tnl_socks, list) {
+ if (ts->port == mutable->dst_port) {
+ if (udp_sk(ts->socket->sk)->encap_rcv !=
tnl_ops->udp_rcv)
+ return -EADDRINUSE;
+ ts->refcnt++;
+ return 0;
+ }
+ }
+
+ ts = kmalloc(sizeof(struct tnl_sock), GFP_KERNEL);
+ if (!ts) {
+ err = -ENOMEM;
+ goto error;
+ }
+ ts->port = mutable->dst_port;
+ ts->refcnt = 1;
+
+ err = sock_create(AF_INET, SOCK_DGRAM, 0, &ts->socket);
+ if (err)
+ goto error_kfree_ts;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = mutable->dst_port;
+
+ err = kernel_bind(ts->socket, (struct sockaddr *)&sin,
+ sizeof(struct sockaddr_in));
+ if (err)
+ goto error_release_sock;
+
+ /* The particular encap_type value set here is unimportant, as long as
+ * it's not 0, since we set the handler.
+ */
+ udp_sk(ts->socket->sk)->encap_type = !0;
+ udp_sk(ts->socket->sk)->encap_rcv = tnl_ops->udp_rcv;
+
+ list_add(&ts->list, &tnl_socks);
+
+ return 0;
+
+error_release_sock:
+ sock_release(ts->socket);
+error_kfree_ts:
+ kfree(ts);
+error:
+ return err;
+#else
+ WARN_ON(tnl_ops->ipproto == IPPROTO_UDP);
+
+ return 0;
+#endif
+}
+
static const struct nla_policy tnl_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
[OVS_TUNNEL_ATTR_FLAGS] = { .type = NLA_U32 },
[OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NLA_U32 },
[OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NLA_U32 },
+ [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NLA_U16 },
+ [OVS_TUNNEL_ATTR_SRC_PORT_BASE] = { .type = NLA_U16 },
+ [OVS_TUNNEL_ATTR_SRC_PORT_HASH] = { .type = NLA_U8 },
[OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NLA_U64 },
[OVS_TUNNEL_ATTR_IN_KEY] = { .type = NLA_U64 },
[OVS_TUNNEL_ATTR_TOS] = { .type = NLA_U8 },
@@ -1347,29 +1438,57 @@ static int tnl_set_config(struct nlattr *options, const
struct tnl_ops *tnl_ops,
struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
int err;
+ err = -EINVAL;
if (!options)
- return -EINVAL;
+ goto error;
err = nla_parse_nested(a, OVS_TUNNEL_ATTR_MAX, options, tnl_policy);
if (err)
- return err;
+ goto error;
- if (!a[OVS_TUNNEL_ATTR_FLAGS] || !a[OVS_TUNNEL_ATTR_DST_IPV4])
- return -EINVAL;
+ if (!a[OVS_TUNNEL_ATTR_FLAGS] || !a[OVS_TUNNEL_ATTR_DST_IPV4]) {
+ err = -EINVAL;
+ goto error;
+ }
mutable->flags = nla_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_PUBLIC;
mutable->key.daddr = nla_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
- if (ipv4_is_multicast(mutable->key.daddr))
- return -EINVAL;
+ if (ipv4_is_multicast(mutable->key.daddr)) {
+ err = -EINVAL;
+ goto error;
+ }
mutable->key.saddr = nla_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
}
+ if (tnl_ops->ipproto == IPPROTO_UDP) {
+ struct nlattr *src_base = a[OVS_TUNNEL_ATTR_SRC_PORT_BASE];
+ struct nlattr *src_hash = a[OVS_TUNNEL_ATTR_SRC_PORT_HASH];
+ struct nlattr *dst = a[OVS_TUNNEL_ATTR_DST_PORT];
+
+ if (!src_base || !dst) {
+ err = -EINVAL;
+ goto error;
+ }
+ mutable->src_port_base = ntohs(nla_get_be16(src_base));
+ mutable->dst_port = nla_get_be16(dst);
+
+ if (src_hash) {
+ mutable->src_port_hash = nla_get_u8(src_hash);
+ if (mutable->src_port_hash > 16) {
+ err = -EINVAL;
+ goto error;
+ }
+ }
+ }
+
if (a[OVS_TUNNEL_ATTR_TOS]) {
mutable->tos = nla_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
- if (mutable->tos != RT_TOS(mutable->tos))
- return -EINVAL;
+ if (mutable->tos != RT_TOS(mutable->tos)) {
+ err = -EINVAL;
+ goto error;
+ }
}
if (a[OVS_TUNNEL_ATTR_TTL])
@@ -1396,8 +1515,10 @@ static int tnl_set_config(struct nlattr *options, const
struct tnl_ops *tnl_ops,
mutable->tunnel_hlen += sizeof(struct iphdr);
old_vport = port_table_lookup(&mutable->key, &old_mutable);
- if (old_vport && old_vport != cur_vport)
- return -EEXIST;
+ if (old_vport && old_vport != cur_vport) {
+ err = -EEXIST;
+ goto error;
+ }
mutable->mlink = 0;
if (ipv4_is_multicast(mutable->key.daddr)) {
@@ -1405,17 +1526,30 @@ static int tnl_set_config(struct nlattr *options, const
struct tnl_ops *tnl_ops,
struct rtable *rt;
rt = __find_route(mutable, tnl_ops->ipproto, mutable->tos);
- if (IS_ERR(rt))
- return -EADDRNOTAVAIL;
+ if (IS_ERR(rt)) {
+ err = -EADDRNOTAVAIL;
+ goto error;
+ }
dev = rt_dst(rt).dev;
ip_rt_put(rt);
- if (__in_dev_get_rtnl(dev) == NULL)
- return -EADDRNOTAVAIL;
+ if (__in_dev_get_rtnl(dev) == NULL) {
+ err = -EADDRNOTAVAIL;
+ goto error;
+ }
mutable->mlink = dev->ifindex;
ip_mc_inc_group(__in_dev_get_rtnl(dev), mutable->key.daddr);
}
+ err = tnl_sock_bind(tnl_ops, mutable);
+ if (err)
+ goto err_dec_mc_group;
+
return 0;
+
+err_dec_mc_group:
+ free_mutable_rtnl(mutable);
+error:
+ return err;
}
struct vport *tnl_create(const struct vport_parms *parms,
@@ -1531,6 +1665,13 @@ int tnl_get_options(const struct vport *vport, struct
sk_buff *skb)
if (mutable->ttl)
NLA_PUT_U8(skb, OVS_TUNNEL_ATTR_TTL, mutable->ttl);
+ if (tnl_vport->tnl_ops->ipproto == IPPROTO_UDP) {
+ NLA_PUT_BE16(skb, OVS_TUNNEL_ATTR_SRC_PORT_BASE,
htons(mutable->src_port_base));
+ if (mutable->src_port_hash)
+ NLA_PUT_U8(skb, OVS_TUNNEL_ATTR_SRC_PORT_HASH,
mutable->src_port_hash);
+ NLA_PUT_BE16(skb, OVS_TUNNEL_ATTR_DST_PORT, mutable->dst_port);
+ }
+
return 0;
nla_put_failure:
@@ -1568,7 +1709,9 @@ int tnl_set_addr(struct vport *vport, const unsigned char
*addr)
if (!mutable)
return -ENOMEM;
+ /* Transfer state ownership from old_mutable to mutable. */
old_mutable->mlink = 0;
+ old_mutable->tnl_sock = NULL;
memcpy(mutable->eth_addr, addr, ETH_ALEN);
assign_config_rcu(vport, mutable);
@@ -1597,6 +1740,16 @@ void tnl_free_linked_skbs(struct sk_buff *skb)
}
}
+__be16 tnl_get_src_port(const struct sk_buff *skb, const struct
tnl_mutable_config *mutable)
+{
+ u16 port = mutable->src_port_base;
+ if (mutable->src_port_hash) {
+ u16 mask = (1 << mutable->src_port_hash) - 1;
+ port += OVS_CB(skb)->flow->hash & mask;
+ }
+ return htons(port);
+}
+
int tnl_init(void)
{
int i;
@@ -1616,6 +1769,7 @@ void tnl_exit(void)
{
int i;
+ BUG_ON(!list_empty(&tnl_socks));
for (i = 0; i < PORT_TABLE_SIZE; i++) {
struct tnl_vport * tnl_vport;
struct hlist_head *hash_head;
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index f80df99..dfcd3a8 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -69,10 +69,20 @@ struct port_lookup_key {
* @tunnel_hlen: Tunnel header length.
* @eth_addr: Source address for packets generated by tunnel itself
* (e.g. ICMP fragmentation needed messages).
+ * @src_port_base: Base source port.
+ * @src_port_hash: Number of bits of flow hash to add to @src_port_base.
+ * @dst_port: Destination port.
* @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
* @flags: TNL_F_* flags.
* @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
* @ttl: IPv4 TTL value to use for tunnel, 0 if no fixed TTL.
+ * @mlink: ifindex of network device on which a multicast address for this
+ * tunnel is registered, 0 if none.
+ * @tnl_sock: &struct tnl_sock in which this tunnel owns a refcount, or %NULL.
+ *
+ * The @src_port_base, @src_port_hash, and @dst_port members are relevant only
+ * to tunnels with L4 port numbers. Currently that's just tunnels with
+ * @ipproto == %IPPROTO_UDP.
*/
struct tnl_mutable_config {
struct port_lookup_key key;
@@ -85,13 +95,17 @@ struct tnl_mutable_config {
unsigned char eth_addr[ETH_ALEN];
/* Configured via OVS_TUNNEL_ATTR_* attributes. */
+ u16 src_port_base;
+ u8 src_port_hash;
+ __be16 dst_port;
__be64 out_key;
u32 flags;
u8 tos;
u8 ttl;
- /* Multicast configuration. */
+ /* State that needs to be freed. */
int mlink;
+ struct tnl_sock *tnl_sock;
};
struct tnl_ops {
@@ -127,6 +141,15 @@ struct tnl_ops {
struct sk_buff *(*update_header)(const struct vport *,
const struct tnl_mutable_config *,
struct dst_entry *, struct sk_buff *);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ /*
+ * Handler for received packets, used as encap_rcv in struct udp_sock.
+ *
+ * Required if ipproto is IPPROTO_UDP, not used otherwise.
+ */
+ int (*udp_rcv)(struct sock *sk, struct sk_buff *skb);
+#endif
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
@@ -250,6 +273,8 @@ bool tnl_frag_needed(struct vport *vport,
struct sk_buff *skb, unsigned int mtu, __be64 flow_key);
void tnl_free_linked_skbs(struct sk_buff *skb);
+__be16 tnl_get_src_port(const struct sk_buff *, const struct
tnl_mutable_config *);
+
int tnl_init(void);
void tnl_exit(void);
static inline struct tnl_vport *tnl_vport_priv(const struct vport *vport)
diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c
index 8d78b6d..74b8bbd 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -27,9 +27,6 @@
#include "vport.h"
#include "vport-generic.h"
-#define CAPWAP_SRC_PORT 58881
-#define CAPWAP_DST_PORT 58882
-
#define CAPWAP_FRAG_TIMEOUT (30 * HZ)
#define CAPWAP_FRAG_MAX_MEM (256 * 1024)
#define CAPWAP_FRAG_PRUNE_MEM (192 *1024)
@@ -137,8 +134,6 @@ struct frag_skb_cb {
static struct sk_buff *fragment(struct sk_buff *, const struct vport *,
struct dst_entry *dst, unsigned int hlen);
-static void defrag_init(void);
-static void defrag_exit(void);
static struct sk_buff *defrag(struct sk_buff *, bool frag_last);
static void capwap_frag_init(struct inet_frag_queue *, void *match);
@@ -160,8 +155,6 @@ static struct netns_frags frag_netns_state = {
.low_thresh = CAPWAP_FRAG_PRUNE_MEM,
};
-static struct socket *capwap_rcv_socket;
-
static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
{
int size = CAPWAP_MIN_HLEN;
@@ -186,8 +179,7 @@ static void capwap_build_header(const struct vport *vport,
struct udphdr *udph = header;
struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
- udph->source = htons(CAPWAP_SRC_PORT);
- udph->dest = htons(CAPWAP_DST_PORT);
+ udph->dest = mutable->dst_port;
udph->check = 0;
cwh->frag_id = 0;
@@ -230,6 +222,7 @@ static struct sk_buff *capwap_update_header(const struct
vport *vport,
opt->key = OVS_CB(skb)->tun_id;
}
+ udph->source = tnl_get_src_port(skb, mutable);
udph->len = htons(skb->len - skb_transport_offset(skb));
if (unlikely(skb->len - skb_network_offset(skb) > dst_mtu(dst))) {
@@ -361,6 +354,7 @@ static const struct tnl_ops capwap_tnl_ops = {
.hdr_len = capwap_hdr_len,
.build_header = capwap_build_header,
.update_header = capwap_update_header,
+ .udp_rcv = capwap_rcv,
};
static struct vport *capwap_create(const struct vport_parms *parms)
@@ -368,46 +362,6 @@ static struct vport *capwap_create(const struct
vport_parms *parms)
return tnl_create(parms, &capwap_vport_ops, &capwap_tnl_ops);
}
-/* Random value. Irrelevant as long as it's not 0 since we set the handler. */
-#define UDP_ENCAP_CAPWAP 10
-static int capwap_init(void)
-{
- int err;
- struct sockaddr_in sin;
-
- err = sock_create(AF_INET, SOCK_DGRAM, 0, &capwap_rcv_socket);
- if (err)
- goto error;
-
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(CAPWAP_DST_PORT);
-
- err = kernel_bind(capwap_rcv_socket, (struct sockaddr *)&sin,
- sizeof(struct sockaddr_in));
- if (err)
- goto error_sock;
-
- udp_sk(capwap_rcv_socket->sk)->encap_type = UDP_ENCAP_CAPWAP;
- udp_sk(capwap_rcv_socket->sk)->encap_rcv = capwap_rcv;
-
- defrag_init();
-
- return 0;
-
-error_sock:
- sock_release(capwap_rcv_socket);
-error:
- pr_warn("cannot register capwap protocol handler\n");
- return err;
-}
-
-static void capwap_exit(void)
-{
- defrag_exit();
- sock_release(capwap_rcv_socket);
-}
-
static void copy_skb_metadata(struct sk_buff *from, struct sk_buff *to)
{
to->pkt_type = from->pkt_type;
@@ -738,13 +692,14 @@ static struct sk_buff *defrag(struct sk_buff *skb, bool
frag_last)
return NULL;
}
-static void defrag_init(void)
+static int capwap_init(void)
{
inet_frags_init(&frag_state);
inet_frags_init_net(&frag_netns_state);
+ return 0;
}
-static void defrag_exit(void)
+static void capwap_exit(void)
{
inet_frags_exit_net(&frag_netns_state, &frag_state);
inet_frags_fini(&frag_state);
diff --git a/include/openvswitch/tunnel.h b/include/openvswitch/tunnel.h
index 110e652..c2febb1 100644
--- a/include/openvswitch/tunnel.h
+++ b/include/openvswitch/tunnel.h
@@ -45,7 +45,9 @@
/* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
*
- * OVS_TUNNEL_ATTR_FLAGS and OVS_TUNNEL_ATTR_DST_IPV4 are required. All other
+ * All tunnels require OVS_TUNNEL_ATTR_FLAGS and OVS_TUNNEL_ATTR_DST_IPV4.
+ * Tunnels with L4 port numbers (currently just UDP-based tunnels) require
+ * OVS_TUNNEL_ATTR_SRC_PORT_BASE and OVS_TUNNEL_ATTR_DST_PORT. All other
* attributes are optional.
*/
enum {
@@ -53,6 +55,9 @@ enum {
OVS_TUNNEL_ATTR_FLAGS, /* 32-bit TNL_F_*. */
OVS_TUNNEL_ATTR_DST_IPV4, /* IPv4 destination address. */
OVS_TUNNEL_ATTR_SRC_IPV4, /* IPv4 source address. */
+ OVS_TUNNEL_ATTR_SRC_PORT_BASE, /* __be16 base source port. */
+ OVS_TUNNEL_ATTR_SRC_PORT_HASH, /* u8 number of random source port bits.
*/
+ OVS_TUNNEL_ATTR_DST_PORT, /* __be16 destination port. */
OVS_TUNNEL_ATTR_OUT_KEY, /* __be64 key to use on output. */
OVS_TUNNEL_ATTR_IN_KEY, /* __be64 key to match on input. */
OVS_TUNNEL_ATTR_TOS, /* 8-bit TOS value. */
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index f6dbd03..5a562c3 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -30,6 +30,7 @@
#include "daemon.h"
#include "dirs.h"
#include "dpif-linux.h"
+#include "dynamic-string.h"
#include "hash.h"
#include "hmap.h"
#include "list.h"
@@ -63,10 +64,18 @@ struct netdev_vport {
struct vport_class {
enum ovs_vport_type type;
+
+ /* Defaults. src_port_base and dst_port must be nonzero if and only if
+ * this vport class is a tunnel that uses L4 port numbers. */
+ uint16_t src_port_base;
+ uint8_t src_port_hash;
+ uint16_t dst_port;
+
struct netdev_class netdev_class;
- int (*parse_config)(const char *name, const char *type,
+
+ int (*parse_config)(const char *name, const struct vport_class *,
const struct shash *args, struct ofpbuf *options);
- int (*unparse_config)(const char *name, const char *type,
+ int (*unparse_config)(const char *name, const struct vport_class *,
const struct nlattr *options, size_t options_len,
struct shash *args);
};
@@ -246,7 +255,7 @@ netdev_vport_get_config(struct netdev_dev *dev_, struct
shash *args)
ofpbuf_delete(buf);
}
- error = vport_class->unparse_config(name, netdev_class->type,
+ error = vport_class->unparse_config(name, vport_class,
dev->options->data,
dev->options->size,
args);
@@ -268,8 +277,7 @@ netdev_vport_set_config(struct netdev_dev *dev_, const
struct shash *args)
int error;
options = ofpbuf_new(64);
- error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
- args, options);
+ error = vport_class->parse_config(name, vport_class, args, options);
if (!error
&& (!dev->options
|| options->size != dev->options->size
@@ -570,15 +578,19 @@ set_key(const struct shash *args, const char *name,
uint16_t type,
}
static int
-parse_tunnel_config(const char *name, const char *type,
+parse_tunnel_config(const char *name, const struct vport_class *vport_class,
const struct shash *args, struct ofpbuf *options)
{
+ const char *type = vport_class->netdev_class.type;
bool is_gre = false;
bool is_ipsec = false;
struct shash_node *node;
bool ipsec_mech_set = false;
ovs_be32 daddr = htonl(0);
ovs_be32 saddr = htonl(0);
+ uint16_t src_port_base;
+ uint8_t src_port_hash;
+ uint16_t dst_port;
uint32_t flags;
flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE;
@@ -590,8 +602,13 @@ parse_tunnel_config(const char *name, const char *type,
flags |= TNL_F_IPSEC;
flags &= ~TNL_F_HDR_CACHE;
}
+ src_port_base = vport_class->src_port_base;
+ src_port_hash = vport_class->src_port_hash;
+ dst_port = vport_class->dst_port;
SHASH_FOR_EACH (node, args) {
+ const char *value = node->data;
+
if (!strcmp(node->name, "remote_ip")) {
struct in_addr in_addr;
if (lookup_ip(node->data, &in_addr)) {
@@ -606,6 +623,26 @@ parse_tunnel_config(const char *name, const char *type,
} else {
saddr = in_addr.s_addr;
}
+ } else if (src_port_base && !strcmp(node->name, "src_port")) {
+ int n = -1;
+ if (sscanf(node->data, "%"SCNu16"/%"SCNu8"%n", &src_port_base,
+ &src_port_hash, &n) > 0 && src_port_base != 0) {
+ if (n == -1) {
+ src_port_hash = 0;
+ } else if (src_port_hash > 16) {
+ VLOG_WARN("%s: ignoring invalid src_port hash bits", name);
+ src_port_hash = 0;
+ }
+ } else {
+ VLOG_ERR("%s: invalid src_port \"%s\"", name, value);
+ return EINVAL;
+ }
+ } else if (dst_port && !strcmp(node->name, "dst_port")) {
+ dst_port = atoi(node->data);
+ if (!dst_port) {
+ VLOG_ERR("%s: invalid dst_port \"%s\"", name, value);
+ return EINVAL;
+ }
} else if (!strcmp(node->name, "tos")) {
if (!strcmp(node->data, "inherit")) {
flags |= TNL_F_TOS_INHERIT;
@@ -715,6 +752,19 @@ parse_tunnel_config(const char *name, const char *type,
}
}
+ if (src_port_base) {
+ nl_msg_put_be16(options, OVS_TUNNEL_ATTR_SRC_PORT_BASE,
+ htons(src_port_base));
+ if (src_port_hash) {
+ nl_msg_put_u8(options, OVS_TUNNEL_ATTR_SRC_PORT_HASH,
+ src_port_hash);
+ }
+ }
+
+ if (dst_port) {
+ nl_msg_put_be16(options, OVS_TUNNEL_ATTR_DST_PORT, htons(dst_port));
+ }
+
nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags);
return 0;
@@ -728,6 +778,11 @@ tnl_port_config_from_nlattr(const struct nlattr *options,
size_t options_len,
[OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 },
[OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 },
[OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
+ [OVS_TUNNEL_ATTR_SRC_PORT_BASE] = { .type = NL_A_BE16,
+ .optional = true },
+ [OVS_TUNNEL_ATTR_SRC_PORT_HASH] = { .type = NL_A_U8,
+ .optional = true },
+ [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_BE16, .optional = true },
[OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
[OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
[OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
@@ -749,12 +804,27 @@ get_be64_or_zero(const struct nlattr *a)
return a ? ntohll(nl_attr_get_be64(a)) : 0;
}
+static uint16_t
+get_be16_or_zero(const struct nlattr *a)
+{
+ return a ? ntohs(nl_attr_get_be16(a)) : 0;
+}
+
+static uint8_t
+get_u8_or_zero(const struct nlattr *a)
+{
+ return a ? nl_attr_get_u8(a) : 0;
+}
+
static int
-unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
+unparse_tunnel_config(const char *name OVS_UNUSED,
+ const struct vport_class *vport_class,
const struct nlattr *options, size_t options_len,
struct shash *args)
{
struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
+ uint16_t src_port_base, dst_port;
+ int src_port_hash;
ovs_be32 daddr;
uint32_t flags;
int error;
@@ -778,6 +848,25 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const
char *type OVS_UNUSED,
shash_add(args, "local_ip", xasprintf(IP_FMT, IP_ARGS(&saddr)));
}
+ src_port_base = get_be16_or_zero(a[OVS_TUNNEL_ATTR_SRC_PORT_BASE]);
+ src_port_hash = get_u8_or_zero(a[OVS_TUNNEL_ATTR_SRC_PORT_HASH]);
+ if (src_port_base != vport_class->src_port_base ||
+ src_port_hash != vport_class->src_port_hash) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "%"PRIu16, src_port_base);
+ if (src_port_hash) {
+ ds_put_format(&s, "/%d", src_port_hash);
+ }
+ shash_add_nocopy(args, "src_port", ds_steal_cstr(&s));
+ }
+
+ dst_port = get_be16_or_zero(a[OVS_TUNNEL_ATTR_DST_PORT]);
+ if (dst_port != vport_class->dst_port) {
+ shash_add_nocopy(args, "dst_port", xasprintf("%"PRIu16, dst_port));
+ }
+
if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
smap_add(args, "key", "flow");
} else {
@@ -832,7 +921,8 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const
char *type OVS_UNUSED,
}
static int
-parse_patch_config(const char *name, const char *type OVS_UNUSED,
+parse_patch_config(const char *name,
+ const struct vport_class *vport_class OVS_UNUSED,
const struct shash *args, struct ofpbuf *options)
{
const char *peer;
@@ -864,7 +954,8 @@ parse_patch_config(const char *name, const char *type
OVS_UNUSED,
}
static int
-unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
+unparse_patch_config(const char *name OVS_UNUSED,
+ const struct vport_class *vport_class OVS_UNUSED,
const struct nlattr *options, size_t options_len,
struct shash *args)
{
@@ -951,19 +1042,19 @@ void
netdev_vport_register(void)
{
static const struct vport_class vport_classes[] = {
- { OVS_VPORT_TYPE_GRE,
+ { OVS_VPORT_TYPE_GRE, 0, 0, 0,
{ "gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
parse_tunnel_config, unparse_tunnel_config },
- { OVS_VPORT_TYPE_GRE,
+ { OVS_VPORT_TYPE_GRE, 0, 0, 0,
{ "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
parse_tunnel_config, unparse_tunnel_config },
- { OVS_VPORT_TYPE_CAPWAP,
+ { OVS_VPORT_TYPE_CAPWAP, 58881, 0, 58882,
{ "capwap", VPORT_FUNCTIONS(netdev_vport_get_status) },
parse_tunnel_config, unparse_tunnel_config },
- { OVS_VPORT_TYPE_PATCH,
+ { OVS_VPORT_TYPE_PATCH, 0, 0, 0,
{ "patch", VPORT_FUNCTIONS(NULL) },
parse_patch_config, unparse_patch_config }
};
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index bcb6b6f..8791114 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1084,9 +1084,8 @@
An Ethernet tunnel over the UDP transport portion of CAPWAP (RFC
5415). This allows interoperability with certain switches that do
not support GRE. Only the tunneling component of the protocol is
- implemented. UDP ports 58881 and 58882 are used as the source and
- destination ports respectively. CAPWAP is currently supported only
- with the Linux kernel datapath with kernel version 2.6.26 or later.
+ implemented. CAPWAP is currently supported only with the Linux
+ kernel datapath with kernel version 2.6.26 or later.
</dd>
<dt><code>patch</code></dt>
@@ -1306,6 +1305,48 @@
tunnel.
</column>
</group>
+
+ <group title="Tunnel Options: capwap only">
+ <p>
+ Only <code>capwap</code> interfaces support this options.
+ </p>
+
+ <column name="options" key="src_port">
+ Determines the UDP source port used for sending packets, in one of
+ these forms:
+
+ <dl>
+ <dt><code><var>port</var></code></dt>
+ <dd>
+ Uses <var>port</var> as the source port.
+ </dd>
+
+ <dt><code><var>base</var>/<var>hashbits</var></code></dt>
+ <dd>
+ <p>
+ The source port is a range of
+ <code>2**<var>hashbits</var></code> total ports starting at
+ port <var>base</var>. For example, <code>32768/15</code>
+ allows the source ports to vary between 32768 and 65535, a
+ range of 32768 (<code>2**15</code>) total ports. For any given
+ packet, the port within the range is chosen based on a hash of
+ its flow.
+ </p>
+ <p>
+ Source port ranges allow for load balancing across network
+ elements that cannot look into the tunneled packet.
+ </p>
+ </dd>
+ </dl>
+
+ The default source port is 58881.
+ </column>
+
+ <column name="options" key="dst_port">
+ The UDP destination port to which packets are sent and on which they
+ are expected to be received. The default is 58882.
+ </column>
+ </group>
</group>
<group title="Patch Options">
--
1.7.2.5
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev