blob: cddbd534b08ed8e58a824326d2d6e0a4c06c39ea [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>
39#include <osmocom/gsm/ipaccess.h>
40
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
82const char *ipaccess_idtag_name(uint8_t tag)
83{
84 if (tag >= ARRAY_SIZE(idtag_names))
85 return "unknown";
86
87 return idtag_names[tag];
88}
89
90int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
91{
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
108 DEBUGPC(DLMI, "%s='%s' ", ipaccess_idtag_name(t_tag), cur);
109
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
119int ipaccess_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
120{
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
152int ipaccess_tlv_to_unitdata(struct ipaccess_unit *ud,
153 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))
189 rc = ipaccess_parse_unitid((char *)
190 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
191
192out:
193 return rc;
194}
195
196int ipaccess_send(int fd, const void *msg, size_t msglen)
197{
198 int ret;
199
200 ret = write(fd, msg, msglen);
201 if (ret < 0)
202 return ret;
203 if (ret < msglen) {
204 LOGP(DLINP, LOGL_ERROR, "ipaccess_send: short write\n");
205 return -EIO;
206 }
207 return ret;
208}
209
210int ipaccess_send_pong(int fd)
211{
212 return ipaccess_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
213}
214
215int ipaccess_send_id_ack(int fd)
216{
217 return ipaccess_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
218}
219
220int ipaccess_send_id_req(int fd)
221{
222 return ipaccess_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
223}
224
225/* base handling of the ip.access protocol */
226int ipaccess_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
227{
228 uint8_t msg_type = *(msg->l2h);
229 int ret;
230
231 switch (msg_type) {
232 case IPAC_MSGT_PING:
233 ret = ipaccess_send_pong(bfd->fd);
234 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");
247 ret = ipaccess_send_id_ack(bfd->fd);
248 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 */
264int ipaccess_rcvmsg_bts_base(struct msgb *msg,
265 struct osmo_fd *bfd)
266{
267 uint8_t msg_type = *(msg->l2h);
268 int ret = 0;
269
270 switch (msg_type) {
271 case IPAC_MSGT_PING:
272 ret = ipaccess_send_pong(bfd->fd);
273 if (ret < 0) {
274 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
275 "message. Reason: %s\n", strerror(errno));
276 }
277 break;
278 case IPAC_MSGT_PONG:
279 DEBUGP(DLMI, "PONG!\n");
280 break;
281 case IPAC_MSGT_ID_ACK:
282 DEBUGP(DLMI, "ID_ACK\n");
283 break;
284 }
285 return ret;
286}
287
288
289void ipaccess_prepend_header_ext(struct msgb *msg, int proto)
290{
291 struct ipaccess_head_ext *hh_ext;
292
293 /* prepend the osmo ip.access header extension */
294 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
295 hh_ext->proto = proto;
296}
297
298void ipaccess_prepend_header(struct msgb *msg, int proto)
299{
300 struct ipaccess_head *hh;
301
302 /* prepend the ip.access header */
303 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
304 hh->len = htons(msg->len - sizeof(*hh));
305 hh->proto = proto;
306}
307
308int ipa_msg_recv(int fd, struct msgb **rmsg)
309{
310 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
311 if (rc < 0) {
312 errno = -rc;
313 rc = -1;
314 }
315 return rc;
316}
317
318int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
319{
320 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
321 struct ipaccess_head *hh;
322 int len, ret;
323 int needed;
324
325 if (msg == NULL) {
326 msg = ipa_msg_alloc(0);
327 if (msg == NULL) {
328 ret = -ENOMEM;
329 goto discard_msg;
330 }
331 msg->l1h = msg->tail;
332 }
333
334 if (msg->l2h == NULL) {
335 /* first read our 3-byte header */
336 needed = sizeof(*hh) - msg->len;
337 ret = recv(fd, msg->tail, needed, 0);
338 if (ret == 0)
339 goto discard_msg;
340
341 if (ret < 0) {
342 if (errno == EAGAIN || errno == EINTR)
343 ret = 0;
344 else {
345 ret = -errno;
346 goto discard_msg;
347 }
348 }
349
350 msgb_put(msg, ret);
351
352 if (ret < needed) {
353 if (msg->len == 0) {
354 ret = -EAGAIN;
355 goto discard_msg;
356 }
357
358 LOGP(DLINP, LOGL_INFO,
359 "Received part of IPA message header (%d/%d)\n",
360 msg->len, sizeof(*hh));
361 if (!tmp_msg) {
362 ret = -EIO;
363 goto discard_msg;
364 }
365 *tmp_msg = msg;
366 return -EAGAIN;
367 }
368
369 msg->l2h = msg->tail;
370 }
371
372 hh = (struct ipaccess_head *) msg->data;
373
374 /* then read the length as specified in header */
375 len = ntohs(hh->len);
376
377 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
378 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
379 "received %d bytes\n", len, msg->len);
380 ret = -EIO;
381 goto discard_msg;
382 }
383
384 needed = len - msgb_l2len(msg);
385
386 if (needed > 0) {
387 ret = recv(fd, msg->tail, needed, 0);
388
389 if (ret == 0)
390 goto discard_msg;
391
392 if (ret < 0) {
393 if (errno == EAGAIN || errno == EINTR)
394 ret = 0;
395 else {
396 ret = -errno;
397 goto discard_msg;
398 }
399 }
400
401 msgb_put(msg, ret);
402
403 if (ret < needed) {
404 LOGP(DLINP, LOGL_INFO,
405 "Received part of IPA message L2 data (%d/%d)\n",
406 msgb_l2len(msg), len);
407 if (!tmp_msg) {
408 ret = -EIO;
409 goto discard_msg;
410 }
411 *tmp_msg = msg;
412 return -EAGAIN;
413 }
414 }
415
416 ret = msgb_l2len(msg);
417
418 if (ret == 0) {
419 LOGP(DLINP, LOGL_INFO,
420 "Discarding IPA message without payload\n");
421 ret = -EAGAIN;
422 goto discard_msg;
423 }
424
425 if (tmp_msg)
426 *tmp_msg = NULL;
427 *rmsg = msg;
428 return ret;
429
430discard_msg:
431 if (tmp_msg)
432 *tmp_msg = NULL;
433 msgb_free(msg);
434 return ret;
435}
436
437struct msgb *ipa_msg_alloc(int headroom)
438{
439 struct msgb *nmsg;
440
441 headroom += sizeof(struct ipaccess_head);
442
443 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
444 if (!nmsg)
445 return NULL;
446 return nmsg;
447}