blob: a6b433f94d64b88d16b0a1646f1cfe168f8d65ea [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
Harald Welte9af6ddf2011-01-01 15:25:50 +010010 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
Harald Welte2ca7c312009-12-23 22:44:04 +010012 * (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
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * GNU Affero General Public License for more details.
Harald Welte2ca7c312009-12-23 22:44:04 +010018 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010019 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte2ca7c312009-12-23 22:44:04 +010021 *
22 */
23
24#include <stdio.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <errno.h>
28#include <string.h>
29#include <signal.h>
30#include <time.h>
31#include <sys/fcntl.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/ioctl.h>
35#include <arpa/inet.h>
36#include <netinet/in.h>
37
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +080038#define _GNU_SOURCE
39#include <getopt.h>
40
Harald Welte2ca7c312009-12-23 22:44:04 +010041#include <openbsc/gsm_data.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010042#include <osmocom/core/select.h>
43#include <osmocom/gsm/tlv.h>
44#include <osmocom/core/msgb.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010045#include <openbsc/debug.h>
46#include <openbsc/ipaccess.h>
Pablo Neira Ayusoda2f7692011-04-05 18:33:28 +020047#include <openbsc/socket.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010048#include <osmocom/core/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
Harald Welte2ca7c312009-12-23 22:44:04 +0100292static int handle_udp_read(struct bsc_fd *bfd)
293{
294 struct ipa_bts_conn *ipbc = bfd->data;
295 struct ipa_proxy_conn *other_conn = NULL;
296 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
297 struct ipaccess_head *hh;
298 int ret;
299
300 /* with UDP sockets, we cannot read partial packets but have to read
301 * all of it in one go */
302 hh = (struct ipaccess_head *) msg->data;
303 ret = recv(bfd->fd, msg->data, msg->data_len, 0);
304 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100305 if (errno != EAGAIN)
306 LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100307 msgb_free(msg);
308 return ret;
309 }
310 if (ret == 0) {
311 DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
312 bsc_unregister_fd(bfd);
313 close(bfd->fd);
314 bfd->fd = -1;
315 msgb_free(msg);
316 return -EIO;
317 }
318 if (ret < sizeof(*hh)) {
319 DEBUGP(DINP, "could not even read header!?!\n");
320 msgb_free(msg);
321 return -EIO;
322 }
323 msgb_put(msg, ret);
324 msg->l2h = msg->data + sizeof(*hh);
325 DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
326
327 if (hh->len != msg->len - sizeof(*hh)) {
328 DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
329 msg->len, msg->len - 3, hh->len);
330 msgb_free(msg);
331 return -EIO;
332 }
333
334 switch (bfd->priv_nr & 0xff) {
335 case UDP_TO_BTS:
336 /* injection towards BTS */
337 switch (hh->proto) {
338 case IPAC_PROTO_RSL:
339 /* FIXME: what to do about TRX > 0 */
340 other_conn = ipbc->rsl_conn[0];
341 break;
342 default:
343 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
344 "OML FD\n", hh->proto);
345 /* fall through */
346 case IPAC_PROTO_IPACCESS:
347 case IPAC_PROTO_OML:
348 other_conn = ipbc->oml_conn;
349 break;
350 }
351 break;
352 case UDP_TO_BSC:
353 /* injection towards BSC */
354 switch (hh->proto) {
355 case IPAC_PROTO_RSL:
356 /* FIXME: what to do about TRX > 0 */
357 other_conn = ipbc->bsc_rsl_conn[0];
358 break;
359 default:
360 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
361 "OML FD\n", hh->proto);
362 case IPAC_PROTO_IPACCESS:
363 case IPAC_PROTO_OML:
364 other_conn = ipbc->bsc_oml_conn;
365 break;
366 }
367 break;
368 default:
369 DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
370 break;
371 }
372
373 if (other_conn) {
374 /* enqueue the message for TX on the respective FD */
375 msgb_enqueue(&other_conn->tx_queue, msg);
376 other_conn->fd.when |= BSC_FD_WRITE;
377 } else
378 msgb_free(msg);
379
380 return 0;
381}
382
383static int handle_udp_write(struct bsc_fd *bfd)
384{
385 /* not implemented yet */
386 bfd->when &= ~BSC_FD_WRITE;
387
388 return -EIO;
389}
390
391/* callback from select.c in case one of the fd's can be read/written */
392static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
393{
394 int rc = 0;
395
396 if (what & BSC_FD_READ)
397 rc = handle_udp_read(bfd);
398 if (what & BSC_FD_WRITE)
399 rc = handle_udp_write(bfd);
400
401 return rc;
402}
403
404
405static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
406 u_int16_t site_id, u_int16_t bts_id,
407 u_int16_t trx_id, struct tlv_parsed *tlvp,
408 struct msgb *msg)
409{
410 struct ipa_bts_conn *ipbc;
411 u_int16_t udp_port;
412 int ret = 0;
413 struct sockaddr_in sin;
414
415 memset(&sin, 0, sizeof(sin));
416 sin.sin_family = AF_INET;
417 inet_aton(bsc_ipaddr, &sin.sin_addr);
418
419 DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
420 site_id, bts_id, trx_id);
421
422 /* OML needs to be established before RSL */
423 if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
424 DEBUGPC(DINP, "Not a OML connection ?!?\n");
425 return -EIO;
426 }
427
428 /* allocate new BTS connection data structure */
429 ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
430 if (!ipbc) {
431 ret = -ENOMEM;
432 goto err_out;
433 }
434
435 DEBUGPC(DINP, "Created BTS Conn data structure\n");
436 ipbc->ipp = ipp;
437 ipbc->unit_id.site_id = site_id;
438 ipbc->unit_id.bts_id = bts_id;
439 ipbc->oml_conn = ipc;
440 ipc->bts_conn = ipbc;
441
442 /* store the content of the ID TAGS for later reference */
443 store_idtags(ipbc, tlvp);
444 ipbc->id_resp_len = msg->len;
445 ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
446 memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
447
448 /* Create OML TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100449 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100450 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
451 if (!ipbc->bsc_oml_conn) {
452 ret = -EIO;
453 goto err_bsc_conn;
454 }
455
456 DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
457 site_id, bts_id, trx_id);
458
459 /* Create UDP socket for BTS packet injection */
460 udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
Pablo Neira Ayusoda2f7692011-04-05 18:33:28 +0200461 ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
Harald Welte2ca7c312009-12-23 22:44:04 +0100462 UDP_TO_BTS, udp_fd_cb, ipbc);
463 if (ret < 0)
464 goto err_udp_bts;
465 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
466 "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
467
468 /* Create UDP socket for BSC packet injection */
469 udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
Pablo Neira Ayusoda2f7692011-04-05 18:33:28 +0200470 ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
Harald Welte2ca7c312009-12-23 22:44:04 +0100471 UDP_TO_BSC, udp_fd_cb, ipbc);
472 if (ret < 0)
473 goto err_udp_bsc;
474 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
475 "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800476
477
478 /* GPRS NS related code */
479 if (gprs_ns_ipaddr) {
480 struct sockaddr_in sock;
481 socklen_t len = sizeof(sock);
482 ret = make_gprs_sock(&ipbc->gprs_ns_fd, gprs_ns_cb, ipbc);
483 if (ret < 0) {
484 LOGP(DINP, LOGL_ERROR, "Creating the GPRS socket failed.\n");
485 goto err_udp_bsc;
486 }
487
488 ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len);
489 ipbc->gprs_local_port = ntohs(sock.sin_port);
490 LOGP(DINP, LOGL_NOTICE,
491 "Created GPRS NS Socket. Listening on: %s:%d\n",
492 inet_ntoa(sock.sin_addr), ipbc->gprs_local_port);
493
494 ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len);
495 ipbc->bts_addr = sock.sin_addr;
496 }
497
Harald Welte2ca7c312009-12-23 22:44:04 +0100498 llist_add(&ipbc->list, &ipp->bts_list);
499
500 return 0;
501
502err_udp_bsc:
503 bsc_unregister_fd(&ipbc->udp_bts_fd);
504err_udp_bts:
505 bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
506 close(ipbc->bsc_oml_conn->fd.fd);
507 talloc_free(ipbc->bsc_oml_conn);
508 ipbc->bsc_oml_conn = NULL;
509err_bsc_conn:
510 talloc_free(ipbc->id_resp);
511 talloc_free(ipbc);
512#if 0
513 bsc_unregister_fd(bfd);
514 close(bfd->fd);
515 talloc_free(bfd);
516#endif
517err_out:
518 return ret;
519}
520
521static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
522 struct bsc_fd *bfd)
523{
524 struct tlv_parsed tlvp;
525 u_int8_t msg_type = *(msg->l2h);
526 u_int16_t site_id, bts_id, trx_id;
527 struct ipa_bts_conn *ipbc;
528 int ret = 0;
529
530 switch (msg_type) {
531 case IPAC_MSGT_PING:
532 ret = write(bfd->fd, pong, sizeof(pong));
533 if (ret < 0)
534 return ret;
535 if (ret < sizeof(pong)) {
536 DEBUGP(DINP, "short write\n");
537 return -EIO;
538 }
539 break;
540 case IPAC_MSGT_PONG:
541 DEBUGP(DMI, "PONG!\n");
542 break;
543 case IPAC_MSGT_ID_RESP:
544 DEBUGP(DMI, "ID_RESP ");
545 /* parse tags, search for Unit ID */
546 ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
547 msgb_l2len(msg)-2);
548 DEBUGP(DMI, "\n");
549
550 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
551 LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
552 return -EIO;
553 }
554
555 /* lookup BTS, create sign_link, ... */
Holger Hans Peter Freyther82ae7162010-03-30 15:20:46 +0200556 site_id = bts_id = trx_id = 0;
Harald Welte2ca7c312009-12-23 22:44:04 +0100557 parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
558 &site_id, &bts_id, &trx_id);
559 ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
560 if (!ipbc) {
561 /* We have not found an ipbc (per-bts proxy instance)
562 * for this BTS yet. The first connection of a new BTS must
563 * be a OML connection. We allocate the associated data structures,
564 * and try to connect to the remote end */
565
566 return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
567 trx_id, &tlvp, msg);
568 /* if this fails, the caller will clean up bfd */
569 } else {
570 struct sockaddr_in sin;
571 memset(&sin, 0, sizeof(sin));
572 sin.sin_family = AF_INET;
573 inet_aton(bsc_ipaddr, &sin.sin_addr);
574
575 DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
576 site_id, bts_id, trx_id);
577
578 if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
579 LOGP(DINP, LOGL_ERROR, "Second OML connection from "
580 "same BTS ?!?\n");
581 return 0;
582 }
583
584 if (trx_id > MAX_TRX) {
585 LOGP(DINP, LOGL_ERROR, "We don't support more "
586 "than %u TRX\n", MAX_TRX);
587 return -EINVAL;
588 }
589
590 ipc->bts_conn = ipbc;
591 /* store TRX number in higher 8 bit of the bfd private number */
592 bfd->priv_nr |= trx_id << 8;
593 ipbc->rsl_conn[trx_id] = ipc;
594
595 /* Create RSL TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100596 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100597 ipbc->bsc_rsl_conn[trx_id] =
598 connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
599 if (!ipbc->bsc_oml_conn)
600 return -EIO;
601 DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
602 site_id, bts_id, trx_id);
603 }
604 break;
605 case IPAC_MSGT_ID_GET:
606 DEBUGP(DMI, "ID_GET\n");
607 if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
608 (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
609 DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
610 return -EIO;
611 }
612 ipbc = ipc->bts_conn;
613 if (!ipbc) {
614 DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
615 return -EIO;
616 }
617 ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
618 break;
619 case IPAC_MSGT_ID_ACK:
620 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
621 ret = write(bfd->fd, id_ack, sizeof(id_ack));
622 break;
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800623 default:
624 LOGP(DMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type);
625 return 1;
626 break;
Harald Welte2ca7c312009-12-23 22:44:04 +0100627 }
628 return 0;
629}
630
631struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
632{
633 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
634 struct ipaccess_head *hh;
635 int len, ret = 0;
636
637 if (!msg) {
638 *error = -ENOMEM;
639 return NULL;
640 }
641
642 /* first read our 3-byte header */
643 hh = (struct ipaccess_head *) msg->data;
644 ret = recv(bfd->fd, msg->data, 3, 0);
645 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100646 if (errno != EAGAIN)
Harald Welteda956932009-12-24 12:49:43 +0100647 LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100648 msgb_free(msg);
649 *error = ret;
650 return NULL;
651 } else if (ret == 0) {
652 msgb_free(msg);
653 *error = ret;
654 return NULL;
655 }
656
657 msgb_put(msg, ret);
658
659 /* then read te length as specified in header */
660 msg->l2h = msg->data + sizeof(*hh);
661 len = ntohs(hh->len);
662 ret = recv(bfd->fd, msg->l2h, len, 0);
663 if (ret < len) {
664 LOGP(DINP, LOGL_ERROR, "short read!\n");
665 msgb_free(msg);
666 *error = -EIO;
667 return NULL;
668 }
669 msgb_put(msg, ret);
670
671 return msg;
672}
673
674static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
675 unsigned int priv_nr)
676{
677 struct ipa_proxy_conn *bsc_conn;
678 unsigned int trx_id = priv_nr >> 8;
679
680 switch (priv_nr & 0xff) {
681 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
682 bsc_conn = ipbc->bsc_oml_conn;
683 break;
684 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
685 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
686 break;
687 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
688 bsc_conn = ipbc->oml_conn;
689 break;
690 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
691 bsc_conn = ipbc->rsl_conn[trx_id];
692 break;
693 default:
694 bsc_conn = NULL;
695 break;
696 }
697 return bsc_conn;
698}
699
700static void reconn_tmr_cb(void *data)
701{
702 struct ipa_proxy *ipp = data;
703 struct ipa_bts_conn *ipbc;
704 struct sockaddr_in sin;
705 int i;
706
707 DEBUGP(DINP, "Running reconnect timer\n");
708
709 memset(&sin, 0, sizeof(sin));
710 sin.sin_family = AF_INET;
711 inet_aton(bsc_ipaddr, &sin.sin_addr);
712
713 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
714 /* if OML to BSC is dead, try to restore it */
715 if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
Harald Welte87ed5cd2009-12-23 22:47:53 +0100716 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100717 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
718 LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
719 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
720 if (!ipbc->bsc_oml_conn)
721 goto reschedule;
722 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
723 LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
724 }
725 /* if we (still) don't have a OML connection, skip RSL */
726 if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
727 continue;
728
729 for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
730 unsigned int priv_nr;
731 /* don't establish RSL links which we don't have */
732 if (!ipbc->rsl_conn[i])
733 continue;
734 if (ipbc->bsc_rsl_conn[i])
735 continue;
736 priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
737 priv_nr &= ~0xff;
738 priv_nr |= RSL_TO_BSC;
Harald Welte87ed5cd2009-12-23 22:47:53 +0100739 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100740 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
741 LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
742 ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
743 if (!ipbc->bsc_rsl_conn)
744 goto reschedule;
745 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
746 LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
747 }
748 }
749 return;
750
751reschedule:
752 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
753}
754
755static void handle_dead_socket(struct bsc_fd *bfd)
756{
757 struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
758 struct ipa_proxy_conn *bsc_conn; /* remote conn */
759 struct ipa_bts_conn *ipbc = ipc->bts_conn;
760 unsigned int trx_id = bfd->priv_nr >> 8;
761 struct msgb *msg, *msg2;
762
763 bsc_unregister_fd(bfd);
764 close(bfd->fd);
765 bfd->fd = -1;
766
767 /* FIXME: clear tx_queue, remove all references, etc. */
768 llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
769 msgb_free(msg);
770
771 switch (bfd->priv_nr & 0xff) {
772 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
Pablo Neira Ayuso3c409c22011-03-27 21:31:48 +0200773 /* The BTS started a connection with us but we got no
774 * IPAC_MSGT_ID_RESP message yet, in that scenario we did not
775 * allocate the ipa_bts_conn structure. */
776 if (ipbc == NULL)
777 break;
Harald Welte2ca7c312009-12-23 22:44:04 +0100778 ipbc->oml_conn = NULL;
779 bsc_conn = ipbc->bsc_oml_conn;
780 /* close the connection to the BSC */
781 bsc_unregister_fd(&bsc_conn->fd);
782 close(bsc_conn->fd.fd);
783 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
784 msgb_free(msg);
785 talloc_free(bsc_conn);
786 ipbc->bsc_oml_conn = NULL;
787 /* FIXME: do we need to delete the entire ipbc ? */
788 break;
789 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
790 ipbc->rsl_conn[trx_id] = NULL;
791 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
792 /* close the connection to the BSC */
793 bsc_unregister_fd(&bsc_conn->fd);
794 close(bsc_conn->fd.fd);
795 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
796 msgb_free(msg);
797 talloc_free(bsc_conn);
798 ipbc->bsc_rsl_conn[trx_id] = NULL;
799 break;
800 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
801 ipbc->bsc_oml_conn = NULL;
802 bsc_conn = ipbc->oml_conn;
803 /* start reconnect timer */
804 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
805 break;
806 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
807 ipbc->bsc_rsl_conn[trx_id] = NULL;
808 bsc_conn = ipbc->rsl_conn[trx_id];
809 /* start reconnect timer */
810 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
811 break;
812 default:
813 bsc_conn = NULL;
814 break;
815 }
816
817 talloc_free(ipc);
818}
819
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800820static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg)
821{
822 uint8_t *nsvci;
823
824 if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC)
825 return;
826
827 if (msgb_l2len(msg) != 39)
828 return;
829
830 /*
831 * Check if this is a IPA Set Attribute or IPA Set Attribute ACK
832 * and if the FOM Class is GPRS NSVC0 and then we will patch it.
833 *
834 * The patch assumes the message looks like the one from the trace
835 * but we only match messages with a specific size anyway... So
836 * this hack should work just fine.
837 */
838
839 if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
840 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
841 msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800842 nsvci = &msg->l2h[23];
843 ipbc->gprs_orig_port = *(u_int16_t *)(nsvci+8);
844 ipbc->gprs_orig_ip = *(u_int32_t *)(nsvci+10);
845 *(u_int16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
846 *(u_int32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
847 } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
848 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
849 msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800850 nsvci = &msg->l2h[23];
851 *(u_int16_t *)(nsvci+8) = ipbc->gprs_orig_port;
852 *(u_int32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
853 }
854}
855
Harald Welte2ca7c312009-12-23 22:44:04 +0100856static int handle_tcp_read(struct bsc_fd *bfd)
857{
858 struct ipa_proxy_conn *ipc = bfd->data;
859 struct ipa_bts_conn *ipbc = ipc->bts_conn;
860 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100861 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100862 struct ipaccess_head *hh;
863 int ret = 0;
864 char *btsbsc;
865
Harald Welte2ca7c312009-12-23 22:44:04 +0100866 if ((bfd->priv_nr & 0xff) <= 2)
867 btsbsc = "BTS";
868 else
869 btsbsc = "BSC";
870
871 msg = ipaccess_read_msg(bfd, &ret);
872 if (!msg) {
873 if (ret == 0) {
874 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
875 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
876 "dead socket\n", btsbsc);
877 handle_dead_socket(bfd);
878 }
879 return ret;
880 }
881
882 msgb_put(msg, ret);
883 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
884 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
885
886 hh = (struct ipaccess_head *) msg->data;
887 if (hh->proto == IPAC_PROTO_IPACCESS) {
888 ret = ipaccess_rcvmsg(ipc, msg, bfd);
889 if (ret < 0) {
890 bsc_unregister_fd(bfd);
891 close(bfd->fd);
892 bfd->fd = -1;
893 talloc_free(bfd);
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800894 msgb_free(msg);
895 return ret;
896 } else if (ret == 0) {
897 /* we do not forward parts of the CCM protocol
898 * through the proxy but rather terminate it ourselves. */
899 msgb_free(msg);
900 return ret;
Harald Welte2ca7c312009-12-23 22:44:04 +0100901 }
Harald Welte2ca7c312009-12-23 22:44:04 +0100902 }
903
904 if (!ipbc) {
905 LOGP(DINP, LOGL_ERROR,
906 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
907 msgb_free(msg);
908 return -EIO;
909 }
910
911 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
912 if (bsc_conn) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800913 if (gprs_ns_ipaddr)
914 patch_gprs_msg(ipbc, bfd->priv_nr, msg);
Harald Welte2ca7c312009-12-23 22:44:04 +0100915 /* enqueue packet towards BSC */
916 msgb_enqueue(&bsc_conn->tx_queue, msg);
917 /* mark respective filedescriptor as 'we want to write' */
918 bsc_conn->fd.when |= BSC_FD_WRITE;
919 } else {
920 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
921 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
922 "since remote connection is dead\n", btsbsc);
923 msgb_free(msg);
924 }
925
926 return ret;
927}
928
929/* a TCP socket is ready to be written to */
930static int handle_tcp_write(struct bsc_fd *bfd)
931{
932 struct ipa_proxy_conn *ipc = bfd->data;
933 struct ipa_bts_conn *ipbc = ipc->bts_conn;
934 struct llist_head *lh;
935 struct msgb *msg;
936 char *btsbsc;
937 int ret;
938
939 if ((bfd->priv_nr & 0xff) <= 2)
940 btsbsc = "BTS";
941 else
942 btsbsc = "BSC";
943
944
945 /* get the next msg for this timeslot */
946 if (llist_empty(&ipc->tx_queue)) {
947 bfd->when &= ~BSC_FD_WRITE;
948 return 0;
949 }
950 lh = ipc->tx_queue.next;
951 llist_del(lh);
952 msg = llist_entry(lh, struct msgb, list);
953
954 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
955 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
956 hexdump(msg->data, msg->len));
957
958 ret = send(bfd->fd, msg->data, msg->len, 0);
959 msgb_free(msg);
960
961 if (ret == 0) {
962 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
963 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
964 handle_dead_socket(bfd);
965 }
966
967 return ret;
968}
969
970/* callback from select.c in case one of the fd's can be read/written */
971static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
972{
973 int rc = 0;
974
975 if (what & BSC_FD_READ) {
976 rc = handle_tcp_read(bfd);
977 if (rc < 0)
978 return rc;
979 }
980 if (what & BSC_FD_WRITE)
981 rc = handle_tcp_write(bfd);
982
983 return rc;
984}
985
986/* callback of the listening filedescriptor */
987static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
988{
989 int ret;
990 struct ipa_proxy_conn *ipc;
991 struct bsc_fd *bfd;
992 struct sockaddr_in sa;
993 socklen_t sa_len = sizeof(sa);
994
995 if (!(what & BSC_FD_READ))
996 return 0;
997
998 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
999 if (ret < 0) {
1000 perror("accept");
1001 return ret;
1002 }
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +02001003 DEBUGP(DINP, "accept()ed new %s link from %s\n",
Harald Welte2ca7c312009-12-23 22:44:04 +01001004 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
1005 inet_ntoa(sa.sin_addr));
1006
1007 ipc = alloc_conn();
1008 if (!ipc) {
1009 close(ret);
1010 return -ENOMEM;
1011 }
1012
1013 bfd = &ipc->fd;
1014 bfd->fd = ret;
1015 bfd->data = ipc;
1016 bfd->priv_nr = listen_bfd->priv_nr;
1017 bfd->cb = ipaccess_fd_cb;
1018 bfd->when = BSC_FD_READ;
1019 ret = bsc_register_fd(bfd);
1020 if (ret < 0) {
1021 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
1022 close(bfd->fd);
1023 talloc_free(ipc);
1024 return ret;
1025 }
1026
1027 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
1028 ret = write(bfd->fd, id_req, sizeof(id_req));
1029
1030 return 0;
1031}
1032
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001033static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
1034{
1035 int ret;
1036 struct sockaddr_in addr;
1037 socklen_t len = sizeof(addr);
1038 memset(&addr, 0, sizeof(addr));
1039
1040 addr.sin_family = AF_INET;
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001041 addr.sin_port = htons(port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001042 addr.sin_addr = ip;
1043
1044 ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
1045 if (ret < 0) {
1046 LOGP(DINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
1047 }
1048}
1049
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001050static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what)
1051{
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001052 struct ipa_bts_conn *bts;
1053 char buf[4096];
1054 int ret;
1055 struct sockaddr_in sock;
1056 socklen_t len = sizeof(sock);
1057
1058 /* 1. get the data... */
1059 ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
1060 if (ret < 0) {
1061 LOGP(DINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
1062 return -1;
1063 }
1064
1065 bts = bfd->data;
1066
1067 /* 2. figure out where to send it to */
1068 if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
1069 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
1070 send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
1071 } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
1072 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
1073 send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001074 } else {
1075 LOGP(DINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001076 }
1077
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001078 return 0;
1079}
1080
Harald Welte2ca7c312009-12-23 22:44:04 +01001081static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
1082 int (*cb)(struct bsc_fd *fd, unsigned int what))
1083{
1084 struct sockaddr_in addr;
1085 int ret, on = 1;
1086
1087 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1088 bfd->cb = cb;
1089 bfd->when = BSC_FD_READ;
1090 bfd->priv_nr = priv_nr;
1091
1092 memset(&addr, 0, sizeof(addr));
1093 addr.sin_family = AF_INET;
1094 addr.sin_port = htons(port);
1095 if (!listen_ipaddr)
1096 addr.sin_addr.s_addr = INADDR_ANY;
1097 else
1098 inet_aton(listen_ipaddr, &addr.sin_addr);
1099
1100 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1101
1102 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1103 if (ret < 0) {
Holger Hans Peter Freyther76706512010-06-09 15:17:53 +08001104 LOGP(DINP, LOGL_ERROR,
1105 "Could not bind listen socket for IP %s with error: %s.\n",
1106 listen_ipaddr, strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +01001107 return -EIO;
1108 }
1109
1110 ret = listen(bfd->fd, 1);
1111 if (ret < 0) {
1112 perror("listen");
1113 return ret;
1114 }
1115
1116 ret = bsc_register_fd(bfd);
1117 if (ret < 0) {
1118 perror("register_listen_fd");
1119 return ret;
1120 }
1121 return 0;
1122}
1123
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001124static 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 +08001125{
1126 struct sockaddr_in addr;
1127 int ret;
1128
1129 bfd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1130 bfd->cb = cb;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001131 bfd->data = data;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001132 bfd->when = BSC_FD_READ;
1133
1134 memset(&addr, 0, sizeof(addr));
1135 addr.sin_family = AF_INET;
1136 addr.sin_port = 0;
1137 inet_aton(listen_ipaddr, &addr.sin_addr);
1138
1139 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1140 if (ret < 0) {
1141 LOGP(DINP, LOGL_ERROR,
1142 "Could not bind n socket for IP %s with error: %s.\n",
1143 listen_ipaddr, strerror(errno));
1144 return -EIO;
1145 }
1146
1147 ret = bsc_register_fd(bfd);
1148 if (ret < 0) {
1149 perror("register_listen_fd");
1150 return ret;
1151 }
1152 return 0;
1153}
1154
Harald Welte2ca7c312009-12-23 22:44:04 +01001155/* Actively connect to a BSC. */
1156static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1157{
1158 struct ipa_proxy_conn *ipc;
1159 struct bsc_fd *bfd;
1160 int ret, on = 1;
1161
1162 ipc = alloc_conn();
1163 if (!ipc)
1164 return NULL;
1165
1166 ipc->bts_conn = data;
1167
1168 bfd = &ipc->fd;
1169 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1170 bfd->cb = ipaccess_fd_cb;
1171 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1172 bfd->data = ipc;
1173 bfd->priv_nr = priv_nr;
1174
1175 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1176
1177 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1178 if (ret < 0) {
Holger Hans Peter Freyther6cb9b232010-06-09 15:15:11 +08001179 LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n",
1180 inet_ntoa(sa->sin_addr));
Harald Welte2ca7c312009-12-23 22:44:04 +01001181 close(bfd->fd);
1182 talloc_free(ipc);
1183 return NULL;
1184 }
1185
1186 /* pre-fill tx_queue with identity request */
1187 ret = bsc_register_fd(bfd);
1188 if (ret < 0) {
1189 close(bfd->fd);
1190 talloc_free(ipc);
1191 return NULL;
1192 }
1193
1194 return ipc;
1195}
1196
1197static int ipaccess_proxy_setup(void)
1198{
1199 int ret;
1200
1201 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1202 if (!ipp)
1203 return -ENOMEM;
1204 INIT_LLIST_HEAD(&ipp->bts_list);
1205 ipp->reconn_timer.cb = reconn_tmr_cb;
1206 ipp->reconn_timer.data = ipp;
1207
1208 /* Listen for OML connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001209 ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
1210 OML_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001211 if (ret < 0)
1212 return ret;
1213
1214 /* Listen for RSL connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001215 ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
1216 RSL_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001217
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001218 if (ret < 0)
1219 return ret;
1220
1221 /* Connect the GPRS NS Socket */
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001222 if (gprs_ns_ipaddr) {
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001223 inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001224 inet_aton(listen_ipaddr, &ipp->listen_addr);
1225 }
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001226
Harald Welte2ca7c312009-12-23 22:44:04 +01001227 return ret;
1228}
1229
1230static void signal_handler(int signal)
1231{
1232 fprintf(stdout, "signal %u received\n", signal);
1233
1234 switch (signal) {
1235 case SIGABRT:
1236 /* in case of abort, we want to obtain a talloc report
1237 * and then return to the caller, who will abort the process */
1238 case SIGUSR1:
1239 talloc_report_full(tall_bsc_ctx, stderr);
1240 break;
1241 default:
1242 break;
1243 }
1244}
1245
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001246static void print_help()
1247{
1248 printf(" ipaccess-proxy is a proxy BTS.\n");
1249 printf(" -h --help. This help text.\n");
1250 printf(" -l --listen IP. The ip to listen to.\n");
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001251 printf(" -b --bsc IP. The BSC IP address.\n");
1252 printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
1253 printf("\n");
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001254 printf(" -s --disable-color. Disable the color inside the logging message.\n");
1255 printf(" -e --log-level number. Set the global loglevel.\n");
1256 printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
1257 printf(" -V --version. Print the version of OpenBSC.\n");
1258}
1259
1260static void print_usage()
1261{
1262 printf("Usage: ipaccess-proxy\n");
1263}
1264
1265static void handle_options(int argc, char** argv)
1266{
1267 while (1) {
1268 int option_index = 0, c;
1269 static struct option long_options[] = {
1270 {"help", 0, 0, 'h'},
1271 {"disable-color", 0, 0, 's'},
1272 {"timestamp", 0, 0, 'T'},
1273 {"log-level", 1, 0, 'e'},
1274 {"listen", 1, 0, 'l'},
1275 {"bsc", 1, 0, 'b'},
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001276 {"udp", 1, 0, 'u'},
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001277 {0, 0, 0, 0}
1278 };
1279
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001280 c = getopt_long(argc, argv, "hsTe:l:b:g:",
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001281 long_options, &option_index);
1282 if (c == -1)
1283 break;
1284
1285 switch (c) {
1286 case 'h':
1287 print_usage();
1288 print_help();
1289 exit(0);
1290 case 'l':
1291 listen_ipaddr = optarg;
1292 break;
1293 case 'b':
1294 bsc_ipaddr = optarg;
1295 break;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001296 case 'g':
1297 gprs_ns_ipaddr = optarg;
1298 break;
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001299 case 's':
1300 log_set_use_color(stderr_target, 0);
1301 break;
1302 case 'T':
1303 log_set_print_timestamp(stderr_target, 1);
1304 break;
1305 case 'e':
1306 log_set_log_level(stderr_target, atoi(optarg));
1307 break;
1308 default:
1309 /* ignore */
1310 break;
1311 }
1312 }
1313}
1314
Harald Welte2ca7c312009-12-23 22:44:04 +01001315int main(int argc, char **argv)
1316{
1317 int rc;
1318
1319 listen_ipaddr = "192.168.100.11";
1320 bsc_ipaddr = "192.168.100.239";
1321
1322 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1323
Harald Weltedc5062b2010-03-26 21:28:59 +08001324 log_init(&log_info);
1325 stderr_target = log_target_create_stderr();
1326 log_add_target(stderr_target);
1327 log_set_all_filter(stderr_target, 1);
1328 log_parse_category_mask(stderr_target, "DINP:DMI");
Harald Welte2ca7c312009-12-23 22:44:04 +01001329
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001330 handle_options(argc, argv);
1331
Harald Welte2ca7c312009-12-23 22:44:04 +01001332 rc = ipaccess_proxy_setup();
1333 if (rc < 0)
1334 exit(1);
1335
1336 signal(SIGUSR1, &signal_handler);
1337 signal(SIGABRT, &signal_handler);
1338
1339 while (1) {
1340 bsc_select_main(0);
1341 }
1342}