blob: be575d337bd6b4cc3e28ed977133bcef751faf8d [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;
Jacob Erlbeck9b3ca642015-02-03 13:47:53 +0100129 struct apn_ctx *actx;
Harald Welte55fe0552010-05-01 16:48:27 +0200130
131 vty_out(vty, "sgsn%s", VTY_NEWLINE);
132
Harald Weltee0aea392010-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 Weltec1f6bfe2010-05-17 22:58:03 +0200136 llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
Harald Welte31f0a232010-05-19 15:09:09 +0200137 vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200138 inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
Harald Welte31f0a232010-05-19 15:09:09 +0200139 vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200140 gctx->gtp_version, VTY_NEWLINE);
Harald Welte55fe0552010-05-01 16:48:27 +0200141 }
142
Harald Welte2b9693d2013-03-19 11:48:54 +0100143 vty_out(vty, " auth-policy %s%s",
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100144 get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
145 VTY_NEWLINE);
Jacob Erlbeck233715c2014-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 Weltea0879c12013-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 Erlbeck9b3ca642015-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 Welte55fe0552010-05-01 16:48:27 +0200167 return CMD_SUCCESS;
168}
169
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100170#define SGSN_STR "Configure the SGSN\n"
171#define GGSN_STR "Configure the GGSN information\n"
Harald Weltee0aea392010-06-02 12:41:34 +0200172
173DEFUN(cfg_sgsn, cfg_sgsn_cmd,
174 "sgsn",
175 SGSN_STR)
Harald Welte55fe0552010-05-01 16:48:27 +0200176{
177 vty->node = SGSN_NODE;
178 return CMD_SUCCESS;
179}
180
Harald Weltee0aea392010-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 Freytherf403c482011-11-05 15:21:16 +0100184 "Set the IP address for the local GTP bind\n"
185 "IPv4 Address\n")
Harald Weltee0aea392010-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 Weltec1f6bfe2010-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 Freytherf403c482011-11-05 15:21:16 +0100194 GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200195{
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 Welte55fe0552010-05-01 16:48:27 +0200198
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200199 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte55fe0552010-05-01 16:48:27 +0200200
Harald Weltec1f6bfe2010-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 Welteeb471c92010-05-18 14:32:29 +0200210 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-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 Freytherf403c482011-11-05 15:21:16 +0100218 GGSN_STR "GGSN Number\n" "GTP Version\n"
219 "Version 0\n" "Version 1\n")
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200220{
221 uint32_t id = atoi(argv[0]);
Harald Welteeb471c92010-05-18 14:32:29 +0200222 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Weltec1f6bfe2010-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 Erlbeck9b3ca642015-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 Weltec1f6bfe2010-05-17 22:58:03 +0200260DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd,
261 "apn APNAME ggsn <0-255>",
Jacob Erlbeck9b3ca642015-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 Weltec1f6bfe2010-05-17 22:58:03 +0200265{
Jacob Erlbeck9b3ca642015-02-03 13:47:53 +0100266
267 return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1]));
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200268}
Jacob Erlbeck9b3ca642015-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 Weltec1f6bfe2010-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 Welte3ba2ce12010-06-09 15:50:45 +0200286 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Weltec1f6bfe2010-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 Erlbeckd781c7a2014-10-13 10:32:00 +0200294 const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200295 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Jacob Erlbeckd781c7a2014-10-13 10:32:00 +0200296 pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Welte493ba622010-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 Welte0fe506b2010-06-10 00:20:12 +0200303 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Weltec1f6bfe2010-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 Welte8a035af2010-05-18 10:57:45 +0200319 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
320
Harald Weltec1f6bfe2010-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 Erlbeck42a1aaf2014-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 Weltec1f6bfe2010-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 Weltec1f6bfe2010-05-17 22:58:03 +0200396DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
397 "show pdp-context all",
Holger Hans Peter Freytherf403c482011-11-05 15:21:16 +0100398 SHOW_STR "Display information on PDP Context\n" "Show everything\n")
Harald Weltec1f6bfe2010-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 Welte55fe0552010-05-01 16:48:27 +0200407
Harald Weltea0879c12013-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 Erlbeck4760eae2014-10-24 15:11:03 +0200421 rc = sgsn_acl_add(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100422 else
Jacob Erlbeck4760eae2014-10-24 15:11:03 +0200423 rc = sgsn_acl_del(imsi, g_cfg);
Harald Weltea0879c12013-03-19 11:00:13 +0100424
425 if (rc < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100426 vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
427
Harald Weltea0879c12013-03-19 11:00:13 +0100428 return CMD_WARNING;
429 }
430
431 return CMD_SUCCESS;
432}
433
Harald Welte2b9693d2013-03-19 11:48:54 +0100434DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100435 "auth-policy (accept-all|closed|acl-only|remote)",
Harald Welte2b9693d2013-03-19 11:48:54 +0100436 "Autorization Policy of SGSN\n"
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100437 "Accept all IMSIs (DANGEROUS)\n"
438 "Accept only home network subscribers or those in the ACL\n"
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100439 "Accept only subscribers in the ACL\n"
440 "Use remote subscription data only (HLR)\n")
Harald Welte2b9693d2013-03-19 11:48:54 +0100441{
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100442 int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
Jacob Erlbeckd04f7cc2014-11-12 10:18:09 +0100443 OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeckd7b77732014-11-04 10:08:37 +0100444 g_cfg->auth_policy = val;
Jacob Erlbeck16b17ed2014-12-17 13:20:08 +0100445 g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
Jacob Erlbeck6ff7f642014-12-19 18:08:48 +0100446 g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
Harald Welte2b9693d2013-03-19 11:48:54 +0100447
448 return CMD_SUCCESS;
449}
450
Jacob Erlbeckc16c3502014-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 Erlbeckb1332b62014-12-08 15:52:00 +0100457 struct gsm_auth_tuple *at;
458 int at_idx;
Jacob Erlbeck94a346a2014-12-17 14:03:35 +0100459 struct sgsn_subscriber_pdp_data *pdp;
Jacob Erlbeckc16c3502014-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);
Holger Hans Peter Freyther0f0efd02015-04-23 16:58:33 -0400474 if (subscr->sgsn_data->msisdn_len > 0)
475 vty_out(vty, " MSISDN (BCD): %s%s",
476 osmo_hexdump(subscr->sgsn_data->msisdn,
477 subscr->sgsn_data->msisdn_len),
478 VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100479
480 if (strlen(subscr->equipment.imei) > 0)
481 vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
482
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100483 for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets);
484 at_idx++) {
485 at = &subscr->sgsn_data->auth_triplets[at_idx];
486 if (at->key_seq == GSM_KEY_SEQ_INVAL)
487 continue;
488
489 vty_out(vty, " A3A8 tuple (used %d times): ",
490 at->use_count);
491 vty_out(vty, " seq # : %d, ",
492 at->key_seq);
493 vty_out(vty, " RAND : %s, ",
494 osmo_hexdump(at->rand, sizeof(at->rand)));
495 vty_out(vty, " SRES : %s, ",
496 osmo_hexdump(at->sres, sizeof(at->sres)));
497 vty_out(vty, " Kc : %s%s",
498 osmo_hexdump(at->kc, sizeof(at->kc)),
499 VTY_NEWLINE);
500 }
501
Jacob Erlbeck94a346a2014-12-17 14:03:35 +0100502 llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
Holger Hans Peter Freyther703d7c92015-04-23 16:59:04 -0400503 vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s",
Jacob Erlbeck94a346a2014-12-17 14:03:35 +0100504 pdp->context_id, pdp->pdp_type, pdp->apn_str,
Holger Hans Peter Freyther703d7c92015-04-23 16:59:04 -0400505 osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len),
Jacob Erlbeck94a346a2014-12-17 14:03:35 +0100506 VTY_NEWLINE);
507 }
508
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100509 /* print the expiration time of a subscriber */
510 if (subscr->expire_lu) {
511 strftime(expire_time, sizeof(expire_time),
512 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
513 expire_time[sizeof(expire_time) - 1] = '\0';
514 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
515 }
516
517 if (subscr->flags)
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100518 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100519 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
520 "FIRST_CONTACT " : "",
521 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
522 "CANCELLED " : "",
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100523 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
524 "UPDATE_LOCATION_PENDING " : "",
525 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
526 "AUTH_INFO_PENDING " : "",
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100527 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
528 "ENABLE_PURGE " : "",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100529 VTY_NEWLINE);
530
531 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
532}
533
534DEFUN(show_subscr_cache,
535 show_subscr_cache_cmd,
536 "show subscriber cache",
537 SHOW_STR "Show information about subscribers\n"
538 "Display contents of subscriber cache\n")
539{
540 struct gsm_subscriber *subscr;
541
542 llist_for_each_entry(subscr, &active_subscribers, entry) {
543 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
544 subscr_dump_full_vty(vty, subscr, 0);
545 }
546
547 return CMD_SUCCESS;
548}
549
550#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
551#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
552 "Use the IMSI to select the subscriber\n" \
553 "The IMSI\n"
554
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100555#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
556
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100557DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
558 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
559 UPDATE_SUBSCR_HELP
560 UPDATE_SUBSCR_INSERT_HELP
561 "Update authentication triplet\n"
562 "Triplet index\n"
563 "Set SRES value\nSRES value (4 byte) in hex\n"
564 "Set RAND value\nRAND value (16 byte) in hex\n"
565 "Set Kc value\nKc value (8 byte) in hex\n")
566{
567 const char *imsi = argv[0];
568 const int cksn = atoi(argv[1]) - 1;
569 const char *sres_str = argv[2];
570 const char *rand_str = argv[3];
571 const char *kc_str = argv[4];
572 struct gsm_auth_tuple at = {0,};
573
574 struct gsm_subscriber *subscr;
575
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100576 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100577 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100578 vty_out(vty, "%% unable get subscriber record for %s%s",
579 imsi, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100580 return CMD_WARNING;
581 }
582
583 OSMO_ASSERT(subscr->sgsn_data);
584
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100585 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100586 vty_out(vty, "%% invalid SRES value '%s'%s",
587 sres_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100588 goto failed;
589 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100590 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100591 vty_out(vty, "%% invalid RAND value '%s'%s",
592 rand_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100593 goto failed;
594 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100595 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100596 vty_out(vty, "%% invalid Kc value '%s'%s",
597 kc_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100598 goto failed;
599 }
600 at.key_seq = cksn;
601
602 subscr->sgsn_data->auth_triplets[cksn] = at;
603 subscr->sgsn_data->auth_triplets_updated = 1;
604
605 subscr_put(subscr);
606
607 return CMD_SUCCESS;
608
609failed:
610 subscr_put(subscr);
611 return CMD_SUCCESS;
612}
613
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100614DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100615 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100616 UPDATE_SUBSCR_HELP
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100617 "Cancel (remove) subscriber record\n"
618 "The MS moved to another SGSN\n"
619 "The subscription is no longer valid\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100620{
621 const char *imsi = argv[0];
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100622 const char *cancel_type = argv[1];
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100623
624 struct gsm_subscriber *subscr;
625
626 subscr = gprs_subscr_get_by_imsi(imsi);
627 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100628 vty_out(vty, "%% no subscriber record for %s%s",
629 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100630 return CMD_WARNING;
631 }
632
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100633 if (strcmp(cancel_type, "update-procedure") == 0)
634 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
635 else
636 subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
637
Jacob Erlbeck7a7d8812015-01-23 13:52:55 +0100638 gprs_subscr_cancel(subscr);
639 subscr_put(subscr);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100640
641 return CMD_SUCCESS;
642}
643
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100644DEFUN(update_subscr_create, update_subscr_create_cmd,
645 UPDATE_SUBSCR_STR "create",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100646 UPDATE_SUBSCR_HELP
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100647 "Create a subscriber entry\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100648{
649 const char *imsi = argv[0];
650
651 struct gsm_subscriber *subscr;
652
653 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100654 if (subscr) {
655 vty_out(vty, "%% subscriber record already exists for %s%s",
656 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100657 return CMD_WARNING;
658 }
659
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100660 subscr = gprs_subscr_get_or_create(imsi);
661 subscr->keep_in_ram = 1;
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100662 subscr_put(subscr);
663
664 return CMD_SUCCESS;
665}
666
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100667DEFUN(update_subscr_destroy, update_subscr_destroy_cmd,
668 UPDATE_SUBSCR_STR "destroy",
669 UPDATE_SUBSCR_HELP
670 "Destroy a subscriber entry\n")
671{
672 const char *imsi = argv[0];
673
674 struct gsm_subscriber *subscr;
675
676 subscr = gprs_subscr_get_by_imsi(imsi);
677 if (!subscr) {
678 vty_out(vty, "%% subscriber record does not exist for %s%s",
679 imsi, VTY_NEWLINE);
680 return CMD_WARNING;
681 }
682
683 subscr->keep_in_ram = 0;
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100684 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100685 gprs_subscr_cancel(subscr);
686 if (subscr->use_count > 1)
687 vty_out(vty, "%% subscriber is still in use%s",
688 VTY_NEWLINE);
689 subscr_put(subscr);
690
691 return CMD_SUCCESS;
692}
693
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100694#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
695 "unknown-subscriber|roaming-not-allowed"
696
697#define UL_ERR_HELP \
698 "Force error code SystemFailure\n" \
699 "Force error code DataMissing\n" \
700 "Force error code UnexpectedDataValue\n" \
701 "Force error code UnknownSubscriber\n" \
702 "Force error code RoamingNotAllowed\n"
703
704DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
705 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
706 UPDATE_SUBSCR_HELP
707 "Complete the update location procedure\n"
708 "The update location request succeeded\n"
709 UL_ERR_HELP)
710{
711 const char *imsi = argv[0];
712 const char *ret_code_str = argv[1];
713
714 struct gsm_subscriber *subscr;
715
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100716 const struct value_string cause_mapping[] = {
717 { GMM_CAUSE_NET_FAIL, "system-failure" },
718 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
719 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
720 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
721 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
722 { 0, NULL }
723 };
724
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100725 subscr = gprs_subscr_get_by_imsi(imsi);
726 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100727 vty_out(vty, "%% unable to get subscriber record for %s%s",
728 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100729 return CMD_WARNING;
730 }
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100731
732 if (strcmp(ret_code_str, "ok") == 0) {
733 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100734 subscr->authorized = 1;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100735 } else {
736 subscr->sgsn_data->error_cause =
737 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100738 subscr->authorized = 0;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100739 }
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100740
741 gprs_subscr_update(subscr);
742
743 subscr_put(subscr);
744
745 return CMD_SUCCESS;
746}
747
748DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
749 UPDATE_SUBSCR_STR "update-auth-info",
750 UPDATE_SUBSCR_HELP
751 "Complete the send authentication info procedure\n")
752{
753 const char *imsi = argv[0];
754
755 struct gsm_subscriber *subscr;
756
757 subscr = gprs_subscr_get_by_imsi(imsi);
758 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100759 vty_out(vty, "%% unable to get subscriber record for %s%s",
760 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100761 return CMD_WARNING;
762 }
763
764 gprs_subscr_update_auth_info(subscr);
765
766 subscr_put(subscr);
767
768 return CMD_SUCCESS;
769}
770
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100771DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
772 "gsup remote-ip A.B.C.D",
773 "GSUP Parameters\n"
774 "Set the IP address of the remote GSUP server\n"
775 "IPv4 Address\n")
776{
777 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
778
779 return CMD_SUCCESS;
780}
781
782DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
783 "gsup remote-port <0-65535>",
784 "GSUP Parameters\n"
785 "Set the TCP port of the remote GSUP server\n"
786 "Remote TCP port\n")
787{
788 g_cfg->gsup_server_port = atoi(argv[0]);
789
790 return CMD_SUCCESS;
791}
792
Holger Hans Peter Freyther37391482015-02-06 16:23:29 +0100793DEFUN(cfg_apn_name, cfg_apn_name_cmd,
794 "access-point-name NAME",
795 "Configure a global list of allowed APNs\n"
796 "Add this NAME to the list\n")
797{
798 return add_apn_ggsn_mapping(vty, argv[0], "", 0);
799}
800
801DEFUN(cfg_no_apn_name, cfg_no_apn_name_cmd,
802 "no access-point-name NAME",
803 NO_STR "Configure a global list of allowed APNs\n"
804 "Remove entry with NAME\n")
805{
806 struct apn_ctx *apn_ctx = sgsn_apn_ctx_by_name(argv[0], "");
807 if (!apn_ctx)
808 return CMD_SUCCESS;
809
810 sgsn_apn_ctx_free(apn_ctx);
811 return CMD_SUCCESS;
812}
813
Harald Welte55fe0552010-05-01 16:48:27 +0200814int sgsn_vty_init(void)
815{
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200816 install_element_ve(&show_sgsn_cmd);
817 //install_element_ve(&show_mmctx_tlli_cmd);
818 install_element_ve(&show_mmctx_imsi_cmd);
819 install_element_ve(&show_mmctx_all_cmd);
820 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100821 install_element_ve(&show_subscr_cache_cmd);
822
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100823 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100824 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100825 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100826 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100827 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
828 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200829
830 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
831 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeckf414e852013-10-29 09:30:30 +0100832 vty_install_default(SGSN_NODE);
Harald Weltee0aea392010-06-02 12:41:34 +0200833 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200834 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
835 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
836 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Weltea0879c12013-03-19 11:00:13 +0100837 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte2b9693d2013-03-19 11:48:54 +0100838 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100839 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
840 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Jacob Erlbeck9b3ca642015-02-03 13:47:53 +0100841 install_element(SGSN_NODE, &cfg_apn_ggsn_cmd);
842 install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd);
Holger Hans Peter Freyther37391482015-02-06 16:23:29 +0100843 install_element(SGSN_NODE, &cfg_apn_name_cmd);
844 install_element(SGSN_NODE, &cfg_no_apn_name_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200845
846 return 0;
847}
848
849int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
850{
851 int rc;
852
853 g_cfg = cfg;
Harald Weltea0879c12013-03-19 11:00:13 +0100854
Harald Welte40152872010-05-16 20:52:23 +0200855 rc = vty_read_config_file(config_file, NULL);
Harald Welte55fe0552010-05-01 16:48:27 +0200856 if (rc < 0) {
857 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
858 return rc;
859 }
860
861 return 0;
862}