blob: e8f2019506611f61d97e90c7ff8e2d21913b54de [file] [log] [blame]
Harald Welte288be162010-05-01 16:48:27 +02001/*
2 * (C) 2010 by Harald Welte <laforge@gnumonks.org>
3 * (C) 2010 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26
27#include <osmocore/talloc.h>
Harald Welted193cb32010-05-17 22:58:03 +020028#include <osmocore/utils.h>
Harald Weltecd4dd4d2010-05-18 17:20:49 +020029#include <osmocore/rate_ctr.h>
Harald Welte288be162010-05-01 16:48:27 +020030
31#include <openbsc/debug.h>
32#include <openbsc/sgsn.h>
33#include <openbsc/gprs_ns.h>
Harald Welted193cb32010-05-17 22:58:03 +020034#include <openbsc/gprs_sgsn.h>
Harald Welte62ab20c2010-05-14 18:59:17 +020035#include <openbsc/vty.h>
Harald Weltec5d4a0c2010-07-02 22:47:59 +020036#include <openbsc/gsm_04_08_gprs.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>
Harald Welte288be162010-05-01 16:48:27 +020040
Harald Welted193cb32010-05-17 22:58:03 +020041#include <pdp.h>
42
Harald Welte288be162010-05-01 16:48:27 +020043static struct sgsn_config *g_cfg = NULL;
44
Harald Weltec5d4a0c2010-07-02 22:47:59 +020045
46#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */
47static char *gprs_apn2str(uint8_t *apn, unsigned int len)
48{
49 static char apnbuf[GSM48_MAX_APN_LEN+1];
50 unsigned int i;
51
52 if (!apn)
53 return "";
54
55 if (len > sizeof(apnbuf)-1)
56 len = sizeof(apnbuf)-1;
57
58 memcpy(apnbuf, apn, len);
59 apnbuf[len] = '\0';
60
61 /* replace the domain name step sizes with dots */
62 while (i < len) {
63 unsigned int step = apnbuf[i];
64 apnbuf[i] = '.';
65 i += step+1;
66 }
67
68 return apnbuf+1;
69}
70
71static char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len)
72{
73 static char str[INET6_ADDRSTRLEN + 10];
74
75 if (!pdpa || len < 2)
76 return "none";
77
78 switch (pdpa[0] & 0x0f) {
79 case PDP_TYPE_ORG_IETF:
80 switch (pdpa[1]) {
81 case PDP_TYPE_N_IETF_IPv4:
82 if (len < 2 + 4)
83 break;
84 strcpy(str, "IPv4 ");
85 inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5);
86 return str;
87 case PDP_TYPE_N_IETF_IPv6:
88 if (len < 2 + 8)
89 break;
90 strcpy(str, "IPv6 ");
91 inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5);
92 return str;
93 default:
94 break;
95 }
96 break;
97 case PDP_TYPE_ORG_ETSI:
98 if (pdpa[1] == PDP_TYPE_N_ETSI_PPP)
99 return "PPP";
100 break;
101 default:
102 break;
103 }
104
105 return "invalid";
106}
107
Harald Welte288be162010-05-01 16:48:27 +0200108static struct cmd_node sgsn_node = {
109 SGSN_NODE,
110 "%s(sgsn)#",
111 1,
112};
113
114static int config_write_sgsn(struct vty *vty)
115{
Harald Welte77289c22010-05-18 14:32:29 +0200116 struct sgsn_ggsn_ctx *gctx;
Harald Welte288be162010-05-01 16:48:27 +0200117
118 vty_out(vty, "sgsn%s", VTY_NEWLINE);
119
Harald Weltee300d002010-06-02 12:41:34 +0200120 vty_out(vty, " gtp local-ip %s%s",
121 inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE);
122
Harald Welted193cb32010-05-17 22:58:03 +0200123 llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
Harald Welteff3bde82010-05-19 15:09:09 +0200124 vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200125 inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
Harald Welteff3bde82010-05-19 15:09:09 +0200126 vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
Harald Welted193cb32010-05-17 22:58:03 +0200127 gctx->gtp_version, VTY_NEWLINE);
Harald Welte288be162010-05-01 16:48:27 +0200128 }
129
130 return CMD_SUCCESS;
131}
132
Harald Weltee300d002010-06-02 12:41:34 +0200133#define SGSN_STR "Configure the SGSN"
134
135DEFUN(cfg_sgsn, cfg_sgsn_cmd,
136 "sgsn",
137 SGSN_STR)
Harald Welte288be162010-05-01 16:48:27 +0200138{
139 vty->node = SGSN_NODE;
140 return CMD_SUCCESS;
141}
142
Harald Weltee300d002010-06-02 12:41:34 +0200143DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd,
144 "gtp local-ip A.B.C.D",
145 "GTP Parameters\n"
146 "Set the IP address for the local GTP bind\n")
147{
148 inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr);
149
150 return CMD_SUCCESS;
151}
152
Harald Welted193cb32010-05-17 22:58:03 +0200153DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd,
154 "ggsn <0-255> remote-ip A.B.C.D",
155 "")
156{
157 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200158 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welte288be162010-05-01 16:48:27 +0200159
Harald Welted193cb32010-05-17 22:58:03 +0200160 inet_aton(argv[1], &ggc->remote_addr);
Harald Welte288be162010-05-01 16:48:27 +0200161
Harald Welted193cb32010-05-17 22:58:03 +0200162 return CMD_SUCCESS;
163}
164
165#if 0
166DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd,
167 "ggsn <0-255> remote-port <0-65535>",
168 "")
169{
170 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200171 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200172 uint16_t port = atoi(argv[1]);
173
174}
175#endif
176
177DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd,
178 "ggsn <0-255> gtp-version (0|1)",
179 "")
180{
181 uint32_t id = atoi(argv[0]);
Harald Welte77289c22010-05-18 14:32:29 +0200182 struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200183
184 if (atoi(argv[1]))
185 ggc->gtp_version = 1;
186 else
187 ggc->gtp_version = 0;
188
189 return CMD_SUCCESS;
190}
191
192#if 0
193DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd,
194 "apn APNAME ggsn <0-255>",
195 "")
196{
197 struct apn_ctx **
198}
199#endif
200
201const struct value_string gprs_mm_st_strs[] = {
202 { GMM_DEREGISTERED, "DEREGISTERED" },
203 { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" },
204 { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" },
Harald Weltebffeff82010-06-09 15:50:45 +0200205 { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" },
Harald Welted193cb32010-05-17 22:58:03 +0200206 { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" },
207 { 0, NULL }
208};
209
210static void vty_dump_pdp(struct vty *vty, const char *pfx,
211 struct sgsn_pdp_ctx *pdp)
212{
213 vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
Harald Welte6abf94e2010-05-18 10:35:06 +0200214 pfx, pdp->mm->imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
Harald Weltec5d4a0c2010-07-02 22:47:59 +0200215 vty_out(vty, "%s APN: %s%s", pfx,
216 gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
217 VTY_NEWLINE);
218 vty_out(vty, "%s PDP Address: %s%s", pfx,
219 gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
220 VTY_NEWLINE);
Harald Welteefbdee92010-06-10 00:20:12 +0200221 vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
Harald Welted193cb32010-05-17 22:58:03 +0200222}
223
224static void vty_dump_mmctx(struct vty *vty, const char *pfx,
225 struct sgsn_mm_ctx *mm, int pdp)
226{
227 vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
228 pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
229 vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn,
230 mm->tlli, VTY_NEWLINE);
231 vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, "
232 "Cell ID: %u%s", pfx,
233 get_value_string(gprs_mm_st_strs, mm->mm_state),
234 mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
235 mm->cell_id, VTY_NEWLINE);
236
Harald Welte8acd88f2010-05-18 10:57:45 +0200237 vty_out_rate_ctr_group(vty, " ", mm->ctrg);
238
Harald Welted193cb32010-05-17 22:58:03 +0200239 if (pdp) {
240 struct sgsn_pdp_ctx *pdp;
241
242 llist_for_each_entry(pdp, &mm->pdp_list, list)
243 vty_dump_pdp(vty, " ", pdp);
244 }
245}
246
247DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
248 SHOW_STR "Display information about the SGSN")
249{
250 /* FIXME: statistics */
251 return CMD_SUCCESS;
252}
253
254#define MMCTX_STR "MM Context\n"
255#define INCLUDE_PDP_STR "Include PDP Context Information\n"
256
257#if 0
258DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd,
259 "show mm-context tlli HEX [pdp]",
260 SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR)
261{
262 uint32_t tlli;
263 struct sgsn_mm_ctx *mm;
264
265 tlli = strtoul(argv[0], NULL, 16);
266 mm = sgsn_mm_ctx_by_tlli(tlli);
267 if (!mm) {
268 vty_out(vty, "No MM context for TLLI %08x%s",
269 tlli, VTY_NEWLINE);
270 return CMD_WARNING;
271 }
272 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
273 return CMD_SUCCESS;
274}
275#endif
276
277DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd,
278 "show mm-context imsi IMSI [pdp]",
279 SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n"
280 INCLUDE_PDP_STR)
281{
282 struct sgsn_mm_ctx *mm;
283
284 mm = sgsn_mm_ctx_by_imsi(argv[0]);
285 if (!mm) {
286 vty_out(vty, "No MM context for IMSI %s%s",
287 argv[0], VTY_NEWLINE);
288 return CMD_WARNING;
289 }
290 vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0);
291 return CMD_SUCCESS;
292}
293
294DEFUN(swow_mmctx_all, show_mmctx_all_cmd,
295 "show mm-context all [pdp]",
296 SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR)
297{
298 struct sgsn_mm_ctx *mm;
299
300 llist_for_each_entry(mm, &sgsn_mm_ctxts, list)
301 vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0);
302
303 return CMD_SUCCESS;
304}
305
306DEFUN(show_ggsn, show_ggsn_cmd,
307 "show ggsn",
308 "")
309{
310
311}
312
313DEFUN(show_pdpctx_all, show_pdpctx_all_cmd,
314 "show pdp-context all",
315 SHOW_STR "Display information on PDP Context\n")
316{
317 struct sgsn_pdp_ctx *pdp;
318
319 llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list)
320 vty_dump_pdp(vty, "", pdp);
321
322 return CMD_SUCCESS;
323}
Harald Welte288be162010-05-01 16:48:27 +0200324
325int sgsn_vty_init(void)
326{
Harald Welted193cb32010-05-17 22:58:03 +0200327 install_element_ve(&show_sgsn_cmd);
328 //install_element_ve(&show_mmctx_tlli_cmd);
329 install_element_ve(&show_mmctx_imsi_cmd);
330 install_element_ve(&show_mmctx_all_cmd);
331 install_element_ve(&show_pdpctx_all_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200332
333 install_element(CONFIG_NODE, &cfg_sgsn_cmd);
334 install_node(&sgsn_node, config_write_sgsn);
335 install_default(SGSN_NODE);
Harald Welte62ab20c2010-05-14 18:59:17 +0200336 install_element(SGSN_NODE, &ournode_exit_cmd);
Harald Welte54f74242010-05-14 19:11:04 +0200337 install_element(SGSN_NODE, &ournode_end_cmd);
Harald Weltee300d002010-06-02 12:41:34 +0200338 install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
Harald Welted193cb32010-05-17 22:58:03 +0200339 install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
340 //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
341 install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
Harald Welte288be162010-05-01 16:48:27 +0200342
343 return 0;
344}
345
346int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
347{
348 int rc;
349
350 g_cfg = cfg;
Harald Weltedcccb182010-05-16 20:52:23 +0200351 rc = vty_read_config_file(config_file, NULL);
Harald Welte288be162010-05-01 16:48:27 +0200352 if (rc < 0) {
353 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
354 return rc;
355 }
356
357 return 0;
358}