aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/include
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-09-24 17:32:30 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-11-16 15:16:26 +0100
commitc8a614d2e9c56fe13c22f239be82f2a2c958e039 (patch)
treece820c172725b3072ed106ea2c233df108b8ef1d /openbsc/include
parent65482c919f82b28aa53cd519c4f7799b104051c0 (diff)
Add GTP hub initial code base.
First steps towards a new GTP hub. The aim is to mux GTP connections, so that multiple SGSN <--> GGSN links can pass through a single point. Background: allow having more than one SGSN, possibly in various remote locations. The recent addition of OAP to GSUP is related to the same background idea. (This is a collapsed patch of various changes that do not make sense to review in chronological order anymore, since a lot of it has thorougly transmorphed after it was first committed.) Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/include')
-rw-r--r--openbsc/include/openbsc/Makefile.am1
-rw-r--r--openbsc/include/openbsc/debug.h1
-rw-r--r--openbsc/include/openbsc/gtphub.h424
-rw-r--r--openbsc/include/openbsc/vty.h1
4 files changed, 427 insertions, 0 deletions
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 8a074c244..15c38d1e9 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -18,6 +18,7 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
gprs_gb_parse.h smpp.h meas_feed.h gprs_gsup_messages.h \
gprs_gsup_client.h bsc_msg_filter.h \
oap.h oap_messages.h
+ gtphub.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 19d8fc2de..189ca476e 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -33,6 +33,7 @@ enum {
DCTRL,
DSMPP,
DFILTER,
+ DGTPHUB,
Debug_LastEntry,
};
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h
new file mode 100644
index 000000000..5298505db
--- /dev/null
+++ b/openbsc/include/openbsc/gtphub.h
@@ -0,0 +1,424 @@
+/* GTP Hub Implementation */
+
+/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+
+
+/* support */
+
+/* TODO move to osmocom/core/socket.c ? */
+#include <netdb.h> /* for IPPROTO_* etc */
+struct osmo_sockaddr {
+ struct sockaddr_storage a;
+ socklen_t l;
+};
+
+/* TODO move to osmocom/core/socket.c ? */
+/*! \brief Initialize a sockaddr
+ * \param[out] addr Valid osmo_sockaddr pointer to write result to
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] host Remote host name or IP address in string form
+ * \param[in] port Remote port number in host byte order
+ * \returns 0 on success, otherwise an error code (from getaddrinfo()).
+ *
+ * Copy the first result from a getaddrinfo() call with the given parameters to
+ * *addr and *addr_len. On error, do not change *addr and return nonzero.
+ */
+int osmo_sockaddr_init(struct osmo_sockaddr *addr,
+ uint16_t family, uint16_t type, uint8_t proto,
+ const char *host, uint16_t port);
+
+/* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to
+ * osmo_sockaddr_init(). */
+static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr,
+ const char *host, uint16_t port)
+{
+ return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port);
+}
+
+/*! \brief convert sockaddr to human readable string.
+ * \param[out] addr_str Valid pointer to a buffer of length addr_str_len.
+ * \param[in] addr_str_len Size of buffer addr_str points at.
+ * \param[out] port_str Valid pointer to a buffer of length port_str_len.
+ * \param[in] port_str_len Size of buffer port_str points at.
+ * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
+ * \param[in] flags flags as passed to getnameinfo().
+ * \returns 0 on success, an error code on error.
+ *
+ * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string
+ * representations of the given struct osmo_sockaddr in two caller provided
+ * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric
+ * address and port. Either one of addr_str or port_str may be NULL, in which
+ * case nothing is returned there.
+ *
+ * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */
+int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
+ char *port_str, size_t port_str_len,
+ const struct osmo_sockaddr *addr,
+ int flags);
+
+
+/*! \brief conveniently concatenate the parts returned by osmo_sockaddr_to_strs().
+ * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
+ * \param[in] buf A buffer to use for string operations.
+ * \param[in] buf_len Length of the buffer.
+ * \returns Address string (in buffer).
+ *
+ * Compose a string of the numeric IP-address and port represented by *addr of
+ * the form "<ip-addr> port <port>". The returned string is valid until the
+ * next invocation of this function.
+ */
+const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
+ char *buf, size_t buf_len);
+
+/*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer.
+ * \param[in] addr Binary representation as returned by osmo_sockaddr_init().
+ * \returns Address string in static buffer.
+ *
+ * See osmo_sockaddr_to_strb().
+ *
+ * Note: only one osmo_sockaddr_to_str() call will work per print/log
+ * statement. For two or more, use osmo_sockaddr_to_strb() with a separate
+ * buffer each.
+ */
+const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr);
+
+/*! \brief compare two osmo_sockaddr.
+ * \param[in] a The first address to compare.
+ * \param[in] b The other address to compare.
+ * \returns 0 if equal, otherwise -1 or 1.
+ */
+int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, const struct osmo_sockaddr *b);
+
+/*! \brief Overwrite *dst with *src.
+ * Like memcpy(), but copy only the valid bytes. */
+void osmo_sockaddr_copy(struct osmo_sockaddr *dst, const struct osmo_sockaddr *src);
+
+
+/* general */
+
+enum gtphub_plane_idx {
+ GTPH_PLANE_CTRL = 0,
+ GTPH_PLANE_USER = 1,
+ GTPH_PLANE_N
+};
+
+extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N];
+extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N];
+
+/* A host address in the form that is expected in the 7.7.32 GSN Address IE.
+ * len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no
+ * address is set, len shall be 0. */
+struct gsn_addr {
+ uint16_t len;
+ uint8_t buf[16];
+};
+
+void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src);
+int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str);
+
+/* Return gsna in numeric string form, in a static buffer. */
+const char *gsn_addr_to_str(const struct gsn_addr *gsna);
+
+/* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */
+const char *gsn_addr_to_strb(const struct gsn_addr *gsna,
+ char *strbuf, int strbuf_len);
+
+/* Return 1 on match, zero otherwise. */
+int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b);
+
+
+/* expiry */
+
+struct expiring_item;
+typedef void (*del_cb_t)(struct expiring_item *);
+
+struct expiring_item {
+ struct llist_head entry;
+ time_t expiry;
+ del_cb_t del_cb;
+};
+
+struct expiry {
+ int expiry_in_seconds;
+ struct llist_head items;
+};
+
+/* Initialize an expiry queue. */
+void expiry_init(struct expiry *exq, int expiry_in_seconds);
+
+/* Add a new mapping, or restart the expiry timeout for an already listed mapping. */
+void expiry_add(struct expiry *exq, struct expiring_item *mapping, time_t now);
+
+/* Remove the given item from its expiry queue, and call item->del_cb, if set.
+ * This sets item->del_cb to NULL and is harmless when run a second time on the
+ * same item, so the del_cb may choose to call this function, too, to allow
+ * deleting items from several code paths. */
+void expiring_item_del(struct expiring_item *item);
+
+/* Carry out due expiry of mappings. Must be invoked regularly.
+ * 'now' is the current clock count in seconds and must correspond to the clock
+ * count passed to nr_map_add(). A monotonous clock counter should be used. */
+int expiry_tick(struct expiry *exq, time_t now);
+
+
+/* number map */
+
+/* A number map assigns a "random" mapped number to each user provided number.
+ * If the same number is requested multiple times, the same mapped number is
+ * returned.
+ *
+ * Number maps plug into possibly shared pools and expiry queues, for example:
+ *
+ * mapA -----------+-> pool1 <-+-- mapB
+ * {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3}
+ * | |
+ * | |
+ * /-> \-> expiry1 <-/
+ * | (30 seconds)
+ * |
+ * mapC -------+-----> pool2 <-+-- mapD
+ * {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5}
+ * |
+ * expiry2 <-/
+ * (60 seconds)
+ *
+ * A map contains mappings ("10->1"). Each map needs a number pool, which can
+ * be shared with other maps. Each new mapping receives a number from the pool,
+ * which is then unavailable to any other map using the same pool.
+ *
+ * A map may point at an expiry queue, in which case all mappings added to it
+ * are also appended to the expiry queue (using a separate llist entry in the
+ * mapping). Any number of maps may submit to the same expiry queue, if they
+ * desire the same expiry timeout. An expiry queue stores the mappings in
+ * chronological order, so that expiry checking is needed only from the start
+ * of the queue; hence only mappings with identical expiry timeout can be added
+ * to the same expiry queue. Upon expiry, a mapping is dropped from the map it
+ * was submitted at. expiry_tick() needs to be called regularly for each expiry
+ * queue.
+ *
+ * A nr_mapping can be embedded in a larger struct: each mapping can have a
+ * distinct destructor (del_cb), and each del_cb can figure out the container
+ * struct's address and free that upon expiry or manual deletion. So in expiry
+ * queues (and even maps), mappings of different container types can be mixed.
+ * This can help to drastically reduce the amount of unnecessary visits during
+ * expiry checking, for the case that no expiry is pending. An expiry queue
+ * always knows which mappings to expire next, because they are right at the
+ * start of its list.
+ *
+ * Mapping allocation and a del_cb are provided by the caller. If del_cb is
+ * NULL, no deallocation will be done (allowing statically allocated entries).
+ */
+/* TODO at some point I thought the allocation & del_cb complexity was
+ * needed/helpful, but by now it seems like overkill. Maybe lose that again. */
+
+typedef int nr_t;
+
+/* Generator for unused numbers. So far this counts upwards from zero, but the
+ * implementation may change in the future. Treat this like an opaque struct.
+ * If this becomes random, the tests need to be fixed. */
+struct nr_pool {
+ nr_t last_nr;
+ /* TODO add min, max, for safe wrapping */
+};
+
+struct nr_mapping {
+ struct llist_head entry;
+ struct expiring_item expiry_entry;
+
+ void *origin;
+ nr_t orig;
+ nr_t repl;
+};
+
+struct nr_map {
+ struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */
+ struct expiry *add_items_to_expiry;
+ struct llist_head mappings;
+};
+
+
+void nr_pool_init(struct nr_pool *pool);
+
+/* Return the next unused number from the nr_pool. */
+nr_t nr_pool_next(struct nr_pool *pool);
+
+/* Initialize the nr_mapping to zero/empty values. */
+void nr_mapping_init(struct nr_mapping *mapping);
+
+/* Remove the given mapping from its parent map and expiry queue, and call
+ * mapping->del_cb, if set. */
+void nr_mapping_del(struct nr_mapping *mapping);
+
+/* Initialize an (already allocated) nr_map, and set the map's number pool.
+ * Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry
+ * queue to exq, so that all added mappings are automatically expired after the
+ * time configured in exq. exq may be NULL to disable automatic expiry. */
+void nr_map_init(struct nr_map *map, struct nr_pool *pool,
+ struct expiry *exq);
+
+/* Add a new entry to the map. mapping->orig, mapping->origin and
+ * mapping->del_cb must be set before calling this function. The remaining
+ * fields of *mapping will be overwritten. mapping->repl is set to the next
+ * available mapped number from map->pool. 'now' is the current clock count in
+ * seconds; if no map->expiry is used, just pass 0 for 'now'. */
+void nr_map_add(struct nr_map *map, struct nr_mapping *mapping,
+ time_t now);
+
+/* Return a known mapping from nr_orig and the given origin. If nr_orig is
+ * unknown, return NULL. */
+struct nr_mapping *nr_map_get(const struct nr_map *map,
+ void *origin, nr_t nr_orig);
+
+/* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */
+struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl);
+
+/* Remove all mappings from map. */
+void nr_map_clear(struct nr_map *map);
+
+/* Return 1 if map has no entries, 0 otherwise. */
+int nr_map_empty(const struct nr_map *map);
+
+
+/* config */
+
+static const int GTPH_SEQ_MAPPING_EXPIRY_SECS = 30; /* TODO is there a spec for this? */
+static const int GTPH_TEI_MAPPING_EXPIRY_MINUTES = 6 * 60; /* TODO is there a spec for this? */
+
+struct gtphub_cfg_addr {
+ const char *addr_str;
+ uint16_t port;
+};
+
+struct gtphub_cfg_bind {
+ struct gtphub_cfg_addr bind;
+};
+
+struct gtphub_cfg {
+ struct gtphub_cfg_bind to_sgsns[GTPH_PLANE_N];
+ struct gtphub_cfg_bind to_ggsns[GTPH_PLANE_N];
+ struct gtphub_cfg_addr sgsn_proxy[GTPH_PLANE_N];
+ struct gtphub_cfg_addr ggsn_proxy[GTPH_PLANE_N];
+};
+
+
+/* state */
+
+struct gtphub_peer {
+ struct llist_head entry;
+
+ struct llist_head addresses; /* Alternatives, not load balancing. */
+ struct nr_pool seq_pool;
+ struct nr_map seq_map;
+};
+
+struct gtphub_peer_addr {
+ struct llist_head entry;
+
+ struct gtphub_peer *peer;
+ struct gsn_addr addr;
+ struct llist_head ports;
+};
+
+struct gtphub_peer_port {
+ struct llist_head entry;
+
+ struct gtphub_peer_addr *peer_addr;
+ uint16_t port;
+ unsigned int ref_count; /* references from other peers' seq_maps */
+ struct osmo_sockaddr sa;
+};
+
+struct gtphub_bind {
+ struct gsn_addr local_addr;
+ struct osmo_fd ofd;
+
+ /* list of struct gtphub_peer */
+ struct llist_head peers;
+};
+
+struct gtphub {
+ struct gtphub_bind to_sgsns[GTPH_PLANE_N];
+ struct gtphub_bind to_ggsns[GTPH_PLANE_N];
+
+ /* pointers to an entry of to_sgsns[x].peers */
+ struct gtphub_peer_port *sgsn_proxy[GTPH_PLANE_N];
+
+ /* pointers to an entry of to_ggsns[x].peers */
+ struct gtphub_peer_port *ggsn_proxy[GTPH_PLANE_N];
+
+ struct nr_map tei_map[GTPH_PLANE_N];
+ struct nr_pool tei_pool[GTPH_PLANE_N];
+
+ struct osmo_timer_list gc_timer;
+ struct expiry expire_seq_maps;
+ struct expiry expire_tei_maps;
+};
+
+struct gtp_packet_desc;
+
+
+/* api */
+
+int gtphub_vty_init(void);
+int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file);
+
+/* Initialize and start gtphub: bind to ports, run expiry timers. */
+int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg);
+
+time_t gtphub_now(void);
+
+/* Remove expired items, empty peers, ... */
+void gtphub_gc(struct gtphub *hub, time_t now);
+
+/* Return the string of the first address for this peer. */
+const char *gtphub_peer_str(struct gtphub_peer *peer);
+/* Same with a different static buffer. We often want to print two peers. */
+const char *gtphub_peer_str2(struct gtphub_peer *peer);
+
+int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
+ unsigned int port_idx,
+ const struct osmo_sockaddr *from_addr,
+ uint8_t *buf,
+ size_t received,
+ time_t now,
+ struct osmo_fd **to_ofd,
+ struct osmo_sockaddr *to_addr);
+
+int gtphub_from_ggsns_handle_buf(struct gtphub *hub,
+ unsigned int port_idx,
+ const struct osmo_sockaddr *from_addr,
+ uint8_t *buf,
+ size_t received,
+ time_t now,
+ struct osmo_fd **to_ofd,
+ struct osmo_sockaddr *to_addr);
+
+struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
+ const struct osmo_sockaddr *addr);
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
index 818a20e64..bc30e2357 100644
--- a/openbsc/include/openbsc/vty.h
+++ b/openbsc/include/openbsc/vty.h
@@ -36,6 +36,7 @@ enum bsc_vty_node {
BSC_NODE,
SMPP_NODE,
SMPP_ESME_NODE,
+ GTPHUB_NODE,
};
extern int bsc_vty_is_config_node(struct vty *vty, int node);