blob: a22f70c38fa4b3ccbddc9964b4523815285f04aa [file] [log] [blame]
Harald Welte55fe0552010-05-01 16:48:27 +02001/*
Harald Weltea0879c12013-03-19 11:00:13 +01002 * (C) 2010-2013 by Harald Welte <laforge@gnumonks.org>
Harald Welte55fe0552010-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 Welte0e3e88e2011-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 Welte55fe0552010-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 Welte0e3e88e2011-01-01 15:25:50 +010014 * GNU Affero General Public License for more details.
Harald Welte55fe0552010-05-01 16:48:27 +020015 *
Harald Welte0e3e88e2011-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 Welte55fe0552010-05-01 16:48:27 +020018 *
19 */
20
Harald Welte55fe0552010-05-01 16:48:27 +020021#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
Jacob Erlbeckc16c3502014-11-11 14:01:48 +010024#include <time.h>
Harald Welte55fe0552010-05-01 16:48:27 +020025
Pablo Neira Ayusodd5fff42011-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 Welte55fe0552010-05-01 16:48:27 +020029
30#include <openbsc/debug.h>
31#include <openbsc/sgsn.h>
Harald Weltecfb6b282012-06-16 14:59:56 +080032#include <osmocom/gprs/gprs_ns.h>
Harald Weltec1f6bfe2010-05-17 22:58:03 +020033#include <openbsc/gprs_sgsn.h>
Harald Welte58ed1cb2010-05-14 18:59:17 +020034#include <openbsc/vty.h>
Harald Welte493ba622010-07-02 22:47:59 +020035#include <openbsc/gsm_04_08_gprs.h>
Jacob Erlbeck42a1aaf2014-12-19 19:19:46 +010036#include <openbsc/gprs_gsup_client.h>
Harald Welte55fe0552010-05-01 16:48:27 +020037
Harald Weltebd9591f2010-05-19 19:45:32 +020038#include <osmocom/vty/command.h>
39#include <osmocom/vty/vty.h>
Pablo Neira Ayuso3d31c3a2011-03-28 19:35:00 +020040#include <osmocom/vty/misc.h>
Harald Welte55fe0552010-05-01 16:48:27 +020041
Jacob Erlbeck42a1aaf2014-12-19 19:19:46 +010042#include <osmocom/abis/ipa.h>
43
Harald Weltec1f6bfe2010-05-17 22:58:03 +020044#include <pdp.h>
45
Harald Welte55fe0552010-05-01 16:48:27 +020046static struct sgsn_config *g_cfg = NULL;
47
Jacob Erlbeckd7b77732014-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 Erlbeckd04f7cc2014-11-12 10:18:09 +010052 { SGSN_AUTH_POLICY_REMOTE, "remote" },
Jacob Erlbeckd7b77732014-11-04 10:08:37 +010053 { 0, NULL }
54};
55
56
Harald Welte493ba622010-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 Freyther41514d92013-07-04 18:44:16 +020061 unsigned int i = 0;
Harald Welte493ba622010-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 Freythere8e5ef22014-03-23 18:08:26 +010082char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len)
Harald Welte493ba622010-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 Welte55fe0552010-05-01 16:48:27 +0200119static struct cmd_node sgsn_node = {
120 SGSN_NODE,
Harald Welte45362bb2012-08-17 13:16:10 +0200121 "%s(config-sgsn)# ",
Harald Welte55fe0552010-05-01 16:48:27 +0200122 1,
123};
124
125static int config_write_sgsn(struct vty *vty)
126{
Harald Welteeb471c92010-05-18 14:32:29 +0200127 struct sgsn_ggsn_ctx *gctx;
Harald Weltea0879c12013-03-19 11:00:13 +0100128 struct imsi_acl_entry *acl;
Harald Welte55fe0552010-05-01 16:48:27 +0200129
130 vty_out(vty, "sgsn%s", VTY_NEWLINE);
131
Harald Weltee0aea392010-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 Weltec1f6bfe2010-05-17 22:58:03 +0200135 llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
Harald Welte31f0a232010-05-19 15:09:09 +0200136 vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200137 inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
Harald Welte31f0a232010-05-19 15:09:09 +0200138 vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200139 gctx->gtp_version, VTY_NEWLINE);
Harald Welte55fe0552010-05-01 16:48:27 +0200140 }
141
Harald Welte2b9693d2013-03-19 11:48:54 +0100142 vty_out(vty, " auth-policy %s%s",
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100143 get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
144 VTY_NEWLINE);
Jacob Erlbeck233715c2014-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 Weltea0879c12013-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
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100154 if (g_cfg->subscriber_expiry_timeout != SGSN_TIMEOUT_NEVER)
155 vty_out(vty, " subscriber-expiry-timeout %d%s",
156 g_cfg->subscriber_expiry_timeout, VTY_NEWLINE);
157
Harald Welte55fe0552010-05-01 16:48:27 +0200158 return CMD_SUCCESS;
159}
160
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100161#define SGSN_STR "Configure the SGSN\n"
162#define GGSN_STR "Configure the GGSN information\n"
Harald Weltee0aea392010-06-02 12:41:34 +0200163
164DEFUN(cfg_sgsn, cfg_sgsn_cmd,
165 "sgsn",
166 SGSN_STR)
Harald Welte55fe0552010-05-01 16:48:27 +0200167{
168 vty->node = SGSN_NODE;
169 return CMD_SUCCESS;
170}
171
Harald Weltee0aea392010-06-02 12:41:34 +0200172DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd,
173 "gtp local-ip A.B.C.D",
174 "GTP Parameters\n"
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100175 "Set the IP address for the local GTP bind\n"
176 "IPv4 Address\n")
Harald Weltee0aea392010-06-02 12:41:34 +0200177{
178 inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr);
179
180 return CMD_SUCCESS;
181}
182
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200183DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd,
184 "ggsn <0-255> remote-ip A.B.C.D",
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100185 GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200186{
187 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200188 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welte55fe0552010-05-01 16:48:27 +0200189
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200190 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte55fe0552010-05-01 16:48:27 +0200191
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200192 return CMD_SUCCESS;
193}
194
195#if 0
196DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd,
197 "ggsn <0-255> remote-port <0-65535>",
198 "")
199{
200 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200201 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200202 uint16_t port = atoi(argv[1]);
203
204}
205#endif
206
207DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd,
208 "ggsn <0-255> gtp-version (0|1)",
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100209 GGSN_STR "GGSN Number\n" "GTP Version\n"
210 "Version 0\n" "Version 1\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200211{
212 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200213 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200214
215 if (atoi(argv[1]))
216 ggc->gtp_version = 1;
217 else
218 ggc->gtp_version = 0;
219
220 return CMD_SUCCESS;
221}
222
223#if 0
224DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd,
225 "apn APNAME ggsn <0-255>",
226 "")
227{
228 struct apn_ctx **
229}
230#endif
231
232const struct value_string gprs_mm_st_strs[] = {
233 { GMM_DEREGISTERED, "DEREGISTERED" },
234 { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" },
235 { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" },
Harald Welte3ba2ce12010-06-09 15:50:45 +0200236 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200237 { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" },
238 { 0, NULL }
239};
240
241static void vty_dump_pdp(struct vty *vty, const char *pfx,
242 struct sgsn_pdp_ctx *pdp)
243{
Jacob Erlbeckd781c7a2014-10-13 10:32:00 +0200244 const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200245 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Jacob Erlbeckd781c7a2014-10-13 10:32:00 +0200246 pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Welte493ba622010-07-02 22:47:59 +0200247 vty_out(vty, "%s APN: %s%s", pfx,
248 gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
249 VTY_NEWLINE);
250 vty_out(vty, "%s PDP Address: %s%s", pfx,
251 gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
252 VTY_NEWLINE);
Harald Welte0fe506b2010-06-10 00:20:12 +0200253 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200254}
255
256static void vty_dump_mmctx(struct vty *vty, const char *pfx,
257 struct sgsn_mm_ctx *mm, int pdp)
258{
259 vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
260 pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
261 vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn,
262 mm->tlli, VTY_NEWLINE);
263 vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, "
264 "Cell ID: %u%s", pfx,
265 get_value_string(gprs_mm_st_strs, mm->mm_state),
266 mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
267 mm->cell_id, VTY_NEWLINE);
268
Harald Welte8a035af2010-05-18 10:57:45 +0200269 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
270
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200271 if (pdp) {
272 struct sgsn_pdp_ctx *pdp;
273
274 llist_for_each_entry(pdp, &mm->pdp_list, list)
275 vty_dump_pdp(vty, " ", pdp);
276 }
277}
278
279DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
280 SHOW_STR "Display information about the SGSN")
281{
Jacob Erlbeck42a1aaf2014-12-19 19:19:46 +0100282 if (sgsn->gsup_client) {
283 struct ipa_client_conn *link = sgsn->gsup_client->link;
284 vty_out(vty,
285 " Remote authorization: %sconnected to %s:%d via GSUP%s",
286 sgsn->gsup_client->is_connected ? "" : "not ",
287 link->addr, link->port,
288 VTY_NEWLINE);
289 }
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200290 /* FIXME: statistics */
291 return CMD_SUCCESS;
292}
293
294#define MMCTX_STR "MM Context\n"
295#define INCLUDE_PDP_STR "Include PDP Context Information\n"
296
297#if 0
298DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd,
299 "show mm-context tlli HEX [pdp]",
300 SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR)
301{
302 uint32_t tlli;
303 struct sgsn_mm_ctx *mm;
304
305 tlli = strtoul(argv[0], NULL, 16);
306 mm = sgsn_mm_ctx_by_tlli(tlli);
307 if (!mm) {
308 vty_out(vty, "No MM context for TLLI %08x%s",
309 tlli, VTY_NEWLINE);
310 return CMD_WARNING;
311 }
312 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
313 return CMD_SUCCESS;
314}
315#endif
316
317DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd,
318 "show mm-context imsi IMSI [pdp]",
319 SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n"
320 INCLUDE_PDP_STR)
321{
322 struct sgsn_mm_ctx *mm;
323
324 mm = sgsn_mm_ctx_by_imsi(argv[0]);
325 if (!mm) {
326 vty_out(vty, "No MM context for IMSI %s%s",
327 argv[0], VTY_NEWLINE);
328 return CMD_WARNING;
329 }
330 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
331 return CMD_SUCCESS;
332}
333
334DEFUN(swow_mmctx_all, show_mmctx_all_cmd,
335 "show mm-context all [pdp]",
336 SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR)
337{
338 struct sgsn_mm_ctx *mm;
339
340 llist_for_each_entry(mm, &sgsn_mm_ctxts, list)
341 vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0);
342
343 return CMD_SUCCESS;
344}
345
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200346DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
347 "show pdp-context all",
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100348 SHOW_STR "Display information on PDP Context\n" "Show everything\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200349{
350 struct sgsn_pdp_ctx *pdp;
351
352 llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list)
353 vty_dump_pdp(vty, "", pdp);
354
355 return CMD_SUCCESS;
356}
Harald Welte55fe0552010-05-01 16:48:27 +0200357
Harald Weltea0879c12013-03-19 11:00:13 +0100358
359DEFUN(imsi_acl, cfg_imsi_acl_cmd,
360 "imsi-acl (add|del) IMSI",
361 "Access Control List of foreign IMSIs\n"
362 "Add IMSI to ACL\n"
363 "Remove IMSI from ACL\n"
364 "IMSI of subscriber\n")
365{
366 const char *op = argv[0];
367 const char *imsi = argv[1];
368 int rc;
369
370 if (!strcmp(op, "add"))
Jacob Erlbeck4760eae2014-10-24 15:11:03 +0200371 rc = sgsn_acl_add(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100372 else
Jacob Erlbeck4760eae2014-10-24 15:11:03 +0200373 rc = sgsn_acl_del(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100374
375 if (rc < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100376 vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
377
Harald Weltea0879c12013-03-19 11:00:13 +0100378 return CMD_WARNING;
379 }
380
381 return CMD_SUCCESS;
382}
383
Harald Welte2b9693d2013-03-19 11:48:54 +0100384DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100385 "auth-policy (accept-all|closed|acl-only|remote)",
Harald Welte2b9693d2013-03-19 11:48:54 +0100386 "Autorization Policy of SGSN\n"
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100387 "Accept all IMSIs (DANGEROUS)\n"
388 "Accept only home network subscribers or those in the ACL\n"
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100389 "Accept only subscribers in the ACL\n"
390 "Use remote subscription data only (HLR)\n")
Harald Welte2b9693d2013-03-19 11:48:54 +0100391{
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100392 int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100393 OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100394 g_cfg->auth_policy = val;
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100395 g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck6ff7f642014-12-19 18:08:48 +0100396 g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
Harald Welte2b9693d2013-03-19 11:48:54 +0100397
398 return CMD_SUCCESS;
399}
400
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100401/* Subscriber */
402#include <openbsc/gsm_subscriber.h>
403
404static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending)
405{
406 char expire_time[200];
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100407 struct gsm_auth_tuple *at;
408 int at_idx;
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100409 struct timeval tv;
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100410
411 vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
412 subscr->authorized, VTY_NEWLINE);
413 if (strlen(subscr->name))
414 vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
415 if (strlen(subscr->extension))
416 vty_out(vty, " Extension: %s%s", subscr->extension,
417 VTY_NEWLINE);
418 vty_out(vty, " LAC: %d/0x%x%s",
419 subscr->lac, subscr->lac, VTY_NEWLINE);
420 vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
421 if (subscr->tmsi != GSM_RESERVED_TMSI)
422 vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
423 VTY_NEWLINE);
424
425 if (strlen(subscr->equipment.imei) > 0)
426 vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
427
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100428 for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets);
429 at_idx++) {
430 at = &subscr->sgsn_data->auth_triplets[at_idx];
431 if (at->key_seq == GSM_KEY_SEQ_INVAL)
432 continue;
433
434 vty_out(vty, " A3A8 tuple (used %d times): ",
435 at->use_count);
436 vty_out(vty, " seq # : %d, ",
437 at->key_seq);
438 vty_out(vty, " RAND : %s, ",
439 osmo_hexdump(at->rand, sizeof(at->rand)));
440 vty_out(vty, " SRES : %s, ",
441 osmo_hexdump(at->sres, sizeof(at->sres)));
442 vty_out(vty, " Kc : %s%s",
443 osmo_hexdump(at->kc, sizeof(at->kc)),
444 VTY_NEWLINE);
445 }
446
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100447 /* print the expiration time of a subscriber */
448 if (subscr->expire_lu) {
449 strftime(expire_time, sizeof(expire_time),
450 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
451 expire_time[sizeof(expire_time) - 1] = '\0';
452 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
453 }
454
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100455 /* print the expiration time if the timer is active */
456 if (osmo_timer_pending(&subscr->sgsn_data->timer)) {
457 osmo_timer_remaining(&subscr->sgsn_data->timer, NULL, &tv);
458 strftime(expire_time, sizeof(expire_time),
459 "%a, %d %b %Y %T %z",
460 localtime(&subscr->sgsn_data->timer.timeout.tv_sec));
461 expire_time[sizeof(expire_time) - 1] = '\0';
462 vty_out(vty, " Expires in: %ds (%s)%s",
463 (int)tv.tv_sec, expire_time, VTY_NEWLINE);
464 }
465
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100466 if (subscr->flags)
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100467 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100468 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
469 "FIRST_CONTACT " : "",
470 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
471 "CANCELLED " : "",
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100472 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
473 "UPDATE_LOCATION_PENDING " : "",
474 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
475 "AUTH_INFO_PENDING " : "",
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100476 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
477 "ENABLE_PURGE " : "",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100478 VTY_NEWLINE);
479
480 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
481}
482
483DEFUN(show_subscr_cache,
484 show_subscr_cache_cmd,
485 "show subscriber cache",
486 SHOW_STR "Show information about subscribers\n"
487 "Display contents of subscriber cache\n")
488{
489 struct gsm_subscriber *subscr;
490
491 llist_for_each_entry(subscr, &active_subscribers, entry) {
492 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
493 subscr_dump_full_vty(vty, subscr, 0);
494 }
495
496 return CMD_SUCCESS;
497}
498
499#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
500#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
501 "Use the IMSI to select the subscriber\n" \
502 "The IMSI\n"
503
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100504#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
505
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100506DEFUN(update_subscr_insert, update_subscr_insert_cmd,
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100507 UPDATE_SUBSCR_STR "insert authorized <0-1>)",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100508 UPDATE_SUBSCR_HELP
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100509 UPDATE_SUBSCR_INSERT_HELP
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100510 "Authorize the subscriber to attach\n"
511 "New option value\n")
512{
513 const char *imsi = argv[0];
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100514 const char *value = argv[1];
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100515
516 struct gsm_subscriber *subscr;
517
518 subscr = gprs_subscr_get_or_create(imsi);
519 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100520 vty_out(vty, "%% unable get subscriber record for %s%s",
521 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100522 return CMD_WARNING;
523 }
524
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100525 subscr->authorized = atoi(value);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100526
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100527 subscr_put(subscr);
528
529 return CMD_SUCCESS;
530}
531
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100532DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
533 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
534 UPDATE_SUBSCR_HELP
535 UPDATE_SUBSCR_INSERT_HELP
536 "Update authentication triplet\n"
537 "Triplet index\n"
538 "Set SRES value\nSRES value (4 byte) in hex\n"
539 "Set RAND value\nRAND value (16 byte) in hex\n"
540 "Set Kc value\nKc value (8 byte) in hex\n")
541{
542 const char *imsi = argv[0];
543 const int cksn = atoi(argv[1]) - 1;
544 const char *sres_str = argv[2];
545 const char *rand_str = argv[3];
546 const char *kc_str = argv[4];
547 struct gsm_auth_tuple at = {0,};
548
549 struct gsm_subscriber *subscr;
550
551 subscr = gprs_subscr_get_or_create(imsi);
552 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100553 vty_out(vty, "%% unable get subscriber record for %s%s",
554 imsi, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100555 return CMD_WARNING;
556 }
557
558 OSMO_ASSERT(subscr->sgsn_data);
559
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100560 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100561 vty_out(vty, "%% invalid SRES value '%s'%s",
562 sres_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100563 goto failed;
564 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100565 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100566 vty_out(vty, "%% invalid RAND value '%s'%s",
567 rand_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100568 goto failed;
569 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100570 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100571 vty_out(vty, "%% invalid Kc value '%s'%s",
572 kc_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100573 goto failed;
574 }
575 at.key_seq = cksn;
576
577 subscr->sgsn_data->auth_triplets[cksn] = at;
578 subscr->sgsn_data->auth_triplets_updated = 1;
579
580 subscr_put(subscr);
581
582 return CMD_SUCCESS;
583
584failed:
585 subscr_put(subscr);
586 return CMD_SUCCESS;
587}
588
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100589DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
590 UPDATE_SUBSCR_STR "cancel",
591 UPDATE_SUBSCR_HELP
592 "Cancel (remove) subscriber record\n")
593{
594 const char *imsi = argv[0];
595
596 struct gsm_subscriber *subscr;
597
598 subscr = gprs_subscr_get_by_imsi(imsi);
599 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100600 vty_out(vty, "%% no subscriber record for %s%s",
601 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100602 return CMD_WARNING;
603 }
604
605 gprs_subscr_put_and_cancel(subscr);
606
607 return CMD_SUCCESS;
608}
609
610DEFUN(update_subscr_commit, update_subscr_commit_cmd,
611 UPDATE_SUBSCR_STR "commit",
612 UPDATE_SUBSCR_HELP
613 "Apply the changes made by the insert commands\n")
614{
615 const char *imsi = argv[0];
616
617 struct gsm_subscriber *subscr;
618
619 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100620 if (subscr) {
621 vty_out(vty, "%% subscriber record already exists for %s%s",
622 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100623 return CMD_WARNING;
624 }
625
626 gprs_subscr_update(subscr);
627
628 subscr_put(subscr);
629
630 return CMD_SUCCESS;
631}
632
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100633#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
634 "unknown-subscriber|roaming-not-allowed"
635
636#define UL_ERR_HELP \
637 "Force error code SystemFailure\n" \
638 "Force error code DataMissing\n" \
639 "Force error code UnexpectedDataValue\n" \
640 "Force error code UnknownSubscriber\n" \
641 "Force error code RoamingNotAllowed\n"
642
643DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
644 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
645 UPDATE_SUBSCR_HELP
646 "Complete the update location procedure\n"
647 "The update location request succeeded\n"
648 UL_ERR_HELP)
649{
650 const char *imsi = argv[0];
651 const char *ret_code_str = argv[1];
652
653 struct gsm_subscriber *subscr;
654
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100655 const struct value_string cause_mapping[] = {
656 { GMM_CAUSE_NET_FAIL, "system-failure" },
657 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
658 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
659 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
660 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
661 { 0, NULL }
662 };
663
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100664 subscr = gprs_subscr_get_by_imsi(imsi);
665 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100666 vty_out(vty, "%% unable to get subscriber record for %s%s",
667 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100668 return CMD_WARNING;
669 }
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100670
671 if (strcmp(ret_code_str, "ok") == 0) {
672 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100673 subscr->authorized = 1;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100674 } else {
675 subscr->sgsn_data->error_cause =
676 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100677 subscr->authorized = 0;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100678 }
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100679
680 gprs_subscr_update(subscr);
681
682 subscr_put(subscr);
683
684 return CMD_SUCCESS;
685}
686
687DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
688 UPDATE_SUBSCR_STR "update-auth-info",
689 UPDATE_SUBSCR_HELP
690 "Complete the send authentication info procedure\n")
691{
692 const char *imsi = argv[0];
693
694 struct gsm_subscriber *subscr;
695
696 subscr = gprs_subscr_get_by_imsi(imsi);
697 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100698 vty_out(vty, "%% unable to get subscriber record for %s%s",
699 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100700 return CMD_WARNING;
701 }
702
703 gprs_subscr_update_auth_info(subscr);
704
705 subscr_put(subscr);
706
707 return CMD_SUCCESS;
708}
709
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100710DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
711 "gsup remote-ip A.B.C.D",
712 "GSUP Parameters\n"
713 "Set the IP address of the remote GSUP server\n"
714 "IPv4 Address\n")
715{
716 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
717
718 return CMD_SUCCESS;
719}
720
721DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
722 "gsup remote-port <0-65535>",
723 "GSUP Parameters\n"
724 "Set the TCP port of the remote GSUP server\n"
725 "Remote TCP port\n")
726{
727 g_cfg->gsup_server_port = atoi(argv[0]);
728
729 return CMD_SUCCESS;
730}
731
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100732DEFUN(cfg_subscriber_expiry_timeout, cfg_subscriber_expiry_timeout_cmd,
733 "subscriber-expiry-timeout <0-999999>",
734 "Set the expiry time for unused subscriber entries\n"
735 "Expiry time in seconds\n")
736{
737 g_cfg->subscriber_expiry_timeout = atoi(argv[0]);
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100738
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100739 return CMD_SUCCESS;
740}
741
742DEFUN(cfg_no_subscriber_expiry_timeout, cfg_no_subscriber_expiry_timeout_cmd,
743 "no subscriber-expiry-timeout",
744 NO_STR "Set the expiry time for unused subscriber entries\n")
745{
746 g_cfg->subscriber_expiry_timeout = atoi(argv[0]);
747
748 return CMD_SUCCESS;
749}
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100750
Harald Welte55fe0552010-05-01 16:48:27 +0200751int sgsn_vty_init(void)
752{
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200753 install_element_ve(&show_sgsn_cmd);
754 //install_element_ve(&show_mmctx_tlli_cmd);
755 install_element_ve(&show_mmctx_imsi_cmd);
756 install_element_ve(&show_mmctx_all_cmd);
757 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100758 install_element_ve(&show_subscr_cache_cmd);
759
760 install_element(ENABLE_NODE, &update_subscr_insert_cmd);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100761 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100762 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
763 install_element(ENABLE_NODE, &update_subscr_commit_cmd);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100764 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
765 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200766
767 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
768 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeckf414e852013-10-29 09:30:30 +0100769 vty_install_default(SGSN_NODE);
Harald Weltee0aea392010-06-02 12:41:34 +0200770 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200771 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
772 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
773 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Weltea0879c12013-03-19 11:00:13 +0100774 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte2b9693d2013-03-19 11:48:54 +0100775 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100776 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
777 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Jacob Erlbeck9bf4be92015-01-06 16:32:41 +0100778 install_element(SGSN_NODE, &cfg_subscriber_expiry_timeout_cmd);
779 install_element(SGSN_NODE, &cfg_no_subscriber_expiry_timeout_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200780
781 return 0;
782}
783
784int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
785{
786 int rc;
787
788 g_cfg = cfg;
Harald Weltea0879c12013-03-19 11:00:13 +0100789
Harald Welte40152872010-05-16 20:52:23 +0200790 rc = vty_read_config_file(config_file, NULL);
Harald Welte55fe0552010-05-01 16:48:27 +0200791 if (rc < 0) {
792 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
793 return rc;
794 }
795
796 return 0;
797}