blob: bdb1709e566e079f98cccd9a06345e9943763cbf [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
Harald Welteba874b82014-08-20 23:47:15 +020028#include <openbsc/ctrl.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010029#include <openbsc/bsc_nat.h>
Holger Hans Peter Freythercd485252014-08-29 10:58:12 +020030#include <openbsc/vty.h>
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +020031#include <openbsc/gsm_data.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010032
33#include <unistd.h>
Harald Welteba874b82014-08-20 23:47:15 +020034#include <string.h>
35#include <errno.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010036
37
38#define NAT_MAX_CTRL_ID 65535
39
40static struct bsc_nat *g_nat;
41
42static int bsc_id_unused(int id, struct bsc_connection *bsc)
43{
44 struct bsc_cmd_list *pending;
45
46 llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
47 if (pending->nat_id == id)
48 return 0;
49 }
50 return 1;
51}
52
53static int get_next_free_bsc_id(struct bsc_connection *bsc)
54{
55 int new_id, overflow = 0;
56
57 new_id = bsc->last_id;
58
59 do {
60 new_id++;
61 if (new_id == NAT_MAX_CTRL_ID) {
62 new_id = 1;
63 overflow++;
64 }
65
66 if (bsc_id_unused(new_id, bsc)) {
67 bsc->last_id = new_id;
68 return new_id;
69 }
70 } while (overflow != 2);
71
72 return -1;
73}
74
75void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
76{
77 llist_del(&pending->list_entry);
78 osmo_timer_del(&pending->timeout);
79 talloc_free(pending->cmd);
80 talloc_free(pending);
81}
82
83static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
84{
85 struct bsc_cmd_list *cmd_entry;
86 int id = atoi(id_str);
87 if (id == 0)
88 return NULL;
89
90 llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
91 if (cmd_entry->nat_id == id) {
92 return cmd_entry;
93 }
94 }
95 return NULL;
96}
97
98int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
99{
100 struct ctrl_cmd *cmd;
101 struct bsc_cmd_list *pending;
102 char *var, *id;
103
104 cmd = ctrl_cmd_parse(bsc, msg);
105 msgb_free(msg);
106
107 if (!cmd) {
108 cmd = talloc_zero(bsc, struct ctrl_cmd);
109 if (!cmd) {
110 LOGP(DNAT, LOGL_ERROR, "OOM!\n");
111 return -ENOMEM;
112 }
113 cmd->type = CTRL_TYPE_ERROR;
114 cmd->id = "err";
115 cmd->reply = "Failed to parse command.";
116 goto err;
117 }
118
119 if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
120 if (cmd->variable) {
121 var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
122 cmd->variable);
123 if (!var) {
124 cmd->type = CTRL_TYPE_ERROR;
125 cmd->reply = "OOM";
126 goto err;
127 }
128 talloc_free(cmd->variable);
129 cmd->variable = var;
130 }
131
132 /* We have to handle TRAPs before matching pending */
133 if (cmd->type == CTRL_TYPE_TRAP) {
134 ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
135 talloc_free(cmd);
136 return 0;
137 }
138
139 /* Find the pending command */
140 pending = bsc_get_pending(bsc, cmd->id);
141 if (pending) {
142 id = talloc_strdup(cmd, pending->cmd->id);
143 if (!id) {
144 cmd->type = CTRL_TYPE_ERROR;
145 cmd->reply = "OOM";
146 goto err;
147 }
148 cmd->id = id;
149 ctrl_cmd_send(&pending->ccon->write_queue, cmd);
150 bsc_nat_ctrl_del_pending(pending);
151 } else {
152 /* We need to handle TRAPS here */
153 if ((cmd->type != CTRL_TYPE_ERROR) &&
154 (cmd->type != CTRL_TYPE_TRAP)) {
155 LOGP(DNAT, LOGL_NOTICE, "Got control message "
156 "from BSC without pending entry\n");
157 cmd->type = CTRL_TYPE_ERROR;
158 cmd->reply = "No request outstanding";
159 goto err;
160 }
161 }
162 }
163 talloc_free(cmd);
164 return 0;
165err:
166 ctrl_cmd_send(&bsc->write_queue, cmd);
167 talloc_free(cmd);
168 return 0;
169}
170
171static void pending_timeout_cb(void *data)
172{
173 struct bsc_cmd_list *pending = data;
174 LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
175 pending->cmd->type = CTRL_TYPE_ERROR;
176 pending->cmd->reply = "Command timed out";
177 ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
178
179 bsc_nat_ctrl_del_pending(pending);
180}
181
182static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
183{
184 struct bsc_connection *bsc;
185 struct bsc_cmd_list *pending, *tmp;
186
187 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
188 llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
189 if (pending->ccon == connection)
190 bsc_nat_ctrl_del_pending(pending);
191 }
192 }
193}
194
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100195static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
196{
197 char *nr_str, *tmp, *saveptr = NULL;
198
199 tmp = strtok_r(variable, ".", &saveptr);
200 tmp = strtok_r(NULL, ".", &saveptr);
201 tmp = strtok_r(NULL, ".", &saveptr);
202 nr_str = strtok_r(NULL, ".", &saveptr);
203 if (!nr_str)
204 return 0;
205 *nr = atoi(nr_str);
206
207 tmp = strtok_r(NULL, "\0", &saveptr);
208 if (!tmp)
209 return 0;
210
211 *bsc_variable = tmp;
212 return 1;
213}
214
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100215static int forward_to_bsc(struct ctrl_cmd *cmd)
216{
217 int ret = CTRL_CMD_HANDLED;
218 struct ctrl_cmd *bsc_cmd = NULL;
219 struct bsc_connection *bsc;
220 struct bsc_cmd_list *pending;
221 unsigned int nr;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100222 char *bsc_variable;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100223
224 /* Skip over the beginning (bsc.) */
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100225 if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100226 cmd->reply = "command incomplete";
227 goto err;
228 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100229
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100230
231 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
232 if (!bsc->cfg)
233 continue;
234 if (!bsc->authenticated)
235 continue;
236 if (bsc->cfg->nr == nr) {
237 /* Add pending command to list */
238 pending = talloc_zero(bsc, struct bsc_cmd_list);
239 if (!pending) {
240 cmd->reply = "OOM";
241 goto err;
242 }
243
244 pending->nat_id = get_next_free_bsc_id(bsc);
245 if (pending->nat_id < 0) {
246 cmd->reply = "No free ID found";
247 goto err;
248 }
249
250 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
251 if (!bsc_cmd) {
252 cmd->reply = "Could not forward command";
253 goto err;
254 }
255
256 talloc_free(bsc_cmd->id);
257 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
258 if (!bsc_cmd->id) {
259 cmd->reply = "OOM";
260 goto err;
261 }
262
263 talloc_free(bsc_cmd->variable);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100264 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100265 if (!bsc_cmd->variable) {
266 cmd->reply = "OOM";
267 goto err;
268 }
269
270 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
271 cmd->reply = "Sending failed";
272 goto err;
273 }
274 pending->ccon = cmd->ccon;
275 pending->ccon->closed_cb = ctrl_conn_closed_cb;
276 pending->cmd = cmd;
277
278 /* Setup the timeout */
279 pending->timeout.data = pending;
280 pending->timeout.cb = pending_timeout_cb;
281 /* TODO: Make timeout configurable */
282 osmo_timer_schedule(&pending->timeout, 10, 0);
283 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
284
285 goto done;
286 }
287 }
288 /* We end up here if there's no bsc to handle our LAC */
289 cmd->reply = "no BSC with this nr";
290err:
291 ret = CTRL_CMD_ERROR;
292done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100293 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100294 return ret;
295
296}
297
298
299CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
300static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
301{
302 return forward_to_bsc(cmd);
303}
304
305static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
306{
307 return forward_to_bsc(cmd);
308}
309
310static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
311{
312 return 0;
313}
314
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100315static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
316 char **bsc_variable)
317{
318 unsigned int nr;
319
320 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
321 cmd->reply = "command incomplete";
322 return 0;
323 }
324
325 *cfg = bsc_config_num(g_nat, nr);
326 if (!*cfg) {
327 cmd->reply = "Unknown BSC";
328 return 0;
329 }
330
331 return 1;
332}
333
334CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
335static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
336{
337 char *bsc_variable;
338 struct bsc_config *bsc_cfg;
339
340 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
341 return CTRL_CMD_ERROR;
342
343 if (strcmp(bsc_variable, "access-list-name") == 0) {
344 cmd->reply = talloc_asprintf(cmd, "%s",
345 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
346 return CTRL_CMD_REPLY;
347 }
348
349 cmd->reply = "unknown command";
350 return CTRL_CMD_ERROR;
351}
352
353static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
354{
355 char *bsc_variable;
356 struct bsc_config *bsc_cfg;
357
358 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
359 return CTRL_CMD_ERROR;
360
361 if (strcmp(bsc_variable, "access-list-name") == 0) {
362 bsc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
363 cmd->reply = talloc_asprintf(cmd, "%s",
364 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
365 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100366 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
367 talloc_free(bsc_cfg->acc_lst_name);
368 bsc_cfg->acc_lst_name = NULL;
369 cmd->reply = "";
370 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100371 }
372
373 cmd->reply = "unknown command";
374 return CTRL_CMD_ERROR;
375}
376
377static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
378{
379 return 0;
380}
381
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200382CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
383static const char *extract_acc_name(const char *var)
384{
385 char *str;
386
387 str = strstr(var, "net.0.add.allow.access-list.");
388 if (!str)
389 return NULL;
390 str += strlen("net.0.add.allow.access-list.");
391 if (strlen(str) == 0)
392 return NULL;
393 return str;
394}
395
396static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
397{
398 cmd->reply = "Append only";
399 return CTRL_CMD_ERROR;
400}
401
402static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
403{
404 const char *access_name = extract_acc_name(cmd->variable);
405 struct bsc_nat_acc_lst *acc = bsc_nat_acc_lst_find(g_nat, access_name);
406 struct bsc_nat_acc_lst_entry *entry;
407 const char *value = cmd->value;
408 int rc;
409
410 entry = bsc_nat_acc_lst_entry_create(acc);
411 if (!entry) {
412 cmd->reply = "OOM";
413 return CTRL_CMD_ERROR;
414 }
415
416 rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
417 if (rc != 0) {
418 cmd->reply = "Failed to compile expression";
419 return CTRL_CMD_ERROR;
420 }
421
422 cmd->reply = "IMSI allow added to access list";
423 return CTRL_CMD_REPLY;
424}
425
426static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
427{
428 const char *access_name = extract_acc_name(cmd->variable);
429 struct bsc_nat_acc_lst *acc = bsc_nat_acc_lst_find(g_nat, access_name);
430
431 if (!acc) {
432 cmd->reply = "Access list not known";
433 return -1;
434 }
435
436 return 0;
437}
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100438
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100439struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port)
440{
441 struct ctrl_handle *ctrl;
442 int rc;
443
444
Harald Welte55dc31e2014-08-24 17:54:49 +0200445 ctrl = bsc_controlif_setup(NULL, OSMO_CTRL_PORT_BSC_NAT);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100446 if (!ctrl) {
447 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
448 return NULL;
449 }
450
451 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
452 if (rc) {
453 fprintf(stderr, "Failed to install the control command. Exiting.\n");
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100454 goto error;
455 }
456 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
457 if (rc) {
458 fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
459 goto error;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100460 }
Holger Hans Peter Freytherab94ca12015-04-05 15:15:36 +0200461 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
462 if (rc) {
463 fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
464 goto error;
465 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100466
467 g_nat = nat;
468 return ctrl;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100469
470error:
471 osmo_fd_unregister(&ctrl->listen_fd);
472 close(ctrl->listen_fd.fd);
473 talloc_free(ctrl);
474 return NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100475}
476
Holger Hans Peter Freyther7c009832015-04-05 13:45:53 +0200477void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
478{
479 struct ctrl_cmd *cmd;
480
481 cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
482 if (!cmd) {
483 LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
484 return;
485 }
486
487 cmd->id = "0";
488 cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
489 conn->cfg->nr);
490 cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
491
492 ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
493 talloc_free(cmd);
494}