blob: 1f395eeb8c213e17015e777619a5606a9124ca3e [file] [log] [blame]
Neels Hofmeyr3df5d532016-12-08 21:17:01 +01001/* Generic Subscriber Update Protocol client */
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +01002
Neels Hofmeyr3df5d532016-12-08 21:17:01 +01003/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +01004 * All Rights Reserved
5 *
6 * Author: Jacob Erlbeck
Neels Hofmeyr3df5d532016-12-08 21:17:01 +01007 * Author: Neels Hofmeyr
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +01008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
Neels Hofmeyr90843962017-09-04 15:04:35 +020024#include <osmocom/msc/gsup_client.h>
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +010025
26#include <osmocom/abis/ipa.h>
27#include <osmocom/gsm/protocol/ipaccess.h>
28#include <osmocom/core/msgb.h>
Harald Welte4f8e34b2016-05-06 23:27:38 +020029#include <osmocom/core/logging.h>
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +010030
Neels Hofmeyr90843962017-09-04 15:04:35 +020031#include <osmocom/msc/debug.h>
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +010032
33#include <errno.h>
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010034#include <string.h>
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +010035
Neels Hofmeyr814fef02016-12-08 21:19:57 +010036static void start_test_procedure(struct gsup_client *gsupc);
Jacob Erlbeck03b46302014-12-19 19:18:54 +010037
Neels Hofmeyr814fef02016-12-08 21:19:57 +010038static void gsup_client_send_ping(struct gsup_client *gsupc)
Jacob Erlbeck03b46302014-12-19 19:18:54 +010039{
Neels Hofmeyr814fef02016-12-08 21:19:57 +010040 struct msgb *msg = gsup_client_msgb_alloc();
Jacob Erlbeck03b46302014-12-19 19:18:54 +010041
42 msg->l2h = msgb_put(msg, 1);
43 msg->l2h[0] = IPAC_MSGT_PING;
44 ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
45 ipa_client_conn_send(gsupc->link, msg);
46}
47
Neels Hofmeyr814fef02016-12-08 21:19:57 +010048static int gsup_client_connect(struct gsup_client *gsupc)
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010049{
50 int rc;
51
52 if (gsupc->is_connected)
53 return 0;
54
55 if (osmo_timer_pending(&gsupc->connect_timer)) {
Neels Hofmeyref022782016-12-08 21:28:29 +010056 LOGP(DLGSUP, LOGL_DEBUG,
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010057 "GSUP connect: connect timer already running\n");
58 osmo_timer_del(&gsupc->connect_timer);
59 }
60
Jacob Erlbeck03b46302014-12-19 19:18:54 +010061 if (osmo_timer_pending(&gsupc->ping_timer)) {
Neels Hofmeyref022782016-12-08 21:28:29 +010062 LOGP(DLGSUP, LOGL_DEBUG,
Jacob Erlbeck03b46302014-12-19 19:18:54 +010063 "GSUP connect: ping timer already running\n");
64 osmo_timer_del(&gsupc->ping_timer);
65 }
66
Jacob Erlbeck4188c302014-12-19 18:50:05 +010067 if (ipa_client_conn_clear_queue(gsupc->link) > 0)
Neels Hofmeyref022782016-12-08 21:28:29 +010068 LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n");
Jacob Erlbeck4188c302014-12-19 18:50:05 +010069
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010070 rc = ipa_client_conn_open(gsupc->link);
71
Jacob Erlbeck69e16b92014-12-19 19:00:56 +010072 if (rc >= 0) {
Harald Welte2483f1b2016-06-19 18:06:02 +020073 LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
Jacob Erlbeck69e16b92014-12-19 19:00:56 +010074 gsupc->link->addr, gsupc->link->port);
75 return 0;
76 }
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010077
Harald Welte2483f1b2016-06-19 18:06:02 +020078 LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n",
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010079 gsupc->link->addr, gsupc->link->port, strerror(-rc));
80
81 if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
82 rc == -EINVAL)
83 return rc;
84
Neels Hofmeyr814fef02016-12-08 21:19:57 +010085 osmo_timer_schedule(&gsupc->connect_timer,
86 GSUP_CLIENT_RECONNECT_INTERVAL, 0);
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010087
Neels Hofmeyref022782016-12-08 21:28:29 +010088 LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n",
Jacob Erlbeck37184902015-01-19 10:56:15 +010089 gsupc->link->addr, gsupc->link->port);
90
91 return 0;
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010092}
93
94static void connect_timer_cb(void *gsupc_)
95{
Neels Hofmeyr814fef02016-12-08 21:19:57 +010096 struct gsup_client *gsupc = gsupc_;
Jacob Erlbeck849d0a82014-12-18 15:00:29 +010097
98 if (gsupc->is_connected)
99 return;
100
101 gsup_client_connect(gsupc);
102}
103
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100104static void client_send(struct gsup_client *gsupc, int proto_ext,
105 struct msgb *msg_tx)
Neels Hofmeyr06cfe002015-10-01 15:23:51 +0200106{
107 ipa_prepend_header_ext(msg_tx, proto_ext);
108 ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
109 ipa_client_conn_send(gsupc->link, msg_tx);
110 /* msg_tx is now queued and will be freed. */
111}
112
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100113static void gsup_client_oap_register(struct gsup_client *gsupc)
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200114{
115 struct msgb *msg_tx;
116 int rc;
Neels Hofmeyr49012f12016-12-08 21:30:34 +0100117 rc = oap_client_register(&gsupc->oap_state, &msg_tx);
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200118
119 if ((rc < 0) || (!msg_tx)) {
Neels Hofmeyref022782016-12-08 21:28:29 +0100120 LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n");
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200121 return;
122 }
123
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100124 client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200125}
126
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100127static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
128{
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100129 struct gsup_client *gsupc = link->data;
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100130
Neels Hofmeyref022782016-12-08 21:28:29 +0100131 LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n",
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100132 link->addr, link->port, up ? "UP" : "DOWN");
133
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100134 gsupc->is_connected = up;
135
136 if (up) {
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100137 start_test_procedure(gsupc);
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100138
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200139 if (gsupc->oap_state.state == OAP_INITIALIZED)
140 gsup_client_oap_register(gsupc);
141
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100142 osmo_timer_del(&gsupc->connect_timer);
143 } else {
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100144 osmo_timer_del(&gsupc->ping_timer);
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100145
146 osmo_timer_schedule(&gsupc->connect_timer,
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100147 GSUP_CLIENT_RECONNECT_INTERVAL, 0);
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100148 }
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100149}
150
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100151static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx)
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200152{
153 int rc;
154 struct msgb *msg_tx;
155
Neels Hofmeyr2fa74fa2016-12-08 23:12:17 +0100156 /* If the oap_state is disabled, this will reject the messages. */
Neels Hofmeyr49012f12016-12-08 21:30:34 +0100157 rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx);
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200158 msgb_free(msg_rx);
159 if (rc < 0)
160 return rc;
161
162 if (msg_tx)
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100163 client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200164
165 return 0;
166}
167
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100168static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
169{
170 struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
171 struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100172 struct gsup_client *gsupc = (struct gsup_client *)link->data;
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100173 int rc;
Neels Hofmeyrf2ba8132017-03-04 03:15:53 +0100174 struct ipaccess_unit ipa_dev = {
175 /* see gsup_client_create() on const vs non-const */
176 .unit_name = (char*)gsupc->unit_name,
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100177 };
178
Neels Hofmeyrf2ba8132017-03-04 03:15:53 +0100179 OSMO_ASSERT(ipa_dev.unit_name);
180
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100181 msg->l2h = &hh->data[0];
182
183 rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
184
185 if (rc < 0) {
Neels Hofmeyref022782016-12-08 21:28:29 +0100186 LOGP(DLGSUP, LOGL_NOTICE,
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100187 "GSUP received an invalid IPA/CCM message from %s:%d\n",
188 link->addr, link->port);
189 /* Link has been closed */
190 gsupc->is_connected = 0;
191 msgb_free(msg);
192 return -1;
193 }
194
195 if (rc == 1) {
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100196 uint8_t msg_type = *(msg->l2h);
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100197 /* CCM message */
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100198 if (msg_type == IPAC_MSGT_PONG) {
Neels Hofmeyref022782016-12-08 21:28:29 +0100199 LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n");
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100200 gsupc->got_ipa_pong = 1;
201 }
Jacob Erlbecke154d8b2014-12-19 19:15:55 +0100202
203 msgb_free(msg);
204 return 0;
205 }
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100206
207 if (hh->proto != IPAC_PROTO_OSMO)
208 goto invalid;
209
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200210 if (!he || msgb_l2len(msg) < sizeof(*he))
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100211 goto invalid;
212
213 msg->l2h = &he->data[0];
214
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200215 if (he->proto == IPAC_PROTO_EXT_GSUP) {
216 OSMO_ASSERT(gsupc->read_cb != NULL);
217 gsupc->read_cb(gsupc, msg);
218 /* expecting read_cb() to free msg */
219 } else if (he->proto == IPAC_PROTO_EXT_OAP) {
220 return gsup_client_oap_handle(gsupc, msg);
221 /* gsup_client_oap_handle frees msg */
222 } else
223 goto invalid;
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100224
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100225 return 0;
226
227invalid:
Neels Hofmeyref022782016-12-08 21:28:29 +0100228 LOGP(DLGSUP, LOGL_NOTICE,
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100229 "GSUP received an invalid IPA message from %s:%d, size = %d\n",
230 link->addr, link->port, msgb_length(msg));
231
232 msgb_free(msg);
233 return -1;
234}
235
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100236static void ping_timer_cb(void *gsupc_)
237{
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100238 struct gsup_client *gsupc = gsupc_;
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100239
Neels Hofmeyref022782016-12-08 21:28:29 +0100240 LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n",
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100241 gsupc->is_connected ? "connected" : "not connected",
242 gsupc->got_ipa_pong ? "got" : "didn't get");
243
244 if (gsupc->got_ipa_pong) {
245 start_test_procedure(gsupc);
246 return;
247 }
248
Neels Hofmeyref022782016-12-08 21:28:29 +0100249 LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n");
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100250 ipa_client_conn_close(gsupc->link);
251 gsupc->is_connected = 0;
252
253 gsup_client_connect(gsupc);
254}
255
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100256static void start_test_procedure(struct gsup_client *gsupc)
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100257{
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200258 osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc);
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100259
260 gsupc->got_ipa_pong = 0;
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100261 osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0);
Neels Hofmeyref022782016-12-08 21:28:29 +0100262 LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n");
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100263 gsup_client_send_ping(gsupc);
264}
265
Neels Hofmeyrc01e9092018-03-22 15:56:49 +0100266struct gsup_client *gsup_client_create(void *talloc_ctx,
267 const char *unit_name,
Neels Hofmeyrf2ba8132017-03-04 03:15:53 +0100268 const char *ip_addr,
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100269 unsigned int tcp_port,
270 gsup_client_read_cb_t read_cb,
Neels Hofmeyr37f92522016-12-08 23:58:31 +0100271 struct oap_client_config *oapc_config)
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100272{
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100273 struct gsup_client *gsupc;
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100274 int rc;
275
Neels Hofmeyrc01e9092018-03-22 15:56:49 +0100276 gsupc = talloc_zero(talloc_ctx, struct gsup_client);
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100277 OSMO_ASSERT(gsupc);
278
Neels Hofmeyrf2ba8132017-03-04 03:15:53 +0100279 /* struct ipaccess_unit has a non-const unit_name, so let's copy to be
280 * able to have a non-const unit_name here as well. To not taint the
281 * public gsup_client API, let's store it in a const char* anyway. */
282 gsupc->unit_name = talloc_strdup(gsupc, unit_name);
283 OSMO_ASSERT(gsupc->unit_name);
284
Neels Hofmeyr37f92522016-12-08 23:58:31 +0100285 /* a NULL oapc_config will mark oap_state disabled. */
286 rc = oap_client_init(oapc_config, &gsupc->oap_state);
Neels Hofmeyr9c534fd2015-10-12 11:57:37 +0200287 if (rc != 0)
288 goto failed;
289
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100290 gsupc->link = ipa_client_conn_create(gsupc,
291 /* no e1inp */ NULL,
292 0,
293 ip_addr, tcp_port,
294 gsup_client_updown_cb,
295 gsup_client_read_cb,
296 /* default write_cb */ NULL,
297 gsupc);
298 if (!gsupc->link)
299 goto failed;
300
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200301 osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100302
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100303 rc = gsup_client_connect(gsupc);
304
Jacob Erlbeck3ee67ff2015-01-26 09:22:39 +0100305 if (rc < 0)
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100306 goto failed;
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100307
308 gsupc->read_cb = read_cb;
309
310 return gsupc;
311
312failed:
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100313 gsup_client_destroy(gsupc);
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100314 return NULL;
315}
316
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100317void gsup_client_destroy(struct gsup_client *gsupc)
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100318{
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100319 osmo_timer_del(&gsupc->connect_timer);
Jacob Erlbeck03b46302014-12-19 19:18:54 +0100320 osmo_timer_del(&gsupc->ping_timer);
Jacob Erlbeck849d0a82014-12-18 15:00:29 +0100321
322 if (gsupc->link) {
323 ipa_client_conn_close(gsupc->link);
324 ipa_client_conn_destroy(gsupc->link);
325 gsupc->link = NULL;
326 }
327 talloc_free(gsupc);
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100328}
329
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100330int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg)
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100331{
Neels Hofmeyrbc5dd492017-07-27 14:06:21 +0200332 if (!gsupc || !gsupc->is_connected) {
Harald Welte703f2ec2018-01-25 00:36:42 +0100333 LOGP(DLGSUP, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg));
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100334 msgb_free(msg);
335 return -ENOTCONN;
336 }
337
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100338 client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg);
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100339
340 return 0;
341}
342
Neels Hofmeyr814fef02016-12-08 21:19:57 +0100343struct msgb *gsup_client_msgb_alloc(void)
Jacob Erlbeckbb23dc12014-12-18 12:28:21 +0100344{
345 return msgb_alloc_headroom(4000, 64, __func__);
346}