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