blob: 7ea8890b3c233fb8c8b835dd853e0039cf927a6f [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) {
503 vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'%s",
504 pdp->context_id, pdp->pdp_type, pdp->apn_str,
505 VTY_NEWLINE);
506 }
507
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100508 /* print the expiration time of a subscriber */
509 if (subscr->expire_lu) {
510 strftime(expire_time, sizeof(expire_time),
511 "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
512 expire_time[sizeof(expire_time) - 1] = '\0';
513 vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
514 }
515
516 if (subscr->flags)
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100517 vty_out(vty, " Flags: %s%s%s%s%s%s",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100518 subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
519 "FIRST_CONTACT " : "",
520 subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
521 "CANCELLED " : "",
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100522 subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
523 "UPDATE_LOCATION_PENDING " : "",
524 subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
525 "AUTH_INFO_PENDING " : "",
Jacob Erlbeckb3982c12015-01-06 16:32:41 +0100526 subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
527 "ENABLE_PURGE " : "",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100528 VTY_NEWLINE);
529
530 vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
531}
532
533DEFUN(show_subscr_cache,
534 show_subscr_cache_cmd,
535 "show subscriber cache",
536 SHOW_STR "Show information about subscribers\n"
537 "Display contents of subscriber cache\n")
538{
539 struct gsm_subscriber *subscr;
540
541 llist_for_each_entry(subscr, &active_subscribers, entry) {
542 vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
543 subscr_dump_full_vty(vty, subscr, 0);
544 }
545
546 return CMD_SUCCESS;
547}
548
549#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI "
550#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \
551 "Use the IMSI to select the subscriber\n" \
552 "The IMSI\n"
553
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100554#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n"
555
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100556DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd,
557 UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC",
558 UPDATE_SUBSCR_HELP
559 UPDATE_SUBSCR_INSERT_HELP
560 "Update authentication triplet\n"
561 "Triplet index\n"
562 "Set SRES value\nSRES value (4 byte) in hex\n"
563 "Set RAND value\nRAND value (16 byte) in hex\n"
564 "Set Kc value\nKc value (8 byte) in hex\n")
565{
566 const char *imsi = argv[0];
567 const int cksn = atoi(argv[1]) - 1;
568 const char *sres_str = argv[2];
569 const char *rand_str = argv[3];
570 const char *kc_str = argv[4];
571 struct gsm_auth_tuple at = {0,};
572
573 struct gsm_subscriber *subscr;
574
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100575 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100576 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100577 vty_out(vty, "%% unable get subscriber record for %s%s",
578 imsi, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100579 return CMD_WARNING;
580 }
581
582 OSMO_ASSERT(subscr->sgsn_data);
583
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100584 if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100585 vty_out(vty, "%% invalid SRES value '%s'%s",
586 sres_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100587 goto failed;
588 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100589 if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100590 vty_out(vty, "%% invalid RAND value '%s'%s",
591 rand_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100592 goto failed;
593 }
Jacob Erlbeckf7f55bd2015-01-05 09:43:51 +0100594 if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100595 vty_out(vty, "%% invalid Kc value '%s'%s",
596 kc_str, VTY_NEWLINE);
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100597 goto failed;
598 }
599 at.key_seq = cksn;
600
601 subscr->sgsn_data->auth_triplets[cksn] = at;
602 subscr->sgsn_data->auth_triplets_updated = 1;
603
604 subscr_put(subscr);
605
606 return CMD_SUCCESS;
607
608failed:
609 subscr_put(subscr);
610 return CMD_SUCCESS;
611}
612
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100613DEFUN(update_subscr_cancel, update_subscr_cancel_cmd,
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100614 UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100615 UPDATE_SUBSCR_HELP
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100616 "Cancel (remove) subscriber record\n"
617 "The MS moved to another SGSN\n"
618 "The subscription is no longer valid\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100619{
620 const char *imsi = argv[0];
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100621 const char *cancel_type = argv[1];
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100622
623 struct gsm_subscriber *subscr;
624
625 subscr = gprs_subscr_get_by_imsi(imsi);
626 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100627 vty_out(vty, "%% no subscriber record for %s%s",
628 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100629 return CMD_WARNING;
630 }
631
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100632 if (strcmp(cancel_type, "update-procedure") == 0)
633 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
634 else
635 subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
636
Jacob Erlbeck7a7d8812015-01-23 13:52:55 +0100637 gprs_subscr_cancel(subscr);
638 subscr_put(subscr);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100639
640 return CMD_SUCCESS;
641}
642
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100643DEFUN(update_subscr_create, update_subscr_create_cmd,
644 UPDATE_SUBSCR_STR "create",
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100645 UPDATE_SUBSCR_HELP
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100646 "Create a subscriber entry\n")
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100647{
648 const char *imsi = argv[0];
649
650 struct gsm_subscriber *subscr;
651
652 subscr = gprs_subscr_get_by_imsi(imsi);
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100653 if (subscr) {
654 vty_out(vty, "%% subscriber record already exists for %s%s",
655 imsi, VTY_NEWLINE);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100656 return CMD_WARNING;
657 }
658
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100659 subscr = gprs_subscr_get_or_create(imsi);
660 subscr->keep_in_ram = 1;
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100661 subscr_put(subscr);
662
663 return CMD_SUCCESS;
664}
665
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100666DEFUN(update_subscr_destroy, update_subscr_destroy_cmd,
667 UPDATE_SUBSCR_STR "destroy",
668 UPDATE_SUBSCR_HELP
669 "Destroy a subscriber entry\n")
670{
671 const char *imsi = argv[0];
672
673 struct gsm_subscriber *subscr;
674
675 subscr = gprs_subscr_get_by_imsi(imsi);
676 if (!subscr) {
677 vty_out(vty, "%% subscriber record does not exist for %s%s",
678 imsi, VTY_NEWLINE);
679 return CMD_WARNING;
680 }
681
682 subscr->keep_in_ram = 0;
Jacob Erlbeck3b0d0c02015-01-27 14:56:40 +0100683 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100684 gprs_subscr_cancel(subscr);
685 if (subscr->use_count > 1)
686 vty_out(vty, "%% subscriber is still in use%s",
687 VTY_NEWLINE);
688 subscr_put(subscr);
689
690 return CMD_SUCCESS;
691}
692
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100693#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
694 "unknown-subscriber|roaming-not-allowed"
695
696#define UL_ERR_HELP \
697 "Force error code SystemFailure\n" \
698 "Force error code DataMissing\n" \
699 "Force error code UnexpectedDataValue\n" \
700 "Force error code UnknownSubscriber\n" \
701 "Force error code RoamingNotAllowed\n"
702
703DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
704 UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
705 UPDATE_SUBSCR_HELP
706 "Complete the update location procedure\n"
707 "The update location request succeeded\n"
708 UL_ERR_HELP)
709{
710 const char *imsi = argv[0];
711 const char *ret_code_str = argv[1];
712
713 struct gsm_subscriber *subscr;
714
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100715 const struct value_string cause_mapping[] = {
716 { GMM_CAUSE_NET_FAIL, "system-failure" },
717 { GMM_CAUSE_INV_MAND_INFO, "data-missing" },
718 { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" },
719 { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" },
720 { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" },
721 { 0, NULL }
722 };
723
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100724 subscr = gprs_subscr_get_by_imsi(imsi);
725 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100726 vty_out(vty, "%% unable to get subscriber record for %s%s",
727 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100728 return CMD_WARNING;
729 }
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100730
731 if (strcmp(ret_code_str, "ok") == 0) {
732 subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100733 subscr->authorized = 1;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100734 } else {
735 subscr->sgsn_data->error_cause =
736 get_string_value(cause_mapping, ret_code_str);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100737 subscr->authorized = 0;
Jacob Erlbeckf96779f2015-01-19 11:10:04 +0100738 }
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100739
740 gprs_subscr_update(subscr);
741
742 subscr_put(subscr);
743
744 return CMD_SUCCESS;
745}
746
747DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
748 UPDATE_SUBSCR_STR "update-auth-info",
749 UPDATE_SUBSCR_HELP
750 "Complete the send authentication info procedure\n")
751{
752 const char *imsi = argv[0];
753
754 struct gsm_subscriber *subscr;
755
756 subscr = gprs_subscr_get_by_imsi(imsi);
757 if (!subscr) {
Jacob Erlbeckfa7ac452015-01-19 14:29:43 +0100758 vty_out(vty, "%% unable to get subscriber record for %s%s",
759 imsi, VTY_NEWLINE);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100760 return CMD_WARNING;
761 }
762
763 gprs_subscr_update_auth_info(subscr);
764
765 subscr_put(subscr);
766
767 return CMD_SUCCESS;
768}
769
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100770DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd,
771 "gsup remote-ip A.B.C.D",
772 "GSUP Parameters\n"
773 "Set the IP address of the remote GSUP server\n"
774 "IPv4 Address\n")
775{
776 inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr);
777
778 return CMD_SUCCESS;
779}
780
781DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
782 "gsup remote-port <0-65535>",
783 "GSUP Parameters\n"
784 "Set the TCP port of the remote GSUP server\n"
785 "Remote TCP port\n")
786{
787 g_cfg->gsup_server_port = atoi(argv[0]);
788
789 return CMD_SUCCESS;
790}
791
Holger Hans Peter Freyther37391482015-02-06 16:23:29 +0100792DEFUN(cfg_apn_name, cfg_apn_name_cmd,
793 "access-point-name NAME",
794 "Configure a global list of allowed APNs\n"
795 "Add this NAME to the list\n")
796{
797 return add_apn_ggsn_mapping(vty, argv[0], "", 0);
798}
799
800DEFUN(cfg_no_apn_name, cfg_no_apn_name_cmd,
801 "no access-point-name NAME",
802 NO_STR "Configure a global list of allowed APNs\n"
803 "Remove entry with NAME\n")
804{
805 struct apn_ctx *apn_ctx = sgsn_apn_ctx_by_name(argv[0], "");
806 if (!apn_ctx)
807 return CMD_SUCCESS;
808
809 sgsn_apn_ctx_free(apn_ctx);
810 return CMD_SUCCESS;
811}
812
Harald Welte55fe0552010-05-01 16:48:27 +0200813int sgsn_vty_init(void)
814{
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200815 install_element_ve(&show_sgsn_cmd);
816 //install_element_ve(&show_mmctx_tlli_cmd);
817 install_element_ve(&show_mmctx_imsi_cmd);
818 install_element_ve(&show_mmctx_all_cmd);
819 install_element_ve(&show_pdpctx_all_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100820 install_element_ve(&show_subscr_cache_cmd);
821
Jacob Erlbeckb1332b62014-12-08 15:52:00 +0100822 install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
Jacob Erlbeck90e3ead2015-01-19 14:11:46 +0100823 install_element(ENABLE_NODE, &update_subscr_create_cmd);
Jacob Erlbeckeafb8492015-01-27 12:41:19 +0100824 install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
Jacob Erlbeckc16c3502014-11-11 14:01:48 +0100825 install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
Jacob Erlbeck828059f2014-11-28 14:55:25 +0100826 install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
827 install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200828
829 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
830 install_node(&sgsn_node, config_write_sgsn);
Jacob Erlbeckf414e852013-10-29 09:30:30 +0100831 vty_install_default(SGSN_NODE);
Harald Weltee0aea392010-06-02 12:41:34 +0200832 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200833 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
834 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
835 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Weltea0879c12013-03-19 11:00:13 +0100836 install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
Harald Welte2b9693d2013-03-19 11:48:54 +0100837 install_element(SGSN_NODE, &cfg_auth_policy_cmd);
Jacob Erlbeck233715c2014-12-18 12:46:47 +0100838 install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
839 install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
Jacob Erlbeck9b3ca642015-02-03 13:47:53 +0100840 install_element(SGSN_NODE, &cfg_apn_ggsn_cmd);
841 install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd);
Holger Hans Peter Freyther37391482015-02-06 16:23:29 +0100842 install_element(SGSN_NODE, &cfg_apn_name_cmd);
843 install_element(SGSN_NODE, &cfg_no_apn_name_cmd);
Harald Welte55fe0552010-05-01 16:48:27 +0200844
845 return 0;
846}
847
848int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
849{
850 int rc;
851
852 g_cfg = cfg;
Harald Weltea0879c12013-03-19 11:00:13 +0100853
Harald Welte40152872010-05-16 20:52:23 +0200854 rc = vty_read_config_file(config_file, NULL);
Harald Welte55fe0552010-05-01 16:48:27 +0200855 if (rc < 0) {
856 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
857 return rc;
858 }
859
860 return 0;
861}