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