On May 7, 2013, at 1:13 PM, Jarno Rajahalme <jarno.rajaha...@nsn.com> wrote:
> This reduces repeated code and makes it easier to add new UDP tunneling > protocols. > I haven't reviewed this in detail yet, but when I tried to compile it on Fedora, it failed. The small incremental below fixes this on Fedora. I'll continue reviewing it now. I did pass VXLAN traffic over a tunnel between two hosts and that worked just fine. Thanks, Kyle [kmestery@fedora-build ovs-vxlan]$ git diff diff --git a/datapath/tunnel.c b/datapath/tunnel.c index bef1f7d..c186ddf 100644 --- a/datapath/tunnel.c +++ b/datapath/tunnel.c @@ -28,6 +28,7 @@ #include <linux/workqueue.h> #include <linux/rculist.h> #include <net/route.h> +#include <net/udp.h> #include <net/xfrm.h> #include "checksum.h" [kmestery@fedora-build ovs-vxlan]$ > Signed-off-by: Jarno Rajahalme <jarno.rajaha...@nsn.com> > --- > datapath/tunnel.c | 126 ++++++++++++++++++++++++++++++++++++ > datapath/tunnel.h | 48 ++++++++++++++ > datapath/vport-lisp.c | 167 ++++-------------------------------------------- > datapath/vport-vxlan.c | 166 ++++------------------------------------------- > 4 files changed, 201 insertions(+), 306 deletions(-) > > diff --git a/datapath/tunnel.c b/datapath/tunnel.c > index 8c93e18..bef1f7d 100644 > --- a/datapath/tunnel.c > +++ b/datapath/tunnel.c > @@ -319,3 +319,129 @@ err_free_rt: > error: > return err; > } > + > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) > +/* > + * Generic UDP tunnel handling > + */ > +static int ovs_tnl_udp_init_socket(struct tnl_udp_vport *udp_vport, > + struct net *net, > + int (*udp_rcv)(struct sock *, > + struct sk_buff *)) > +{ > + int err; > + struct sockaddr_in sin; > + > + err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, > + &udp_vport->udp_rcv_socket); > + if (err) > + goto error; > + > + /* Release net ref. */ > + sk_change_net(udp_vport->udp_rcv_socket->sk, net); > + > + sin.sin_family = AF_INET; > + sin.sin_addr.s_addr = htonl(INADDR_ANY); > + sin.sin_port = udp_vport->dst_port; > + > + err = kernel_bind(udp_vport->udp_rcv_socket, (struct sockaddr *)&sin, > + sizeof(struct sockaddr_in)); > + if (err) > + goto error_sock; > + > + /* Arbitrary value > + * Irrelevant as long as it's not 0 since we set the handler. */ > + udp_sk(udp_vport->udp_rcv_socket->sk)->encap_type = 1; > + udp_sk(udp_vport->udp_rcv_socket->sk)->encap_rcv = udp_rcv; > + > + udp_encap_enable(); > + > + return 0; > + > +error_sock: > + sk_release_kernel(udp_vport->udp_rcv_socket->sk); > +error: > + pr_warn("Cannot register UDP protocol handler : %d\n", err); > + return err; > +} > + > +void ovs_tnl_udp_destroy(struct vport *vport) > +{ > + struct tnl_udp_vport *udp_vport = tnl_udp_vport(vport); > + > + list_del_rcu(&udp_vport->list); > + /* Release socket */ > + sk_release_kernel(udp_vport->udp_rcv_socket->sk); > + > + ovs_vport_deferred_free(vport); > +} > + > +struct vport * ovs_tnl_udp_create(const struct vport_parms *parms, > + const struct vport_ops *ops, > + struct list_head *ports, > + int (*udp_rcv)(struct sock *, > + struct sk_buff *)) > +{ > + struct net *net = ovs_dp_get_net(parms->dp); > + struct vport *vport; > + struct tnl_udp_vport *udp_vport; > + struct nlattr *a; > + int err; > + u16 dst_port; > + > + if (!parms->options) { > + err = -EINVAL; > + goto error; > + } > + > + a = nla_find_nested(parms->options, OVS_TUNNEL_ATTR_DST_PORT); > + if (a && nla_len(a) == sizeof(u16)) { > + dst_port = nla_get_u16(a); > + } else { > + /* Require destination port from userspace. */ > + err = -EINVAL; > + goto error; > + } > + > + /* Verify if we already have a socket created for this port. */ > + if (ovs_tnl_udp_find_port(net, htons(dst_port), ports)) { > + err = -EEXIST; > + goto error; > + } > + > + vport = ovs_vport_alloc(sizeof(struct tnl_udp_vport), ops, parms); > + if (IS_ERR(vport)) > + return vport; > + > + udp_vport = tnl_udp_vport(vport); > + udp_vport->dst_port = htons(dst_port); > + strncpy(udp_vport->name, parms->name, IFNAMSIZ); > + > + err = ovs_tnl_udp_init_socket(udp_vport, net, udp_rcv); > + if (err) > + goto error_free; > + > + list_add_tail_rcu(&udp_vport->list, ports); > + return vport; > + > +error_free: > + ovs_vport_free(vport); > +error: > + return ERR_PTR(err); > +} > + > +int ovs_tnl_udp_get_options(const struct vport *vport, struct sk_buff *skb) > +{ > + const struct tnl_udp_vport *udp_vport = tnl_udp_vport(vport); > + > + if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, > + ntohs(udp_vport->dst_port))) > + return -EMSGSIZE; > + return 0; > +} > + > +const char *ovs_tnl_udp_get_name(const struct vport *vport) > +{ > + return tnl_udp_vport(vport)->name; > +} > +#endif > diff --git a/datapath/tunnel.h b/datapath/tunnel.h > index 89c4e16..3384684 100644 > --- a/datapath/tunnel.h > +++ b/datapath/tunnel.h > @@ -52,4 +52,52 @@ static inline void tnl_tun_key_init(struct > ovs_key_ipv4_tunnel *tun_key, > sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE); > } > > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) > + > +/** > + * struct tnl_udp_vport - Private part of struct vport to keep track of open > + * UDP ports > + * @dst_port: UDP port no. > + * @list: list element for protocol specific port list. > + * @udp_rcv_socket: The socket created for this UDP port number. > + * @name: vport name. > + */ > +struct tnl_udp_vport { > + __be16 dst_port; > + struct list_head list; > + struct socket *udp_rcv_socket; > + char name[IFNAMSIZ]; > +}; > + > +static inline struct tnl_udp_vport *tnl_udp_vport(const struct vport *vport) > +{ > + return vport_priv(vport); > +} > + > +static inline struct tnl_udp_vport *ovs_tnl_udp_find_port(struct net *net, > + __be16 port, > + struct list_head > *head) > +{ > + struct tnl_udp_vport *udp_vport; > + > + list_for_each_entry_rcu(udp_vport, head, list) { > + if (udp_vport->dst_port == port && > + net_eq(sock_net(udp_vport->udp_rcv_socket->sk), net)) > + return udp_vport; > + } > + > + return NULL; > +} > + > +void ovs_tnl_udp_destroy(struct vport *); > +struct vport *ovs_tnl_udp_create(const struct vport_parms *, > + const struct vport_ops *, > + struct list_head *ports, > + int (*udp_rcv)(struct sock*, > + struct sk_buff *)); > +int ovs_tnl_udp_get_options(const struct vport *, struct sk_buff *); > + > +const char *ovs_tnl_udp_get_name(const struct vport *); > +#endif > + > #endif /* tunnel.h */ > diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c > index ca2b441..dbfc4c5 100644 > --- a/datapath/vport-lisp.c > +++ b/datapath/vport-lisp.c > @@ -94,40 +94,8 @@ struct lisphdr { > > #define LISP_HLEN (sizeof(struct udphdr) + sizeof(struct lisphdr)) > > -/** > - * struct lisp_port - Keeps track of open UDP ports > - * @dst_port: lisp UDP port no. > - * @list: list element in @lisp_ports. > - * @lisp_rcv_socket: The socket created for this port number. > - * @name: vport name. > - */ > -struct lisp_port { > - __be16 dst_port; > - struct list_head list; > - struct socket *lisp_rcv_socket; > - char name[IFNAMSIZ]; > -}; > - > static LIST_HEAD(lisp_ports); > > -static inline struct lisp_port *lisp_vport(const struct vport *vport) > -{ > - return vport_priv(vport); > -} > - > -static struct lisp_port *lisp_find_port(struct net *net, __be16 port) > -{ > - struct lisp_port *lisp_port; > - > - list_for_each_entry_rcu(lisp_port, &lisp_ports, list) { > - if (lisp_port->dst_port == port && > - net_eq(sock_net(lisp_port->lisp_rcv_socket->sk), net)) > - return lisp_port; > - } > - > - return NULL; > -} > - > static inline struct lisphdr *lisp_hdr(const struct sk_buff *skb) > { > return (struct lisphdr *)(udp_hdr(skb) + 1); > @@ -164,12 +132,12 @@ static void lisp_build_header(const struct vport *vport, > struct sk_buff *skb, > int tunnel_hlen) > { > - struct lisp_port *lisp_port = lisp_vport(vport); > + struct tnl_udp_vport *udp_vport = tnl_udp_vport(vport); > struct udphdr *udph = udp_hdr(skb); > struct lisphdr *lisph = (struct lisphdr *)(udph + 1); > const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key; > > - udph->dest = lisp_port->dst_port; > + udph->dest = udp_vport->dst_port; > udph->source = htons(ovs_tnl_get_src_port(skb)); > udph->check = 0; > udph->len = htons(skb->len - skb_transport_offset(skb)); > @@ -192,7 +160,7 @@ static void lisp_build_header(const struct vport *vport, > /* Called with rcu_read_lock and BH disabled. */ > static int lisp_rcv(struct sock *sk, struct sk_buff *skb) > { > - struct lisp_port *lisp_port; > + struct tnl_udp_vport *udp_vport; > struct lisphdr *lisph; > struct iphdr *iph, *inner_iph; > struct ovs_key_ipv4_tunnel tun_key; > @@ -200,8 +168,10 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) > struct ethhdr *ethh; > __be16 protocol; > > - lisp_port = lisp_find_port(dev_net(skb->dev), udp_hdr(skb)->dest); > - if (unlikely(!lisp_port)) > + udp_vport = ovs_tnl_udp_find_port(dev_net(skb->dev), > + udp_hdr(skb)->dest, > + &lisp_ports); > + if (unlikely(!udp_vport)) > goto error; > > if (unlikely(!pskb_may_pull(skb, LISP_HLEN))) > @@ -240,7 +210,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) > ethh->h_source[0] = 0x02; > ethh->h_proto = protocol; > > - ovs_tnl_rcv(vport_from_priv(lisp_port), skb, &tun_key); > + ovs_tnl_rcv(vport_from_priv(udp_vport), skb, &tun_key); > goto out; > > error: > @@ -249,114 +219,11 @@ out: > return 0; > } > > -/* Arbitrary value. Irrelevant as long as it's not 0 since we set the > handler. */ > -#define UDP_ENCAP_LISP 1 > -static int lisp_socket_init(struct lisp_port *lisp_port, struct net *net) > -{ > - struct sockaddr_in sin; > - int err; > - > - err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, > - &lisp_port->lisp_rcv_socket); > - if (err) > - goto error; > - > - /* release net ref. */ > - sk_change_net(lisp_port->lisp_rcv_socket->sk, net); > - > - sin.sin_family = AF_INET; > - sin.sin_addr.s_addr = htonl(INADDR_ANY); > - sin.sin_port = lisp_port->dst_port; > - > - err = kernel_bind(lisp_port->lisp_rcv_socket, (struct sockaddr *)&sin, > - sizeof(struct sockaddr_in)); > - if (err) > - goto error_sock; > - > - udp_sk(lisp_port->lisp_rcv_socket->sk)->encap_type = UDP_ENCAP_LISP; > - udp_sk(lisp_port->lisp_rcv_socket->sk)->encap_rcv = lisp_rcv; > - > - udp_encap_enable(); > - > - return 0; > - > -error_sock: > - sk_release_kernel(lisp_port->lisp_rcv_socket->sk); > -error: > - pr_warn("cannot register lisp protocol handler: %d\n", err); > - return err; > -} > - > -static int lisp_get_options(const struct vport *vport, struct sk_buff *skb) > -{ > - struct lisp_port *lisp_port = lisp_vport(vport); > - > - if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, > ntohs(lisp_port->dst_port))) > - return -EMSGSIZE; > - return 0; > -} > - > -static void lisp_tnl_destroy(struct vport *vport) > -{ > - struct lisp_port *lisp_port = lisp_vport(vport); > - > - list_del_rcu(&lisp_port->list); > - /* Release socket */ > - sk_release_kernel(lisp_port->lisp_rcv_socket->sk); > - > - ovs_vport_deferred_free(vport); > -} > > static struct vport *lisp_tnl_create(const struct vport_parms *parms) > { > - struct net *net = ovs_dp_get_net(parms->dp); > - struct nlattr *options = parms->options; > - struct lisp_port *lisp_port; > - struct vport *vport; > - struct nlattr *a; > - int err; > - u16 dst_port; > - > - if (!options) { > - err = -EINVAL; > - goto error; > - } > - > - a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); > - if (a && nla_len(a) == sizeof(u16)) { > - dst_port = nla_get_u16(a); > - } else { > - /* Require destination port from userspace. */ > - err = -EINVAL; > - goto error; > - } > - > - /* Verify if we already have a socket created for this port */ > - if (lisp_find_port(net, htons(dst_port))) { > - err = -EEXIST; > - goto error; > - } > - > - vport = ovs_vport_alloc(sizeof(struct lisp_port), > - &ovs_lisp_vport_ops, parms); > - if (IS_ERR(vport)) > - return vport; > - > - lisp_port = lisp_vport(vport); > - lisp_port->dst_port = htons(dst_port); > - strncpy(lisp_port->name, parms->name, IFNAMSIZ); > - > - err = lisp_socket_init(lisp_port, net); > - if (err) > - goto error_free; > - > - list_add_tail_rcu(&lisp_port->list, &lisp_ports); > - return vport; > - > -error_free: > - ovs_vport_free(vport); > -error: > - return ERR_PTR(err); > + return ovs_tnl_udp_create(parms, &ovs_lisp_vport_ops, &lisp_ports, > + lisp_rcv); > } > > static int lisp_tnl_send(struct vport *vport, struct sk_buff *skb) > @@ -374,25 +241,19 @@ static int lisp_tnl_send(struct vport *vport, struct > sk_buff *skb) > /* Pop off "inner" Ethernet header */ > skb_pull(skb, network_offset); > tnl_len = ovs_tnl_send(vport, skb, IPPROTO_UDP, > - LISP_HLEN, lisp_build_header); > + LISP_HLEN, lisp_build_header); > return tnl_len > 0 ? tnl_len + network_offset : tnl_len; > default: > return 0; > } > } > > -static const char *lisp_get_name(const struct vport *vport) > -{ > - struct lisp_port *lisp_port = lisp_vport(vport); > - return lisp_port->name; > -} > - > const struct vport_ops ovs_lisp_vport_ops = { > .type = OVS_VPORT_TYPE_LISP, > .create = lisp_tnl_create, > - .destroy = lisp_tnl_destroy, > - .get_name = lisp_get_name, > - .get_options = lisp_get_options, > + .destroy = ovs_tnl_udp_destroy, > + .get_name = ovs_tnl_udp_get_name, > + .get_options = ovs_tnl_udp_get_options, > .send = lisp_tnl_send, > }; > #else > diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c > index d140c3b..a7f7e1e 100644 > --- a/datapath/vport-vxlan.c > +++ b/datapath/vport-vxlan.c > @@ -50,41 +50,8 @@ struct vxlanhdr { > > #define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) > > -/** > - * struct vxlan_port - Keeps track of open UDP ports > - * @dst_port: vxlan UDP port no. > - * @list: list element in @vxlan_ports. > - * @vxlan_rcv_socket: The socket created for this port number. > - * @name: vport name. > - */ > -struct vxlan_port { > - __be16 dst_port; > - struct list_head list; > - struct socket *vxlan_rcv_socket; > - char name[IFNAMSIZ]; > -}; > - > static LIST_HEAD(vxlan_ports); > > -static inline struct vxlan_port *vxlan_vport(const struct vport *vport) > -{ > - return vport_priv(vport); > -} > - > -static struct vxlan_port *vxlan_find_port(struct net *net, __be16 port) > -{ > - struct vxlan_port *vxlan_port; > - > - list_for_each_entry_rcu(vxlan_port, &vxlan_ports, list) { > - > - if (vxlan_port->dst_port == port && > - net_eq(sock_net(vxlan_port->vxlan_rcv_socket->sk), net)) > - return vxlan_port; > - } > - > - return NULL; > -} > - > static inline struct vxlanhdr *vxlan_hdr(const struct sk_buff *skb) > { > return (struct vxlanhdr *)(udp_hdr(skb) + 1); > @@ -94,12 +61,12 @@ static void vxlan_build_header(const struct vport *vport, > struct sk_buff *skb, > int tunnel_hlen) > { > - struct vxlan_port *vxlan_port = vxlan_vport(vport); > + struct tnl_udp_vport *udp_vport = tnl_udp_vport(vport); > struct udphdr *udph = udp_hdr(skb); > struct vxlanhdr *vxh = (struct vxlanhdr *)(udph + 1); > const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key; > > - udph->dest = vxlan_port->dst_port; > + udph->dest = udp_vport->dst_port; > udph->source = htons(ovs_tnl_get_src_port(skb)); > udph->check = 0; > udph->len = htons(skb->len - skb_transport_offset(skb)); > @@ -111,14 +78,16 @@ static void vxlan_build_header(const struct vport *vport, > /* Called with rcu_read_lock and BH disabled. */ > static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) > { > - struct vxlan_port *vxlan_vport; > + struct tnl_udp_vport *udp_vport; > struct vxlanhdr *vxh; > struct iphdr *iph; > struct ovs_key_ipv4_tunnel tun_key; > __be64 key; > > - vxlan_vport = vxlan_find_port(dev_net(skb->dev), udp_hdr(skb)->dest); > - if (unlikely(!vxlan_vport)) > + udp_vport = ovs_tnl_udp_find_port(dev_net(skb->dev), > + udp_hdr(skb)->dest, > + &vxlan_ports); > + if (unlikely(!udp_vport)) > goto error; > > if (unlikely(!pskb_may_pull(skb, VXLAN_HLEN + ETH_HLEN))) > @@ -138,7 +107,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) > iph = ip_hdr(skb); > tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY); > > - ovs_tnl_rcv(vport_from_priv(vxlan_vport), skb, &tun_key); > + ovs_tnl_rcv(vport_from_priv(udp_vport), skb, &tun_key); > goto out; > > error: > @@ -147,113 +116,10 @@ out: > return 0; > } > > -/* Random value. Irrelevant as long as it's not 0 since we set the handler. > */ > -#define UDP_ENCAP_VXLAN 1 > -static int vxlan_socket_init(struct vxlan_port *vxlan_port, struct net *net) > -{ > - struct sockaddr_in sin; > - int err; > - > - err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, > - &vxlan_port->vxlan_rcv_socket); > - if (err) > - goto error; > - > - /* release net ref. */ > - sk_change_net(vxlan_port->vxlan_rcv_socket->sk, net); > - > - sin.sin_family = AF_INET; > - sin.sin_addr.s_addr = htonl(INADDR_ANY); > - sin.sin_port = vxlan_port->dst_port; > - > - err = kernel_bind(vxlan_port->vxlan_rcv_socket, (struct sockaddr *)&sin, > - sizeof(struct sockaddr_in)); > - if (err) > - goto error_sock; > - > - udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_type = UDP_ENCAP_VXLAN; > - udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_rcv = vxlan_rcv; > - > - udp_encap_enable(); > - > - return 0; > - > -error_sock: > - sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk); > -error: > - pr_warn("cannot register vxlan protocol handler\n"); > - return err; > -} > - > -static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb) > -{ > - struct vxlan_port *vxlan_port = vxlan_vport(vport); > - > - if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, > ntohs(vxlan_port->dst_port))) > - return -EMSGSIZE; > - return 0; > -} > - > -static void vxlan_tnl_destroy(struct vport *vport) > -{ > - struct vxlan_port *vxlan_port = vxlan_vport(vport); > - > - list_del_rcu(&vxlan_port->list); > - /* Release socket */ > - sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk); > - > - ovs_vport_deferred_free(vport); > -} > - > static struct vport *vxlan_tnl_create(const struct vport_parms *parms) > { > - struct net *net = ovs_dp_get_net(parms->dp); > - struct nlattr *options = parms->options; > - struct vxlan_port *vxlan_port; > - struct vport *vport; > - struct nlattr *a; > - int err; > - u16 dst_port; > - > - if (!options) { > - err = -EINVAL; > - goto error; > - } > - a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); > - if (a && nla_len(a) == sizeof(u16)) { > - dst_port = nla_get_u16(a); > - } else { > - /* Require destination port from userspace. */ > - err = -EINVAL; > - goto error; > - } > - > - /* Verify if we already have a socket created for this port */ > - if (vxlan_find_port(net, htons(dst_port))) { > - err = -EEXIST; > - goto error; > - } > - > - vport = ovs_vport_alloc(sizeof(struct vxlan_port), > - &ovs_vxlan_vport_ops, parms); > - if (IS_ERR(vport)) > - return vport; > - > - vxlan_port = vxlan_vport(vport); > - vxlan_port->dst_port = htons(dst_port); > - strncpy(vxlan_port->name, parms->name, IFNAMSIZ); > - > - err = vxlan_socket_init(vxlan_port, net); > - if (err) > - goto error_free; > - > - list_add_tail_rcu(&vxlan_port->list, &vxlan_ports); > - return vport; > - > -error_free: > - ovs_vport_free(vport); > -error: > - return ERR_PTR(err); > + return ovs_tnl_udp_create(parms, &ovs_vxlan_vport_ops, &vxlan_ports, > + vxlan_rcv); > } > > static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) > @@ -265,18 +131,12 @@ static int vxlan_tnl_send(struct vport *vport, struct > sk_buff *skb) > VXLAN_HLEN, vxlan_build_header); > } > > -static const char *vxlan_get_name(const struct vport *vport) > -{ > - struct vxlan_port *vxlan_port = vxlan_vport(vport); > - return vxlan_port->name; > -} > - > const struct vport_ops ovs_vxlan_vport_ops = { > .type = OVS_VPORT_TYPE_VXLAN, > .create = vxlan_tnl_create, > - .destroy = vxlan_tnl_destroy, > - .get_name = vxlan_get_name, > - .get_options = vxlan_get_options, > + .destroy = ovs_tnl_udp_destroy, > + .get_name = ovs_tnl_udp_get_name, > + .get_options = ovs_tnl_udp_get_options, > .send = vxlan_tnl_send, > }; > #else > -- > 1.7.10.4 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev