aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs
diff options
context:
space:
mode:
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];