blob: 81b9d7f67cb1c864ee890a8d864b32e55f9ef392 [file] [log] [blame]
Harald Welte288be162010-05-01 16:48:27 +02001/*
Harald Welte7f6da482013-03-19 11:00:13 +01002 * (C) 2010-2013 by Harald Welte <laforge@gnumonks.org>
Harald Welte288be162010-05-01 16:48:27 +02003 * (C) 2010 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01007 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
Harald Welte288be162010-05-01 16:48:27 +02009 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010014 * GNU Affero General Public License for more details.
Harald Welte288be162010-05-01 16:48:27 +020015 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010016 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte288be162010-05-01 16:48:27 +020018 *
19 */
20
Harald Welte288be162010-05-01 16:48:27 +020021#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
Jacob Erlbeck207f4a52014-11-11 14:01:48 +010024#include <time.h>
Harald Welte288be162010-05-01 16:48:27 +020025
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010026#include <osmocom/core/talloc.h>
27#include <osmocom/core/utils.h>
28#include <osmocom/core/rate_ctr.h>
Harald Welte288be162010-05-01 16:48:27 +020029
30#include <openbsc/debug.h>
31#include <openbsc/sgsn.h>
Harald Welteea34a4e2012-06-16 14:59:56 +080032#include <osmocom/gprs/gprs_ns.h>
Harald Welted193cb32010-05-17 22:58:03 +020033#include <openbsc/gprs_sgsn.h>
Harald Welte62ab20c2010-05-14 18:59:17 +020034#include <openbsc/vty.h>
Harald Weltec5d4a0c2010-07-02 22:47:59 +020035#include <openbsc/gsm_04_08_gprs.h>
Jacob Erlbeck80547992014-12-19 19:19:46 +010036#include <openbsc/gprs_gsup_client.h>
Harald Welte288be162010-05-01 16:48:27 +020037
Harald Welte4b037e42010-05-19 19:45:32 +020038#include <osmocom/vty/command.h>
39#include <osmocom/vty/vty.h>
Pablo Neira Ayuso6110a3f2011-03-28 19:35:00 +020040#include <osmocom/vty/misc.h>
Harald Welte288be162010-05-01 16:48:27 +020041
Jacob Erlbeck80547992014-12-19 19:19:46 +010042#include <osmocom/abis/ipa.h>
43
Harald Welted193cb32010-05-17 22:58:03 +020044#include <pdp.h>
45
Harald Welte288be162010-05-01 16:48:27 +020046static struct sgsn_config *g_cfg = NULL;
47
Jacob Erlbeck106f5472014-11-04 10:08:37 +010048const struct value_string sgsn_auth_pol_strs[] = {
49 { SGSN_AUTH_POLICY_OPEN, "accept-all" },
50 { SGSN_AUTH_POLICY_CLOSED, "closed" },
51 { SGSN_AUTH_POLICY_ACL_ONLY, "acl-only" },
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +010052 { SGSN_AUTH_POLICY_REMOTE, "remote" },
Jacob Erlbeck106f5472014-11-04 10:08:37 +010053 { 0, NULL }
54};
55
56
Harald Weltec5d4a0c2010-07-02 22:47:59 +020057#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */
58static char *gprs_apn2str(uint8_t *apn, unsigned int len)
59{
60 static char apnbuf[GSM48_MAX_APN_LEN+1];
Holger Hans Peter Freyther80e03652013-07-04 18:44:16 +020061 unsigned int i = 0;
Harald Weltec5d4a0c2010-07-02 22:47:59 +020062
63 if (!apn)
64 return "";
65
66 if (len > sizeof(apnbuf)-1)
67 len = sizeof(apnbuf)-1;
68
69 memcpy(apnbuf, apn, len);
70 apnbuf[len] = '\0';
71
72 /* replace the domain name step sizes with dots */
73 while (i < len) {
74 unsigned int step = apnbuf[i];
75 apnbuf[i] = '.';
76 i += step+1;
77 }
78
79 return apnbuf+1;
80}
81
Holger Hans Peter Freythera2730302014-03-23 18:08:26 +010082char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len)
Harald Weltec5d4a0c2010-07-02 22:47:59 +020083{
84 static char str[INET6_ADDRSTRLEN + 10];
85
86 if (!pdpa || len < 2)
87 return "none";
88
89 switch (pdpa[0] & 0x0f) {
90 case PDP_TYPE_ORG_IETF:
91 switch (pdpa[1]) {
92 case PDP_TYPE_N_IETF_IPv4:
93 if (len < 2 + 4)
94 break;
95 strcpy(str, "IPv4 ");
96 inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5);
97 return str;
98 case PDP_TYPE_N_IETF_IPv6:
99 if (len < 2 + 8)
100 break;
101 strcpy(str, "IPv6 ");
102 inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5);
103 return str;
104 default:
105 break;
106 }
107 break;
108 case PDP_TYPE_ORG_ETSI:
109 if (pdpa[1] == PDP_TYPE_N_ETSI_PPP)
110 return "PPP";
111 break;
112 default:
113 break;
114 }
115
116 return "invalid";
117}
118
Harald Welte288be162010-05-01 16:48:27 +0200119static struct cmd_node sgsn_node = {
120 SGSN_NODE,
Harald Welte570ce242012-08-17 13:16:10 +0200121 "%s(config-sgsn)# ",
Harald Welte288be162010-05-01 16:48:27 +0200122 1,
123};
124
125static int config_write_sgsn(struct vty *vty)
126{
Harald Welte77289c22010-05-18 14:32:29 +0200127 struct sgsn_ggsn_ctx *gctx;
Harald Welte7f6da482013-03-19 11:00:13 +0100128 struct imsi_acl_entry *acl;
Harald Welte288be162010-05-01 16:48:27 +0200129
130 vty_out(vty, "sgsn%s", VTY_NEWLINE);
131
Harald Weltee300d002010-06-02 12:41:34 +0200132 vty_out(vty, " gtp local-ip %s%s",
133 inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE);
134
Harald Welted193cb32010-05-17 22:58:03 +0200135 llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
Harald Welteff3bde82010-05-19 15:09:09 +0200136 vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200137 inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
Harald Welteff3bde82010-05-19 15:09:09 +0200138 vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200139 gctx->gtp_version, VTY_NEWLINE);
Harald Welte288be162010-05-01 16:48:27 +0200140 }
141
Harald Welte3dfb5492013-03-19 11:48:54 +0100142 vty_out(vty, " auth-policy %s%s",
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100143 get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
144 VTY_NEWLINE);
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100145 if (g_cfg->gsup_server_addr.sin_addr.s_addr)
146 vty_out(vty, " gsup remote-ip %s%s",
147 inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE);
148 if (g_cfg->gsup_server_port)
149 vty_out(vty, " gsup remote-port %d%s",
150 g_cfg->gsup_server_port, VTY_NEWLINE);
Harald Welte7f6da482013-03-19 11:00:13 +0100151 llist_for_each_entry(acl, &g_cfg->imsi_acl, list)
152 vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE);
153
Harald Welte288be162010-05-01 16:48:27 +0200154 return CMD_SUCCESS;
155}
156
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100157#define SGSN_STR "Configure the SGSN\n"
158#define GGSN_STR "Configure the GGSN information\n"
Harald Weltee300d002010-06-02 12:41:34 +0200159
160DEFUN(cfg_sgsn, cfg_sgsn_cmd,
161 "sgsn",
162 SGSN_STR)
Harald Welte288be162010-05-01 16:48:27 +0200163{
164 vty->node = SGSN_NODE;
165 return CMD_SUCCESS;
166}
167
Harald Weltee300d002010-06-02 12:41:34 +0200168DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd,
169 "gtp local-ip A.B.C.D",
170 "GTP Parameters\n"
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100171 "Set the IP address for the local GTP bind\n"
172 "IPv4 Address\n")
Harald Weltee300d002010-06-02 12:41:34 +0200173{
174 inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr);
175
176 return CMD_SUCCESS;
177}
178
Harald Welted193cb32010-05-17 22:58:03 +0200179DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd,
180 "ggsn <0-255> remote-ip A.B.C.D",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100181 GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n")
Harald Welted193cb32010-05-17 22:58:03 +0200182{
183 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200184 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welte288be162010-05-01 16:48:27 +0200185
Harald Welted193cb32010-05-17 22:58:03 +0200186 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte288be162010-05-01 16:48:27 +0200187
Harald Welted193cb32010-05-17 22:58:03 +0200188 return CMD_SUCCESS;
189}
190
191#if 0
192DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd,
193 "ggsn <0-255> remote-port <0-65535>",
194 "")
195{
196 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200197 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200198 uint16_t port = atoi(argv[1]);
199
200}
201#endif
202
203DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd,
204 "ggsn <0-255> gtp-version (0|1)",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100205 GGSN_STR "GGSN Number\n" "GTP Version\n"
206 "Version 0\n" "Version 1\n")
Harald Welted193cb32010-05-17 22:58:03 +0200207{
208 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200209 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200210
211 if (atoi(argv[1]))
212 ggc->gtp_version = 1;
213 else
214 ggc->gtp_version = 0;
215
216 return CMD_SUCCESS;
217}
218
219#if 0
220DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd,
221 "apn APNAME ggsn <0-255>",
222 "")
223{
224 struct apn_ctx **
225}
226#endif
227
228const struct value_string gprs_mm_st_strs[] = {
229 { GMM_DEREGISTERED, "DEREGISTERED" },
230 { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" },
231 { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" },
Harald Weltebffeff82010-06-09 15:50:45 +0200232 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Welted193cb32010-05-17 22:58:03 +0200233 { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" },
234 { 0, NULL }
235};
236
237static void vty_dump_pdp(struct vty *vty, const char *pfx,
238 struct sgsn_pdp_ctx *pdp)
239{
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200240 const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
Harald Welted193cb32010-05-17 22:58:03 +0200241 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200242 pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Weltec5d4a0c2010-07-02 22:47:59 +0200243 vty_out(vty, "%s APN: %s%s", pfx,
244 gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
245 VTY_NEWLINE);
246 vty_out(vty, "%s PDP Address: %s%s", pfx,
247 gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
248 VTY_NEWLINE);
Harald Welteefbdee92010-06-10 00:20:12 +0200249 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Welted193cb32010-05-17 22:58:03 +0200250}
251
252static void vty_dump_mmctx(struct vty *vty, const char *pfx,
253 struct sgsn_mm_ctx *mm, int pdp)
254{
255 vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
256 pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
257 vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn,
258 mm->tlli, VTY_NEWLINE);
259 vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, "
260 "Cell ID: %u%s", pfx,
261 get_value_string(gprs_mm_st_strs, mm->mm_state),
262 mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
263 mm->cell_id, VTY_NEWLINE);
264
Harald Welte8acd88f2010-05-18 10:57:45 +0200265 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
266
Harald Welted193cb32010-05-17 22:58:03 +0200267 if (pdp) {
268 struct sgsn_pdp_ctx *pdp;
269
270 llist_for_each_entry(pdp, &mm->pdp_list, list)
271 vty_dump_pdp(vty, " ", pdp);
272 }
273}
274
275DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
276 SHOW_STR "Display information about the SGSN")
277{
Jacob Erlbeck80547992014-12-19 19:19:46 +0100278 if (sgsn->gsup_client) {
279 struct ipa_client_conn *link = sgsn->gsup_client->link;
280 vty_out(vty,
281 " Remote authorization: %sconnected to %s:%d via GSUP%s",
282 sgsn->gsup_client->is_connected ? "" : "not ",
283 link->addr, link->port,
284 VTY_NEWLINE);
285 }
Harald Welted193cb32010-05-17 22:58:03 +0200286 /* FIXME: statistics */
287 return CMD_SUCCESS;
288}
289
290#define MMCTX_STR "MM Context\n"
291#define INCLUDE_PDP_STR "Include PDP Context Information\n"
292
293#if 0
294DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd,
295 "show mm-context tlli HEX [pdp]",
296 SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR)
297{
298 uint32_t tlli;
299 struct sgsn_mm_ctx *mm;
300
301 tlli = strtoul(argv[0], NULL, 16);
302 mm = sgsn_mm_ctx_by_tlli(tlli);
303 if (!mm) {
304 vty_out(vty, "No MM context for TLLI %08x%s",
305 tlli, VTY_NEWLINE);
306 return CMD_WARNING;
307 }
308 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
309 return CMD_SUCCESS;
310}
311#endif
312
313DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd,
314 "show mm-context imsi IMSI [pdp]",
315 SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n"
316 INCLUDE_PDP_STR)
317{
318 struct sgsn_mm_ctx *mm;
319
320 mm = sgsn_mm_ctx_by_imsi(argv[0]);
321 if (!mm) {
322 vty_out(vty, "No MM context for IMSI %s%s",
323 argv[0], VTY_NEWLINE);
324 return CMD_WARNING;
325 }
326 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
327 return CMD_SUCCESS;
328}
329
330DEFUN(swow_mmctx_all, show_mmctx_all_cmd,
331 "show mm-context all [pdp]",
332 SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR)
333{
334 struct sgsn_mm_ctx *mm;
335
336 llist_for_each_entry(mm, &sgsn_mm_ctxts, list)
337 vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0);
338
339 return CMD_SUCCESS;
340}
341
Harald Welted193cb32010-05-17 22:58:03 +0200342DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
343 "show pdp-context all",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100344 SHOW_STR "Display information on PDP Context\n" "Show everything\n")
Harald Welted193cb32010-05-17 22:58:03 +0200345{
346 struct sgsn_pdp_ctx *pdp;
347
348 llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list)
349 vty_dump_pdp(vty, "", pdp);
350
351 return CMD_SUCCESS;
352}
Harald Welte288be162010-05-01 16:48:27 +0200353
Harald Welte7f6da482013-03-19 11:00:13 +0100354
355DEFUN(imsi_acl, cfg_imsi_acl_cmd,
356 "imsi-acl (add|del) IMSI",
357 "Access Control List of foreign IMSIs\n"
358 "Add IMSI to ACL\n"
359 "Remove IMSI from ACL\n"
360 "IMSI of subscriber\n")
361{
362 const char *op = argv[0];
363 const char *imsi = argv[1];
364 int rc;
365
366 if (!strcmp(op, "add"))
Jacob Erlbeck3b5d4072014-10-24 15:11:03 +0200367 rc = sgsn_acl_add(imsi, g_cfg);
Harald Welte7f6da482013-03-19 11:00:13 +0100368 else
Jacob Erlbeck3b5d4072014-10-24 15:11:03 +0200369 rc = sgsn_acl_del(imsi, g_cfg);
Harald Welte7f6da482013-03-19 11:00:13 +0100370
371 if (rc < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100372 vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
373
Harald Welte7f6da482013-03-19 11:00:13 +0100374 return CMD_WARNING;
375 }
376
377 return CMD_SUCCESS;
378}
379
Harald Welte3dfb5492013-03-19 11:48:54 +0100380DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100381 "auth-policy (accept-all|closed|acl-only|remote)",
Harald Welte3dfb5492013-03-19 11:48:54 +0100382 "Autorization Policy of SGSN\n"
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100383 "Accept all IMSIs (DANGEROUS)\n"
384 "Accept only home network subscribers or those in the ACL\n"
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100385 "Accept only subscribers in the ACL\n"
386 "Use remote subscription data only (HLR)\n")
Harald Welte3dfb5492013-03-19 11:48:54 +0100387{
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100388 int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100389 OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100390 g_cfg->auth_policy = val;
Jacob Erlbeck9d4f46c2014-12-17 13:20:08 +0100391 g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck771573c2014-12-19 18:08:48 +0100392 g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
Harald Welte3dfb5492013-03-19 11:48:54 +0100393
394 return CMD_SUCCESS;
395}
396
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100397/* Subscriber */
398#include <openbsc/gsm_subscriber.h>
399
400static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending)
401{
402 char expire_time[200];
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100403 struct gsm_auth_tuple *at;
404 int at_idx;
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100405 struct sgsn_subscriber_pdp_data *pdp;
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100406
407 vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
408 subscr->authorized, VTY_NEWLINE);
409 if (strlen(subscr->name))
410 vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
411 if (strlen(subscr->extension))
412 vty_out(vty, " Extension: %s%s", subscr->extension,
413 VTY_NEWLINE);
414 vty_out(vty, " LAC: %d/0x%x%s",
415 subscr->lac, subscr->lac, VTY_NEWLINE);
416 vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
417 if (subscr->tmsi != GSM_RESERVED_TMSI)
418 vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
419 VTY_NEWLINE);
420
421 if (strlen(subscr->equipment.imei) > 0)
422 vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
423
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100424 for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets);
425 at_idx++) {
426 at = &subscr->sgsn_data->auth_triplets[at_idx];
427 if (at->key_seq == GSM_KEY_SEQ_INVAL)
428 continue;
429
430 vty_out(vty, " A3A8 tuple (used %d times): ",
431 at->use_count);
432 vty_out(vty, " seq # : %d, ",
433 at->key_seq);
434 vty_out(vty, " RAND : %s, ",
435 osmo_hexdump(at->rand, sizeof(at->rand)));
436 vty_out(vty, " SRES : %s, ",
437 osmo_hexdump(at->sres, sizeof(at->sres)));
438 vty_out(vty, " Kc : %s%s",
439 osmo_hexdump(at->kc, sizeof(at->kc)),
440 VTY_NEWLINE);
441 }
442
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100443 llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
444 vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'%s",
445 pdp->context_id, pdp->pdp_type, pdp->apn_str,
446 VTY_NEWLINE);
447 }
448
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100449 /* print the expiration time of a subscriber */
450 if (subscr->expire_lu) {
451 strftime(expire_time, sizeof(expire_time),
452 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
453 expire_time[sizeof(expire_time) - 1] = '\0';
454 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
455 }
456
457 if (subscr->flags)
Jacob Erlbeck65fa3f72015-01-06 16:32:41 +0100458 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100459 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
460 "FIRST_CONTACT " : "",
461 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
462 "CANCELLED " : "",
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100463 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
464 "UPDATE_LOCATION_PENDING " : "",
465 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
466 "AUTH_INFO_PENDING " : "",
Jacob Erlbeck65fa3f72015-01-06 16:32:41 +0100467 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
468 "ENABLE_PURGE " : "",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100469 VTY_NEWLINE);
470
471 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
472}
473
474DEFUN(show_subscr_cache,
475 show_subscr_cache_cmd,
476 "show subscriber cache",
477 SHOW_STR "Show information about subscribers\n"
478 "Display contents of subscriber cache\n")
479{
480 struct gsm_subscriber *subscr;
481
482 llist_for_each_entry(subscr, &active_subscribers, entry) {
483 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
484 subscr_dump_full_vty(vty, subscr, 0);
485 }
486
487 return CMD_SUCCESS;
488}
489
490#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
491#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
492 "Use the IMSI to select the subscriber\n" \
493 "The IMSI\n"
494
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100495#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
496
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100497DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
498 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
499 UPDATE_SUBSCR_HELP
500 UPDATE_SUBSCR_INSERT_HELP
501 "Update authentication triplet\n"
502 "Triplet index\n"
503 "Set SRES value\nSRES value (4 byte) in hex\n"
504 "Set RAND value\nRAND value (16 byte) in hex\n"
505 "Set Kc value\nKc value (8 byte) in hex\n")
506{
507 const char *imsi = argv[0];
508 const int cksn = atoi(argv[1]) - 1;
509 const char *sres_str = argv[2];
510 const char *rand_str = argv[3];
511 const char *kc_str = argv[4];
512 struct gsm_auth_tuple at = {0,};
513
514 struct gsm_subscriber *subscr;
515
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100516 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100517 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100518 vty_out(vty, "%% unable get subscriber record for %s%s",
519 imsi, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100520 return CMD_WARNING;
521 }
522
523 OSMO_ASSERT(subscr->sgsn_data);
524
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100525 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100526 vty_out(vty, "%% invalid SRES value '%s'%s",
527 sres_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100528 goto failed;
529 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100530 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100531 vty_out(vty, "%% invalid RAND value '%s'%s",
532 rand_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100533 goto failed;
534 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100535 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100536 vty_out(vty, "%% invalid Kc value '%s'%s",
537 kc_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100538 goto failed;
539 }
540 at.key_seq = cksn;
541
542 subscr->sgsn_data->auth_triplets[cksn] = at;
543 subscr->sgsn_data->auth_triplets_updated = 1;
544
545 subscr_put(subscr);
546
547 return CMD_SUCCESS;
548
549failed:
550 subscr_put(subscr);
551 return CMD_SUCCESS;
552}
553
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100554DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100555 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100556 UPDATE_SUBSCR_HELP
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100557 "Cancel (remove) subscriber record\n"
558 "The MS moved to another SGSN\n"
559 "The subscription is no longer valid\n")
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100560{
561 const char *imsi = argv[0];
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100562 const char *cancel_type = argv[1];
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100563
564 struct gsm_subscriber *subscr;
565
566 subscr = gprs_subscr_get_by_imsi(imsi);
567 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100568 vty_out(vty, "%% no subscriber record for %s%s",
569 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100570 return CMD_WARNING;
571 }
572
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100573 if (strcmp(cancel_type, "update-procedure") == 0)
574 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
575 else
576 subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
577
Jacob Erlbeck37139e52015-01-23 13:52:55 +0100578 gprs_subscr_cancel(subscr);
579 subscr_put(subscr);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100580
581 return CMD_SUCCESS;
582}
583
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100584DEFUN(update_subscr_create, update_subscr_create_cmd,
585 UPDATE_SUBSCR_STR "create",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100586 UPDATE_SUBSCR_HELP
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100587 "Create a subscriber entry\n")
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100588{
589 const char *imsi = argv[0];
590
591 struct gsm_subscriber *subscr;
592
593 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100594 if (subscr) {
595 vty_out(vty, "%% subscriber record already exists for %s%s",
596 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100597 return CMD_WARNING;
598 }
599
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100600 subscr = gprs_subscr_get_or_create(imsi);
601 subscr->keep_in_ram = 1;
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100602 subscr_put(subscr);
603
604 return CMD_SUCCESS;
605}
606
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100607DEFUN(update_subscr_destroy, update_subscr_destroy_cmd,
608 UPDATE_SUBSCR_STR "destroy",
609 UPDATE_SUBSCR_HELP
610 "Destroy a subscriber entry\n")
611{
612 const char *imsi = argv[0];
613
614 struct gsm_subscriber *subscr;
615
616 subscr = gprs_subscr_get_by_imsi(imsi);
617 if (!subscr) {
618 vty_out(vty, "%% subscriber record does not exist for %s%s",
619 imsi, VTY_NEWLINE);
620 return CMD_WARNING;
621 }
622
623 subscr->keep_in_ram = 0;
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100624 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100625 gprs_subscr_cancel(subscr);
626 if (subscr->use_count > 1)
627 vty_out(vty, "%% subscriber is still in use%s",
628 VTY_NEWLINE);
629 subscr_put(subscr);
630
631 return CMD_SUCCESS;
632}
633
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100634#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
635 "unknown-subscriber|roaming-not-allowed"
636
637#define UL_ERR_HELP \
638 "Force error code SystemFailure\n" \
639 "Force error code DataMissing\n" \
640 "Force error code UnexpectedDataValue\n" \
641 "Force error code UnknownSubscriber\n" \
642 "Force error code RoamingNotAllowed\n"
643
644DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
645 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
646 UPDATE_SUBSCR_HELP
647 "Complete the update location procedure\n"
648 "The update location request succeeded\n"
649 UL_ERR_HELP)
650{
651 const char *imsi = argv[0];
652 const char *ret_code_str = argv[1];
653
654 struct gsm_subscriber *subscr;
655
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100656 const struct value_string cause_mapping[] = {
657 { GMM_CAUSE_NET_FAIL, "system-failure" },
658 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
659 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
660 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
661 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
662 { 0, NULL }
663 };
664
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100665 subscr = gprs_subscr_get_by_imsi(imsi);
666 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100667 vty_out(vty, "%% unable to get subscriber record for %s%s",
668 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100669 return CMD_WARNING;
670 }
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100671
672 if (strcmp(ret_code_str, "ok") == 0) {
673 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100674 subscr->authorized = 1;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100675 } else {
676 subscr->sgsn_data->error_cause =
677 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100678 subscr->authorized = 0;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100679 }
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100680
681 gprs_subscr_update(subscr);
682
683 subscr_put(subscr);
684
685 return CMD_SUCCESS;
686}
687
688DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
689 UPDATE_SUBSCR_STR "update-auth-info",
690 UPDATE_SUBSCR_HELP
691 "Complete the send authentication info procedure\n")
692{
693 const char *imsi = argv[0];
694
695 struct gsm_subscriber *subscr;
696
697 subscr = gprs_subscr_get_by_imsi(imsi);
698 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100699 vty_out(vty, "%% unable to get subscriber record for %s%s",
700 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100701 return CMD_WARNING;
702 }
703
704 gprs_subscr_update_auth_info(subscr);
705
706 subscr_put(subscr);
707
708 return CMD_SUCCESS;
709}
710
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100711DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
712 "gsup remote-ip A.B.C.D",
713 "GSUP Parameters\n"
714 "Set the IP address of the remote GSUP server\n"
715 "IPv4 Address\n")
716{
717 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
718
719 return CMD_SUCCESS;
720}
721
722DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
723 "gsup remote-port <0-65535>",
724 "GSUP Parameters\n"
725 "Set the TCP port of the remote GSUP server\n"
726 "Remote TCP port\n")
727{
728 g_cfg->gsup_server_port = atoi(argv[0]);
729
730 return CMD_SUCCESS;
731}
732
Harald Welte288be162010-05-01 16:48:27 +0200733int sgsn_vty_init(void)
734{
Harald Welted193cb32010-05-17 22:58:03 +0200735 install_element_ve(&show_sgsn_cmd);
736 //install_element_ve(&show_mmctx_tlli_cmd);
737 install_element_ve(&show_mmctx_imsi_cmd);
738 install_element_ve(&show_mmctx_all_cmd);
739 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100740 install_element_ve(&show_subscr_cache_cmd);
741
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100742 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100743 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100744 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100745 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100746 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
747 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200748
749 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
750 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeck36722e12013-10-29 09:30:30 +0100751 vty_install_default(SGSN_NODE);
Harald Weltee300d002010-06-02 12:41:34 +0200752 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Welted193cb32010-05-17 22:58:03 +0200753 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
754 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
755 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Welte7f6da482013-03-19 11:00:13 +0100756 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte3dfb5492013-03-19 11:48:54 +0100757 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100758 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
759 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200760
761 return 0;
762}
763
764int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
765{
766 int rc;
767
768 g_cfg = cfg;
Harald Welte7f6da482013-03-19 11:00:13 +0100769
Harald Weltedcccb182010-05-16 20:52:23 +0200770 rc = vty_read_config_file(config_file, NULL);
Harald Welte288be162010-05-01 16:48:27 +0200771 if (rc < 0) {
772 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
773 return rc;
774 }
775
776 return 0;
777}