blob: 774563db4214892be1ab4726fd975072db88268d [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 *
Neels Hofmeyr4960fab2015-11-18 17:53:00 +01006 * gtphub_ares.c.
7 *
8 * This file is kept separate so that these functions can be wrapped for
9 * gtphub_test.c. When a function and its callers are in the same compilational
10 * unit, the wrappability may be optimized away.
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020011 *
12 * Author: Neels Hofmeyr
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Affero General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28#include <string.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010029#include <unistd.h>
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020030
Pau Espin Pedrolf2307c42023-01-09 11:55:18 +010031#include <osmocom/gtphub/gtphub.h>
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020032#include <osmocom/sgsn/debug.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010033
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020034#include <osmocom/core/utils.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010035#include <osmocom/gsm/apn.h>
Pau Espin Pedrol6aad14c2023-01-09 13:27:24 +010036#include <osmocom/gsm/protocol/gsm_23_003.h>
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020037
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010038/* TODO split GRX ares from sgsn into a separate struct and allow use without
39 * globals. */
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020040#include <osmocom/sgsn/sgsn.h>
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010041extern struct sgsn_instance *sgsn;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020042
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010043struct sgsn_instance sgsn_inst = { 0 };
44struct sgsn_instance *sgsn = &sgsn_inst;
45
46extern void *osmo_gtphub_ctx;
47
48int gtphub_ares_init(struct gtphub *hub)
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020049{
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010050 return sgsn_ares_init(sgsn);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020051}
52
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010053struct ggsn_lookup {
54 struct llist_head entry;
55 struct expiring_item expiry_entry;
56
57 struct gtphub *hub;
58
Harald Welted3fa84d2016-04-20 17:50:17 +020059 char imsi_str[GSM23003_IMSI_MAX_DIGITS+1];
Pau Espin Pedrol6aad14c2023-01-09 13:27:24 +010060 char apn_ni_str[APN_MAXLEN+1];
61 char apn_oi_str[APN_MAXLEN+1];
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010062 int have_3dig_mnc;
63};
64
65static int start_ares_query(struct ggsn_lookup *lookup);
66
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010067static void ggsn_lookup_cb(void *arg, int status, int timeouts,
68 struct hostent *hostent)
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010069{
70 struct ggsn_lookup *lookup = arg;
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010071 LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
72 &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010073
74 if (status != ARES_SUCCESS) {
75 LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
76
77 /* Need to try with three digits now */
78 if (!lookup->have_3dig_mnc) {
79 lookup->have_3dig_mnc = 1;
80 if (start_ares_query(lookup) == 0)
81 return;
82 }
83
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +010084 LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
85 lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +010086 goto remove_from_queue;
87 }
88
89 struct gsn_addr resolved_addr;
90 if (hostent->h_length > sizeof(resolved_addr.buf)) {
91 LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
92 (int)hostent->h_length, (int)sizeof(resolved_addr.buf));
93 goto remove_from_queue;
94 }
95
96 /* Get the first addr from the list */
97 char *addr0 = hostent->h_addr_list[0];
98 if (!addr0) {
99 LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
100 goto remove_from_queue;
101 }
102
Neels Hofmeyr16c3f572015-11-11 17:27:01 +0100103 memcpy(resolved_addr.buf, addr0, hostent->h_length);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100104 resolved_addr.len = hostent->h_length;
105
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100106 LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100107 osmo_hexdump((unsigned char*)&resolved_addr,
108 sizeof(resolved_addr)));
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100109
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100110 gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
111 gtphub_now());
112
113remove_from_queue:
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100114 LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
115 &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100116 expiring_item_del(&lookup->expiry_entry);
117}
118
119static void make_addr_str(struct ggsn_lookup *lookup)
120{
121 char *apn_oi_str;
122 apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
123 lookup->apn_ni_str,
124 lookup->have_3dig_mnc);
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100125 osmo_strlcpy(lookup->apn_oi_str, apn_oi_str,
126 sizeof(lookup->apn_oi_str));
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100127}
128
129static int start_ares_query(struct ggsn_lookup *lookup)
130{
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100131 LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
132 lookup->apn_oi_str, lookup, &lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100133
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100134 int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
135 lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100136 if (rc != 0)
137 LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
138 return rc;
139}
140
141static void ggsn_lookup_del_cb(struct expiring_item *expi)
142{
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100143 struct ggsn_lookup *lookup;
144 lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100145
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100146 LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
147 expi);
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100148
149 lookup->expiry_entry.del_cb = 0;
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100150 expiring_item_del(expi);
151
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100152 llist_del(&lookup->entry);
153 talloc_free(lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100154}
155
156struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
157 const char *imsi_str,
158 const char *apn_ni_str)
159{
Neels Hofmeyr5b664f42015-11-10 20:32:13 +0100160 OSMO_ASSERT(imsi_str);
161 OSMO_ASSERT(apn_ni_str);
162
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100163 struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
164 struct ggsn_lookup);
Neels Hofmeyr5b664f42015-11-10 20:32:13 +0100165 OSMO_ASSERT(lookup);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100166
Neels Hofmeyrd9b1d492015-11-18 18:11:09 +0100167 LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI"
Neels Hofmeyr9cfe0372015-11-16 14:52:05 +0100168 " '%s' with APN-NI '%s' (%p / %p)\n",
Neels Hofmeyr3317c842015-11-11 17:20:42 +0100169 imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
170
Neels Hofmeyr16c3f572015-11-11 17:27:01 +0100171 expiring_item_init(&lookup->expiry_entry);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100172 lookup->hub = hub;
173
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100174 osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
175 osmo_strlcpy(lookup->apn_ni_str, apn_ni_str,
176 sizeof(lookup->apn_ni_str));
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100177
178 make_addr_str(lookup);
179
180 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) {
Neels Hofmeyrd9b1d492015-11-18 18:11:09 +0100184 LOGP(DGTPHUB, LOGL_DEBUG,
185 "Query already pending for %s\n",
186 lookup->apn_oi_str);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100187 /* A query already pending. Just tip our hat. */
188 return NULL;
189 }
190 }
191
192 struct gtphub_resolved_ggsn *resolved;
193 llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
194 if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
195 sizeof(lookup->apn_oi_str)) == 0) {
Neels Hofmeyrd9b1d492015-11-18 18:11:09 +0100196 LOGP(DGTPHUB, LOGL_DEBUG,
197 "GGSN resolved from cache: %s -> %s\n",
198 lookup->apn_oi_str,
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +0100199 gtphub_port_str(resolved->peer));
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100200 return resolved->peer;
201 }
202 }
203
204 /* Kick off a resolution, but so far return nothing. The hope is that
205 * the peer will resend the request (a couple of times), and by then
206 * the GGSN will be resolved. */
Neels Hofmeyrd9b1d492015-11-18 18:11:09 +0100207 LOGP(DGTPHUB, LOGL_DEBUG,
208 "Sending out DNS query for %s..."
209 " (Returning failure, hoping for a retry once resolution"
210 " has concluded)\n",
211 lookup->apn_oi_str);
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100212
213 llist_add(&lookup->entry, &hub->ggsn_lookups);
214
215 lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
Neels Hofmeyre54cd152015-11-24 13:31:06 +0100216 expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100217
218 start_ares_query(lookup);
219
220 return NULL;
221}