blob: ff6a73978a6ee922d1fceef5cdfe8ca9ee3f98e8 [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 Freyther973dbae2015-04-04 20:47:03 +020032#include <openbsc/bsc_msg_filter.h>
Holger Hans Peter Freythercd485252014-08-29 10:58:12 +020033#include <openbsc/vty.h>
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +020034#include <openbsc/gsm_data.h>
Neels Hofmeyr979cd262017-07-19 16:48:42 +020035#include <openbsc/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;
106 char *var, *id;
107
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) {
146 id = talloc_strdup(cmd, pending->cmd->id);
147 if (!id) {
148 cmd->type = CTRL_TYPE_ERROR;
149 cmd->reply = "OOM";
150 goto err;
151 }
152 cmd->id = id;
153 ctrl_cmd_send(&pending->ccon->write_queue, cmd);
154 bsc_nat_ctrl_del_pending(pending);
155 } else {
156 /* We need to handle TRAPS here */
157 if ((cmd->type != CTRL_TYPE_ERROR) &&
158 (cmd->type != CTRL_TYPE_TRAP)) {
159 LOGP(DNAT, LOGL_NOTICE, "Got control message "
160 "from BSC without pending entry\n");
161 cmd->type = CTRL_TYPE_ERROR;
162 cmd->reply = "No request outstanding";
163 goto err;
164 }
165 }
166 }
167 talloc_free(cmd);
168 return 0;
169err:
170 ctrl_cmd_send(&bsc->write_queue, cmd);
171 talloc_free(cmd);
172 return 0;
173}
174
175static void pending_timeout_cb(void *data)
176{
177 struct bsc_cmd_list *pending = data;
178 LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
179 pending->cmd->type = CTRL_TYPE_ERROR;
180 pending->cmd->reply = "Command timed out";
181 ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
182
183 bsc_nat_ctrl_del_pending(pending);
184}
185
186static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
187{
188 struct bsc_connection *bsc;
189 struct bsc_cmd_list *pending, *tmp;
190
191 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
192 llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
193 if (pending->ccon == connection)
194 bsc_nat_ctrl_del_pending(pending);
195 }
196 }
197}
198
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100199static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
200{
201 char *nr_str, *tmp, *saveptr = NULL;
202
203 tmp = strtok_r(variable, ".", &saveptr);
204 tmp = strtok_r(NULL, ".", &saveptr);
205 tmp = strtok_r(NULL, ".", &saveptr);
206 nr_str = strtok_r(NULL, ".", &saveptr);
207 if (!nr_str)
208 return 0;
209 *nr = atoi(nr_str);
210
211 tmp = strtok_r(NULL, "\0", &saveptr);
212 if (!tmp)
213 return 0;
214
215 *bsc_variable = tmp;
216 return 1;
217}
218
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100219static int forward_to_bsc(struct ctrl_cmd *cmd)
220{
221 int ret = CTRL_CMD_HANDLED;
222 struct ctrl_cmd *bsc_cmd = NULL;
223 struct bsc_connection *bsc;
224 struct bsc_cmd_list *pending;
225 unsigned int nr;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100226 char *bsc_variable;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100227
228 /* Skip over the beginning (bsc.) */
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100229 if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100230 cmd->reply = "command incomplete";
231 goto err;
232 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100233
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100234
235 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
236 if (!bsc->cfg)
237 continue;
238 if (!bsc->authenticated)
239 continue;
240 if (bsc->cfg->nr == nr) {
241 /* Add pending command to list */
242 pending = talloc_zero(bsc, struct bsc_cmd_list);
243 if (!pending) {
244 cmd->reply = "OOM";
245 goto err;
246 }
247
248 pending->nat_id = get_next_free_bsc_id(bsc);
249 if (pending->nat_id < 0) {
250 cmd->reply = "No free ID found";
251 goto err;
252 }
253
254 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
255 if (!bsc_cmd) {
256 cmd->reply = "Could not forward command";
257 goto err;
258 }
259
260 talloc_free(bsc_cmd->id);
261 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
262 if (!bsc_cmd->id) {
263 cmd->reply = "OOM";
264 goto err;
265 }
266
267 talloc_free(bsc_cmd->variable);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100268 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100269 if (!bsc_cmd->variable) {
270 cmd->reply = "OOM";
271 goto err;
272 }
273
274 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
275 cmd->reply = "Sending failed";
276 goto err;
277 }
278 pending->ccon = cmd->ccon;
279 pending->ccon->closed_cb = ctrl_conn_closed_cb;
280 pending->cmd = cmd;
281
282 /* Setup the timeout */
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200283 osmo_timer_setup(&pending->timeout, pending_timeout_cb,
284 pending);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100285 /* TODO: Make timeout configurable */
286 osmo_timer_schedule(&pending->timeout, 10, 0);
287 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
288
289 goto done;
290 }
291 }
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;
296done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100297 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100298 return ret;
299
300}
301
302
303CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
304static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
305{
306 return forward_to_bsc(cmd);
307}
308
309static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
310{
311 return forward_to_bsc(cmd);
312}
313
314static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
315{
316 return 0;
317}
318
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100319static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
320 char **bsc_variable)
321{
322 unsigned int nr;
323
324 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
325 cmd->reply = "command incomplete";
326 return 0;
327 }
328
329 *cfg = bsc_config_num(g_nat, nr);
330 if (!*cfg) {
331 cmd->reply = "Unknown BSC";
332 return 0;
333 }
334
335 return 1;
336}
337
338CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
339static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
340{
341 char *bsc_variable;
342 struct bsc_config *bsc_cfg;
343
344 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
345 return CTRL_CMD_ERROR;
346
347 if (strcmp(bsc_variable, "access-list-name") == 0) {
348 cmd->reply = talloc_asprintf(cmd, "%s",
349 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
350 return CTRL_CMD_REPLY;
351 }
352
353 cmd->reply = "unknown command";
354 return CTRL_CMD_ERROR;
355}
356
357static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
358{
359 char *bsc_variable;
360 struct bsc_config *bsc_cfg;
361
362 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
363 return CTRL_CMD_ERROR;
364
365 if (strcmp(bsc_variable, "access-list-name") == 0) {
Harald Welte4a824ca2017-05-29 13:54:27 +0200366 osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100367 cmd->reply = talloc_asprintf(cmd, "%s",
368 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
369 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100370 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
371 talloc_free(bsc_cfg->acc_lst_name);
372 bsc_cfg->acc_lst_name = NULL;
373 cmd->reply = "";
374 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100375 }
376
377 cmd->reply = "unknown command";
378 return CTRL_CMD_ERROR;
379}
380
381static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
382{
383 return 0;
384}
385
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200386CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
387static const char *extract_acc_name(const char *var)
388{
389 char *str;
390
391 str = strstr(var, "net.0.add.allow.access-list.");
392 if (!str)
393 return NULL;
394 str += strlen("net.0.add.allow.access-list.");
395 if (strlen(str) == 0)
396 return NULL;
397 return str;
398}
399
400static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
401{
402 cmd->reply = "Append only";
403 return CTRL_CMD_ERROR;
404}
405
406static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
407{
408 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200409 struct bsc_msg_acc_lst *acc;
410 struct bsc_msg_acc_lst_entry *entry;
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200411 const char *value = cmd->value;
412 int rc;
413
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200414 /* Should have been caught by verify_net_cfg_acc_cmd */
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200415 acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200416 if (!acc) {
417 cmd->reply = "Access list not found";
418 return CTRL_CMD_ERROR;
419 }
420
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200421 entry = bsc_msg_acc_lst_entry_create(acc);
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200422 if (!entry) {
423 cmd->reply = "OOM";
424 return CTRL_CMD_ERROR;
425 }
426
427 rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
428 if (rc != 0) {
429 cmd->reply = "Failed to compile expression";
430 return CTRL_CMD_ERROR;
431 }
432
433 cmd->reply = "IMSI allow added to access list";
434 return CTRL_CMD_REPLY;
435}
436
437static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
438{
439 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200440 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 +0200441
442 if (!acc) {
443 cmd->reply = "Access list not known";
444 return -1;
445 }
446
447 return 0;
448}
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100449
Maxf6e51702017-01-11 18:37:55 +0100450CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration");
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200451
452static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
453{
454 int rc = osmo_vty_save_config_file();
455 cmd->reply = talloc_asprintf(cmd, "%d", rc);
456 if (!cmd->reply) {
457 cmd->reply = "OOM";
458 return CTRL_CMD_ERROR;
459 }
460
461 return CTRL_CMD_REPLY;
462}
463
Neels Hofmeyr73828152016-02-23 15:10:33 +0100464struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
465 const char *bind_addr, int port)
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100466{
467 struct ctrl_handle *ctrl;
468 int rc;
469
470
Neels Hofmeyr73828152016-02-23 15:10:33 +0100471 ctrl = bsc_controlif_setup(NULL, bind_addr, 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}