blob: 6dc3ab6060b8e7aaab3d3db80efc8fff2aa60fc1 [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>
Holger Hans Peter Freytheree6652b2015-11-09 16:21:19 +000030#include <sys/types.h>
Harald Welte28aa9912014-08-20 22:06:04 +020031
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/logging.h>
35#include <osmocom/core/macaddr.h>
36#include <osmocom/core/select.h>
37
38#include <osmocom/gsm/tlv.h>
39#include <osmocom/gsm/protocol/ipaccess.h>
Harald Weltee3919962014-08-20 22:28:23 +020040#include <osmocom/gsm/ipa.h>
Harald Welte28aa9912014-08-20 22:06:04 +020041
42#define IPA_ALLOC_SIZE 1200
43
44/*
45 * Common propietary IPA messages:
46 * - PONG: in reply to PING.
47 * - ID_REQUEST: first messages once OML has been established.
48 * - ID_ACK: in reply to ID_ACK.
49 */
50static const uint8_t ipa_pong_msg[] = {
51 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
52};
53
54static const uint8_t ipa_id_ack_msg[] = {
55 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
56};
57
58static const uint8_t ipa_id_req_msg[] = {
59 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
60 0x01, IPAC_IDTAG_UNIT,
61 0x01, IPAC_IDTAG_MACADDR,
62 0x01, IPAC_IDTAG_LOCATION1,
63 0x01, IPAC_IDTAG_LOCATION2,
64 0x01, IPAC_IDTAG_EQUIPVERS,
65 0x01, IPAC_IDTAG_SWVERSION,
66 0x01, IPAC_IDTAG_UNITNAME,
67 0x01, IPAC_IDTAG_SERNR,
68};
69
70
71static const char *idtag_names[] = {
72 [IPAC_IDTAG_SERNR] = "Serial_Number",
73 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
74 [IPAC_IDTAG_LOCATION1] = "Location_1",
75 [IPAC_IDTAG_LOCATION2] = "Location_2",
76 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
77 [IPAC_IDTAG_SWVERSION] = "Software_Version",
78 [IPAC_IDTAG_IPADDR] = "IP_Address",
79 [IPAC_IDTAG_MACADDR] = "MAC_Address",
80 [IPAC_IDTAG_UNIT] = "Unit_ID",
81};
82
Harald Weltee3919962014-08-20 22:28:23 +020083const char *ipa_ccm_idtag_name(uint8_t tag)
Harald Welte28aa9912014-08-20 22:06:04 +020084{
85 if (tag >= ARRAY_SIZE(idtag_names))
86 return "unknown";
87
88 return idtag_names[tag];
89}
90
Harald Weltee3919962014-08-20 22:28:23 +020091int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
Harald Welte28aa9912014-08-20 22:06:04 +020092{
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +020093 return ipa_ccm_idtag_parse_off(dec, buf, len, 0);
94}
95
96int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
97{
Harald Welte28aa9912014-08-20 22:06:04 +020098 uint8_t t_len;
99 uint8_t t_tag;
100 uint8_t *cur = buf;
101
102 memset(dec, 0, sizeof(*dec));
103
104 while (len >= 2) {
105 len -= 2;
106 t_len = *cur++;
107 t_tag = *cur++;
108
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200109 if (t_len < len_offset) {
Max9b4d0652016-11-15 19:21:23 +0100110 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200111 return -EINVAL;
112 }
113
Harald Welte28aa9912014-08-20 22:06:04 +0200114 if (t_len > len + 1) {
Max9b4d0652016-11-15 19:21:23 +0100115 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Harald Welte28aa9912014-08-20 22:06:04 +0200116 return -EINVAL;
117 }
118
Harald Weltee3919962014-08-20 22:28:23 +0200119 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200120
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200121 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200122 dec->lv[t_tag].val = cur;
123
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200124 cur += t_len - len_offset;
125 len -= t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200126 }
127 return 0;
128}
129
Harald Weltee3919962014-08-20 22:28:23 +0200130int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200131{
132 unsigned long ul;
133 char *endptr;
134 const char *nptr;
135
136 nptr = str;
137 ul = strtoul(nptr, &endptr, 10);
138 if (endptr <= nptr)
139 return -EINVAL;
140 unit_data->site_id = ul & 0xffff;
141
142 if (*endptr++ != '/')
143 return -EINVAL;
144
145 nptr = endptr;
146 ul = strtoul(nptr, &endptr, 10);
147 if (endptr <= nptr)
148 return -EINVAL;
149 unit_data->bts_id = ul & 0xffff;
150
151 if (*endptr++ != '/')
152 return -EINVAL;
153
154 nptr = endptr;
155 ul = strtoul(nptr, &endptr, 10);
156 if (endptr <= nptr)
157 return -EINVAL;
158 unit_data->trx_id = ul & 0xffff;
159
160 return 0;
161}
162
Harald Weltee3919962014-08-20 22:28:23 +0200163int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200164 const struct tlv_parsed *tp)
165{
166 int rc = 0;
167
168 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
169 ud->serno = talloc_strdup(ud, (char *)
170 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
171
172 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
173 ud->unit_name = talloc_strdup(ud, (char *)
174 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
175
176 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
177 ud->location1 = talloc_strdup(ud, (char *)
178 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
179
180 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
181 ud->location2 = talloc_strdup(ud, (char *)
182 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
183
184 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
185 ud->equipvers = talloc_strdup(ud, (char *)
186 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
187
188 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
189 ud->swversion = talloc_strdup(ud, (char *)
190 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
191
192 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
193 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
194 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
195 if (rc < 0)
196 goto out;
197 }
198
199 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200200 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200201 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
202
203out:
204 return rc;
205}
206
Harald Weltee3919962014-08-20 22:28:23 +0200207int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200208{
209 int ret;
210
211 ret = write(fd, msg, msglen);
212 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100213 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200214 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200215 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200216 return -EIO;
217 }
218 return ret;
219}
220
Harald Weltee3919962014-08-20 22:28:23 +0200221int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200222{
Harald Weltee3919962014-08-20 22:28:23 +0200223 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200224}
225
Harald Weltee3919962014-08-20 22:28:23 +0200226int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200227{
Harald Weltee3919962014-08-20 22:28:23 +0200228 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200229}
230
Harald Weltee3919962014-08-20 22:28:23 +0200231int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200232{
Harald Weltee3919962014-08-20 22:28:23 +0200233 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200234}
235
236/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200237int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200238{
239 uint8_t msg_type = *(msg->l2h);
240 int ret;
241
242 switch (msg_type) {
243 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200244 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200245 if (ret < 0) {
246 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
247 "message. Reason: %s\n", strerror(errno));
248 break;
249 }
250 ret = 1;
251 break;
252 case IPAC_MSGT_PONG:
253 DEBUGP(DLMI, "PONG!\n");
254 ret = 1;
255 break;
256 case IPAC_MSGT_ID_ACK:
257 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200258 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200259 if (ret < 0) {
260 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
261 "message. Reason: %s\n", strerror(errno));
262 break;
263 }
264 ret = 1;
265 break;
266 default:
267 /* This is not an IPA PING, PONG or ID_ACK message */
268 ret = 0;
269 break;
270 }
271 return ret;
272}
273
274/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200275int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200276{
277 uint8_t msg_type = *(msg->l2h);
278 int ret = 0;
279
280 switch (msg_type) {
281 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200282 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200283 if (ret < 0) {
284 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
285 "message. Reason: %s\n", strerror(errno));
286 }
287 break;
288 case IPAC_MSGT_PONG:
289 DEBUGP(DLMI, "PONG!\n");
290 break;
291 case IPAC_MSGT_ID_ACK:
292 DEBUGP(DLMI, "ID_ACK\n");
293 break;
294 }
295 return ret;
296}
297
298
Harald Weltee3919962014-08-20 22:28:23 +0200299void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200300{
301 struct ipaccess_head_ext *hh_ext;
302
303 /* prepend the osmo ip.access header extension */
304 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
305 hh_ext->proto = proto;
306}
307
Harald Weltee3919962014-08-20 22:28:23 +0200308void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200309{
310 struct ipaccess_head *hh;
311
312 /* prepend the ip.access header */
313 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
314 hh->len = htons(msg->len - sizeof(*hh));
315 hh->proto = proto;
316}
317
318int ipa_msg_recv(int fd, struct msgb **rmsg)
319{
320 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
321 if (rc < 0) {
322 errno = -rc;
323 rc = -1;
324 }
325 return rc;
326}
327
328int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
329{
330 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
331 struct ipaccess_head *hh;
332 int len, ret;
333 int needed;
334
335 if (msg == NULL) {
336 msg = ipa_msg_alloc(0);
337 if (msg == NULL) {
338 ret = -ENOMEM;
339 goto discard_msg;
340 }
341 msg->l1h = msg->tail;
342 }
343
344 if (msg->l2h == NULL) {
345 /* first read our 3-byte header */
346 needed = sizeof(*hh) - msg->len;
347 ret = recv(fd, msg->tail, needed, 0);
348 if (ret == 0)
349 goto discard_msg;
350
351 if (ret < 0) {
352 if (errno == EAGAIN || errno == EINTR)
353 ret = 0;
354 else {
355 ret = -errno;
356 goto discard_msg;
357 }
358 }
359
360 msgb_put(msg, ret);
361
362 if (ret < needed) {
363 if (msg->len == 0) {
364 ret = -EAGAIN;
365 goto discard_msg;
366 }
367
368 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200369 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200370 msg->len, sizeof(*hh));
371 if (!tmp_msg) {
372 ret = -EIO;
373 goto discard_msg;
374 }
375 *tmp_msg = msg;
376 return -EAGAIN;
377 }
378
379 msg->l2h = msg->tail;
380 }
381
382 hh = (struct ipaccess_head *) msg->data;
383
384 /* then read the length as specified in header */
385 len = ntohs(hh->len);
386
387 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
388 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
389 "received %d bytes\n", len, msg->len);
390 ret = -EIO;
391 goto discard_msg;
392 }
393
394 needed = len - msgb_l2len(msg);
395
396 if (needed > 0) {
397 ret = recv(fd, msg->tail, needed, 0);
398
399 if (ret == 0)
400 goto discard_msg;
401
402 if (ret < 0) {
403 if (errno == EAGAIN || errno == EINTR)
404 ret = 0;
405 else {
406 ret = -errno;
407 goto discard_msg;
408 }
409 }
410
411 msgb_put(msg, ret);
412
413 if (ret < needed) {
414 LOGP(DLINP, LOGL_INFO,
415 "Received part of IPA message L2 data (%d/%d)\n",
416 msgb_l2len(msg), len);
417 if (!tmp_msg) {
418 ret = -EIO;
419 goto discard_msg;
420 }
421 *tmp_msg = msg;
422 return -EAGAIN;
423 }
424 }
425
426 ret = msgb_l2len(msg);
427
428 if (ret == 0) {
429 LOGP(DLINP, LOGL_INFO,
430 "Discarding IPA message without payload\n");
431 ret = -EAGAIN;
432 goto discard_msg;
433 }
434
435 if (tmp_msg)
436 *tmp_msg = NULL;
437 *rmsg = msg;
438 return ret;
439
440discard_msg:
441 if (tmp_msg)
442 *tmp_msg = NULL;
443 msgb_free(msg);
444 return ret;
445}
446
447struct msgb *ipa_msg_alloc(int headroom)
448{
449 struct msgb *nmsg;
450
451 headroom += sizeof(struct ipaccess_head);
452
453 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
454 if (!nmsg)
455 return NULL;
456 return nmsg;
457}