blob: 4221e14da74631e196a9bc0a2cdff128aab49495 [file] [log] [blame]
Harald Weltef7365592020-04-15 21:48:45 +02001/* SPDX-License-Identifier: GPL-2.0 */
2#include <stdint.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <unistd.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 <netdb.h>
12
13#include <pthread.h>
14
15#include <osmocom/core/linuxlist.h>
16#include <osmocom/core/socket.h>
17#include <osmocom/core/talloc.h>
18#include <osmocom/core/logging.h>
19
20#include "gtp.h"
21#include "internal.h"
22
23#define LOGEP(ep, lvl, fmt, args ...) \
24 LOGP(DEP, lvl, "%s: " fmt, (ep)->name, ## args)
25
26/***********************************************************************
27 * GTP Endpoint (UDP socket)
28 ***********************************************************************/
29
30/* one thread for reading from each GTP/UDP socket (GTP decapsulation -> tun) */
31static void *gtp_endpoint_thread(void *arg)
32{
33 struct gtp_endpoint *ep = (struct gtp_endpoint *)arg;
34 struct gtp_daemon *d = ep->d;
35
36 uint8_t buffer[MAX_UDP_PACKET+sizeof(struct gtp1_header)];
37
38 while (1) {
39 struct gtp_tunnel *t;
40 const struct gtp1_header *gtph;
41 int rc, nread, outfd;
42 uint32_t teid;
43
44 /* 1) read GTP packet from UDP socket */
45 rc = recvfrom(ep->fd, buffer, sizeof(buffer), 0, (struct sockaddr *)NULL, 0);
46 if (rc < 0) {
47 LOGEP(ep, LOGL_FATAL, "Error reading from UDP socket: %s\n", strerror(errno));
48 exit(1);
49 }
50 nread = rc;
51 if (nread < sizeof(*gtph)) {
52 LOGEP(ep, LOGL_NOTICE, "Short read: %d < %lu\n", nread, sizeof(*gtph));
53 continue;
54 }
55 gtph = (struct gtp1_header *)buffer;
56
57 /* check GTP heaader contents */
58 if (gtph->flags != 0x30) {
59 LOGEP(ep, LOGL_NOTICE, "Unexpected GTP Flags: 0x%02x\n", gtph->flags);
60 continue;
61 }
62 if (gtph->type != GTP_TPDU) {
63 LOGEP(ep, LOGL_NOTICE, "Unexpected GTP Message Type: 0x%02x\n", gtph->type);
64 continue;
65 }
66 if (sizeof(*gtph)+ntohs(gtph->length) > nread) {
67 LOGEP(ep, LOGL_NOTICE, "Shotr GTP Message: %lu < len=%d\n",
68 sizeof(*gtph)+ntohs(gtph->length), nread);
69 continue;
70 }
71 teid = ntohl(gtph->tid);
72
73 /* 2) look-up tunnel based on TEID */
74 pthread_rwlock_rdlock(&d->rwlock);
75 t = _gtp_tunnel_find_r(d, teid, ep);
76 if (!t) {
77 pthread_rwlock_unlock(&d->rwlock);
78 LOGEP(ep, LOGL_NOTICE, "Unable to find tunnel for TEID=0x%08x\n", teid);
79 continue;
80 }
81 outfd = t->tun_dev->fd;
82 pthread_rwlock_unlock(&d->rwlock);
83
84 /* 3) write to TUN device */
85 rc = write(outfd, buffer+sizeof(*gtph), ntohs(gtph->length));
86 if (rc < nread-sizeof(struct gtp1_header)) {
87 LOGEP(ep, LOGL_FATAL, "Error writing to tun device %s\n", strerror(errno));
88 exit(1);
89 }
90 }
91}
92
93static struct gtp_endpoint *
94_gtp_endpoint_create(struct gtp_daemon *d, const struct sockaddr_storage *bind_addr)
95{
96 struct gtp_endpoint *ep = talloc_zero(d, struct gtp_endpoint);
97 char ipstr[INET6_ADDRSTRLEN];
98 char portstr[8];
99 int rc;
100
101 if (!ep)
102 return NULL;
103
104 rc = getnameinfo((struct sockaddr *)bind_addr, sizeof(*bind_addr),
105 ipstr, sizeof(ipstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
106 if (rc != 0)
107 goto out_free;
108 ep->name = talloc_asprintf(ep, "%s:%s", ipstr, portstr);
109
110 ep->d = d;
111 ep->use_count = 1;
112 ep->bind_addr = *bind_addr;
113 ep->fd = socket(ep->bind_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
114 if (ep->fd < 0) {
115 LOGEP(ep, LOGL_ERROR, "Cannot create UDP socket: %s\n", strerror(errno));
116 goto out_free;
117 }
118 rc = bind(ep->fd, (struct sockaddr *) &ep->bind_addr, sizeof(ep->bind_addr));
119 if (rc < 0) {
120 LOGEP(ep, LOGL_ERROR, "Cannot bind UDP socket: %s\n", strerror(errno));
121 goto out_close;
122 }
123
124 if (pthread_create(&ep->thread, NULL, gtp_endpoint_thread, ep)) {
125 LOGEP(ep, LOGL_ERROR, "Cannot start GTP thread: %s\n", strerror(errno));
126 goto out_close;
127 }
128
129 llist_add_tail(&ep->list, &d->gtp_endpoints);
130 LOGEP(ep, LOGL_INFO, "Created\n");
131
132 return ep;
133
134out_close:
135 close(ep->fd);
136out_free:
137 talloc_free(ep);
138 return NULL;
139}
140
141struct gtp_endpoint *
142_gtp_endpoint_find(struct gtp_daemon *d, const struct sockaddr_storage *bind_addr)
143{
144 struct gtp_endpoint *ep;
145
146 llist_for_each_entry(ep, &d->gtp_endpoints, list) {
147 if (sockaddr_equals((const struct sockaddr *) &ep->bind_addr,
148 (const struct sockaddr *) bind_addr)) {
149 return ep;
150 }
151 }
152 return NULL;
153}
154
155struct gtp_endpoint *
156gtp_endpoint_find_or_create(struct gtp_daemon *d, const struct sockaddr_storage *bind_addr)
157{
158 struct gtp_endpoint *ep;
159
160 /* talloc is not thread safe, all alloc/free must come from main thread */
161 ASSERT_MAIN_THREAD(d);
162
163 pthread_rwlock_wrlock(&d->rwlock);
164 ep = _gtp_endpoint_find(d, bind_addr);
165 if (ep)
166 ep->use_count++;
167 else
168 ep = _gtp_endpoint_create(d, bind_addr);
169 pthread_rwlock_unlock(&d->rwlock);
170
171 return ep;
172}
173
174/* UNLOCKED hard/forced destroy; caller must make sure references are cleaned up */
175static void _gtp_endpoint_destroy(struct gtp_endpoint *ep)
176{
177 /* talloc is not thread safe, all alloc/free must come from main thread */
178 ASSERT_MAIN_THREAD(ep->d);
179
180 if (ep->use_count)
181 LOGEP(ep, LOGL_ERROR, "Destroying despite use_count %lu != 0\n", ep->use_count);
182 else
183 LOGEP(ep, LOGL_INFO, "Destroying\n");
184
185 pthread_cancel(ep->thread);
186 llist_del(&ep->list);
187 close(ep->fd);
188 talloc_free(ep);
189}
190
191/* UNLOCKED remove all objects referencing this ep and then destroy */
192void _gtp_endpoint_deref_destroy(struct gtp_endpoint *ep)
193{
194 struct gtp_daemon *d = ep->d;
195 struct sockaddr_storage ss = ep->bind_addr;
196 struct gtp_tunnel *t, *t2;
197 struct gtp_endpoint *ep2;
198
199 /* talloc is not thread safe, all alloc/free must come from main thread */
200 ASSERT_MAIN_THREAD(ep->d);
201
202 /* iterate over all tunnels; delete all references to ep */
203 llist_for_each_entry_safe(t, t2, &d->gtp_tunnels, list) {
204 if (t->gtp_ep == ep)
205 _gtp_tunnel_destroy(t);
206 }
207
208 /* _gtp_endpoint_destroy may already have been called via
209 * _gtp_tunnel_destroy -> gtp_endpoint_release, so we have to
210 * check if the ep can still be found in the list */
211 ep2 = _gtp_endpoint_find(d, &ss);
212 if (ep2 && ep2 == ep)
213 _gtp_endpoint_destroy(ep2);
214}
215
216/* UNLOCKED release a reference; destroy if refcount drops to 0 */
217bool _gtp_endpoint_release(struct gtp_endpoint *ep)
218{
219 bool released = false;
220
221 /* talloc is not thread safe, all alloc/free must come from main thread */
222 ASSERT_MAIN_THREAD(ep->d);
223
224 ep->use_count--;
225 if (ep->use_count == 0) {
226 _gtp_endpoint_destroy(ep);
227 released = true;
228 } else
229 LOGEP(ep, LOGL_DEBUG, "Release; new use_count=%lu\n", ep->use_count);
230
231 return released;
232}
233
234
235/* release a reference; destroy if refcount drops to 0 */
236bool gtp_endpoint_release(struct gtp_endpoint *ep)
237{
238 struct gtp_daemon *d = ep->d;
239 bool released;
240
241 /* talloc is not thread safe, all alloc/free must come from main thread */
242 ASSERT_MAIN_THREAD(ep->d);
243
244 pthread_rwlock_wrlock(&d->rwlock);
245 released = _gtp_endpoint_release(ep);
246 pthread_rwlock_unlock(&d->rwlock);
247
248 return released;
249}