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