blob: a0125669d9e65d17e8320dc9ba59a277196d9226 [file] [log] [blame]
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +02001/* GTP Hub Implementation */
2
3/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Neels Hofmeyr
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#pragma once
23
24#include <stdint.h>
25#include <sys/socket.h>
26
27#include <osmocom/core/select.h>
28#include <osmocom/core/timer.h>
29
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010030#include <openbsc/gprs_sgsn.h>
31
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020032
33/* support */
34
35/* TODO move to osmocom/core/socket.c ? */
36#include <netdb.h> /* for IPPROTO_* etc */
37struct osmo_sockaddr {
38 struct sockaddr_storage a;
39 socklen_t l;
40};
41
42/* TODO move to osmocom/core/socket.c ? */
43/*! \brief Initialize a sockaddr
44 * \param[out] addr Valid osmo_sockaddr pointer to write result to
45 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
46 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
47 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
48 * \param[in] host Remote host name or IP address in string form
49 * \param[in] port Remote port number in host byte order
50 * \returns 0 on success, otherwise an error code (from getaddrinfo()).
51 *
52 * Copy the first result from a getaddrinfo() call with the given parameters to
53 * *addr and *addr_len. On error, do not change *addr and return nonzero.
54 */
55int osmo_sockaddr_init(struct osmo_sockaddr *addr,
56 uint16_t family, uint16_t type, uint8_t proto,
57 const char *host, uint16_t port);
58
59/* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to
60 * osmo_sockaddr_init(). */
61static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr,
62 const char *host, uint16_t port)
63{
64 return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port);
65}
66
67/*! \brief convert sockaddr to human readable string.
68 * \param[out] addr_str Valid pointer to a buffer of length addr_str_len.
69 * \param[in] addr_str_len Size of buffer addr_str points at.
70 * \param[out] port_str Valid pointer to a buffer of length port_str_len.
71 * \param[in] port_str_len Size of buffer port_str points at.
72 * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
73 * \param[in] flags flags as passed to getnameinfo().
74 * \returns 0 on success, an error code on error.
75 *
76 * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string
77 * representations of the given struct osmo_sockaddr in two caller provided
78 * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric
79 * address and port. Either one of addr_str or port_str may be NULL, in which
80 * case nothing is returned there.
81 *
82 * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */
83int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
84 char *port_str, size_t port_str_len,
85 const struct osmo_sockaddr *addr,
86 int flags);
87
88
89/*! \brief conveniently concatenate the parts returned by osmo_sockaddr_to_strs().
90 * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
91 * \param[in] buf A buffer to use for string operations.
92 * \param[in] buf_len Length of the buffer.
93 * \returns Address string (in buffer).
94 *
95 * Compose a string of the numeric IP-address and port represented by *addr of
96 * the form "<ip-addr> port <port>". The returned string is valid until the
97 * next invocation of this function.
98 */
99const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
100 char *buf, size_t buf_len);
101
102/*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer.
103 * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
104 * \returns Address string in static buffer.
105 *
106 * See osmo_sockaddr_to_strb().
107 *
108 * Note: only one osmo_sockaddr_to_str() call will work per print/log
109 * statement. For two or more, use osmo_sockaddr_to_strb() with a separate
110 * buffer each.
111 */
112const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr);
113
114/*! \brief compare two osmo_sockaddr.
115 * \param[in] a The first address to compare.
116 * \param[in] b The other address to compare.
117 * \returns 0 if equal, otherwise -1 or 1.
118 */
119int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, const struct osmo_sockaddr *b);
120
121/*! \brief Overwrite *dst with *src.
122 * Like memcpy(), but copy only the valid bytes. */
123void osmo_sockaddr_copy(struct osmo_sockaddr *dst, const struct osmo_sockaddr *src);
124
125
126/* general */
127
128enum gtphub_plane_idx {
129 GTPH_PLANE_CTRL = 0,
130 GTPH_PLANE_USER = 1,
131 GTPH_PLANE_N
132};
133
134extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N];
135extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N];
136
137/* A host address in the form that is expected in the 7.7.32 GSN Address IE.
138 * len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no
139 * address is set, len shall be 0. */
140struct gsn_addr {
141 uint16_t len;
142 uint8_t buf[16];
143};
144
145void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src);
146int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str);
147
148/* Return gsna in numeric string form, in a static buffer. */
149const char *gsn_addr_to_str(const struct gsn_addr *gsna);
150
151/* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */
152const char *gsn_addr_to_strb(const struct gsn_addr *gsna,
153 char *strbuf, int strbuf_len);
154
155/* Return 1 on match, zero otherwise. */
156int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b);
157
Neels Hofmeyrc2275942015-11-10 22:07:04 +0100158/* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number
159 * from sa is also returned. */
160int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
161 const struct osmo_sockaddr *sa);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200162
163/* expiry */
164
165struct expiring_item;
166typedef void (*del_cb_t)(struct expiring_item *);
167
168struct expiring_item {
169 struct llist_head entry;
170 time_t expiry;
171 del_cb_t del_cb;
172};
173
174struct expiry {
175 int expiry_in_seconds;
176 struct llist_head items;
177};
178
179/* Initialize an expiry queue. */
180void expiry_init(struct expiry *exq, int expiry_in_seconds);
181
182/* Add a new mapping, or restart the expiry timeout for an already listed mapping. */
183void expiry_add(struct expiry *exq, struct expiring_item *mapping, time_t now);
184
Neels Hofmeyr16c3f572015-11-11 17:27:01 +0100185/* Initialize to all-empty; must be called before using the item in any way. */
186void expiring_item_init(struct expiring_item *item);
187
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200188/* Remove the given item from its expiry queue, and call item->del_cb, if set.
189 * This sets item->del_cb to NULL and is harmless when run a second time on the
190 * same item, so the del_cb may choose to call this function, too, to allow
191 * deleting items from several code paths. */
192void expiring_item_del(struct expiring_item *item);
193
194/* Carry out due expiry of mappings. Must be invoked regularly.
195 * 'now' is the current clock count in seconds and must correspond to the clock
196 * count passed to nr_map_add(). A monotonous clock counter should be used. */
197int expiry_tick(struct expiry *exq, time_t now);
198
199
200/* number map */
201
202/* A number map assigns a "random" mapped number to each user provided number.
203 * If the same number is requested multiple times, the same mapped number is
204 * returned.
205 *
206 * Number maps plug into possibly shared pools and expiry queues, for example:
207 *
208 * mapA -----------+-> pool1 <-+-- mapB
209 * {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3}
210 * | |
211 * | |
212 * /-> \-> expiry1 <-/
213 * | (30 seconds)
214 * |
215 * mapC -------+-----> pool2 <-+-- mapD
216 * {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5}
217 * |
218 * expiry2 <-/
219 * (60 seconds)
220 *
221 * A map contains mappings ("10->1"). Each map needs a number pool, which can
222 * be shared with other maps. Each new mapping receives a number from the pool,
223 * which is then unavailable to any other map using the same pool.
224 *
225 * A map may point at an expiry queue, in which case all mappings added to it
226 * are also appended to the expiry queue (using a separate llist entry in the
227 * mapping). Any number of maps may submit to the same expiry queue, if they
228 * desire the same expiry timeout. An expiry queue stores the mappings in
229 * chronological order, so that expiry checking is needed only from the start
230 * of the queue; hence only mappings with identical expiry timeout can be added
231 * to the same expiry queue. Upon expiry, a mapping is dropped from the map it
232 * was submitted at. expiry_tick() needs to be called regularly for each expiry
233 * queue.
234 *
235 * A nr_mapping can be embedded in a larger struct: each mapping can have a
236 * distinct destructor (del_cb), and each del_cb can figure out the container
237 * struct's address and free that upon expiry or manual deletion. So in expiry
238 * queues (and even maps), mappings of different container types can be mixed.
239 * This can help to drastically reduce the amount of unnecessary visits during
240 * expiry checking, for the case that no expiry is pending. An expiry queue
241 * always knows which mappings to expire next, because they are right at the
242 * start of its list.
243 *
244 * Mapping allocation and a del_cb are provided by the caller. If del_cb is
245 * NULL, no deallocation will be done (allowing statically allocated entries).
246 */
247/* TODO at some point I thought the allocation & del_cb complexity was
248 * needed/helpful, but by now it seems like overkill. Maybe lose that again. */
249
250typedef int nr_t;
251
252/* Generator for unused numbers. So far this counts upwards from zero, but the
253 * implementation may change in the future. Treat this like an opaque struct.
254 * If this becomes random, the tests need to be fixed. */
255struct nr_pool {
256 nr_t last_nr;
257 /* TODO add min, max, for safe wrapping */
258};
259
260struct nr_mapping {
261 struct llist_head entry;
262 struct expiring_item expiry_entry;
263
264 void *origin;
265 nr_t orig;
266 nr_t repl;
267};
268
269struct nr_map {
270 struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */
271 struct expiry *add_items_to_expiry;
272 struct llist_head mappings;
273};
274
275
276void nr_pool_init(struct nr_pool *pool);
277
278/* Return the next unused number from the nr_pool. */
279nr_t nr_pool_next(struct nr_pool *pool);
280
281/* Initialize the nr_mapping to zero/empty values. */
282void nr_mapping_init(struct nr_mapping *mapping);
283
284/* Remove the given mapping from its parent map and expiry queue, and call
285 * mapping->del_cb, if set. */
286void nr_mapping_del(struct nr_mapping *mapping);
287
288/* Initialize an (already allocated) nr_map, and set the map's number pool.
289 * Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry
290 * queue to exq, so that all added mappings are automatically expired after the
291 * time configured in exq. exq may be NULL to disable automatic expiry. */
292void nr_map_init(struct nr_map *map, struct nr_pool *pool,
293 struct expiry *exq);
294
295/* Add a new entry to the map. mapping->orig, mapping->origin and
296 * mapping->del_cb must be set before calling this function. The remaining
297 * fields of *mapping will be overwritten. mapping->repl is set to the next
298 * available mapped number from map->pool. 'now' is the current clock count in
299 * seconds; if no map->expiry is used, just pass 0 for 'now'. */
300void nr_map_add(struct nr_map *map, struct nr_mapping *mapping,
301 time_t now);
302
303/* Return a known mapping from nr_orig and the given origin. If nr_orig is
304 * unknown, return NULL. */
305struct nr_mapping *nr_map_get(const struct nr_map *map,
306 void *origin, nr_t nr_orig);
307
308/* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */
309struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl);
310
311/* Remove all mappings from map. */
312void nr_map_clear(struct nr_map *map);
313
314/* Return 1 if map has no entries, 0 otherwise. */
315int nr_map_empty(const struct nr_map *map);
316
317
318/* config */
319
320static const int GTPH_SEQ_MAPPING_EXPIRY_SECS = 30; /* TODO is there a spec for this? */
321static const int GTPH_TEI_MAPPING_EXPIRY_MINUTES = 6 * 60; /* TODO is there a spec for this? */
322
323struct gtphub_cfg_addr {
324 const char *addr_str;
325 uint16_t port;
326};
327
328struct gtphub_cfg_bind {
329 struct gtphub_cfg_addr bind;
330};
331
332struct gtphub_cfg {
333 struct gtphub_cfg_bind to_sgsns[GTPH_PLANE_N];
334 struct gtphub_cfg_bind to_ggsns[GTPH_PLANE_N];
335 struct gtphub_cfg_addr sgsn_proxy[GTPH_PLANE_N];
336 struct gtphub_cfg_addr ggsn_proxy[GTPH_PLANE_N];
337};
338
339
340/* state */
341
342struct gtphub_peer {
343 struct llist_head entry;
344
345 struct llist_head addresses; /* Alternatives, not load balancing. */
346 struct nr_pool seq_pool;
347 struct nr_map seq_map;
348};
349
350struct gtphub_peer_addr {
351 struct llist_head entry;
352
353 struct gtphub_peer *peer;
354 struct gsn_addr addr;
355 struct llist_head ports;
356};
357
358struct gtphub_peer_port {
359 struct llist_head entry;
360
361 struct gtphub_peer_addr *peer_addr;
362 uint16_t port;
363 unsigned int ref_count; /* references from other peers' seq_maps */
364 struct osmo_sockaddr sa;
365};
366
367struct gtphub_bind {
368 struct gsn_addr local_addr;
369 struct osmo_fd ofd;
370
371 /* list of struct gtphub_peer */
372 struct llist_head peers;
373};
374
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100375struct gtphub_resolved_ggsn {
376 struct llist_head entry;
377 struct expiring_item expiry_entry;
378
379 /* The APN OI, the Operator Identifier, is the combined address,
380 * including parts of the IMSI and APN NI, and ending with ".gprs". */
381 char apn_oi_str[GSM_APN_LENGTH];
382
383 /* Which address and port we resolved that to. */
384 struct gtphub_peer_port *peer;
385};
386
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200387struct gtphub {
388 struct gtphub_bind to_sgsns[GTPH_PLANE_N];
389 struct gtphub_bind to_ggsns[GTPH_PLANE_N];
390
391 /* pointers to an entry of to_sgsns[x].peers */
392 struct gtphub_peer_port *sgsn_proxy[GTPH_PLANE_N];
393
394 /* pointers to an entry of to_ggsns[x].peers */
395 struct gtphub_peer_port *ggsn_proxy[GTPH_PLANE_N];
396
397 struct nr_map tei_map[GTPH_PLANE_N];
398 struct nr_pool tei_pool[GTPH_PLANE_N];
399
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100400 struct llist_head ggsn_lookups; /* opaque (gtphub_ext.c) */
401 struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */
402
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200403 struct osmo_timer_list gc_timer;
404 struct expiry expire_seq_maps;
405 struct expiry expire_tei_maps;
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100406
407 uint16_t restart_counter;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200408};
409
410struct gtp_packet_desc;
411
412
413/* api */
414
415int gtphub_vty_init(void);
416int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file);
417
418/* Initialize and start gtphub: bind to ports, run expiry timers. */
419int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg);
420
421time_t gtphub_now(void);
422
423/* Remove expired items, empty peers, ... */
424void gtphub_gc(struct gtphub *hub, time_t now);
425
426/* Return the string of the first address for this peer. */
427const char *gtphub_peer_str(struct gtphub_peer *peer);
428/* Same with a different static buffer. We often want to print two peers. */
429const char *gtphub_peer_str2(struct gtphub_peer *peer);
430
431int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
432 unsigned int port_idx,
433 const struct osmo_sockaddr *from_addr,
434 uint8_t *buf,
435 size_t received,
436 time_t now,
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100437 uint8_t **reply_buf,
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200438 struct osmo_fd **to_ofd,
439 struct osmo_sockaddr *to_addr);
440
441int gtphub_from_ggsns_handle_buf(struct gtphub *hub,
442 unsigned int port_idx,
443 const struct osmo_sockaddr *from_addr,
444 uint8_t *buf,
445 size_t received,
446 time_t now,
Neels Hofmeyrbb3d6782015-11-09 15:12:25 +0100447 uint8_t **reply_buf,
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200448 struct osmo_fd **to_ofd,
449 struct osmo_sockaddr *to_addr);
450
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100451struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
452 struct gtphub_bind *bind,
453 const struct gsn_addr *addr,
454 uint16_t port);
455
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200456struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
457 const struct osmo_sockaddr *addr);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100458
459void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
460 struct gsn_addr *resolved_addr,
461 time_t now);
462
463const char *gtphub_port_str(struct gtphub_peer_port *port);