Feature #1832 ยป ovs-dev-v1-Basic-GTP-U-tunnel-implementation-in-ovs.patch
datapath/Modules.mk | ||
---|---|---|
9 | 9 |
vport_geneve \ |
10 | 10 |
vport_gre \ |
11 | 11 |
vport_lisp \ |
12 |
vport_gtp \ /* including gtp virtual port to kernel module */ |
|
12 | 13 |
vport_stt \ |
13 | 14 |
vport_vxlan |
14 | 15 |
# When changing the name of 'build_modules', please also update the |
... | ... | |
32 | 33 |
vport_vxlan_sources = vport-vxlan.c |
33 | 34 |
vport_gre_sources = vport-gre.c |
34 | 35 |
vport_lisp_sources = vport-lisp.c |
36 |
vport_gtp_sources = vport-gtp.c /* including the source file to include gtp vport into kernel */ |
|
35 | 37 |
vport_stt_sources = vport-stt.c |
36 | 38 | |
37 | 39 |
openvswitch_headers = \ |
datapath/linux/Modules.mk | ||
---|---|---|
14 | 14 |
linux/compat/ip_tunnels_core.c \ |
15 | 15 |
linux/compat/ip6_output.c \ |
16 | 16 |
linux/compat/lisp.c \ |
17 |
linux/compat/gtp.c \ /* Did not understand why it is included here also */ |
|
17 | 18 |
linux/compat/netdevice.c \ |
18 | 19 |
linux/compat/net_namespace.c \ |
19 | 20 |
linux/compat/nf_conntrack_core.c \ |
... | ... | |
51 | 52 |
linux/compat/include/linux/kconfig.h \ |
52 | 53 |
linux/compat/include/linux/kernel.h \ |
53 | 54 |
linux/compat/include/net/lisp.h \ |
55 |
linux/compat/include/net/gtp.h \ /* including the necessary header files */ |
|
54 | 56 |
linux/compat/include/linux/list.h \ |
55 | 57 |
linux/compat/include/linux/mpls.h \ |
56 | 58 |
linux/compat/include/linux/net.h \ |
datapath/linux/compat/gtp.c | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright (c) 2015 Nicira, Inc. |
|
3 |
* Copyright (c) 2013 Cisco Systems, Inc. |
|
4 |
* |
|
5 |
* This program is free software; you can redistribute it and/or |
|
6 |
* modify it under the terms of version 2 of the GNU General Public |
|
7 |
* License as published by the Free Software Foundation. |
|
8 |
* |
|
9 |
* This program is distributed in the hope that it will be useful, but |
|
10 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 |
* General Public License for more details. |
|
13 |
* |
|
14 |
*/ |
|
15 | ||
16 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|
17 | ||
18 |
#include <linux/version.h> |
|
19 | ||
20 |
#include <linux/in.h> |
|
21 |
#include <linux/ip.h> |
|
22 |
#include <linux/net.h> |
|
23 |
#include <linux/module.h> |
|
24 |
#include <linux/rculist.h> |
|
25 |
#include <linux/udp.h> |
|
26 | ||
27 |
#include <net/icmp.h> |
|
28 |
#include <net/ip.h> |
|
29 |
#include <net/gtp.h> |
|
30 |
#include <net/net_namespace.h> |
|
31 |
#include <net/netns/generic.h> |
|
32 |
#include <net/route.h> |
|
33 |
#include <net/udp.h> |
|
34 |
#include <net/udp_tunnel.h> |
|
35 |
#include <net/xfrm.h> |
|
36 | ||
37 |
#include "datapath.h" |
|
38 |
#include "gso.h" |
|
39 |
#include "vport.h" |
|
40 |
#include "gso.h" |
|
41 |
#include "vport-netdev.h" |
|
42 | ||
43 |
#define GTP_UDP_PORT 2152 |
|
44 |
#define GTP_NETDEV_VER "0.1" |
|
45 |
static int gtp_net_id; |
|
46 | ||
47 |
/* Pseudo network device */ |
|
48 |
struct gtp_dev { |
|
49 |
struct net *net; /* netns for packet i/o */ |
|
50 |
struct net_device *dev; /* netdev for gtp tunnel */ |
|
51 |
struct socket *sock; |
|
52 |
__be16 dst_port; |
|
53 |
struct list_head next; /*pointer to another gtp_dev */ |
|
54 |
}; |
|
55 | ||
56 |
/* per-network namespace private data for this module */ |
|
57 |
struct gtp_net { |
|
58 |
struct list_head gtp_list;/*pointer to another gtp_net */ |
|
59 |
}; |
|
60 | ||
61 |
/* |
|
62 |
* GTP encapsulation header: |
|
63 |
* |
|
64 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
65 |
* | V |P|R|E|S|N| Message Type | Total Length | |
|
66 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
67 |
* | TEID | |
|
68 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
69 |
* | Sequence Number | N-PDU Number | Next Extension| |
|
70 |
* | | | Header type | |
|
71 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
72 |
* |
|
73 |
* Extension Header: |
|
74 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
75 |
* | Total length | Contents | |
|
76 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
77 |
* | Contents | |
|
78 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
79 |
* | Contents | Next Extntn hdr | |
|
80 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
81 |
*/ |
|
82 | ||
83 |
/** |
|
84 |
* struct gtphdr - GTP header: |
|
85 |
* |
|
86 |
* @version(V): 3-bit field. For GTPv1, this has a value of 1. |
|
87 |
* @protocol_type(P): a 1-bit value that differentiates GTP (value 1) from |
|
88 |
* GTP' (value 0). |
|
89 |
* @reserved(R): a 1-bit reserved field (must be 0). |
|
90 |
* @extension_header_flag(E): a 1-bit value that states whether there is an |
|
91 |
* extension header optional field. |
|
92 |
* @sequence_number_flag(S): a 1-bit value that states whether there is a |
|
93 |
* Sequence Number optional field. |
|
94 |
* @n_pdu_number_flag(N): a 1-bit value that states whether there is a N-PDU |
|
95 |
* number optional field. |
|
96 |
* @message_type: an 8-bit field that indicates the type of GTP message. |
|
97 |
* @total_length: a 16-bit field that indicates the length of the payload in |
|
98 |
* bytes (rest of the packet following the mandatory 8-byte GTP |
|
99 |
* header). Includes the optional fields. |
|
100 |
* @teid: A 32-bit(4-octet) field used to multiplex different connections in |
|
101 |
the same GTP tunnel. |
|
102 |
* @sequence_number: an (optional) 16-bit field. This field exists if any of the |
|
103 |
* E, S, or PN bits are on. The field must be interpreted only |
|
104 |
* if the S bit is on. |
|
105 |
* @n_pdu_number: an (optional) 8-bit field. This field exists if any of the E, |
|
106 |
* S, or PN bits are on. The field must be interpreted only if |
|
107 |
* the PN bit is on. |
|
108 |
* @next_extension_header_type: an (optional) 8-bit field. This field exists if |
|
109 |
* any of the E, S, or PN bits are on. The field |
|
110 |
* must be interpreted only if the E bit is on. |
|
111 |
* |
|
112 |
* Extenstion header: |
|
113 |
* @length: an 8-bit field. This field states the length of this extension |
|
114 |
* header, including the length, the contents, and the next extension |
|
115 |
* header field, in 4-octet units, so the length of the extension must |
|
116 |
* always be a multiple of 4. |
|
117 |
* @contents: extension header contents. |
|
118 |
* @next_extension_header: an 8-bit field. It states the type of the next |
|
119 |
* extension, or 0 if no next extension exists. This |
|
120 |
* permits chaining several next extension headers. |
|
121 |
*/ |
|
122 | ||
123 |
struct gtp_extension_hdr { |
|
124 |
u8 length; |
|
125 |
u8 next_extension_hdr_type; |
|
126 |
u8 extension_data[]; |
|
127 |
}; |
|
128 | ||
129 |
struct gtphdr { |
|
130 |
#ifdef __LITTLE_ENDIAN_BITFIELD |
|
131 |
__u8 n_pdu_number_flag:1; |
|
132 |
__u8 sequence_hdr_flag:1; |
|
133 |
__u8 extension_hdr_flag:1; |
|
134 |
__u8 reserved:1; |
|
135 |
__u8 protocol_type:1; |
|
136 |
__u8 version:2; |
|
137 |
#else |
|
138 |
__u8 version:2; |
|
139 |
__u8 protocol_type:1; |
|
140 |
__u8 reserved:1; |
|
141 |
__u8 extension_hdr_flag:1; |
|
142 |
__u8 sequence_hdr_flag:1; |
|
143 |
__u8 n_pdu_number_flag:1; |
|
144 |
#endif |
|
145 |
__u8 message_type; |
|
146 |
__be16 total_length; |
|
147 |
__be32 teid; |
|
148 |
struct gtp_extension_hdr extensions[]; |
|
149 |
}; |
|
150 | ||
151 |
#define GTP_HLEN (sizeof(struct udphdr) + sizeof(struct gtphdr)) |
|
152 | ||
153 |
static inline struct gtphdr *gtp_hdr(const struct sk_buff *skb) |
|
154 |
{ |
|
155 |
return (struct gtphdr *)(udp_hdr(skb) + 1); /* Did not understand this command and also the struct declaration */ |
|
156 |
} |
|
157 | ||
158 |
/* Compute source UDP port for outgoing packet. |
|
159 |
* Currently we use the flow hash. |
|
160 |
*/ |
|
161 |
static u16 get_src_port(struct net *net, struct sk_buff *skb) |
|
162 |
{ |
|
163 |
u32 hash = skb_get_hash(skb); |
|
164 |
unsigned int range; |
|
165 |
int high;/* Not defined any value */ |
|
166 |
int low;/* Not defined any value */ |
|
167 | ||
168 |
if (!hash) { |
|
169 |
if (skb->protocol == htons(ETH_P_IP)) { |
|
170 |
struct iphdr *iph; |
|
171 |
int size = (sizeof(iph->saddr) * 2) / sizeof(u32); |
|
172 | ||
173 |
iph = (struct iphdr *) skb_network_header(skb); /* Did not understand */ |
|
174 |
hash = jhash2((const u32 *)&iph->saddr, size, 0); /* Did not understand */ |
|
175 |
} else if (skb->protocol == htons(ETH_P_IPV6)) { |
|
176 |
struct ipv6hdr *ipv6hdr; |
|
177 | ||
178 |
ipv6hdr = (struct ipv6hdr *) skb_network_header(skb); |
|
179 |
hash = jhash2((const u32 *)&ipv6hdr->saddr, |
|
180 |
(sizeof(struct in6_addr) * 2) / sizeof(u32), 0); /* Did not understand */ |
|
181 |
} else { |
|
182 |
pr_warn_once("GTP inner protocol is not IP when " |
|
183 |
"calculating hash.\n"); |
|
184 |
} |
|
185 |
} |
|
186 | ||
187 |
inet_get_local_port_range(net, &low, &high); |
|
188 |
range = (high - low) + 1; |
|
189 |
return (((u64) hash * range) >> 32) + low; /* Did not understand */ |
|
190 |
} |
|
191 | ||
192 |
static void gtp_build_header(struct sk_buff *skb, |
|
193 |
const struct ip_tunnel_key *tun_key) |
|
194 |
{ |
|
195 |
struct gtphdr *gtph; |
|
196 | ||
197 |
gtph = (struct gtphdr *)__skb_push(skb, sizeof(struct gtphdr)); /* Did not understand */ |
|
198 |
gtph->version = 1; /* GTP-U version 1 */ |
|
199 |
gtph->protocol_type = 1; /* GTP Protocol */ |
|
200 |
gtph->reserved = 0; /* Reserved flags, set to 0 */ |
|
201 |
gtph->extension_hdr_flag = 0; /* No extension header present */ |
|
202 |
gtph->sequence_hdr_flag = 0; /* No Sequence No. present */ |
|
203 |
gtph->n_pdu_number_flag = 0; /* No N PDU present */ |
|
204 |
gtph->message_type = 255; /* GPDU Packets */ |
|
205 |
/* mandatory part of GTP header first 8 octets */ |
|
206 |
gtph->total_length = htons(skb->len) - htons(sizeof(struct gtphdr)); |
|
207 |
gtph->teid = htonl(be64_to_cpu(tun_key->tun_id)); |
|
208 |
} |
|
209 | ||
210 |
/* Called with rcu_read_lock and BH disabled. */ |
|
211 |
static int gtp_rcv(struct sock *sk, struct sk_buff *skb) |
|
212 |
{ |
|
213 |
struct net_device *dev; |
|
214 |
struct gtphdr *gtph; |
|
215 |
struct iphdr *inner_iph; |
|
216 |
struct metadata_dst *tun_dst; |
|
217 |
#ifndef HAVE_METADATA_DST |
|
218 |
struct metadata_dst temp; |
|
219 |
#endif |
|
220 |
__be64 key; |
|
221 |
struct ethhdr *ethh; |
|
222 |
__be16 protocol; |
|
223 | ||
224 |
dev = rcu_dereference_sk_user_data(sk); |
|
225 |
if (unlikely(!dev)) |
|
226 |
goto error; |
|
227 | ||
228 |
if (iptunnel_pull_header(skb, GTP_HLEN, 0)) /* Did not understand */ |
|
229 |
goto error; |
|
230 | ||
231 |
gtph = gtp_hdr(skb); |
|
232 | ||
233 |
key = cpu_to_be64(ntohl(gtph->teid)); |
|
234 | ||
235 |
/* Save outer tunnel values */ |
|
236 |
#ifndef HAVE_METADATA_DST |
|
237 |
tun_dst = &temp; |
|
238 |
ovs_udp_tun_rx_dst(&tun_dst->u.tun_info, skb, AF_INET, TUNNEL_KEY, key, 0); /* Did not understand */ |
|
239 |
#else |
|
240 |
tun_dst = udp_tun_rx_dst(skb, AF_INET, TUNNEL_KEY, key, 0); |
|
241 |
#endif |
|
242 |
/* Drop non-IP inner packets */ |
|
243 |
inner_iph = (struct iphdr *)(gtph + 1); |
|
244 |
switch (inner_iph->version) { |
|
245 |
case 4: |
|
246 |
protocol = htons(ETH_P_IP); |
|
247 |
break; |
|
248 |
case 6: |
|
249 |
protocol = htons(ETH_P_IPV6); |
|
250 |
break; |
|
251 |
default: |
|
252 |
goto error; |
|
253 |
} |
|
254 |
skb->protocol = protocol; |
|
255 | ||
256 |
/* Add Ethernet header */ |
|
257 |
ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); /* Did not understand */ |
|
258 |
memset(ethh, 0, ETH_HLEN); |
|
259 |
ethh->h_dest[0] = 0x06; |
|
260 |
ethh->h_source[0] = 0x06; |
|
261 |
ethh->h_proto = protocol; |
|
262 | ||
263 |
ovs_ip_tunnel_rcv(dev, skb, tun_dst); |
|
264 |
goto out; |
|
265 | ||
266 |
error: |
|
267 |
kfree_skb(skb); |
|
268 |
out: |
|
269 |
return 0; |
|
270 |
} |
|
271 | ||
272 |
netdev_tx_t rpl_gtp_xmit(struct sk_buff *skb) |
|
273 |
{ |
|
274 |
struct net_device *dev = skb->dev; |
|
275 |
struct gtp_dev *gtp_dev = netdev_priv(dev); |
|
276 |
struct net *net = gtp_dev->net; |
|
277 |
int network_offset = skb_network_offset(skb); |
|
278 |
struct ip_tunnel_info *info; |
|
279 |
struct ip_tunnel_key *tun_key; |
|
280 |
struct rtable *rt; |
|
281 |
int min_headroom; |
|
282 |
__be16 src_port, dst_port; |
|
283 |
struct flowi4 fl; |
|
284 |
__be16 df; |
|
285 |
int err; |
|
286 | ||
287 |
info = skb_tunnel_info(skb); |
|
288 |
if (unlikely(!info)) { |
|
289 |
err = -EINVAL; |
|
290 |
goto error; |
|
291 |
} |
|
292 | ||
293 |
if (skb->protocol != htons(ETH_P_IP) && |
|
294 |
skb->protocol != htons(ETH_P_IPV6)) { |
|
295 |
err = 0; |
|
296 |
goto error; |
|
297 |
} |
|
298 | ||
299 |
tun_key = &info->key; |
|
300 | ||
301 |
/* Route lookup */ |
|
302 |
memset(&fl, 0, sizeof(fl)); |
|
303 |
fl.daddr = tun_key->u.ipv4.dst; |
|
304 |
fl.saddr = tun_key->u.ipv4.src; |
|
305 |
fl.flowi4_tos = RT_TOS(tun_key->tos); |
|
306 |
fl.flowi4_mark = skb->mark; |
|
307 |
fl.flowi4_proto = IPPROTO_UDP; |
|
308 |
rt = ip_route_output_key(net, &fl); |
|
309 |
if (IS_ERR(rt)) { |
|
310 |
err = PTR_ERR(rt); |
|
311 |
goto error; |
|
312 |
} |
|
313 | ||
314 |
min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len |
|
315 |
+ sizeof(struct iphdr) + GTP_HLEN; |
|
316 | ||
317 |
if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { |
|
318 |
int head_delta = SKB_DATA_ALIGN(min_headroom - |
|
319 |
skb_headroom(skb) + 16); |
|
320 | ||
321 |
err = pskb_expand_head(skb, max_t(int, head_delta, 0), |
|
322 |
0, GFP_ATOMIC); |
|
323 |
if (unlikely(err)) |
|
324 |
goto err_free_rt; |
|
325 |
} |
|
326 | ||
327 |
/* Reset l2 headers. */ |
|
328 |
skb_pull(skb, network_offset); |
|
329 |
skb_reset_mac_header(skb); |
|
330 |
vlan_set_tci(skb, 0); |
|
331 | ||
332 |
skb = udp_tunnel_handle_offloads(skb, false, 0, false); |
|
333 |
if (IS_ERR(skb)) { |
|
334 |
err = PTR_ERR(skb); |
|
335 |
skb = NULL; |
|
336 |
goto err_free_rt; |
|
337 |
} |
|
338 | ||
339 |
src_port = htons(get_src_port(net, skb)); |
|
340 |
dst_port = gtp_dev->dst_port; |
|
341 | ||
342 |
gtp_build_header(skb, tun_key); |
|
343 | ||
344 |
skb->ignore_df = 1; |
|
345 | ||
346 |
ovs_skb_set_inner_protocol(skb, skb->protocol); |
|
347 | ||
348 |
df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; |
|
349 |
err = udp_tunnel_xmit_skb(rt, gtp_dev->sock->sk, skb, |
|
350 |
fl.saddr, tun_key->u.ipv4.dst, |
|
351 |
tun_key->tos, tun_key->ttl, |
|
352 |
df, src_port, dst_port, false, true); |
|
353 | ||
354 |
iptunnel_xmit_stats(err, &dev->stats, |
|
355 |
(struct pcpu_sw_netstats __percpu *)dev->tstats); |
|
356 |
return NETDEV_TX_OK; |
|
357 | ||
358 |
err_free_rt: |
|
359 |
ip_rt_put(rt); |
|
360 |
error: |
|
361 |
kfree_skb(skb); |
|
362 |
return NETDEV_TX_OK; |
|
363 |
} |
|
364 |
EXPORT_SYMBOL(rpl_gtp_xmit); |
|
365 | ||
366 |
#ifdef HAVE_DEV_TSTATS |
|
367 |
/* Setup stats when device is created */ |
|
368 |
static int gtp_init(struct net_device *dev) |
|
369 |
{ |
|
370 |
dev->tstats = (typeof(dev->tstats)) |
|
371 |
netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); |
|
372 |
if (!dev->tstats) |
|
373 |
return -ENOMEM; |
|
374 | ||
375 |
return 0; |
|
376 |
} |
|
377 | ||
378 |
static void gtp_uninit(struct net_device *dev) |
|
379 |
{ |
|
380 |
free_percpu(dev->tstats); |
|
381 |
} |
|
382 |
#endif |
|
383 | ||
384 |
static struct socket *create_sock(struct net *net, bool ipv6, |
|
385 |
__be16 port) |
|
386 |
{ |
|
387 |
struct socket *sock; |
|
388 |
struct udp_port_cfg udp_conf; |
|
389 |
int err; |
|
390 | ||
391 |
memset(&udp_conf, 0, sizeof(udp_conf)); |
|
392 | ||
393 |
if (ipv6) { |
|
394 |
udp_conf.family = AF_INET6; |
|
395 |
} else { |
|
396 |
udp_conf.family = AF_INET; |
|
397 |
udp_conf.local_ip.s_addr = htonl(INADDR_ANY); |
|
398 |
} |
|
399 | ||
400 |
udp_conf.local_udp_port = port; |
|
401 | ||
402 |
/* Open UDP socket */ |
|
403 |
err = udp_sock_create(net, &udp_conf, &sock); |
|
404 |
if (err < 0) |
|
405 |
return ERR_PTR(err); |
|
406 | ||
407 |
return sock; |
|
408 |
} |
|
409 | ||
410 |
static int gtp_open(struct net_device *dev) |
|
411 |
{ |
|
412 |
struct gtp_dev *gtp = netdev_priv(dev); |
|
413 |
struct udp_tunnel_sock_cfg tunnel_cfg; |
|
414 |
struct net *net = gtp->net; |
|
415 | ||
416 |
gtp->sock = create_sock(net, false, gtp->dst_port); |
|
417 |
if (IS_ERR(gtp->sock)) |
|
418 |
return PTR_ERR(gtp->sock); |
|
419 | ||
420 |
/* Mark socket as an encapsulation socket */ |
|
421 |
tunnel_cfg.sk_user_data = dev; |
|
422 |
tunnel_cfg.encap_type = 1; |
|
423 |
tunnel_cfg.encap_rcv = gtp_rcv; |
|
424 |
tunnel_cfg.encap_destroy = NULL; |
|
425 |
setup_udp_tunnel_sock(net, gtp->sock, &tunnel_cfg); |
|
426 |
return 0; |
|
427 |
} |
|
428 | ||
429 |
static int gtp_stop(struct net_device *dev) |
|
430 |
{ |
|
431 |
struct gtp_dev *gtp = netdev_priv(dev); |
|
432 | ||
433 |
udp_tunnel_sock_release(gtp->sock); |
|
434 |
gtp->sock = NULL; |
|
435 |
return 0; |
|
436 |
} |
|
437 | ||
438 |
static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) |
|
439 |
{ |
|
440 |
#ifdef HAVE_METADATA_DST |
|
441 |
return rpl_gtp_xmit(skb); |
|
442 |
#else |
|
443 |
/* Drop All packets coming from networking stack. OVS-CB is |
|
444 |
* not initialized for these packets. |
|
445 |
*/ |
|
446 | ||
447 |
dev_kfree_skb(skb); |
|
448 |
dev->stats.tx_dropped++; |
|
449 |
return NETDEV_TX_OK; |
|
450 |
#endif |
|
451 |
} |
|
452 | ||
453 |
static const struct net_device_ops gtp_netdev_ops = { |
|
454 |
#ifdef HAVE_DEV_TSTATS |
|
455 |
.ndo_init = gtp_init, |
|
456 |
.ndo_uninit = gtp_uninit, |
|
457 |
.ndo_get_stats64 = ip_tunnel_get_stats64, |
|
458 |
#endif |
|
459 |
.ndo_open = gtp_open, |
|
460 |
.ndo_stop = gtp_stop, |
|
461 |
.ndo_start_xmit = gtp_dev_xmit, |
|
462 |
.ndo_change_mtu = eth_change_mtu, |
|
463 |
.ndo_validate_addr = eth_validate_addr, |
|
464 |
.ndo_set_mac_address = eth_mac_addr, |
|
465 |
}; |
|
466 | ||
467 |
static void gtp_get_drvinfo(struct net_device *dev, |
|
468 |
struct ethtool_drvinfo *drvinfo) |
|
469 |
{ |
|
470 |
strlcpy(drvinfo->version, GTP_NETDEV_VER, sizeof(drvinfo->version)); |
|
471 |
strlcpy(drvinfo->driver, "gtp", sizeof(drvinfo->driver)); |
|
472 |
} |
|
473 | ||
474 |
static const struct ethtool_ops gtp_ethtool_ops = { |
|
475 |
.get_drvinfo = gtp_get_drvinfo, |
|
476 |
.get_link = ethtool_op_get_link, |
|
477 |
}; |
|
478 | ||
479 |
/* Info for udev, that this is a virtual tunnel endpoint */ |
|
480 |
static struct device_type gtp_type = { |
|
481 |
.name = "gtp", |
|
482 |
}; |
|
483 | ||
484 |
/* Initialize the device structure. */ |
|
485 |
static void gtp_setup(struct net_device *dev) |
|
486 |
{ |
|
487 |
ether_setup(dev); |
|
488 | ||
489 |
dev->netdev_ops = >p_netdev_ops; |
|
490 |
dev->ethtool_ops = >p_ethtool_ops; |
|
491 |
dev->destructor = free_netdev; |
|
492 | ||
493 |
SET_NETDEV_DEVTYPE(dev, >p_type); |
|
494 | ||
495 |
dev->features |= NETIF_F_LLTX | NETIF_F_NETNS_LOCAL; |
|
496 |
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; |
|
497 |
dev->features |= NETIF_F_RXCSUM; |
|
498 |
dev->features |= NETIF_F_GSO_SOFTWARE; |
|
499 | ||
500 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) |
|
501 |
dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; |
|
502 |
dev->hw_features |= NETIF_F_GSO_SOFTWARE; |
|
503 |
#endif |
|
504 |
#ifdef HAVE_METADATA_DST |
|
505 |
netif_keep_dst(dev); |
|
506 |
#endif |
|
507 |
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; |
|
508 |
eth_hw_addr_random(dev); |
|
509 |
} |
|
510 | ||
511 |
static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { |
|
512 |
[IFLA_GTP_PORT] = { .type = NLA_U16 }, |
|
513 |
}; |
|
514 | ||
515 |
static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) |
|
516 |
{ |
|
517 |
if (tb[IFLA_ADDRESS]) { |
|
518 |
if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) |
|
519 |
return -EINVAL; |
|
520 | ||
521 |
if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) |
|
522 |
return -EADDRNOTAVAIL; |
|
523 |
} |
|
524 | ||
525 |
return 0; |
|
526 |
} |
|
527 | ||
528 |
static struct gtp_dev *find_dev(struct net *net, __be16 dst_port) |
|
529 |
{ |
|
530 |
struct gtp_net *ln = net_generic(net, gtp_net_id); |
|
531 |
struct gtp_dev *dev; |
|
532 | ||
533 |
list_for_each_entry(dev, &ln->gtp_list, next) { |
|
534 |
if (dev->dst_port == dst_port) |
|
535 |
return dev; |
|
536 |
} |
|
537 |
return NULL; |
|
538 |
} |
|
539 | ||
540 |
static int gtp_configure(struct net *net, struct net_device *dev, |
|
541 |
__be16 dst_port) |
|
542 |
{ |
|
543 |
struct gtp_net *ln = net_generic(net, gtp_net_id); |
|
544 |
struct gtp_dev *gtp = netdev_priv(dev); |
|
545 |
int err; |
|
546 | ||
547 |
gtp->net = net; |
|
548 |
gtp->dev = dev; |
|
549 | ||
550 |
gtp->dst_port = dst_port; |
|
551 | ||
552 |
if (find_dev(net, dst_port)) |
|
553 |
return -EBUSY; |
|
554 | ||
555 |
err = register_netdevice(dev); |
|
556 |
if (err) |
|
557 |
return err; |
|
558 | ||
559 |
list_add(>p->next, &ln->gtp_list); |
|
560 |
return 0; |
|
561 |
} |
|
562 | ||
563 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) |
|
564 |
static int gtp_newlink(struct net *net, struct net_device *dev, |
|
565 |
struct nlattr *tb[], struct nlattr *data[]) |
|
566 |
{ |
|
567 |
#else |
|
568 |
static int gtp_newlink(struct net_device *dev, |
|
569 |
struct nlattr *tb[], struct nlattr *data[]) |
|
570 | ||
571 |
{ |
|
572 |
struct net *net = &init_net; |
|
573 |
#endif |
|
574 |
__be16 dst_port = htons(GTP_UDP_PORT); |
|
575 | ||
576 |
if (data[IFLA_GTP_PORT]) |
|
577 |
dst_port = nla_get_be16(data[IFLA_GTP_PORT]); |
|
578 | ||
579 |
return gtp_configure(net, dev, dst_port); |
|
580 |
} |
|
581 | ||
582 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) |
|
583 |
static void gtp_dellink(struct net_device *dev, struct list_head *head) |
|
584 |
#else |
|
585 |
static void gtp_dellink(struct net_device *dev) |
|
586 |
#endif |
|
587 |
{ |
|
588 |
struct gtp_dev *gtp = netdev_priv(dev); |
|
589 | ||
590 |
list_del(>p->next); |
|
591 |
unregister_netdevice_queue(dev, head); |
|
592 |
} |
|
593 | ||
594 |
static size_t gtp_get_size(const struct net_device *dev) |
|
595 |
{ |
|
596 |
return nla_total_size(sizeof(__be32)); /* IFLA_GTP_PORT */ |
|
597 |
} |
|
598 | ||
599 |
static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) |
|
600 |
{ |
|
601 |
struct gtp_dev *gtp = netdev_priv(dev); |
|
602 | ||
603 |
if (nla_put_be16(skb, IFLA_GTP_PORT, gtp->dst_port)) |
|
604 |
goto nla_put_failure; |
|
605 | ||
606 |
return 0; |
|
607 | ||
608 |
nla_put_failure: |
|
609 |
return -EMSGSIZE; |
|
610 |
} |
|
611 | ||
612 |
static struct rtnl_link_ops gtp_link_ops __read_mostly = { |
|
613 |
.kind = "gtp", |
|
614 |
.maxtype = IFLA_GTP_MAX, |
|
615 |
.policy = gtp_policy, |
|
616 |
.priv_size = sizeof(struct gtp_dev), |
|
617 |
.setup = gtp_setup, |
|
618 |
.validate = gtp_validate, |
|
619 |
.newlink = gtp_newlink, |
|
620 |
.dellink = gtp_dellink, |
|
621 |
.get_size = gtp_get_size, |
|
622 |
.fill_info = gtp_fill_info, |
|
623 |
}; |
|
624 | ||
625 |
struct net_device *rpl_gtp_dev_create_fb(struct net *net, const char *name, |
|
626 |
u8 name_assign_type, u16 dst_port) |
|
627 |
{ |
|
628 |
struct nlattr *tb[IFLA_MAX + 1]; |
|
629 |
struct net_device *dev; |
|
630 |
int err; |
|
631 | ||
632 |
memset(tb, 0, sizeof(tb)); |
|
633 |
dev = rtnl_create_link(net, (char *) name, name_assign_type, |
|
634 |
>p_link_ops, tb); |
|
635 |
if (IS_ERR(dev)) |
|
636 |
return dev; |
|
637 | ||
638 |
err = gtp_configure(net, dev, htons(dst_port)); |
|
639 |
if (err) { |
|
640 |
free_netdev(dev); |
|
641 |
return ERR_PTR(err); |
|
642 |
} |
|
643 |
return dev; |
|
644 |
} |
|
645 |
EXPORT_SYMBOL_GPL(rpl_gtp_dev_create_fb); |
|
646 | ||
647 |
static int gtp_init_net(struct net *net) |
|
648 |
{ |
|
649 |
struct gtp_net *ln = net_generic(net, gtp_net_id); |
|
650 | ||
651 |
INIT_LIST_HEAD(&ln->gtp_list); |
|
652 |
return 0; |
|
653 |
} |
|
654 | ||
655 |
static void gtp_exit_net(struct net *net) |
|
656 |
{ |
|
657 |
struct gtp_net *ln = net_generic(net, gtp_net_id); |
|
658 |
struct gtp_dev *gtp, *next; |
|
659 |
struct net_device *dev, *aux; |
|
660 |
LIST_HEAD(list); |
|
661 | ||
662 |
rtnl_lock(); |
|
663 | ||
664 |
/* gather any gtp devices that were moved into this ns */ |
|
665 |
for_each_netdev_safe(net, dev, aux) |
|
666 |
if (dev->rtnl_link_ops == >p_link_ops) |
|
667 |
unregister_netdevice_queue(dev, &list); |
|
668 | ||
669 |
list_for_each_entry_safe(gtp, next, &ln->gtp_list, next) { |
|
670 |
/* If gtp->dev is in the same netns, it was already added |
|
671 |
* to the gtp by the previous loop. |
|
672 |
*/ |
|
673 |
if (!net_eq(dev_net(gtp->dev), net)) |
|
674 |
unregister_netdevice_queue(gtp->dev, &list); |
|
675 |
} |
|
676 | ||
677 |
/* unregister the devices gathered above */ |
|
678 |
unregister_netdevice_many(&list); |
|
679 |
rtnl_unlock(); |
|
680 |
} |
|
681 | ||
682 |
static struct pernet_operations gtp_net_ops = { |
|
683 |
.init = gtp_init_net, |
|
684 |
.exit = gtp_exit_net, |
|
685 |
.id = >p_net_id, |
|
686 |
.size = sizeof(struct gtp_net), |
|
687 |
}; |
|
688 | ||
689 |
DEFINE_COMPAT_PNET_REG_FUNC(device) |
|
690 |
int rpl_gtp_init_module(void) |
|
691 |
{ |
|
692 |
int rc; |
|
693 | ||
694 |
rc = register_pernet_subsys(>p_net_ops); |
|
695 |
if (rc) |
|
696 |
goto out1; |
|
697 | ||
698 |
rc = rtnl_link_register(>p_link_ops); |
|
699 |
if (rc) |
|
700 |
goto out2; |
|
701 | ||
702 |
pr_info("GTP tunneling driver\n"); |
|
703 |
return 0; |
|
704 |
out2: |
|
705 |
unregister_pernet_subsys(>p_net_ops); |
|
706 |
out1: |
|
707 |
return rc; |
|
708 |
} |
|
709 | ||
710 |
void rpl_gtp_cleanup_module(void) |
|
711 |
{ |
|
712 |
rtnl_link_unregister(>p_link_ops); |
|
713 |
unregister_pernet_subsys(>p_net_ops); |
|
714 |
} |
datapath/linux/compat/include/linux/if_link.h | ||
---|---|---|
46 | 46 |
}; |
47 | 47 |
#define IFLA_LISP_MAX (__IFLA_LISP_MAX - 1) |
48 | 48 | |
49 |
/* GTP section */ |
|
50 |
enum { |
|
51 |
IFLA_GTP_PORT, /* destination port */ |
|
52 |
__IFLA_GTP_MAX |
|
53 |
}; |
|
54 |
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) |
|
55 | ||
49 | 56 |
/* VXLAN section */ |
50 | 57 |
enum { |
51 | 58 |
#define IFLA_VXLAN_UNSPEC rpl_IFLA_VXLAN_UNSPEC |
datapath/linux/compat/include/linux/openvswitch.h | ||
---|---|---|
235 | 235 |
OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ |
236 | 236 |
OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ |
237 | 237 |
OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ |
238 |
OVS_VPORT_TYPE_GTP = 107, /* GTP tunnel */ |
|
238 | 239 |
__OVS_VPORT_TYPE_MAX |
239 | 240 |
}; |
240 | 241 |
datapath/linux/compat/include/net/gtp.h | ||
---|---|---|
1 |
#ifndef __NET_GTP_WRAPPER_H |
|
2 |
#define __NET_GTP_WRAPPER_H 1 |
|
3 | ||
4 |
#ifdef CONFIG_INET |
|
5 |
#include <net/udp_tunnel.h> |
|
6 |
#endif |
|
7 | ||
8 | ||
9 |
#ifdef CONFIG_INET |
|
10 |
#define gtp_dev_create_fb rpl_gtp_dev_create_fb |
|
11 |
struct net_device *rpl_gtp_dev_create_fb(struct net *net, const char *name, |
|
12 |
u8 name_assign_type, u16 dst_port); |
|
13 |
#endif /*ifdef CONFIG_INET */ |
|
14 | ||
15 |
#define gtp_init_module rpl_gtp_init_module |
|
16 |
int rpl_gtp_init_module(void); |
|
17 | ||
18 |
#define gtp_cleanup_module rpl_gtp_cleanup_module |
|
19 |
void rpl_gtp_cleanup_module(void); |
|
20 | ||
21 |
#define gtp_xmit rpl_gtp_xmit |
|
22 |
netdev_tx_t rpl_gtp_xmit(struct sk_buff *skb); |
|
23 | ||
24 |
#endif /*ifdef__NET_GTP_H */ |
datapath/vport-gtp.c | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright (c) 2015 Nicira, Inc. |
|
3 |
* |
|
4 |
* This program is free software; you can redistribute it and/or |
|
5 |
* modify it under the terms of the GNU General Public License |
|
6 |
* as published by the Free Software Foundation; either version |
|
7 |
* 2 of the License, or (at your option) any later version. |
|
8 |
*/ |
|
9 | ||
10 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|
11 | ||
12 |
#include <linux/in.h> |
|
13 |
#include <linux/ip.h> |
|
14 |
#include <linux/net.h> |
|
15 |
#include <linux/rculist.h> |
|
16 |
#include <linux/udp.h> |
|
17 |
#include <linux/if_vlan.h> |
|
18 |
#include <linux/module.h> |
|
19 | ||
20 |
#include <net/gtp.h> |
|
21 |
#include <net/icmp.h> |
|
22 |
#include <net/ip.h> |
|
23 |
#include <net/route.h> |
|
24 |
#include <net/udp.h> |
|
25 |
#include <net/xfrm.h> |
|
26 | ||
27 |
#include "datapath.h" |
|
28 |
#include "vport.h" |
|
29 |
#include "vport-netdev.h" |
|
30 | ||
31 |
static struct vport_ops ovs_gtp_vport_ops; |
|
32 |
/** |
|
33 |
* struct gtp_port - Keeps track of open UDP ports |
|
34 |
* @dst_port: destination port. |
|
35 |
*/ |
|
36 |
struct gtp_port { |
|
37 |
u16 port_no; |
|
38 |
}; |
|
39 | ||
40 |
static inline struct gtp_port *gtp_vport(const struct vport *vport) |
|
41 |
{ |
|
42 |
return vport_priv(vport); |
|
43 |
} |
|
44 | ||
45 |
static int gtp_get_options(const struct vport *vport, |
|
46 |
struct sk_buff *skb) |
|
47 |
{ |
|
48 |
struct gtp_port *gtp_port = gtp_vport(vport); |
|
49 | ||
50 |
if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, gtp_port->port_no)) |
|
51 |
return -EMSGSIZE; |
|
52 |
return 0; |
|
53 |
} |
|
54 | ||
55 |
static int gtp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, |
|
56 |
struct dp_upcall_info *upcall) |
|
57 |
{ |
|
58 |
struct gtp_port *gtp_port = gtp_vport(vport); |
|
59 |
struct net *net = ovs_dp_get_net(vport->dp); |
|
60 |
__be16 dport = htons(gtp_port->port_no); |
|
61 |
__be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); |
|
62 | ||
63 |
return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), |
|
64 |
skb, IPPROTO_UDP, sport, dport); |
|
65 |
} |
|
66 | ||
67 |
static struct vport *gtp_tnl_create(const struct vport_parms *parms) |
|
68 |
{ |
|
69 |
struct net *net = ovs_dp_get_net(parms->dp); |
|
70 |
struct nlattr *options = parms->options; |
|
71 |
struct gtp_port *gtp_port; |
|
72 |
struct net_device *dev; |
|
73 |
struct vport *vport; |
|
74 |
struct nlattr *a; |
|
75 |
u16 dst_port; |
|
76 |
int err; |
|
77 | ||
78 |
if (!options) { |
|
79 |
err = -EINVAL; |
|
80 |
goto error; |
|
81 |
} |
|
82 | ||
83 |
a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); |
|
84 |
if (a && nla_len(a) == sizeof(u16)) { |
|
85 |
dst_port = nla_get_u16(a); |
|
86 |
} else { |
|
87 |
/* Require destination port from userspace. */ |
|
88 |
err = -EINVAL; |
|
89 |
goto error; |
|
90 |
} |
|
91 | ||
92 |
vport = ovs_vport_alloc(sizeof(struct gtp_port), |
|
93 |
&ovs_gtp_vport_ops, parms); |
|
94 |
if (IS_ERR(vport)) |
|
95 |
return vport; |
|
96 | ||
97 |
gtp_port = gtp_vport(vport); |
|
98 |
gtp_port->port_no = dst_port; |
|
99 | ||
100 |
rtnl_lock(); |
|
101 |
dev = gtp_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port); |
|
102 |
if (IS_ERR(dev)) { |
|
103 |
rtnl_unlock(); |
|
104 |
ovs_vport_free(vport); |
|
105 |
return ERR_CAST(dev); |
|
106 |
} |
|
107 | ||
108 |
dev_change_flags(dev, dev->flags | IFF_UP); |
|
109 |
rtnl_unlock(); |
|
110 |
return vport; |
|
111 |
error: |
|
112 |
return ERR_PTR(err); |
|
113 |
} |
|
114 | ||
115 |
static struct vport *gtp_create(const struct vport_parms *parms) |
|
116 |
{ |
|
117 |
struct vport *vport; |
|
118 | ||
119 |
vport = gtp_tnl_create(parms); |
|
120 |
if (IS_ERR(vport)) |
|
121 |
return vport; |
|
122 | ||
123 |
return ovs_netdev_link(vport, parms->name); |
|
124 |
} |
|
125 | ||
126 |
static struct vport_ops ovs_gtp_vport_ops = { |
|
127 |
.type = OVS_VPORT_TYPE_GTP, |
|
128 |
.create = gtp_create, |
|
129 |
.destroy = ovs_netdev_tunnel_destroy, |
|
130 |
.get_options = gtp_get_options, |
|
131 |
.send = gtp_xmit, |
|
132 |
.get_egress_tun_info = gtp_get_egress_tun_info, |
|
133 |
}; |
|
134 | ||
135 |
static int __init ovs_gtp_tnl_init(void) |
|
136 |
{ |
|
137 |
return ovs_vport_ops_register(&ovs_gtp_vport_ops); |
|
138 |
} |
|
139 | ||
140 |
static void __exit ovs_gtp_tnl_exit(void) |
|
141 |
{ |
|
142 |
ovs_vport_ops_unregister(&ovs_gtp_vport_ops); |
|
143 |
} |
|
144 | ||
145 |
module_init(ovs_gtp_tnl_init); |
|
146 |
module_exit(ovs_gtp_tnl_exit); |
|
147 | ||
148 |
MODULE_DESCRIPTION("OVS: Gtp switching port"); |
|
149 |
MODULE_LICENSE("GPL"); |
|
150 |
MODULE_ALIAS("vport-type-107"); |
datapath/vport.c | ||
---|---|---|
31 | 31 |
#include <linux/if_link.h> |
32 | 32 |
#include <net/net_namespace.h> |
33 | 33 |
#include <net/lisp.h> |
34 |
#include <net/gtp.h> |
|
34 | 35 |
#include <net/gre.h> |
35 | 36 |
#include <net/geneve.h> |
36 | 37 |
#include <net/vxlan.h> |
... | ... | |
64 | 65 |
err = lisp_init_module(); |
65 | 66 |
if (err) |
66 | 67 |
goto err_lisp; |
68 |
err = gtp_init_module(); |
|
69 |
if (err) |
|
70 |
goto err_gtp; |
|
67 | 71 |
err = ipgre_init(); |
68 | 72 |
if (err) |
69 | 73 |
goto err_gre; |
... | ... | |
86 | 90 |
err_geneve: |
87 | 91 |
ipgre_fini(); |
88 | 92 |
err_gre: |
93 |
gtp_cleanup_module(); |
|
94 |
err_gtp: |
|
89 | 95 |
lisp_cleanup_module(); |
90 | 96 |
err_lisp: |
91 | 97 |
kfree(dev_table); |
... | ... | |
103 | 109 |
vxlan_cleanup_module(); |
104 | 110 |
geneve_cleanup_module(); |
105 | 111 |
ipgre_fini(); |
112 |
gtp_cleanup_module(); |
|
106 | 113 |
lisp_cleanup_module(); |
107 | 114 |
kfree(dev_table); |
108 | 115 |
} |
lib/dpif-netlink.c | ||
---|---|---|
766 | 766 |
case OVS_VPORT_TYPE_LISP: |
767 | 767 |
return "lisp"; |
768 | 768 | |
769 |
case OVS_VPORT_TYPE_GTP: |
|
770 |
return "gtp"; |
|
771 | ||
769 | 772 |
case OVS_VPORT_TYPE_STT: |
770 | 773 |
return "stt"; |
771 | 774 | |
... | ... | |
798 | 801 |
return OVS_VPORT_TYPE_VXLAN; |
799 | 802 |
} else if (!strcmp(type, "lisp")) { |
800 | 803 |
return OVS_VPORT_TYPE_LISP; |
804 |
} else if (!strcmp(type, "gtp")) { |
|
805 |
return OVS_VPORT_TYPE_GTP; |
|
801 | 806 |
} else { |
802 | 807 |
return OVS_VPORT_TYPE_UNSPEC; |
803 | 808 |
} |
lib/netdev-vport.c | ||
---|---|---|
56 | 56 |
#define GENEVE_DST_PORT 6081 |
57 | 57 |
#define VXLAN_DST_PORT 4789 |
58 | 58 |
#define LISP_DST_PORT 4341 |
59 |
#define GTP_DST_PORT 2152 |
|
59 | 60 |
#define STT_DST_PORT 7471 |
60 | 61 | |
61 | 62 |
#define VXLAN_HLEN (sizeof(struct udp_header) + \ |
... | ... | |
156 | 157 | |
157 | 158 |
return (class->get_config == get_tunnel_config && |
158 | 159 |
(!strcmp("geneve", type) || !strcmp("vxlan", type) || |
159 |
!strcmp("lisp", type) || !strcmp("stt", type)) ); |
|
160 |
!strcmp("lisp", type) || !strcmp("gtp", type) || |
|
161 |
!strcmp("stt", type)) ); |
|
160 | 162 |
} |
161 | 163 | |
162 | 164 |
const char * |
... | ... | |
255 | 257 |
dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT); |
256 | 258 |
} else if (!strcmp(type, "lisp")) { |
257 | 259 |
dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); |
260 |
} else if (!strcmp(type, "gtp")) { |
|
261 |
dev->tnl_cfg.dst_port = htons(GTP_DST_PORT); |
|
258 | 262 |
} else if (!strcmp(type, "stt")) { |
259 | 263 |
dev->tnl_cfg.dst_port = htons(STT_DST_PORT); |
260 | 264 |
} |
... | ... | |
481 | 485 |
tnl_cfg.dst_port = htons(LISP_DST_PORT); |
482 | 486 |
} |
483 | 487 | |
488 |
if (!strcmp(type, "gtp")) { |
|
489 |
tnl_cfg.dst_port = htons(GTP_DST_PORT); |
|
490 |
} |
|
491 | ||
484 | 492 |
if (!strcmp(type, "stt")) { |
485 | 493 |
tnl_cfg.dst_port = htons(STT_DST_PORT); |
486 | 494 |
} |
... | ... | |
728 | 736 |
if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || |
729 | 737 |
(!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || |
730 | 738 |
(!strcmp("lisp", type) && dst_port != LISP_DST_PORT) || |
739 |
(!strcmp("gtp", type) && dst_port != GTP_DST_PORT) || |
|
731 | 740 |
(!strcmp("stt", type) && dst_port != STT_DST_PORT)) { |
732 | 741 |
smap_add_format(args, "dst_port", "%d", dst_port); |
733 | 742 |
} |
... | ... | |
1563 | 1572 |
push_udp_header, |
1564 | 1573 |
netdev_vxlan_pop_header), |
1565 | 1574 |
TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL), |
1575 |
TUNNEL_CLASS("gtp", "gtp_sys", NULL, NULL, NULL), |
|
1566 | 1576 |
TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL), |
1567 | 1577 |
}; |
1568 | 1578 |
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
ofproto/ofproto-dpif-ipfix.c | ||
---|---|---|
71 | 71 |
DPIF_IPFIX_TUNNEL_STT = 0x04, |
72 | 72 |
DPIF_IPFIX_TUNNEL_IPSEC_GRE = 0x05, |
73 | 73 |
DPIF_IPFIX_TUNNEL_GENEVE = 0x07, |
74 |
DPIF_IPFIX_TUNNEL_GTP = 0x08, |
|
74 | 75 |
NUM_DPIF_IPFIX_TUNNEL |
75 | 76 |
}; |
76 | 77 | |
... | ... | |
308 | 309 |
IPPROTO_GRE, /* DPIF_IPFIX_TUNNEL_IPSEC_GRE */ |
309 | 310 |
0 , /* reserved */ |
310 | 311 |
IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GENEVE*/ |
312 |
IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GTP*/ |
|
311 | 313 |
}; |
312 | 314 | |
313 | 315 |
OVS_PACKED( |
... | ... | |
358 | 360 |
* VxLAN: 24-bit VIN, |
359 | 361 |
* GRE: 32-bit key, |
360 | 362 |
* LISP: 24-bit instance ID |
363 |
* GTP: 32-bit key |
|
361 | 364 |
* STT: 64-bit key |
362 | 365 |
*/ |
363 | 366 |
#define MAX_TUNNEL_KEY_LEN 8 |
... | ... | |
602 | 605 |
} else if (strcmp(type, "lisp") == 0) { |
603 | 606 |
dip->tunnel_type = DPIF_IPFIX_TUNNEL_LISP; |
604 | 607 |
dip->tunnel_key_length = 3; |
608 |
} else if (strcmp(type, "gtp") == 0) { |
|
609 |
dip->tunnel_type = DPIF_IPFIX_TUNNEL_GTP;tun_src |
|
610 |
dip->tunnel_key_length = 4; |
|
605 | 611 |
} else if (strcmp(type, "geneve") == 0) { |
606 | 612 |
dip->tunnel_type = DPIF_IPFIX_TUNNEL_GENEVE; |
607 | 613 |
dip->tunnel_key_length = 3; |
ofproto/ofproto-dpif-sflow.c | ||
---|---|---|
62 | 62 |
DPIF_SFLOW_TUNNEL_GRE, |
63 | 63 |
DPIF_SFLOW_TUNNEL_LISP, |
64 | 64 |
DPIF_SFLOW_TUNNEL_IPSEC_GRE, |
65 |
DPIF_SFLOW_TUNNEL_GENEVE |
|
65 |
DPIF_SFLOW_TUNNEL_GENEVE, |
|
66 |
DPIF_SFLOW_TUNNEL_GTP |
|
66 | 67 |
}; |
67 | 68 | |
68 | 69 |
struct dpif_sflow_port { |
... | ... | |
592 | 593 |
return DPIF_SFLOW_TUNNEL_VXLAN; |
593 | 594 |
} else if (strcmp(type, "lisp") == 0) { |
594 | 595 |
return DPIF_SFLOW_TUNNEL_LISP; |
596 |
} else if (strcmp(type, "gtp") == 0) { |
|
597 |
return DPIF_SFLOW_TUNNEL_GTP; |
|
595 | 598 |
} else if (strcmp(type, "geneve") == 0) { |
596 | 599 |
return DPIF_SFLOW_TUNNEL_GENEVE; |
597 | 600 |
} |
... | ... | |
616 | 619 | |
617 | 620 |
case DPIF_SFLOW_TUNNEL_VXLAN: |
618 | 621 |
case DPIF_SFLOW_TUNNEL_LISP: |
622 |
case DPIF_SFLOW_TUNNEL_GTP: |
|
619 | 623 |
case DPIF_SFLOW_TUNNEL_GENEVE: |
620 | 624 |
ipproto = IPPROTO_UDP; |
621 | 625 |
tests/ovs-vsctl.at | ||
---|---|---|
1235 | 1235 |
[genev_sys], |
1236 | 1236 |
[gre_sys], |
1237 | 1237 |
[lisp_sys], |
1238 |
[gtp_sys], |
|
1238 | 1239 |
[vxlan_sys]], |
1239 | 1240 |
[ |
1240 | 1241 |
# Try creating the port |
... | ... | |
1263 | 1264 |
-- add-port br0 p4 -- set Interface p4 type=vxlan \ |
1264 | 1265 |
options:remote_ip=2.2.2.2 ofport_request=4 \ |
1265 | 1266 |
-- add-port br0 p5 -- set Interface p5 type=geneve \ |
1266 |
options:remote_ip=2.2.2.2 ofport_request=5]) |
|
1267 |
options:remote_ip=2.2.2.2 ofport_request=5 \ |
|
1268 |
-- add-port br0 p6 -- set Interface p6 type=gtp \ |
|
1269 |
options:remote_ip=2.2.2.2 ofport_request=6]) |
|
1267 | 1270 | |
1268 | 1271 |
# Test creating all reserved tunnel port names |
1269 | 1272 |
m4_foreach( |
... | ... | |
1271 | 1274 |
[[genev_sys], |
1272 | 1275 |
[gre_sys], |
1273 | 1276 |
[lisp_sys], |
1277 |
[gtp_sys], |
|
1274 | 1278 |
[vxlan_sys]], |
1275 | 1279 |
[ |
1276 | 1280 |
# Try creating the port |
tests/system-kmod-macros.at | ||
---|---|---|
18 | 18 |
m4_define([OVS_TRAFFIC_VSWITCHD_START], |
19 | 19 |
[AT_CHECK([modprobe openvswitch]) |
20 | 20 |
on_exit 'modprobe -r openvswitch' |
21 |
m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_lisp], [vport_stt], [vport_vxlan]], |
|
21 |
m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_lisp], [vport_stt], [vport_vxlan], [vport_gtp]],
|
|
22 | 22 |
[modprobe -q mod || echo "Module mod not loaded." |
23 | 23 |
on_exit 'modprobe -q -r mod']) |
24 | 24 |
on_exit 'ovs-dpctl del-dp ovs-system' |
tests/tunnel.at | ||
---|---|---|
352 | 352 |
OVS_VSWITCHD_STOP |
353 | 353 |
AT_CLEANUP |
354 | 354 | |
355 |
AT_SETUP([tunnel - GTP]) |
|
356 |
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtp \ |
|
357 |
options:remote_ip=1.1.1.1 ofport_request=1]) |
|
358 | ||
359 |
AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl |
|
360 |
br0 65534/100: (dummy) |
|
361 |
p1 1/2152: (gtp: remote_ip=1.1.1.1) |
|
362 |
]) |
|
363 | ||
364 |
OVS_VSWITCHD_STOP |
|
365 |
AT_CLEANUP |
|
366 | ||
355 | 367 |
AT_SETUP([tunnel - different VXLAN UDP port]) |
356 | 368 |
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ |
357 | 369 |
options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341]) |
vswitchd/vswitch.xml | ||
---|---|---|
1897 | 1897 |
</p> |
1898 | 1898 |
</dd> |
1899 | 1899 | |
1900 |
<dt><code>gtp</code></dt> |
|
1901 |
<dd> |
|
1902 |
GPRS Tunneling Protocol (GTP) is a group of IP-based communications |
|
1903 |
protocols used to carry general packet radio service (GPRS) within GSM, |
|
1904 |
UMTS and LTE networks.GTP-U is used for carrying user data within the GPRS |
|
1905 |
core network and between the radio access network and the core network. |
|
1906 |
The user data transported can be packets in any of IPv4, IPv6, or PPP |
|
1907 |
formats. |
|
1908 |
The protocol is documented at |
|
1909 |
http://www.3gpp.org/DynaReport/29281.htm |
|
1910 | ||
1911 |
Open vSwitch uses UDP destination port 2152. The source port used for |
|
1912 |
GTP traffic varies on a per-flow basis and is in the ephemeral port |
|
1913 |
range. |
|
1914 |
</dd> |
|
1915 | ||
1900 | 1916 |
<dt><code>stt</code></dt> |
1901 | 1917 |
<dd> |
1902 | 1918 |
The Stateless TCP Tunnel (STT) is particularly useful when tunnel |