blob: 3e1bcdc34c4249deb921c6b330bc1f9fc9ecbbac [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>
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08004 * (C) 2010 by On Waves
5 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
Harald Welte2ca7c312009-12-23 22:44:04 +01006 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <stdio.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <string.h>
30#include <signal.h>
31#include <time.h>
32#include <sys/fcntl.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/ioctl.h>
36#include <arpa/inet.h>
37#include <netinet/in.h>
38
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +080039#define _GNU_SOURCE
40#include <getopt.h>
41
Harald Welte2ca7c312009-12-23 22:44:04 +010042#include <openbsc/gsm_data.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010043#include <osmocore/select.h>
44#include <osmocore/tlv.h>
45#include <osmocore/msgb.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010046#include <openbsc/debug.h>
47#include <openbsc/ipaccess.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010048#include <osmocore/talloc.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010049
Harald Weltedc5062b2010-03-26 21:28:59 +080050static struct log_target *stderr_target;
Harald Welte2ca7c312009-12-23 22:44:04 +010051
52/* one instance of an ip.access protocol proxy */
53struct ipa_proxy {
54 /* socket where we listen for incoming OML from BTS */
55 struct bsc_fd oml_listen_fd;
56 /* socket where we listen for incoming RSL from BTS */
57 struct bsc_fd rsl_listen_fd;
58 /* list of BTS's (struct ipa_bts_conn */
59 struct llist_head bts_list;
60 /* the BSC reconnect timer */
61 struct timer_list reconn_timer;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +080062 /* global GPRS NS data */
63 struct in_addr gprs_addr;
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +080064 struct in_addr listen_addr;
Harald Welte2ca7c312009-12-23 22:44:04 +010065};
66
67/* global pointer to the proxy structure */
68static struct ipa_proxy *ipp;
69
70struct ipa_proxy_conn {
71 struct bsc_fd fd;
72 struct llist_head tx_queue;
73 struct ipa_bts_conn *bts_conn;
74};
Harald Welte2ca7c312009-12-23 22:44:04 +010075#define MAX_TRX 4
76
77/* represents a particular BTS in our proxy */
78struct ipa_bts_conn {
79 /* list of BTS's (ipa_proxy->bts_list) */
80 struct llist_head list;
81 /* back pointer to the proxy which we belong to */
82 struct ipa_proxy *ipp;
83 /* the unit ID as determined by CCM */
84 struct {
85 u_int16_t site_id;
86 u_int16_t bts_id;
87 } unit_id;
88
89 /* incoming connections from BTS */
90 struct ipa_proxy_conn *oml_conn;
91 struct ipa_proxy_conn *rsl_conn[MAX_TRX];
92
93 /* outgoing connections to BSC */
94 struct ipa_proxy_conn *bsc_oml_conn;
95 struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
96
97 /* UDP sockets for BTS and BSC injection */
98 struct bsc_fd udp_bts_fd;
99 struct bsc_fd udp_bsc_fd;
100
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800101 /* NS data */
102 struct in_addr bts_addr;
103 struct bsc_fd gprs_ns_fd;
104 int gprs_local_port;
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800105 uint16_t gprs_orig_port;
106 uint32_t gprs_orig_ip;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800107
Harald Welte2ca7c312009-12-23 22:44:04 +0100108 char *id_tags[0xff];
109 u_int8_t *id_resp;
110 unsigned int id_resp_len;
111};
112
113enum ipp_fd_type {
114 OML_FROM_BTS = 1,
115 RSL_FROM_BTS = 2,
116 OML_TO_BSC = 3,
117 RSL_TO_BSC = 4,
118 UDP_TO_BTS = 5,
119 UDP_TO_BSC = 6,
120};
121
122/* some of the code against we link from OpenBSC needs this */
123void *tall_bsc_ctx;
124
125static char *listen_ipaddr;
126static char *bsc_ipaddr;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +0800127static char *gprs_ns_ipaddr;
Harald Welte2ca7c312009-12-23 22:44:04 +0100128
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800129static int make_gprs_sock(struct bsc_fd *bfd, int (*cb)(struct bsc_fd*,unsigned int), void *);
130static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what);
131
Holger Hans Peter Freyther3dac8812010-06-09 14:39:22 +0800132#define PROXY_ALLOC_SIZE 1200
Harald Welte2ca7c312009-12-23 22:44:04 +0100133
134static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
135static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
136static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
137 0x01, IPAC_IDTAG_UNIT,
138 0x01, IPAC_IDTAG_MACADDR,
139 0x01, IPAC_IDTAG_LOCATION1,
140 0x01, IPAC_IDTAG_LOCATION2,
141 0x01, IPAC_IDTAG_EQUIPVERS,
142 0x01, IPAC_IDTAG_SWVERSION,
143 0x01, IPAC_IDTAG_UNITNAME,
144 0x01, IPAC_IDTAG_SERNR,
145 };
146
147static const char *idtag_names[] = {
148 [IPAC_IDTAG_SERNR] = "Serial_Number",
149 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
150 [IPAC_IDTAG_LOCATION1] = "Location_1",
151 [IPAC_IDTAG_LOCATION2] = "Location_2",
152 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
153 [IPAC_IDTAG_SWVERSION] = "Software_Version",
154 [IPAC_IDTAG_IPADDR] = "IP_Address",
155 [IPAC_IDTAG_MACADDR] = "MAC_Address",
156 [IPAC_IDTAG_UNIT] = "Unit_ID",
157};
158
159static const char *ipac_idtag_name(int tag)
160{
161 if (tag >= ARRAY_SIZE(idtag_names))
162 return "unknown";
163
164 return idtag_names[tag];
165}
166
167static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
168{
169 u_int8_t t_len;
170 u_int8_t t_tag;
171 u_int8_t *cur = buf;
172
173 while (cur < buf + len) {
174 t_len = *cur++;
175 t_tag = *cur++;
176
177 DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
178
179 dec->lv[t_tag].len = t_len;
180 dec->lv[t_tag].val = cur;
181
182 cur += t_len;
183 }
184 return 0;
185}
186
187static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
188 u_int16_t *trx_id)
189{
190 unsigned long ul;
191 char *endptr;
192 const char *nptr;
193
194 nptr = str;
195 ul = strtoul(nptr, &endptr, 10);
196 if (endptr <= nptr)
197 return -EINVAL;
198 if (site_id)
199 *site_id = ul & 0xffff;
200
201 if (*endptr++ != '/')
202 return -EINVAL;
203
204 nptr = endptr;
205 ul = strtoul(nptr, &endptr, 10);
206 if (endptr <= nptr)
207 return -EINVAL;
208 if (bts_id)
209 *bts_id = ul & 0xffff;
210
211 if (*endptr++ != '/')
212 return -EINVAL;
213
214 nptr = endptr;
215 ul = strtoul(nptr, &endptr, 10);
216 if (endptr <= nptr)
217 return -EINVAL;
218 if (trx_id)
219 *trx_id = ul & 0xffff;
220
221 return 0;
222}
223
224static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
225 u_int16_t site_id,
226 u_int16_t bts_id)
227{
228 struct ipa_bts_conn *ipbc;
229
230 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
231 if (ipbc->unit_id.site_id == site_id &&
232 ipbc->unit_id.bts_id == bts_id)
233 return ipbc;
234 }
235
236 return NULL;
237}
238
239struct ipa_proxy_conn *alloc_conn(void)
240{
241 struct ipa_proxy_conn *ipc;
242
243 ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
244 if (!ipc)
245 return NULL;
246
247 INIT_LLIST_HEAD(&ipc->tx_queue);
248
249 return ipc;
250}
251
252static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
253{
254 unsigned int i, len;
255
256 for (i = 0; i <= 0xff; i++) {
257 if (!TLVP_PRESENT(tlvp, i))
258 continue;
259
260 len = TLVP_LEN(tlvp, i);
261#if 0
262 if (!ipbc->id_tags[i])
263 ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
264 else
265#endif
Harald Weltea16ef3d2009-12-23 23:35:51 +0100266 ipbc->id_tags[i] = talloc_realloc_size(ipbc,
Harald Welte2ca7c312009-12-23 22:44:04 +0100267 ipbc->id_tags[i], len);
268 if (!ipbc->id_tags[i])
269 return -ENOMEM;
270
271 memset(ipbc->id_tags[i], 0, len);
272 //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
273 }
274 return 0;
275}
276
277
278static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
279
280#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
281
282static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
283 struct ipa_bts_conn *ipbc, u_int8_t trx_id)
284{
285 if (ipbc)
Harald Weltedc5062b2010-03-26 21:28:59 +0800286 logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
Harald Welte2ca7c312009-12-23 22:44:04 +0100287 ipbc->unit_id.bts_id, trx_id);
288 else
Harald Weltedc5062b2010-03-26 21:28:59 +0800289 logp2(ss, lvl, file, line, 0, "unknown ");
Harald Welte2ca7c312009-12-23 22:44:04 +0100290}
291
292/* UDP socket handling */
293
294static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr,
295 int (*cb)(struct bsc_fd *fd, unsigned int what),
296 void *data)
297{
298 struct sockaddr_in addr;
299 int ret, on = 1;
300
301 bfd->fd = socket(AF_INET, SOCK_DGRAM, proto);
302 bfd->cb = cb;
303 bfd->when = BSC_FD_READ;
304 bfd->data = data;
305 bfd->priv_nr = priv_nr;
306
307 memset(&addr, 0, sizeof(addr));
308 addr.sin_family = AF_INET;
309 addr.sin_port = htons(port);
310 addr.sin_addr.s_addr = INADDR_ANY;
311
312 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
313
314 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
315 if (ret < 0) {
316 LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n",
317 strerror(errno));
318 return -EIO;
319 }
320
321 ret = bsc_register_fd(bfd);
322 if (ret < 0) {
323 perror("register UDP fd");
324 return ret;
325 }
326 return 0;
327}
328
329static int handle_udp_read(struct bsc_fd *bfd)
330{
331 struct ipa_bts_conn *ipbc = bfd->data;
332 struct ipa_proxy_conn *other_conn = NULL;
333 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
334 struct ipaccess_head *hh;
335 int ret;
336
337 /* with UDP sockets, we cannot read partial packets but have to read
338 * all of it in one go */
339 hh = (struct ipaccess_head *) msg->data;
340 ret = recv(bfd->fd, msg->data, msg->data_len, 0);
341 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100342 if (errno != EAGAIN)
343 LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100344 msgb_free(msg);
345 return ret;
346 }
347 if (ret == 0) {
348 DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
349 bsc_unregister_fd(bfd);
350 close(bfd->fd);
351 bfd->fd = -1;
352 msgb_free(msg);
353 return -EIO;
354 }
355 if (ret < sizeof(*hh)) {
356 DEBUGP(DINP, "could not even read header!?!\n");
357 msgb_free(msg);
358 return -EIO;
359 }
360 msgb_put(msg, ret);
361 msg->l2h = msg->data + sizeof(*hh);
362 DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
363
364 if (hh->len != msg->len - sizeof(*hh)) {
365 DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
366 msg->len, msg->len - 3, hh->len);
367 msgb_free(msg);
368 return -EIO;
369 }
370
371 switch (bfd->priv_nr & 0xff) {
372 case UDP_TO_BTS:
373 /* injection towards BTS */
374 switch (hh->proto) {
375 case IPAC_PROTO_RSL:
376 /* FIXME: what to do about TRX > 0 */
377 other_conn = ipbc->rsl_conn[0];
378 break;
379 default:
380 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
381 "OML FD\n", hh->proto);
382 /* fall through */
383 case IPAC_PROTO_IPACCESS:
384 case IPAC_PROTO_OML:
385 other_conn = ipbc->oml_conn;
386 break;
387 }
388 break;
389 case UDP_TO_BSC:
390 /* injection towards BSC */
391 switch (hh->proto) {
392 case IPAC_PROTO_RSL:
393 /* FIXME: what to do about TRX > 0 */
394 other_conn = ipbc->bsc_rsl_conn[0];
395 break;
396 default:
397 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
398 "OML FD\n", hh->proto);
399 case IPAC_PROTO_IPACCESS:
400 case IPAC_PROTO_OML:
401 other_conn = ipbc->bsc_oml_conn;
402 break;
403 }
404 break;
405 default:
406 DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
407 break;
408 }
409
410 if (other_conn) {
411 /* enqueue the message for TX on the respective FD */
412 msgb_enqueue(&other_conn->tx_queue, msg);
413 other_conn->fd.when |= BSC_FD_WRITE;
414 } else
415 msgb_free(msg);
416
417 return 0;
418}
419
420static int handle_udp_write(struct bsc_fd *bfd)
421{
422 /* not implemented yet */
423 bfd->when &= ~BSC_FD_WRITE;
424
425 return -EIO;
426}
427
428/* callback from select.c in case one of the fd's can be read/written */
429static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
430{
431 int rc = 0;
432
433 if (what & BSC_FD_READ)
434 rc = handle_udp_read(bfd);
435 if (what & BSC_FD_WRITE)
436 rc = handle_udp_write(bfd);
437
438 return rc;
439}
440
441
442static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
443 u_int16_t site_id, u_int16_t bts_id,
444 u_int16_t trx_id, struct tlv_parsed *tlvp,
445 struct msgb *msg)
446{
447 struct ipa_bts_conn *ipbc;
448 u_int16_t udp_port;
449 int ret = 0;
450 struct sockaddr_in sin;
451
452 memset(&sin, 0, sizeof(sin));
453 sin.sin_family = AF_INET;
454 inet_aton(bsc_ipaddr, &sin.sin_addr);
455
456 DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
457 site_id, bts_id, trx_id);
458
459 /* OML needs to be established before RSL */
460 if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
461 DEBUGPC(DINP, "Not a OML connection ?!?\n");
462 return -EIO;
463 }
464
465 /* allocate new BTS connection data structure */
466 ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
467 if (!ipbc) {
468 ret = -ENOMEM;
469 goto err_out;
470 }
471
472 DEBUGPC(DINP, "Created BTS Conn data structure\n");
473 ipbc->ipp = ipp;
474 ipbc->unit_id.site_id = site_id;
475 ipbc->unit_id.bts_id = bts_id;
476 ipbc->oml_conn = ipc;
477 ipc->bts_conn = ipbc;
478
479 /* store the content of the ID TAGS for later reference */
480 store_idtags(ipbc, tlvp);
481 ipbc->id_resp_len = msg->len;
482 ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
483 memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
484
485 /* Create OML TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100486 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100487 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
488 if (!ipbc->bsc_oml_conn) {
489 ret = -EIO;
490 goto err_bsc_conn;
491 }
492
493 DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
494 site_id, bts_id, trx_id);
495
496 /* Create UDP socket for BTS packet injection */
497 udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
498 ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP,
499 UDP_TO_BTS, udp_fd_cb, ipbc);
500 if (ret < 0)
501 goto err_udp_bts;
502 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
503 "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
504
505 /* Create UDP socket for BSC packet injection */
506 udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
507 ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP,
508 UDP_TO_BSC, udp_fd_cb, ipbc);
509 if (ret < 0)
510 goto err_udp_bsc;
511 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
512 "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800513
514
515 /* GPRS NS related code */
516 if (gprs_ns_ipaddr) {
517 struct sockaddr_in sock;
518 socklen_t len = sizeof(sock);
519 ret = make_gprs_sock(&ipbc->gprs_ns_fd, gprs_ns_cb, ipbc);
520 if (ret < 0) {
521 LOGP(DINP, LOGL_ERROR, "Creating the GPRS socket failed.\n");
522 goto err_udp_bsc;
523 }
524
525 ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len);
526 ipbc->gprs_local_port = ntohs(sock.sin_port);
527 LOGP(DINP, LOGL_NOTICE,
528 "Created GPRS NS Socket. Listening on: %s:%d\n",
529 inet_ntoa(sock.sin_addr), ipbc->gprs_local_port);
530
531 ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len);
532 ipbc->bts_addr = sock.sin_addr;
533 }
534
Harald Welte2ca7c312009-12-23 22:44:04 +0100535 llist_add(&ipbc->list, &ipp->bts_list);
536
537 return 0;
538
539err_udp_bsc:
540 bsc_unregister_fd(&ipbc->udp_bts_fd);
541err_udp_bts:
542 bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
543 close(ipbc->bsc_oml_conn->fd.fd);
544 talloc_free(ipbc->bsc_oml_conn);
545 ipbc->bsc_oml_conn = NULL;
546err_bsc_conn:
547 talloc_free(ipbc->id_resp);
548 talloc_free(ipbc);
549#if 0
550 bsc_unregister_fd(bfd);
551 close(bfd->fd);
552 talloc_free(bfd);
553#endif
554err_out:
555 return ret;
556}
557
558static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
559 struct bsc_fd *bfd)
560{
561 struct tlv_parsed tlvp;
562 u_int8_t msg_type = *(msg->l2h);
563 u_int16_t site_id, bts_id, trx_id;
564 struct ipa_bts_conn *ipbc;
565 int ret = 0;
566
567 switch (msg_type) {
568 case IPAC_MSGT_PING:
569 ret = write(bfd->fd, pong, sizeof(pong));
570 if (ret < 0)
571 return ret;
572 if (ret < sizeof(pong)) {
573 DEBUGP(DINP, "short write\n");
574 return -EIO;
575 }
576 break;
577 case IPAC_MSGT_PONG:
578 DEBUGP(DMI, "PONG!\n");
579 break;
580 case IPAC_MSGT_ID_RESP:
581 DEBUGP(DMI, "ID_RESP ");
582 /* parse tags, search for Unit ID */
583 ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
584 msgb_l2len(msg)-2);
585 DEBUGP(DMI, "\n");
586
587 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
588 LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
589 return -EIO;
590 }
591
592 /* lookup BTS, create sign_link, ... */
Holger Hans Peter Freyther82ae7162010-03-30 15:20:46 +0200593 site_id = bts_id = trx_id = 0;
Harald Welte2ca7c312009-12-23 22:44:04 +0100594 parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
595 &site_id, &bts_id, &trx_id);
596 ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
597 if (!ipbc) {
598 /* We have not found an ipbc (per-bts proxy instance)
599 * for this BTS yet. The first connection of a new BTS must
600 * be a OML connection. We allocate the associated data structures,
601 * and try to connect to the remote end */
602
603 return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
604 trx_id, &tlvp, msg);
605 /* if this fails, the caller will clean up bfd */
606 } else {
607 struct sockaddr_in sin;
608 memset(&sin, 0, sizeof(sin));
609 sin.sin_family = AF_INET;
610 inet_aton(bsc_ipaddr, &sin.sin_addr);
611
612 DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
613 site_id, bts_id, trx_id);
614
615 if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
616 LOGP(DINP, LOGL_ERROR, "Second OML connection from "
617 "same BTS ?!?\n");
618 return 0;
619 }
620
621 if (trx_id > MAX_TRX) {
622 LOGP(DINP, LOGL_ERROR, "We don't support more "
623 "than %u TRX\n", MAX_TRX);
624 return -EINVAL;
625 }
626
627 ipc->bts_conn = ipbc;
628 /* store TRX number in higher 8 bit of the bfd private number */
629 bfd->priv_nr |= trx_id << 8;
630 ipbc->rsl_conn[trx_id] = ipc;
631
632 /* Create RSL TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100633 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100634 ipbc->bsc_rsl_conn[trx_id] =
635 connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
636 if (!ipbc->bsc_oml_conn)
637 return -EIO;
638 DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
639 site_id, bts_id, trx_id);
640 }
641 break;
642 case IPAC_MSGT_ID_GET:
643 DEBUGP(DMI, "ID_GET\n");
644 if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
645 (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
646 DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
647 return -EIO;
648 }
649 ipbc = ipc->bts_conn;
650 if (!ipbc) {
651 DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
652 return -EIO;
653 }
654 ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
655 break;
656 case IPAC_MSGT_ID_ACK:
657 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
658 ret = write(bfd->fd, id_ack, sizeof(id_ack));
659 break;
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800660 default:
661 LOGP(DMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type);
662 return 1;
663 break;
Harald Welte2ca7c312009-12-23 22:44:04 +0100664 }
665 return 0;
666}
667
668struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
669{
670 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
671 struct ipaccess_head *hh;
672 int len, ret = 0;
673
674 if (!msg) {
675 *error = -ENOMEM;
676 return NULL;
677 }
678
679 /* first read our 3-byte header */
680 hh = (struct ipaccess_head *) msg->data;
681 ret = recv(bfd->fd, msg->data, 3, 0);
682 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100683 if (errno != EAGAIN)
Harald Welteda956932009-12-24 12:49:43 +0100684 LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100685 msgb_free(msg);
686 *error = ret;
687 return NULL;
688 } else if (ret == 0) {
689 msgb_free(msg);
690 *error = ret;
691 return NULL;
692 }
693
694 msgb_put(msg, ret);
695
696 /* then read te length as specified in header */
697 msg->l2h = msg->data + sizeof(*hh);
698 len = ntohs(hh->len);
699 ret = recv(bfd->fd, msg->l2h, len, 0);
700 if (ret < len) {
701 LOGP(DINP, LOGL_ERROR, "short read!\n");
702 msgb_free(msg);
703 *error = -EIO;
704 return NULL;
705 }
706 msgb_put(msg, ret);
707
708 return msg;
709}
710
711static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
712 unsigned int priv_nr)
713{
714 struct ipa_proxy_conn *bsc_conn;
715 unsigned int trx_id = priv_nr >> 8;
716
717 switch (priv_nr & 0xff) {
718 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
719 bsc_conn = ipbc->bsc_oml_conn;
720 break;
721 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
722 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
723 break;
724 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
725 bsc_conn = ipbc->oml_conn;
726 break;
727 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
728 bsc_conn = ipbc->rsl_conn[trx_id];
729 break;
730 default:
731 bsc_conn = NULL;
732 break;
733 }
734 return bsc_conn;
735}
736
737static void reconn_tmr_cb(void *data)
738{
739 struct ipa_proxy *ipp = data;
740 struct ipa_bts_conn *ipbc;
741 struct sockaddr_in sin;
742 int i;
743
744 DEBUGP(DINP, "Running reconnect timer\n");
745
746 memset(&sin, 0, sizeof(sin));
747 sin.sin_family = AF_INET;
748 inet_aton(bsc_ipaddr, &sin.sin_addr);
749
750 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
751 /* if OML to BSC is dead, try to restore it */
752 if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
Harald Welte87ed5cd2009-12-23 22:47:53 +0100753 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100754 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
755 LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
756 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
757 if (!ipbc->bsc_oml_conn)
758 goto reschedule;
759 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
760 LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
761 }
762 /* if we (still) don't have a OML connection, skip RSL */
763 if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
764 continue;
765
766 for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
767 unsigned int priv_nr;
768 /* don't establish RSL links which we don't have */
769 if (!ipbc->rsl_conn[i])
770 continue;
771 if (ipbc->bsc_rsl_conn[i])
772 continue;
773 priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
774 priv_nr &= ~0xff;
775 priv_nr |= RSL_TO_BSC;
Harald Welte87ed5cd2009-12-23 22:47:53 +0100776 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100777 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
778 LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
779 ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
780 if (!ipbc->bsc_rsl_conn)
781 goto reschedule;
782 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
783 LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
784 }
785 }
786 return;
787
788reschedule:
789 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
790}
791
792static void handle_dead_socket(struct bsc_fd *bfd)
793{
794 struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
795 struct ipa_proxy_conn *bsc_conn; /* remote conn */
796 struct ipa_bts_conn *ipbc = ipc->bts_conn;
797 unsigned int trx_id = bfd->priv_nr >> 8;
798 struct msgb *msg, *msg2;
799
800 bsc_unregister_fd(bfd);
801 close(bfd->fd);
802 bfd->fd = -1;
803
804 /* FIXME: clear tx_queue, remove all references, etc. */
805 llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
806 msgb_free(msg);
807
808 switch (bfd->priv_nr & 0xff) {
809 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
810 ipbc->oml_conn = NULL;
811 bsc_conn = ipbc->bsc_oml_conn;
812 /* close the connection to the BSC */
813 bsc_unregister_fd(&bsc_conn->fd);
814 close(bsc_conn->fd.fd);
815 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
816 msgb_free(msg);
817 talloc_free(bsc_conn);
818 ipbc->bsc_oml_conn = NULL;
819 /* FIXME: do we need to delete the entire ipbc ? */
820 break;
821 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
822 ipbc->rsl_conn[trx_id] = NULL;
823 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
824 /* close the connection to the BSC */
825 bsc_unregister_fd(&bsc_conn->fd);
826 close(bsc_conn->fd.fd);
827 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
828 msgb_free(msg);
829 talloc_free(bsc_conn);
830 ipbc->bsc_rsl_conn[trx_id] = NULL;
831 break;
832 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
833 ipbc->bsc_oml_conn = NULL;
834 bsc_conn = ipbc->oml_conn;
835 /* start reconnect timer */
836 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
837 break;
838 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
839 ipbc->bsc_rsl_conn[trx_id] = NULL;
840 bsc_conn = ipbc->rsl_conn[trx_id];
841 /* start reconnect timer */
842 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
843 break;
844 default:
845 bsc_conn = NULL;
846 break;
847 }
848
849 talloc_free(ipc);
850}
851
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800852static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg)
853{
854 uint8_t *nsvci;
855
856 if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC)
857 return;
858
859 if (msgb_l2len(msg) != 39)
860 return;
861
862 /*
863 * Check if this is a IPA Set Attribute or IPA Set Attribute ACK
864 * and if the FOM Class is GPRS NSVC0 and then we will patch it.
865 *
866 * The patch assumes the message looks like the one from the trace
867 * but we only match messages with a specific size anyway... So
868 * this hack should work just fine.
869 */
870
871 if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
872 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
873 msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) {
874 printf("found GPRS NSVC SET foo...\n");
875 nsvci = &msg->l2h[23];
876 ipbc->gprs_orig_port = *(u_int16_t *)(nsvci+8);
877 ipbc->gprs_orig_ip = *(u_int32_t *)(nsvci+10);
878 *(u_int16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
879 *(u_int32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
880 } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
881 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
882 msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
883 printf("found GPRS NSVC SET ACK...\n");
884 nsvci = &msg->l2h[23];
885 *(u_int16_t *)(nsvci+8) = ipbc->gprs_orig_port;
886 *(u_int32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
887 }
888}
889
Harald Welte2ca7c312009-12-23 22:44:04 +0100890static int handle_tcp_read(struct bsc_fd *bfd)
891{
892 struct ipa_proxy_conn *ipc = bfd->data;
893 struct ipa_bts_conn *ipbc = ipc->bts_conn;
894 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100895 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100896 struct ipaccess_head *hh;
897 int ret = 0;
898 char *btsbsc;
899
Harald Welte2ca7c312009-12-23 22:44:04 +0100900 if ((bfd->priv_nr & 0xff) <= 2)
901 btsbsc = "BTS";
902 else
903 btsbsc = "BSC";
904
905 msg = ipaccess_read_msg(bfd, &ret);
906 if (!msg) {
907 if (ret == 0) {
908 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
909 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
910 "dead socket\n", btsbsc);
911 handle_dead_socket(bfd);
912 }
913 return ret;
914 }
915
916 msgb_put(msg, ret);
917 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
918 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
919
920 hh = (struct ipaccess_head *) msg->data;
921 if (hh->proto == IPAC_PROTO_IPACCESS) {
922 ret = ipaccess_rcvmsg(ipc, msg, bfd);
923 if (ret < 0) {
924 bsc_unregister_fd(bfd);
925 close(bfd->fd);
926 bfd->fd = -1;
927 talloc_free(bfd);
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800928 msgb_free(msg);
929 return ret;
930 } else if (ret == 0) {
931 /* we do not forward parts of the CCM protocol
932 * through the proxy but rather terminate it ourselves. */
933 msgb_free(msg);
934 return ret;
Harald Welte2ca7c312009-12-23 22:44:04 +0100935 }
Harald Welte2ca7c312009-12-23 22:44:04 +0100936 }
937
938 if (!ipbc) {
939 LOGP(DINP, LOGL_ERROR,
940 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
941 msgb_free(msg);
942 return -EIO;
943 }
944
945 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
946 if (bsc_conn) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800947 if (gprs_ns_ipaddr)
948 patch_gprs_msg(ipbc, bfd->priv_nr, msg);
Harald Welte2ca7c312009-12-23 22:44:04 +0100949 /* enqueue packet towards BSC */
950 msgb_enqueue(&bsc_conn->tx_queue, msg);
951 /* mark respective filedescriptor as 'we want to write' */
952 bsc_conn->fd.when |= BSC_FD_WRITE;
953 } else {
954 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
955 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
956 "since remote connection is dead\n", btsbsc);
957 msgb_free(msg);
958 }
959
960 return ret;
961}
962
963/* a TCP socket is ready to be written to */
964static int handle_tcp_write(struct bsc_fd *bfd)
965{
966 struct ipa_proxy_conn *ipc = bfd->data;
967 struct ipa_bts_conn *ipbc = ipc->bts_conn;
968 struct llist_head *lh;
969 struct msgb *msg;
970 char *btsbsc;
971 int ret;
972
973 if ((bfd->priv_nr & 0xff) <= 2)
974 btsbsc = "BTS";
975 else
976 btsbsc = "BSC";
977
978
979 /* get the next msg for this timeslot */
980 if (llist_empty(&ipc->tx_queue)) {
981 bfd->when &= ~BSC_FD_WRITE;
982 return 0;
983 }
984 lh = ipc->tx_queue.next;
985 llist_del(lh);
986 msg = llist_entry(lh, struct msgb, list);
987
988 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
989 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
990 hexdump(msg->data, msg->len));
991
992 ret = send(bfd->fd, msg->data, msg->len, 0);
993 msgb_free(msg);
994
995 if (ret == 0) {
996 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
997 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
998 handle_dead_socket(bfd);
999 }
1000
1001 return ret;
1002}
1003
1004/* callback from select.c in case one of the fd's can be read/written */
1005static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
1006{
1007 int rc = 0;
1008
1009 if (what & BSC_FD_READ) {
1010 rc = handle_tcp_read(bfd);
1011 if (rc < 0)
1012 return rc;
1013 }
1014 if (what & BSC_FD_WRITE)
1015 rc = handle_tcp_write(bfd);
1016
1017 return rc;
1018}
1019
1020/* callback of the listening filedescriptor */
1021static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
1022{
1023 int ret;
1024 struct ipa_proxy_conn *ipc;
1025 struct bsc_fd *bfd;
1026 struct sockaddr_in sa;
1027 socklen_t sa_len = sizeof(sa);
1028
1029 if (!(what & BSC_FD_READ))
1030 return 0;
1031
1032 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
1033 if (ret < 0) {
1034 perror("accept");
1035 return ret;
1036 }
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +02001037 DEBUGP(DINP, "accept()ed new %s link from %s\n",
Harald Welte2ca7c312009-12-23 22:44:04 +01001038 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
1039 inet_ntoa(sa.sin_addr));
1040
1041 ipc = alloc_conn();
1042 if (!ipc) {
1043 close(ret);
1044 return -ENOMEM;
1045 }
1046
1047 bfd = &ipc->fd;
1048 bfd->fd = ret;
1049 bfd->data = ipc;
1050 bfd->priv_nr = listen_bfd->priv_nr;
1051 bfd->cb = ipaccess_fd_cb;
1052 bfd->when = BSC_FD_READ;
1053 ret = bsc_register_fd(bfd);
1054 if (ret < 0) {
1055 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
1056 close(bfd->fd);
1057 talloc_free(ipc);
1058 return ret;
1059 }
1060
1061 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
1062 ret = write(bfd->fd, id_req, sizeof(id_req));
1063
1064 return 0;
1065}
1066
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001067static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
1068{
1069 int ret;
1070 struct sockaddr_in addr;
1071 socklen_t len = sizeof(addr);
1072 memset(&addr, 0, sizeof(addr));
1073
1074 addr.sin_family = AF_INET;
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001075 addr.sin_port = htons(port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001076 addr.sin_addr = ip;
1077
1078 ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
1079 if (ret < 0) {
1080 LOGP(DINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
1081 }
1082}
1083
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001084static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what)
1085{
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001086 struct ipa_bts_conn *bts;
1087 char buf[4096];
1088 int ret;
1089 struct sockaddr_in sock;
1090 socklen_t len = sizeof(sock);
1091
1092 /* 1. get the data... */
1093 ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
1094 if (ret < 0) {
1095 LOGP(DINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
1096 return -1;
1097 }
1098
1099 bts = bfd->data;
1100
1101 /* 2. figure out where to send it to */
1102 if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
1103 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
1104 send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
1105 } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
1106 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
1107 send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001108 } else {
1109 LOGP(DINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001110 }
1111
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001112 return 0;
1113}
1114
Harald Welte2ca7c312009-12-23 22:44:04 +01001115static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
1116 int (*cb)(struct bsc_fd *fd, unsigned int what))
1117{
1118 struct sockaddr_in addr;
1119 int ret, on = 1;
1120
1121 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1122 bfd->cb = cb;
1123 bfd->when = BSC_FD_READ;
1124 bfd->priv_nr = priv_nr;
1125
1126 memset(&addr, 0, sizeof(addr));
1127 addr.sin_family = AF_INET;
1128 addr.sin_port = htons(port);
1129 if (!listen_ipaddr)
1130 addr.sin_addr.s_addr = INADDR_ANY;
1131 else
1132 inet_aton(listen_ipaddr, &addr.sin_addr);
1133
1134 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1135
1136 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1137 if (ret < 0) {
Holger Hans Peter Freyther76706512010-06-09 15:17:53 +08001138 LOGP(DINP, LOGL_ERROR,
1139 "Could not bind listen socket for IP %s with error: %s.\n",
1140 listen_ipaddr, strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +01001141 return -EIO;
1142 }
1143
1144 ret = listen(bfd->fd, 1);
1145 if (ret < 0) {
1146 perror("listen");
1147 return ret;
1148 }
1149
1150 ret = bsc_register_fd(bfd);
1151 if (ret < 0) {
1152 perror("register_listen_fd");
1153 return ret;
1154 }
1155 return 0;
1156}
1157
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001158static int make_gprs_sock(struct bsc_fd *bfd, int (*cb)(struct bsc_fd*,unsigned int), void *data)
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001159{
1160 struct sockaddr_in addr;
1161 int ret;
1162
1163 bfd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1164 bfd->cb = cb;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001165 bfd->data = data;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001166 bfd->when = BSC_FD_READ;
1167
1168 memset(&addr, 0, sizeof(addr));
1169 addr.sin_family = AF_INET;
1170 addr.sin_port = 0;
1171 inet_aton(listen_ipaddr, &addr.sin_addr);
1172
1173 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1174 if (ret < 0) {
1175 LOGP(DINP, LOGL_ERROR,
1176 "Could not bind n socket for IP %s with error: %s.\n",
1177 listen_ipaddr, strerror(errno));
1178 return -EIO;
1179 }
1180
1181 ret = bsc_register_fd(bfd);
1182 if (ret < 0) {
1183 perror("register_listen_fd");
1184 return ret;
1185 }
1186 return 0;
1187}
1188
Harald Welte2ca7c312009-12-23 22:44:04 +01001189/* Actively connect to a BSC. */
1190static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1191{
1192 struct ipa_proxy_conn *ipc;
1193 struct bsc_fd *bfd;
1194 int ret, on = 1;
1195
1196 ipc = alloc_conn();
1197 if (!ipc)
1198 return NULL;
1199
1200 ipc->bts_conn = data;
1201
1202 bfd = &ipc->fd;
1203 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1204 bfd->cb = ipaccess_fd_cb;
1205 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1206 bfd->data = ipc;
1207 bfd->priv_nr = priv_nr;
1208
1209 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1210
1211 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1212 if (ret < 0) {
Holger Hans Peter Freyther6cb9b232010-06-09 15:15:11 +08001213 LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n",
1214 inet_ntoa(sa->sin_addr));
Harald Welte2ca7c312009-12-23 22:44:04 +01001215 close(bfd->fd);
1216 talloc_free(ipc);
1217 return NULL;
1218 }
1219
1220 /* pre-fill tx_queue with identity request */
1221 ret = bsc_register_fd(bfd);
1222 if (ret < 0) {
1223 close(bfd->fd);
1224 talloc_free(ipc);
1225 return NULL;
1226 }
1227
1228 return ipc;
1229}
1230
1231static int ipaccess_proxy_setup(void)
1232{
1233 int ret;
1234
1235 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1236 if (!ipp)
1237 return -ENOMEM;
1238 INIT_LLIST_HEAD(&ipp->bts_list);
1239 ipp->reconn_timer.cb = reconn_tmr_cb;
1240 ipp->reconn_timer.data = ipp;
1241
1242 /* Listen for OML connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001243 ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
1244 OML_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001245 if (ret < 0)
1246 return ret;
1247
1248 /* Listen for RSL connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001249 ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
1250 RSL_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001251
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001252 if (ret < 0)
1253 return ret;
1254
1255 /* Connect the GPRS NS Socket */
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001256 if (gprs_ns_ipaddr) {
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001257 inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001258 inet_aton(listen_ipaddr, &ipp->listen_addr);
1259 }
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001260
Harald Welte2ca7c312009-12-23 22:44:04 +01001261 return ret;
1262}
1263
1264static void signal_handler(int signal)
1265{
1266 fprintf(stdout, "signal %u received\n", signal);
1267
1268 switch (signal) {
1269 case SIGABRT:
1270 /* in case of abort, we want to obtain a talloc report
1271 * and then return to the caller, who will abort the process */
1272 case SIGUSR1:
1273 talloc_report_full(tall_bsc_ctx, stderr);
1274 break;
1275 default:
1276 break;
1277 }
1278}
1279
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001280static void print_help()
1281{
1282 printf(" ipaccess-proxy is a proxy BTS.\n");
1283 printf(" -h --help. This help text.\n");
1284 printf(" -l --listen IP. The ip to listen to.\n");
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001285 printf(" -b --bsc IP. The BSC IP address.\n");
1286 printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
1287 printf("\n");
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001288 printf(" -s --disable-color. Disable the color inside the logging message.\n");
1289 printf(" -e --log-level number. Set the global loglevel.\n");
1290 printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
1291 printf(" -V --version. Print the version of OpenBSC.\n");
1292}
1293
1294static void print_usage()
1295{
1296 printf("Usage: ipaccess-proxy\n");
1297}
1298
1299static void handle_options(int argc, char** argv)
1300{
1301 while (1) {
1302 int option_index = 0, c;
1303 static struct option long_options[] = {
1304 {"help", 0, 0, 'h'},
1305 {"disable-color", 0, 0, 's'},
1306 {"timestamp", 0, 0, 'T'},
1307 {"log-level", 1, 0, 'e'},
1308 {"listen", 1, 0, 'l'},
1309 {"bsc", 1, 0, 'b'},
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001310 {"udp", 1, 0, 'u'},
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001311 {0, 0, 0, 0}
1312 };
1313
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001314 c = getopt_long(argc, argv, "hsTe:l:b:g:",
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001315 long_options, &option_index);
1316 if (c == -1)
1317 break;
1318
1319 switch (c) {
1320 case 'h':
1321 print_usage();
1322 print_help();
1323 exit(0);
1324 case 'l':
1325 listen_ipaddr = optarg;
1326 break;
1327 case 'b':
1328 bsc_ipaddr = optarg;
1329 break;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001330 case 'g':
1331 gprs_ns_ipaddr = optarg;
1332 break;
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001333 case 's':
1334 log_set_use_color(stderr_target, 0);
1335 break;
1336 case 'T':
1337 log_set_print_timestamp(stderr_target, 1);
1338 break;
1339 case 'e':
1340 log_set_log_level(stderr_target, atoi(optarg));
1341 break;
1342 default:
1343 /* ignore */
1344 break;
1345 }
1346 }
1347}
1348
Harald Welte2ca7c312009-12-23 22:44:04 +01001349int main(int argc, char **argv)
1350{
1351 int rc;
1352
1353 listen_ipaddr = "192.168.100.11";
1354 bsc_ipaddr = "192.168.100.239";
1355
1356 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1357
Harald Weltedc5062b2010-03-26 21:28:59 +08001358 log_init(&log_info);
1359 stderr_target = log_target_create_stderr();
1360 log_add_target(stderr_target);
1361 log_set_all_filter(stderr_target, 1);
1362 log_parse_category_mask(stderr_target, "DINP:DMI");
Harald Welte2ca7c312009-12-23 22:44:04 +01001363
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001364 handle_options(argc, argv);
1365
Harald Welte2ca7c312009-12-23 22:44:04 +01001366 rc = ipaccess_proxy_setup();
1367 if (rc < 0)
1368 exit(1);
1369
1370 signal(SIGUSR1, &signal_handler);
1371 signal(SIGABRT, &signal_handler);
1372
1373 while (1) {
1374 bsc_select_main(0);
1375 }
1376}