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 dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev