summaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-11-08 20:34:47 +0100
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-11-16 15:16:30 +0100
commit30f7bcbd79ad1b661dff844a43f0ccbf4f07cebf (patch)
tree68715097523724d61c56a99f2db6f947fac2f55b /openbsc/src/gprs
parentc8a614d2e9c56fe13c22f239be82f2a2c958e039 (diff)
gtphub: add first bits of GRX ares
For the resolving function, change the function signature to return a gtphub_peer_port. In consequence, publish two functions concerned with gtphub_peer_port instances for use in test and gtphub_ext.c. Add GGSN resolution queue, callback and cache. Simple implementation: if an SGSN asks for a GGSN, it will first get no answer, and I hope it will ask again once the GGSN is in the cache. Within gtphub_ext.c, have a dummy sgsn struct, as the sgsn_ares code currently depends on it (half the functions pass an sgsn instance pointer around, but the other half use the global one). In the unit tests, wrap away the ares initialization so that they can work without a DNS server around. The netcat test breaks because of this, will remove it. Using sgsn_ares, implement the gtphub_resolve_ggsn_addr() function, I hope: untested. Minor cosmetics just to see if you're paying attention... ;) Sponsored-by: On-Waves ehi
Diffstat (limited to 'openbsc/src/gprs')
-rw-r--r--openbsc/src/gprs/Makefile.am6
-rw-r--r--openbsc/src/gprs/gprs_utils.c4
-rw-r--r--openbsc/src/gprs/gtphub.c151
-rw-r--r--openbsc/src/gprs/gtphub_ext.c174
-rw-r--r--openbsc/src/gprs/sgsn_vty.c5
5 files changed, 255 insertions, 85 deletions
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
index 0dd38f99c..5212c67f2 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -35,7 +35,9 @@ osmo_sgsn_LDADD = \
-lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \
$(LIBCRYPTO_LIBS) -lrt
-osmo_gtphub_SOURCES = gtphub_main.c gtphub.c gtphub_ext.c gtphub_vty.c
+osmo_gtphub_SOURCES = gtphub_main.c gtphub.c gtphub_ext.c gtphub_vty.c \
+ sgsn_ares.c gprs_utils.c
osmo_gtphub_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
- -lgtp $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) -lrt
+ -lgtp $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
+ $(LIBCARES_LIBS) -lrt
diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c
index 2293f0254..ad479db81 100644
--- a/openbsc/src/gprs/gprs_utils.c
+++ b/openbsc/src/gprs/gprs_utils.c
@@ -114,7 +114,9 @@ int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
}
/* TODO: Move these conversion functions to a utils file. */
-/**
+/* TODO: consolidate with gprs_apn2str(). */
+/** memmove apn_enc to out_str, replacing the length octets in apn_enc with '.'
+ * (omitting the first one) and terminating with a '\0'.
* out_str needs to have rest_chars amount of bytes or 1 whatever is bigger.
*/
char * gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars)
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index 440f00fe3..eae4cf2cf 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -33,11 +33,13 @@
#include <openbsc/gtphub.h>
#include <openbsc/debug.h>
+#include <openbsc/gprs_utils.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
+
#define GTPHUB_DEBUG 1
static const int GTPH_GC_TICK_SECONDS = 1;
@@ -345,13 +347,13 @@ void validate_gtp_header(struct gtp_packet_desc *p)
* The first IEI is reached by passing i = 0.
* imsi must point at allocated space of (at least) 8 bytes.
* Return 1 on success, or 0 if not found. */
-static int get_ie_imsi(union gtpie_member *ie[], uint8_t *imsi, int i)
+static int get_ie_imsi(union gtpie_member *ie[], int i, uint8_t *imsi)
{
return gtpie_gettv0(ie, GTPIE_IMSI, i, imsi, 8) == 0;
}
/* Analogous to get_ie_imsi(). nsapi must point at a single uint8_t. */
-static int get_ie_nsapi(union gtpie_member *ie[], uint8_t *nsapi, int i)
+static int get_ie_nsapi(union gtpie_member *ie[], int i, uint8_t *nsapi)
{
return gtpie_gettv1(ie, GTPIE_NSAPI, i, nsapi) == 0;
}
@@ -379,6 +381,33 @@ static const char *imsi_to_str(uint8_t *imsi)
return str;
}
+static const char *get_ie_imsi_str(union gtpie_member *ie[], int i)
+{
+ uint8_t imsi_buf[8];
+ if (!get_ie_imsi(ie, i, imsi_buf))
+ return NULL;
+ return imsi_to_str(imsi_buf);
+}
+
+static const char *get_ie_apn_str(union gtpie_member *ie[])
+{
+ static char apn_buf[GSM_APN_LENGTH];
+ unsigned int len;
+ if (gtpie_gettlv(ie, GTPIE_APN, 0,
+ &len, apn_buf, sizeof(apn_buf)) != 0)
+ return NULL;
+
+ if (!len)
+ return NULL;
+
+ if (len > (sizeof(apn_buf) - 1))
+ len = sizeof(apn_buf) - 1;
+ apn_buf[len] = '\0';
+
+ return gprs_apn_to_str(apn_buf, (uint8_t*)apn_buf, len);
+}
+
+
/* Validate header, and index information elements. Write decoded packet
* information to *res. res->data will point at the given data buffer. On
* error, p->rc is set <= 0 (see enum gtp_rc). */
@@ -416,15 +445,15 @@ static void gtp_decode(const uint8_t *data, int data_len,
int i;
for (i = 0; i < 10; i++) {
- uint8_t imsi[8];
- if (!get_ie_imsi(res->ie, imsi, i))
+ const char *imsi = get_ie_imsi_str(res->ie, i);
+ if (!imsi)
break;
- LOG("| IMSI %s\n", imsi_to_str(imsi));
+ LOG("| IMSI %s\n", imsi);
}
for (i = 0; i < 10; i++) {
uint8_t nsapi;
- if (!get_ie_nsapi(res->ie, &nsapi, i))
+ if (!get_ie_nsapi(res->ie, i, &nsapi))
break;
LOG("| NSAPI %d\n", (int)nsapi);
}
@@ -481,7 +510,6 @@ int expiry_tick(struct expiry *exq, time_t now)
expiring_item_del(m);
expired ++;
} else {
- LOG("Not expired: %d > %d\n", (int)m->expiry, (int)now);
/* The items are added sorted by expiry. So when we hit
* an unexpired entry, only more unexpired ones will
* follow. */
@@ -654,19 +682,15 @@ static struct gtphub_peer_port *gtphub_resolve_ggsn(struct gtphub *hub,
struct gtp_packet_desc *p);
/* See gtphub_ext.c (wrapped by unit test) */
-int gtphub_resolve_ggsn_addr(struct gtphub *hub,
- struct osmo_sockaddr *result,
- struct gtp_packet_desc *p);
+struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
+ const char *imsi_str,
+ const char *apn_ni_str);
+int gtphub_ares_init(struct gtphub *hub);
static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind,
const struct gsn_addr *addr,
uint16_t port);
-static struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port);
-
static void gtphub_zero(struct gtphub *hub)
{
ZERO_STRUCT(hub);
@@ -833,7 +857,7 @@ const char *gtphub_peer_str2(struct gtphub_peer *peer)
return gtphub_peer_strb(peer, buf, sizeof(buf));
}
-static const char *gtphub_port_str(struct gtphub_peer_port *port)
+const char *gtphub_port_str(struct gtphub_peer_port *port)
{
static char buf[256];
return gtphub_port_strb(port, buf, sizeof(buf));
@@ -1562,6 +1586,49 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
return received;
}
+static void resolved_gssn_del_cb(struct expiring_item *expi)
+{
+ struct gtphub_resolved_ggsn *ggsn;
+ ggsn = container_of(expi, struct gtphub_resolved_ggsn, expiry_entry);
+
+ gtphub_port_ref_count_dec(ggsn->peer);
+ llist_del(&ggsn->entry);
+
+ ggsn->expiry_entry.del_cb = 0;
+ expiring_item_del(&ggsn->expiry_entry);
+
+ talloc_free(ggsn);
+}
+
+void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
+ struct gsn_addr *resolved_addr,
+ time_t now)
+{
+ struct gtphub_peer_port *pp;
+ struct gtphub_resolved_ggsn *ggsn;
+
+ pp = gtphub_port_have(hub, &hub->to_ggsns[GTPH_PLANE_CTRL],
+ resolved_addr, 2123);
+ if (!pp) {
+ LOGERR("Internal: Cannot create/find peer '%s'\n",
+ gsn_addr_to_str(resolved_addr));
+ return;
+ }
+
+ ggsn = talloc_zero(osmo_gtphub_ctx, struct gtphub_resolved_ggsn);
+ OSMO_ASSERT(ggsn);
+
+ ggsn->peer = pp;
+ gtphub_port_ref_count_inc(pp);
+
+ strncpy(ggsn->apn_oi_str, apn_oi_str, sizeof(ggsn->apn_oi_str));
+
+ ggsn->expiry_entry.del_cb = resolved_gssn_del_cb;
+ expiry_add(&hub->expire_tei_maps, &ggsn->expiry_entry, now);
+
+ llist_add(&ggsn->entry, &hub->resolved_ggsns);
+}
+
static int gtphub_gc_peer_port(struct gtphub_peer_port *pp)
{
return pp->ref_count == 0;
@@ -1654,6 +1721,8 @@ void gtphub_init(struct gtphub *hub)
{
gtphub_zero(hub);
+ INIT_LLIST_HEAD(&hub->resolved_ggsns);
+
expiry_init(&hub->expire_seq_maps, GTPH_SEQ_MAPPING_EXPIRY_SECS);
expiry_init(&hub->expire_tei_maps, GTPH_TEI_MAPPING_EXPIRY_MINUTES * 60);
@@ -1693,6 +1762,7 @@ int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg)
int rc;
gtphub_init(hub);
+ gtphub_ares_init(hub);
int plane_idx;
for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
@@ -1882,10 +1952,10 @@ static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a,
return pp;
}
-static struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port)
+struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
+ struct gtphub_bind *bind,
+ const struct gsn_addr *addr,
+ uint16_t port)
{
struct gtphub_peer_addr *a = gtphub_addr_have(hub, bind, addr);
@@ -1896,47 +1966,12 @@ static struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
return gtphub_addr_add_port(a, port);
}
-/* port_override: <=0: use port from addr; >0: use this number as port. Return
- * NULL if the address cannot be parsed. */
-static struct gtphub_peer_port *gtphub_port_have_sockaddr(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr,
- int port_override)
-{
- struct gsn_addr gsna;
- uint16_t port;
- if (gsn_addr_from_sockaddr(&gsna, &port, addr) != 0)
- return NULL;
-
- if (port_override > 0)
- port = port_override;
- return gtphub_port_have(hub, bind, &gsna, port);
-}
-
-static struct gtphub_peer_port *gtphub_have_ggsn(struct gtphub *hub,
- struct osmo_sockaddr *addr,
- unsigned int plane_idx,
- int port_override)
-{
- if (port_override == 0)
- port_override = gtphub_plane_idx_default_port[plane_idx];
-
- return gtphub_port_have_sockaddr(hub, &hub->to_ggsns[plane_idx], addr,
- port_override);
-}
-
static struct gtphub_peer_port *gtphub_resolve_ggsn(struct gtphub *hub,
struct gtp_packet_desc *p)
{
- int rc;
-
- struct osmo_sockaddr addr;
-
- rc = gtphub_resolve_ggsn_addr(hub, &addr, p);
- if (rc < 0)
- return NULL;
-
- return gtphub_have_ggsn(hub, &addr, p->plane_idx, -1);
+ return gtphub_resolve_ggsn_addr(hub,
+ get_ie_imsi_str(p->ie, 0),
+ get_ie_apn_str(p->ie));
}
diff --git a/openbsc/src/gprs/gtphub_ext.c b/openbsc/src/gprs/gtphub_ext.c
index 0a4164c73..d739614ca 100644
--- a/openbsc/src/gprs/gtphub_ext.c
+++ b/openbsc/src/gprs/gtphub_ext.c
@@ -24,35 +24,161 @@
*/
#include <string.h>
+#include <unistd.h>
#include <openbsc/gtphub.h>
+#include <openbsc/debug.h>
+
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/apn.h>
+
+/* TODO split GRX ares from sgsn into a separate struct and allow use without
+ * globals. */
+#include <openbsc/sgsn.h>
+extern struct sgsn_instance *sgsn;
+
+struct sgsn_instance sgsn_inst = { 0 };
+struct sgsn_instance *sgsn = &sgsn_inst;
+
+extern void *osmo_gtphub_ctx;
+
+int gtphub_ares_init(struct gtphub *hub)
+{
+ return sgsn_ares_init(sgsn);
+}
+
+struct ggsn_lookup {
+ struct llist_head entry;
+ struct expiring_item expiry_entry;
+
+ struct gtphub *hub;
+
+ char imsi_str[GSM_IMSI_LENGTH];
+ char apn_ni_str[GSM_APN_LENGTH];
+ char apn_oi_str[GSM_APN_LENGTH];
+ int have_3dig_mnc;
+};
+
+static int start_ares_query(struct ggsn_lookup *lookup);
-#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next)
-#define llist_first(head, type, entry) llist_entry(__llist_first(head), type, entry)
+static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent)
+{
+ struct ggsn_lookup *lookup = arg;
+
+ if (status != ARES_SUCCESS) {
+ LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
+
+ /* Need to try with three digits now */
+ if (!lookup->have_3dig_mnc) {
+ lookup->have_3dig_mnc = 1;
+ if (start_ares_query(lookup) == 0)
+ return;
+ }
+
+ LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN.\n");
+ goto remove_from_queue;
+ }
+
+ struct gsn_addr resolved_addr;
+ if (hostent->h_length > sizeof(resolved_addr.buf)) {
+ LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
+ (int)hostent->h_length, (int)sizeof(resolved_addr.buf));
+ goto remove_from_queue;
+ }
+
+ /* Get the first addr from the list */
+ char *addr0 = hostent->h_addr_list[0];
+ if (!addr0) {
+ LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
+ goto remove_from_queue;
+ }
-int gtphub_resolve_ggsn_addr(struct gtphub *hub,
- struct osmo_sockaddr *result,
- struct gtp_packet_desc *p)
+ memcpy(&resolved_addr.buf, addr0, hostent->h_length);
+ resolved_addr.len = hostent->h_length;
+
+ gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
+ gtphub_now());
+
+remove_from_queue:
+ expiring_item_del(&lookup->expiry_entry);
+}
+
+static void make_addr_str(struct ggsn_lookup *lookup)
{
- /* TODO This is just hardcodedly returning the first known address.
- * Should resolve from actual subscriber data. */
- struct gtphub_peer *peer = llist_first(&hub->to_ggsns[GTPH_PLANE_CTRL].peers,
- struct gtphub_peer, entry);
- if (!peer)
- return -1;
-
- struct gtphub_peer_addr *pa = llist_first(&peer->addresses,
- struct gtphub_peer_addr, entry);
- if (!pa)
- return -1;
-
- struct gtphub_peer_port *pp = llist_first(&pa->ports,
- struct gtphub_peer_port, entry);
- if (!pp)
- return -1;
-
- *result = pp->sa;
- return 0;
+ char *apn_oi_str;
+ apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
+ lookup->apn_ni_str,
+ lookup->have_3dig_mnc);
+ strncpy(lookup->apn_oi_str, apn_oi_str, sizeof(lookup->apn_oi_str));
+ lookup->apn_oi_str[sizeof(lookup->apn_oi_str)-1] = '\0';
}
+static int start_ares_query(struct ggsn_lookup *lookup)
+{
+ LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s\n", lookup->apn_oi_str);
+
+ int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb, &lookup);
+ if (rc != 0)
+ LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
+ return rc;
+}
+
+static void ggsn_lookup_del_cb(struct expiring_item *expi)
+{
+ struct ggsn_lookup *ggsn;
+ ggsn = container_of(expi, struct ggsn_lookup, expiry_entry);
+
+ ggsn->expiry_entry.del_cb = 0;
+ expiring_item_del(expi);
+
+ llist_del(&ggsn->entry);
+ talloc_free(ggsn);
+}
+
+struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
+ const char *imsi_str,
+ const char *apn_ni_str)
+{
+ struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx, struct ggsn_lookup);
+
+ lookup->hub = hub;
+
+ strncpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
+ lookup->imsi_str[sizeof(lookup->imsi_str)-1] = '\0';
+
+ strncpy(lookup->apn_ni_str, apn_ni_str, sizeof(lookup->apn_ni_str));
+ lookup->apn_ni_str[sizeof(lookup->apn_ni_str)-1] = '\0';
+
+ make_addr_str(lookup);
+
+ struct ggsn_lookup *active;
+ llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
+ if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
+ sizeof(lookup->apn_oi_str)) == 0) {
+ /* A query already pending. Just tip our hat. */
+ return NULL;
+ }
+ }
+
+ struct gtphub_resolved_ggsn *resolved;
+ llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
+ if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
+ sizeof(lookup->apn_oi_str)) == 0) {
+ /* Already resolved. */
+ return resolved->peer;
+ }
+ }
+
+ /* Kick off a resolution, but so far return nothing. The hope is that
+ * the peer will resend the request (a couple of times), and by then
+ * the GGSN will be resolved. */
+
+ llist_add(&lookup->entry, &hub->ggsn_lookups);
+
+ lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
+ expiry_add(&hub->expire_seq_maps, &lookup->expiry_entry, gtphub_now());
+
+ start_ares_query(lookup);
+
+ return NULL;
+}
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 3a73c9f55..3f6116393 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -110,6 +110,11 @@ DECLARE_TIMER(3397, "Wait for DEACT AA PDP CTX ACK timer (s)")
#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */
+/* TODO: consolidate with gprs_apn_to_str(). */
+/** Copy apn to a static buffer, replacing the length octets in apn_enc with '.'
+ * and terminating with a '\0'. Return the static buffer.
+ * len: the length of the encoded APN (which has no terminating zero).
+ */
static char *gprs_apn2str(uint8_t *apn, unsigned int len)
{
static char apnbuf[GSM48_MAX_APN_LEN+1];