blob: b8b42c02567f4aa70a53d243cf5d93779f50834d [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
Harald Welte2ca7c312009-12-23 22:44:04 +0100134static const char *idtag_names[] = {
135 [IPAC_IDTAG_SERNR] = "Serial_Number",
136 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
137 [IPAC_IDTAG_LOCATION1] = "Location_1",
138 [IPAC_IDTAG_LOCATION2] = "Location_2",
139 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
140 [IPAC_IDTAG_SWVERSION] = "Software_Version",
141 [IPAC_IDTAG_IPADDR] = "IP_Address",
142 [IPAC_IDTAG_MACADDR] = "MAC_Address",
143 [IPAC_IDTAG_UNIT] = "Unit_ID",
144};
145
146static const char *ipac_idtag_name(int tag)
147{
148 if (tag >= ARRAY_SIZE(idtag_names))
149 return "unknown";
150
151 return idtag_names[tag];
152}
153
154static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
155{
156 u_int8_t t_len;
157 u_int8_t t_tag;
158 u_int8_t *cur = buf;
159
160 while (cur < buf + len) {
161 t_len = *cur++;
162 t_tag = *cur++;
163
164 DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
165
166 dec->lv[t_tag].len = t_len;
167 dec->lv[t_tag].val = cur;
168
169 cur += t_len;
170 }
171 return 0;
172}
173
174static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
175 u_int16_t *trx_id)
176{
177 unsigned long ul;
178 char *endptr;
179 const char *nptr;
180
181 nptr = str;
182 ul = strtoul(nptr, &endptr, 10);
183 if (endptr <= nptr)
184 return -EINVAL;
185 if (site_id)
186 *site_id = ul & 0xffff;
187
188 if (*endptr++ != '/')
189 return -EINVAL;
190
191 nptr = endptr;
192 ul = strtoul(nptr, &endptr, 10);
193 if (endptr <= nptr)
194 return -EINVAL;
195 if (bts_id)
196 *bts_id = ul & 0xffff;
197
198 if (*endptr++ != '/')
199 return -EINVAL;
200
201 nptr = endptr;
202 ul = strtoul(nptr, &endptr, 10);
203 if (endptr <= nptr)
204 return -EINVAL;
205 if (trx_id)
206 *trx_id = ul & 0xffff;
207
208 return 0;
209}
210
211static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
212 u_int16_t site_id,
213 u_int16_t bts_id)
214{
215 struct ipa_bts_conn *ipbc;
216
217 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
218 if (ipbc->unit_id.site_id == site_id &&
219 ipbc->unit_id.bts_id == bts_id)
220 return ipbc;
221 }
222
223 return NULL;
224}
225
226struct ipa_proxy_conn *alloc_conn(void)
227{
228 struct ipa_proxy_conn *ipc;
229
230 ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
231 if (!ipc)
232 return NULL;
233
234 INIT_LLIST_HEAD(&ipc->tx_queue);
235
236 return ipc;
237}
238
239static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
240{
241 unsigned int i, len;
242
243 for (i = 0; i <= 0xff; i++) {
244 if (!TLVP_PRESENT(tlvp, i))
245 continue;
246
247 len = TLVP_LEN(tlvp, i);
248#if 0
249 if (!ipbc->id_tags[i])
250 ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
251 else
252#endif
Harald Weltea16ef3d2009-12-23 23:35:51 +0100253 ipbc->id_tags[i] = talloc_realloc_size(ipbc,
Harald Welte2ca7c312009-12-23 22:44:04 +0100254 ipbc->id_tags[i], len);
255 if (!ipbc->id_tags[i])
256 return -ENOMEM;
257
258 memset(ipbc->id_tags[i], 0, len);
259 //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
260 }
261 return 0;
262}
263
264
265static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
266
267#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
268
269static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
270 struct ipa_bts_conn *ipbc, u_int8_t trx_id)
271{
272 if (ipbc)
Harald Weltedc5062b2010-03-26 21:28:59 +0800273 logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
Harald Welte2ca7c312009-12-23 22:44:04 +0100274 ipbc->unit_id.bts_id, trx_id);
275 else
Harald Weltedc5062b2010-03-26 21:28:59 +0800276 logp2(ss, lvl, file, line, 0, "unknown ");
Harald Welte2ca7c312009-12-23 22:44:04 +0100277}
278
Harald Welte2ca7c312009-12-23 22:44:04 +0100279static int handle_udp_read(struct bsc_fd *bfd)
280{
281 struct ipa_bts_conn *ipbc = bfd->data;
282 struct ipa_proxy_conn *other_conn = NULL;
283 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
284 struct ipaccess_head *hh;
285 int ret;
286
287 /* with UDP sockets, we cannot read partial packets but have to read
288 * all of it in one go */
289 hh = (struct ipaccess_head *) msg->data;
290 ret = recv(bfd->fd, msg->data, msg->data_len, 0);
291 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100292 if (errno != EAGAIN)
293 LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100294 msgb_free(msg);
295 return ret;
296 }
297 if (ret == 0) {
298 DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
299 bsc_unregister_fd(bfd);
300 close(bfd->fd);
301 bfd->fd = -1;
302 msgb_free(msg);
303 return -EIO;
304 }
305 if (ret < sizeof(*hh)) {
306 DEBUGP(DINP, "could not even read header!?!\n");
307 msgb_free(msg);
308 return -EIO;
309 }
310 msgb_put(msg, ret);
311 msg->l2h = msg->data + sizeof(*hh);
312 DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
313
314 if (hh->len != msg->len - sizeof(*hh)) {
315 DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
316 msg->len, msg->len - 3, hh->len);
317 msgb_free(msg);
318 return -EIO;
319 }
320
321 switch (bfd->priv_nr & 0xff) {
322 case UDP_TO_BTS:
323 /* injection towards BTS */
324 switch (hh->proto) {
325 case IPAC_PROTO_RSL:
326 /* FIXME: what to do about TRX > 0 */
327 other_conn = ipbc->rsl_conn[0];
328 break;
329 default:
330 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
331 "OML FD\n", hh->proto);
332 /* fall through */
333 case IPAC_PROTO_IPACCESS:
334 case IPAC_PROTO_OML:
335 other_conn = ipbc->oml_conn;
336 break;
337 }
338 break;
339 case UDP_TO_BSC:
340 /* injection towards BSC */
341 switch (hh->proto) {
342 case IPAC_PROTO_RSL:
343 /* FIXME: what to do about TRX > 0 */
344 other_conn = ipbc->bsc_rsl_conn[0];
345 break;
346 default:
347 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
348 "OML FD\n", hh->proto);
349 case IPAC_PROTO_IPACCESS:
350 case IPAC_PROTO_OML:
351 other_conn = ipbc->bsc_oml_conn;
352 break;
353 }
354 break;
355 default:
356 DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
357 break;
358 }
359
360 if (other_conn) {
361 /* enqueue the message for TX on the respective FD */
362 msgb_enqueue(&other_conn->tx_queue, msg);
363 other_conn->fd.when |= BSC_FD_WRITE;
364 } else
365 msgb_free(msg);
366
367 return 0;
368}
369
370static int handle_udp_write(struct bsc_fd *bfd)
371{
372 /* not implemented yet */
373 bfd->when &= ~BSC_FD_WRITE;
374
375 return -EIO;
376}
377
378/* callback from select.c in case one of the fd's can be read/written */
379static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
380{
381 int rc = 0;
382
383 if (what & BSC_FD_READ)
384 rc = handle_udp_read(bfd);
385 if (what & BSC_FD_WRITE)
386 rc = handle_udp_write(bfd);
387
388 return rc;
389}
390
391
392static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
393 u_int16_t site_id, u_int16_t bts_id,
394 u_int16_t trx_id, struct tlv_parsed *tlvp,
395 struct msgb *msg)
396{
397 struct ipa_bts_conn *ipbc;
398 u_int16_t udp_port;
399 int ret = 0;
400 struct sockaddr_in sin;
401
402 memset(&sin, 0, sizeof(sin));
403 sin.sin_family = AF_INET;
404 inet_aton(bsc_ipaddr, &sin.sin_addr);
405
406 DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
407 site_id, bts_id, trx_id);
408
409 /* OML needs to be established before RSL */
410 if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
411 DEBUGPC(DINP, "Not a OML connection ?!?\n");
412 return -EIO;
413 }
414
415 /* allocate new BTS connection data structure */
416 ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
417 if (!ipbc) {
418 ret = -ENOMEM;
419 goto err_out;
420 }
421
422 DEBUGPC(DINP, "Created BTS Conn data structure\n");
423 ipbc->ipp = ipp;
424 ipbc->unit_id.site_id = site_id;
425 ipbc->unit_id.bts_id = bts_id;
426 ipbc->oml_conn = ipc;
427 ipc->bts_conn = ipbc;
428
429 /* store the content of the ID TAGS for later reference */
430 store_idtags(ipbc, tlvp);
431 ipbc->id_resp_len = msg->len;
432 ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
433 memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
434
435 /* Create OML TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100436 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100437 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
438 if (!ipbc->bsc_oml_conn) {
439 ret = -EIO;
440 goto err_bsc_conn;
441 }
442
443 DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
444 site_id, bts_id, trx_id);
445
446 /* Create UDP socket for BTS packet injection */
447 udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
Pablo Neira Ayusoda2f7692011-04-05 18:33:28 +0200448 ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
Harald Welte2ca7c312009-12-23 22:44:04 +0100449 UDP_TO_BTS, udp_fd_cb, ipbc);
450 if (ret < 0)
451 goto err_udp_bts;
452 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
453 "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
454
455 /* Create UDP socket for BSC packet injection */
456 udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
Pablo Neira Ayusoda2f7692011-04-05 18:33:28 +0200457 ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
Harald Welte2ca7c312009-12-23 22:44:04 +0100458 UDP_TO_BSC, udp_fd_cb, ipbc);
459 if (ret < 0)
460 goto err_udp_bsc;
461 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
462 "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +0800463
464
465 /* GPRS NS related code */
466 if (gprs_ns_ipaddr) {
467 struct sockaddr_in sock;
468 socklen_t len = sizeof(sock);
469 ret = make_gprs_sock(&ipbc->gprs_ns_fd, gprs_ns_cb, ipbc);
470 if (ret < 0) {
471 LOGP(DINP, LOGL_ERROR, "Creating the GPRS socket failed.\n");
472 goto err_udp_bsc;
473 }
474
475 ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len);
476 ipbc->gprs_local_port = ntohs(sock.sin_port);
477 LOGP(DINP, LOGL_NOTICE,
478 "Created GPRS NS Socket. Listening on: %s:%d\n",
479 inet_ntoa(sock.sin_addr), ipbc->gprs_local_port);
480
481 ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len);
482 ipbc->bts_addr = sock.sin_addr;
483 }
484
Harald Welte2ca7c312009-12-23 22:44:04 +0100485 llist_add(&ipbc->list, &ipp->bts_list);
486
487 return 0;
488
489err_udp_bsc:
490 bsc_unregister_fd(&ipbc->udp_bts_fd);
491err_udp_bts:
492 bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
493 close(ipbc->bsc_oml_conn->fd.fd);
494 talloc_free(ipbc->bsc_oml_conn);
495 ipbc->bsc_oml_conn = NULL;
496err_bsc_conn:
497 talloc_free(ipbc->id_resp);
498 talloc_free(ipbc);
499#if 0
500 bsc_unregister_fd(bfd);
501 close(bfd->fd);
502 talloc_free(bfd);
503#endif
504err_out:
505 return ret;
506}
507
508static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
509 struct bsc_fd *bfd)
510{
511 struct tlv_parsed tlvp;
512 u_int8_t msg_type = *(msg->l2h);
513 u_int16_t site_id, bts_id, trx_id;
514 struct ipa_bts_conn *ipbc;
515 int ret = 0;
516
517 switch (msg_type) {
518 case IPAC_MSGT_PING:
Pablo Neira Ayuso22f58a92011-04-07 14:15:06 +0200519 ret = ipaccess_send_pong(bfd->fd);
Harald Welte2ca7c312009-12-23 22:44:04 +0100520 break;
521 case IPAC_MSGT_PONG:
522 DEBUGP(DMI, "PONG!\n");
523 break;
524 case IPAC_MSGT_ID_RESP:
525 DEBUGP(DMI, "ID_RESP ");
526 /* parse tags, search for Unit ID */
527 ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
528 msgb_l2len(msg)-2);
529 DEBUGP(DMI, "\n");
530
531 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
532 LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
533 return -EIO;
534 }
535
536 /* lookup BTS, create sign_link, ... */
Holger Hans Peter Freyther82ae7162010-03-30 15:20:46 +0200537 site_id = bts_id = trx_id = 0;
Harald Welte2ca7c312009-12-23 22:44:04 +0100538 parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
539 &site_id, &bts_id, &trx_id);
540 ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
541 if (!ipbc) {
542 /* We have not found an ipbc (per-bts proxy instance)
543 * for this BTS yet. The first connection of a new BTS must
544 * be a OML connection. We allocate the associated data structures,
545 * and try to connect to the remote end */
546
547 return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
548 trx_id, &tlvp, msg);
549 /* if this fails, the caller will clean up bfd */
550 } else {
551 struct sockaddr_in sin;
552 memset(&sin, 0, sizeof(sin));
553 sin.sin_family = AF_INET;
554 inet_aton(bsc_ipaddr, &sin.sin_addr);
555
556 DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
557 site_id, bts_id, trx_id);
558
559 if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
560 LOGP(DINP, LOGL_ERROR, "Second OML connection from "
561 "same BTS ?!?\n");
562 return 0;
563 }
564
565 if (trx_id > MAX_TRX) {
566 LOGP(DINP, LOGL_ERROR, "We don't support more "
567 "than %u TRX\n", MAX_TRX);
568 return -EINVAL;
569 }
570
571 ipc->bts_conn = ipbc;
572 /* store TRX number in higher 8 bit of the bfd private number */
573 bfd->priv_nr |= trx_id << 8;
574 ipbc->rsl_conn[trx_id] = ipc;
575
576 /* Create RSL TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100577 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100578 ipbc->bsc_rsl_conn[trx_id] =
579 connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
580 if (!ipbc->bsc_oml_conn)
581 return -EIO;
582 DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
583 site_id, bts_id, trx_id);
584 }
585 break;
586 case IPAC_MSGT_ID_GET:
587 DEBUGP(DMI, "ID_GET\n");
588 if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
589 (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
590 DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
591 return -EIO;
592 }
593 ipbc = ipc->bts_conn;
594 if (!ipbc) {
595 DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
596 return -EIO;
597 }
598 ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
599 break;
600 case IPAC_MSGT_ID_ACK:
601 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
Pablo Neira Ayuso22f58a92011-04-07 14:15:06 +0200602 ret = ipaccess_send_id_ack(bfd->fd);
Harald Welte2ca7c312009-12-23 22:44:04 +0100603 break;
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800604 default:
605 LOGP(DMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type);
606 return 1;
607 break;
Harald Welte2ca7c312009-12-23 22:44:04 +0100608 }
609 return 0;
610}
611
Pablo Neira Ayuso22f58a92011-04-07 14:15:06 +0200612struct msgb *ipaccess_proxy_read_msg(struct bsc_fd *bfd, int *error)
Harald Welte2ca7c312009-12-23 22:44:04 +0100613{
614 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
615 struct ipaccess_head *hh;
616 int len, ret = 0;
617
618 if (!msg) {
619 *error = -ENOMEM;
620 return NULL;
621 }
622
623 /* first read our 3-byte header */
624 hh = (struct ipaccess_head *) msg->data;
625 ret = recv(bfd->fd, msg->data, 3, 0);
626 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100627 if (errno != EAGAIN)
Harald Welteda956932009-12-24 12:49:43 +0100628 LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100629 msgb_free(msg);
630 *error = ret;
631 return NULL;
632 } else if (ret == 0) {
633 msgb_free(msg);
634 *error = ret;
635 return NULL;
636 }
637
638 msgb_put(msg, ret);
639
640 /* then read te length as specified in header */
641 msg->l2h = msg->data + sizeof(*hh);
642 len = ntohs(hh->len);
643 ret = recv(bfd->fd, msg->l2h, len, 0);
644 if (ret < len) {
645 LOGP(DINP, LOGL_ERROR, "short read!\n");
646 msgb_free(msg);
647 *error = -EIO;
648 return NULL;
649 }
650 msgb_put(msg, ret);
651
652 return msg;
653}
654
655static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
656 unsigned int priv_nr)
657{
658 struct ipa_proxy_conn *bsc_conn;
659 unsigned int trx_id = priv_nr >> 8;
660
661 switch (priv_nr & 0xff) {
662 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
663 bsc_conn = ipbc->bsc_oml_conn;
664 break;
665 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
666 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
667 break;
668 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
669 bsc_conn = ipbc->oml_conn;
670 break;
671 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
672 bsc_conn = ipbc->rsl_conn[trx_id];
673 break;
674 default:
675 bsc_conn = NULL;
676 break;
677 }
678 return bsc_conn;
679}
680
681static void reconn_tmr_cb(void *data)
682{
683 struct ipa_proxy *ipp = data;
684 struct ipa_bts_conn *ipbc;
685 struct sockaddr_in sin;
686 int i;
687
688 DEBUGP(DINP, "Running reconnect timer\n");
689
690 memset(&sin, 0, sizeof(sin));
691 sin.sin_family = AF_INET;
692 inet_aton(bsc_ipaddr, &sin.sin_addr);
693
694 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
695 /* if OML to BSC is dead, try to restore it */
696 if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
Harald Welte87ed5cd2009-12-23 22:47:53 +0100697 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100698 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
699 LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
700 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
701 if (!ipbc->bsc_oml_conn)
702 goto reschedule;
703 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
704 LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
705 }
706 /* if we (still) don't have a OML connection, skip RSL */
707 if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
708 continue;
709
710 for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
711 unsigned int priv_nr;
712 /* don't establish RSL links which we don't have */
713 if (!ipbc->rsl_conn[i])
714 continue;
715 if (ipbc->bsc_rsl_conn[i])
716 continue;
717 priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
718 priv_nr &= ~0xff;
719 priv_nr |= RSL_TO_BSC;
Harald Welte87ed5cd2009-12-23 22:47:53 +0100720 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100721 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
722 LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
723 ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
724 if (!ipbc->bsc_rsl_conn)
725 goto reschedule;
726 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
727 LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
728 }
729 }
730 return;
731
732reschedule:
733 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
734}
735
736static void handle_dead_socket(struct bsc_fd *bfd)
737{
738 struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
739 struct ipa_proxy_conn *bsc_conn; /* remote conn */
740 struct ipa_bts_conn *ipbc = ipc->bts_conn;
741 unsigned int trx_id = bfd->priv_nr >> 8;
742 struct msgb *msg, *msg2;
743
744 bsc_unregister_fd(bfd);
745 close(bfd->fd);
746 bfd->fd = -1;
747
748 /* FIXME: clear tx_queue, remove all references, etc. */
749 llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
750 msgb_free(msg);
751
752 switch (bfd->priv_nr & 0xff) {
753 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
Pablo Neira Ayuso3c409c22011-03-27 21:31:48 +0200754 /* The BTS started a connection with us but we got no
755 * IPAC_MSGT_ID_RESP message yet, in that scenario we did not
756 * allocate the ipa_bts_conn structure. */
757 if (ipbc == NULL)
758 break;
Harald Welte2ca7c312009-12-23 22:44:04 +0100759 ipbc->oml_conn = NULL;
760 bsc_conn = ipbc->bsc_oml_conn;
761 /* close the connection to the BSC */
762 bsc_unregister_fd(&bsc_conn->fd);
763 close(bsc_conn->fd.fd);
764 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
765 msgb_free(msg);
766 talloc_free(bsc_conn);
767 ipbc->bsc_oml_conn = NULL;
768 /* FIXME: do we need to delete the entire ipbc ? */
769 break;
770 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
771 ipbc->rsl_conn[trx_id] = NULL;
772 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
773 /* close the connection to the BSC */
774 bsc_unregister_fd(&bsc_conn->fd);
775 close(bsc_conn->fd.fd);
776 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
777 msgb_free(msg);
778 talloc_free(bsc_conn);
779 ipbc->bsc_rsl_conn[trx_id] = NULL;
780 break;
781 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
782 ipbc->bsc_oml_conn = NULL;
783 bsc_conn = ipbc->oml_conn;
784 /* start reconnect timer */
785 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
786 break;
787 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
788 ipbc->bsc_rsl_conn[trx_id] = NULL;
789 bsc_conn = ipbc->rsl_conn[trx_id];
790 /* start reconnect timer */
791 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
792 break;
793 default:
794 bsc_conn = NULL;
795 break;
796 }
797
798 talloc_free(ipc);
799}
800
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800801static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg)
802{
803 uint8_t *nsvci;
804
805 if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC)
806 return;
807
808 if (msgb_l2len(msg) != 39)
809 return;
810
811 /*
812 * Check if this is a IPA Set Attribute or IPA Set Attribute ACK
813 * and if the FOM Class is GPRS NSVC0 and then we will patch it.
814 *
815 * The patch assumes the message looks like the one from the trace
816 * but we only match messages with a specific size anyway... So
817 * this hack should work just fine.
818 */
819
820 if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
821 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
822 msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800823 nsvci = &msg->l2h[23];
824 ipbc->gprs_orig_port = *(u_int16_t *)(nsvci+8);
825 ipbc->gprs_orig_ip = *(u_int32_t *)(nsvci+10);
826 *(u_int16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
827 *(u_int32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
828 } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
829 msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
830 msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800831 nsvci = &msg->l2h[23];
832 *(u_int16_t *)(nsvci+8) = ipbc->gprs_orig_port;
833 *(u_int32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
834 }
835}
836
Harald Welte2ca7c312009-12-23 22:44:04 +0100837static int handle_tcp_read(struct bsc_fd *bfd)
838{
839 struct ipa_proxy_conn *ipc = bfd->data;
840 struct ipa_bts_conn *ipbc = ipc->bts_conn;
841 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100842 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100843 struct ipaccess_head *hh;
844 int ret = 0;
845 char *btsbsc;
846
Harald Welte2ca7c312009-12-23 22:44:04 +0100847 if ((bfd->priv_nr & 0xff) <= 2)
848 btsbsc = "BTS";
849 else
850 btsbsc = "BSC";
851
Pablo Neira Ayuso22f58a92011-04-07 14:15:06 +0200852 msg = ipaccess_proxy_read_msg(bfd, &ret);
Harald Welte2ca7c312009-12-23 22:44:04 +0100853 if (!msg) {
854 if (ret == 0) {
855 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
856 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
857 "dead socket\n", btsbsc);
858 handle_dead_socket(bfd);
859 }
860 return ret;
861 }
862
863 msgb_put(msg, ret);
864 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
865 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
866
867 hh = (struct ipaccess_head *) msg->data;
868 if (hh->proto == IPAC_PROTO_IPACCESS) {
869 ret = ipaccess_rcvmsg(ipc, msg, bfd);
870 if (ret < 0) {
871 bsc_unregister_fd(bfd);
872 close(bfd->fd);
873 bfd->fd = -1;
874 talloc_free(bfd);
Holger Hans Peter Freyther0897dad2010-06-09 17:38:59 +0800875 msgb_free(msg);
876 return ret;
877 } else if (ret == 0) {
878 /* we do not forward parts of the CCM protocol
879 * through the proxy but rather terminate it ourselves. */
880 msgb_free(msg);
881 return ret;
Harald Welte2ca7c312009-12-23 22:44:04 +0100882 }
Harald Welte2ca7c312009-12-23 22:44:04 +0100883 }
884
885 if (!ipbc) {
886 LOGP(DINP, LOGL_ERROR,
887 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
888 msgb_free(msg);
889 return -EIO;
890 }
891
892 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
893 if (bsc_conn) {
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +0800894 if (gprs_ns_ipaddr)
895 patch_gprs_msg(ipbc, bfd->priv_nr, msg);
Harald Welte2ca7c312009-12-23 22:44:04 +0100896 /* enqueue packet towards BSC */
897 msgb_enqueue(&bsc_conn->tx_queue, msg);
898 /* mark respective filedescriptor as 'we want to write' */
899 bsc_conn->fd.when |= BSC_FD_WRITE;
900 } else {
901 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
902 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
903 "since remote connection is dead\n", btsbsc);
904 msgb_free(msg);
905 }
906
907 return ret;
908}
909
910/* a TCP socket is ready to be written to */
911static int handle_tcp_write(struct bsc_fd *bfd)
912{
913 struct ipa_proxy_conn *ipc = bfd->data;
914 struct ipa_bts_conn *ipbc = ipc->bts_conn;
915 struct llist_head *lh;
916 struct msgb *msg;
917 char *btsbsc;
918 int ret;
919
920 if ((bfd->priv_nr & 0xff) <= 2)
921 btsbsc = "BTS";
922 else
923 btsbsc = "BSC";
924
925
926 /* get the next msg for this timeslot */
927 if (llist_empty(&ipc->tx_queue)) {
928 bfd->when &= ~BSC_FD_WRITE;
929 return 0;
930 }
931 lh = ipc->tx_queue.next;
932 llist_del(lh);
933 msg = llist_entry(lh, struct msgb, list);
934
935 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
936 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
937 hexdump(msg->data, msg->len));
938
939 ret = send(bfd->fd, msg->data, msg->len, 0);
940 msgb_free(msg);
941
942 if (ret == 0) {
943 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
944 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
945 handle_dead_socket(bfd);
946 }
947
948 return ret;
949}
950
951/* callback from select.c in case one of the fd's can be read/written */
952static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
953{
954 int rc = 0;
955
956 if (what & BSC_FD_READ) {
957 rc = handle_tcp_read(bfd);
958 if (rc < 0)
959 return rc;
960 }
961 if (what & BSC_FD_WRITE)
962 rc = handle_tcp_write(bfd);
963
964 return rc;
965}
966
967/* callback of the listening filedescriptor */
968static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
969{
970 int ret;
971 struct ipa_proxy_conn *ipc;
972 struct bsc_fd *bfd;
973 struct sockaddr_in sa;
974 socklen_t sa_len = sizeof(sa);
975
976 if (!(what & BSC_FD_READ))
977 return 0;
978
979 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
980 if (ret < 0) {
981 perror("accept");
982 return ret;
983 }
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200984 DEBUGP(DINP, "accept()ed new %s link from %s\n",
Harald Welte2ca7c312009-12-23 22:44:04 +0100985 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
986 inet_ntoa(sa.sin_addr));
987
988 ipc = alloc_conn();
989 if (!ipc) {
990 close(ret);
991 return -ENOMEM;
992 }
993
994 bfd = &ipc->fd;
995 bfd->fd = ret;
996 bfd->data = ipc;
997 bfd->priv_nr = listen_bfd->priv_nr;
998 bfd->cb = ipaccess_fd_cb;
999 bfd->when = BSC_FD_READ;
1000 ret = bsc_register_fd(bfd);
1001 if (ret < 0) {
1002 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
1003 close(bfd->fd);
1004 talloc_free(ipc);
1005 return ret;
1006 }
1007
1008 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
Pablo Neira Ayuso22f58a92011-04-07 14:15:06 +02001009 ret = ipaccess_send_id_req(bfd->fd);
Harald Welte2ca7c312009-12-23 22:44:04 +01001010
1011 return 0;
1012}
1013
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001014static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
1015{
1016 int ret;
1017 struct sockaddr_in addr;
1018 socklen_t len = sizeof(addr);
1019 memset(&addr, 0, sizeof(addr));
1020
1021 addr.sin_family = AF_INET;
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001022 addr.sin_port = htons(port);
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001023 addr.sin_addr = ip;
1024
1025 ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
1026 if (ret < 0) {
1027 LOGP(DINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
1028 }
1029}
1030
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001031static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what)
1032{
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001033 struct ipa_bts_conn *bts;
1034 char buf[4096];
1035 int ret;
1036 struct sockaddr_in sock;
1037 socklen_t len = sizeof(sock);
1038
1039 /* 1. get the data... */
1040 ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
1041 if (ret < 0) {
1042 LOGP(DINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
1043 return -1;
1044 }
1045
1046 bts = bfd->data;
1047
1048 /* 2. figure out where to send it to */
1049 if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
1050 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
1051 send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
1052 } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
1053 LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
1054 send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
Holger Hans Peter Freyther21e1c0d2010-06-10 15:56:26 +08001055 } else {
1056 LOGP(DINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001057 }
1058
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001059 return 0;
1060}
1061
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001062static 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 +08001063{
1064 struct sockaddr_in addr;
1065 int ret;
1066
1067 bfd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1068 bfd->cb = cb;
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001069 bfd->data = data;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001070 bfd->when = BSC_FD_READ;
1071
1072 memset(&addr, 0, sizeof(addr));
1073 addr.sin_family = AF_INET;
1074 addr.sin_port = 0;
1075 inet_aton(listen_ipaddr, &addr.sin_addr);
1076
1077 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1078 if (ret < 0) {
1079 LOGP(DINP, LOGL_ERROR,
1080 "Could not bind n socket for IP %s with error: %s.\n",
1081 listen_ipaddr, strerror(errno));
1082 return -EIO;
1083 }
1084
1085 ret = bsc_register_fd(bfd);
1086 if (ret < 0) {
1087 perror("register_listen_fd");
1088 return ret;
1089 }
1090 return 0;
1091}
1092
Harald Welte2ca7c312009-12-23 22:44:04 +01001093/* Actively connect to a BSC. */
1094static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1095{
1096 struct ipa_proxy_conn *ipc;
1097 struct bsc_fd *bfd;
1098 int ret, on = 1;
1099
1100 ipc = alloc_conn();
1101 if (!ipc)
1102 return NULL;
1103
1104 ipc->bts_conn = data;
1105
1106 bfd = &ipc->fd;
1107 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1108 bfd->cb = ipaccess_fd_cb;
1109 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1110 bfd->data = ipc;
1111 bfd->priv_nr = priv_nr;
1112
1113 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1114
1115 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1116 if (ret < 0) {
Holger Hans Peter Freyther6cb9b232010-06-09 15:15:11 +08001117 LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n",
1118 inet_ntoa(sa->sin_addr));
Harald Welte2ca7c312009-12-23 22:44:04 +01001119 close(bfd->fd);
1120 talloc_free(ipc);
1121 return NULL;
1122 }
1123
1124 /* pre-fill tx_queue with identity request */
1125 ret = bsc_register_fd(bfd);
1126 if (ret < 0) {
1127 close(bfd->fd);
1128 talloc_free(ipc);
1129 return NULL;
1130 }
1131
1132 return ipc;
1133}
1134
1135static int ipaccess_proxy_setup(void)
1136{
1137 int ret;
1138
1139 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1140 if (!ipp)
1141 return -ENOMEM;
1142 INIT_LLIST_HEAD(&ipp->bts_list);
1143 ipp->reconn_timer.cb = reconn_tmr_cb;
1144 ipp->reconn_timer.data = ipp;
1145
1146 /* Listen for OML connections */
Pablo Neira Ayuso3ab864a2011-04-07 14:15:02 +02001147 ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY,
1148 IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL);
Harald Welte2ca7c312009-12-23 22:44:04 +01001149 if (ret < 0)
1150 return ret;
1151
1152 /* Listen for RSL connections */
Pablo Neira Ayuso3ab864a2011-04-07 14:15:02 +02001153 ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY,
1154 IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL);
Harald Welte2ca7c312009-12-23 22:44:04 +01001155
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001156 if (ret < 0)
1157 return ret;
1158
1159 /* Connect the GPRS NS Socket */
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001160 if (gprs_ns_ipaddr) {
Holger Hans Peter Freyther952aba72010-06-09 17:07:43 +08001161 inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
Holger Hans Peter Freyther4fe22cc2010-06-10 15:57:08 +08001162 inet_aton(listen_ipaddr, &ipp->listen_addr);
1163 }
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001164
Harald Welte2ca7c312009-12-23 22:44:04 +01001165 return ret;
1166}
1167
1168static void signal_handler(int signal)
1169{
1170 fprintf(stdout, "signal %u received\n", signal);
1171
1172 switch (signal) {
1173 case SIGABRT:
1174 /* in case of abort, we want to obtain a talloc report
1175 * and then return to the caller, who will abort the process */
1176 case SIGUSR1:
1177 talloc_report_full(tall_bsc_ctx, stderr);
1178 break;
1179 default:
1180 break;
1181 }
1182}
1183
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001184static void print_help()
1185{
1186 printf(" ipaccess-proxy is a proxy BTS.\n");
1187 printf(" -h --help. This help text.\n");
1188 printf(" -l --listen IP. The ip to listen to.\n");
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001189 printf(" -b --bsc IP. The BSC IP address.\n");
1190 printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
1191 printf("\n");
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001192 printf(" -s --disable-color. Disable the color inside the logging message.\n");
1193 printf(" -e --log-level number. Set the global loglevel.\n");
1194 printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
1195 printf(" -V --version. Print the version of OpenBSC.\n");
1196}
1197
1198static void print_usage()
1199{
1200 printf("Usage: ipaccess-proxy\n");
1201}
1202
1203static void handle_options(int argc, char** argv)
1204{
1205 while (1) {
1206 int option_index = 0, c;
1207 static struct option long_options[] = {
1208 {"help", 0, 0, 'h'},
1209 {"disable-color", 0, 0, 's'},
1210 {"timestamp", 0, 0, 'T'},
1211 {"log-level", 1, 0, 'e'},
1212 {"listen", 1, 0, 'l'},
1213 {"bsc", 1, 0, 'b'},
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001214 {"udp", 1, 0, 'u'},
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001215 {0, 0, 0, 0}
1216 };
1217
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001218 c = getopt_long(argc, argv, "hsTe:l:b:g:",
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001219 long_options, &option_index);
1220 if (c == -1)
1221 break;
1222
1223 switch (c) {
1224 case 'h':
1225 print_usage();
1226 print_help();
1227 exit(0);
1228 case 'l':
1229 listen_ipaddr = optarg;
1230 break;
1231 case 'b':
1232 bsc_ipaddr = optarg;
1233 break;
Holger Hans Peter Freyther6147c5d2010-06-09 16:20:39 +08001234 case 'g':
1235 gprs_ns_ipaddr = optarg;
1236 break;
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001237 case 's':
1238 log_set_use_color(stderr_target, 0);
1239 break;
1240 case 'T':
1241 log_set_print_timestamp(stderr_target, 1);
1242 break;
1243 case 'e':
1244 log_set_log_level(stderr_target, atoi(optarg));
1245 break;
1246 default:
1247 /* ignore */
1248 break;
1249 }
1250 }
1251}
1252
Harald Welte2ca7c312009-12-23 22:44:04 +01001253int main(int argc, char **argv)
1254{
1255 int rc;
1256
1257 listen_ipaddr = "192.168.100.11";
1258 bsc_ipaddr = "192.168.100.239";
1259
1260 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1261
Harald Weltedc5062b2010-03-26 21:28:59 +08001262 log_init(&log_info);
1263 stderr_target = log_target_create_stderr();
1264 log_add_target(stderr_target);
1265 log_set_all_filter(stderr_target, 1);
1266 log_parse_category_mask(stderr_target, "DINP:DMI");
Harald Welte2ca7c312009-12-23 22:44:04 +01001267
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001268 handle_options(argc, argv);
1269
Harald Welte2ca7c312009-12-23 22:44:04 +01001270 rc = ipaccess_proxy_setup();
1271 if (rc < 0)
1272 exit(1);
1273
1274 signal(SIGUSR1, &signal_handler);
1275 signal(SIGABRT, &signal_handler);
1276
1277 while (1) {
1278 bsc_select_main(0);
1279 }
1280}