blob: d85ea0192372a780d1043cbfb0bcc0148cf4359d [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;
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100129 struct apn_ctx *actx;
Harald Welte288be162010-05-01 16:48:27 +0200130
131 vty_out(vty, "sgsn%s", VTY_NEWLINE);
132
Harald Weltee300d002010-06-02 12:41:34 +0200133 vty_out(vty, " gtp local-ip %s%s",
134 inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE);
135
Harald Welted193cb32010-05-17 22:58:03 +0200136 llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
Harald Welteff3bde82010-05-19 15:09:09 +0200137 vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200138 inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
Harald Welteff3bde82010-05-19 15:09:09 +0200139 vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200140 gctx->gtp_version, VTY_NEWLINE);
Harald Welte288be162010-05-01 16:48:27 +0200141 }
142
Harald Welte3dfb5492013-03-19 11:48:54 +0100143 vty_out(vty, " auth-policy %s%s",
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100144 get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
145 VTY_NEWLINE);
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100146 if (g_cfg->gsup_server_addr.sin_addr.s_addr)
147 vty_out(vty, " gsup remote-ip %s%s",
148 inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE);
149 if (g_cfg->gsup_server_port)
150 vty_out(vty, " gsup remote-port %d%s",
151 g_cfg->gsup_server_port, VTY_NEWLINE);
Harald Welte7f6da482013-03-19 11:00:13 +0100152 llist_for_each_entry(acl, &g_cfg->imsi_acl, list)
153 vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE);
154
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100155 if (llist_empty(&sgsn_apn_ctxts))
156 vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE);
157 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
158 if (strlen(actx->imsi_prefix) > 0)
159 vty_out(vty, " apn %s imsi-prefix %s ggsn %d%s",
160 actx->name, actx->imsi_prefix, actx->ggsn->id,
161 VTY_NEWLINE);
162 else
163 vty_out(vty, " apn %s ggsn %d%s", actx->name,
164 actx->ggsn->id, VTY_NEWLINE);
165 }
166
Harald Welte288be162010-05-01 16:48:27 +0200167 return CMD_SUCCESS;
168}
169
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100170#define SGSN_STR "Configure the SGSN\n"
171#define GGSN_STR "Configure the GGSN information\n"
Harald Weltee300d002010-06-02 12:41:34 +0200172
173DEFUN(cfg_sgsn, cfg_sgsn_cmd,
174 "sgsn",
175 SGSN_STR)
Harald Welte288be162010-05-01 16:48:27 +0200176{
177 vty->node = SGSN_NODE;
178 return CMD_SUCCESS;
179}
180
Harald Weltee300d002010-06-02 12:41:34 +0200181DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd,
182 "gtp local-ip A.B.C.D",
183 "GTP Parameters\n"
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100184 "Set the IP address for the local GTP bind\n"
185 "IPv4 Address\n")
Harald Weltee300d002010-06-02 12:41:34 +0200186{
187 inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr);
188
189 return CMD_SUCCESS;
190}
191
Harald Welted193cb32010-05-17 22:58:03 +0200192DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd,
193 "ggsn <0-255> remote-ip A.B.C.D",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100194 GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n")
Harald Welted193cb32010-05-17 22:58:03 +0200195{
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 Welte288be162010-05-01 16:48:27 +0200198
Harald Welted193cb32010-05-17 22:58:03 +0200199 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte288be162010-05-01 16:48:27 +0200200
Harald Welted193cb32010-05-17 22:58:03 +0200201 return CMD_SUCCESS;
202}
203
204#if 0
205DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd,
206 "ggsn <0-255> remote-port <0-65535>",
207 "")
208{
209 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200210 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200211 uint16_t port = atoi(argv[1]);
212
213}
214#endif
215
216DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd,
217 "ggsn <0-255> gtp-version (0|1)",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100218 GGSN_STR "GGSN Number\n" "GTP Version\n"
219 "Version 0\n" "Version 1\n")
Harald Welted193cb32010-05-17 22:58:03 +0200220{
221 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200222 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200223
224 if (atoi(argv[1]))
225 ggc->gtp_version = 1;
226 else
227 ggc->gtp_version = 0;
228
229 return CMD_SUCCESS;
230}
231
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100232#define APN_STR "Configure the information per APN\n"
233#define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n"
234
235static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str,
236 const char *imsi_prefix, int ggsn_id)
237{
238 struct apn_ctx *actx;
239 struct sgsn_ggsn_ctx *ggsn;
240
241 ggsn = sgsn_ggsn_ctx_by_id(ggsn_id);
242 if (ggsn == NULL) {
243 vty_out(vty, "%% a GGSN with id %d has not been defined%s",
244 ggsn_id, VTY_NEWLINE);
245 return CMD_WARNING;
246 }
247
248 actx = sgsn_apn_ctx_find_alloc(apn_str, imsi_prefix);
249 if (!actx) {
250 vty_out(vty, "%% unable to create APN context for %s/%s%s",
251 apn_str, imsi_prefix, VTY_NEWLINE);
252 return CMD_WARNING;
253 }
254
255 actx->ggsn = ggsn;
256
257 return CMD_SUCCESS;
258}
259
Harald Welted193cb32010-05-17 22:58:03 +0200260DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd,
261 "apn APNAME ggsn <0-255>",
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100262 APN_STR APN_GW_STR
263 "Select the GGSN to use when the APN gateway prefix matches\n"
264 "The GGSN id")
Harald Welted193cb32010-05-17 22:58:03 +0200265{
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100266
267 return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1]));
Harald Welted193cb32010-05-17 22:58:03 +0200268}
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100269
270DEFUN(cfg_apn_imsi_ggsn, cfg_apn_imsi_ggsn_cmd,
271 "apn APNAME imsi-prefix IMSIPRE ggsn <0-255>",
272 APN_STR APN_GW_STR
273 "Restrict rule to a certain IMSI prefix\n"
274 "An IMSI prefix\n"
275 "Select the GGSN to use when APN gateway and IMSI prefix match\n"
276 "The GGSN id")
277{
278
279 return add_apn_ggsn_mapping(vty, argv[0], argv[1], atoi(argv[2]));
280}
Harald Welted193cb32010-05-17 22:58:03 +0200281
282const struct value_string gprs_mm_st_strs[] = {
283 { GMM_DEREGISTERED, "DEREGISTERED" },
284 { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" },
285 { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" },
Harald Weltebffeff82010-06-09 15:50:45 +0200286 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Welted193cb32010-05-17 22:58:03 +0200287 { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" },
288 { 0, NULL }
289};
290
291static void vty_dump_pdp(struct vty *vty, const char *pfx,
292 struct sgsn_pdp_ctx *pdp)
293{
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200294 const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
Harald Welted193cb32010-05-17 22:58:03 +0200295 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200296 pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Weltec5d4a0c2010-07-02 22:47:59 +0200297 vty_out(vty, "%s APN: %s%s", pfx,
298 gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
299 VTY_NEWLINE);
300 vty_out(vty, "%s PDP Address: %s%s", pfx,
301 gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
302 VTY_NEWLINE);
Harald Welteefbdee92010-06-10 00:20:12 +0200303 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Welted193cb32010-05-17 22:58:03 +0200304}
305
306static void vty_dump_mmctx(struct vty *vty, const char *pfx,
307 struct sgsn_mm_ctx *mm, int pdp)
308{
309 vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
310 pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
311 vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn,
312 mm->tlli, VTY_NEWLINE);
313 vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, "
314 "Cell ID: %u%s", pfx,
315 get_value_string(gprs_mm_st_strs, mm->mm_state),
316 mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
317 mm->cell_id, VTY_NEWLINE);
318
Harald Welte8acd88f2010-05-18 10:57:45 +0200319 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
320
Harald Welted193cb32010-05-17 22:58:03 +0200321 if (pdp) {
322 struct sgsn_pdp_ctx *pdp;
323
324 llist_for_each_entry(pdp, &mm->pdp_list, list)
325 vty_dump_pdp(vty, " ", pdp);
326 }
327}
328
329DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
330 SHOW_STR "Display information about the SGSN")
331{
Jacob Erlbeck80547992014-12-19 19:19:46 +0100332 if (sgsn->gsup_client) {
333 struct ipa_client_conn *link = sgsn->gsup_client->link;
334 vty_out(vty,
335 " Remote authorization: %sconnected to %s:%d via GSUP%s",
336 sgsn->gsup_client->is_connected ? "" : "not ",
337 link->addr, link->port,
338 VTY_NEWLINE);
339 }
Harald Welted193cb32010-05-17 22:58:03 +0200340 /* FIXME: statistics */
341 return CMD_SUCCESS;
342}
343
344#define MMCTX_STR "MM Context\n"
345#define INCLUDE_PDP_STR "Include PDP Context Information\n"
346
347#if 0
348DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd,
349 "show mm-context tlli HEX [pdp]",
350 SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR)
351{
352 uint32_t tlli;
353 struct sgsn_mm_ctx *mm;
354
355 tlli = strtoul(argv[0], NULL, 16);
356 mm = sgsn_mm_ctx_by_tlli(tlli);
357 if (!mm) {
358 vty_out(vty, "No MM context for TLLI %08x%s",
359 tlli, VTY_NEWLINE);
360 return CMD_WARNING;
361 }
362 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
363 return CMD_SUCCESS;
364}
365#endif
366
367DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd,
368 "show mm-context imsi IMSI [pdp]",
369 SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n"
370 INCLUDE_PDP_STR)
371{
372 struct sgsn_mm_ctx *mm;
373
374 mm = sgsn_mm_ctx_by_imsi(argv[0]);
375 if (!mm) {
376 vty_out(vty, "No MM context for IMSI %s%s",
377 argv[0], VTY_NEWLINE);
378 return CMD_WARNING;
379 }
380 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
381 return CMD_SUCCESS;
382}
383
384DEFUN(swow_mmctx_all, show_mmctx_all_cmd,
385 "show mm-context all [pdp]",
386 SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR)
387{
388 struct sgsn_mm_ctx *mm;
389
390 llist_for_each_entry(mm, &sgsn_mm_ctxts, list)
391 vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0);
392
393 return CMD_SUCCESS;
394}
395
Harald Welted193cb32010-05-17 22:58:03 +0200396DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
397 "show pdp-context all",
Holger Hans Peter Freyther1491f2e2011-11-05 15:21:16 +0100398 SHOW_STR "Display information on PDP Context\n" "Show everything\n")
Harald Welted193cb32010-05-17 22:58:03 +0200399{
400 struct sgsn_pdp_ctx *pdp;
401
402 llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list)
403 vty_dump_pdp(vty, "", pdp);
404
405 return CMD_SUCCESS;
406}
Harald Welte288be162010-05-01 16:48:27 +0200407
Harald Welte7f6da482013-03-19 11:00:13 +0100408
409DEFUN(imsi_acl, cfg_imsi_acl_cmd,
410 "imsi-acl (add|del) IMSI",
411 "Access Control List of foreign IMSIs\n"
412 "Add IMSI to ACL\n"
413 "Remove IMSI from ACL\n"
414 "IMSI of subscriber\n")
415{
416 const char *op = argv[0];
417 const char *imsi = argv[1];
418 int rc;
419
420 if (!strcmp(op, "add"))
Jacob Erlbeck3b5d4072014-10-24 15:11:03 +0200421 rc = sgsn_acl_add(imsi, g_cfg);
Harald Welte7f6da482013-03-19 11:00:13 +0100422 else
Jacob Erlbeck3b5d4072014-10-24 15:11:03 +0200423 rc = sgsn_acl_del(imsi, g_cfg);
Harald Welte7f6da482013-03-19 11:00:13 +0100424
425 if (rc < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100426 vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
427
Harald Welte7f6da482013-03-19 11:00:13 +0100428 return CMD_WARNING;
429 }
430
431 return CMD_SUCCESS;
432}
433
Harald Welte3dfb5492013-03-19 11:48:54 +0100434DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100435 "auth-policy (accept-all|closed|acl-only|remote)",
Harald Welte3dfb5492013-03-19 11:48:54 +0100436 "Autorization Policy of SGSN\n"
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100437 "Accept all IMSIs (DANGEROUS)\n"
438 "Accept only home network subscribers or those in the ACL\n"
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100439 "Accept only subscribers in the ACL\n"
440 "Use remote subscription data only (HLR)\n")
Harald Welte3dfb5492013-03-19 11:48:54 +0100441{
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100442 int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100443 OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck106f5472014-11-04 10:08:37 +0100444 g_cfg->auth_policy = val;
Jacob Erlbeck9d4f46c2014-12-17 13:20:08 +0100445 g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck771573c2014-12-19 18:08:48 +0100446 g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
Harald Welte3dfb5492013-03-19 11:48:54 +0100447
448 return CMD_SUCCESS;
449}
450
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100451/* Subscriber */
452#include <openbsc/gsm_subscriber.h>
453
454static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending)
455{
456 char expire_time[200];
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100457 struct gsm_auth_tuple *at;
458 int at_idx;
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100459 struct sgsn_subscriber_pdp_data *pdp;
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100460
461 vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
462 subscr->authorized, VTY_NEWLINE);
463 if (strlen(subscr->name))
464 vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
465 if (strlen(subscr->extension))
466 vty_out(vty, " Extension: %s%s", subscr->extension,
467 VTY_NEWLINE);
468 vty_out(vty, " LAC: %d/0x%x%s",
469 subscr->lac, subscr->lac, VTY_NEWLINE);
470 vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
471 if (subscr->tmsi != GSM_RESERVED_TMSI)
472 vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
473 VTY_NEWLINE);
474
475 if (strlen(subscr->equipment.imei) > 0)
476 vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
477
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100478 for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets);
479 at_idx++) {
480 at = &subscr->sgsn_data->auth_triplets[at_idx];
481 if (at->key_seq == GSM_KEY_SEQ_INVAL)
482 continue;
483
484 vty_out(vty, " A3A8 tuple (used %d times): ",
485 at->use_count);
486 vty_out(vty, " seq # : %d, ",
487 at->key_seq);
488 vty_out(vty, " RAND : %s, ",
489 osmo_hexdump(at->rand, sizeof(at->rand)));
490 vty_out(vty, " SRES : %s, ",
491 osmo_hexdump(at->sres, sizeof(at->sres)));
492 vty_out(vty, " Kc : %s%s",
493 osmo_hexdump(at->kc, sizeof(at->kc)),
494 VTY_NEWLINE);
495 }
496
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100497 llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
498 vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'%s",
499 pdp->context_id, pdp->pdp_type, pdp->apn_str,
500 VTY_NEWLINE);
501 }
502
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100503 /* print the expiration time of a subscriber */
504 if (subscr->expire_lu) {
505 strftime(expire_time, sizeof(expire_time),
506 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
507 expire_time[sizeof(expire_time) - 1] = '\0';
508 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
509 }
510
511 if (subscr->flags)
Jacob Erlbeck65fa3f72015-01-06 16:32:41 +0100512 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100513 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
514 "FIRST_CONTACT " : "",
515 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
516 "CANCELLED " : "",
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100517 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
518 "UPDATE_LOCATION_PENDING " : "",
519 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
520 "AUTH_INFO_PENDING " : "",
Jacob Erlbeck65fa3f72015-01-06 16:32:41 +0100521 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
522 "ENABLE_PURGE " : "",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100523 VTY_NEWLINE);
524
525 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
526}
527
528DEFUN(show_subscr_cache,
529 show_subscr_cache_cmd,
530 "show subscriber cache",
531 SHOW_STR "Show information about subscribers\n"
532 "Display contents of subscriber cache\n")
533{
534 struct gsm_subscriber *subscr;
535
536 llist_for_each_entry(subscr, &active_subscribers, entry) {
537 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
538 subscr_dump_full_vty(vty, subscr, 0);
539 }
540
541 return CMD_SUCCESS;
542}
543
544#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
545#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
546 "Use the IMSI to select the subscriber\n" \
547 "The IMSI\n"
548
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100549#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
550
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100551DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
552 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
553 UPDATE_SUBSCR_HELP
554 UPDATE_SUBSCR_INSERT_HELP
555 "Update authentication triplet\n"
556 "Triplet index\n"
557 "Set SRES value\nSRES value (4 byte) in hex\n"
558 "Set RAND value\nRAND value (16 byte) in hex\n"
559 "Set Kc value\nKc value (8 byte) in hex\n")
560{
561 const char *imsi = argv[0];
562 const int cksn = atoi(argv[1]) - 1;
563 const char *sres_str = argv[2];
564 const char *rand_str = argv[3];
565 const char *kc_str = argv[4];
566 struct gsm_auth_tuple at = {0,};
567
568 struct gsm_subscriber *subscr;
569
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100570 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100571 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100572 vty_out(vty, "%% unable get subscriber record for %s%s",
573 imsi, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100574 return CMD_WARNING;
575 }
576
577 OSMO_ASSERT(subscr->sgsn_data);
578
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100579 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100580 vty_out(vty, "%% invalid SRES value '%s'%s",
581 sres_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100582 goto failed;
583 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100584 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100585 vty_out(vty, "%% invalid RAND value '%s'%s",
586 rand_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100587 goto failed;
588 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100589 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100590 vty_out(vty, "%% invalid Kc value '%s'%s",
591 kc_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100592 goto failed;
593 }
594 at.key_seq = cksn;
595
596 subscr->sgsn_data->auth_triplets[cksn] = at;
597 subscr->sgsn_data->auth_triplets_updated = 1;
598
599 subscr_put(subscr);
600
601 return CMD_SUCCESS;
602
603failed:
604 subscr_put(subscr);
605 return CMD_SUCCESS;
606}
607
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100608DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100609 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100610 UPDATE_SUBSCR_HELP
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100611 "Cancel (remove) subscriber record\n"
612 "The MS moved to another SGSN\n"
613 "The subscription is no longer valid\n")
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100614{
615 const char *imsi = argv[0];
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100616 const char *cancel_type = argv[1];
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100617
618 struct gsm_subscriber *subscr;
619
620 subscr = gprs_subscr_get_by_imsi(imsi);
621 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100622 vty_out(vty, "%% no subscriber record for %s%s",
623 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100624 return CMD_WARNING;
625 }
626
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100627 if (strcmp(cancel_type, "update-procedure") == 0)
628 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
629 else
630 subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
631
Jacob Erlbeck37139e52015-01-23 13:52:55 +0100632 gprs_subscr_cancel(subscr);
633 subscr_put(subscr);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100634
635 return CMD_SUCCESS;
636}
637
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100638DEFUN(update_subscr_create, update_subscr_create_cmd,
639 UPDATE_SUBSCR_STR "create",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100640 UPDATE_SUBSCR_HELP
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100641 "Create a subscriber entry\n")
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100642{
643 const char *imsi = argv[0];
644
645 struct gsm_subscriber *subscr;
646
647 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100648 if (subscr) {
649 vty_out(vty, "%% subscriber record already exists for %s%s",
650 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100651 return CMD_WARNING;
652 }
653
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100654 subscr = gprs_subscr_get_or_create(imsi);
655 subscr->keep_in_ram = 1;
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100656 subscr_put(subscr);
657
658 return CMD_SUCCESS;
659}
660
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100661DEFUN(update_subscr_destroy, update_subscr_destroy_cmd,
662 UPDATE_SUBSCR_STR "destroy",
663 UPDATE_SUBSCR_HELP
664 "Destroy a subscriber entry\n")
665{
666 const char *imsi = argv[0];
667
668 struct gsm_subscriber *subscr;
669
670 subscr = gprs_subscr_get_by_imsi(imsi);
671 if (!subscr) {
672 vty_out(vty, "%% subscriber record does not exist for %s%s",
673 imsi, VTY_NEWLINE);
674 return CMD_WARNING;
675 }
676
677 subscr->keep_in_ram = 0;
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100678 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100679 gprs_subscr_cancel(subscr);
680 if (subscr->use_count > 1)
681 vty_out(vty, "%% subscriber is still in use%s",
682 VTY_NEWLINE);
683 subscr_put(subscr);
684
685 return CMD_SUCCESS;
686}
687
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100688#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
689 "unknown-subscriber|roaming-not-allowed"
690
691#define UL_ERR_HELP \
692 "Force error code SystemFailure\n" \
693 "Force error code DataMissing\n" \
694 "Force error code UnexpectedDataValue\n" \
695 "Force error code UnknownSubscriber\n" \
696 "Force error code RoamingNotAllowed\n"
697
698DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
699 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
700 UPDATE_SUBSCR_HELP
701 "Complete the update location procedure\n"
702 "The update location request succeeded\n"
703 UL_ERR_HELP)
704{
705 const char *imsi = argv[0];
706 const char *ret_code_str = argv[1];
707
708 struct gsm_subscriber *subscr;
709
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100710 const struct value_string cause_mapping[] = {
711 { GMM_CAUSE_NET_FAIL, "system-failure" },
712 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
713 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
714 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
715 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
716 { 0, NULL }
717 };
718
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100719 subscr = gprs_subscr_get_by_imsi(imsi);
720 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100721 vty_out(vty, "%% unable to get subscriber record for %s%s",
722 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100723 return CMD_WARNING;
724 }
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100725
726 if (strcmp(ret_code_str, "ok") == 0) {
727 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100728 subscr->authorized = 1;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100729 } else {
730 subscr->sgsn_data->error_cause =
731 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100732 subscr->authorized = 0;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100733 }
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100734
735 gprs_subscr_update(subscr);
736
737 subscr_put(subscr);
738
739 return CMD_SUCCESS;
740}
741
742DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
743 UPDATE_SUBSCR_STR "update-auth-info",
744 UPDATE_SUBSCR_HELP
745 "Complete the send authentication info procedure\n")
746{
747 const char *imsi = argv[0];
748
749 struct gsm_subscriber *subscr;
750
751 subscr = gprs_subscr_get_by_imsi(imsi);
752 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100753 vty_out(vty, "%% unable to get subscriber record for %s%s",
754 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100755 return CMD_WARNING;
756 }
757
758 gprs_subscr_update_auth_info(subscr);
759
760 subscr_put(subscr);
761
762 return CMD_SUCCESS;
763}
764
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100765DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
766 "gsup remote-ip A.B.C.D",
767 "GSUP Parameters\n"
768 "Set the IP address of the remote GSUP server\n"
769 "IPv4 Address\n")
770{
771 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
772
773 return CMD_SUCCESS;
774}
775
776DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
777 "gsup remote-port <0-65535>",
778 "GSUP Parameters\n"
779 "Set the TCP port of the remote GSUP server\n"
780 "Remote TCP port\n")
781{
782 g_cfg->gsup_server_port = atoi(argv[0]);
783
784 return CMD_SUCCESS;
785}
786
Harald Welte288be162010-05-01 16:48:27 +0200787int sgsn_vty_init(void)
788{
Harald Welted193cb32010-05-17 22:58:03 +0200789 install_element_ve(&show_sgsn_cmd);
790 //install_element_ve(&show_mmctx_tlli_cmd);
791 install_element_ve(&show_mmctx_imsi_cmd);
792 install_element_ve(&show_mmctx_all_cmd);
793 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100794 install_element_ve(&show_subscr_cache_cmd);
795
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100796 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100797 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100798 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100799 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100800 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
801 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200802
803 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
804 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeck36722e12013-10-29 09:30:30 +0100805 vty_install_default(SGSN_NODE);
Harald Weltee300d002010-06-02 12:41:34 +0200806 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Welted193cb32010-05-17 22:58:03 +0200807 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
808 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
809 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Welte7f6da482013-03-19 11:00:13 +0100810 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte3dfb5492013-03-19 11:48:54 +0100811 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck39f040d2014-12-18 12:46:47 +0100812 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
813 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Jacob Erlbeckcb1db8b2015-02-03 13:47:53 +0100814 install_element(SGSN_NODE, &cfg_apn_ggsn_cmd);
815 install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200816
817 return 0;
818}
819
820int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
821{
822 int rc;
823
824 g_cfg = cfg;
Harald Welte7f6da482013-03-19 11:00:13 +0100825
Harald Weltedcccb182010-05-16 20:52:23 +0200826 rc = vty_read_config_file(config_file, NULL);
Harald Welte288be162010-05-01 16:48:27 +0200827 if (rc < 0) {
828 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
829 return rc;
830 }
831
832 return 0;
833}