diff options
Diffstat (limited to 'openbsc/src')
-rw-r--r-- | openbsc/src/gprs/Makefile.am | 6 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_utils.c | 4 | ||||
-rw-r--r-- | openbsc/src/gprs/gtphub.c | 151 | ||||
-rw-r--r-- | openbsc/src/gprs/gtphub_ext.c | 174 | ||||
-rw-r--r-- | openbsc/src/gprs/sgsn_vty.c | 5 |
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]; |