blob: 94af908165424eb2804eb9f4bcbe75bdfe758d75 [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 Freyther85531cc2010-10-06 20:37:09 +08004 * (C) 2010 by On-Waves
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08005 * (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) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800874 nsvci = &msg->l2h[23];
875 ipbc->gprs_orig_port = *(u_int16_t *)(nsvci+8);
876 ipbc->gprs_orig_ip = *(u_int32_t *)(nsvci+10);
877 *(u_int16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
878 *(u_int32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
879 } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
880 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
881 msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800882 nsvci = &msg->l2h[23];
883 *(u_int16_t *)(nsvci+8) = ipbc->gprs_orig_port;
884 *(u_int32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
885 }
886}
887
Harald Welte2ca7c312009-12-23 22:44:04 +0100888static int handle_tcp_read(struct bsc_fd *bfd)
889{
890 struct ipa_proxy_conn *ipc = bfd->data;
891 struct ipa_bts_conn *ipbc = ipc->bts_conn;
892 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100893 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100894 struct ipaccess_head *hh;
895 int ret = 0;
896 char *btsbsc;
897
Harald Welte2ca7c312009-12-23 22:44:04 +0100898 if ((bfd->priv_nr & 0xff) <= 2)
899 btsbsc = "BTS";
900 else
901 btsbsc = "BSC";
902
903 msg = ipaccess_read_msg(bfd, &ret);
904 if (!msg) {
905 if (ret == 0) {
906 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
907 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
908 "dead socket\n", btsbsc);
909 handle_dead_socket(bfd);
910 }
911 return ret;
912 }
913
914 msgb_put(msg, ret);
915 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
916 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
917
918 hh = (struct ipaccess_head *) msg->data;
919 if (hh->proto == IPAC_PROTO_IPACCESS) {
920 ret = ipaccess_rcvmsg(ipc, msg, bfd);
921 if (ret < 0) {
922 bsc_unregister_fd(bfd);
923 close(bfd->fd);
924 bfd->fd = -1;
925 talloc_free(bfd);
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800926 msgb_free(msg);
927 return ret;
928 } else if (ret == 0) {
929 /* we do not forward parts of the CCM protocol
930 * through the proxy but rather terminate it ourselves. */
931 msgb_free(msg);
932 return ret;
Harald Welte2ca7c312009-12-23 22:44:04 +0100933 }
Harald Welte2ca7c312009-12-23 22:44:04 +0100934 }
935
936 if (!ipbc) {
937 LOGP(DINP, LOGL_ERROR,
938 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
939 msgb_free(msg);
940 return -EIO;
941 }
942
943 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
944 if (bsc_conn) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800945 if (gprs_ns_ipaddr)
946 patch_gprs_msg(ipbc, bfd->priv_nr, msg);
Harald Welte2ca7c312009-12-23 22:44:04 +0100947 /* enqueue packet towards BSC */
948 msgb_enqueue(&bsc_conn->tx_queue, msg);
949 /* mark respective filedescriptor as 'we want to write' */
950 bsc_conn->fd.when |= BSC_FD_WRITE;
951 } else {
952 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
953 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
954 "since remote connection is dead\n", btsbsc);
955 msgb_free(msg);
956 }
957
958 return ret;
959}
960
961/* a TCP socket is ready to be written to */
962static int handle_tcp_write(struct bsc_fd *bfd)
963{
964 struct ipa_proxy_conn *ipc = bfd->data;
965 struct ipa_bts_conn *ipbc = ipc->bts_conn;
966 struct llist_head *lh;
967 struct msgb *msg;
968 char *btsbsc;
969 int ret;
970
971 if ((bfd->priv_nr & 0xff) <= 2)
972 btsbsc = "BTS";
973 else
974 btsbsc = "BSC";
975
976
977 /* get the next msg for this timeslot */
978 if (llist_empty(&ipc->tx_queue)) {
979 bfd->when &= ~BSC_FD_WRITE;
980 return 0;
981 }
982 lh = ipc->tx_queue.next;
983 llist_del(lh);
984 msg = llist_entry(lh, struct msgb, list);
985
986 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
987 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
988 hexdump(msg->data, msg->len));
989
990 ret = send(bfd->fd, msg->data, msg->len, 0);
991 msgb_free(msg);
992
993 if (ret == 0) {
994 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
995 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
996 handle_dead_socket(bfd);
997 }
998
999 return ret;
1000}
1001
1002/* callback from select.c in case one of the fd's can be read/written */
1003static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
1004{
1005 int rc = 0;
1006
1007 if (what & BSC_FD_READ) {
1008 rc = handle_tcp_read(bfd);
1009 if (rc < 0)
1010 return rc;
1011 }
1012 if (what & BSC_FD_WRITE)
1013 rc = handle_tcp_write(bfd);
1014
1015 return rc;
1016}
1017
1018/* callback of the listening filedescriptor */
1019static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
1020{
1021 int ret;
1022 struct ipa_proxy_conn *ipc;
1023 struct bsc_fd *bfd;
1024 struct sockaddr_in sa;
1025 socklen_t sa_len = sizeof(sa);
1026
1027 if (!(what & BSC_FD_READ))
1028 return 0;
1029
1030 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
1031 if (ret < 0) {
1032 perror("accept");
1033 return ret;
1034 }
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +02001035 DEBUGP(DINP, "accept()ed new %s link from %s\n",
Harald Welte2ca7c312009-12-23 22:44:04 +01001036 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
1037 inet_ntoa(sa.sin_addr));
1038
1039 ipc = alloc_conn();
1040 if (!ipc) {
1041 close(ret);
1042 return -ENOMEM;
1043 }
1044
1045 bfd = &ipc->fd;
1046 bfd->fd = ret;
1047 bfd->data = ipc;
1048 bfd->priv_nr = listen_bfd->priv_nr;
1049 bfd->cb = ipaccess_fd_cb;
1050 bfd->when = BSC_FD_READ;
1051 ret = bsc_register_fd(bfd);
1052 if (ret < 0) {
1053 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
1054 close(bfd->fd);
1055 talloc_free(ipc);
1056 return ret;
1057 }
1058
1059 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
1060 ret = write(bfd->fd, id_req, sizeof(id_req));
1061
1062 return 0;
1063}
1064
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001065static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
1066{
1067 int ret;
1068 struct sockaddr_in addr;
1069 socklen_t len = sizeof(addr);
1070 memset(&addr, 0, sizeof(addr));
1071
1072 addr.sin_family = AF_INET;
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001073 addr.sin_port = htons(port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001074 addr.sin_addr = ip;
1075
1076 ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
1077 if (ret < 0) {
1078 LOGP(DINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
1079 }
1080}
1081
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001082static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what)
1083{
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001084 struct ipa_bts_conn *bts;
1085 char buf[4096];
1086 int ret;
1087 struct sockaddr_in sock;
1088 socklen_t len = sizeof(sock);
1089
1090 /* 1. get the data... */
1091 ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
1092 if (ret < 0) {
1093 LOGP(DINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
1094 return -1;
1095 }
1096
1097 bts = bfd->data;
1098
1099 /* 2. figure out where to send it to */
1100 if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
1101 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
1102 send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
1103 } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
1104 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
1105 send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001106 } else {
1107 LOGP(DINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001108 }
1109
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001110 return 0;
1111}
1112
Harald Welte2ca7c312009-12-23 22:44:04 +01001113static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
1114 int (*cb)(struct bsc_fd *fd, unsigned int what))
1115{
1116 struct sockaddr_in addr;
1117 int ret, on = 1;
1118
1119 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1120 bfd->cb = cb;
1121 bfd->when = BSC_FD_READ;
1122 bfd->priv_nr = priv_nr;
1123
1124 memset(&addr, 0, sizeof(addr));
1125 addr.sin_family = AF_INET;
1126 addr.sin_port = htons(port);
1127 if (!listen_ipaddr)
1128 addr.sin_addr.s_addr = INADDR_ANY;
1129 else
1130 inet_aton(listen_ipaddr, &addr.sin_addr);
1131
1132 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1133
1134 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1135 if (ret < 0) {
Holger Hans Peter Freyther76706512010-06-09 15:17:53 +08001136 LOGP(DINP, LOGL_ERROR,
1137 "Could not bind listen socket for IP %s with error: %s.\n",
1138 listen_ipaddr, strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +01001139 return -EIO;
1140 }
1141
1142 ret = listen(bfd->fd, 1);
1143 if (ret < 0) {
1144 perror("listen");
1145 return ret;
1146 }
1147
1148 ret = bsc_register_fd(bfd);
1149 if (ret < 0) {
1150 perror("register_listen_fd");
1151 return ret;
1152 }
1153 return 0;
1154}
1155
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001156static 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 +08001157{
1158 struct sockaddr_in addr;
1159 int ret;
1160
1161 bfd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1162 bfd->cb = cb;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001163 bfd->data = data;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001164 bfd->when = BSC_FD_READ;
1165
1166 memset(&addr, 0, sizeof(addr));
1167 addr.sin_family = AF_INET;
1168 addr.sin_port = 0;
1169 inet_aton(listen_ipaddr, &addr.sin_addr);
1170
1171 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1172 if (ret < 0) {
1173 LOGP(DINP, LOGL_ERROR,
1174 "Could not bind n socket for IP %s with error: %s.\n",
1175 listen_ipaddr, strerror(errno));
1176 return -EIO;
1177 }
1178
1179 ret = bsc_register_fd(bfd);
1180 if (ret < 0) {
1181 perror("register_listen_fd");
1182 return ret;
1183 }
1184 return 0;
1185}
1186
Harald Welte2ca7c312009-12-23 22:44:04 +01001187/* Actively connect to a BSC. */
1188static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1189{
1190 struct ipa_proxy_conn *ipc;
1191 struct bsc_fd *bfd;
1192 int ret, on = 1;
1193
1194 ipc = alloc_conn();
1195 if (!ipc)
1196 return NULL;
1197
1198 ipc->bts_conn = data;
1199
1200 bfd = &ipc->fd;
1201 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1202 bfd->cb = ipaccess_fd_cb;
1203 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1204 bfd->data = ipc;
1205 bfd->priv_nr = priv_nr;
1206
1207 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1208
1209 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1210 if (ret < 0) {
Holger Hans Peter Freyther6cb9b232010-06-09 15:15:11 +08001211 LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n",
1212 inet_ntoa(sa->sin_addr));
Harald Welte2ca7c312009-12-23 22:44:04 +01001213 close(bfd->fd);
1214 talloc_free(ipc);
1215 return NULL;
1216 }
1217
1218 /* pre-fill tx_queue with identity request */
1219 ret = bsc_register_fd(bfd);
1220 if (ret < 0) {
1221 close(bfd->fd);
1222 talloc_free(ipc);
1223 return NULL;
1224 }
1225
1226 return ipc;
1227}
1228
1229static int ipaccess_proxy_setup(void)
1230{
1231 int ret;
1232
1233 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1234 if (!ipp)
1235 return -ENOMEM;
1236 INIT_LLIST_HEAD(&ipp->bts_list);
1237 ipp->reconn_timer.cb = reconn_tmr_cb;
1238 ipp->reconn_timer.data = ipp;
1239
1240 /* Listen for OML connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001241 ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
1242 OML_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001243 if (ret < 0)
1244 return ret;
1245
1246 /* Listen for RSL connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001247 ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
1248 RSL_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001249
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001250 if (ret < 0)
1251 return ret;
1252
1253 /* Connect the GPRS NS Socket */
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001254 if (gprs_ns_ipaddr) {
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001255 inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001256 inet_aton(listen_ipaddr, &ipp->listen_addr);
1257 }
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001258
Harald Welte2ca7c312009-12-23 22:44:04 +01001259 return ret;
1260}
1261
1262static void signal_handler(int signal)
1263{
1264 fprintf(stdout, "signal %u received\n", signal);
1265
1266 switch (signal) {
1267 case SIGABRT:
1268 /* in case of abort, we want to obtain a talloc report
1269 * and then return to the caller, who will abort the process */
1270 case SIGUSR1:
1271 talloc_report_full(tall_bsc_ctx, stderr);
1272 break;
1273 default:
1274 break;
1275 }
1276}
1277
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001278static void print_help()
1279{
1280 printf(" ipaccess-proxy is a proxy BTS.\n");
1281 printf(" -h --help. This help text.\n");
1282 printf(" -l --listen IP. The ip to listen to.\n");
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001283 printf(" -b --bsc IP. The BSC IP address.\n");
1284 printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
1285 printf("\n");
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001286 printf(" -s --disable-color. Disable the color inside the logging message.\n");
1287 printf(" -e --log-level number. Set the global loglevel.\n");
1288 printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
1289 printf(" -V --version. Print the version of OpenBSC.\n");
1290}
1291
1292static void print_usage()
1293{
1294 printf("Usage: ipaccess-proxy\n");
1295}
1296
1297static void handle_options(int argc, char** argv)
1298{
1299 while (1) {
1300 int option_index = 0, c;
1301 static struct option long_options[] = {
1302 {"help", 0, 0, 'h'},
1303 {"disable-color", 0, 0, 's'},
1304 {"timestamp", 0, 0, 'T'},
1305 {"log-level", 1, 0, 'e'},
1306 {"listen", 1, 0, 'l'},
1307 {"bsc", 1, 0, 'b'},
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001308 {"udp", 1, 0, 'u'},
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001309 {0, 0, 0, 0}
1310 };
1311
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001312 c = getopt_long(argc, argv, "hsTe:l:b:g:",
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001313 long_options, &option_index);
1314 if (c == -1)
1315 break;
1316
1317 switch (c) {
1318 case 'h':
1319 print_usage();
1320 print_help();
1321 exit(0);
1322 case 'l':
1323 listen_ipaddr = optarg;
1324 break;
1325 case 'b':
1326 bsc_ipaddr = optarg;
1327 break;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001328 case 'g':
1329 gprs_ns_ipaddr = optarg;
1330 break;
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001331 case 's':
1332 log_set_use_color(stderr_target, 0);
1333 break;
1334 case 'T':
1335 log_set_print_timestamp(stderr_target, 1);
1336 break;
1337 case 'e':
1338 log_set_log_level(stderr_target, atoi(optarg));
1339 break;
1340 default:
1341 /* ignore */
1342 break;
1343 }
1344 }
1345}
1346
Harald Welte2ca7c312009-12-23 22:44:04 +01001347int main(int argc, char **argv)
1348{
1349 int rc;
1350
1351 listen_ipaddr = "192.168.100.11";
1352 bsc_ipaddr = "192.168.100.239";
1353
1354 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1355
Harald Weltedc5062b2010-03-26 21:28:59 +08001356 log_init(&log_info);
1357 stderr_target = log_target_create_stderr();
1358 log_add_target(stderr_target);
1359 log_set_all_filter(stderr_target, 1);
1360 log_parse_category_mask(stderr_target, "DINP:DMI");
Harald Welte2ca7c312009-12-23 22:44:04 +01001361
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001362 handle_options(argc, argv);
1363
Harald Welte2ca7c312009-12-23 22:44:04 +01001364 rc = ipaccess_proxy_setup();
1365 if (rc < 0)
1366 exit(1);
1367
1368 signal(SIGUSR1, &signal_handler);
1369 signal(SIGABRT, &signal_handler);
1370
1371 while (1) {
1372 bsc_select_main(0);
1373 }
1374}