blob: 54e1dc4c2e3ddc347a95d8fd68cd15af7ac689ae [file] [log] [blame]
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +01001/*
2 * (C) 2011-2012 by Holger Hans Peter Freyther
3 * (C) 2011-2012 by On-Waves
4 * (C) 2011 by Daniel Willmann
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <osmocom/core/talloc.h>
23
Harald Welteba874b82014-08-20 23:47:15 +020024#include <osmocom/ctrl/control_cmd.h>
25#include <osmocom/ctrl/control_if.h>
Harald Welte55dc31e2014-08-24 17:54:49 +020026#include <osmocom/ctrl/ports.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010027
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +020028#include <osmocom/vty/misc.h>
29
Harald Welteba874b82014-08-20 23:47:15 +020030#include <openbsc/ctrl.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010031#include <openbsc/bsc_nat.h>
Holger Hans Peter Freythercd485252014-08-29 10:58:12 +020032#include <openbsc/vty.h>
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +020033#include <openbsc/gsm_data.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010034
35#include <unistd.h>
Harald Welteba874b82014-08-20 23:47:15 +020036#include <string.h>
37#include <errno.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010038
39
40#define NAT_MAX_CTRL_ID 65535
41
42static struct bsc_nat *g_nat;
43
44static int bsc_id_unused(int id, struct bsc_connection *bsc)
45{
46 struct bsc_cmd_list *pending;
47
48 llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
49 if (pending->nat_id == id)
50 return 0;
51 }
52 return 1;
53}
54
55static int get_next_free_bsc_id(struct bsc_connection *bsc)
56{
57 int new_id, overflow = 0;
58
59 new_id = bsc->last_id;
60
61 do {
62 new_id++;
63 if (new_id == NAT_MAX_CTRL_ID) {
64 new_id = 1;
65 overflow++;
66 }
67
68 if (bsc_id_unused(new_id, bsc)) {
69 bsc->last_id = new_id;
70 return new_id;
71 }
72 } while (overflow != 2);
73
74 return -1;
75}
76
77void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
78{
79 llist_del(&pending->list_entry);
80 osmo_timer_del(&pending->timeout);
81 talloc_free(pending->cmd);
82 talloc_free(pending);
83}
84
85static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
86{
87 struct bsc_cmd_list *cmd_entry;
88 int id = atoi(id_str);
89 if (id == 0)
90 return NULL;
91
92 llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
93 if (cmd_entry->nat_id == id) {
94 return cmd_entry;
95 }
96 }
97 return NULL;
98}
99
100int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
101{
102 struct ctrl_cmd *cmd;
103 struct bsc_cmd_list *pending;
104 char *var, *id;
105
106 cmd = ctrl_cmd_parse(bsc, msg);
107 msgb_free(msg);
108
109 if (!cmd) {
110 cmd = talloc_zero(bsc, struct ctrl_cmd);
111 if (!cmd) {
112 LOGP(DNAT, LOGL_ERROR, "OOM!\n");
113 return -ENOMEM;
114 }
115 cmd->type = CTRL_TYPE_ERROR;
116 cmd->id = "err";
117 cmd->reply = "Failed to parse command.";
118 goto err;
119 }
120
121 if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
122 if (cmd->variable) {
123 var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
124 cmd->variable);
125 if (!var) {
126 cmd->type = CTRL_TYPE_ERROR;
127 cmd->reply = "OOM";
128 goto err;
129 }
130 talloc_free(cmd->variable);
131 cmd->variable = var;
132 }
133
134 /* We have to handle TRAPs before matching pending */
135 if (cmd->type == CTRL_TYPE_TRAP) {
136 ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
137 talloc_free(cmd);
138 return 0;
139 }
140
141 /* Find the pending command */
142 pending = bsc_get_pending(bsc, cmd->id);
143 if (pending) {
144 id = talloc_strdup(cmd, pending->cmd->id);
145 if (!id) {
146 cmd->type = CTRL_TYPE_ERROR;
147 cmd->reply = "OOM";
148 goto err;
149 }
150 cmd->id = id;
151 ctrl_cmd_send(&pending->ccon->write_queue, cmd);
152 bsc_nat_ctrl_del_pending(pending);
153 } else {
154 /* We need to handle TRAPS here */
155 if ((cmd->type != CTRL_TYPE_ERROR) &&
156 (cmd->type != CTRL_TYPE_TRAP)) {
157 LOGP(DNAT, LOGL_NOTICE, "Got control message "
158 "from BSC without pending entry\n");
159 cmd->type = CTRL_TYPE_ERROR;
160 cmd->reply = "No request outstanding";
161 goto err;
162 }
163 }
164 }
165 talloc_free(cmd);
166 return 0;
167err:
168 ctrl_cmd_send(&bsc->write_queue, cmd);
169 talloc_free(cmd);
170 return 0;
171}
172
173static void pending_timeout_cb(void *data)
174{
175 struct bsc_cmd_list *pending = data;
176 LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
177 pending->cmd->type = CTRL_TYPE_ERROR;
178 pending->cmd->reply = "Command timed out";
179 ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
180
181 bsc_nat_ctrl_del_pending(pending);
182}
183
184static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
185{
186 struct bsc_connection *bsc;
187 struct bsc_cmd_list *pending, *tmp;
188
189 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
190 llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
191 if (pending->ccon == connection)
192 bsc_nat_ctrl_del_pending(pending);
193 }
194 }
195}
196
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100197static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
198{
199 char *nr_str, *tmp, *saveptr = NULL;
200
201 tmp = strtok_r(variable, ".", &saveptr);
202 tmp = strtok_r(NULL, ".", &saveptr);
203 tmp = strtok_r(NULL, ".", &saveptr);
204 nr_str = strtok_r(NULL, ".", &saveptr);
205 if (!nr_str)
206 return 0;
207 *nr = atoi(nr_str);
208
209 tmp = strtok_r(NULL, "\0", &saveptr);
210 if (!tmp)
211 return 0;
212
213 *bsc_variable = tmp;
214 return 1;
215}
216
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100217static int forward_to_bsc(struct ctrl_cmd *cmd)
218{
219 int ret = CTRL_CMD_HANDLED;
220 struct ctrl_cmd *bsc_cmd = NULL;
221 struct bsc_connection *bsc;
222 struct bsc_cmd_list *pending;
223 unsigned int nr;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100224 char *bsc_variable;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100225
226 /* Skip over the beginning (bsc.) */
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100227 if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100228 cmd->reply = "command incomplete";
229 goto err;
230 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100231
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100232
233 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
234 if (!bsc->cfg)
235 continue;
236 if (!bsc->authenticated)
237 continue;
238 if (bsc->cfg->nr == nr) {
239 /* Add pending command to list */
240 pending = talloc_zero(bsc, struct bsc_cmd_list);
241 if (!pending) {
242 cmd->reply = "OOM";
243 goto err;
244 }
245
246 pending->nat_id = get_next_free_bsc_id(bsc);
247 if (pending->nat_id < 0) {
248 cmd->reply = "No free ID found";
249 goto err;
250 }
251
252 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
253 if (!bsc_cmd) {
254 cmd->reply = "Could not forward command";
255 goto err;
256 }
257
258 talloc_free(bsc_cmd->id);
259 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
260 if (!bsc_cmd->id) {
261 cmd->reply = "OOM";
262 goto err;
263 }
264
265 talloc_free(bsc_cmd->variable);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100266 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100267 if (!bsc_cmd->variable) {
268 cmd->reply = "OOM";
269 goto err;
270 }
271
272 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
273 cmd->reply = "Sending failed";
274 goto err;
275 }
276 pending->ccon = cmd->ccon;
277 pending->ccon->closed_cb = ctrl_conn_closed_cb;
278 pending->cmd = cmd;
279
280 /* Setup the timeout */
281 pending->timeout.data = pending;
282 pending->timeout.cb = pending_timeout_cb;
283 /* TODO: Make timeout configurable */
284 osmo_timer_schedule(&pending->timeout, 10, 0);
285 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
286
287 goto done;
288 }
289 }
290 /* We end up here if there's no bsc to handle our LAC */
291 cmd->reply = "no BSC with this nr";
292err:
293 ret = CTRL_CMD_ERROR;
294done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100295 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100296 return ret;
297
298}
299
300
301CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
302static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
303{
304 return forward_to_bsc(cmd);
305}
306
307static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
308{
309 return forward_to_bsc(cmd);
310}
311
312static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
313{
314 return 0;
315}
316
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100317static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
318 char **bsc_variable)
319{
320 unsigned int nr;
321
322 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
323 cmd->reply = "command incomplete";
324 return 0;
325 }
326
327 *cfg = bsc_config_num(g_nat, nr);
328 if (!*cfg) {
329 cmd->reply = "Unknown BSC";
330 return 0;
331 }
332
333 return 1;
334}
335
336CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
337static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
338{
339 char *bsc_variable;
340 struct bsc_config *bsc_cfg;
341
342 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
343 return CTRL_CMD_ERROR;
344
345 if (strcmp(bsc_variable, "access-list-name") == 0) {
346 cmd->reply = talloc_asprintf(cmd, "%s",
347 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
348 return CTRL_CMD_REPLY;
349 }
350
351 cmd->reply = "unknown command";
352 return CTRL_CMD_ERROR;
353}
354
355static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
356{
357 char *bsc_variable;
358 struct bsc_config *bsc_cfg;
359
360 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
361 return CTRL_CMD_ERROR;
362
363 if (strcmp(bsc_variable, "access-list-name") == 0) {
364 bsc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
365 cmd->reply = talloc_asprintf(cmd, "%s",
366 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
367 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100368 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
369 talloc_free(bsc_cfg->acc_lst_name);
370 bsc_cfg->acc_lst_name = NULL;
371 cmd->reply = "";
372 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100373 }
374
375 cmd->reply = "unknown command";
376 return CTRL_CMD_ERROR;
377}
378
379static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
380{
381 return 0;
382}
383
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200384CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
385static const char *extract_acc_name(const char *var)
386{
387 char *str;
388
389 str = strstr(var, "net.0.add.allow.access-list.");
390 if (!str)
391 return NULL;
392 str += strlen("net.0.add.allow.access-list.");
393 if (strlen(str) == 0)
394 return NULL;
395 return str;
396}
397
398static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
399{
400 cmd->reply = "Append only";
401 return CTRL_CMD_ERROR;
402}
403
404static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
405{
406 const char *access_name = extract_acc_name(cmd->variable);
407 struct bsc_nat_acc_lst *acc = bsc_nat_acc_lst_find(g_nat, access_name);
408 struct bsc_nat_acc_lst_entry *entry;
409 const char *value = cmd->value;
410 int rc;
411
412 entry = bsc_nat_acc_lst_entry_create(acc);
413 if (!entry) {
414 cmd->reply = "OOM";
415 return CTRL_CMD_ERROR;
416 }
417
418 rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
419 if (rc != 0) {
420 cmd->reply = "Failed to compile expression";
421 return CTRL_CMD_ERROR;
422 }
423
424 cmd->reply = "IMSI allow added to access list";
425 return CTRL_CMD_REPLY;
426}
427
428static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
429{
430 const char *access_name = extract_acc_name(cmd->variable);
431 struct bsc_nat_acc_lst *acc = bsc_nat_acc_lst_find(g_nat, access_name);
432
433 if (!acc) {
434 cmd->reply = "Access list not known";
435 return -1;
436 }
437
438 return 0;
439}
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100440
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200441CTRL_CMD_DEFINE(net_save_cmd, "net 0 save-configuration");
442static int verify_net_save_cmd(struct ctrl_cmd *cmd, const char *v, void *d)
443{
444 return 0;
445}
446
447static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
448{
449 int rc = osmo_vty_save_config_file();
450 cmd->reply = talloc_asprintf(cmd, "%d", rc);
451 if (!cmd->reply) {
452 cmd->reply = "OOM";
453 return CTRL_CMD_ERROR;
454 }
455
456 return CTRL_CMD_REPLY;
457}
458
459static int get_net_save_cmd(struct ctrl_cmd *cmd, void *data)
460{
461 cmd->reply = "Write only attribute";
462 return CTRL_CMD_ERROR;
463}
464
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100465struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port)
466{
467 struct ctrl_handle *ctrl;
468 int rc;
469
470
Harald Welte55dc31e2014-08-24 17:54:49 +0200471 ctrl = bsc_controlif_setup(NULL, OSMO_CTRL_PORT_BSC_NAT);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100472 if (!ctrl) {
473 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
474 return NULL;
475 }
476
477 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
478 if (rc) {
479 fprintf(stderr, "Failed to install the control command. Exiting.\n");
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100480 goto error;
481 }
482 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
483 if (rc) {
484 fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
485 goto error;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100486 }
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200487 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
488 if (rc) {
489 fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
490 goto error;
491 }
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200492 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd);
493 if (rc) {
494 fprintf(stderr, "Failed to install the net save command. Exiting.\n");
495 goto error;
496 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100497
498 g_nat = nat;
499 return ctrl;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100500
501error:
502 osmo_fd_unregister(&ctrl->listen_fd);
503 close(ctrl->listen_fd.fd);
504 talloc_free(ctrl);
505 return NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100506}
507
Holger Hans Peter Freyther7c009832015-04-05 13:45:53 +0200508void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
509{
510 struct ctrl_cmd *cmd;
511
512 cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
513 if (!cmd) {
514 LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
515 return;
516 }
517
518 cmd->id = "0";
519 cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
520 conn->cfg->nr);
521 cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
522
523 ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
524 talloc_free(cmd);
525}