blob: 5ed15fc2685484e38a70cc5ecc22cb60365b8224 [file] [log] [blame]
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +02001/* GTP Hub Implementation */
2
3/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * gtphub_ext.c -- ext means extern. This file is kept separate so that these
7 * functions can be wrapped for gtphub_test.c. When a function and its callers
8 * are in the same compilational unit, the wrappability may be optimized away.
9 *
10 * Author: Neels Hofmeyr
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Affero General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include <string.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010027#include <unistd.h>
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020028
29#include <openbsc/gtphub.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010030#include <openbsc/debug.h>
31
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020032#include <osmocom/core/utils.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010033#include <osmocom/gsm/apn.h>
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020034
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010035/* TODO split GRX ares from sgsn into a separate struct and allow use without
36 * globals. */
37#include <openbsc/sgsn.h>
38extern struct sgsn_instance *sgsn;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020039
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010040struct sgsn_instance sgsn_inst = { 0 };
41struct sgsn_instance *sgsn = &sgsn_inst;
42
43extern void *osmo_gtphub_ctx;
44
45int gtphub_ares_init(struct gtphub *hub)
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020046{
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010047 return sgsn_ares_init(sgsn);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020048}
49
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010050struct ggsn_lookup {
51 struct llist_head entry;
52 struct expiring_item expiry_entry;
53
54 struct gtphub *hub;
55
56 char imsi_str[GSM_IMSI_LENGTH];
57 char apn_ni_str[GSM_APN_LENGTH];
58 char apn_oi_str[GSM_APN_LENGTH];
59 int have_3dig_mnc;
60};
61
62static int start_ares_query(struct ggsn_lookup *lookup);
63
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010064static void ggsn_lookup_cb(void *arg, int status, int timeouts,
65 struct hostent *hostent)
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010066{
67 struct ggsn_lookup *lookup = arg;
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010068 LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
69 &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010070
71 if (status != ARES_SUCCESS) {
72 LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
73
74 /* Need to try with three digits now */
75 if (!lookup->have_3dig_mnc) {
76 lookup->have_3dig_mnc = 1;
77 if (start_ares_query(lookup) == 0)
78 return;
79 }
80
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010081 LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
82 lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010083 goto remove_from_queue;
84 }
85
86 struct gsn_addr resolved_addr;
87 if (hostent->h_length > sizeof(resolved_addr.buf)) {
88 LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
89 (int)hostent->h_length, (int)sizeof(resolved_addr.buf));
90 goto remove_from_queue;
91 }
92
93 /* Get the first addr from the list */
94 char *addr0 = hostent->h_addr_list[0];
95 if (!addr0) {
96 LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
97 goto remove_from_queue;
98 }
99
Neels Hofmeyr16c3f572015-11-11 17:27:01 +0100100 memcpy(resolved_addr.buf, addr0, hostent->h_length);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100101 resolved_addr.len = hostent->h_length;
102
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100103 LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100104 osmo_hexdump((unsigned char*)&resolved_addr,
105 sizeof(resolved_addr)));
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100106
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100107 gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
108 gtphub_now());
109
110remove_from_queue:
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100111 LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
112 &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100113 expiring_item_del(&lookup->expiry_entry);
114}
115
116static void make_addr_str(struct ggsn_lookup *lookup)
117{
118 char *apn_oi_str;
119 apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
120 lookup->apn_ni_str,
121 lookup->have_3dig_mnc);
122 strncpy(lookup->apn_oi_str, apn_oi_str, sizeof(lookup->apn_oi_str));
123 lookup->apn_oi_str[sizeof(lookup->apn_oi_str)-1] = '\0';
124}
125
126static int start_ares_query(struct ggsn_lookup *lookup)
127{
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100128 LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
129 lookup->apn_oi_str, lookup, &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100130
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100131 int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
132 lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100133 if (rc != 0)
134 LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
135 return rc;
136}
137
138static void ggsn_lookup_del_cb(struct expiring_item *expi)
139{
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100140 struct ggsn_lookup *lookup;
141 lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100142
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100143 LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
144 expi);
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100145
146 lookup->expiry_entry.del_cb = 0;
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100147 expiring_item_del(expi);
148
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100149 llist_del(&lookup->entry);
150 talloc_free(lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100151}
152
153struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
154 const char *imsi_str,
155 const char *apn_ni_str)
156{
Neels Hofmeyr5b664f42015-11-10 20:32:13 +0100157 OSMO_ASSERT(imsi_str);
158 OSMO_ASSERT(apn_ni_str);
159
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100160 struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
161 struct ggsn_lookup);
Neels Hofmeyr5b664f42015-11-10 20:32:13 +0100162 OSMO_ASSERT(lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100163
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100164 LOGP(DGTPHUB, LOGL_NOTICE, "Request to resolve IMSI"
165 " '%s' with APN-NI '%s' (%p / %p)\n",
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100166 imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
167
Neels Hofmeyr16c3f572015-11-11 17:27:01 +0100168 expiring_item_init(&lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100169 lookup->hub = hub;
170
171 strncpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
172 lookup->imsi_str[sizeof(lookup->imsi_str)-1] = '\0';
173
174 strncpy(lookup->apn_ni_str, apn_ni_str, sizeof(lookup->apn_ni_str));
175 lookup->apn_ni_str[sizeof(lookup->apn_ni_str)-1] = '\0';
176
177 make_addr_str(lookup);
178
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100179 LOGP(DGTPHUB, LOGL_NOTICE, "looking for active queries...\n");
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100180 struct ggsn_lookup *active;
181 llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
182 if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
183 sizeof(lookup->apn_oi_str)) == 0) {
184 /* A query already pending. Just tip our hat. */
185 return NULL;
186 }
187 }
188
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100189 LOGP(DGTPHUB, LOGL_NOTICE, "looking for already resolved GGSNs...\n");
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100190 struct gtphub_resolved_ggsn *resolved;
191 llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
192 if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
193 sizeof(lookup->apn_oi_str)) == 0) {
194 /* Already resolved. */
195 return resolved->peer;
196 }
197 }
198
199 /* Kick off a resolution, but so far return nothing. The hope is that
200 * the peer will resend the request (a couple of times), and by then
201 * the GGSN will be resolved. */
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100202 LOGP(DGTPHUB, LOGL_NOTICE, "kick off resolution.\n");
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100203
204 llist_add(&lookup->entry, &hub->ggsn_lookups);
205
206 lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
207 expiry_add(&hub->expire_seq_maps, &lookup->expiry_entry, gtphub_now());
208
209 start_ares_query(lookup);
Neels Hofmeyra208c732015-11-11 19:26:09 +0100210 LOGP(DGTPHUB, LOGL_NOTICE, "Resolving %s %s ..."
211 " (Returning failure, hoping for a retry"
212 "once resolution has concluded)\n",
213 imsi_str, apn_ni_str);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100214
215 return NULL;
216}