blob: a49d8ac2cd15892c892ef6ac870d038d0dc401a8 [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
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 <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{
92 uint8_t t_len;
93 uint8_t t_tag;
94 uint8_t *cur = buf;
95
96 memset(dec, 0, sizeof(*dec));
97
98 while (len >= 2) {
99 len -= 2;
100 t_len = *cur++;
101 t_tag = *cur++;
102
103 if (t_len > len + 1) {
104 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
105 return -EINVAL;
106 }
107
Harald Weltee3919962014-08-20 22:28:23 +0200108 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200109
110 dec->lv[t_tag].len = t_len;
111 dec->lv[t_tag].val = cur;
112
113 cur += t_len;
114 len -= t_len;
115 }
116 return 0;
117}
118
Harald Weltee3919962014-08-20 22:28:23 +0200119int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200120{
121 unsigned long ul;
122 char *endptr;
123 const char *nptr;
124
125 nptr = str;
126 ul = strtoul(nptr, &endptr, 10);
127 if (endptr <= nptr)
128 return -EINVAL;
129 unit_data->site_id = ul & 0xffff;
130
131 if (*endptr++ != '/')
132 return -EINVAL;
133
134 nptr = endptr;
135 ul = strtoul(nptr, &endptr, 10);
136 if (endptr <= nptr)
137 return -EINVAL;
138 unit_data->bts_id = ul & 0xffff;
139
140 if (*endptr++ != '/')
141 return -EINVAL;
142
143 nptr = endptr;
144 ul = strtoul(nptr, &endptr, 10);
145 if (endptr <= nptr)
146 return -EINVAL;
147 unit_data->trx_id = ul & 0xffff;
148
149 return 0;
150}
151
Harald Weltee3919962014-08-20 22:28:23 +0200152int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200153 const struct tlv_parsed *tp)
154{
155 int rc = 0;
156
157 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
158 ud->serno = talloc_strdup(ud, (char *)
159 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
160
161 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
162 ud->unit_name = talloc_strdup(ud, (char *)
163 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
164
165 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
166 ud->location1 = talloc_strdup(ud, (char *)
167 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
168
169 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
170 ud->location2 = talloc_strdup(ud, (char *)
171 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
172
173 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
174 ud->equipvers = talloc_strdup(ud, (char *)
175 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
176
177 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
178 ud->swversion = talloc_strdup(ud, (char *)
179 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
180
181 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
182 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
183 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
184 if (rc < 0)
185 goto out;
186 }
187
188 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200189 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200190 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
191
192out:
193 return rc;
194}
195
Harald Weltee3919962014-08-20 22:28:23 +0200196int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200197{
198 int ret;
199
200 ret = write(fd, msg, msglen);
201 if (ret < 0)
202 return ret;
203 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200204 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200205 return -EIO;
206 }
207 return ret;
208}
209
Harald Weltee3919962014-08-20 22:28:23 +0200210int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200211{
Harald Weltee3919962014-08-20 22:28:23 +0200212 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200213}
214
Harald Weltee3919962014-08-20 22:28:23 +0200215int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200216{
Harald Weltee3919962014-08-20 22:28:23 +0200217 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200218}
219
Harald Weltee3919962014-08-20 22:28:23 +0200220int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200221{
Harald Weltee3919962014-08-20 22:28:23 +0200222 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200223}
224
225/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200226int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200227{
228 uint8_t msg_type = *(msg->l2h);
229 int ret;
230
231 switch (msg_type) {
232 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200233 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200234 if (ret < 0) {
235 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
236 "message. Reason: %s\n", strerror(errno));
237 break;
238 }
239 ret = 1;
240 break;
241 case IPAC_MSGT_PONG:
242 DEBUGP(DLMI, "PONG!\n");
243 ret = 1;
244 break;
245 case IPAC_MSGT_ID_ACK:
246 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200247 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200248 if (ret < 0) {
249 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
250 "message. Reason: %s\n", strerror(errno));
251 break;
252 }
253 ret = 1;
254 break;
255 default:
256 /* This is not an IPA PING, PONG or ID_ACK message */
257 ret = 0;
258 break;
259 }
260 return ret;
261}
262
263/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200264int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200265{
266 uint8_t msg_type = *(msg->l2h);
267 int ret = 0;
268
269 switch (msg_type) {
270 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200271 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200272 if (ret < 0) {
273 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
274 "message. Reason: %s\n", strerror(errno));
275 }
276 break;
277 case IPAC_MSGT_PONG:
278 DEBUGP(DLMI, "PONG!\n");
279 break;
280 case IPAC_MSGT_ID_ACK:
281 DEBUGP(DLMI, "ID_ACK\n");
282 break;
283 }
284 return ret;
285}
286
287
Harald Weltee3919962014-08-20 22:28:23 +0200288void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200289{
290 struct ipaccess_head_ext *hh_ext;
291
292 /* prepend the osmo ip.access header extension */
293 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
294 hh_ext->proto = proto;
295}
296
Harald Weltee3919962014-08-20 22:28:23 +0200297void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200298{
299 struct ipaccess_head *hh;
300
301 /* prepend the ip.access header */
302 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
303 hh->len = htons(msg->len - sizeof(*hh));
304 hh->proto = proto;
305}
306
307int ipa_msg_recv(int fd, struct msgb **rmsg)
308{
309 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
310 if (rc < 0) {
311 errno = -rc;
312 rc = -1;
313 }
314 return rc;
315}
316
317int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
318{
319 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
320 struct ipaccess_head *hh;
321 int len, ret;
322 int needed;
323
324 if (msg == NULL) {
325 msg = ipa_msg_alloc(0);
326 if (msg == NULL) {
327 ret = -ENOMEM;
328 goto discard_msg;
329 }
330 msg->l1h = msg->tail;
331 }
332
333 if (msg->l2h == NULL) {
334 /* first read our 3-byte header */
335 needed = sizeof(*hh) - msg->len;
336 ret = recv(fd, msg->tail, needed, 0);
337 if (ret == 0)
338 goto discard_msg;
339
340 if (ret < 0) {
341 if (errno == EAGAIN || errno == EINTR)
342 ret = 0;
343 else {
344 ret = -errno;
345 goto discard_msg;
346 }
347 }
348
349 msgb_put(msg, ret);
350
351 if (ret < needed) {
352 if (msg->len == 0) {
353 ret = -EAGAIN;
354 goto discard_msg;
355 }
356
357 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200358 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200359 msg->len, sizeof(*hh));
360 if (!tmp_msg) {
361 ret = -EIO;
362 goto discard_msg;
363 }
364 *tmp_msg = msg;
365 return -EAGAIN;
366 }
367
368 msg->l2h = msg->tail;
369 }
370
371 hh = (struct ipaccess_head *) msg->data;
372
373 /* then read the length as specified in header */
374 len = ntohs(hh->len);
375
376 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
377 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
378 "received %d bytes\n", len, msg->len);
379 ret = -EIO;
380 goto discard_msg;
381 }
382
383 needed = len - msgb_l2len(msg);
384
385 if (needed > 0) {
386 ret = recv(fd, msg->tail, needed, 0);
387
388 if (ret == 0)
389 goto discard_msg;
390
391 if (ret < 0) {
392 if (errno == EAGAIN || errno == EINTR)
393 ret = 0;
394 else {
395 ret = -errno;
396 goto discard_msg;
397 }
398 }
399
400 msgb_put(msg, ret);
401
402 if (ret < needed) {
403 LOGP(DLINP, LOGL_INFO,
404 "Received part of IPA message L2 data (%d/%d)\n",
405 msgb_l2len(msg), len);
406 if (!tmp_msg) {
407 ret = -EIO;
408 goto discard_msg;
409 }
410 *tmp_msg = msg;
411 return -EAGAIN;
412 }
413 }
414
415 ret = msgb_l2len(msg);
416
417 if (ret == 0) {
418 LOGP(DLINP, LOGL_INFO,
419 "Discarding IPA message without payload\n");
420 ret = -EAGAIN;
421 goto discard_msg;
422 }
423
424 if (tmp_msg)
425 *tmp_msg = NULL;
426 *rmsg = msg;
427 return ret;
428
429discard_msg:
430 if (tmp_msg)
431 *tmp_msg = NULL;
432 msgb_free(msg);
433 return ret;
434}
435
436struct msgb *ipa_msg_alloc(int headroom)
437{
438 struct msgb *nmsg;
439
440 headroom += sizeof(struct ipaccess_head);
441
442 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
443 if (!nmsg)
444 return NULL;
445 return nmsg;
446}