From 30f7bcbd79ad1b661dff844a43f0ccbf4f07cebf Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Sun, 8 Nov 2015 20:34:47 +0100 Subject: 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 --- openbsc/include/openbsc/gtphub.h | 28 ++++++ openbsc/src/gprs/Makefile.am | 6 +- openbsc/src/gprs/gprs_utils.c | 4 +- openbsc/src/gprs/gtphub.c | 151 +++++++++++++++++++------------ openbsc/src/gprs/gtphub_ext.c | 174 +++++++++++++++++++++++++++++++----- openbsc/src/gprs/sgsn_vty.c | 5 ++ openbsc/tests/gtphub/Makefile.am | 4 +- openbsc/tests/gtphub/gtphub_test.c | 132 ++++++++++++++++++++------- openbsc/tests/gtphub/gtphub_test.ok | 4 +- 9 files changed, 388 insertions(+), 120 deletions(-) diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index 5298505db..05eee851e 100644 --- a/openbsc/include/openbsc/gtphub.h +++ b/openbsc/include/openbsc/gtphub.h @@ -27,6 +27,8 @@ #include #include +#include + /* support */ @@ -363,6 +365,18 @@ struct gtphub_bind { struct llist_head peers; }; +struct gtphub_resolved_ggsn { + struct llist_head entry; + struct expiring_item expiry_entry; + + /* The APN OI, the Operator Identifier, is the combined address, + * including parts of the IMSI and APN NI, and ending with ".gprs". */ + char apn_oi_str[GSM_APN_LENGTH]; + + /* Which address and port we resolved that to. */ + struct gtphub_peer_port *peer; +}; + struct gtphub { struct gtphub_bind to_sgsns[GTPH_PLANE_N]; struct gtphub_bind to_ggsns[GTPH_PLANE_N]; @@ -376,6 +390,9 @@ struct gtphub { struct nr_map tei_map[GTPH_PLANE_N]; struct nr_pool tei_pool[GTPH_PLANE_N]; + struct llist_head ggsn_lookups; /* opaque (gtphub_ext.c) */ + struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */ + struct osmo_timer_list gc_timer; struct expiry expire_seq_maps; struct expiry expire_tei_maps; @@ -420,5 +437,16 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr); +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_find_sa(const struct gtphub_bind *bind, const struct osmo_sockaddr *addr); + +void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, + struct gsn_addr *resolved_addr, + time_t now); + +const char *gtphub_port_str(struct gtphub_peer_port *port); 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 #include +#include #include #include #include + #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 +#include #include +#include + #include +#include + +/* TODO split GRX ares from sgsn into a separate struct and allow use without + * globals. */ +#include +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]; diff --git a/openbsc/tests/gtphub/Makefile.am b/openbsc/tests/gtphub/Makefile.am index ecc6d62f0..d818811d9 100644 --- a/openbsc/tests/gtphub/Makefile.am +++ b/openbsc/tests/gtphub/Makefile.am @@ -11,10 +11,12 @@ noinst_PROGRAMS = gtphub_test gtphub_test_SOURCES = gtphub_test.c gtphub_test_LDFLAGS = \ - -Wl,--wrap=gtphub_resolve_ggsn_addr + -Wl,--wrap=gtphub_resolve_ggsn_addr \ + -Wl,--wrap=gtphub_ares_init gtphub_test_LDADD = \ $(top_builddir)/src/gprs/gtphub.o \ + $(top_builddir)/src/gprs/gprs_utils.o \ $(LIBOSMOCORE_LIBS) \ -lgtp -lrt diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c index af8f3ed75..81875f1c7 100644 --- a/openbsc/tests/gtphub/gtphub_test.c +++ b/openbsc/tests/gtphub/gtphub_test.c @@ -37,8 +37,6 @@ #define EXPIRE_ALL ((60 * GTPH_TEI_MAPPING_EXPIRY_MINUTES) + 1) -/* Make non-public API accessible */ - void gtphub_init(struct gtphub *hub); void *osmo_gtphub_ctx; @@ -328,19 +326,75 @@ static void test_expiry(void) /* override, requires '-Wl,--wrap=gtphub_resolve_ggsn_addr' */ -int __real_gtphub_resolve_ggsn_addr(struct gtphub *hub, - struct osmo_sockaddr *result, - struct gtp_packet_desc *p); - -struct osmo_sockaddr resolved_ggsn_addr = {.l = 0}; -int __wrap_gtphub_resolve_ggsn_addr(struct gtphub *hub, - struct osmo_sockaddr *result, - struct gtp_packet_desc *p) +struct gtphub_peer_port *__real_gtphub_resolve_ggsn_addr(struct gtphub *hub, + const char *imsi_str, + const char *apn_ni_str); + +struct gsn_addr resolved_ggsn_addr = { 0 }; +uint16_t resolved_ggsn_port = 2123; +char resolve_ggsn_got_imsi[256]; +char resolve_ggsn_got_ni[256]; +struct gtphub_peer_port *__wrap_gtphub_resolve_ggsn_addr(struct gtphub *hub, + const char *imsi_str, + const char *apn_ni_str) +{ + struct gtphub_peer_port *pp; + pp = gtphub_port_have(hub, &hub->to_ggsns[GTPH_PLANE_CTRL], + &resolved_ggsn_addr, resolved_ggsn_port); + printf("Wrap: returning GGSN addr from imsi %s ni %s: %s\n", + imsi_str, apn_ni_str, gtphub_port_str(pp)); + + if (imsi_str) { + strncpy(resolve_ggsn_got_imsi, imsi_str, sizeof(resolve_ggsn_got_imsi)); + resolve_ggsn_got_imsi[sizeof(resolve_ggsn_got_imsi) - 1] = '\0'; + } + else + strcpy(resolve_ggsn_got_imsi, "(null)"); + + if (apn_ni_str) { + strncpy(resolve_ggsn_got_ni, apn_ni_str, sizeof(resolve_ggsn_got_ni)); + resolve_ggsn_got_ni[sizeof(resolve_ggsn_got_ni) - 1] = '\0'; + } + else + strcpy(resolve_ggsn_got_ni, "(null)"); + + return pp; +} + +#define was_resolved_for(IMSI,NI) _was_resolved_for(IMSI, NI, __FILE__, __LINE__) +static int _was_resolved_for(const char *imsi, const char *ni, const char *file, int line) +{ + int cmp0 = strncmp(imsi, resolve_ggsn_got_imsi, sizeof(resolve_ggsn_got_imsi)); + + if (cmp0 != 0) { + printf("\n%s:%d: was_resolved_for(): MISMATCH for IMSI\n" + " expecting: '%s'\n" + " got: '%s'\n\n", + file, + line, + imsi, resolve_ggsn_got_imsi); + } + + int cmp1 = strncmp(ni, resolve_ggsn_got_ni, sizeof(resolve_ggsn_got_ni)); + if (cmp1 != 0) { + printf("\n%s:%d: was_resolved_for(): MISMATCH for NI\n" + " expecting: '%s'\n" + " got: '%s'\n\n", + file, + line, + ni, resolve_ggsn_got_ni); + } + + return (cmp0 == 0) && (cmp1 == 0); +} + +/* override, requires '-Wl,--wrap=gtphub_ares_init' */ +int __real_gtphub_ares_init(struct gtphub *hub); + +int __wrap_gtphub_ares_init(struct gtphub *hub) { - osmo_sockaddr_copy(result, &resolved_ggsn_addr); - printf("Wrap: returning GGSN addr: %s\n", - osmo_sockaddr_to_str(result)); - return (resolved_ggsn_addr.l != 0)? 0 : -1; + /* Do nothing. */ + return 0; } #define buf_len 1024 @@ -413,6 +467,9 @@ static void test_echo(void) gtphub_init(hub); + /* TODO This test should test for gtphub echoing back to each side. + * Echos must not be routed through. */ + const char *gtp_ping_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ "01" /* type 01: Echo request */ @@ -444,16 +501,17 @@ static void test_echo(void) "00" "00" "0e01"; /* Set the GGSN address that gtphub is forced to resolve to. */ - OSMO_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_addr, - "192.168.43.34", 434) + const char *resolved_ggsn_str = "192.168.43.34"; + resolved_ggsn_port = 434; + OSMO_ASSERT(gsn_addr_from_str(&resolved_ggsn_addr, resolved_ggsn_str) == 0); - /* according to spec, we'd always send to port 2123 instead... - struct osmo_sockaddr ggsn_standard_port; - OSMO_ASSERT(osmo_sockaddr_init_udp(&ggsn_standard_port, - "192.168.43.34", 2123) + /* A sockaddr for comparing later */ + struct osmo_sockaddr resolved_ggsn_sa; + OSMO_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_sa, + resolved_ggsn_str, + resolved_ggsn_port) == 0); - */ struct osmo_sockaddr orig_sgsn_addr; OSMO_ASSERT(osmo_sockaddr_init(&orig_sgsn_addr, @@ -467,7 +525,7 @@ static void test_echo(void) &ggsn_ofd, &ggsn_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(ggsn_addr.l); - OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_addr)); + OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_sa)); OSMO_ASSERT(msg_is(gtp_ping_to_ggsn)); struct osmo_fd *sgsn_ofd; @@ -481,7 +539,7 @@ static void test_echo(void) struct gtphub_peer_port *ggsn_port = gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL], - &resolved_ggsn_addr); + &resolved_ggsn_sa); OSMO_ASSERT(ggsn_port); struct gtphub_peer *ggsn = ggsn_port->peer_addr->peer; /* now == 123; now + 30 == 153. */ @@ -515,7 +573,7 @@ static void test_create_pdp_ctx(void) const char *gtp_req_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ "10" /* type 16: Create PDP Context Request */ - "0067" /* length = 8 + 103 */ + "0068" /* length = 8 + 104 */ "00000000" /* No TEI yet */ "abcd" /* Sequence nr */ "00" /* N-PDU 0 */ @@ -536,8 +594,8 @@ static void test_create_pdp_ctx(void) "0002" /* length = 2: empty PDP Address */ "f121" /* spare 0xf0, PDP organization 1, PDP type number 0x21 = 33 */ "83" /* 131: Access Point Name */ - "0008" /* length = 8 */ - "696e7465726e6574" /* "internet" */ + "0009" /* length */ + "08696e7465726e6574" /* "internet" */ "84" /* 132: Protocol Configuration Options */ "0015" /* length = 21 */ "80c0231101010011036d69670868656d6d656c6967" @@ -557,13 +615,13 @@ static void test_create_pdp_ctx(void) ; const char *gtp_req_to_ggsn = - "32" "10" "0067" "00000000" + "32" "10" "0068" "00000000" "6d31" /* mapped seq ("abcd") */ "00" "00" "02" "42000121436587f9" "0e60" "0f01" "10" "00000001" /* mapped TEI Data I ("123") */ "11" "00000001" /* mapped TEI Control ("321") */ "1400" "1a" "0800" "80" "0002" "f121" "83" - "0008" "696e7465726e6574" "84" "0015" + "0009" "08696e7465726e6574" "84" "0015" "80c0231101010011036d69670868656d6d656c6967" "85" "0004" "7f000201" /* replaced with gtphub's address ggsn ctrl */ "85" "0004" @@ -627,14 +685,23 @@ static void test_create_pdp_ctx(void) ; /* Set the GGSN address that gtphub is forced to resolve to. */ - OSMO_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_addr, - "192.168.43.34", 434) + const char *resolved_ggsn_str = "192.168.43.34"; + resolved_ggsn_port = 434; + OSMO_ASSERT(gsn_addr_from_str(&resolved_ggsn_addr, resolved_ggsn_str) + == 0); + + /* A sockaddr for comparing later */ + struct osmo_sockaddr resolved_ggsn_sa; + OSMO_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_sa, + resolved_ggsn_str, + resolved_ggsn_port) == 0); struct osmo_sockaddr orig_sgsn_addr; OSMO_ASSERT(osmo_sockaddr_init(&orig_sgsn_addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "192.168.42.23", 423) == 0); + struct osmo_fd *ggsn_ofd = NULL; struct osmo_sockaddr ggsn_addr; int send; @@ -642,8 +709,9 @@ static void test_create_pdp_ctx(void) buf, msg(gtp_req_from_sgsn), now, &ggsn_ofd, &ggsn_addr); OSMO_ASSERT(send > 0); - OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_addr)); + OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_sa)); OSMO_ASSERT(msg_is(gtp_req_to_ggsn)); + OSMO_ASSERT(was_resolved_for("240010123456789", "internet")); struct osmo_fd *sgsn_ofd; struct osmo_sockaddr sgsn_addr; @@ -656,7 +724,7 @@ static void test_create_pdp_ctx(void) struct gtphub_peer_port *ggsn_port = gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL], - &resolved_ggsn_addr); + &resolved_ggsn_sa); OSMO_ASSERT(ggsn_port); struct gtphub_peer *ggsn = ggsn_port->peer_addr->peer; /* now == 345; now + 30 == 375. diff --git a/openbsc/tests/gtphub/gtphub_test.ok b/openbsc/tests/gtphub/gtphub_test.ok index 7be13fe86..8d1075ab7 100644 --- a/openbsc/tests/gtphub/gtphub_test.ok +++ b/openbsc/tests/gtphub/gtphub_test.ok @@ -1,3 +1,3 @@ -Wrap: returning GGSN addr: 192.168.43.34 port 434 -Wrap: returning GGSN addr: 192.168.43.34 port 434 +Wrap: returning GGSN addr from imsi (null) ni (null): 192.168.43.34 port 434 +Wrap: returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 434 Done -- cgit v1.2.3