blob: cb901ee92771d45c23c6f6fe38f535a6ccaa6217 [file] [log] [blame]
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +02001/* OpenBSC Abis input driver for ip.access */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2010 by Holger Hans Peter Freyther
5 * (C) 2010 by On-Waves
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * 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/>.
21 *
22 */
23
24#include "internal.h"
25
26#include <stdio.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <string.h>
31#include <time.h>
32#include <sys/fcntl.h>
33#include <sys/socket.h>
34#include <sys/ioctl.h>
35#include <arpa/inet.h>
36
37#include <osmocom/core/select.h>
38#include <osmocom/gsm/tlv.h>
39#include <osmocom/core/msgb.h>
40#include <osmocom/core/logging.h>
41#include <talloc.h>
Pablo Neira Ayuso177094b2011-06-07 12:21:51 +020042#include <osmocom/abis/e1_input.h>
43#include <osmocom/abis/ipaccess.h>
Pablo Neira Ayusof67471f2011-06-07 11:25:49 +020044#include <osmocom/core/socket.h>
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +020045#include <osmocom/abis/ipa.h>
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020046
Pablo Neira Ayuso54b49792011-06-07 12:15:26 +020047static void *tall_ipa_ctx;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020048
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020049#define TS1_ALLOC_SIZE 900
50
51/*
52 * Common propietary IPA messages:
53 * - PONG: in reply to PING.
54 * - ID_REQUEST: first messages once OML has been established.
55 * - ID_ACK: in reply to ID_ACK.
56 */
57const uint8_t ipa_pong_msg[] = {
58 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
59};
60
61const uint8_t ipa_id_ack_msg[] = {
62 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
63};
64
65const uint8_t ipa_id_req_msg[] = {
66 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
67 0x01, IPAC_IDTAG_UNIT,
68 0x01, IPAC_IDTAG_MACADDR,
69 0x01, IPAC_IDTAG_LOCATION1,
70 0x01, IPAC_IDTAG_LOCATION2,
71 0x01, IPAC_IDTAG_EQUIPVERS,
72 0x01, IPAC_IDTAG_SWVERSION,
73 0x01, IPAC_IDTAG_UNITNAME,
74 0x01, IPAC_IDTAG_SERNR,
75};
76
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +020077static const char *idtag_names[] = {
78 [IPAC_IDTAG_SERNR] = "Serial_Number",
79 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
80 [IPAC_IDTAG_LOCATION1] = "Location_1",
81 [IPAC_IDTAG_LOCATION2] = "Location_2",
82 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
83 [IPAC_IDTAG_SWVERSION] = "Software_Version",
84 [IPAC_IDTAG_IPADDR] = "IP_Address",
85 [IPAC_IDTAG_MACADDR] = "MAC_Address",
86 [IPAC_IDTAG_UNIT] = "Unit_ID",
87};
88
89const char *ipaccess_idtag_name(uint8_t tag)
90{
91 if (tag >= ARRAY_SIZE(idtag_names))
92 return "unknown";
93
94 return idtag_names[tag];
95}
96
97int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
98{
99 uint8_t t_len;
100 uint8_t t_tag;
101 uint8_t *cur = buf;
102
103 memset(dec, 0, sizeof(*dec));
104
105 while (len >= 2) {
106 len -= 2;
107 t_len = *cur++;
108 t_tag = *cur++;
109
110 if (t_len > len + 1) {
111 LOGP(DMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
112 return -EINVAL;
113 }
114
115 DEBUGPC(DMI, "%s='%s' ", ipaccess_idtag_name(t_tag), cur);
116
117 dec->lv[t_tag].len = t_len;
118 dec->lv[t_tag].val = cur;
119
120 cur += t_len;
121 len -= t_len;
122 }
123 return 0;
124}
125
126int ipaccess_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
127{
128 unsigned long ul;
129 char *endptr;
130 const char *nptr;
131
132 nptr = str;
133 ul = strtoul(nptr, &endptr, 10);
134 if (endptr <= nptr)
135 return -EINVAL;
Pablo Neira Ayuso62d345a2011-07-05 16:37:37 +0200136 unit_data->site_id = ul & 0xffff;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200137
138 if (*endptr++ != '/')
139 return -EINVAL;
140
141 nptr = endptr;
142 ul = strtoul(nptr, &endptr, 10);
143 if (endptr <= nptr)
144 return -EINVAL;
Pablo Neira Ayuso62d345a2011-07-05 16:37:37 +0200145 unit_data->bts_id = ul & 0xffff;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200146
147 if (*endptr++ != '/')
148 return -EINVAL;
149
150 nptr = endptr;
151 ul = strtoul(nptr, &endptr, 10);
152 if (endptr <= nptr)
153 return -EINVAL;
Pablo Neira Ayuso62d345a2011-07-05 16:37:37 +0200154 unit_data->trx_id = ul & 0xffff;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200155
156 return 0;
157}
158
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200159static int ipaccess_send(int fd, const void *msg, size_t msglen)
160{
161 int ret;
162
163 ret = write(fd, msg, msglen);
164 if (ret < 0)
165 return ret;
166 if (ret < msglen) {
167 LOGP(DINP, LOGL_ERROR, "ipaccess_send: short write\n");
168 return -EIO;
169 }
170 return ret;
171}
172
173int ipaccess_send_pong(int fd)
174{
175 return ipaccess_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
176}
177
178int ipaccess_send_id_ack(int fd)
179{
180 return ipaccess_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
181}
182
183int ipaccess_send_id_req(int fd)
184{
185 return ipaccess_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
186}
187
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200188/* base handling of the ip.access protocol */
189int ipaccess_rcvmsg_base(struct msgb *msg,
190 struct osmo_fd *bfd)
191{
192 uint8_t msg_type = *(msg->l2h);
193 int ret = 0;
194
195 switch (msg_type) {
196 case IPAC_MSGT_PING:
197 ret = ipaccess_send_pong(bfd->fd);
198 break;
199 case IPAC_MSGT_PONG:
200 DEBUGP(DMI, "PONG!\n");
201 break;
202 case IPAC_MSGT_ID_ACK:
203 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
204 ret = ipaccess_send_id_ack(bfd->fd);
205 break;
206 }
207 return 0;
208}
209
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200210/* base handling of the ip.access protocol */
211int ipaccess_rcvmsg_bts_base(struct msgb *msg,
212 struct osmo_fd *bfd)
213{
214 uint8_t msg_type = *(msg->l2h);
215 int ret = 0;
216
217 switch (msg_type) {
218 case IPAC_MSGT_PING:
219 ret = ipaccess_send_pong(bfd->fd);
220 break;
221 case IPAC_MSGT_PONG:
222 DEBUGP(DMI, "PONG!\n");
223 break;
224 case IPAC_MSGT_ID_ACK:
225 DEBUGP(DMI, "ID_ACK\n");
226 break;
227 }
228 return 0;
229}
230
231static void ipaccess_drop(struct osmo_fd *bfd)
232{
233 struct e1inp_line *line = bfd->data;
234
235 /* e1inp_sign_link_destroy releases the socket descriptors for us. */
236 line->ops->sign_link_down(line);
237
238 /* RSL connection without ID_RESP, release temporary socket. */
239 if (line->ts[E1INP_SIGN_OML-1].type == E1INP_SIGN_NONE)
240 talloc_free(bfd);
241}
242
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200243static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
244 struct osmo_fd *bfd)
245{
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200246 struct tlv_parsed tlvp;
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200247 uint8_t msg_type = *(msg->l2h);
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200248 struct ipaccess_unit unit_data = {};
249 struct e1inp_sign_link *sign_link;
250 char *unitid;
251 int len, ret;
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200252
253 /* handle base messages */
254 ipaccess_rcvmsg_base(msg, bfd);
255
256 switch (msg_type) {
257 case IPAC_MSGT_ID_RESP:
258 DEBUGP(DMI, "ID_RESP\n");
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200259 /* parse tags, search for Unit ID */
260 ret = ipaccess_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2,
261 msgb_l2len(msg)-2);
262 DEBUGP(DMI, "\n");
263 if (ret < 0) {
264 LOGP(DINP, LOGL_ERROR, "ignoring IPA response message "
265 "with malformed TLVs\n");
266 return ret;
267 }
268 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT))
269 break;
270
271 len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT);
272 if (len < 1)
273 break;
274
275 unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT);
276 unitid[len - 1] = '\0';
277 ipaccess_parse_unitid(unitid, &unit_data);
278
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200279 if (!line->ops->sign_link_up) {
Pablo Neira Ayusocd8d2e52011-07-02 17:05:21 +0200280 LOGP(DINP, LOGL_ERROR,
281 "Unable to set signal link, closing socket.\n");
282 osmo_fd_unregister(bfd);
283 close(bfd->fd);
284 bfd->fd = -1;
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200285 return -EINVAL;
286 }
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200287 /* the BSC creates the new sign links at this stage. */
288 if (bfd->priv_nr == E1INP_SIGN_OML) {
289 sign_link =
290 line->ops->sign_link_up(&unit_data, line,
291 E1INP_SIGN_OML);
292 if (sign_link == NULL) {
293 LOGP(DINP, LOGL_ERROR,
Pablo Neira Ayusocd8d2e52011-07-02 17:05:21 +0200294 "Unable to set signal link, "
295 "closing socket.\n");
296 osmo_fd_unregister(bfd);
297 close(bfd->fd);
298 bfd->fd = -1;
299 return -EINVAL;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200300 }
301 } else if (bfd->priv_nr == E1INP_SIGN_RSL) {
302 struct e1inp_ts *e1i_ts;
303 struct osmo_fd *newbfd;
304
305 sign_link =
306 line->ops->sign_link_up(&unit_data, line,
307 E1INP_SIGN_RSL);
308 if (sign_link == NULL) {
Pablo Neira Ayusocd8d2e52011-07-02 17:05:21 +0200309 LOGP(DINP, LOGL_ERROR,
310 "Unable to set signal link, "
311 "closing socket.\n");
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200312 osmo_fd_unregister(bfd);
313 close(bfd->fd);
314 bfd->fd = -1;
315 talloc_free(bfd);
316 return 0;
317 }
318 /* Finally, we know which OML link is associated with
319 * this RSL link, attach it to this socket. */
320 bfd->data = line = sign_link->ts->line;
321 e1i_ts = &line->ts[E1INP_SIGN_RSL+unit_data.trx_id-1];
322 newbfd = &e1i_ts->driver.ipaccess.fd;
323
324 /* get rid of our old temporary bfd */
325 memcpy(newbfd, bfd, sizeof(*newbfd));
326 newbfd->priv_nr = E1INP_SIGN_RSL + unit_data.trx_id;
327 osmo_fd_unregister(bfd);
328 bfd->fd = -1;
329 talloc_free(bfd);
330 osmo_fd_register(newbfd);
331 }
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200332 break;
333 }
334 return 0;
335}
336
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200337static int handle_ts1_read(struct osmo_fd *bfd)
338{
339 struct e1inp_line *line = bfd->data;
340 unsigned int ts_nr = bfd->priv_nr;
Pablo Neira Ayuso29465d32011-06-21 14:21:33 +0200341 struct e1inp_ts *e1i_ts = NULL;
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200342 struct e1inp_sign_link *link;
343 struct ipaccess_head *hh;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200344 struct msgb *msg;
345 int ret = 0, error;
346
Pablo Neira Ayuso7a249402011-06-21 14:15:46 +0200347 error = ipa_msg_recv(bfd->fd, &msg);
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200348 if (error < 0)
349 return error;
350 else if (error == 0) {
351 ipaccess_drop(bfd);
352 LOGP(DINP, LOGL_NOTICE, "Sign link vanished, dead socket\n");
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200353 return error;
354 }
355 DEBUGP(DMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
356
Pablo Neira Ayuso29465d32011-06-21 14:21:33 +0200357 /* Now that we're sure that we've got a line for socket. */
358 e1i_ts = &line->ts[ts_nr-1];
359
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200360 hh = (struct ipaccess_head *) msg->data;
361 if (hh->proto == IPAC_PROTO_IPACCESS) {
362 ipaccess_rcvmsg(line, msg, bfd);
363 msgb_free(msg);
364 return ret;
365 }
366 /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
367 * might have free'd it !!! */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200368
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200369 link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
370 if (!link) {
371 LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
372 "hh->proto=0x%02x\n", hh->proto);
373 msgb_free(msg);
374 return -EIO;
375 }
376 msg->dst = link;
377
378 /* XXX better use e1inp_ts_rx? */
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200379 if (!e1i_ts->line->ops->sign_link) {
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200380 LOGP(DINP, LOGL_ERROR, "Fix your application, "
381 "no action set for signalling messages.\n");
382 return -ENOENT;
383 }
Pablo Neira Ayusodbd82fb2011-07-05 15:29:23 +0200384 e1i_ts->line->ops->sign_link(msg);
Pablo Neira Ayuso5a4b7c52011-06-07 14:07:48 +0200385
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200386 return ret;
387}
388
389void ipaccess_prepend_header_ext(struct msgb *msg, int proto)
390{
391 struct ipaccess_head_ext *hh_ext;
392
393 /* prepend the osmo ip.access header extension */
394 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
395 hh_ext->proto = proto;
396}
397
398void ipaccess_prepend_header(struct msgb *msg, int proto)
399{
400 struct ipaccess_head *hh;
401
402 /* prepend the ip.access header */
403 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
404 hh->len = htons(msg->len - sizeof(*hh));
405 hh->proto = proto;
406}
407
408static int ts_want_write(struct e1inp_ts *e1i_ts)
409{
410 e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE;
411
412 return 0;
413}
414
Pablo Neira Ayusoadd3ec82011-07-05 14:45:46 +0200415static void ipaccess_close(struct e1inp_sign_link *sign_link)
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200416{
Pablo Neira Ayusoadd3ec82011-07-05 14:45:46 +0200417 struct e1inp_ts *e1i_ts = sign_link->ts;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200418 struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd;
Pablo Neira Ayusoadd3ec82011-07-05 14:45:46 +0200419 e1inp_event(e1i_ts, S_INP_TEI_DN, sign_link->tei, sign_link->sapi);
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200420 osmo_fd_unregister(bfd);
421 close(bfd->fd);
422 bfd->fd = -1;
423}
424
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200425static void timeout_ts1_write(void *data)
426{
427 struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
428
429 /* trigger write of ts1, due to tx delay timer */
430 ts_want_write(e1i_ts);
431}
432
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200433static int __handle_ts1_write(struct osmo_fd *bfd, struct e1inp_line *line)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200434{
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200435 unsigned int ts_nr = bfd->priv_nr;
436 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
437 struct e1inp_sign_link *sign_link;
438 struct msgb *msg;
439 uint8_t proto;
440 int ret;
441
442 bfd->when &= ~BSC_FD_WRITE;
443
444 /* get the next msg for this timeslot */
445 msg = e1inp_tx_ts(e1i_ts, &sign_link);
446 if (!msg) {
447 /* no message after tx delay timer */
448 return 0;
449 }
450
451 switch (sign_link->type) {
452 case E1INP_SIGN_OML:
453 proto = IPAC_PROTO_OML;
454 break;
455 case E1INP_SIGN_RSL:
456 proto = IPAC_PROTO_RSL;
457 break;
458 default:
459 msgb_free(msg);
460 bfd->when |= BSC_FD_WRITE; /* come back for more msg */
461 return -EINVAL;
462 }
463
464 msg->l2h = msg->data;
Pablo Neira Ayusob9ed7e32011-07-05 18:31:59 +0200465 ipaccess_prepend_header(msg, sign_link->tei);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200466
467 DEBUGP(DMI, "TX %u: %s\n", ts_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
468
469 ret = send(bfd->fd, msg->data, msg->len, 0);
470 msgb_free(msg);
471
472 /* set tx delay timer for next event */
473 e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
474 e1i_ts->sign.tx_timer.data = e1i_ts;
475
476 /* Reducing this might break the nanoBTS 900 init. */
477 osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
478
479 return ret;
480}
481
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200482int handle_ts1_write(struct osmo_fd *bfd)
483{
484 struct e1inp_line *line = bfd->data;
485
486 return __handle_ts1_write(bfd, line);
487}
488
489int ipaccess_bts_write_cb(struct ipa_client_link *link)
490{
491 struct e1inp_line *line = link->line;
492
493 return __handle_ts1_write(link->ofd, line);
494}
495
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200496/* callback from select.c in case one of the fd's can be read/written */
497static int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what)
498{
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200499 int rc = 0;
500
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200501 if (what & BSC_FD_READ)
502 rc = handle_ts1_read(bfd);
503 if (what & BSC_FD_WRITE)
504 rc = handle_ts1_write(bfd);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200505
506 return rc;
507}
508
Pablo Neira Ayusoc00ee732011-06-21 12:22:49 +0200509static int ipaccess_line_update(struct e1inp_line *line,
510 enum e1inp_line_role role, const char *addr);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200511
512struct e1inp_driver ipaccess_driver = {
513 .name = "ipa",
514 .want_write = ts_want_write,
515 .line_update = ipaccess_line_update,
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200516 .close = ipaccess_close,
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200517 .default_delay = 0,
518};
519
520/* callback of the OML listening filedescriptor */
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200521static int ipaccess_bsc_oml_cb(struct ipa_server_link *link, int fd)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200522{
523 int ret;
524 int idx = 0;
525 int i;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200526 struct e1inp_line *line;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200527 struct e1inp_ts *e1i_ts;
528 struct osmo_fd *bfd;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200529
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200530 /* clone virtual E1 line for this new OML link. */
531 line = talloc_zero(tall_ipa_ctx, struct e1inp_line);
532 if (line == NULL) {
533 LOGP(DINP, LOGL_ERROR, "could not clone E1 line\n");
534 return -1;
535 }
536 memcpy(line, link->line, sizeof(struct e1inp_line));
537
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200538 /* create virrtual E1 timeslots for signalling */
Pablo Neira Ayuso59301852011-06-27 15:44:23 +0200539 e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200540
541 /* initialize the fds */
542 for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
543 line->ts[i].driver.ipaccess.fd.fd = -1;
544
545 e1i_ts = &line->ts[idx];
546
Pablo Neira Ayuso93c62012011-06-26 19:12:47 +0200547 bfd = &e1i_ts->driver.ipaccess.fd;
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200548 bfd->fd = fd;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200549 bfd->data = line;
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200550 bfd->priv_nr = E1INP_SIGN_OML;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200551 bfd->cb = ipaccess_fd_cb;
552 bfd->when = BSC_FD_READ;
553 ret = osmo_fd_register(bfd);
554 if (ret < 0) {
555 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
556 close(bfd->fd);
557 return ret;
558 }
559
560 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
561 ret = ipaccess_send_id_req(bfd->fd);
562
563 return ret;
564}
565
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200566static int ipaccess_bsc_rsl_cb(struct ipa_server_link *link, int fd)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200567{
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200568 struct osmo_fd *bfd;
569 int ret;
570
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200571 /* We don't know yet which OML link to associate it with. Thus, we
572 * allocate a temporary bfd until we have received ID. */
Pablo Neira Ayuso54b49792011-06-07 12:15:26 +0200573 bfd = talloc_zero(tall_ipa_ctx, struct osmo_fd);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200574 if (!bfd)
575 return -ENOMEM;
576
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200577 bfd->fd = fd;
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200578 /* This dummy line is replaced once we can attach it to the OML link */
579 bfd->data = link->line;
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200580 bfd->priv_nr = E1INP_SIGN_RSL;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200581 bfd->cb = ipaccess_fd_cb;
582 bfd->when = BSC_FD_READ;
583 ret = osmo_fd_register(bfd);
584 if (ret < 0) {
585 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
586 close(bfd->fd);
587 talloc_free(bfd);
588 return ret;
589 }
590 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
591 ret = ipaccess_send_id_req(bfd->fd);
592
593 return 0;
594}
595
Pablo Neira Ayusodfafe682011-07-02 14:32:32 +0200596static struct msgb *abis_msgb_alloc(int headroom)
597{
598 struct msgb *nmsg;
599
600 headroom += sizeof(struct ipaccess_head);
601
602 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "dummy BTS");
603 if (!nmsg)
604 return NULL;
605 return nmsg;
606}
607
608static void abis_push_ipa(struct msgb *msg, uint8_t proto)
609{
610 struct ipaccess_head *nhh;
611
612 msg->l2h = msg->data;
613 nhh = (struct ipaccess_head *) msgb_push(msg, sizeof(*nhh));
614 nhh->proto = proto;
615 nhh->len = htons(msgb_l2len(msg));
616}
617
618static struct msgb *
619ipa_bts_id_resp(struct ipaccess_unit *dev, uint8_t *data, int len)
620{
621 struct msgb *nmsg;
622 char str[64];
623 uint8_t *tag;
624
625 nmsg = abis_msgb_alloc(0);
626 if (!nmsg)
627 return NULL;
628
629 *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP;
630 while (len) {
631 if (len < 2) {
632 LOGP(DINP, LOGL_NOTICE,
633 "Short read of ipaccess tag\n");
634 msgb_free(nmsg);
635 return NULL;
636 }
637 switch (data[1]) {
638 case IPAC_IDTAG_UNIT:
639 sprintf(str, "%u/%u/%u",
640 dev->site_id, dev->bts_id, dev->trx_id);
641 break;
642 case IPAC_IDTAG_MACADDR:
643 sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
644 dev->mac_addr[0], dev->mac_addr[1],
645 dev->mac_addr[2], dev->mac_addr[3],
646 dev->mac_addr[4], dev->mac_addr[5]);
647 break;
648 case IPAC_IDTAG_LOCATION1:
649 strcpy(str, dev->location1);
650 break;
651 case IPAC_IDTAG_LOCATION2:
652 strcpy(str, dev->location2);
653 break;
654 case IPAC_IDTAG_EQUIPVERS:
655 strcpy(str, dev->equipvers);
656 break;
657 case IPAC_IDTAG_SWVERSION:
658 strcpy(str, dev->swversion);
659 break;
660 case IPAC_IDTAG_UNITNAME:
661 sprintf(str, "%s-%02x-%02x-%02x-%02x-%02x-%02x",
662 dev->unit_name,
663 dev->mac_addr[0], dev->mac_addr[1],
664 dev->mac_addr[2], dev->mac_addr[3],
665 dev->mac_addr[4], dev->mac_addr[5]);
666 break;
667 case IPAC_IDTAG_SERNR:
668 strcpy(str, dev->serno);
669 break;
670 default:
671 LOGP(DINP, LOGL_NOTICE,
672 "Unknown ipaccess tag 0x%02x\n", *data);
673 msgb_free(nmsg);
674 return NULL;
675 }
676 LOGP(DINP, LOGL_INFO, " tag %d: %s\n", data[1], str);
677 tag = msgb_put(nmsg, 3 + strlen(str) + 1);
678 tag[0] = 0x00;
679 tag[1] = 1 + strlen(str) + 1;
680 tag[2] = data[1];
681 memcpy(tag + 3, str, strlen(str) + 1);
682 data += 2;
683 len -= 2;
684 }
685 abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS);
686 return nmsg;
687}
688
689static struct msgb *ipa_bts_id_ack(void)
690{
691 struct msgb *nmsg2;
692
693 nmsg2 = abis_msgb_alloc(0);
694 if (!nmsg2)
695 return NULL;
696
697 *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK;
698 abis_push_ipa(nmsg2, IPAC_PROTO_IPACCESS);
699
700 return nmsg2;
701}
702
Pablo Neira Ayusoc07a8e72011-06-21 19:50:04 +0200703static int ipaccess_bts_cb(struct ipa_client_link *link, struct msgb *msg)
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200704{
705 struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
706 struct e1inp_ts *e1i_ts = NULL;
707 struct e1inp_sign_link *sign_link;
708
709 /* special handling for IPA CCM. */
710 if (hh->proto == IPAC_PROTO_IPACCESS) {
711 uint8_t msg_type = *(msg->l2h);
712
713 /* ping, pong and acknowledgment cases. */
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200714 ipaccess_rcvmsg_bts_base(msg, link->ofd);
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200715
716 /* this is a request for identification from the BSC. */
717 if (msg_type == IPAC_MSGT_ID_GET) {
Pablo Neira Ayusodfafe682011-07-02 14:32:32 +0200718 struct e1inp_sign_link *sign_link;
719 struct msgb *rmsg;
720 uint8_t *data = msgb_l2(msg);
721 int len = msgb_l2len(msg);
722
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200723 LOGP(DINP, LOGL_NOTICE, "received ID get\n");
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200724 if (!link->line->ops->sign_link_up) {
Pablo Neira Ayusocd8d2e52011-07-02 17:05:21 +0200725 LOGP(DINP, LOGL_ERROR,
726 "Unable to set signal link, "
727 "closing socket.\n");
728 osmo_fd_unregister(link->ofd);
729 close(link->ofd->fd);
730 link->ofd->fd = -1;
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200731 return -EINVAL;
732 }
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200733 sign_link = link->line->ops->sign_link_up(msg,
734 link->line,
735 link->ofd->priv_nr);
Pablo Neira Ayusodfafe682011-07-02 14:32:32 +0200736 if (sign_link == NULL) {
737 LOGP(DINP, LOGL_ERROR,
Pablo Neira Ayusocd8d2e52011-07-02 17:05:21 +0200738 "Unable to set signal link, "
739 "closing socket.\n");
740 osmo_fd_unregister(link->ofd);
741 close(link->ofd->fd);
742 link->ofd->fd = -1;
Pablo Neira Ayusodfafe682011-07-02 14:32:32 +0200743 return -EINVAL;
744 }
745 rmsg = ipa_bts_id_resp(link->line->ops->data,
746 data + 1, len - 1);
747 ipaccess_send(link->ofd->fd, rmsg->data, rmsg->len);
748 msgb_free(rmsg);
749
750 /* send ID_ACK. */
751 rmsg = ipa_bts_id_ack();
752 ipaccess_send(link->ofd->fd, rmsg->data, rmsg->len);
753 msgb_free(rmsg);
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200754 }
755 return 0;
756 } else if (link->port == IPA_TCP_PORT_OML)
757 e1i_ts = &link->line->ts[0];
758 else if (link->port == IPA_TCP_PORT_RSL)
759 e1i_ts = &link->line->ts[1];
760
761 /* look up for some existing signaling link. */
762 sign_link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
763 if (sign_link == NULL) {
764 LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
765 "hh->proto=0x%02x\n", hh->proto);
766 msgb_free(msg);
767 return -EIO;
768 }
769 msg->dst = sign_link;
770
771 /* XXX better use e1inp_ts_rx? */
Pablo Neira Ayusof163d232011-06-25 18:42:55 +0200772 if (!link->line->ops->sign_link) {
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200773 LOGP(DINP, LOGL_ERROR, "Fix your application, "
774 "no action set for signalling messages.\n");
775 return -ENOENT;
776 }
Pablo Neira Ayusodbd82fb2011-07-05 15:29:23 +0200777 link->line->ops->sign_link(msg);
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200778 return 0;
779}
780
Pablo Neira Ayusoc00ee732011-06-21 12:22:49 +0200781static int ipaccess_line_update(struct e1inp_line *line,
782 enum e1inp_line_role role, const char *addr)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200783{
784 int ret = -ENOENT;
785
786 switch(role) {
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200787 case E1INP_LINE_R_BSC: {
788 struct ipa_server_link *oml_link, *rsl_link;
789
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200790 LOGP(DINP, LOGL_NOTICE, "enabling ipaccess BSC mode\n");
791
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200792 oml_link = ipa_server_link_create(tall_ipa_ctx, line,
793 "0.0.0.0", IPA_TCP_PORT_OML,
Pablo Neira Ayusoe009f4a2011-06-23 13:36:34 +0200794 ipaccess_bsc_oml_cb, NULL);
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200795 if (oml_link == NULL) {
796 LOGP(DINP, LOGL_ERROR, "cannot create OML "
797 "BSC link: %s\n", strerror(errno));
798 return -ENOMEM;
Pablo Neira Ayusof67471f2011-06-07 11:25:49 +0200799 }
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200800 if (ipa_server_link_open(oml_link) < 0) {
801 LOGP(DINP, LOGL_ERROR, "cannot open OML BSC link: %s\n",
802 strerror(errno));
803 ipa_server_link_close(oml_link);
804 ipa_server_link_destroy(oml_link);
805 return -EIO;
Pablo Neira Ayusof67471f2011-06-07 11:25:49 +0200806 }
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200807 rsl_link = ipa_server_link_create(tall_ipa_ctx, line,
808 "0.0.0.0", IPA_TCP_PORT_RSL,
Pablo Neira Ayusoe009f4a2011-06-23 13:36:34 +0200809 ipaccess_bsc_rsl_cb, NULL);
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200810 if (rsl_link == NULL) {
811 LOGP(DINP, LOGL_ERROR, "cannot create RSL "
812 "BSC link: %s\n", strerror(errno));
813 return -ENOMEM;
814 }
815 if (ipa_server_link_open(rsl_link) < 0) {
816 LOGP(DINP, LOGL_ERROR, "cannot open RSL BSC link: %s\n",
817 strerror(errno));
818 ipa_server_link_close(rsl_link);
819 ipa_server_link_destroy(rsl_link);
820 return -EIO;
821 }
822 ret = 0;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200823 break;
Pablo Neira Ayuso986191f2011-06-21 19:56:26 +0200824 }
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200825 case E1INP_LINE_R_BTS: {
Pablo Neira Ayusoc07a8e72011-06-21 19:50:04 +0200826 struct ipa_client_link *link, *rsl_link;
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200827
828 LOGP(DINP, LOGL_NOTICE, "enabling ipaccess BTS mode\n");
829
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200830 link = ipa_client_link_create(tall_ipa_ctx,
831 &line->ts[E1INP_SIGN_OML-1],
832 "ipa", E1INP_SIGN_OML,
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200833 addr, IPA_TCP_PORT_OML,
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200834 ipaccess_bts_cb,
835 ipaccess_bts_write_cb,
836 NULL);
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200837 if (link == NULL) {
Pablo Neira Ayuso29465d32011-06-21 14:21:33 +0200838 LOGP(DINP, LOGL_ERROR, "cannot create OML "
839 "BTS link: %s\n", strerror(errno));
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200840 return -ENOMEM;
841 }
842 if (ipa_client_link_open(link) < 0) {
Pablo Neira Ayuso29465d32011-06-21 14:21:33 +0200843 LOGP(DINP, LOGL_ERROR, "cannot open OML BTS link: %s\n",
844 strerror(errno));
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200845 ipa_client_link_close(link);
846 ipa_client_link_destroy(link);
847 return -EIO;
848 }
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200849 rsl_link = ipa_client_link_create(tall_ipa_ctx,
850 &line->ts[E1INP_SIGN_RSL-1],
851 "ipa", E1INP_SIGN_RSL,
Pablo Neira Ayuso591ddad2011-06-21 18:16:42 +0200852 addr, IPA_TCP_PORT_RSL,
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200853 ipaccess_bts_cb,
854 ipaccess_bts_write_cb,
855 NULL);
Pablo Neira Ayuso29465d32011-06-21 14:21:33 +0200856 if (rsl_link == NULL) {
857 LOGP(DINP, LOGL_ERROR, "cannot create RSL "
858 "BTS link: %s\n", strerror(errno));
859 return -ENOMEM;
860 }
861 if (ipa_client_link_open(rsl_link) < 0) {
862 LOGP(DINP, LOGL_ERROR, "cannot open RSL BTS link: %s\n",
863 strerror(errno));
864 ipa_client_link_close(rsl_link);
865 ipa_client_link_destroy(rsl_link);
866 return -EIO;
867 }
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200868 ret = 0;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200869 break;
Pablo Neira Ayuso96e81282011-06-09 15:06:11 +0200870 }
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200871 default:
872 break;
873 }
874 return ret;
875}
876
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200877void e1inp_ipaccess_init(void)
878{
Pablo Neira Ayuso54b49792011-06-07 12:15:26 +0200879 tall_ipa_ctx = talloc_named_const(libosmo_abis_ctx, 1, "ipa");
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200880 e1inp_driver_register(&ipaccess_driver);
881}