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