blob: e9c040029ce4aadc58ed89304851d9b6281423d3 [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;
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200142 int old_cancelst_unused;
Harald Weltef7365592020-04-15 21:48:45 +0200143
144 /* initialize the fixed part of the GTP header */
145 gtph->flags = 0x30;
146 gtph->type = GTP_TPDU;
147
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200148 pthread_cleanup_push(tun_device_pthread_cleanup_routine, tun);
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200149 /* IMPORTANT!: All logging functions in this function block must be called with
150 * PTHREAD_CANCEL_DISABLE set, otherwise the thread could be cancelled while
151 * holding the logging mutex, hence causing deadlock with main (or other)
152 * thread. */
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200153
Harald Weltef7365592020-04-15 21:48:45 +0200154 while (1) {
155 struct gtp_tunnel *t;
156 struct pkt_info pinfo;
157 int rc, nread, outfd;
158
159 /* 1) read from tun */
160 rc = read(tun->fd, buffer, MAX_UDP_PACKET);
161 if (rc < 0) {
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200162 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200163 LOGTUN(tun, LOGL_FATAL, "Error readingfrom tun device: %s\n", strerror(errno));
164 exit(1);
165 }
166 nread = rc;
167 gtph->length = htons(nread);
168
169 rc = parse_pkt(&pinfo, buffer, nread);
170 if (rc < 0) {
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200171 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200172 LOGTUN(tun, LOGL_NOTICE, "Error parsing IP packet: %s\n",
173 osmo_hexdump(buffer, nread));
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200174 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200175 continue;
176 }
177
178 if (pinfo.saddr.ss_family == AF_INET6 && pinfo.proto == IPPROTO_ICMPV6) {
179 /* 2) TODO: magic voodoo for IPv6 neighbor discovery */
180 }
181
182 /* 3) look-up tunnel based on source IP address (+ filter) */
183 pthread_rwlock_rdlock(&d->rwlock);
184 t = _gtp_tunnel_find_eua(tun, (struct sockaddr *) &pinfo.saddr, pinfo.proto);
185 if (!t) {
186 char host[128];
187 char port[8];
188 pthread_rwlock_unlock(&d->rwlock);
189 getnameinfo((const struct sockaddr *)&pinfo.saddr,
190 sizeof(pinfo.saddr), host, sizeof(host), port, sizeof(port),
191 NI_NUMERICHOST | NI_NUMERICSERV);
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200192 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200193 LOGTUN(tun, LOGL_NOTICE, "No tunnel found for source address %s:%s\n", host, port);
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200194 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200195 continue;
196 }
197 outfd = t->gtp_ep->fd;
198 memcpy(&daddr, &t->remote_udp, sizeof(daddr));
199 gtph->tid = htonl(t->tx_teid);
200 pthread_rwlock_unlock(&d->rwlock);
201
202 /* 4) write to GTP/UDP socket */
203 rc = sendto(outfd, base_buffer, nread+sizeof(*gtph), 0,
204 (struct sockaddr *)&daddr, sizeof(daddr));
205 if (rc < 0) {
Pau Espin Pedrol7b2a7292022-04-13 18:04:21 +0200206 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused);
Harald Weltef7365592020-04-15 21:48:45 +0200207 LOGTUN(tun, LOGL_FATAL, "Error Writing to UDP socket: %s\n", strerror(errno));
208 exit(1);
209 }
210 }
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200211 pthread_cleanup_pop(1);
Harald Weltef7365592020-04-15 21:48:45 +0200212}
213
214static int tun_open(int flags, const char *name)
215{
216 struct ifreq ifr;
217 int fd, rc;
218
219 fd = open("/dev/net/tun", O_RDWR);
220 if (fd < 0) {
221 LOGP(DTUN, LOGL_ERROR, "Cannot open /dev/net/tun: %s\n", strerror(errno));
222 return fd;
223 }
224
225 memset(&ifr, 0, sizeof(ifr));
226 ifr.ifr_flags = IFF_TUN | IFF_NO_PI | flags;
227 if (name) {
228 /* if a TUN interface name was specified, put it in the structure; otherwise,
229 the kernel will try to allocate the "next" device of the specified type */
Harald Weltef23abd72020-04-20 12:09:32 +0200230 osmo_strlcpy(ifr.ifr_name, name, IFNAMSIZ);
Harald Weltef7365592020-04-15 21:48:45 +0200231 }
232
233 /* try to create the device */
234 rc = ioctl(fd, TUNSETIFF, (void *) &ifr);
235 if (rc < 0) {
236 close(fd);
237 return rc;
238 }
239
240 /* FIXME: read name back from device? */
241 /* FIXME: SIOCSIFTXQLEN / SIOCSIFFLAGS */
242
243 return fd;
244}
245
246static struct tun_device *
247_tun_device_create(struct gtp_daemon *d, const char *devname, const char *netns_name)
248{
249 struct rtnl_link *link;
250 struct tun_device *tun;
251 sigset_t oldmask;
252 int rc;
253
254 tun = talloc_zero(d, struct tun_device);
255 if (!tun)
256 return NULL;
257
258 tun->d = d;
259 tun->use_count = 1;
260 tun->devname = talloc_strdup(tun, devname);
261
262 if (netns_name) {
263 tun->netns_name = talloc_strdup(tun, netns_name);
264 tun->netns_fd = get_nsfd(tun->netns_name);
265 if (tun->netns_fd < 0) {
266 LOGTUN(tun, LOGL_ERROR, "Cannot obtain netns file descriptor: %s\n",
267 strerror(errno));
268 goto err_free;
269 }
270 }
271
272 /* temporarily switch to specified namespace to create tun device */
273 if (tun->netns_name) {
274 rc = switch_ns(tun->netns_fd, &oldmask);
275 if (rc < 0) {
276 LOGTUN(tun, LOGL_ERROR, "Cannot switch to netns '%s': %s\n",
277 tun->netns_name, strerror(errno));
278 goto err_close_ns;
279 }
280 }
281
282 tun->fd = tun_open(0, tun->devname);
283 if (tun->fd < 0) {
284 LOGTUN(tun, LOGL_ERROR, "Cannot open TUN device: %s\n", strerror(errno));
285 goto err_restore_ns;
286 }
287
288 tun->nl = nl_socket_alloc();
289 if (!tun->nl || nl_connect(tun->nl, NETLINK_ROUTE) < 0) {
290 LOGTUN(tun, LOGL_ERROR, "Cannot create netlink socket in namespace '%s'\n",
291 tun->netns_name);
292 goto err_close;
293 }
294
295 rc = rtnl_link_get_kernel(tun->nl, 0, tun->devname, &link);
296 if (rc < 0) {
297 LOGTUN(tun, LOGL_ERROR, "Cannot get ifindex for netif after create?!?\n");
298 goto err_free_nl;
299 }
300 tun->ifindex = rtnl_link_get_ifindex(link);
301 rtnl_link_put(link);
302
303 /* switch back to default namespace before creating new thread */
304 if (tun->netns_name)
305 OSMO_ASSERT(restore_ns(&oldmask) == 0);
306
307 /* bring the network device up */
308 rc = netdev_set_link(tun->nl, tun->ifindex, true);
309 if (rc < 0)
310 LOGTUN(tun, LOGL_ERROR, "Cannot set interface to 'up'\n");
311
312 if (tun->netns_name) {
313 rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET);
314 if (rc < 0)
Vadim Yanitskiy115a1762022-01-31 02:36:00 +0600315 LOGTUN(tun, LOGL_ERROR, "Cannot add IPv4 default route "
316 "(rc=%d): %s\n", rc, nl_geterror(rc));
Harald Weltef7365592020-04-15 21:48:45 +0200317 else
318 LOGTUN(tun, LOGL_INFO, "Added IPv4 default route\n");
319
320 rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET6);
321 if (rc < 0)
Vadim Yanitskiy115a1762022-01-31 02:36:00 +0600322 LOGTUN(tun, LOGL_ERROR, "Cannot add IPv6 default route "
323 "(rc=%d): %s\n", rc, nl_geterror(rc));
Harald Weltef7365592020-04-15 21:48:45 +0200324 else
325 LOGTUN(tun, LOGL_INFO, "Added IPv6 default route\n");
326 }
327
328 if (pthread_create(&tun->thread, NULL, tun_device_thread, tun)) {
329 LOGTUN(tun, LOGL_ERROR, "Cannot create TUN thread: %s\n", strerror(errno));
330 goto err_free_nl;
331 }
332
333 LOGTUN(tun, LOGL_INFO, "Created (in netns '%s')\n", tun->netns_name);
334 llist_add_tail(&tun->list, &d->tun_devices);
335
336 return tun;
337
338err_free_nl:
339 nl_socket_free(tun->nl);
340err_close:
341 close(tun->fd);
342err_restore_ns:
343 if (tun->netns_name)
344 OSMO_ASSERT(restore_ns(&oldmask) == 0);
345err_close_ns:
346 if (tun->netns_name)
347 close(tun->netns_fd);
348err_free:
349 talloc_free(tun);
350 return NULL;
351}
352
353struct tun_device *
354_tun_device_find(struct gtp_daemon *d, const char *devname)
355{
356 struct tun_device *tun;
357
358 llist_for_each_entry(tun, &d->tun_devices, list) {
359 if (!strcmp(tun->devname, devname))
360 return tun;
361 }
362 return NULL;
363}
364
Harald Welte24557a72020-04-17 22:08:29 +0200365/* find the first tun device within given named netns */
366struct tun_device *
367tun_device_find_netns(struct gtp_daemon *d, const char *netns_name)
368{
369 struct tun_device *tun;
370
371 pthread_rwlock_rdlock(&d->rwlock);
372 llist_for_each_entry(tun, &d->tun_devices, list) {
373 if (!strcmp(tun->netns_name, netns_name)) {
374 pthread_rwlock_unlock(&d->rwlock);
375 return tun;
376 }
377 }
378 pthread_rwlock_unlock(&d->rwlock);
379 return NULL;
380}
381
Harald Weltef7365592020-04-15 21:48:45 +0200382struct tun_device *
383tun_device_find_or_create(struct gtp_daemon *d, const char *devname, const char *netns_name)
384{
385 struct tun_device *tun;
386
387 /* talloc is not thread safe, all alloc/free must come from main thread */
388 ASSERT_MAIN_THREAD(d);
389
390 pthread_rwlock_wrlock(&d->rwlock);
391 tun = _tun_device_find(d, devname);
392 if (tun)
393 tun->use_count++;
394 else
395 tun = _tun_device_create(d, devname, netns_name);
396 pthread_rwlock_unlock(&d->rwlock);
397
398 return tun;
399}
400
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200401/* UNLOCKED hard/forced destroy; caller must make sure references are cleaned
402 * up, and tun thread is stopped beforehand by calling
403 * _tun_device_{deref_}release */
404void _tun_device_destroy(struct tun_device *tun)
Harald Weltef7365592020-04-15 21:48:45 +0200405{
406 /* talloc is not thread safe, all alloc/free must come from main thread */
407 ASSERT_MAIN_THREAD(tun->d);
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200408 LOGTUN(tun, LOGL_INFO, "Destroying\n");
Harald Weltef7365592020-04-15 21:48:45 +0200409
Harald Weltef7365592020-04-15 21:48:45 +0200410 if (tun->netns_name)
411 close(tun->netns_fd);
412 close(tun->fd);
413 nl_socket_free(tun->nl);
Harald Weltef7365592020-04-15 21:48:45 +0200414 talloc_free(tun);
415}
416
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200417/* UNLOCKED remove all objects referencing this tun and then start async tun release procedure */
418void _tun_device_deref_release(struct tun_device *tun)
Harald Weltef7365592020-04-15 21:48:45 +0200419{
420 struct gtp_daemon *d = tun->d;
421 char *devname = talloc_strdup(d, tun->devname);
422 struct gtp_tunnel *t, *t2;
423 struct tun_device *tun2;
424
425 /* talloc is not thread safe, all alloc/free must come from main thread */
426 ASSERT_MAIN_THREAD(tun->d);
427
428 llist_for_each_entry_safe(t, t2, &g_daemon->gtp_tunnels, list) {
429 if (t->tun_dev == tun)
430 _gtp_tunnel_destroy(t);
431 }
432 /* _tun_device_destroy may already have been called via
433 * _gtp_tunnel_destroy -> _tun_device_release, so we have to
434 * check if the tun can still be found in the list */
435 tun2 = _tun_device_find(d, devname);
436 if (tun2 && tun2 == tun)
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200437 _tun_device_release(tun2);
Harald Weltef7365592020-04-15 21:48:45 +0200438
439 talloc_free(devname);
440}
441
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200442/* UNLOCKED release a reference; start async tun release procedure if refcount drops to 0 */
Harald Weltef7365592020-04-15 21:48:45 +0200443bool _tun_device_release(struct tun_device *tun)
444{
445 bool released = false;
446
447 /* talloc is not thread safe, all alloc/free must come from main thread */
448 ASSERT_MAIN_THREAD(tun->d);
449
450 tun->use_count--;
451 if (tun->use_count == 0) {
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200452 LOGTUN(tun, LOGL_INFO, "Releasing\n");
453 llist_del(&tun->list);
454 tun->itq_msg.tun_released.tun = tun;
455 tun->d->reset_all_state_tun_remaining++;
456 /* We cancel the thread: the pthread_cleanup routing will send a message
457 * back to us (main thread) when finally cancelled. */
458 pthread_cancel(tun->thread);
Harald Weltef7365592020-04-15 21:48:45 +0200459 released = true;
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200460 } else {
Harald Weltef7365592020-04-15 21:48:45 +0200461 LOGTUN(tun, LOGL_DEBUG, "Release; new use_count=%lu\n", tun->use_count);
Pau Espin Pedrola459b5c2022-04-11 17:00:36 +0200462 }
Harald Weltef7365592020-04-15 21:48:45 +0200463
464 return released;
465}
466
467/* release a reference; destroy if refcount drops to 0 */
468bool tun_device_release(struct tun_device *tun)
469{
470 struct gtp_daemon *d = tun->d;
471 bool released;
472
473 /* talloc is not thread safe, all alloc/free must come from main thread */
474 ASSERT_MAIN_THREAD(tun->d);
475
476 pthread_rwlock_wrlock(&d->rwlock);
477 released = _tun_device_release(tun);
478 pthread_rwlock_unlock(&d->rwlock);
479
480 return released;
481}