blob: 32ae0681b56722e54f540d5290a795392ccc1eb1 [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
189static int forward_to_bsc(struct ctrl_cmd *cmd)
190{
191 int ret = CTRL_CMD_HANDLED;
192 struct ctrl_cmd *bsc_cmd = NULL;
193 struct bsc_connection *bsc;
194 struct bsc_cmd_list *pending;
195 unsigned int nr;
196 char *nr_str, *tmp, *saveptr;
197
198 /* Skip over the beginning (bsc.) */
199 tmp = strtok_r(cmd->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 cmd->reply = "command incomplete";
205 goto err;
206 }
207 nr = atoi(nr_str);
208
209 tmp = strtok_r(NULL, "\0", &saveptr);
210 if (!tmp) {
211 cmd->reply = "command incomplete";
212 goto err;
213 }
214
215 llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
216 if (!bsc->cfg)
217 continue;
218 if (!bsc->authenticated)
219 continue;
220 if (bsc->cfg->nr == nr) {
221 /* Add pending command to list */
222 pending = talloc_zero(bsc, struct bsc_cmd_list);
223 if (!pending) {
224 cmd->reply = "OOM";
225 goto err;
226 }
227
228 pending->nat_id = get_next_free_bsc_id(bsc);
229 if (pending->nat_id < 0) {
230 cmd->reply = "No free ID found";
231 goto err;
232 }
233
234 bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
235 if (!bsc_cmd) {
236 cmd->reply = "Could not forward command";
237 goto err;
238 }
239
240 talloc_free(bsc_cmd->id);
241 bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
242 if (!bsc_cmd->id) {
243 cmd->reply = "OOM";
244 goto err;
245 }
246
247 talloc_free(bsc_cmd->variable);
248 bsc_cmd->variable = talloc_strdup(bsc_cmd, tmp);
249 if (!bsc_cmd->variable) {
250 cmd->reply = "OOM";
251 goto err;
252 }
253
254 if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
255 cmd->reply = "Sending failed";
256 goto err;
257 }
258 pending->ccon = cmd->ccon;
259 pending->ccon->closed_cb = ctrl_conn_closed_cb;
260 pending->cmd = cmd;
261
262 /* Setup the timeout */
263 pending->timeout.data = pending;
264 pending->timeout.cb = pending_timeout_cb;
265 /* TODO: Make timeout configurable */
266 osmo_timer_schedule(&pending->timeout, 10, 0);
267 llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
268
269 goto done;
270 }
271 }
272 /* We end up here if there's no bsc to handle our LAC */
273 cmd->reply = "no BSC with this nr";
274err:
275 ret = CTRL_CMD_ERROR;
276done:
277 if (bsc_cmd)
278 talloc_free(bsc_cmd);
279 return ret;
280
281}
282
283
284CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
285static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
286{
287 return forward_to_bsc(cmd);
288}
289
290static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
291{
292 return forward_to_bsc(cmd);
293}
294
295static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
296{
297 return 0;
298}
299
300struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port)
301{
302 struct ctrl_handle *ctrl;
303 int rc;
304
305
306 ctrl = controlif_setup(NULL, 4250);
307 if (!ctrl) {
308 fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
309 return NULL;
310 }
311
312 rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
313 if (rc) {
314 fprintf(stderr, "Failed to install the control command. Exiting.\n");
315 osmo_fd_unregister(&ctrl->listen_fd);
316 close(ctrl->listen_fd.fd);
317 talloc_free(ctrl);
318 return NULL;
319 }
320
321 g_nat = nat;
322 return ctrl;
323}
324