blob: ba71555ecb4a313d326cc30f576cd40979222c10 [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
Harald Welte55fe0552010-05-01 16:48:27 +0200154 return CMD_SUCCESS;
155}
156
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100157#define SGSN_STR "Configure the SGSN\n"
158#define GGSN_STR "Configure the GGSN information\n"
Harald Weltee0aea392010-06-02 12:41:34 +0200159
160DEFUN(cfg_sgsn, cfg_sgsn_cmd,
161 "sgsn",
162 SGSN_STR)
Harald Welte55fe0552010-05-01 16:48:27 +0200163{
164 vty->node = SGSN_NODE;
165 return CMD_SUCCESS;
166}
167
Harald Weltee0aea392010-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 Freytherf403c482011-11-05 15:21:16 +0100171 "Set the IP address for the local GTP bind\n"
172 "IPv4 Address\n")
Harald Weltee0aea392010-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 Weltec1f6bfe2010-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 Freytherf403c482011-11-05 15:21:16 +0100181 GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200182{
183 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200184 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welte55fe0552010-05-01 16:48:27 +0200185
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200186 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte55fe0552010-05-01 16:48:27 +0200187
Harald Weltec1f6bfe2010-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 Welteeb471c92010-05-18 14:32:29 +0200197 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-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 Freytherf403c482011-11-05 15:21:16 +0100205 GGSN_STR "GGSN Number\n" "GTP Version\n"
206 "Version 0\n" "Version 1\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200207{
208 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200209 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-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 Welte3ba2ce12010-06-09 15:50:45 +0200232 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Weltec1f6bfe2010-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 Erlbeckd781c7a2014-10-13 10:32:00 +0200240 const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200241 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Jacob Erlbeckd781c7a2014-10-13 10:32:00 +0200242 pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Welte493ba622010-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 Welte0fe506b2010-06-10 00:20:12 +0200249 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Weltec1f6bfe2010-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 Welte8a035af2010-05-18 10:57:45 +0200265 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
266
Harald Weltec1f6bfe2010-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 Erlbeck42a1aaf2014-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 Weltec1f6bfe2010-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 Weltec1f6bfe2010-05-17 22:58:03 +0200342DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
343 "show pdp-context all",
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100344 SHOW_STR "Display information on PDP Context\n" "Show everything\n")
Harald Weltec1f6bfe2010-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 Welte55fe0552010-05-01 16:48:27 +0200353
Harald Weltea0879c12013-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 Erlbeck4760eae2014-10-24 15:11:03 +0200367 rc = sgsn_acl_add(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100368 else
Jacob Erlbeck4760eae2014-10-24 15:11:03 +0200369 rc = sgsn_acl_del(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100370
371 if (rc < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100372 vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
373
Harald Weltea0879c12013-03-19 11:00:13 +0100374 return CMD_WARNING;
375 }
376
377 return CMD_SUCCESS;
378}
379
Harald Welte2b9693d2013-03-19 11:48:54 +0100380DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100381 "auth-policy (accept-all|closed|acl-only|remote)",
Harald Welte2b9693d2013-03-19 11:48:54 +0100382 "Autorization Policy of SGSN\n"
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100383 "Accept all IMSIs (DANGEROUS)\n"
384 "Accept only home network subscribers or those in the ACL\n"
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100385 "Accept only subscribers in the ACL\n"
386 "Use remote subscription data only (HLR)\n")
Harald Welte2b9693d2013-03-19 11:48:54 +0100387{
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100388 int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100389 OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100390 g_cfg->auth_policy = val;
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100391 g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck6ff7f642014-12-19 18:08:48 +0100392 g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
Harald Welte2b9693d2013-03-19 11:48:54 +0100393
394 return CMD_SUCCESS;
395}
396
Jacob Erlbeckc16c3502014-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 Erlbeckb1332b62014-12-08 15:52:00 +0100403 struct gsm_auth_tuple *at;
404 int at_idx;
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100405
406 vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
407 subscr->authorized, VTY_NEWLINE);
408 if (strlen(subscr->name))
409 vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
410 if (strlen(subscr->extension))
411 vty_out(vty, " Extension: %s%s", subscr->extension,
412 VTY_NEWLINE);
413 vty_out(vty, " LAC: %d/0x%x%s",
414 subscr->lac, subscr->lac, VTY_NEWLINE);
415 vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
416 if (subscr->tmsi != GSM_RESERVED_TMSI)
417 vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
418 VTY_NEWLINE);
419
420 if (strlen(subscr->equipment.imei) > 0)
421 vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
422
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100423 for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets);
424 at_idx++) {
425 at = &subscr->sgsn_data->auth_triplets[at_idx];
426 if (at->key_seq == GSM_KEY_SEQ_INVAL)
427 continue;
428
429 vty_out(vty, " A3A8 tuple (used %d times): ",
430 at->use_count);
431 vty_out(vty, " seq # : %d, ",
432 at->key_seq);
433 vty_out(vty, " RAND : %s, ",
434 osmo_hexdump(at->rand, sizeof(at->rand)));
435 vty_out(vty, " SRES : %s, ",
436 osmo_hexdump(at->sres, sizeof(at->sres)));
437 vty_out(vty, " Kc : %s%s",
438 osmo_hexdump(at->kc, sizeof(at->kc)),
439 VTY_NEWLINE);
440 }
441
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100442 /* print the expiration time of a subscriber */
443 if (subscr->expire_lu) {
444 strftime(expire_time, sizeof(expire_time),
445 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
446 expire_time[sizeof(expire_time) - 1] = '\0';
447 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
448 }
449
450 if (subscr->flags)
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100451 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100452 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
453 "FIRST_CONTACT " : "",
454 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
455 "CANCELLED " : "",
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100456 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
457 "UPDATE_LOCATION_PENDING " : "",
458 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
459 "AUTH_INFO_PENDING " : "",
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100460 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
461 "ENABLE_PURGE " : "",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100462 VTY_NEWLINE);
463
464 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
465}
466
467DEFUN(show_subscr_cache,
468 show_subscr_cache_cmd,
469 "show subscriber cache",
470 SHOW_STR "Show information about subscribers\n"
471 "Display contents of subscriber cache\n")
472{
473 struct gsm_subscriber *subscr;
474
475 llist_for_each_entry(subscr, &active_subscribers, entry) {
476 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
477 subscr_dump_full_vty(vty, subscr, 0);
478 }
479
480 return CMD_SUCCESS;
481}
482
483#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
484#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
485 "Use the IMSI to select the subscriber\n" \
486 "The IMSI\n"
487
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100488#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
489
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100490DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
491 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
492 UPDATE_SUBSCR_HELP
493 UPDATE_SUBSCR_INSERT_HELP
494 "Update authentication triplet\n"
495 "Triplet index\n"
496 "Set SRES value\nSRES value (4 byte) in hex\n"
497 "Set RAND value\nRAND value (16 byte) in hex\n"
498 "Set Kc value\nKc value (8 byte) in hex\n")
499{
500 const char *imsi = argv[0];
501 const int cksn = atoi(argv[1]) - 1;
502 const char *sres_str = argv[2];
503 const char *rand_str = argv[3];
504 const char *kc_str = argv[4];
505 struct gsm_auth_tuple at = {0,};
506
507 struct gsm_subscriber *subscr;
508
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100509 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100510 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100511 vty_out(vty, "%% unable get subscriber record for %s%s",
512 imsi, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100513 return CMD_WARNING;
514 }
515
516 OSMO_ASSERT(subscr->sgsn_data);
517
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100518 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100519 vty_out(vty, "%% invalid SRES value '%s'%s",
520 sres_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100521 goto failed;
522 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100523 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100524 vty_out(vty, "%% invalid RAND value '%s'%s",
525 rand_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100526 goto failed;
527 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100528 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100529 vty_out(vty, "%% invalid Kc value '%s'%s",
530 kc_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100531 goto failed;
532 }
533 at.key_seq = cksn;
534
535 subscr->sgsn_data->auth_triplets[cksn] = at;
536 subscr->sgsn_data->auth_triplets_updated = 1;
537
538 subscr_put(subscr);
539
540 return CMD_SUCCESS;
541
542failed:
543 subscr_put(subscr);
544 return CMD_SUCCESS;
545}
546
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100547DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100548 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100549 UPDATE_SUBSCR_HELP
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100550 "Cancel (remove) subscriber record\n"
551 "The MS moved to another SGSN\n"
552 "The subscription is no longer valid\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100553{
554 const char *imsi = argv[0];
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100555 const char *cancel_type = argv[1];
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100556
557 struct gsm_subscriber *subscr;
558
559 subscr = gprs_subscr_get_by_imsi(imsi);
560 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100561 vty_out(vty, "%% no subscriber record for %s%s",
562 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100563 return CMD_WARNING;
564 }
565
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100566 if (strcmp(cancel_type, "update-procedure") == 0)
567 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
568 else
569 subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
570
Jacob Erlbeck7a7d8812015-01-23 13:52:55 +0100571 gprs_subscr_cancel(subscr);
572 subscr_put(subscr);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100573
574 return CMD_SUCCESS;
575}
576
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100577DEFUN(update_subscr_create, update_subscr_create_cmd,
578 UPDATE_SUBSCR_STR "create",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100579 UPDATE_SUBSCR_HELP
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100580 "Create a subscriber entry\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100581{
582 const char *imsi = argv[0];
583
584 struct gsm_subscriber *subscr;
585
586 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100587 if (subscr) {
588 vty_out(vty, "%% subscriber record already exists for %s%s",
589 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100590 return CMD_WARNING;
591 }
592
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100593 subscr = gprs_subscr_get_or_create(imsi);
594 subscr->keep_in_ram = 1;
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100595 subscr_put(subscr);
596
597 return CMD_SUCCESS;
598}
599
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100600DEFUN(update_subscr_destroy, update_subscr_destroy_cmd,
601 UPDATE_SUBSCR_STR "destroy",
602 UPDATE_SUBSCR_HELP
603 "Destroy a subscriber entry\n")
604{
605 const char *imsi = argv[0];
606
607 struct gsm_subscriber *subscr;
608
609 subscr = gprs_subscr_get_by_imsi(imsi);
610 if (!subscr) {
611 vty_out(vty, "%% subscriber record does not exist for %s%s",
612 imsi, VTY_NEWLINE);
613 return CMD_WARNING;
614 }
615
616 subscr->keep_in_ram = 0;
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100617 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100618 gprs_subscr_cancel(subscr);
619 if (subscr->use_count > 1)
620 vty_out(vty, "%% subscriber is still in use%s",
621 VTY_NEWLINE);
622 subscr_put(subscr);
623
624 return CMD_SUCCESS;
625}
626
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100627#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
628 "unknown-subscriber|roaming-not-allowed"
629
630#define UL_ERR_HELP \
631 "Force error code SystemFailure\n" \
632 "Force error code DataMissing\n" \
633 "Force error code UnexpectedDataValue\n" \
634 "Force error code UnknownSubscriber\n" \
635 "Force error code RoamingNotAllowed\n"
636
637DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
638 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
639 UPDATE_SUBSCR_HELP
640 "Complete the update location procedure\n"
641 "The update location request succeeded\n"
642 UL_ERR_HELP)
643{
644 const char *imsi = argv[0];
645 const char *ret_code_str = argv[1];
646
647 struct gsm_subscriber *subscr;
648
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100649 const struct value_string cause_mapping[] = {
650 { GMM_CAUSE_NET_FAIL, "system-failure" },
651 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
652 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
653 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
654 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
655 { 0, NULL }
656 };
657
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100658 subscr = gprs_subscr_get_by_imsi(imsi);
659 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100660 vty_out(vty, "%% unable to get subscriber record for %s%s",
661 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100662 return CMD_WARNING;
663 }
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100664
665 if (strcmp(ret_code_str, "ok") == 0) {
666 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100667 subscr->authorized = 1;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100668 } else {
669 subscr->sgsn_data->error_cause =
670 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100671 subscr->authorized = 0;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100672 }
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100673
674 gprs_subscr_update(subscr);
675
676 subscr_put(subscr);
677
678 return CMD_SUCCESS;
679}
680
681DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
682 UPDATE_SUBSCR_STR "update-auth-info",
683 UPDATE_SUBSCR_HELP
684 "Complete the send authentication info procedure\n")
685{
686 const char *imsi = argv[0];
687
688 struct gsm_subscriber *subscr;
689
690 subscr = gprs_subscr_get_by_imsi(imsi);
691 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100692 vty_out(vty, "%% unable to get subscriber record for %s%s",
693 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100694 return CMD_WARNING;
695 }
696
697 gprs_subscr_update_auth_info(subscr);
698
699 subscr_put(subscr);
700
701 return CMD_SUCCESS;
702}
703
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100704DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
705 "gsup remote-ip A.B.C.D",
706 "GSUP Parameters\n"
707 "Set the IP address of the remote GSUP server\n"
708 "IPv4 Address\n")
709{
710 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
711
712 return CMD_SUCCESS;
713}
714
715DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
716 "gsup remote-port <0-65535>",
717 "GSUP Parameters\n"
718 "Set the TCP port of the remote GSUP server\n"
719 "Remote TCP port\n")
720{
721 g_cfg->gsup_server_port = atoi(argv[0]);
722
723 return CMD_SUCCESS;
724}
725
Harald Welte55fe0552010-05-01 16:48:27 +0200726int sgsn_vty_init(void)
727{
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200728 install_element_ve(&show_sgsn_cmd);
729 //install_element_ve(&show_mmctx_tlli_cmd);
730 install_element_ve(&show_mmctx_imsi_cmd);
731 install_element_ve(&show_mmctx_all_cmd);
732 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100733 install_element_ve(&show_subscr_cache_cmd);
734
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100735 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100736 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100737 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100738 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100739 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
740 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200741
742 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
743 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeckf414e852013-10-29 09:30:30 +0100744 vty_install_default(SGSN_NODE);
Harald Weltee0aea392010-06-02 12:41:34 +0200745 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200746 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
747 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
748 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Weltea0879c12013-03-19 11:00:13 +0100749 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte2b9693d2013-03-19 11:48:54 +0100750 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100751 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
752 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200753
754 return 0;
755}
756
757int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
758{
759 int rc;
760
761 g_cfg = cfg;
Harald Weltea0879c12013-03-19 11:00:13 +0100762
Harald Welte40152872010-05-16 20:52:23 +0200763 rc = vty_read_config_file(config_file, NULL);
Harald Welte55fe0552010-05-01 16:48:27 +0200764 if (rc < 0) {
765 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
766 return rc;
767 }
768
769 return 0;
770}