From patchwork Fri Feb 5 10:47:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: niti1489@gmail.com X-Patchwork-Id: 579431 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (unknown [IPv6:2600:3c00::f03c:91ff:fe6e:bdf7]) by ozlabs.org (Postfix) with ESMTP id 923FE140778 for ; Fri, 5 Feb 2016 21:48:18 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=ij4QL1H5; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id E5A2C10E1A; Fri, 5 Feb 2016 02:48:16 -0800 (PST) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id 52FDA10E19 for ; Fri, 5 Feb 2016 02:48:15 -0800 (PST) Received: from bar3.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 717E016340A for ; Fri, 5 Feb 2016 03:48:14 -0700 (MST) X-ASG-Debug-ID: 1454669289-03dd7b24a631570001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar3.cudamail.com with ESMTP id snxawdvSHWu8VCMY (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 05 Feb 2016 03:48:10 -0700 (MST) X-Barracuda-Envelope-From: niti1489@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO mail-pa0-f67.google.com) (209.85.220.67) by mx3-pf2.cudamail.com with ESMTPS (RC4-SHA encrypted); 5 Feb 2016 10:48:02 -0000 Received-SPF: pass (mx3-pf2.cudamail.com: SPF record at _netblocks.google.com designates 209.85.220.67 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.85.220.67 X-Barracuda-RBL-IP: 209.85.220.67 Received: by mail-pa0-f67.google.com with SMTP id pv5so2327036pac.0 for ; Fri, 05 Feb 2016 02:48:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=Duz/Zz+y0erjnTyOW4q5IxwKhmBSwi9kd2mGkC/9WgE=; b=ij4QL1H5FXMe7po6R5BbG9FnQAv6cgc/T8j+LBFpyqC8CCfdOfQ43Um+6/PBrEhhMc tsTKX/qgeHeXOH0yVAivMHl1Rf4rO2Map+liNPi1vx3/2wCTRr3euV11TUUHhT2D5WpM +G4bTOASITtq1csFEhB9I3DwVrvsgwfo9lfOiS2ieqlHY7/2oLFJoOPuppuxOj+aapKp BV8ODipHzbtJ0t9oJAS1gWEKvAQLdUOBDmkvOyi7dXCEnyEM+MkCzkwX+iJDnee0SQa0 I8OHuhLDfrTpRp3h3GCVxd4PS5ChH2jrx//tm4lv5zsqciumdIA3acZmVo8w+EeBXVZi r9IQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=Duz/Zz+y0erjnTyOW4q5IxwKhmBSwi9kd2mGkC/9WgE=; b=NaienGAwqJre4nCSqVwYQoidi4rYFYY0BpsUT5m0st/Zf8IE5klTvsZiBOjfhK7G0w sf8xC8TjvDSwrNHPdpVNO++ssyEOcTF07LUA5RK0Rald5pmp8qj1uuhyZ225GCyg6C07 oOGwh80GYzZO5V0ProvEpMJ8TFKXFUuKWPbGGeQevJCcakXL2Tdlt4+RWVSTT16C7T1a OxjdyGawXoCA+zIuVDfu/SHgDlijHaUU30uioGI6NdexW7kNilqWH57Y89jKX6v0EPaq fi1naRvBfp2NvxBLkI1XuyBR6L6T0+OCXOhbSTrQTRTJGMHrkMHuF/qQ+WiyD2pvIQj2 Bt2w== X-Gm-Message-State: AG10YOR8vju2DaJlSgG61B69BkqWG7EsB76MvZHqtVNvZul7F/UFPb8dsvSwQg3ktlvY4Q== X-Received: by 10.66.124.200 with SMTP id mk8mr18861749pab.43.1454669282224; Fri, 05 Feb 2016 02:48:02 -0800 (PST) Received: from localhost.localdomain ([14.98.159.111]) by smtp.gmail.com with ESMTPSA id v29sm23582615pfa.31.2016.02.05.02.47.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 05 Feb 2016 02:48:01 -0800 (PST) X-CudaMail-Envelope-Sender: niti1489@gmail.com From: Niti Rohilla X-Google-Original-From: Niti Rohilla To: dev@openvswitch.org X-CudaMail-MID: CM-V2-204004229 X-CudaMail-DTE: 020516 X-CudaMail-Originating-IP: 209.85.220.67 Date: Fri, 5 Feb 2016 16:17:43 +0530 X-ASG-Orig-Subj: [##CM-V2-204004229##][PATCH v1] Basic GTP-U tunnel implementation in ovs Message-Id: <1454669263-12898-1-git-send-email-niti.rohilla@tcs.com> X-Mailer: git-send-email 2.5.0 X-GBUdb-Analysis: 0, 209.85.220.67, Ugly c=0.165853 p=-0.2 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1454669290 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using per-user scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.26766 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Cc: deepankar.gupta@tcs.com, partha.datta@tcs.com Subject: [ovs-dev] [PATCH v1] Basic GTP-U tunnel implementation in ovs X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" GPRS Tunneling Protocol (GTP) is a group of IP-based communications protocols used to carry general packet radio service (GPRS) within GSM, UMTS and LTE networks. GTP-U is used for carrying user data within the GPRS core network and between the radio access network and the core network. In case of GTP, the tunneling code attaches a header with harcoded source and destination MAC address 06:00:00:00:00:00, where the locally administered bit is set to 1. This patch adds the implementation of mandatory part of GTP-U protocol and supports only G-PDU messages. In G-PDU message, GTP-U header is followed by a T-PDU. Signed-off-by:Niti Rohilla Co-authored-by:Saloni Jain --- datapath/Modules.mk | 2 + datapath/linux/Modules.mk | 2 + datapath/linux/compat/gtp.c | 714 ++++++++++++++++++++++ datapath/linux/compat/include/linux/if_link.h | 7 + datapath/linux/compat/include/linux/openvswitch.h | 1 + datapath/linux/compat/include/net/gtp.h | 24 + datapath/vport-gtp.c | 150 +++++ datapath/vport.c | 7 + lib/dpif-netlink.c | 5 + lib/netdev-vport.c | 12 +- ofproto/ofproto-dpif-ipfix.c | 6 + ofproto/ofproto-dpif-sflow.c | 6 +- tests/ovs-vsctl.at | 6 +- tests/system-kmod-macros.at | 2 +- tests/tunnel.at | 12 + vswitchd/vswitch.xml | 16 + 16 files changed, 968 insertions(+), 4 deletions(-) create mode 100644 datapath/linux/compat/gtp.c create mode 100644 datapath/linux/compat/include/net/gtp.h create mode 100644 datapath/vport-gtp.c diff --git a/datapath/Modules.mk b/datapath/Modules.mk index 3ffeee2..77c2ea6 100644 --- a/datapath/Modules.mk +++ b/datapath/Modules.mk @@ -9,6 +9,7 @@ both_modules = \ vport_geneve \ vport_gre \ vport_lisp \ + vport_gtp \ vport_stt \ vport_vxlan # When changing the name of 'build_modules', please also update the @@ -32,6 +33,7 @@ vport_geneve_sources = vport-geneve.c vport_vxlan_sources = vport-vxlan.c vport_gre_sources = vport-gre.c vport_lisp_sources = vport-lisp.c +vport_gtp_sources = vport-gtp.c vport_stt_sources = vport-stt.c openvswitch_headers = \ diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index 6ab52a7..7c1b19d 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -14,6 +14,7 @@ openvswitch_sources += \ linux/compat/ip_tunnels_core.c \ linux/compat/ip6_output.c \ linux/compat/lisp.c \ + linux/compat/gtp.c \ linux/compat/netdevice.c \ linux/compat/net_namespace.c \ linux/compat/nf_conntrack_core.c \ @@ -51,6 +52,7 @@ openvswitch_headers += \ linux/compat/include/linux/kconfig.h \ linux/compat/include/linux/kernel.h \ linux/compat/include/net/lisp.h \ + linux/compat/include/net/gtp.h \ linux/compat/include/linux/list.h \ linux/compat/include/linux/mpls.h \ linux/compat/include/linux/net.h \ diff --git a/datapath/linux/compat/gtp.c b/datapath/linux/compat/gtp.c new file mode 100644 index 0000000..4671ec2 --- /dev/null +++ b/datapath/linux/compat/gtp.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2015 Nicira, Inc. + * Copyright (c) 2013 Cisco Systems, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "datapath.h" +#include "gso.h" +#include "vport.h" +#include "gso.h" +#include "vport-netdev.h" + +#define GTP_UDP_PORT 2152 +#define GTP_NETDEV_VER "0.1" +static int gtp_net_id; + +/* Pseudo network device */ +struct gtp_dev { + struct net *net; /* netns for packet i/o */ + struct net_device *dev; /* netdev for gtp tunnel */ + struct socket *sock; + __be16 dst_port; + struct list_head next; +}; + +/* per-network namespace private data for this module */ +struct gtp_net { + struct list_head gtp_list; +}; + +/* + * GTP encapsulation header: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | V |P|R|E|S|N| Message Type | Total Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TEID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Sequence Number | N-PDU Number | Next Extension| + * | | | Header type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Extension Header: + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Total length | Contents | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Contents | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Contents | Next Extntn hdr | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/** + * struct gtphdr - GTP header: + * + * @version(V): 3-bit field. For GTPv1, this has a value of 1. + * @protocol_type(P): a 1-bit value that differentiates GTP (value 1) from + * GTP' (value 0). + * @reserved(R): a 1-bit reserved field (must be 0). + * @extension_header_flag(E): a 1-bit value that states whether there is an + * extension header optional field. + * @sequence_number_flag(S): a 1-bit value that states whether there is a + * Sequence Number optional field. + * @n_pdu_number_flag(N): a 1-bit value that states whether there is a N-PDU + * number optional field. + * @message_type: an 8-bit field that indicates the type of GTP message. + * @total_length: a 16-bit field that indicates the length of the payload in + * bytes (rest of the packet following the mandatory 8-byte GTP + * header). Includes the optional fields. + * @teid: A 32-bit(4-octet) field used to multiplex different connections in + the same GTP tunnel. + * @sequence_number: an (optional) 16-bit field. This field exists if any of the + * E, S, or PN bits are on. The field must be interpreted only + * if the S bit is on. + * @n_pdu_number: an (optional) 8-bit field. This field exists if any of the E, + * S, or PN bits are on. The field must be interpreted only if + * the PN bit is on. + * @next_extension_header_type: an (optional) 8-bit field. This field exists if + * any of the E, S, or PN bits are on. The field + * must be interpreted only if the E bit is on. + * + * Extenstion header: + * @length: an 8-bit field. This field states the length of this extension + * header, including the length, the contents, and the next extension + * header field, in 4-octet units, so the length of the extension must + * always be a multiple of 4. + * @contents: extension header contents. + * @next_extension_header: an 8-bit field. It states the type of the next + * extension, or 0 if no next extension exists. This + * permits chaining several next extension headers. + */ + +struct gtp_extension_hdr { + u8 length; + u8 next_extension_hdr_type; + u8 extension_data[]; +}; + +struct gtphdr { +#ifdef __LITTLE_ENDIAN_BITFIELD + __u8 n_pdu_number_flag:1; + __u8 sequence_hdr_flag:1; + __u8 extension_hdr_flag:1; + __u8 reserved:1; + __u8 protocol_type:1; + __u8 version:2; +#else + __u8 version:2; + __u8 protocol_type:1; + __u8 reserved:1; + __u8 extension_hdr_flag:1; + __u8 sequence_hdr_flag:1; + __u8 n_pdu_number_flag:1; +#endif + __u8 message_type; + __be16 total_length; + __be32 teid; + struct gtp_extension_hdr extensions[]; +}; + +#define GTP_HLEN (sizeof(struct udphdr) + sizeof(struct gtphdr)) + +static inline struct gtphdr *gtp_hdr(const struct sk_buff *skb) +{ + return (struct gtphdr *)(udp_hdr(skb) + 1); +} + +/* Compute source UDP port for outgoing packet. + * Currently we use the flow hash. + */ +static u16 get_src_port(struct net *net, struct sk_buff *skb) +{ + u32 hash = skb_get_hash(skb); + unsigned int range; + int high; + int low; + + if (!hash) { + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *iph; + int size = (sizeof(iph->saddr) * 2) / sizeof(u32); + + iph = (struct iphdr *) skb_network_header(skb); + hash = jhash2((const u32 *)&iph->saddr, size, 0); + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6hdr; + + ipv6hdr = (struct ipv6hdr *) skb_network_header(skb); + hash = jhash2((const u32 *)&ipv6hdr->saddr, + (sizeof(struct in6_addr) * 2) / sizeof(u32), 0); + } else { + pr_warn_once("GTP inner protocol is not IP when " + "calculating hash.\n"); + } + } + + inet_get_local_port_range(net, &low, &high); + range = (high - low) + 1; + return (((u64) hash * range) >> 32) + low; +} + +static void gtp_build_header(struct sk_buff *skb, + const struct ip_tunnel_key *tun_key) +{ + struct gtphdr *gtph; + + gtph = (struct gtphdr *)__skb_push(skb, sizeof(struct gtphdr)); + gtph->version = 1; /* GTP-U version 1 */ + gtph->protocol_type = 1; /* GTP Protocol */ + gtph->reserved = 0; /* Reserved flags, set to 0 */ + gtph->extension_hdr_flag = 0; /* No extension header present */ + gtph->sequence_hdr_flag = 0; /* No Sequence No. present */ + gtph->n_pdu_number_flag = 0; /* No N PDU present */ + gtph->message_type = 255; /* GPDU Packets */ + /* mandatory part of GTP header first 8 octets */ + gtph->total_length = htons(skb->len) - htons(sizeof(struct gtphdr)); + gtph->teid = htonl(be64_to_cpu(tun_key->tun_id)); +} + +/* Called with rcu_read_lock and BH disabled. */ +static int gtp_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct net_device *dev; + struct gtphdr *gtph; + struct iphdr *inner_iph; + struct metadata_dst *tun_dst; +#ifndef HAVE_METADATA_DST + struct metadata_dst temp; +#endif + __be64 key; + struct ethhdr *ethh; + __be16 protocol; + + dev = rcu_dereference_sk_user_data(sk); + if (unlikely(!dev)) + goto error; + + if (iptunnel_pull_header(skb, GTP_HLEN, 0)) + goto error; + + gtph = gtp_hdr(skb); + + key = cpu_to_be64(ntohl(gtph->teid)); + + /* Save outer tunnel values */ +#ifndef HAVE_METADATA_DST + tun_dst = &temp; + ovs_udp_tun_rx_dst(&tun_dst->u.tun_info, skb, AF_INET, TUNNEL_KEY, key, 0); +#else + tun_dst = udp_tun_rx_dst(skb, AF_INET, TUNNEL_KEY, key, 0); +#endif + /* Drop non-IP inner packets */ + inner_iph = (struct iphdr *)(gtph + 1); + switch (inner_iph->version) { + case 4: + protocol = htons(ETH_P_IP); + break; + case 6: + protocol = htons(ETH_P_IPV6); + break; + default: + goto error; + } + skb->protocol = protocol; + + /* Add Ethernet header */ + ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); + memset(ethh, 0, ETH_HLEN); + ethh->h_dest[0] = 0x06; + ethh->h_source[0] = 0x06; + ethh->h_proto = protocol; + + ovs_ip_tunnel_rcv(dev, skb, tun_dst); + goto out; + +error: + kfree_skb(skb); +out: + return 0; +} + +netdev_tx_t rpl_gtp_xmit(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct gtp_dev *gtp_dev = netdev_priv(dev); + struct net *net = gtp_dev->net; + int network_offset = skb_network_offset(skb); + struct ip_tunnel_info *info; + struct ip_tunnel_key *tun_key; + struct rtable *rt; + int min_headroom; + __be16 src_port, dst_port; + struct flowi4 fl; + __be16 df; + int err; + + info = skb_tunnel_info(skb); + if (unlikely(!info)) { + err = -EINVAL; + goto error; + } + + if (skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6)) { + err = 0; + goto error; + } + + tun_key = &info->key; + + /* Route lookup */ + memset(&fl, 0, sizeof(fl)); + fl.daddr = tun_key->u.ipv4.dst; + fl.saddr = tun_key->u.ipv4.src; + fl.flowi4_tos = RT_TOS(tun_key->tos); + fl.flowi4_mark = skb->mark; + fl.flowi4_proto = IPPROTO_UDP; + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto error; + } + + min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + + sizeof(struct iphdr) + GTP_HLEN; + + if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { + int head_delta = SKB_DATA_ALIGN(min_headroom - + skb_headroom(skb) + 16); + + err = pskb_expand_head(skb, max_t(int, head_delta, 0), + 0, GFP_ATOMIC); + if (unlikely(err)) + goto err_free_rt; + } + + /* Reset l2 headers. */ + skb_pull(skb, network_offset); + skb_reset_mac_header(skb); + vlan_set_tci(skb, 0); + + skb = udp_tunnel_handle_offloads(skb, false, 0, false); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + skb = NULL; + goto err_free_rt; + } + + src_port = htons(get_src_port(net, skb)); + dst_port = gtp_dev->dst_port; + + gtp_build_header(skb, tun_key); + + skb->ignore_df = 1; + + ovs_skb_set_inner_protocol(skb, skb->protocol); + + df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; + err = udp_tunnel_xmit_skb(rt, gtp_dev->sock->sk, skb, + fl.saddr, tun_key->u.ipv4.dst, + tun_key->tos, tun_key->ttl, + df, src_port, dst_port, false, true); + + iptunnel_xmit_stats(err, &dev->stats, + (struct pcpu_sw_netstats __percpu *)dev->tstats); + return NETDEV_TX_OK; + +err_free_rt: + ip_rt_put(rt); +error: + kfree_skb(skb); + return NETDEV_TX_OK; +} +EXPORT_SYMBOL(rpl_gtp_xmit); + +#ifdef HAVE_DEV_TSTATS +/* Setup stats when device is created */ +static int gtp_init(struct net_device *dev) +{ + dev->tstats = (typeof(dev->tstats)) + netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + return 0; +} + +static void gtp_uninit(struct net_device *dev) +{ + free_percpu(dev->tstats); +} +#endif + +static struct socket *create_sock(struct net *net, bool ipv6, + __be16 port) +{ + struct socket *sock; + struct udp_port_cfg udp_conf; + int err; + + memset(&udp_conf, 0, sizeof(udp_conf)); + + if (ipv6) { + udp_conf.family = AF_INET6; + } else { + udp_conf.family = AF_INET; + udp_conf.local_ip.s_addr = htonl(INADDR_ANY); + } + + udp_conf.local_udp_port = port; + + /* Open UDP socket */ + err = udp_sock_create(net, &udp_conf, &sock); + if (err < 0) + return ERR_PTR(err); + + return sock; +} + +static int gtp_open(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + struct udp_tunnel_sock_cfg tunnel_cfg; + struct net *net = gtp->net; + + gtp->sock = create_sock(net, false, gtp->dst_port); + if (IS_ERR(gtp->sock)) + return PTR_ERR(gtp->sock); + + /* Mark socket as an encapsulation socket */ + tunnel_cfg.sk_user_data = dev; + tunnel_cfg.encap_type = 1; + tunnel_cfg.encap_rcv = gtp_rcv; + tunnel_cfg.encap_destroy = NULL; + setup_udp_tunnel_sock(net, gtp->sock, &tunnel_cfg); + return 0; +} + +static int gtp_stop(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + udp_tunnel_sock_release(gtp->sock); + gtp->sock = NULL; + return 0; +} + +static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) +{ +#ifdef HAVE_METADATA_DST + return rpl_gtp_xmit(skb); +#else + /* Drop All packets coming from networking stack. OVS-CB is + * not initialized for these packets. + */ + + dev_kfree_skb(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; +#endif +} + +static const struct net_device_ops gtp_netdev_ops = { +#ifdef HAVE_DEV_TSTATS + .ndo_init = gtp_init, + .ndo_uninit = gtp_uninit, + .ndo_get_stats64 = ip_tunnel_get_stats64, +#endif + .ndo_open = gtp_open, + .ndo_stop = gtp_stop, + .ndo_start_xmit = gtp_dev_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +}; + +static void gtp_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strlcpy(drvinfo->version, GTP_NETDEV_VER, sizeof(drvinfo->version)); + strlcpy(drvinfo->driver, "gtp", sizeof(drvinfo->driver)); +} + +static const struct ethtool_ops gtp_ethtool_ops = { + .get_drvinfo = gtp_get_drvinfo, + .get_link = ethtool_op_get_link, +}; + +/* Info for udev, that this is a virtual tunnel endpoint */ +static struct device_type gtp_type = { + .name = "gtp", +}; + +/* Initialize the device structure. */ +static void gtp_setup(struct net_device *dev) +{ + ether_setup(dev); + + dev->netdev_ops = >p_netdev_ops; + dev->ethtool_ops = >p_ethtool_ops; + dev->destructor = free_netdev; + + SET_NETDEV_DEVTYPE(dev, >p_type); + + dev->features |= NETIF_F_LLTX | NETIF_F_NETNS_LOCAL; + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->features |= NETIF_F_RXCSUM; + dev->features |= NETIF_F_GSO_SOFTWARE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) + dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_GSO_SOFTWARE; +#endif +#ifdef HAVE_METADATA_DST + netif_keep_dst(dev); +#endif + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; + eth_hw_addr_random(dev); +} + +static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { + [IFLA_GTP_PORT] = { .type = NLA_U16 }, +}; + +static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + + return 0; +} + +static struct gtp_dev *find_dev(struct net *net, __be16 dst_port) +{ + struct gtp_net *ln = net_generic(net, gtp_net_id); + struct gtp_dev *dev; + + list_for_each_entry(dev, &ln->gtp_list, next) { + if (dev->dst_port == dst_port) + return dev; + } + return NULL; +} + +static int gtp_configure(struct net *net, struct net_device *dev, + __be16 dst_port) +{ + struct gtp_net *ln = net_generic(net, gtp_net_id); + struct gtp_dev *gtp = netdev_priv(dev); + int err; + + gtp->net = net; + gtp->dev = dev; + + gtp->dst_port = dst_port; + + if (find_dev(net, dst_port)) + return -EBUSY; + + err = register_netdevice(dev); + if (err) + return err; + + list_add(>p->next, &ln->gtp_list); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static int gtp_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ +#else +static int gtp_newlink(struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) + +{ + struct net *net = &init_net; +#endif + __be16 dst_port = htons(GTP_UDP_PORT); + + if (data[IFLA_GTP_PORT]) + dst_port = nla_get_be16(data[IFLA_GTP_PORT]); + + return gtp_configure(net, dev, dst_port); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static void gtp_dellink(struct net_device *dev, struct list_head *head) +#else +static void gtp_dellink(struct net_device *dev) +#endif +{ + struct gtp_dev *gtp = netdev_priv(dev); + + list_del(>p->next); + unregister_netdevice_queue(dev, head); +} + +static size_t gtp_get_size(const struct net_device *dev) +{ + return nla_total_size(sizeof(__be32)); /* IFLA_GTP_PORT */ +} + +static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + if (nla_put_be16(skb, IFLA_GTP_PORT, gtp->dst_port)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static struct rtnl_link_ops gtp_link_ops __read_mostly = { + .kind = "gtp", + .maxtype = IFLA_GTP_MAX, + .policy = gtp_policy, + .priv_size = sizeof(struct gtp_dev), + .setup = gtp_setup, + .validate = gtp_validate, + .newlink = gtp_newlink, + .dellink = gtp_dellink, + .get_size = gtp_get_size, + .fill_info = gtp_fill_info, +}; + +struct net_device *rpl_gtp_dev_create_fb(struct net *net, const char *name, + u8 name_assign_type, u16 dst_port) +{ + struct nlattr *tb[IFLA_MAX + 1]; + struct net_device *dev; + int err; + + memset(tb, 0, sizeof(tb)); + dev = rtnl_create_link(net, (char *) name, name_assign_type, + >p_link_ops, tb); + if (IS_ERR(dev)) + return dev; + + err = gtp_configure(net, dev, htons(dst_port)); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + return dev; +} +EXPORT_SYMBOL_GPL(rpl_gtp_dev_create_fb); + +static int gtp_init_net(struct net *net) +{ + struct gtp_net *ln = net_generic(net, gtp_net_id); + + INIT_LIST_HEAD(&ln->gtp_list); + return 0; +} + +static void gtp_exit_net(struct net *net) +{ + struct gtp_net *ln = net_generic(net, gtp_net_id); + struct gtp_dev *gtp, *next; + struct net_device *dev, *aux; + LIST_HEAD(list); + + rtnl_lock(); + + /* gather any gtp devices that were moved into this ns */ + for_each_netdev_safe(net, dev, aux) + if (dev->rtnl_link_ops == >p_link_ops) + unregister_netdevice_queue(dev, &list); + + list_for_each_entry_safe(gtp, next, &ln->gtp_list, next) { + /* If gtp->dev is in the same netns, it was already added + * to the gtp by the previous loop. + */ + if (!net_eq(dev_net(gtp->dev), net)) + unregister_netdevice_queue(gtp->dev, &list); + } + + /* unregister the devices gathered above */ + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations gtp_net_ops = { + .init = gtp_init_net, + .exit = gtp_exit_net, + .id = >p_net_id, + .size = sizeof(struct gtp_net), +}; + +DEFINE_COMPAT_PNET_REG_FUNC(device) +int rpl_gtp_init_module(void) +{ + int rc; + + rc = register_pernet_subsys(>p_net_ops); + if (rc) + goto out1; + + rc = rtnl_link_register(>p_link_ops); + if (rc) + goto out2; + + pr_info("GTP tunneling driver\n"); + return 0; +out2: + unregister_pernet_subsys(>p_net_ops); +out1: + return rc; +} + +void rpl_gtp_cleanup_module(void) +{ + rtnl_link_unregister(>p_link_ops); + unregister_pernet_subsys(>p_net_ops); +} diff --git a/datapath/linux/compat/include/linux/if_link.h b/datapath/linux/compat/include/linux/if_link.h index 6209dcb..82e6562 100644 --- a/datapath/linux/compat/include/linux/if_link.h +++ b/datapath/linux/compat/include/linux/if_link.h @@ -46,6 +46,13 @@ enum { }; #define IFLA_LISP_MAX (__IFLA_LISP_MAX - 1) +/* GTP section */ +enum { + IFLA_GTP_PORT, /* destination port */ + __IFLA_GTP_MAX +}; +#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) + /* VXLAN section */ enum { #define IFLA_VXLAN_UNSPEC rpl_IFLA_VXLAN_UNSPEC diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 3b39ebb..49bdd51 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -235,6 +235,7 @@ enum ovs_vport_type { OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ + OVS_VPORT_TYPE_GTP = 107, /* GTP tunnel */ __OVS_VPORT_TYPE_MAX }; diff --git a/datapath/linux/compat/include/net/gtp.h b/datapath/linux/compat/include/net/gtp.h new file mode 100644 index 0000000..05623bd --- /dev/null +++ b/datapath/linux/compat/include/net/gtp.h @@ -0,0 +1,24 @@ +#ifndef __NET_GTP_WRAPPER_H +#define __NET_GTP_WRAPPER_H 1 + +#ifdef CONFIG_INET +#include +#endif + + +#ifdef CONFIG_INET +#define gtp_dev_create_fb rpl_gtp_dev_create_fb +struct net_device *rpl_gtp_dev_create_fb(struct net *net, const char *name, + u8 name_assign_type, u16 dst_port); +#endif /*ifdef CONFIG_INET */ + +#define gtp_init_module rpl_gtp_init_module +int rpl_gtp_init_module(void); + +#define gtp_cleanup_module rpl_gtp_cleanup_module +void rpl_gtp_cleanup_module(void); + +#define gtp_xmit rpl_gtp_xmit +netdev_tx_t rpl_gtp_xmit(struct sk_buff *skb); + +#endif /*ifdef__NET_GTP_H */ diff --git a/datapath/vport-gtp.c b/datapath/vport-gtp.c new file mode 100644 index 0000000..6892b2b --- /dev/null +++ b/datapath/vport-gtp.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "datapath.h" +#include "vport.h" +#include "vport-netdev.h" + +static struct vport_ops ovs_gtp_vport_ops; +/** + * struct gtp_port - Keeps track of open UDP ports + * @dst_port: destination port. + */ +struct gtp_port { + u16 port_no; +}; + +static inline struct gtp_port *gtp_vport(const struct vport *vport) +{ + return vport_priv(vport); +} + +static int gtp_get_options(const struct vport *vport, + struct sk_buff *skb) +{ + struct gtp_port *gtp_port = gtp_vport(vport); + + if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, gtp_port->port_no)) + return -EMSGSIZE; + return 0; +} + +static int gtp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct dp_upcall_info *upcall) +{ + struct gtp_port *gtp_port = gtp_vport(vport); + struct net *net = ovs_dp_get_net(vport->dp); + __be16 dport = htons(gtp_port->port_no); + __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); + + return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), + skb, IPPROTO_UDP, sport, dport); +} + +static struct vport *gtp_tnl_create(const struct vport_parms *parms) +{ + struct net *net = ovs_dp_get_net(parms->dp); + struct nlattr *options = parms->options; + struct gtp_port *gtp_port; + struct net_device *dev; + struct vport *vport; + struct nlattr *a; + u16 dst_port; + int err; + + 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; + } + + vport = ovs_vport_alloc(sizeof(struct gtp_port), + &ovs_gtp_vport_ops, parms); + if (IS_ERR(vport)) + return vport; + + gtp_port = gtp_vport(vport); + gtp_port->port_no = dst_port; + + rtnl_lock(); + dev = gtp_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port); + if (IS_ERR(dev)) { + rtnl_unlock(); + ovs_vport_free(vport); + return ERR_CAST(dev); + } + + dev_change_flags(dev, dev->flags | IFF_UP); + rtnl_unlock(); + return vport; +error: + return ERR_PTR(err); +} + +static struct vport *gtp_create(const struct vport_parms *parms) +{ + struct vport *vport; + + vport = gtp_tnl_create(parms); + if (IS_ERR(vport)) + return vport; + + return ovs_netdev_link(vport, parms->name); +} + +static struct vport_ops ovs_gtp_vport_ops = { + .type = OVS_VPORT_TYPE_GTP, + .create = gtp_create, + .destroy = ovs_netdev_tunnel_destroy, + .get_options = gtp_get_options, + .send = gtp_xmit, + .get_egress_tun_info = gtp_get_egress_tun_info, +}; + +static int __init ovs_gtp_tnl_init(void) +{ + return ovs_vport_ops_register(&ovs_gtp_vport_ops); +} + +static void __exit ovs_gtp_tnl_exit(void) +{ + ovs_vport_ops_unregister(&ovs_gtp_vport_ops); +} + +module_init(ovs_gtp_tnl_init); +module_exit(ovs_gtp_tnl_exit); + +MODULE_DESCRIPTION("OVS: Gtp switching port"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("vport-type-107"); diff --git a/datapath/vport.c b/datapath/vport.c index 7fd9858..24212ba 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,9 @@ int ovs_vport_init(void) err = lisp_init_module(); if (err) goto err_lisp; + err = gtp_init_module(); + if (err) + goto err_gtp; err = ipgre_init(); if (err) goto err_gre; @@ -86,6 +90,8 @@ err_vxlan: err_geneve: ipgre_fini(); err_gre: + gtp_cleanup_module(); +err_gtp: lisp_cleanup_module(); err_lisp: kfree(dev_table); @@ -103,6 +109,7 @@ void ovs_vport_exit(void) vxlan_cleanup_module(); geneve_cleanup_module(); ipgre_fini(); + gtp_cleanup_module(); lisp_cleanup_module(); kfree(dev_table); } diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 5e48393..8106c10 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -766,6 +766,9 @@ get_vport_type(const struct dpif_netlink_vport *vport) case OVS_VPORT_TYPE_LISP: return "lisp"; + case OVS_VPORT_TYPE_GTP: + return "gtp"; + case OVS_VPORT_TYPE_STT: return "stt"; @@ -798,6 +801,8 @@ netdev_to_ovs_vport_type(const struct netdev *netdev) return OVS_VPORT_TYPE_VXLAN; } else if (!strcmp(type, "lisp")) { return OVS_VPORT_TYPE_LISP; + } else if (!strcmp(type, "gtp")) { + return OVS_VPORT_TYPE_GTP; } else { return OVS_VPORT_TYPE_UNSPEC; } diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 88f5022..38ee727 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -56,6 +56,7 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5); #define GENEVE_DST_PORT 6081 #define VXLAN_DST_PORT 4789 #define LISP_DST_PORT 4341 +#define GTP_DST_PORT 2152 #define STT_DST_PORT 7471 #define VXLAN_HLEN (sizeof(struct udp_header) + \ @@ -156,7 +157,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev) return (class->get_config == get_tunnel_config && (!strcmp("geneve", type) || !strcmp("vxlan", type) || - !strcmp("lisp", type) || !strcmp("stt", type)) ); + !strcmp("lisp", type) || !strcmp("gtp", type) || + !strcmp("stt", type)) ); } const char * @@ -255,6 +257,8 @@ netdev_vport_construct(struct netdev *netdev_) dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT); } else if (!strcmp(type, "lisp")) { dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); + } else if (!strcmp(type, "gtp")) { + dev->tnl_cfg.dst_port = htons(GTP_DST_PORT); } else if (!strcmp(type, "stt")) { dev->tnl_cfg.dst_port = htons(STT_DST_PORT); } @@ -481,6 +485,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args) tnl_cfg.dst_port = htons(LISP_DST_PORT); } + if (!strcmp(type, "gtp")) { + tnl_cfg.dst_port = htons(GTP_DST_PORT); + } + if (!strcmp(type, "stt")) { tnl_cfg.dst_port = htons(STT_DST_PORT); } @@ -728,6 +736,7 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) || + (!strcmp("gtp", type) && dst_port != GTP_DST_PORT) || (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { smap_add_format(args, "dst_port", "%d", dst_port); } @@ -1563,6 +1572,7 @@ netdev_vport_tunnel_register(void) push_udp_header, netdev_vxlan_pop_header), TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL), + TUNNEL_CLASS("gtp", "gtp_sys", NULL, NULL, NULL), TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL), }; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index a610c53..3d434bc 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -71,6 +71,7 @@ enum dpif_ipfix_tunnel_type { DPIF_IPFIX_TUNNEL_STT = 0x04, DPIF_IPFIX_TUNNEL_IPSEC_GRE = 0x05, DPIF_IPFIX_TUNNEL_GENEVE = 0x07, + DPIF_IPFIX_TUNNEL_GTP = 0x08, NUM_DPIF_IPFIX_TUNNEL }; @@ -308,6 +309,7 @@ static uint8_t tunnel_protocol[NUM_DPIF_IPFIX_TUNNEL] = { IPPROTO_GRE, /* DPIF_IPFIX_TUNNEL_IPSEC_GRE */ 0 , /* reserved */ IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GENEVE*/ + IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GTP*/ }; OVS_PACKED( @@ -358,6 +360,7 @@ BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 32); * VxLAN: 24-bit VIN, * GRE: 32-bit key, * LISP: 24-bit instance ID + * GTP: 32-bit key * STT: 64-bit key */ #define MAX_TUNNEL_KEY_LEN 8 @@ -602,6 +605,9 @@ dpif_ipfix_add_tunnel_port(struct dpif_ipfix *di, struct ofport *ofport, } else if (strcmp(type, "lisp") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_LISP; dip->tunnel_key_length = 3; + } else if (strcmp(type, "gtp") == 0) { + dip->tunnel_type = DPIF_IPFIX_TUNNEL_GTP; + dip->tunnel_key_length = 4; } else if (strcmp(type, "geneve") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_GENEVE; dip->tunnel_key_length = 3; diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index f11699c..c4fe04f 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -62,7 +62,8 @@ enum dpif_sflow_tunnel_type { DPIF_SFLOW_TUNNEL_GRE, DPIF_SFLOW_TUNNEL_LISP, DPIF_SFLOW_TUNNEL_IPSEC_GRE, - DPIF_SFLOW_TUNNEL_GENEVE + DPIF_SFLOW_TUNNEL_GENEVE, + DPIF_SFLOW_TUNNEL_GTP }; struct dpif_sflow_port { @@ -592,6 +593,8 @@ dpif_sflow_tunnel_type(struct ofport *ofport) { return DPIF_SFLOW_TUNNEL_VXLAN; } else if (strcmp(type, "lisp") == 0) { return DPIF_SFLOW_TUNNEL_LISP; + } else if (strcmp(type, "gtp") == 0) { + return DPIF_SFLOW_TUNNEL_GTP; } else if (strcmp(type, "geneve") == 0) { return DPIF_SFLOW_TUNNEL_GENEVE; } @@ -616,6 +619,7 @@ dpif_sflow_tunnel_proto(enum dpif_sflow_tunnel_type tunnel_type) case DPIF_SFLOW_TUNNEL_VXLAN: case DPIF_SFLOW_TUNNEL_LISP: + case DPIF_SFLOW_TUNNEL_GTP: case DPIF_SFLOW_TUNNEL_GENEVE: ipproto = IPPROTO_UDP; diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index fc59652..c14425b 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -1235,6 +1235,7 @@ m4_foreach( [genev_sys], [gre_sys], [lisp_sys], +[gtp_sys], [vxlan_sys]], [ # Try creating the port @@ -1263,7 +1264,9 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ -- add-port br0 p4 -- set Interface p4 type=vxlan \ options:remote_ip=2.2.2.2 ofport_request=4 \ -- add-port br0 p5 -- set Interface p5 type=geneve \ - options:remote_ip=2.2.2.2 ofport_request=5]) + options:remote_ip=2.2.2.2 ofport_request=5 \ + -- add-port br0 p6 -- set Interface p6 type=gtp \ + options:remote_ip=2.2.2.2 ofport_request=6]) # Test creating all reserved tunnel port names m4_foreach( @@ -1271,6 +1274,7 @@ m4_foreach( [[genev_sys], [gre_sys], [lisp_sys], +[gtp_sys], [vxlan_sys]], [ # Try creating the port diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at index 20ee7bf..d01ade4 100644 --- a/tests/system-kmod-macros.at +++ b/tests/system-kmod-macros.at @@ -18,7 +18,7 @@ m4_define([_ADD_BR], [[add-br $1 -- set Bridge $1 protocols=OpenFlow10,OpenFlow1 m4_define([OVS_TRAFFIC_VSWITCHD_START], [AT_CHECK([modprobe openvswitch]) on_exit 'modprobe -r openvswitch' - m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_lisp], [vport_stt], [vport_vxlan]], + m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_lisp], [vport_stt], [vport_vxlan], [vport_gtp]], [modprobe -q mod || echo "Module mod not loaded." on_exit 'modprobe -q -r mod']) on_exit 'ovs-dpctl del-dp ovs-system' diff --git a/tests/tunnel.at b/tests/tunnel.at index 0c033da..a835d7f 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -352,6 +352,18 @@ AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([tunnel - GTP]) +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtp \ + options:remote_ip=1.1.1.1 ofport_request=1]) + +AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl + br0 65534/100: (dummy) + p1 1/2152: (gtp: remote_ip=1.1.1.1) +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([tunnel - different VXLAN UDP port]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341]) diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index c2ec914..cc757bd 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1897,6 +1897,22 @@

+
gtp
+
+ GPRS Tunneling Protocol (GTP) is a group of IP-based communications + protocols used to carry general packet radio service (GPRS) within GSM, + UMTS and LTE networks.GTP-U is used for carrying user data within the GPRS + core network and between the radio access network and the core network. + The user data transported can be packets in any of IPv4, IPv6, or PPP + formats. + The protocol is documented at + http://www.3gpp.org/DynaReport/29281.htm + + Open vSwitch uses UDP destination port 2152. The source port used for + GTP traffic varies on a per-flow basis and is in the ephemeral port + range. +
+
stt
The Stateless TCP Tunnel (STT) is particularly useful when tunnel