blob: 599385655c6fe0e59a8980181189a8d9eb320551 [file] [log] [blame]
Harald Weltef7365592020-04-15 21:48:45 +02001/* SPDX-License-Identifier: GPL-2.0 */
2#include <unistd.h>
3#include <stdint.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <string.h>
7#include <stdio.h>
8#include <errno.h>
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <signal.h>
14#include <netdb.h>
15
16#include <netinet/ip.h>
17#include <netinet/ip6.h>
18
19#include <pthread.h>
20
21#include <linux/if.h>
22#include <linux/if_tun.h>
23#include <sys/ioctl.h>
24
Harald Weltef2be0992020-04-20 11:12:42 +000025#include <linux/netlink.h>
Harald Weltef7365592020-04-15 21:48:45 +020026#include <netlink/socket.h>
27#include <netlink/route/link.h>
28
29#include <osmocom/core/linuxlist.h>
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/logging.h>
32#include <osmocom/core/utils.h>
33
34#include "gtp.h"
35#include "internal.h"
36#include "netns.h"
37
38/***********************************************************************
39 * TUN Device
40 ***********************************************************************/
41
42#define LOGTUN(tun, lvl, fmt, args ...) \
43 LOGP(DTUN, lvl, "%s: " fmt, (tun)->devname, ## args)
44
45/* extracted information from a packet */
46struct pkt_info {
47 struct sockaddr_storage saddr;
48 struct sockaddr_storage daddr;
49 uint8_t proto;
50};
51
52static int parse_pkt(struct pkt_info *out, const uint8_t *in, unsigned int in_len)
53{
54 const struct iphdr *ip4 = (struct iphdr *) in;
55 const uint16_t *l4h = NULL;
56
57 memset(out, 0, sizeof(*out));
58
59 if (ip4->version == 4) {
60 struct sockaddr_in *saddr4 = (struct sockaddr_in *) &out->saddr;
61 struct sockaddr_in *daddr4 = (struct sockaddr_in *) &out->daddr;
62
63 if (in_len < sizeof(*ip4) || in_len < 4*ip4->ihl)
64 return -1;
65
66 saddr4->sin_family = AF_INET;
67 saddr4->sin_addr.s_addr = ip4->saddr;
68
69 daddr4->sin_family = AF_INET;
70 daddr4->sin_addr.s_addr = ip4->daddr;
71
72 out->proto = ip4->protocol;
73 l4h = (const uint16_t *) (in + sizeof(*ip4));
74
75 switch (out->proto) {
76 case IPPROTO_TCP:
77 case IPPROTO_UDP:
78 case IPPROTO_DCCP:
79 case IPPROTO_SCTP:
80 case IPPROTO_UDPLITE:
81 saddr4->sin_port = ntohs(l4h[0]);
82 daddr4->sin_port = ntohs(l4h[1]);
83 break;
84 default:
85 break;
86 }
87 } else if (ip4->version == 6) {
88 const struct ip6_hdr *ip6 = (struct ip6_hdr *) in;
89 struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) &out->saddr;
90 struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *) &out->daddr;
91
92 if (in_len < sizeof(*ip6))
93 return -1;
94
95 saddr6->sin6_family = AF_INET6;
96 saddr6->sin6_addr = ip6->ip6_src;
97
98 daddr6->sin6_family = AF_INET6;
99 daddr6->sin6_addr = ip6->ip6_dst;
100
101 /* FIXME: ext hdr */
102 out->proto = ip6->ip6_nxt;
103 l4h = (const uint16_t *) (in + sizeof(*ip6));
104
105 switch (out->proto) {
106 case IPPROTO_TCP:
107 case IPPROTO_UDP:
108 case IPPROTO_DCCP:
109 case IPPROTO_SCTP:
110 case IPPROTO_UDPLITE:
111 saddr6->sin6_port = ntohs(l4h[0]);
112 daddr6->sin6_port = ntohs(l4h[1]);
113 break;
114 default:
115 break;
116 }
117 } else
118 return -1;
119
120 return 0;
121}
122
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200123static void tun_device_pthread_cleanup_routine(void *data)
124{
125 struct tun_device *tun = data;
126 LOGTUN(tun, LOGL_DEBUG, "pthread_cleanup\n");
127 int rc = osmo_it_q_enqueue(tun->d->itq, &tun->itq_msg, list);
128 OSMO_ASSERT(rc == 0);
129}
130
Harald Weltef7365592020-04-15 21:48:45 +0200131/* one thread for reading from each TUN device (TUN -> GTP encapsulation) */
132static void *tun_device_thread(void *arg)
133{
134 struct tun_device *tun = (struct tun_device *)arg;
135 struct gtp_daemon *d = tun->d;
136
137 uint8_t base_buffer[MAX_UDP_PACKET+sizeof(struct gtp1_header)];
138 struct gtp1_header *gtph = (struct gtp1_header *)base_buffer;
139 uint8_t *buffer = base_buffer + sizeof(struct gtp1_header);
140
141 struct sockaddr_storage daddr;
142
143 /* initialize the fixed part of the GTP header */
144 gtph->flags = 0x30;
145 gtph->type = GTP_TPDU;
146
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200147 pthread_cleanup_push(tun_device_pthread_cleanup_routine, tun);
148
Harald Weltef7365592020-04-15 21:48:45 +0200149 while (1) {
150 struct gtp_tunnel *t;
151 struct pkt_info pinfo;
152 int rc, nread, outfd;
153
154 /* 1) read from tun */
155 rc = read(tun->fd, buffer, MAX_UDP_PACKET);
156 if (rc < 0) {
157 LOGTUN(tun, LOGL_FATAL, "Error readingfrom tun device: %s\n", strerror(errno));
158 exit(1);
159 }
160 nread = rc;
161 gtph->length = htons(nread);
162
163 rc = parse_pkt(&pinfo, buffer, nread);
164 if (rc < 0) {
165 LOGTUN(tun, LOGL_NOTICE, "Error parsing IP packet: %s\n",
166 osmo_hexdump(buffer, nread));
167 continue;
168 }
169
170 if (pinfo.saddr.ss_family == AF_INET6 && pinfo.proto == IPPROTO_ICMPV6) {
171 /* 2) TODO: magic voodoo for IPv6 neighbor discovery */
172 }
173
174 /* 3) look-up tunnel based on source IP address (+ filter) */
175 pthread_rwlock_rdlock(&d->rwlock);
176 t = _gtp_tunnel_find_eua(tun, (struct sockaddr *) &pinfo.saddr, pinfo.proto);
177 if (!t) {
178 char host[128];
179 char port[8];
180 pthread_rwlock_unlock(&d->rwlock);
181 getnameinfo((const struct sockaddr *)&pinfo.saddr,
182 sizeof(pinfo.saddr), host, sizeof(host), port, sizeof(port),
183 NI_NUMERICHOST | NI_NUMERICSERV);
184 LOGTUN(tun, LOGL_NOTICE, "No tunnel found for source address %s:%s\n", host, port);
185 continue;
186 }
187 outfd = t->gtp_ep->fd;
188 memcpy(&daddr, &t->remote_udp, sizeof(daddr));
189 gtph->tid = htonl(t->tx_teid);
190 pthread_rwlock_unlock(&d->rwlock);
191
192 /* 4) write to GTP/UDP socket */
193 rc = sendto(outfd, base_buffer, nread+sizeof(*gtph), 0,
194 (struct sockaddr *)&daddr, sizeof(daddr));
195 if (rc < 0) {
196 LOGTUN(tun, LOGL_FATAL, "Error Writing to UDP socket: %s\n", strerror(errno));
197 exit(1);
198 }
199 }
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200200 pthread_cleanup_pop(1);
Harald Weltef7365592020-04-15 21:48:45 +0200201}
202
203static int tun_open(int flags, const char *name)
204{
205 struct ifreq ifr;
206 int fd, rc;
207
208 fd = open("/dev/net/tun", O_RDWR);
209 if (fd < 0) {
210 LOGP(DTUN, LOGL_ERROR, "Cannot open /dev/net/tun: %s\n", strerror(errno));
211 return fd;
212 }
213
214 memset(&ifr, 0, sizeof(ifr));
215 ifr.ifr_flags = IFF_TUN | IFF_NO_PI | flags;
216 if (name) {
217 /* if a TUN interface name was specified, put it in the structure; otherwise,
218 the kernel will try to allocate the "next" device of the specified type */
Harald Weltef23abd72020-04-20 12:09:32 +0200219 osmo_strlcpy(ifr.ifr_name, name, IFNAMSIZ);
Harald Weltef7365592020-04-15 21:48:45 +0200220 }
221
222 /* try to create the device */
223 rc = ioctl(fd, TUNSETIFF, (void *) &ifr);
224 if (rc < 0) {
225 close(fd);
226 return rc;
227 }
228
229 /* FIXME: read name back from device? */
230 /* FIXME: SIOCSIFTXQLEN / SIOCSIFFLAGS */
231
232 return fd;
233}
234
235static struct tun_device *
236_tun_device_create(struct gtp_daemon *d, const char *devname, const char *netns_name)
237{
238 struct rtnl_link *link;
239 struct tun_device *tun;
240 sigset_t oldmask;
241 int rc;
242
243 tun = talloc_zero(d, struct tun_device);
244 if (!tun)
245 return NULL;
246
247 tun->d = d;
248 tun->use_count = 1;
249 tun->devname = talloc_strdup(tun, devname);
250
251 if (netns_name) {
252 tun->netns_name = talloc_strdup(tun, netns_name);
253 tun->netns_fd = get_nsfd(tun->netns_name);
254 if (tun->netns_fd < 0) {
255 LOGTUN(tun, LOGL_ERROR, "Cannot obtain netns file descriptor: %s\n",
256 strerror(errno));
257 goto err_free;
258 }
259 }
260
261 /* temporarily switch to specified namespace to create tun device */
262 if (tun->netns_name) {
263 rc = switch_ns(tun->netns_fd, &oldmask);
264 if (rc < 0) {
265 LOGTUN(tun, LOGL_ERROR, "Cannot switch to netns '%s': %s\n",
266 tun->netns_name, strerror(errno));
267 goto err_close_ns;
268 }
269 }
270
271 tun->fd = tun_open(0, tun->devname);
272 if (tun->fd < 0) {
273 LOGTUN(tun, LOGL_ERROR, "Cannot open TUN device: %s\n", strerror(errno));
274 goto err_restore_ns;
275 }
276
277 tun->nl = nl_socket_alloc();
278 if (!tun->nl || nl_connect(tun->nl, NETLINK_ROUTE) < 0) {
279 LOGTUN(tun, LOGL_ERROR, "Cannot create netlink socket in namespace '%s'\n",
280 tun->netns_name);
281 goto err_close;
282 }
283
284 rc = rtnl_link_get_kernel(tun->nl, 0, tun->devname, &link);
285 if (rc < 0) {
286 LOGTUN(tun, LOGL_ERROR, "Cannot get ifindex for netif after create?!?\n");
287 goto err_free_nl;
288 }
289 tun->ifindex = rtnl_link_get_ifindex(link);
290 rtnl_link_put(link);
291
292 /* switch back to default namespace before creating new thread */
293 if (tun->netns_name)
294 OSMO_ASSERT(restore_ns(&oldmask) == 0);
295
296 /* bring the network device up */
297 rc = netdev_set_link(tun->nl, tun->ifindex, true);
298 if (rc < 0)
299 LOGTUN(tun, LOGL_ERROR, "Cannot set interface to 'up'\n");
300
301 if (tun->netns_name) {
302 rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET);
303 if (rc < 0)
Vadim Yanitskiy115a1762022-01-31 02:36:00 +0600304 LOGTUN(tun, LOGL_ERROR, "Cannot add IPv4 default route "
305 "(rc=%d): %s\n", rc, nl_geterror(rc));
Harald Weltef7365592020-04-15 21:48:45 +0200306 else
307 LOGTUN(tun, LOGL_INFO, "Added IPv4 default route\n");
308
309 rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET6);
310 if (rc < 0)
Vadim Yanitskiy115a1762022-01-31 02:36:00 +0600311 LOGTUN(tun, LOGL_ERROR, "Cannot add IPv6 default route "
312 "(rc=%d): %s\n", rc, nl_geterror(rc));
Harald Weltef7365592020-04-15 21:48:45 +0200313 else
314 LOGTUN(tun, LOGL_INFO, "Added IPv6 default route\n");
315 }
316
317 if (pthread_create(&tun->thread, NULL, tun_device_thread, tun)) {
318 LOGTUN(tun, LOGL_ERROR, "Cannot create TUN thread: %s\n", strerror(errno));
319 goto err_free_nl;
320 }
321
322 LOGTUN(tun, LOGL_INFO, "Created (in netns '%s')\n", tun->netns_name);
323 llist_add_tail(&tun->list, &d->tun_devices);
324
325 return tun;
326
327err_free_nl:
328 nl_socket_free(tun->nl);
329err_close:
330 close(tun->fd);
331err_restore_ns:
332 if (tun->netns_name)
333 OSMO_ASSERT(restore_ns(&oldmask) == 0);
334err_close_ns:
335 if (tun->netns_name)
336 close(tun->netns_fd);
337err_free:
338 talloc_free(tun);
339 return NULL;
340}
341
342struct tun_device *
343_tun_device_find(struct gtp_daemon *d, const char *devname)
344{
345 struct tun_device *tun;
346
347 llist_for_each_entry(tun, &d->tun_devices, list) {
348 if (!strcmp(tun->devname, devname))
349 return tun;
350 }
351 return NULL;
352}
353
Harald Welte24557a72020-04-17 22:08:29 +0200354/* find the first tun device within given named netns */
355struct tun_device *
356tun_device_find_netns(struct gtp_daemon *d, const char *netns_name)
357{
358 struct tun_device *tun;
359
360 pthread_rwlock_rdlock(&d->rwlock);
361 llist_for_each_entry(tun, &d->tun_devices, list) {
362 if (!strcmp(tun->netns_name, netns_name)) {
363 pthread_rwlock_unlock(&d->rwlock);
364 return tun;
365 }
366 }
367 pthread_rwlock_unlock(&d->rwlock);
368 return NULL;
369}
370
Harald Weltef7365592020-04-15 21:48:45 +0200371struct tun_device *
372tun_device_find_or_create(struct gtp_daemon *d, const char *devname, const char *netns_name)
373{
374 struct tun_device *tun;
375
376 /* talloc is not thread safe, all alloc/free must come from main thread */
377 ASSERT_MAIN_THREAD(d);
378
379 pthread_rwlock_wrlock(&d->rwlock);
380 tun = _tun_device_find(d, devname);
381 if (tun)
382 tun->use_count++;
383 else
384 tun = _tun_device_create(d, devname, netns_name);
385 pthread_rwlock_unlock(&d->rwlock);
386
387 return tun;
388}
389
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200390/* UNLOCKED hard/forced destroy; caller must make sure references are cleaned
391 * up, and tun thread is stopped beforehand by calling
392 * _tun_device_{deref_}release */
393void _tun_device_destroy(struct tun_device *tun)
Harald Weltef7365592020-04-15 21:48:45 +0200394{
395 /* talloc is not thread safe, all alloc/free must come from main thread */
396 ASSERT_MAIN_THREAD(tun->d);
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200397 LOGTUN(tun, LOGL_INFO, "Destroying\n");
Harald Weltef7365592020-04-15 21:48:45 +0200398
Harald Weltef7365592020-04-15 21:48:45 +0200399 if (tun->netns_name)
400 close(tun->netns_fd);
401 close(tun->fd);
402 nl_socket_free(tun->nl);
Harald Weltef7365592020-04-15 21:48:45 +0200403 talloc_free(tun);
404}
405
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200406/* UNLOCKED remove all objects referencing this tun and then start async tun release procedure */
407void _tun_device_deref_release(struct tun_device *tun)
Harald Weltef7365592020-04-15 21:48:45 +0200408{
409 struct gtp_daemon *d = tun->d;
410 char *devname = talloc_strdup(d, tun->devname);
411 struct gtp_tunnel *t, *t2;
412 struct tun_device *tun2;
413
414 /* talloc is not thread safe, all alloc/free must come from main thread */
415 ASSERT_MAIN_THREAD(tun->d);
416
417 llist_for_each_entry_safe(t, t2, &g_daemon->gtp_tunnels, list) {
418 if (t->tun_dev == tun)
419 _gtp_tunnel_destroy(t);
420 }
421 /* _tun_device_destroy may already have been called via
422 * _gtp_tunnel_destroy -> _tun_device_release, so we have to
423 * check if the tun can still be found in the list */
424 tun2 = _tun_device_find(d, devname);
425 if (tun2 && tun2 == tun)
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200426 _tun_device_release(tun2);
Harald Weltef7365592020-04-15 21:48:45 +0200427
428 talloc_free(devname);
429}
430
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200431/* UNLOCKED release a reference; start async tun release procedure if refcount drops to 0 */
Harald Weltef7365592020-04-15 21:48:45 +0200432bool _tun_device_release(struct tun_device *tun)
433{
434 bool released = false;
435
436 /* talloc is not thread safe, all alloc/free must come from main thread */
437 ASSERT_MAIN_THREAD(tun->d);
438
439 tun->use_count--;
440 if (tun->use_count == 0) {
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200441 LOGTUN(tun, LOGL_INFO, "Releasing\n");
442 llist_del(&tun->list);
443 tun->itq_msg.tun_released.tun = tun;
444 tun->d->reset_all_state_tun_remaining++;
445 /* We cancel the thread: the pthread_cleanup routing will send a message
446 * back to us (main thread) when finally cancelled. */
447 pthread_cancel(tun->thread);
Harald Weltef7365592020-04-15 21:48:45 +0200448 released = true;
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200449 } else {
Harald Weltef7365592020-04-15 21:48:45 +0200450 LOGTUN(tun, LOGL_DEBUG, "Release; new use_count=%lu\n", tun->use_count);
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200451 }
Harald Weltef7365592020-04-15 21:48:45 +0200452
453 return released;
454}
455
456/* release a reference; destroy if refcount drops to 0 */
457bool tun_device_release(struct tun_device *tun)
458{
459 struct gtp_daemon *d = tun->d;
460 bool released;
461
462 /* talloc is not thread safe, all alloc/free must come from main thread */
463 ASSERT_MAIN_THREAD(tun->d);
464
465 pthread_rwlock_wrlock(&d->rwlock);
466 released = _tun_device_release(tun);
467 pthread_rwlock_unlock(&d->rwlock);
468
469 return released;
470}