blob: 1a45e7667cb07df0d84e167fc3237677d93e1fda [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 Freyther98da5442012-11-05 16:04:10 +010031
32#include <unistd.h>
Harald Welteba874b82014-08-20 23:47:15 +020033#include <string.h>
34#include <errno.h>
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +010035
36
37#define NAT_MAX_CTRL_ID 65535
38
39static struct bsc_nat *g_nat;
40
41static int bsc_id_unused(int id, struct bsc_connection *bsc)
42{
43 struct bsc_cmd_list *pending;
44
45 llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
46 if (pending->nat_id == id)
47 return 0;
48 }
49 return 1;
50}
51
52static int get_next_free_bsc_id(struct bsc_connection *bsc)
53{
54 int new_id, overflow = 0;
55
56 new_id = bsc->last_id;
57
58 do {
59 new_id++;
60 if (new_id == NAT_MAX_CTRL_ID) {
61 new_id = 1;
62 overflow++;
63 }
64
65 if (bsc_id_unused(new_id, bsc)) {
66 bsc->last_id = new_id;
67 return new_id;
68 }
69 } while (overflow != 2);
70
71 return -1;
72}
73
74void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
75{
76 llist_del(&pending->list_entry);
77 osmo_timer_del(&pending->timeout);
78 talloc_free(pending->cmd);
79 talloc_free(pending);
80}
81
82static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
83{
84 struct bsc_cmd_list *cmd_entry;
85 int id = atoi(id_str);
86 if (id == 0)
87 return NULL;
88
89 llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
90 if (cmd_entry->nat_id == id) {
91 return cmd_entry;
92 }
93 }
94 return NULL;
95}
96
97int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
98{
99 struct ctrl_cmd *cmd;
100 struct bsc_cmd_list *pending;
101 char *var, *id;
102
103 cmd = ctrl_cmd_parse(bsc, msg);
104 msgb_free(msg);
105
106 if (!cmd) {
107 cmd = talloc_zero(bsc, struct ctrl_cmd);
108 if (!cmd) {
109 LOGP(DNAT, LOGL_ERROR, "OOM!\n");
110 return -ENOMEM;
111 }
112 cmd->type = CTRL_TYPE_ERROR;
113 cmd->id = "err";
114 cmd->reply = "Failed to parse command.";
115 goto err;
116 }
117
118 if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
119 if (cmd->variable) {
120 var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
121 cmd->variable);
122 if (!var) {
123 cmd->type = CTRL_TYPE_ERROR;
124 cmd->reply = "OOM";
125 goto err;
126 }
127 talloc_free(cmd->variable);
128 cmd->variable = var;
129 }
130
131 /* We have to handle TRAPs before matching pending */
132 if (cmd->type == CTRL_TYPE_TRAP) {
133 ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
134 talloc_free(cmd);
135 return 0;
136 }
137
138 /* Find the pending command */
139 pending = bsc_get_pending(bsc, cmd->id);
140 if (pending) {
141 id = talloc_strdup(cmd, pending->cmd->id);
142 if (!id) {
143 cmd->type = CTRL_TYPE_ERROR;
144 cmd->reply = "OOM";
145 goto err;
146 }
147 cmd->id = id;
148 ctrl_cmd_send(&pending->ccon->write_queue, cmd);
149 bsc_nat_ctrl_del_pending(pending);
150 } else {
151 /* We need to handle TRAPS here */
152 if ((cmd->type != CTRL_TYPE_ERROR) &&
153 (cmd->type != CTRL_TYPE_TRAP)) {
154 LOGP(DNAT, LOGL_NOTICE, "Got control message "
155 "from BSC without pending entry\n");
156 cmd->type = CTRL_TYPE_ERROR;
157 cmd->reply = "No request outstanding";
158 goto err;
159 }
160 }
161 }
162 talloc_free(cmd);
163 return 0;
164err:
165 ctrl_cmd_send(&bsc->write_queue, cmd);
166 talloc_free(cmd);
167 return 0;
168}
169
170static void pending_timeout_cb(void *data)
171{
172 struct bsc_cmd_list *pending = data;
173 LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
174 pending->cmd->type = CTRL_TYPE_ERROR;
175 pending->cmd->reply = "Command timed out";
176 ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
177
178 bsc_nat_ctrl_del_pending(pending);
179}
180
181static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
182{
183 struct bsc_connection *bsc;
184 struct bsc_cmd_list *pending, *tmp;
185
186 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
187 llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
188 if (pending->ccon == connection)
189 bsc_nat_ctrl_del_pending(pending);
190 }
191 }
192}
193
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100194static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
195{
196 char *nr_str, *tmp, *saveptr = NULL;
197
198 tmp = strtok_r(variable, ".", &saveptr);
199 tmp = strtok_r(NULL, ".", &saveptr);
200 tmp = strtok_r(NULL, ".", &saveptr);
201 nr_str = strtok_r(NULL, ".", &saveptr);
202 if (!nr_str)
203 return 0;
204 *nr = atoi(nr_str);
205
206 tmp = strtok_r(NULL, "\0", &saveptr);
207 if (!tmp)
208 return 0;
209
210 *bsc_variable = tmp;
211 return 1;
212}
213
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100214static int forward_to_bsc(struct ctrl_cmd *cmd)
215{
216 int ret = CTRL_CMD_HANDLED;
217 struct ctrl_cmd *bsc_cmd = NULL;
218 struct bsc_connection *bsc;
219 struct bsc_cmd_list *pending;
220 unsigned int nr;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100221 char *bsc_variable;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100222
223 /* Skip over the beginning (bsc.) */
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100224 if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100225 cmd->reply = "command incomplete";
226 goto err;
227 }
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100228
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100229
230 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
231 if (!bsc->cfg)
232 continue;
233 if (!bsc->authenticated)
234 continue;
235 if (bsc->cfg->nr == nr) {
236 /* Add pending command to list */
237 pending = talloc_zero(bsc, struct bsc_cmd_list);
238 if (!pending) {
239 cmd->reply = "OOM";
240 goto err;
241 }
242
243 pending->nat_id = get_next_free_bsc_id(bsc);
244 if (pending->nat_id < 0) {
245 cmd->reply = "No free ID found";
246 goto err;
247 }
248
249 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
250 if (!bsc_cmd) {
251 cmd->reply = "Could not forward command";
252 goto err;
253 }
254
255 talloc_free(bsc_cmd->id);
256 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
257 if (!bsc_cmd->id) {
258 cmd->reply = "OOM";
259 goto err;
260 }
261
262 talloc_free(bsc_cmd->variable);
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100263 bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100264 if (!bsc_cmd->variable) {
265 cmd->reply = "OOM";
266 goto err;
267 }
268
269 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
270 cmd->reply = "Sending failed";
271 goto err;
272 }
273 pending->ccon = cmd->ccon;
274 pending->ccon->closed_cb = ctrl_conn_closed_cb;
275 pending->cmd = cmd;
276
277 /* Setup the timeout */
278 pending->timeout.data = pending;
279 pending->timeout.cb = pending_timeout_cb;
280 /* TODO: Make timeout configurable */
281 osmo_timer_schedule(&pending->timeout, 10, 0);
282 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
283
284 goto done;
285 }
286 }
287 /* We end up here if there's no bsc to handle our LAC */
288 cmd->reply = "no BSC with this nr";
289err:
290 ret = CTRL_CMD_ERROR;
291done:
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100292 talloc_free(bsc_cmd);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100293 return ret;
294
295}
296
297
298CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
299static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
300{
301 return forward_to_bsc(cmd);
302}
303
304static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
305{
306 return forward_to_bsc(cmd);
307}
308
309static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
310{
311 return 0;
312}
313
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100314static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
315 char **bsc_variable)
316{
317 unsigned int nr;
318
319 if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
320 cmd->reply = "command incomplete";
321 return 0;
322 }
323
324 *cfg = bsc_config_num(g_nat, nr);
325 if (!*cfg) {
326 cmd->reply = "Unknown BSC";
327 return 0;
328 }
329
330 return 1;
331}
332
333CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
334static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
335{
336 char *bsc_variable;
337 struct bsc_config *bsc_cfg;
338
339 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
340 return CTRL_CMD_ERROR;
341
342 if (strcmp(bsc_variable, "access-list-name") == 0) {
343 cmd->reply = talloc_asprintf(cmd, "%s",
344 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
345 return CTRL_CMD_REPLY;
346 }
347
348 cmd->reply = "unknown command";
349 return CTRL_CMD_ERROR;
350}
351
352static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
353{
354 char *bsc_variable;
355 struct bsc_config *bsc_cfg;
356
357 if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
358 return CTRL_CMD_ERROR;
359
360 if (strcmp(bsc_variable, "access-list-name") == 0) {
361 bsc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
362 cmd->reply = talloc_asprintf(cmd, "%s",
363 bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
364 return CTRL_CMD_REPLY;
Holger Hans Peter Freyther472f3bd2014-03-04 15:20:27 +0100365 } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
366 talloc_free(bsc_cfg->acc_lst_name);
367 bsc_cfg->acc_lst_name = NULL;
368 cmd->reply = "";
369 return CTRL_CMD_REPLY;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100370 }
371
372 cmd->reply = "unknown command";
373 return CTRL_CMD_ERROR;
374}
375
376static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
377{
378 return 0;
379}
380
381
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100382struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port)
383{
384 struct ctrl_handle *ctrl;
385 int rc;
386
387
Harald Welte55dc31e2014-08-24 17:54:49 +0200388 ctrl = bsc_controlif_setup(NULL, OSMO_CTRL_PORT_BSC_NAT);
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100389 if (!ctrl) {
390 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
391 return NULL;
392 }
393
394 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
395 if (rc) {
396 fprintf(stderr, "Failed to install the control command. Exiting.\n");
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100397 goto error;
398 }
399 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
400 if (rc) {
401 fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
402 goto error;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100403 }
404
405 g_nat = nat;
406 return ctrl;
Holger Hans Peter Freytherbc3780a2014-02-04 19:12:04 +0100407
408error:
409 osmo_fd_unregister(&ctrl->listen_fd);
410 close(ctrl->listen_fd.fd);
411 talloc_free(ctrl);
412 return NULL;
Holger Hans Peter Freyther98da5442012-11-05 16:04:10 +0100413}
414