blob: 5d551ac4af325a222b3e40cc149db1da57d3f8e5 [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
Neels Hofmeyrc0164792017-09-04 15:15:32 +020030#include <osmocom/bsc/ctrl.h>
31#include <osmocom/bsc/bsc_nat.h>
32#include <osmocom/bsc/bsc_msg_filter.h>
33#include <osmocom/bsc/vty.h>
34#include <osmocom/bsc/gsm_data.h>
35#include <osmocom/bsc/debug.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010036
37#include <unistd.h>
Harald Welteba874b82014-08-20 23:47:15 +020038#include <string.h>
39#include <errno.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010040
41
42#define NAT_MAX_CTRL_ID 65535
43
44static struct bsc_nat *g_nat;
45
46static int bsc_id_unused(int id, struct bsc_connection *bsc)
47{
48 struct bsc_cmd_list *pending;
49
50 llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
51 if (pending->nat_id == id)
52 return 0;
53 }
54 return 1;
55}
56
57static int get_next_free_bsc_id(struct bsc_connection *bsc)
58{
59 int new_id, overflow = 0;
60
61 new_id = bsc->last_id;
62
63 do {
64 new_id++;
65 if (new_id == NAT_MAX_CTRL_ID) {
66 new_id = 1;
67 overflow++;
68 }
69
70 if (bsc_id_unused(new_id, bsc)) {
71 bsc->last_id = new_id;
72 return new_id;
73 }
74 } while (overflow != 2);
75
76 return -1;
77}
78
79void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
80{
81 llist_del(&pending->list_entry);
82 osmo_timer_del(&pending->timeout);
83 talloc_free(pending->cmd);
84 talloc_free(pending);
85}
86
87static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
88{
89 struct bsc_cmd_list *cmd_entry;
90 int id = atoi(id_str);
91 if (id == 0)
92 return NULL;
93
94 llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
95 if (cmd_entry->nat_id == id) {
96 return cmd_entry;
97 }
98 }
99 return NULL;
100}
101
102int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
103{
104 struct ctrl_cmd *cmd;
105 struct bsc_cmd_list *pending;
Pau Espin Pedrol637e6da2018-04-11 13:16:49 +0200106 char *var;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100107
108 cmd = ctrl_cmd_parse(bsc, msg);
109 msgb_free(msg);
110
111 if (!cmd) {
112 cmd = talloc_zero(bsc, struct ctrl_cmd);
113 if (!cmd) {
114 LOGP(DNAT, LOGL_ERROR, "OOM!\n");
115 return -ENOMEM;
116 }
117 cmd->type = CTRL_TYPE_ERROR;
118 cmd->id = "err";
119 cmd->reply = "Failed to parse command.";
120 goto err;
121 }
122
123 if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
124 if (cmd->variable) {
125 var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
126 cmd->variable);
127 if (!var) {
128 cmd->type = CTRL_TYPE_ERROR;
129 cmd->reply = "OOM";
130 goto err;
131 }
132 talloc_free(cmd->variable);
133 cmd->variable = var;
134 }
135
136 /* We have to handle TRAPs before matching pending */
137 if (cmd->type == CTRL_TYPE_TRAP) {
138 ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
139 talloc_free(cmd);
140 return 0;
141 }
142
143 /* Find the pending command */
144 pending = bsc_get_pending(bsc, cmd->id);
145 if (pending) {
Pau Espin Pedrol637e6da2018-04-11 13:16:49 +0200146 osmo_talloc_replace_string(cmd, &cmd->id, pending->cmd->id);
147 if (!cmd->id) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100148 cmd->type = CTRL_TYPE_ERROR;
149 cmd->reply = "OOM";
150 goto err;
151 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100152 ctrl_cmd_send(&pending->ccon->write_queue, cmd);
153 bsc_nat_ctrl_del_pending(pending);
154 } else {
155 /* We need to handle TRAPS here */
156 if ((cmd->type != CTRL_TYPE_ERROR) &&
157 (cmd->type != CTRL_TYPE_TRAP)) {
158 LOGP(DNAT, LOGL_NOTICE, "Got control message "
159 "from BSC without pending entry\n");
160 cmd->type = CTRL_TYPE_ERROR;
161 cmd->reply = "No request outstanding";
162 goto err;
163 }
164 }
165 }
166 talloc_free(cmd);
167 return 0;
168err:
169 ctrl_cmd_send(&bsc->write_queue, cmd);
170 talloc_free(cmd);
171 return 0;
172}
173
174static void pending_timeout_cb(void *data)
175{
176 struct bsc_cmd_list *pending = data;
177 LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
178 pending->cmd->type = CTRL_TYPE_ERROR;
179 pending->cmd->reply = "Command timed out";
180 ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
181
182 bsc_nat_ctrl_del_pending(pending);
183}
184
185static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
186{
187 struct bsc_connection *bsc;
188 struct bsc_cmd_list *pending, *tmp;
189
190 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
191 llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
192 if (pending->ccon == connection)
193 bsc_nat_ctrl_del_pending(pending);
194 }
195 }
196}
197
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100198static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
199{
200 char *nr_str, *tmp, *saveptr = NULL;
201
202 tmp = strtok_r(variable, ".", &saveptr);
203 tmp = strtok_r(NULL, ".", &saveptr);
204 tmp = strtok_r(NULL, ".", &saveptr);
205 nr_str = strtok_r(NULL, ".", &saveptr);
206 if (!nr_str)
207 return 0;
208 *nr = atoi(nr_str);
209
210 tmp = strtok_r(NULL, "\0", &saveptr);
211 if (!tmp)
212 return 0;
213
214 *bsc_variable = tmp;
215 return 1;
216}
217
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100218static int forward_to_bsc(struct ctrl_cmd *cmd)
219{
220 int ret = CTRL_CMD_HANDLED;
221 struct ctrl_cmd *bsc_cmd = NULL;
222 struct bsc_connection *bsc;
Pau Espin Pedroldf7a9752018-04-11 17:10:46 +0200223 struct bsc_cmd_list *pending = NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100224 unsigned int nr;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100225 char *bsc_variable;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100226
227 /* Skip over the beginning (bsc.) */
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100228 if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100229 cmd->reply = "command incomplete";
230 goto err;
231 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100232
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100233
234 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
235 if (!bsc->cfg)
236 continue;
237 if (!bsc->authenticated)
238 continue;
Pau Espin Pedrol56e564d2018-04-11 16:40:47 +0200239 if (bsc->cfg->nr != nr)
240 continue;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100241
Pau Espin Pedrol56e564d2018-04-11 16:40:47 +0200242 /* Add pending command to list */
243 pending = talloc_zero(bsc, struct bsc_cmd_list);
244 if (!pending) {
245 cmd->reply = "OOM";
246 goto err;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100247 }
Pau Espin Pedrol56e564d2018-04-11 16:40:47 +0200248
249 pending->nat_id = get_next_free_bsc_id(bsc);
250 if (pending->nat_id < 0) {
251 cmd->reply = "No free ID found";
252 goto err;
253 }
254
255 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
256 if (!bsc_cmd) {
257 cmd->reply = "Could not forward command";
258 goto err;
259 }
260
261 talloc_free(bsc_cmd->id);
262 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
263 if (!bsc_cmd->id) {
264 cmd->reply = "OOM";
265 goto err;
266 }
267
268 talloc_free(bsc_cmd->variable);
269 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
270 if (!bsc_cmd->variable) {
271 cmd->reply = "OOM";
272 goto err;
273 }
274
275 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
276 cmd->reply = "Sending failed";
277 goto err;
278 }
279 pending->ccon = cmd->ccon;
280 pending->ccon->closed_cb = ctrl_conn_closed_cb;
281 pending->cmd = cmd;
282
283 /* Setup the timeout */
284 osmo_timer_setup(&pending->timeout, pending_timeout_cb,
285 pending);
286 /* TODO: Make timeout configurable */
287 osmo_timer_schedule(&pending->timeout, 10, 0);
288 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
289
290 goto done;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100291 }
292 /* We end up here if there's no bsc to handle our LAC */
293 cmd->reply = "no BSC with this nr";
294err:
295 ret = CTRL_CMD_ERROR;
Pau Espin Pedroldf7a9752018-04-11 17:10:46 +0200296 talloc_free(pending);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100297done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100298 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100299 return ret;
300
301}
302
303
304CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
305static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
306{
307 return forward_to_bsc(cmd);
308}
309
310static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
311{
312 return forward_to_bsc(cmd);
313}
314
315static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
316{
317 return 0;
318}
319
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100320static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
321 char **bsc_variable)
322{
323 unsigned int nr;
324
325 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
326 cmd->reply = "command incomplete";
327 return 0;
328 }
329
330 *cfg = bsc_config_num(g_nat, nr);
331 if (!*cfg) {
332 cmd->reply = "Unknown BSC";
333 return 0;
334 }
335
336 return 1;
337}
338
339CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
340static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
341{
342 char *bsc_variable;
343 struct bsc_config *bsc_cfg;
344
345 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
346 return CTRL_CMD_ERROR;
347
348 if (strcmp(bsc_variable, "access-list-name") == 0) {
349 cmd->reply = talloc_asprintf(cmd, "%s",
350 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
351 return CTRL_CMD_REPLY;
352 }
353
354 cmd->reply = "unknown command";
355 return CTRL_CMD_ERROR;
356}
357
358static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
359{
360 char *bsc_variable;
361 struct bsc_config *bsc_cfg;
362
363 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
364 return CTRL_CMD_ERROR;
365
366 if (strcmp(bsc_variable, "access-list-name") == 0) {
Harald Welte4a824ca2017-05-29 13:54:27 +0200367 osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100368 cmd->reply = talloc_asprintf(cmd, "%s",
369 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
370 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100371 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
372 talloc_free(bsc_cfg->acc_lst_name);
373 bsc_cfg->acc_lst_name = NULL;
374 cmd->reply = "";
375 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100376 }
377
378 cmd->reply = "unknown command";
379 return CTRL_CMD_ERROR;
380}
381
382static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
383{
384 return 0;
385}
386
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200387CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
388static const char *extract_acc_name(const char *var)
389{
390 char *str;
391
392 str = strstr(var, "net.0.add.allow.access-list.");
393 if (!str)
394 return NULL;
395 str += strlen("net.0.add.allow.access-list.");
396 if (strlen(str) == 0)
397 return NULL;
398 return str;
399}
400
401static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
402{
403 cmd->reply = "Append only";
404 return CTRL_CMD_ERROR;
405}
406
407static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
408{
409 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200410 struct bsc_msg_acc_lst *acc;
411 struct bsc_msg_acc_lst_entry *entry;
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200412 const char *value = cmd->value;
413 int rc;
414
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200415 /* Should have been caught by verify_net_cfg_acc_cmd */
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200416 acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200417 if (!acc) {
418 cmd->reply = "Access list not found";
419 return CTRL_CMD_ERROR;
420 }
421
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200422 entry = bsc_msg_acc_lst_entry_create(acc);
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200423 if (!entry) {
424 cmd->reply = "OOM";
425 return CTRL_CMD_ERROR;
426 }
427
428 rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
429 if (rc != 0) {
430 cmd->reply = "Failed to compile expression";
431 return CTRL_CMD_ERROR;
432 }
433
434 cmd->reply = "IMSI allow added to access list";
435 return CTRL_CMD_REPLY;
436}
437
438static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
439{
440 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200441 struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200442
443 if (!acc) {
444 cmd->reply = "Access list not known";
445 return -1;
446 }
447
448 return 0;
449}
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100450
Maxf6e51702017-01-11 18:37:55 +0100451CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration");
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200452
453static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
454{
455 int rc = osmo_vty_save_config_file();
456 cmd->reply = talloc_asprintf(cmd, "%d", rc);
457 if (!cmd->reply) {
458 cmd->reply = "OOM";
459 return CTRL_CMD_ERROR;
460 }
461
462 return CTRL_CMD_REPLY;
463}
464
Neels Hofmeyr73828152016-02-23 15:10:33 +0100465struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
466 const char *bind_addr, int port)
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100467{
468 struct ctrl_handle *ctrl;
469 int rc;
470
471
Neels Hofmeyr73828152016-02-23 15:10:33 +0100472 ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100473 if (!ctrl) {
474 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
475 return NULL;
476 }
477
478 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
479 if (rc) {
480 fprintf(stderr, "Failed to install the control command. Exiting.\n");
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100481 goto error;
482 }
483 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
484 if (rc) {
485 fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
486 goto error;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100487 }
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200488 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
489 if (rc) {
490 fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
491 goto error;
492 }
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200493 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd);
494 if (rc) {
495 fprintf(stderr, "Failed to install the net save command. Exiting.\n");
496 goto error;
497 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100498
499 g_nat = nat;
500 return ctrl;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100501
502error:
503 osmo_fd_unregister(&ctrl->listen_fd);
504 close(ctrl->listen_fd.fd);
505 talloc_free(ctrl);
506 return NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100507}
508
Holger Hans Peter Freyther7c009832015-04-05 13:45:53 +0200509void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
510{
511 struct ctrl_cmd *cmd;
512
513 cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
514 if (!cmd) {
515 LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
516 return;
517 }
518
519 cmd->id = "0";
520 cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
521 conn->cfg->nr);
522 cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
523
524 ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
525 talloc_free(cmd);
526}