blob: ec4243e8ed85fae007158633aafab44b775693e1 [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>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010035
36#include <unistd.h>
Harald Welteba874b82014-08-20 23:47:15 +020037#include <string.h>
38#include <errno.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010039
40
41#define NAT_MAX_CTRL_ID 65535
42
43static struct bsc_nat *g_nat;
44
45static int bsc_id_unused(int id, struct bsc_connection *bsc)
46{
47 struct bsc_cmd_list *pending;
48
49 llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
50 if (pending->nat_id == id)
51 return 0;
52 }
53 return 1;
54}
55
56static int get_next_free_bsc_id(struct bsc_connection *bsc)
57{
58 int new_id, overflow = 0;
59
60 new_id = bsc->last_id;
61
62 do {
63 new_id++;
64 if (new_id == NAT_MAX_CTRL_ID) {
65 new_id = 1;
66 overflow++;
67 }
68
69 if (bsc_id_unused(new_id, bsc)) {
70 bsc->last_id = new_id;
71 return new_id;
72 }
73 } while (overflow != 2);
74
75 return -1;
76}
77
78void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
79{
80 llist_del(&pending->list_entry);
81 osmo_timer_del(&pending->timeout);
82 talloc_free(pending->cmd);
83 talloc_free(pending);
84}
85
86static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
87{
88 struct bsc_cmd_list *cmd_entry;
89 int id = atoi(id_str);
90 if (id == 0)
91 return NULL;
92
93 llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
94 if (cmd_entry->nat_id == id) {
95 return cmd_entry;
96 }
97 }
98 return NULL;
99}
100
101int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
102{
103 struct ctrl_cmd *cmd;
104 struct bsc_cmd_list *pending;
105 char *var, *id;
106
107 cmd = ctrl_cmd_parse(bsc, msg);
108 msgb_free(msg);
109
110 if (!cmd) {
111 cmd = talloc_zero(bsc, struct ctrl_cmd);
112 if (!cmd) {
113 LOGP(DNAT, LOGL_ERROR, "OOM!\n");
114 return -ENOMEM;
115 }
116 cmd->type = CTRL_TYPE_ERROR;
117 cmd->id = "err";
118 cmd->reply = "Failed to parse command.";
119 goto err;
120 }
121
122 if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
123 if (cmd->variable) {
124 var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
125 cmd->variable);
126 if (!var) {
127 cmd->type = CTRL_TYPE_ERROR;
128 cmd->reply = "OOM";
129 goto err;
130 }
131 talloc_free(cmd->variable);
132 cmd->variable = var;
133 }
134
135 /* We have to handle TRAPs before matching pending */
136 if (cmd->type == CTRL_TYPE_TRAP) {
137 ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
138 talloc_free(cmd);
139 return 0;
140 }
141
142 /* Find the pending command */
143 pending = bsc_get_pending(bsc, cmd->id);
144 if (pending) {
145 id = talloc_strdup(cmd, pending->cmd->id);
146 if (!id) {
147 cmd->type = CTRL_TYPE_ERROR;
148 cmd->reply = "OOM";
149 goto err;
150 }
151 cmd->id = id;
152 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;
223 struct bsc_cmd_list *pending;
224 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;
239 if (bsc->cfg->nr == nr) {
240 /* Add pending command to list */
241 pending = talloc_zero(bsc, struct bsc_cmd_list);
242 if (!pending) {
243 cmd->reply = "OOM";
244 goto err;
245 }
246
247 pending->nat_id = get_next_free_bsc_id(bsc);
248 if (pending->nat_id < 0) {
249 cmd->reply = "No free ID found";
250 goto err;
251 }
252
253 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
254 if (!bsc_cmd) {
255 cmd->reply = "Could not forward command";
256 goto err;
257 }
258
259 talloc_free(bsc_cmd->id);
260 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
261 if (!bsc_cmd->id) {
262 cmd->reply = "OOM";
263 goto err;
264 }
265
266 talloc_free(bsc_cmd->variable);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100267 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100268 if (!bsc_cmd->variable) {
269 cmd->reply = "OOM";
270 goto err;
271 }
272
273 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
274 cmd->reply = "Sending failed";
275 goto err;
276 }
277 pending->ccon = cmd->ccon;
278 pending->ccon->closed_cb = ctrl_conn_closed_cb;
279 pending->cmd = cmd;
280
281 /* Setup the timeout */
282 pending->timeout.data = pending;
283 pending->timeout.cb = pending_timeout_cb;
284 /* TODO: Make timeout configurable */
285 osmo_timer_schedule(&pending->timeout, 10, 0);
286 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
287
288 goto done;
289 }
290 }
291 /* We end up here if there's no bsc to handle our LAC */
292 cmd->reply = "no BSC with this nr";
293err:
294 ret = CTRL_CMD_ERROR;
295done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100296 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100297 return ret;
298
299}
300
301
302CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
303static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
304{
305 return forward_to_bsc(cmd);
306}
307
308static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
309{
310 return forward_to_bsc(cmd);
311}
312
313static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
314{
315 return 0;
316}
317
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100318static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
319 char **bsc_variable)
320{
321 unsigned int nr;
322
323 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
324 cmd->reply = "command incomplete";
325 return 0;
326 }
327
328 *cfg = bsc_config_num(g_nat, nr);
329 if (!*cfg) {
330 cmd->reply = "Unknown BSC";
331 return 0;
332 }
333
334 return 1;
335}
336
337CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
338static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
339{
340 char *bsc_variable;
341 struct bsc_config *bsc_cfg;
342
343 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
344 return CTRL_CMD_ERROR;
345
346 if (strcmp(bsc_variable, "access-list-name") == 0) {
347 cmd->reply = talloc_asprintf(cmd, "%s",
348 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
349 return CTRL_CMD_REPLY;
350 }
351
352 cmd->reply = "unknown command";
353 return CTRL_CMD_ERROR;
354}
355
356static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
357{
358 char *bsc_variable;
359 struct bsc_config *bsc_cfg;
360
361 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
362 return CTRL_CMD_ERROR;
363
364 if (strcmp(bsc_variable, "access-list-name") == 0) {
365 bsc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
366 cmd->reply = talloc_asprintf(cmd, "%s",
367 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
368 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100369 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
370 talloc_free(bsc_cfg->acc_lst_name);
371 bsc_cfg->acc_lst_name = NULL;
372 cmd->reply = "";
373 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100374 }
375
376 cmd->reply = "unknown command";
377 return CTRL_CMD_ERROR;
378}
379
380static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
381{
382 return 0;
383}
384
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200385CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
386static const char *extract_acc_name(const char *var)
387{
388 char *str;
389
390 str = strstr(var, "net.0.add.allow.access-list.");
391 if (!str)
392 return NULL;
393 str += strlen("net.0.add.allow.access-list.");
394 if (strlen(str) == 0)
395 return NULL;
396 return str;
397}
398
399static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
400{
401 cmd->reply = "Append only";
402 return CTRL_CMD_ERROR;
403}
404
405static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
406{
407 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200408 struct bsc_msg_acc_lst *acc;
409 struct bsc_msg_acc_lst_entry *entry;
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200410 const char *value = cmd->value;
411 int rc;
412
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200413 /* Should have been caught by verify_net_cfg_acc_cmd */
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200414 acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
Holger Hans Peter Freyther08ea4d82015-04-07 09:08:32 +0200415 if (!acc) {
416 cmd->reply = "Access list not found";
417 return CTRL_CMD_ERROR;
418 }
419
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200420 entry = bsc_msg_acc_lst_entry_create(acc);
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200421 if (!entry) {
422 cmd->reply = "OOM";
423 return CTRL_CMD_ERROR;
424 }
425
426 rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
427 if (rc != 0) {
428 cmd->reply = "Failed to compile expression";
429 return CTRL_CMD_ERROR;
430 }
431
432 cmd->reply = "IMSI allow added to access list";
433 return CTRL_CMD_REPLY;
434}
435
436static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
437{
438 const char *access_name = extract_acc_name(cmd->variable);
Holger Hans Peter Freythera1e6bd62015-04-04 22:40:12 +0200439 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 +0200440
441 if (!acc) {
442 cmd->reply = "Access list not known";
443 return -1;
444 }
445
446 return 0;
447}
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100448
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200449CTRL_CMD_DEFINE(net_save_cmd, "net 0 save-configuration");
450static int verify_net_save_cmd(struct ctrl_cmd *cmd, const char *v, void *d)
451{
452 return 0;
453}
454
455static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
456{
457 int rc = osmo_vty_save_config_file();
458 cmd->reply = talloc_asprintf(cmd, "%d", rc);
459 if (!cmd->reply) {
460 cmd->reply = "OOM";
461 return CTRL_CMD_ERROR;
462 }
463
464 return CTRL_CMD_REPLY;
465}
466
467static int get_net_save_cmd(struct ctrl_cmd *cmd, void *data)
468{
469 cmd->reply = "Write only attribute";
470 return CTRL_CMD_ERROR;
471}
472
Neels Hofmeyr73828152016-02-23 15:10:33 +0100473struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
474 const char *bind_addr, int port)
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100475{
476 struct ctrl_handle *ctrl;
477 int rc;
478
479
Neels Hofmeyr73828152016-02-23 15:10:33 +0100480 ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100481 if (!ctrl) {
482 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
483 return NULL;
484 }
485
486 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
487 if (rc) {
488 fprintf(stderr, "Failed to install the control command. Exiting.\n");
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100489 goto error;
490 }
491 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
492 if (rc) {
493 fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
494 goto error;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100495 }
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200496 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
497 if (rc) {
498 fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
499 goto error;
500 }
Holger Hans Peter Freyther8ccf06c2015-04-05 15:26:33 +0200501 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd);
502 if (rc) {
503 fprintf(stderr, "Failed to install the net save command. Exiting.\n");
504 goto error;
505 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100506
507 g_nat = nat;
508 return ctrl;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100509
510error:
511 osmo_fd_unregister(&ctrl->listen_fd);
512 close(ctrl->listen_fd.fd);
513 talloc_free(ctrl);
514 return NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100515}
516
Holger Hans Peter Freyther7c009832015-04-05 13:45:53 +0200517void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
518{
519 struct ctrl_cmd *cmd;
520
521 cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
522 if (!cmd) {
523 LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
524 return;
525 }
526
527 cmd->id = "0";
528 cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
529 conn->cfg->nr);
530 cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
531
532 ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
533 talloc_free(cmd);
534}