blob: 217e0bdf16b0b5efb8fbb6b69e85ac8aac221f61 [file] [log] [blame]
Harald Welte2ca7c312009-12-23 22:44:04 +01001/* OpenBSC Abis/IP proxy ip.access nanoBTS */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 *
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 General Public License as published by
9 * the Free Software Foundation; either version 2 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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23#include <stdio.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <string.h>
28#include <signal.h>
29#include <time.h>
30#include <sys/fcntl.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/ioctl.h>
34#include <arpa/inet.h>
35#include <netinet/in.h>
36
37#include <openbsc/gsm_data.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010038#include <osmocore/select.h>
39#include <osmocore/tlv.h>
40#include <osmocore/msgb.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010041#include <openbsc/debug.h>
42#include <openbsc/ipaccess.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010043#include <osmocore/talloc.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010044
45static struct debug_target *stderr_target;
46
47/* one instance of an ip.access protocol proxy */
48struct ipa_proxy {
49 /* socket where we listen for incoming OML from BTS */
50 struct bsc_fd oml_listen_fd;
51 /* socket where we listen for incoming RSL from BTS */
52 struct bsc_fd rsl_listen_fd;
53 /* list of BTS's (struct ipa_bts_conn */
54 struct llist_head bts_list;
55 /* the BSC reconnect timer */
56 struct timer_list reconn_timer;
57};
58
59/* global pointer to the proxy structure */
60static struct ipa_proxy *ipp;
61
62struct ipa_proxy_conn {
63 struct bsc_fd fd;
64 struct llist_head tx_queue;
65 struct ipa_bts_conn *bts_conn;
66};
67
68#define MAX_TRX 4
69
70/* represents a particular BTS in our proxy */
71struct ipa_bts_conn {
72 /* list of BTS's (ipa_proxy->bts_list) */
73 struct llist_head list;
74 /* back pointer to the proxy which we belong to */
75 struct ipa_proxy *ipp;
76 /* the unit ID as determined by CCM */
77 struct {
78 u_int16_t site_id;
79 u_int16_t bts_id;
80 } unit_id;
81
82 /* incoming connections from BTS */
83 struct ipa_proxy_conn *oml_conn;
84 struct ipa_proxy_conn *rsl_conn[MAX_TRX];
85
86 /* outgoing connections to BSC */
87 struct ipa_proxy_conn *bsc_oml_conn;
88 struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
89
90 /* UDP sockets for BTS and BSC injection */
91 struct bsc_fd udp_bts_fd;
92 struct bsc_fd udp_bsc_fd;
93
94 char *id_tags[0xff];
95 u_int8_t *id_resp;
96 unsigned int id_resp_len;
97};
98
99enum ipp_fd_type {
100 OML_FROM_BTS = 1,
101 RSL_FROM_BTS = 2,
102 OML_TO_BSC = 3,
103 RSL_TO_BSC = 4,
104 UDP_TO_BTS = 5,
105 UDP_TO_BSC = 6,
106};
107
108/* some of the code against we link from OpenBSC needs this */
109void *tall_bsc_ctx;
110
111static char *listen_ipaddr;
112static char *bsc_ipaddr;
113
114#define PROXY_ALLOC_SIZE 300
115
116static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
117static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
118static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
119 0x01, IPAC_IDTAG_UNIT,
120 0x01, IPAC_IDTAG_MACADDR,
121 0x01, IPAC_IDTAG_LOCATION1,
122 0x01, IPAC_IDTAG_LOCATION2,
123 0x01, IPAC_IDTAG_EQUIPVERS,
124 0x01, IPAC_IDTAG_SWVERSION,
125 0x01, IPAC_IDTAG_UNITNAME,
126 0x01, IPAC_IDTAG_SERNR,
127 };
128
129static const char *idtag_names[] = {
130 [IPAC_IDTAG_SERNR] = "Serial_Number",
131 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
132 [IPAC_IDTAG_LOCATION1] = "Location_1",
133 [IPAC_IDTAG_LOCATION2] = "Location_2",
134 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
135 [IPAC_IDTAG_SWVERSION] = "Software_Version",
136 [IPAC_IDTAG_IPADDR] = "IP_Address",
137 [IPAC_IDTAG_MACADDR] = "MAC_Address",
138 [IPAC_IDTAG_UNIT] = "Unit_ID",
139};
140
141static const char *ipac_idtag_name(int tag)
142{
143 if (tag >= ARRAY_SIZE(idtag_names))
144 return "unknown";
145
146 return idtag_names[tag];
147}
148
149static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
150{
151 u_int8_t t_len;
152 u_int8_t t_tag;
153 u_int8_t *cur = buf;
154
155 while (cur < buf + len) {
156 t_len = *cur++;
157 t_tag = *cur++;
158
159 DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
160
161 dec->lv[t_tag].len = t_len;
162 dec->lv[t_tag].val = cur;
163
164 cur += t_len;
165 }
166 return 0;
167}
168
169static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
170 u_int16_t *trx_id)
171{
172 unsigned long ul;
173 char *endptr;
174 const char *nptr;
175
176 nptr = str;
177 ul = strtoul(nptr, &endptr, 10);
178 if (endptr <= nptr)
179 return -EINVAL;
180 if (site_id)
181 *site_id = ul & 0xffff;
182
183 if (*endptr++ != '/')
184 return -EINVAL;
185
186 nptr = endptr;
187 ul = strtoul(nptr, &endptr, 10);
188 if (endptr <= nptr)
189 return -EINVAL;
190 if (bts_id)
191 *bts_id = ul & 0xffff;
192
193 if (*endptr++ != '/')
194 return -EINVAL;
195
196 nptr = endptr;
197 ul = strtoul(nptr, &endptr, 10);
198 if (endptr <= nptr)
199 return -EINVAL;
200 if (trx_id)
201 *trx_id = ul & 0xffff;
202
203 return 0;
204}
205
206static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
207 u_int16_t site_id,
208 u_int16_t bts_id)
209{
210 struct ipa_bts_conn *ipbc;
211
212 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
213 if (ipbc->unit_id.site_id == site_id &&
214 ipbc->unit_id.bts_id == bts_id)
215 return ipbc;
216 }
217
218 return NULL;
219}
220
221struct ipa_proxy_conn *alloc_conn(void)
222{
223 struct ipa_proxy_conn *ipc;
224
225 ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
226 if (!ipc)
227 return NULL;
228
229 INIT_LLIST_HEAD(&ipc->tx_queue);
230
231 return ipc;
232}
233
234static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
235{
236 unsigned int i, len;
237
238 for (i = 0; i <= 0xff; i++) {
239 if (!TLVP_PRESENT(tlvp, i))
240 continue;
241
242 len = TLVP_LEN(tlvp, i);
243#if 0
244 if (!ipbc->id_tags[i])
245 ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
246 else
247#endif
Harald Weltea16ef3d2009-12-23 23:35:51 +0100248 ipbc->id_tags[i] = talloc_realloc_size(ipbc,
Harald Welte2ca7c312009-12-23 22:44:04 +0100249 ipbc->id_tags[i], len);
250 if (!ipbc->id_tags[i])
251 return -ENOMEM;
252
253 memset(ipbc->id_tags[i], 0, len);
254 //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
255 }
256 return 0;
257}
258
259
260static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
261
262#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
263
264static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
265 struct ipa_bts_conn *ipbc, u_int8_t trx_id)
266{
267 if (ipbc)
268 debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
269 ipbc->unit_id.bts_id, trx_id);
270 else
271 debugp2(ss, lvl, file, line, 0, "unknown ");
272}
273
274/* UDP socket handling */
275
276static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr,
277 int (*cb)(struct bsc_fd *fd, unsigned int what),
278 void *data)
279{
280 struct sockaddr_in addr;
281 int ret, on = 1;
282
283 bfd->fd = socket(AF_INET, SOCK_DGRAM, proto);
284 bfd->cb = cb;
285 bfd->when = BSC_FD_READ;
286 bfd->data = data;
287 bfd->priv_nr = priv_nr;
288
289 memset(&addr, 0, sizeof(addr));
290 addr.sin_family = AF_INET;
291 addr.sin_port = htons(port);
292 addr.sin_addr.s_addr = INADDR_ANY;
293
294 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
295
296 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
297 if (ret < 0) {
298 LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n",
299 strerror(errno));
300 return -EIO;
301 }
302
303 ret = bsc_register_fd(bfd);
304 if (ret < 0) {
305 perror("register UDP fd");
306 return ret;
307 }
308 return 0;
309}
310
311static int handle_udp_read(struct bsc_fd *bfd)
312{
313 struct ipa_bts_conn *ipbc = bfd->data;
314 struct ipa_proxy_conn *other_conn = NULL;
315 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
316 struct ipaccess_head *hh;
317 int ret;
318
319 /* with UDP sockets, we cannot read partial packets but have to read
320 * all of it in one go */
321 hh = (struct ipaccess_head *) msg->data;
322 ret = recv(bfd->fd, msg->data, msg->data_len, 0);
323 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100324 if (errno != EAGAIN)
325 LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100326 msgb_free(msg);
327 return ret;
328 }
329 if (ret == 0) {
330 DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
331 bsc_unregister_fd(bfd);
332 close(bfd->fd);
333 bfd->fd = -1;
334 msgb_free(msg);
335 return -EIO;
336 }
337 if (ret < sizeof(*hh)) {
338 DEBUGP(DINP, "could not even read header!?!\n");
339 msgb_free(msg);
340 return -EIO;
341 }
342 msgb_put(msg, ret);
343 msg->l2h = msg->data + sizeof(*hh);
344 DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
345
346 if (hh->len != msg->len - sizeof(*hh)) {
347 DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
348 msg->len, msg->len - 3, hh->len);
349 msgb_free(msg);
350 return -EIO;
351 }
352
353 switch (bfd->priv_nr & 0xff) {
354 case UDP_TO_BTS:
355 /* injection towards BTS */
356 switch (hh->proto) {
357 case IPAC_PROTO_RSL:
358 /* FIXME: what to do about TRX > 0 */
359 other_conn = ipbc->rsl_conn[0];
360 break;
361 default:
362 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
363 "OML FD\n", hh->proto);
364 /* fall through */
365 case IPAC_PROTO_IPACCESS:
366 case IPAC_PROTO_OML:
367 other_conn = ipbc->oml_conn;
368 break;
369 }
370 break;
371 case UDP_TO_BSC:
372 /* injection towards BSC */
373 switch (hh->proto) {
374 case IPAC_PROTO_RSL:
375 /* FIXME: what to do about TRX > 0 */
376 other_conn = ipbc->bsc_rsl_conn[0];
377 break;
378 default:
379 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
380 "OML FD\n", hh->proto);
381 case IPAC_PROTO_IPACCESS:
382 case IPAC_PROTO_OML:
383 other_conn = ipbc->bsc_oml_conn;
384 break;
385 }
386 break;
387 default:
388 DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
389 break;
390 }
391
392 if (other_conn) {
393 /* enqueue the message for TX on the respective FD */
394 msgb_enqueue(&other_conn->tx_queue, msg);
395 other_conn->fd.when |= BSC_FD_WRITE;
396 } else
397 msgb_free(msg);
398
399 return 0;
400}
401
402static int handle_udp_write(struct bsc_fd *bfd)
403{
404 /* not implemented yet */
405 bfd->when &= ~BSC_FD_WRITE;
406
407 return -EIO;
408}
409
410/* callback from select.c in case one of the fd's can be read/written */
411static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
412{
413 int rc = 0;
414
415 if (what & BSC_FD_READ)
416 rc = handle_udp_read(bfd);
417 if (what & BSC_FD_WRITE)
418 rc = handle_udp_write(bfd);
419
420 return rc;
421}
422
423
424static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
425 u_int16_t site_id, u_int16_t bts_id,
426 u_int16_t trx_id, struct tlv_parsed *tlvp,
427 struct msgb *msg)
428{
429 struct ipa_bts_conn *ipbc;
430 u_int16_t udp_port;
431 int ret = 0;
432 struct sockaddr_in sin;
433
434 memset(&sin, 0, sizeof(sin));
435 sin.sin_family = AF_INET;
436 inet_aton(bsc_ipaddr, &sin.sin_addr);
437
438 DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
439 site_id, bts_id, trx_id);
440
441 /* OML needs to be established before RSL */
442 if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
443 DEBUGPC(DINP, "Not a OML connection ?!?\n");
444 return -EIO;
445 }
446
447 /* allocate new BTS connection data structure */
448 ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
449 if (!ipbc) {
450 ret = -ENOMEM;
451 goto err_out;
452 }
453
454 DEBUGPC(DINP, "Created BTS Conn data structure\n");
455 ipbc->ipp = ipp;
456 ipbc->unit_id.site_id = site_id;
457 ipbc->unit_id.bts_id = bts_id;
458 ipbc->oml_conn = ipc;
459 ipc->bts_conn = ipbc;
460
461 /* store the content of the ID TAGS for later reference */
462 store_idtags(ipbc, tlvp);
463 ipbc->id_resp_len = msg->len;
464 ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
465 memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
466
467 /* Create OML TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100468 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100469 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
470 if (!ipbc->bsc_oml_conn) {
471 ret = -EIO;
472 goto err_bsc_conn;
473 }
474
475 DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
476 site_id, bts_id, trx_id);
477
478 /* Create UDP socket for BTS packet injection */
479 udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
480 ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP,
481 UDP_TO_BTS, udp_fd_cb, ipbc);
482 if (ret < 0)
483 goto err_udp_bts;
484 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
485 "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
486
487 /* Create UDP socket for BSC packet injection */
488 udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
489 ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP,
490 UDP_TO_BSC, udp_fd_cb, ipbc);
491 if (ret < 0)
492 goto err_udp_bsc;
493 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
494 "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
495 llist_add(&ipbc->list, &ipp->bts_list);
496
497 return 0;
498
499err_udp_bsc:
500 bsc_unregister_fd(&ipbc->udp_bts_fd);
501err_udp_bts:
502 bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
503 close(ipbc->bsc_oml_conn->fd.fd);
504 talloc_free(ipbc->bsc_oml_conn);
505 ipbc->bsc_oml_conn = NULL;
506err_bsc_conn:
507 talloc_free(ipbc->id_resp);
508 talloc_free(ipbc);
509#if 0
510 bsc_unregister_fd(bfd);
511 close(bfd->fd);
512 talloc_free(bfd);
513#endif
514err_out:
515 return ret;
516}
517
518static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
519 struct bsc_fd *bfd)
520{
521 struct tlv_parsed tlvp;
522 u_int8_t msg_type = *(msg->l2h);
523 u_int16_t site_id, bts_id, trx_id;
524 struct ipa_bts_conn *ipbc;
525 int ret = 0;
526
527 switch (msg_type) {
528 case IPAC_MSGT_PING:
529 ret = write(bfd->fd, pong, sizeof(pong));
530 if (ret < 0)
531 return ret;
532 if (ret < sizeof(pong)) {
533 DEBUGP(DINP, "short write\n");
534 return -EIO;
535 }
536 break;
537 case IPAC_MSGT_PONG:
538 DEBUGP(DMI, "PONG!\n");
539 break;
540 case IPAC_MSGT_ID_RESP:
541 DEBUGP(DMI, "ID_RESP ");
542 /* parse tags, search for Unit ID */
543 ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
544 msgb_l2len(msg)-2);
545 DEBUGP(DMI, "\n");
546
547 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
548 LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
549 return -EIO;
550 }
551
552 /* lookup BTS, create sign_link, ... */
553 parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
554 &site_id, &bts_id, &trx_id);
555 ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
556 if (!ipbc) {
557 /* We have not found an ipbc (per-bts proxy instance)
558 * for this BTS yet. The first connection of a new BTS must
559 * be a OML connection. We allocate the associated data structures,
560 * and try to connect to the remote end */
561
562 return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
563 trx_id, &tlvp, msg);
564 /* if this fails, the caller will clean up bfd */
565 } else {
566 struct sockaddr_in sin;
567 memset(&sin, 0, sizeof(sin));
568 sin.sin_family = AF_INET;
569 inet_aton(bsc_ipaddr, &sin.sin_addr);
570
571 DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
572 site_id, bts_id, trx_id);
573
574 if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
575 LOGP(DINP, LOGL_ERROR, "Second OML connection from "
576 "same BTS ?!?\n");
577 return 0;
578 }
579
580 if (trx_id > MAX_TRX) {
581 LOGP(DINP, LOGL_ERROR, "We don't support more "
582 "than %u TRX\n", MAX_TRX);
583 return -EINVAL;
584 }
585
586 ipc->bts_conn = ipbc;
587 /* store TRX number in higher 8 bit of the bfd private number */
588 bfd->priv_nr |= trx_id << 8;
589 ipbc->rsl_conn[trx_id] = ipc;
590
591 /* Create RSL TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100592 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100593 ipbc->bsc_rsl_conn[trx_id] =
594 connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
595 if (!ipbc->bsc_oml_conn)
596 return -EIO;
597 DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
598 site_id, bts_id, trx_id);
599 }
600 break;
601 case IPAC_MSGT_ID_GET:
602 DEBUGP(DMI, "ID_GET\n");
603 if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
604 (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
605 DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
606 return -EIO;
607 }
608 ipbc = ipc->bts_conn;
609 if (!ipbc) {
610 DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
611 return -EIO;
612 }
613 ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
614 break;
615 case IPAC_MSGT_ID_ACK:
616 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
617 ret = write(bfd->fd, id_ack, sizeof(id_ack));
618 break;
619 }
620 return 0;
621}
622
623struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
624{
625 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
626 struct ipaccess_head *hh;
627 int len, ret = 0;
628
629 if (!msg) {
630 *error = -ENOMEM;
631 return NULL;
632 }
633
634 /* first read our 3-byte header */
635 hh = (struct ipaccess_head *) msg->data;
636 ret = recv(bfd->fd, msg->data, 3, 0);
637 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100638 if (errno != EAGAIN)
Harald Welteda956932009-12-24 12:49:43 +0100639 LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100640 msgb_free(msg);
641 *error = ret;
642 return NULL;
643 } else if (ret == 0) {
644 msgb_free(msg);
645 *error = ret;
646 return NULL;
647 }
648
649 msgb_put(msg, ret);
650
651 /* then read te length as specified in header */
652 msg->l2h = msg->data + sizeof(*hh);
653 len = ntohs(hh->len);
654 ret = recv(bfd->fd, msg->l2h, len, 0);
655 if (ret < len) {
656 LOGP(DINP, LOGL_ERROR, "short read!\n");
657 msgb_free(msg);
658 *error = -EIO;
659 return NULL;
660 }
661 msgb_put(msg, ret);
662
663 return msg;
664}
665
666static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
667 unsigned int priv_nr)
668{
669 struct ipa_proxy_conn *bsc_conn;
670 unsigned int trx_id = priv_nr >> 8;
671
672 switch (priv_nr & 0xff) {
673 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
674 bsc_conn = ipbc->bsc_oml_conn;
675 break;
676 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
677 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
678 break;
679 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
680 bsc_conn = ipbc->oml_conn;
681 break;
682 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
683 bsc_conn = ipbc->rsl_conn[trx_id];
684 break;
685 default:
686 bsc_conn = NULL;
687 break;
688 }
689 return bsc_conn;
690}
691
692static void reconn_tmr_cb(void *data)
693{
694 struct ipa_proxy *ipp = data;
695 struct ipa_bts_conn *ipbc;
696 struct sockaddr_in sin;
697 int i;
698
699 DEBUGP(DINP, "Running reconnect timer\n");
700
701 memset(&sin, 0, sizeof(sin));
702 sin.sin_family = AF_INET;
703 inet_aton(bsc_ipaddr, &sin.sin_addr);
704
705 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
706 /* if OML to BSC is dead, try to restore it */
707 if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
Harald Welte87ed5cd2009-12-23 22:47:53 +0100708 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100709 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
710 LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
711 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
712 if (!ipbc->bsc_oml_conn)
713 goto reschedule;
714 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
715 LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
716 }
717 /* if we (still) don't have a OML connection, skip RSL */
718 if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
719 continue;
720
721 for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
722 unsigned int priv_nr;
723 /* don't establish RSL links which we don't have */
724 if (!ipbc->rsl_conn[i])
725 continue;
726 if (ipbc->bsc_rsl_conn[i])
727 continue;
728 priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
729 priv_nr &= ~0xff;
730 priv_nr |= RSL_TO_BSC;
Harald Welte87ed5cd2009-12-23 22:47:53 +0100731 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100732 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
733 LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
734 ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
735 if (!ipbc->bsc_rsl_conn)
736 goto reschedule;
737 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
738 LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
739 }
740 }
741 return;
742
743reschedule:
744 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
745}
746
747static void handle_dead_socket(struct bsc_fd *bfd)
748{
749 struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
750 struct ipa_proxy_conn *bsc_conn; /* remote conn */
751 struct ipa_bts_conn *ipbc = ipc->bts_conn;
752 unsigned int trx_id = bfd->priv_nr >> 8;
753 struct msgb *msg, *msg2;
754
755 bsc_unregister_fd(bfd);
756 close(bfd->fd);
757 bfd->fd = -1;
758
759 /* FIXME: clear tx_queue, remove all references, etc. */
760 llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
761 msgb_free(msg);
762
763 switch (bfd->priv_nr & 0xff) {
764 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
765 ipbc->oml_conn = NULL;
766 bsc_conn = ipbc->bsc_oml_conn;
767 /* close the connection to the BSC */
768 bsc_unregister_fd(&bsc_conn->fd);
769 close(bsc_conn->fd.fd);
770 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
771 msgb_free(msg);
772 talloc_free(bsc_conn);
773 ipbc->bsc_oml_conn = NULL;
774 /* FIXME: do we need to delete the entire ipbc ? */
775 break;
776 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
777 ipbc->rsl_conn[trx_id] = NULL;
778 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
779 /* close the connection to the BSC */
780 bsc_unregister_fd(&bsc_conn->fd);
781 close(bsc_conn->fd.fd);
782 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
783 msgb_free(msg);
784 talloc_free(bsc_conn);
785 ipbc->bsc_rsl_conn[trx_id] = NULL;
786 break;
787 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
788 ipbc->bsc_oml_conn = NULL;
789 bsc_conn = ipbc->oml_conn;
790 /* start reconnect timer */
791 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
792 break;
793 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
794 ipbc->bsc_rsl_conn[trx_id] = NULL;
795 bsc_conn = ipbc->rsl_conn[trx_id];
796 /* start reconnect timer */
797 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
798 break;
799 default:
800 bsc_conn = NULL;
801 break;
802 }
803
804 talloc_free(ipc);
805}
806
807static int handle_tcp_read(struct bsc_fd *bfd)
808{
809 struct ipa_proxy_conn *ipc = bfd->data;
810 struct ipa_bts_conn *ipbc = ipc->bts_conn;
811 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100812 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100813 struct ipaccess_head *hh;
814 int ret = 0;
815 char *btsbsc;
816
Harald Welte2ca7c312009-12-23 22:44:04 +0100817 if ((bfd->priv_nr & 0xff) <= 2)
818 btsbsc = "BTS";
819 else
820 btsbsc = "BSC";
821
822 msg = ipaccess_read_msg(bfd, &ret);
823 if (!msg) {
824 if (ret == 0) {
825 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
826 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
827 "dead socket\n", btsbsc);
828 handle_dead_socket(bfd);
829 }
830 return ret;
831 }
832
833 msgb_put(msg, ret);
834 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
835 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
836
837 hh = (struct ipaccess_head *) msg->data;
838 if (hh->proto == IPAC_PROTO_IPACCESS) {
839 ret = ipaccess_rcvmsg(ipc, msg, bfd);
840 if (ret < 0) {
841 bsc_unregister_fd(bfd);
842 close(bfd->fd);
843 bfd->fd = -1;
844 talloc_free(bfd);
845 }
846 /* we do not forward the CCM protocol through the
847 * proxy but rather terminate it ourselves */
848 msgb_free(msg);
849 return ret;
850 }
851
852 if (!ipbc) {
853 LOGP(DINP, LOGL_ERROR,
854 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
855 msgb_free(msg);
856 return -EIO;
857 }
858
859 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
860 if (bsc_conn) {
861 /* enqueue packet towards BSC */
862 msgb_enqueue(&bsc_conn->tx_queue, msg);
863 /* mark respective filedescriptor as 'we want to write' */
864 bsc_conn->fd.when |= BSC_FD_WRITE;
865 } else {
866 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
867 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
868 "since remote connection is dead\n", btsbsc);
869 msgb_free(msg);
870 }
871
872 return ret;
873}
874
875/* a TCP socket is ready to be written to */
876static int handle_tcp_write(struct bsc_fd *bfd)
877{
878 struct ipa_proxy_conn *ipc = bfd->data;
879 struct ipa_bts_conn *ipbc = ipc->bts_conn;
880 struct llist_head *lh;
881 struct msgb *msg;
882 char *btsbsc;
883 int ret;
884
885 if ((bfd->priv_nr & 0xff) <= 2)
886 btsbsc = "BTS";
887 else
888 btsbsc = "BSC";
889
890
891 /* get the next msg for this timeslot */
892 if (llist_empty(&ipc->tx_queue)) {
893 bfd->when &= ~BSC_FD_WRITE;
894 return 0;
895 }
896 lh = ipc->tx_queue.next;
897 llist_del(lh);
898 msg = llist_entry(lh, struct msgb, list);
899
900 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
901 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
902 hexdump(msg->data, msg->len));
903
904 ret = send(bfd->fd, msg->data, msg->len, 0);
905 msgb_free(msg);
906
907 if (ret == 0) {
908 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
909 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
910 handle_dead_socket(bfd);
911 }
912
913 return ret;
914}
915
916/* callback from select.c in case one of the fd's can be read/written */
917static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
918{
919 int rc = 0;
920
921 if (what & BSC_FD_READ) {
922 rc = handle_tcp_read(bfd);
923 if (rc < 0)
924 return rc;
925 }
926 if (what & BSC_FD_WRITE)
927 rc = handle_tcp_write(bfd);
928
929 return rc;
930}
931
932/* callback of the listening filedescriptor */
933static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
934{
935 int ret;
936 struct ipa_proxy_conn *ipc;
937 struct bsc_fd *bfd;
938 struct sockaddr_in sa;
939 socklen_t sa_len = sizeof(sa);
940
941 if (!(what & BSC_FD_READ))
942 return 0;
943
944 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
945 if (ret < 0) {
946 perror("accept");
947 return ret;
948 }
949 DEBUGP(DINP, "accept()ed new %s link from %s\n",
950 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
951 inet_ntoa(sa.sin_addr));
952
953 ipc = alloc_conn();
954 if (!ipc) {
955 close(ret);
956 return -ENOMEM;
957 }
958
959 bfd = &ipc->fd;
960 bfd->fd = ret;
961 bfd->data = ipc;
962 bfd->priv_nr = listen_bfd->priv_nr;
963 bfd->cb = ipaccess_fd_cb;
964 bfd->when = BSC_FD_READ;
965 ret = bsc_register_fd(bfd);
966 if (ret < 0) {
967 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
968 close(bfd->fd);
969 talloc_free(ipc);
970 return ret;
971 }
972
973 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
974 ret = write(bfd->fd, id_req, sizeof(id_req));
975
976 return 0;
977}
978
979static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
980 int (*cb)(struct bsc_fd *fd, unsigned int what))
981{
982 struct sockaddr_in addr;
983 int ret, on = 1;
984
985 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
986 bfd->cb = cb;
987 bfd->when = BSC_FD_READ;
988 bfd->priv_nr = priv_nr;
989
990 memset(&addr, 0, sizeof(addr));
991 addr.sin_family = AF_INET;
992 addr.sin_port = htons(port);
993 if (!listen_ipaddr)
994 addr.sin_addr.s_addr = INADDR_ANY;
995 else
996 inet_aton(listen_ipaddr, &addr.sin_addr);
997
998 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
999
1000 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1001 if (ret < 0) {
1002 LOGP(DINP, LOGL_ERROR, "could not bind listen socket %s\n",
1003 strerror(errno));
1004 return -EIO;
1005 }
1006
1007 ret = listen(bfd->fd, 1);
1008 if (ret < 0) {
1009 perror("listen");
1010 return ret;
1011 }
1012
1013 ret = bsc_register_fd(bfd);
1014 if (ret < 0) {
1015 perror("register_listen_fd");
1016 return ret;
1017 }
1018 return 0;
1019}
1020
1021/* Actively connect to a BSC. */
1022static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1023{
1024 struct ipa_proxy_conn *ipc;
1025 struct bsc_fd *bfd;
1026 int ret, on = 1;
1027
1028 ipc = alloc_conn();
1029 if (!ipc)
1030 return NULL;
1031
1032 ipc->bts_conn = data;
1033
1034 bfd = &ipc->fd;
1035 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1036 bfd->cb = ipaccess_fd_cb;
1037 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1038 bfd->data = ipc;
1039 bfd->priv_nr = priv_nr;
1040
1041 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1042
1043 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1044 if (ret < 0) {
1045 LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
1046 close(bfd->fd);
1047 talloc_free(ipc);
1048 return NULL;
1049 }
1050
1051 /* pre-fill tx_queue with identity request */
1052 ret = bsc_register_fd(bfd);
1053 if (ret < 0) {
1054 close(bfd->fd);
1055 talloc_free(ipc);
1056 return NULL;
1057 }
1058
1059 return ipc;
1060}
1061
1062static int ipaccess_proxy_setup(void)
1063{
1064 int ret;
1065
1066 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1067 if (!ipp)
1068 return -ENOMEM;
1069 INIT_LLIST_HEAD(&ipp->bts_list);
1070 ipp->reconn_timer.cb = reconn_tmr_cb;
1071 ipp->reconn_timer.data = ipp;
1072
1073 /* Listen for OML connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001074 ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
1075 OML_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001076 if (ret < 0)
1077 return ret;
1078
1079 /* Listen for RSL connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001080 ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
1081 RSL_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001082
1083 return ret;
1084}
1085
1086static void signal_handler(int signal)
1087{
1088 fprintf(stdout, "signal %u received\n", signal);
1089
1090 switch (signal) {
1091 case SIGABRT:
1092 /* in case of abort, we want to obtain a talloc report
1093 * and then return to the caller, who will abort the process */
1094 case SIGUSR1:
1095 talloc_report_full(tall_bsc_ctx, stderr);
1096 break;
1097 default:
1098 break;
1099 }
1100}
1101
1102int main(int argc, char **argv)
1103{
1104 int rc;
1105
1106 listen_ipaddr = "192.168.100.11";
1107 bsc_ipaddr = "192.168.100.239";
1108
1109 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1110
1111 debug_init();
1112 stderr_target = debug_target_create_stderr();
1113 debug_add_target(stderr_target);
1114 debug_set_all_filter(stderr_target, 1);
1115 debug_parse_category_mask(stderr_target, "DINP:DMI");
1116
1117 rc = ipaccess_proxy_setup();
1118 if (rc < 0)
1119 exit(1);
1120
1121 signal(SIGUSR1, &signal_handler);
1122 signal(SIGABRT, &signal_handler);
1123
1124 while (1) {
1125 bsc_select_main(0);
1126 }
1127}