diff options
Diffstat (limited to 'openbsc')
-rw-r--r-- | openbsc/include/openbsc/gtphub.h | 4 | ||||
-rw-r--r-- | openbsc/src/gprs/gtphub.c | 86 | ||||
-rw-r--r-- | openbsc/tests/gtphub/gtphub_test.c | 128 | ||||
-rw-r--r-- | openbsc/tests/gtphub/gtphub_test.ok | 1 |
4 files changed, 125 insertions, 94 deletions
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index 05eee851e..514b1a375 100644 --- a/openbsc/include/openbsc/gtphub.h +++ b/openbsc/include/openbsc/gtphub.h @@ -396,6 +396,8 @@ struct gtphub { struct osmo_timer_list gc_timer; struct expiry expire_seq_maps; struct expiry expire_tei_maps; + + uint16_t restart_counter; }; struct gtp_packet_desc; @@ -425,6 +427,7 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub, uint8_t *buf, size_t received, time_t now, + uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr); @@ -434,6 +437,7 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub, uint8_t *buf, size_t received, time_t now, + uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr); diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index eae4cf2cf..389a0cac6 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -1222,6 +1222,7 @@ static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) struct osmo_sockaddr to_addr; struct osmo_fd *to_ofd; size_t len; + uint8_t *reply_buf; len = gtphub_read(from_ggsns_ofd, &from_addr, buf, sizeof(buf)); if (len < 1) @@ -1229,11 +1230,11 @@ static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) len = gtphub_from_ggsns_handle_buf(hub, plane_idx, &from_addr, buf, len, gtphub_now(), - &to_ofd, &to_addr); + &reply_buf, &to_ofd, &to_addr); if (len < 1) return 0; - return gtphub_write(to_ofd, &to_addr, buf, len); + return gtphub_write(to_ofd, &to_addr, reply_buf, len); } static int gtphub_unmap(struct gtphub *hub, @@ -1311,10 +1312,33 @@ static int gsn_addr_to_sockaddr(struct gsn_addr *src, return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port); } -static int gtphub_handle_echo(const struct gtp_packet_desc *p) +/* If p is an Echo request, replace p's data with the matching response and + * return 1. If p is no Echo request, return 0, or -1 if an invalid packet is + * detected. */ +static int gtphub_handle_echo(struct gtphub *hub, struct gtp_packet_desc *p, uint8_t **reply_buf) { - /* TODO */ - return 0; + if (p->type != GTP_ECHO_REQ) + return 0; + + static uint8_t echo_response_data[14] = { + 0x32, /* flags */ + GTP_ECHO_RSP, + 0x00, 14 - 8, /* Length in network byte order */ + 0x00, 0x00, 0x00, 0x00, /* Zero TEI */ + 0, 0, /* Seq, to be replaced */ + 0, 0, /* no extensions */ + 0x0e, /* Recovery IE */ + 0 /* Recovery counter, to be replaced */ + }; + uint16_t *seq = (uint16_t*)&echo_response_data[8]; + uint8_t *recovery = &echo_response_data[13]; + + *seq = hton16(p->seq); + *recovery = hub->restart_counter; + + *reply_buf = echo_response_data; + + return sizeof(echo_response_data); } /* Parse buffer as GTP packet, replace elements in-place and return the ofd and @@ -1328,29 +1352,30 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub, uint8_t *buf, size_t received, time_t now, + uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr) { LOG("<- rx from GGSN %s\n", osmo_sockaddr_to_str(from_addr)); - *to_ofd = &hub->to_sgsns[plane_idx].ofd; - static struct gtp_packet_desc p; gtp_decode(buf, received, plane_idx, &p); if (p.rc <= 0) return -1; - int rc; - rc = gtphub_handle_echo(&p); - if (rc == 1) { - /* It was en echo. Nothing left to do. */ - /* (*to_ofd already set above.) */ + int reply_len; + reply_len = gtphub_handle_echo(hub, &p, reply_buf); + if (reply_len > 0) { + /* It was an echo. Nothing left to do. */ osmo_sockaddr_copy(to_addr, from_addr); - return 0; + *to_ofd = &hub->to_ggsns[plane_idx].ofd; + return reply_len; } - if (rc < 0) - return -1; /* Invalid packet. */ + if (reply_len < 0) + return -1; + + *to_ofd = &hub->to_sgsns[plane_idx].ofd; /* If a GGSN proxy is configured, check that it's indeed that proxy * talking to us. A proxy is a forced 1:1 connection, e.g. to another @@ -1418,6 +1443,8 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub, gtphub_map_seq(&p, ggsn, sgsn, now); osmo_sockaddr_copy(to_addr, &sgsn->sa); + + *reply_buf = (uint8_t*)p.data; return received; } @@ -1437,6 +1464,7 @@ static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) struct osmo_sockaddr to_addr; struct osmo_fd *to_ofd; size_t len; + uint8_t *reply_buf; len = gtphub_read(from_sgsns_ofd, &from_addr, buf, sizeof(buf)); if (len < 1) @@ -1444,11 +1472,11 @@ static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) len = gtphub_from_sgsns_handle_buf(hub, plane_idx, &from_addr, buf, len, gtphub_now(), - &to_ofd, &to_addr); + &reply_buf, &to_ofd, &to_addr); if (len < 1) return 0; - return gtphub_write(to_ofd, &to_addr, buf, len); + return gtphub_write(to_ofd, &to_addr, reply_buf, len); } /* Analogous to gtphub_from_ggsns_handle_buf(), see the comment there. */ @@ -1458,29 +1486,30 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub, uint8_t *buf, size_t received, time_t now, + uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr) { LOG("-> rx from SGSN %s\n", osmo_sockaddr_to_str(from_addr)); - *to_ofd = &hub->to_ggsns[plane_idx].ofd; - static struct gtp_packet_desc p; gtp_decode(buf, received, plane_idx, &p); if (p.rc <= 0) return -1; - int rc; - rc = gtphub_handle_echo(&p); - if (rc == 1) { - /* It was en echo. Nothing left to do. */ - /* (*to_ofd already set above.) */ + int reply_len; + reply_len = gtphub_handle_echo(hub, &p, reply_buf); + if (reply_len > 0) { + /* It was an echo. Nothing left to do. */ osmo_sockaddr_copy(to_addr, from_addr); - return 0; + *to_ofd = &hub->to_ggsns[plane_idx].ofd; + return reply_len; } - if (rc < 0) - return -1; /* Invalid packet. */ + if (reply_len < 0) + return -1; + + *to_ofd = &hub->to_ggsns[plane_idx].ofd; /* If an SGSN proxy is configured, check that it's indeed that proxy * talking to us. A proxy is a forced 1:1 connection, e.g. to another @@ -1583,6 +1612,7 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub, osmo_sockaddr_copy(to_addr, &ggsn->sa); + *reply_buf = (uint8_t*)p.data; return received; } @@ -1764,6 +1794,8 @@ int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg) gtphub_init(hub); gtphub_ares_init(hub); + /* TODO set hub->restart_counter from external file. */ + int plane_idx; for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) { rc = gtphub_bind_start(&hub->to_ggsns[plane_idx], diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c index 81875f1c7..7839050cd 100644 --- a/openbsc/tests/gtphub/gtphub_test.c +++ b/openbsc/tests/gtphub/gtphub_test.c @@ -399,6 +399,7 @@ int __wrap_gtphub_ares_init(struct gtphub *hub) #define buf_len 1024 static uint8_t buf[buf_len]; +static uint8_t *reply_buf; static unsigned int msg(const char *hex) { @@ -410,16 +411,16 @@ static unsigned int msg(const char *hex) /* Compare static buf to given string constant. The amount of bytes is obtained * from parsing the GTP header in buf. hex must match an osmo_hexdump() of the * desired message. Return 1 if size and content match. */ -#define msg_is(MSG) _msg_is(MSG, __FILE__, __LINE__) -static int _msg_is(const char *hex, const char *file, int line) +#define reply_is(MSG) _reply_is(MSG, __FILE__, __LINE__) +static int _reply_is(const char *hex, const char *file, int line) { - struct gtp1_header_long *h = (void*)buf; + struct gtp1_header_long *h = (void*)reply_buf; int len = ntoh16(h->length) + 8; - const char *dump = osmo_hexdump_nospc(buf, len); + const char *dump = osmo_hexdump_nospc(reply_buf, len); int cmp = strcmp(dump, hex); if (cmp != 0) { - printf("\n%s:%d: msg_is(): MISMATCH\n" + printf("\n%s:%d: reply_is(): MISMATCH\n" " expecting:\n'%s'\n" " got:\n'%s'\n\n", file, @@ -466,87 +467,82 @@ static void test_echo(void) time_t now = 123; gtphub_init(hub); - - /* TODO This test should test for gtphub echoing back to each side. - * Echos must not be routed through. */ + hub->restart_counter = 0x23; const char *gtp_ping_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ "01" /* type 01: Echo request */ "0004" /* length of 4 after header TEI */ "00000000" /* header TEI == 0 in Echo */ - "abcd" /* some 16 octet sequence nr */ + "abcd" /* some 2 octet sequence nr */ "0000" /* N-PDU 0, no extension header (why is this here?) */ ; - /* Same with mapped sequence number */ - const char *gtp_ping_to_ggsn = - "32" "01" "0004" "00000000" - "6d31" /* mapped seq */ - "00" "00"; - - const char *gtp_pong_from_ggsn = + const char *gtp_pong_to_sgsn = "32" "02" /* type 02: Echo response */ - "0006" /* len */ - "00000000" /* tei */ - "6d31" /* mapped seq */ - "0000" /* ext */ - "0e01" /* 0e: Recovery, val == 1 */ + "0006" /* length of 6 after header TEI */ + "00000000" /* header TEI == 0 in Echo */ + "abcd" /* same sequence nr */ + "0000" + "0e23" /* Recovery with restart counter */ ; - /* Same with unmapped sequence number */ - const char *gtp_pong_to_sgsn = - "32" "02" "0006" "00000000" - "abcd" /* unmapped seq */ - "00" "00" "0e01"; - - /* Set the GGSN address that gtphub is forced to resolve to. */ - 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); + OSMO_ASSERT(osmo_sockaddr_init_udp(&orig_sgsn_addr, + "192.168.42.23", 423) == 0); struct osmo_fd *ggsn_ofd = NULL; - struct osmo_sockaddr ggsn_addr; + struct osmo_sockaddr to_addr; int send; send = gtphub_from_sgsns_handle_buf(hub, GTPH_PLANE_CTRL, &orig_sgsn_addr, buf, msg(gtp_ping_from_sgsn), now, - &ggsn_ofd, &ggsn_addr); + &reply_buf, &ggsn_ofd, &to_addr); OSMO_ASSERT(send > 0); - OSMO_ASSERT(ggsn_addr.l); - OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_sa)); - OSMO_ASSERT(msg_is(gtp_ping_to_ggsn)); + OSMO_ASSERT(to_addr.l); + OSMO_ASSERT(same_addr(&to_addr, &orig_sgsn_addr)); + OSMO_ASSERT(reply_is(gtp_pong_to_sgsn)); - struct osmo_fd *sgsn_ofd; - struct osmo_sockaddr sgsn_addr; - send = gtphub_from_ggsns_handle_buf(hub, GTPH_PLANE_CTRL, &ggsn_addr, - buf, msg(gtp_pong_from_ggsn), now, - &sgsn_ofd, &sgsn_addr); + struct gtphub_peer_port *sgsn_port = + gtphub_port_find_sa(&hub->to_sgsns[GTPH_PLANE_CTRL], + &orig_sgsn_addr); + /* We don't record Echo peers. */ + OSMO_ASSERT(!sgsn_port); + + const char *gtp_ping_from_ggsn = + "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ + "01" /* type 01: Echo request */ + "0004" /* length of 4 after header TEI */ + "00000000" /* header TEI == 0 in Echo */ + "cdef" /* some 2 octet sequence nr */ + "0000" /* N-PDU 0, no extension header (why is this here?) */ + ; + + const char *gtp_pong_to_ggsn = + "32" + "02" /* type 02: Echo response */ + "0006" /* length of 6 after header TEI */ + "00000000" /* header TEI == 0 in Echo */ + "cdef" /* same sequence nr */ + "0000" + "0e23" /* Recovery with restart counter */ + ; + + struct osmo_sockaddr orig_ggsn_addr; + OSMO_ASSERT(osmo_sockaddr_init_udp(&orig_ggsn_addr, + "192.168.24.32", 321) == 0); + struct osmo_fd *sgsn_ofd = NULL; + send = gtphub_from_ggsns_handle_buf(hub, GTPH_PLANE_CTRL, &orig_ggsn_addr, + buf, msg(gtp_ping_from_ggsn), now, + &reply_buf, &sgsn_ofd, &to_addr); OSMO_ASSERT(send > 0); - OSMO_ASSERT(same_addr(&sgsn_addr, &orig_sgsn_addr)); - OSMO_ASSERT(msg_is(gtp_pong_to_sgsn)); + OSMO_ASSERT(same_addr(&to_addr, &orig_ggsn_addr)); + OSMO_ASSERT(reply_is(gtp_pong_to_ggsn)); struct gtphub_peer_port *ggsn_port = gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL], - &resolved_ggsn_sa); - OSMO_ASSERT(ggsn_port); - struct gtphub_peer *ggsn = ggsn_port->peer_addr->peer; - /* now == 123; now + 30 == 153. */ - OSMO_ASSERT(nr_map_is(&ggsn->seq_map, "(43981->27953@153), ")); - - OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_CTRL], "")); - OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_USER], "")); + &orig_sgsn_addr); + /* We don't record Echo peers. */ + OSMO_ASSERT(!ggsn_port); gtphub_gc(hub, now + EXPIRE_ALL); } @@ -707,20 +703,20 @@ static void test_create_pdp_ctx(void) int send; send = gtphub_from_sgsns_handle_buf(hub, GTPH_PLANE_CTRL, &orig_sgsn_addr, buf, msg(gtp_req_from_sgsn), now, - &ggsn_ofd, &ggsn_addr); + &reply_buf, &ggsn_ofd, &ggsn_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(same_addr(&ggsn_addr, &resolved_ggsn_sa)); - OSMO_ASSERT(msg_is(gtp_req_to_ggsn)); + OSMO_ASSERT(reply_is(gtp_req_to_ggsn)); OSMO_ASSERT(was_resolved_for("240010123456789", "internet")); struct osmo_fd *sgsn_ofd; struct osmo_sockaddr sgsn_addr; send = gtphub_from_ggsns_handle_buf(hub, GTPH_PLANE_CTRL, &ggsn_addr, buf, msg(gtp_resp_from_ggsn), now, - &sgsn_ofd, &sgsn_addr); + &reply_buf, &sgsn_ofd, &sgsn_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(same_addr(&sgsn_addr, &orig_sgsn_addr)); - OSMO_ASSERT(msg_is(gtp_resp_to_sgsn)); + OSMO_ASSERT(reply_is(gtp_resp_to_sgsn)); struct gtphub_peer_port *ggsn_port = gtphub_port_find_sa(&hub->to_ggsns[GTPH_PLANE_CTRL], diff --git a/openbsc/tests/gtphub/gtphub_test.ok b/openbsc/tests/gtphub/gtphub_test.ok index 8d1075ab7..eac6f0c97 100644 --- a/openbsc/tests/gtphub/gtphub_test.ok +++ b/openbsc/tests/gtphub/gtphub_test.ok @@ -1,3 +1,2 @@ -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 |