blob: be575d337bd6b4cc3e28ed977133bcef751faf8d [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);
Holger Hans Peter Freytherf7b38262015-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 Erlbeck207f4a52014-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 Erlbeck7921ab12014-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 Erlbeck0e8add62014-12-17 14:03:35 +0100502 llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
Holger Hans Peter Freytherd05e0692015-04-23 16:59:04 -0400503 vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s",
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100504 pdp->context_id, pdp->pdp_type, pdp->apn_str,
Holger Hans Peter Freytherd05e0692015-04-23 16:59:04 -0400505 osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len),
Jacob Erlbeck0e8add62014-12-17 14:03:35 +0100506 VTY_NEWLINE);
507 }
508
Jacob Erlbeck207f4a52014-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 Erlbeck65fa3f72015-01-06 16:32:41 +0100518 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100519 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
520 "FIRST_CONTACT " : "",
521 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
522 "CANCELLED " : "",
Jacob Erlbeck98a95ac2014-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 Erlbeck65fa3f72015-01-06 16:32:41 +0100527 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
528 "ENABLE_PURGE " : "",
Jacob Erlbeck207f4a52014-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 Erlbeck7921ab12014-12-08 15:52:00 +0100555#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
556
Jacob Erlbeck7921ab12014-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 Erlbeckd9193432015-01-19 14:11:46 +0100576 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100577 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100578 vty_out(vty, "%% unable get subscriber record for %s%s",
579 imsi, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100580 return CMD_WARNING;
581 }
582
583 OSMO_ASSERT(subscr->sgsn_data);
584
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100585 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100586 vty_out(vty, "%% invalid SRES value '%s'%s",
587 sres_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100588 goto failed;
589 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100590 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100591 vty_out(vty, "%% invalid RAND value '%s'%s",
592 rand_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100593 goto failed;
594 }
Jacob Erlbeck17fb3d42015-01-05 09:43:51 +0100595 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100596 vty_out(vty, "%% invalid Kc value '%s'%s",
597 kc_str, VTY_NEWLINE);
Jacob Erlbeck7921ab12014-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 Erlbeck207f4a52014-11-11 14:01:48 +0100614DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100615 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100616 UPDATE_SUBSCR_HELP
Jacob Erlbeck8000e0e2015-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 Erlbeck207f4a52014-11-11 14:01:48 +0100620{
621 const char *imsi = argv[0];
Jacob Erlbeck8000e0e2015-01-27 14:56:40 +0100622 const char *cancel_type = argv[1];
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100623
624 struct gsm_subscriber *subscr;
625
626 subscr = gprs_subscr_get_by_imsi(imsi);
627 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100628 vty_out(vty, "%% no subscriber record for %s%s",
629 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100630 return CMD_WARNING;
631 }
632
Jacob Erlbeck8000e0e2015-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 Erlbeck37139e52015-01-23 13:52:55 +0100638 gprs_subscr_cancel(subscr);
639 subscr_put(subscr);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100640
641 return CMD_SUCCESS;
642}
643
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100644DEFUN(update_subscr_create, update_subscr_create_cmd,
645 UPDATE_SUBSCR_STR "create",
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100646 UPDATE_SUBSCR_HELP
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100647 "Create a subscriber entry\n")
Jacob Erlbeck207f4a52014-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 Erlbeck15cc8c82015-01-19 14:29:43 +0100654 if (subscr) {
655 vty_out(vty, "%% subscriber record already exists for %s%s",
656 imsi, VTY_NEWLINE);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100657 return CMD_WARNING;
658 }
659
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100660 subscr = gprs_subscr_get_or_create(imsi);
661 subscr->keep_in_ram = 1;
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100662 subscr_put(subscr);
663
664 return CMD_SUCCESS;
665}
666
Jacob Erlbecke988ae42015-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 Erlbeck8000e0e2015-01-27 14:56:40 +0100684 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbecke988ae42015-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 Erlbeck98a95ac2014-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 Erlbeckd6267d12015-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 Erlbeck98a95ac2014-11-28 14:55:25 +0100725 subscr = gprs_subscr_get_by_imsi(imsi);
726 if (!subscr) {
Jacob Erlbeck15cc8c82015-01-19 14:29:43 +0100727 vty_out(vty, "%% unable to get subscriber record for %s%s",
728 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100729 return CMD_WARNING;
730 }
Jacob Erlbeckd6267d12015-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 Erlbeck98a95ac2014-11-28 14:55:25 +0100734 subscr->authorized = 1;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100735 } else {
736 subscr->sgsn_data->error_cause =
737 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck98a95ac2014-11-28 14:55:25 +0100738 subscr->authorized = 0;
Jacob Erlbeckd6267d12015-01-19 11:10:04 +0100739 }
Jacob Erlbeck98a95ac2014-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 Erlbeck15cc8c82015-01-19 14:29:43 +0100759 vty_out(vty, "%% unable to get subscriber record for %s%s",
760 imsi, VTY_NEWLINE);
Jacob Erlbeck98a95ac2014-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 Erlbeck39f040d2014-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 Freyther9c20a5f2015-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 Welte288be162010-05-01 16:48:27 +0200814int sgsn_vty_init(void)
815{
Harald Welted193cb32010-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 Erlbeck207f4a52014-11-11 14:01:48 +0100821 install_element_ve(&show_subscr_cache_cmd);
822
Jacob Erlbeck7921ab12014-12-08 15:52:00 +0100823 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeckd9193432015-01-19 14:11:46 +0100824 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbecke988ae42015-01-27 12:41:19 +0100825 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeck207f4a52014-11-11 14:01:48 +0100826 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck98a95ac2014-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 Welte288be162010-05-01 16:48:27 +0200829
830 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
831 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeck36722e12013-10-29 09:30:30 +0100832 vty_install_default(SGSN_NODE);
Harald Weltee300d002010-06-02 12:41:34 +0200833 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Welted193cb32010-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 Welte7f6da482013-03-19 11:00:13 +0100837 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte3dfb5492013-03-19 11:48:54 +0100838 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck39f040d2014-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 Erlbeckcb1db8b2015-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 Freyther9c20a5f2015-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 Welte288be162010-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 Welte7f6da482013-03-19 11:00:13 +0100854
Harald Weltedcccb182010-05-16 20:52:23 +0200855 rc = vty_read_config_file(config_file, NULL);
Harald Welte288be162010-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}