blob: dac20122bcc9e3a1ffbad8dfc2bfef58dea1798c [file] [log] [blame]
Harald Welte28aa9912014-08-20 22:06:04 +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
Harald Weltefd5ad172014-10-26 20:47:42 +010010 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
Harald Welte28aa9912014-08-20 22:06:04 +020012 * (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 Weltefd5ad172014-10-26 20:47:42 +010017 * GNU General Public License for more details.
Harald Welte28aa9912014-08-20 22:06:04 +020018 *
Harald Weltefd5ad172014-10-26 20:47:42 +010019 * You should have received a copy of the GNU General Public License
Harald Welte28aa9912014-08-20 22:06:04 +020020 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <unistd.h>
25#include <stdint.h>
26#include <errno.h>
27#include <stdlib.h>
28
29#include <arpa/inet.h>
30
31#include <osmocom/core/msgb.h>
32#include <osmocom/core/talloc.h>
33#include <osmocom/core/logging.h>
34#include <osmocom/core/macaddr.h>
35#include <osmocom/core/select.h>
36
37#include <osmocom/gsm/tlv.h>
38#include <osmocom/gsm/protocol/ipaccess.h>
Harald Weltee3919962014-08-20 22:28:23 +020039#include <osmocom/gsm/ipa.h>
Harald Welte28aa9912014-08-20 22:06:04 +020040
41#define IPA_ALLOC_SIZE 1200
42
43/*
44 * Common propietary IPA messages:
45 * - PONG: in reply to PING.
46 * - ID_REQUEST: first messages once OML has been established.
47 * - ID_ACK: in reply to ID_ACK.
48 */
49static const uint8_t ipa_pong_msg[] = {
50 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
51};
52
53static const uint8_t ipa_id_ack_msg[] = {
54 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
55};
56
57static const uint8_t ipa_id_req_msg[] = {
58 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
59 0x01, IPAC_IDTAG_UNIT,
60 0x01, IPAC_IDTAG_MACADDR,
61 0x01, IPAC_IDTAG_LOCATION1,
62 0x01, IPAC_IDTAG_LOCATION2,
63 0x01, IPAC_IDTAG_EQUIPVERS,
64 0x01, IPAC_IDTAG_SWVERSION,
65 0x01, IPAC_IDTAG_UNITNAME,
66 0x01, IPAC_IDTAG_SERNR,
67};
68
69
70static const char *idtag_names[] = {
71 [IPAC_IDTAG_SERNR] = "Serial_Number",
72 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
73 [IPAC_IDTAG_LOCATION1] = "Location_1",
74 [IPAC_IDTAG_LOCATION2] = "Location_2",
75 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
76 [IPAC_IDTAG_SWVERSION] = "Software_Version",
77 [IPAC_IDTAG_IPADDR] = "IP_Address",
78 [IPAC_IDTAG_MACADDR] = "MAC_Address",
79 [IPAC_IDTAG_UNIT] = "Unit_ID",
80};
81
Harald Weltee3919962014-08-20 22:28:23 +020082const char *ipa_ccm_idtag_name(uint8_t tag)
Harald Welte28aa9912014-08-20 22:06:04 +020083{
84 if (tag >= ARRAY_SIZE(idtag_names))
85 return "unknown";
86
87 return idtag_names[tag];
88}
89
Harald Weltee3919962014-08-20 22:28:23 +020090int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
Harald Welte28aa9912014-08-20 22:06:04 +020091{
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +020092 return ipa_ccm_idtag_parse_off(dec, buf, len, 0);
93}
94
95int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
96{
Harald Welte28aa9912014-08-20 22:06:04 +020097 uint8_t t_len;
98 uint8_t t_tag;
99 uint8_t *cur = buf;
100
101 memset(dec, 0, sizeof(*dec));
102
103 while (len >= 2) {
104 len -= 2;
105 t_len = *cur++;
106 t_tag = *cur++;
107
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200108 if (t_len < len_offset) {
109 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d\n", t_len);
110 return -EINVAL;
111 }
112
Harald Welte28aa9912014-08-20 22:06:04 +0200113 if (t_len > len + 1) {
114 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
115 return -EINVAL;
116 }
117
Harald Weltee3919962014-08-20 22:28:23 +0200118 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200119
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200120 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200121 dec->lv[t_tag].val = cur;
122
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200123 cur += t_len - len_offset;
124 len -= t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200125 }
126 return 0;
127}
128
Harald Weltee3919962014-08-20 22:28:23 +0200129int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200130{
131 unsigned long ul;
132 char *endptr;
133 const char *nptr;
134
135 nptr = str;
136 ul = strtoul(nptr, &endptr, 10);
137 if (endptr <= nptr)
138 return -EINVAL;
139 unit_data->site_id = ul & 0xffff;
140
141 if (*endptr++ != '/')
142 return -EINVAL;
143
144 nptr = endptr;
145 ul = strtoul(nptr, &endptr, 10);
146 if (endptr <= nptr)
147 return -EINVAL;
148 unit_data->bts_id = ul & 0xffff;
149
150 if (*endptr++ != '/')
151 return -EINVAL;
152
153 nptr = endptr;
154 ul = strtoul(nptr, &endptr, 10);
155 if (endptr <= nptr)
156 return -EINVAL;
157 unit_data->trx_id = ul & 0xffff;
158
159 return 0;
160}
161
Harald Weltee3919962014-08-20 22:28:23 +0200162int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200163 const struct tlv_parsed *tp)
164{
165 int rc = 0;
166
167 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
168 ud->serno = talloc_strdup(ud, (char *)
169 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
170
171 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
172 ud->unit_name = talloc_strdup(ud, (char *)
173 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
174
175 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
176 ud->location1 = talloc_strdup(ud, (char *)
177 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
178
179 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
180 ud->location2 = talloc_strdup(ud, (char *)
181 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
182
183 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
184 ud->equipvers = talloc_strdup(ud, (char *)
185 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
186
187 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
188 ud->swversion = talloc_strdup(ud, (char *)
189 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
190
191 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
192 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
193 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
194 if (rc < 0)
195 goto out;
196 }
197
198 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200199 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200200 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
201
202out:
203 return rc;
204}
205
Harald Weltee3919962014-08-20 22:28:23 +0200206int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200207{
208 int ret;
209
210 ret = write(fd, msg, msglen);
211 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100212 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200213 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200214 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200215 return -EIO;
216 }
217 return ret;
218}
219
Harald Weltee3919962014-08-20 22:28:23 +0200220int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200221{
Harald Weltee3919962014-08-20 22:28:23 +0200222 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200223}
224
Harald Weltee3919962014-08-20 22:28:23 +0200225int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200226{
Harald Weltee3919962014-08-20 22:28:23 +0200227 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200228}
229
Harald Weltee3919962014-08-20 22:28:23 +0200230int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200231{
Harald Weltee3919962014-08-20 22:28:23 +0200232 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200233}
234
235/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200236int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200237{
238 uint8_t msg_type = *(msg->l2h);
239 int ret;
240
241 switch (msg_type) {
242 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200243 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200244 if (ret < 0) {
245 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
246 "message. Reason: %s\n", strerror(errno));
247 break;
248 }
249 ret = 1;
250 break;
251 case IPAC_MSGT_PONG:
252 DEBUGP(DLMI, "PONG!\n");
253 ret = 1;
254 break;
255 case IPAC_MSGT_ID_ACK:
256 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200257 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200258 if (ret < 0) {
259 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
260 "message. Reason: %s\n", strerror(errno));
261 break;
262 }
263 ret = 1;
264 break;
265 default:
266 /* This is not an IPA PING, PONG or ID_ACK message */
267 ret = 0;
268 break;
269 }
270 return ret;
271}
272
273/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200274int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200275{
276 uint8_t msg_type = *(msg->l2h);
277 int ret = 0;
278
279 switch (msg_type) {
280 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200281 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200282 if (ret < 0) {
283 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
284 "message. Reason: %s\n", strerror(errno));
285 }
286 break;
287 case IPAC_MSGT_PONG:
288 DEBUGP(DLMI, "PONG!\n");
289 break;
290 case IPAC_MSGT_ID_ACK:
291 DEBUGP(DLMI, "ID_ACK\n");
292 break;
293 }
294 return ret;
295}
296
297
Harald Weltee3919962014-08-20 22:28:23 +0200298void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200299{
300 struct ipaccess_head_ext *hh_ext;
301
302 /* prepend the osmo ip.access header extension */
303 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
304 hh_ext->proto = proto;
305}
306
Harald Weltee3919962014-08-20 22:28:23 +0200307void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200308{
309 struct ipaccess_head *hh;
310
311 /* prepend the ip.access header */
312 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
313 hh->len = htons(msg->len - sizeof(*hh));
314 hh->proto = proto;
315}
316
317int ipa_msg_recv(int fd, struct msgb **rmsg)
318{
319 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
320 if (rc < 0) {
321 errno = -rc;
322 rc = -1;
323 }
324 return rc;
325}
326
327int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
328{
329 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
330 struct ipaccess_head *hh;
331 int len, ret;
332 int needed;
333
334 if (msg == NULL) {
335 msg = ipa_msg_alloc(0);
336 if (msg == NULL) {
337 ret = -ENOMEM;
338 goto discard_msg;
339 }
340 msg->l1h = msg->tail;
341 }
342
343 if (msg->l2h == NULL) {
344 /* first read our 3-byte header */
345 needed = sizeof(*hh) - msg->len;
346 ret = recv(fd, msg->tail, needed, 0);
347 if (ret == 0)
348 goto discard_msg;
349
350 if (ret < 0) {
351 if (errno == EAGAIN || errno == EINTR)
352 ret = 0;
353 else {
354 ret = -errno;
355 goto discard_msg;
356 }
357 }
358
359 msgb_put(msg, ret);
360
361 if (ret < needed) {
362 if (msg->len == 0) {
363 ret = -EAGAIN;
364 goto discard_msg;
365 }
366
367 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200368 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200369 msg->len, sizeof(*hh));
370 if (!tmp_msg) {
371 ret = -EIO;
372 goto discard_msg;
373 }
374 *tmp_msg = msg;
375 return -EAGAIN;
376 }
377
378 msg->l2h = msg->tail;
379 }
380
381 hh = (struct ipaccess_head *) msg->data;
382
383 /* then read the length as specified in header */
384 len = ntohs(hh->len);
385
386 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
387 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
388 "received %d bytes\n", len, msg->len);
389 ret = -EIO;
390 goto discard_msg;
391 }
392
393 needed = len - msgb_l2len(msg);
394
395 if (needed > 0) {
396 ret = recv(fd, msg->tail, needed, 0);
397
398 if (ret == 0)
399 goto discard_msg;
400
401 if (ret < 0) {
402 if (errno == EAGAIN || errno == EINTR)
403 ret = 0;
404 else {
405 ret = -errno;
406 goto discard_msg;
407 }
408 }
409
410 msgb_put(msg, ret);
411
412 if (ret < needed) {
413 LOGP(DLINP, LOGL_INFO,
414 "Received part of IPA message L2 data (%d/%d)\n",
415 msgb_l2len(msg), len);
416 if (!tmp_msg) {
417 ret = -EIO;
418 goto discard_msg;
419 }
420 *tmp_msg = msg;
421 return -EAGAIN;
422 }
423 }
424
425 ret = msgb_l2len(msg);
426
427 if (ret == 0) {
428 LOGP(DLINP, LOGL_INFO,
429 "Discarding IPA message without payload\n");
430 ret = -EAGAIN;
431 goto discard_msg;
432 }
433
434 if (tmp_msg)
435 *tmp_msg = NULL;
436 *rmsg = msg;
437 return ret;
438
439discard_msg:
440 if (tmp_msg)
441 *tmp_msg = NULL;
442 msgb_free(msg);
443 return ret;
444}
445
446struct msgb *ipa_msg_alloc(int headroom)
447{
448 struct msgb *nmsg;
449
450 headroom += sizeof(struct ipaccess_head);
451
452 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
453 if (!nmsg)
454 return NULL;
455 return nmsg;
456}