blob: 87d1e69e5848e7c615c8745329fb7d53c11d24b4 [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 <assert.h>
9#include <errno.h>
10
11#include <pthread.h>
12
13#include <osmocom/core/linuxlist.h>
14#include <osmocom/core/talloc.h>
15#include <osmocom/core/logging.h>
16
17#include "internal.h"
18
19#define LOGT(t, lvl, fmt, args ...) \
20 LOGP(DGT, lvl, "%s: " fmt, (t)->name, ## args)
21
22/***********************************************************************
23 * GTP Tunnel
24 ***********************************************************************/
25struct gtp_tunnel *gtp_tunnel_alloc(struct gtp_daemon *d, const struct gtp_tunnel_params *cpars)
26{
27 struct gtp_tunnel *t;
28
29 t = talloc_zero(d, struct gtp_tunnel);
30 if (!t)
Harald Welte03fd4132023-07-18 15:01:14 +020031 goto out;
Harald Weltef7365592020-04-15 21:48:45 +020032 t->d = d;
33 t->name = talloc_asprintf(t, "%s-R%08x-T%08x", cpars->tun_name, cpars->rx_teid, cpars->tx_teid);
34 t->tun_dev = tun_device_find_or_create(d, cpars->tun_name, cpars->tun_netns_name);
35 if (!t->tun_dev) {
36 LOGT(t, LOGL_ERROR, "Cannot find or create tun device %s\n", cpars->tun_name);
37 goto out_free;
38 }
39
40 t->gtp_ep = gtp_endpoint_find_or_create(d, &cpars->local_udp);
41 if (!t->gtp_ep) {
42 LOGT(t, LOGL_ERROR, "Cannot find or create GTP endpoint\n");
43 goto out_tun;
44 }
45
46 pthread_rwlock_wrlock(&d->rwlock);
47 /* check if we already have a tunnel with same Rx-TEID + endpoint */
48 if (_gtp_tunnel_find_r(d, cpars->rx_teid, t->gtp_ep)) {
49 LOGT(t, LOGL_ERROR, "Error: We already have a tunnel for RxTEID 0x%08x "
50 "on this endpoint (%s)\n", cpars->rx_teid, t->gtp_ep->name);
51 goto out_ep;
52 }
53
54 /* FIXME: check if we already have a tunnel with same Tx-TEID + peer */
55 /* FIXME: check if we already have a tunnel with same tun + EUA + filter */
56
57 t->rx_teid = cpars->rx_teid;
58 t->tx_teid = cpars->tx_teid;
59 memcpy(&t->user_addr, &cpars->user_addr, sizeof(t->user_addr));
60 memcpy(&t->remote_udp, &cpars->remote_udp, sizeof(t->remote_udp));
61
62 if (netdev_add_addr(t->tun_dev->nl, t->tun_dev->ifindex, &t->user_addr) < 0) {
63 LOGT(t, LOGL_ERROR, "Cannot add user addr to tun device: %s\n",
64 strerror(errno));
65 }
66
67 /* TODO: hash table? */
68 llist_add_tail(&t->list, &d->gtp_tunnels);
69 pthread_rwlock_unlock(&d->rwlock);
70 LOGT(t, LOGL_NOTICE, "Created\n");
71
72 return t;
73
74out_ep:
Harald Welte03fd4132023-07-18 15:01:14 +020075 pthread_rwlock_unlock(&d->rwlock);
Harald Weltef7365592020-04-15 21:48:45 +020076 _gtp_endpoint_release(t->gtp_ep);
77out_tun:
78 _tun_device_release(t->tun_dev);
79out_free:
80 talloc_free(t);
Harald Welte03fd4132023-07-18 15:01:14 +020081out:
Harald Weltef7365592020-04-15 21:48:45 +020082 return NULL;
83}
84
Harald Weltef23abd72020-04-20 12:09:32 +020085#if 0
Harald Weltef7365592020-04-15 21:48:45 +020086/* find tunnel by R(x_teid), T(x_teid) + A(ddr) */
87static struct gtp_tunnel *
88_gtp_tunnel_find_rta(struct gtp_daemon *d, uint32_t rx_teid, uint32_t tx_teid,
89 const struct sockaddr_storage *user_addr)
90{
91 struct gtp_tunnel *t;
92 llist_for_each_entry(t, &d->gtp_tunnels, list) {
93 if (t->rx_teid == rx_teid && t->tx_teid == tx_teid &&
94 sockaddr_equals((struct sockaddr *) &t->user_addr, (struct sockaddr *)user_addr))
95 return t;
96 }
97 return NULL;
98}
Harald Weltef23abd72020-04-20 12:09:32 +020099#endif
Harald Weltef7365592020-04-15 21:48:45 +0200100
101/* find tunnel by R(x_teid) + optionally local endpoint */
102struct gtp_tunnel *
103_gtp_tunnel_find_r(struct gtp_daemon *d, uint32_t rx_teid, struct gtp_endpoint *ep)
104{
105 struct gtp_tunnel *t;
106 llist_for_each_entry(t, &d->gtp_tunnels, list) {
107 if (t->rx_teid == rx_teid) {
108 if (!ep)
109 return t;
110 if (t->gtp_ep == ep)
111 return t;
112 }
113 }
114 return NULL;
115}
116
117/* UNLOCKED find tunnel by tun + EUA ip (+proto/port) */
118struct gtp_tunnel *
119_gtp_tunnel_find_eua(struct tun_device *tun, const struct sockaddr *sa, uint8_t proto)
120{
121 struct gtp_daemon *d = tun->d;
122 struct gtp_tunnel *t;
123
124 llist_for_each_entry(t, &d->gtp_tunnels, list) {
125 /* TODO: Find best matching filter */
126 if (t->tun_dev == tun && sockaddr_equals(sa, (struct sockaddr *) &t->user_addr))
127 return t;
128 }
129 return NULL;
130}
131
132/* UNLOCKED destroy of tunnel; drops references to EP + TUN */
133void _gtp_tunnel_destroy(struct gtp_tunnel *t)
134{
135 LOGT(t, LOGL_NOTICE, "Destroying\n");
136 /* talloc is not thread safe, all alloc/free must come from main thread */
137 ASSERT_MAIN_THREAD(t->d);
138
139 if (netdev_del_addr(t->tun_dev->nl, t->tun_dev->ifindex, &t->user_addr) < 0)
140 LOGT(t, LOGL_ERROR, "Cannot remove user address: %s\n", strerror(errno));
141
142 llist_del(&t->list);
143
144 /* drop reference to endpoint + tun */
145 _gtp_endpoint_release(t->gtp_ep);
146 _tun_device_release(t->tun_dev);
147
148 talloc_free(t);
149}
150
151bool gtp_tunnel_destroy(struct gtp_daemon *d, const struct sockaddr_storage *bind_addr, uint32_t rx_teid)
152{
153 struct gtp_endpoint *ep;
154 bool rc = false;
155
156 pthread_rwlock_wrlock(&d->rwlock);
157 /* find endpoint for bind_addr */
158 ep = _gtp_endpoint_find(d, bind_addr);
159 if (ep) {
160 /* find tunnel for rx TEID within endpoint */
161 struct gtp_tunnel *t = _gtp_tunnel_find_r(d, rx_teid, ep);
162 if (t) {
163 _gtp_tunnel_destroy(t);
164 rc = true;
165 }
166 }
167 pthread_rwlock_unlock(&d->rwlock);
168
169 return rc;
170}