blob: aecde831f8f413516caf895f41a7b29f5170b3be [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file ipa.c
2 * OpenBSC Abis input driver for ip.access */
3/*
4 * (C) 2009-2017 by Harald Welte <laforge@gnumonks.org>
Harald Welte28aa9912014-08-20 22:06:04 +02005 * (C) 2010 by Holger Hans Peter Freyther
6 * (C) 2010 by On-Waves
7 *
8 * All Rights Reserved
9 *
Harald Weltee08da972017-11-13 01:00:26 +090010 * SPDX-License-Identifier: GPL-2.0+
11 *
Harald Welte28aa9912014-08-20 22:06:04 +020012 * This program is free software; you can redistribute it and/or modify
Harald Weltefd5ad172014-10-26 20:47:42 +010013 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
Harald Welte28aa9912014-08-20 22:06:04 +020015 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Weltefd5ad172014-10-26 20:47:42 +010020 * GNU General Public License for more details.
Harald Welte28aa9912014-08-20 22:06:04 +020021 *
Harald Weltefd5ad172014-10-26 20:47:42 +010022 * You should have received a copy of the GNU General Public License
Harald Welte28aa9912014-08-20 22:06:04 +020023 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 *
25 */
26
Harald Welte20725b92017-05-15 12:50:04 +020027#include "config.h"
28
Harald Welte28aa9912014-08-20 22:06:04 +020029#include <unistd.h>
30#include <stdint.h>
31#include <errno.h>
32#include <stdlib.h>
33
Holger Hans Peter Freytheree6652b2015-11-09 16:21:19 +000034#include <sys/types.h>
Harald Welte28aa9912014-08-20 22:06:04 +020035
Harald Welte95871da2017-05-15 12:11:36 +020036#include <osmocom/core/byteswap.h>
Harald Welte28aa9912014-08-20 22:06:04 +020037#include <osmocom/core/msgb.h>
38#include <osmocom/core/talloc.h>
39#include <osmocom/core/logging.h>
40#include <osmocom/core/macaddr.h>
41#include <osmocom/core/select.h>
42
43#include <osmocom/gsm/tlv.h>
44#include <osmocom/gsm/protocol/ipaccess.h>
Harald Weltee3919962014-08-20 22:28:23 +020045#include <osmocom/gsm/ipa.h>
Harald Welte28aa9912014-08-20 22:06:04 +020046
Harald Welte96e2a002017-06-12 21:44:18 +020047/*! \addtogroup ipa
48 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020049 * IPA Multiplex utility routines
Harald Welte96e2a002017-06-12 21:44:18 +020050 */
51
Harald Welte28aa9912014-08-20 22:06:04 +020052#define IPA_ALLOC_SIZE 1200
53
54/*
55 * Common propietary IPA messages:
56 * - PONG: in reply to PING.
57 * - ID_REQUEST: first messages once OML has been established.
58 * - ID_ACK: in reply to ID_ACK.
59 */
60static const uint8_t ipa_pong_msg[] = {
61 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
62};
63
64static const uint8_t ipa_id_ack_msg[] = {
65 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
66};
67
68static const uint8_t ipa_id_req_msg[] = {
69 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
70 0x01, IPAC_IDTAG_UNIT,
71 0x01, IPAC_IDTAG_MACADDR,
72 0x01, IPAC_IDTAG_LOCATION1,
73 0x01, IPAC_IDTAG_LOCATION2,
74 0x01, IPAC_IDTAG_EQUIPVERS,
75 0x01, IPAC_IDTAG_SWVERSION,
76 0x01, IPAC_IDTAG_UNITNAME,
77 0x01, IPAC_IDTAG_SERNR,
78};
79
80
81static const char *idtag_names[] = {
82 [IPAC_IDTAG_SERNR] = "Serial_Number",
83 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
84 [IPAC_IDTAG_LOCATION1] = "Location_1",
85 [IPAC_IDTAG_LOCATION2] = "Location_2",
86 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
87 [IPAC_IDTAG_SWVERSION] = "Software_Version",
88 [IPAC_IDTAG_IPADDR] = "IP_Address",
89 [IPAC_IDTAG_MACADDR] = "MAC_Address",
90 [IPAC_IDTAG_UNIT] = "Unit_ID",
91};
92
Harald Weltee3919962014-08-20 22:28:23 +020093const char *ipa_ccm_idtag_name(uint8_t tag)
Harald Welte28aa9912014-08-20 22:06:04 +020094{
95 if (tag >= ARRAY_SIZE(idtag_names))
96 return "unknown";
97
98 return idtag_names[tag];
99}
100
Harald Weltee3919962014-08-20 22:28:23 +0200101int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
Harald Welte28aa9912014-08-20 22:06:04 +0200102{
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200103 return ipa_ccm_idtag_parse_off(dec, buf, len, 0);
104}
105
106int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
107{
Harald Welte28aa9912014-08-20 22:06:04 +0200108 uint8_t t_len;
109 uint8_t t_tag;
110 uint8_t *cur = buf;
111
112 memset(dec, 0, sizeof(*dec));
113
114 while (len >= 2) {
115 len -= 2;
116 t_len = *cur++;
117 t_tag = *cur++;
118
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200119 if (t_len < len_offset) {
Max9b4d0652016-11-15 19:21:23 +0100120 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200121 return -EINVAL;
122 }
123
Harald Welte28aa9912014-08-20 22:06:04 +0200124 if (t_len > len + 1) {
Max9b4d0652016-11-15 19:21:23 +0100125 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Harald Welte28aa9912014-08-20 22:06:04 +0200126 return -EINVAL;
127 }
128
Harald Weltee3919962014-08-20 22:28:23 +0200129 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200130
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200131 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200132 dec->lv[t_tag].val = cur;
133
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200134 cur += t_len - len_offset;
135 len -= t_len - len_offset;
Harald Welte28aa9912014-08-20 22:06:04 +0200136 }
137 return 0;
138}
139
Harald Weltee3919962014-08-20 22:28:23 +0200140int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200141{
142 unsigned long ul;
143 char *endptr;
144 const char *nptr;
145
146 nptr = str;
147 ul = strtoul(nptr, &endptr, 10);
148 if (endptr <= nptr)
149 return -EINVAL;
150 unit_data->site_id = ul & 0xffff;
151
152 if (*endptr++ != '/')
153 return -EINVAL;
154
155 nptr = endptr;
156 ul = strtoul(nptr, &endptr, 10);
157 if (endptr <= nptr)
158 return -EINVAL;
159 unit_data->bts_id = ul & 0xffff;
160
161 if (*endptr++ != '/')
162 return -EINVAL;
163
164 nptr = endptr;
165 ul = strtoul(nptr, &endptr, 10);
166 if (endptr <= nptr)
167 return -EINVAL;
168 unit_data->trx_id = ul & 0xffff;
169
170 return 0;
171}
172
Harald Weltee3919962014-08-20 22:28:23 +0200173int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200174 const struct tlv_parsed *tp)
175{
176 int rc = 0;
177
178 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
179 ud->serno = talloc_strdup(ud, (char *)
180 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
181
182 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
183 ud->unit_name = talloc_strdup(ud, (char *)
184 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
185
186 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
187 ud->location1 = talloc_strdup(ud, (char *)
188 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
189
190 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
191 ud->location2 = talloc_strdup(ud, (char *)
192 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
193
194 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
195 ud->equipvers = talloc_strdup(ud, (char *)
196 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
197
198 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
199 ud->swversion = talloc_strdup(ud, (char *)
200 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
201
202 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
203 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
204 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
205 if (rc < 0)
206 goto out;
207 }
208
209 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200210 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200211 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
212
213out:
214 return rc;
215}
216
Harald Welte7bc88bb2017-04-15 19:05:33 +0200217#define IPA_STRING_MAX 64
218
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200219/*! Generate IPA CCM ID RESP based on list of IEs
Harald Welte7bc88bb2017-04-15 19:05:33 +0200220 * \param[in] dev Descriptor describing identity data for response
221 * \param[in] ies_req List of IEIs to include in response
222 * \param[in] num_ies_req Number of IEIs in \a ies_req
223 * \returns Message buffer with IPA CCM ID RESP */
224struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev,
225 const uint8_t *ies_req, unsigned int num_ies_req)
226{
227 struct msgb *msg = ipa_msg_alloc(16);
228 char str[IPA_STRING_MAX];
229 unsigned int i;
230
231 if (!msg)
232 return NULL;
233
234 *msgb_put(msg, 1) = IPAC_MSGT_ID_RESP;
235
236 for (i = 0; i < num_ies_req; i++) {
237 uint8_t *tag;
238
239 str[0] = '\0';
240 switch (ies_req[i]) {
241 case IPAC_IDTAG_UNIT:
242 snprintf(str, sizeof(str), "%u/%u/%u",
243 dev->site_id, dev->bts_id, dev->trx_id);
244 break;
245 case IPAC_IDTAG_MACADDR:
246 snprintf(str, sizeof(str),
247 "%02x:%02x:%02x:%02x:%02x:%02x",
248 dev->mac_addr[0], dev->mac_addr[1],
249 dev->mac_addr[2], dev->mac_addr[3],
250 dev->mac_addr[4], dev->mac_addr[5]);
251 break;
252 case IPAC_IDTAG_LOCATION1:
253 if (dev->location1)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200254 osmo_strlcpy(str, dev->location1, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200255 break;
256 case IPAC_IDTAG_LOCATION2:
257 if (dev->location2)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200258 osmo_strlcpy(str, dev->location2, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200259 break;
260 case IPAC_IDTAG_EQUIPVERS:
261 if (dev->equipvers)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200262 osmo_strlcpy(str, dev->equipvers, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200263 break;
264 case IPAC_IDTAG_SWVERSION:
265 if (dev->swversion)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200266 osmo_strlcpy(str, dev->swversion, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200267 break;
268 case IPAC_IDTAG_UNITNAME:
269 if (dev->unit_name) {
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200270 snprintf(str, sizeof(str), "%s", dev->unit_name);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200271 } else {
272 snprintf(str, sizeof(str),
273 "%02x-%02x-%02x-%02x-%02x-%02x",
274 dev->mac_addr[0], dev->mac_addr[1],
275 dev->mac_addr[2], dev->mac_addr[3],
276 dev->mac_addr[4], dev->mac_addr[5]);
277 }
278 break;
279 case IPAC_IDTAG_SERNR:
280 if (dev->serno)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200281 osmo_strlcpy(str, dev->serno, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200282 break;
283 default:
284 LOGP(DLINP, LOGL_NOTICE,
285 "Unknown ipaccess tag 0x%02x\n", ies_req[i]);
286 msgb_free(msg);
287 return NULL;
288 }
Harald Welte7bc88bb2017-04-15 19:05:33 +0200289
290 LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", ies_req[i], str);
291 tag = msgb_put(msg, 3 + strlen(str) + 1);
292 tag[0] = 0x00;
293 tag[1] = 1 + strlen(str) + 1;
294 tag[2] = ies_req[1];
295 memcpy(tag + 3, str, strlen(str) + 1);
296 }
297 ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
298 return msg;
299}
300
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200301/*! Generate IPA CCM ID RESP based on requets payload
Harald Welte7bc88bb2017-04-15 19:05:33 +0200302 * \param[in] dev Descriptor describing identity data for response
303 * \param[in] data Payload of the IPA CCM ID GET request
304 * \param[in] len Length of \a data in octets
305 * \returns Message buffer with IPA CCM ID RESP */
306struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
307 const uint8_t *data, unsigned int len)
308{
309 uint8_t ies[len/2];
310 unsigned int num_ies = 0;
311 const uint8_t *cur = data;
312
Harald Welte8a4895c2017-04-27 10:25:10 +0200313 memset(ies, 0, sizeof(ies));
314
Harald Welte7bc88bb2017-04-15 19:05:33 +0200315 /* build a array of the IEIs */
316 while (len >= 2) {
317 uint8_t t_len, t_tag;
318 len -= 2;
319 t_len = *cur++;
320 t_tag = *cur++;
321
322 if (t_len > len + 1) {
Thorsten Alteholz5a9dbf82018-04-08 19:13:25 +0200323 LOGP(DLINP, LOGL_ERROR, "IPA CCM tag 0x%02x does not fit\n", t_tag);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200324 break;
325 }
326
327 ies[num_ies++] = t_tag;
328
329 cur += t_len;
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200330 /* prevent any unsigned integer underflow due to somebody sending us
331 * messages with wrong length values */
332 if (len <= t_len)
333 len -= t_len;
334 else
335 len = 0;
Harald Welte7bc88bb2017-04-15 19:05:33 +0200336 }
337 return ipa_ccm_make_id_resp(dev, ies, num_ies);
338}
339
Harald Weltee3919962014-08-20 22:28:23 +0200340int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200341{
342 int ret;
343
344 ret = write(fd, msg, msglen);
345 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100346 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200347 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200348 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200349 return -EIO;
350 }
351 return ret;
352}
353
Harald Weltee3919962014-08-20 22:28:23 +0200354int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200355{
Harald Weltee3919962014-08-20 22:28:23 +0200356 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200357}
358
Harald Weltee3919962014-08-20 22:28:23 +0200359int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200360{
Harald Weltee3919962014-08-20 22:28:23 +0200361 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200362}
363
Harald Weltee3919962014-08-20 22:28:23 +0200364int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200365{
Harald Weltee3919962014-08-20 22:28:23 +0200366 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200367}
368
369/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200370int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200371{
372 uint8_t msg_type = *(msg->l2h);
373 int ret;
374
375 switch (msg_type) {
376 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200377 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200378 if (ret < 0) {
379 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
380 "message. Reason: %s\n", strerror(errno));
381 break;
382 }
383 ret = 1;
384 break;
385 case IPAC_MSGT_PONG:
386 DEBUGP(DLMI, "PONG!\n");
387 ret = 1;
388 break;
389 case IPAC_MSGT_ID_ACK:
390 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200391 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200392 if (ret < 0) {
393 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
394 "message. Reason: %s\n", strerror(errno));
395 break;
396 }
397 ret = 1;
398 break;
399 default:
400 /* This is not an IPA PING, PONG or ID_ACK message */
401 ret = 0;
402 break;
403 }
404 return ret;
405}
406
407/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200408int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200409{
410 uint8_t msg_type = *(msg->l2h);
411 int ret = 0;
412
413 switch (msg_type) {
414 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200415 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200416 if (ret < 0) {
417 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
418 "message. Reason: %s\n", strerror(errno));
419 }
420 break;
421 case IPAC_MSGT_PONG:
422 DEBUGP(DLMI, "PONG!\n");
423 break;
424 case IPAC_MSGT_ID_ACK:
425 DEBUGP(DLMI, "ID_ACK\n");
426 break;
427 }
428 return ret;
429}
430
431
Harald Weltee3919962014-08-20 22:28:23 +0200432void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200433{
434 struct ipaccess_head_ext *hh_ext;
435
436 /* prepend the osmo ip.access header extension */
437 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
438 hh_ext->proto = proto;
439}
440
Harald Weltee3919962014-08-20 22:28:23 +0200441void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200442{
443 struct ipaccess_head *hh;
444
445 /* prepend the ip.access header */
446 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
Harald Welte95871da2017-05-15 12:11:36 +0200447 hh->len = osmo_htons(msg->len - sizeof(*hh));
Harald Welte28aa9912014-08-20 22:06:04 +0200448 hh->proto = proto;
449}
450
Harald Welte20725b92017-05-15 12:50:04 +0200451#ifdef HAVE_SYS_SOCKET_H
452#include <sys/socket.h>
453
Harald Welte28aa9912014-08-20 22:06:04 +0200454int ipa_msg_recv(int fd, struct msgb **rmsg)
455{
456 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
457 if (rc < 0) {
458 errno = -rc;
459 rc = -1;
460 }
461 return rc;
462}
463
464int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
465{
466 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
467 struct ipaccess_head *hh;
468 int len, ret;
469 int needed;
470
471 if (msg == NULL) {
472 msg = ipa_msg_alloc(0);
473 if (msg == NULL) {
474 ret = -ENOMEM;
475 goto discard_msg;
476 }
477 msg->l1h = msg->tail;
478 }
479
480 if (msg->l2h == NULL) {
481 /* first read our 3-byte header */
482 needed = sizeof(*hh) - msg->len;
483 ret = recv(fd, msg->tail, needed, 0);
484 if (ret == 0)
485 goto discard_msg;
486
487 if (ret < 0) {
488 if (errno == EAGAIN || errno == EINTR)
489 ret = 0;
490 else {
491 ret = -errno;
492 goto discard_msg;
493 }
494 }
495
496 msgb_put(msg, ret);
497
498 if (ret < needed) {
499 if (msg->len == 0) {
500 ret = -EAGAIN;
501 goto discard_msg;
502 }
503
504 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200505 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200506 msg->len, sizeof(*hh));
507 if (!tmp_msg) {
508 ret = -EIO;
509 goto discard_msg;
510 }
511 *tmp_msg = msg;
512 return -EAGAIN;
513 }
514
515 msg->l2h = msg->tail;
516 }
517
518 hh = (struct ipaccess_head *) msg->data;
519
520 /* then read the length as specified in header */
Harald Welte95871da2017-05-15 12:11:36 +0200521 len = osmo_ntohs(hh->len);
Harald Welte28aa9912014-08-20 22:06:04 +0200522
523 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
524 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
525 "received %d bytes\n", len, msg->len);
526 ret = -EIO;
527 goto discard_msg;
528 }
529
530 needed = len - msgb_l2len(msg);
531
532 if (needed > 0) {
533 ret = recv(fd, msg->tail, needed, 0);
534
535 if (ret == 0)
536 goto discard_msg;
537
538 if (ret < 0) {
539 if (errno == EAGAIN || errno == EINTR)
540 ret = 0;
541 else {
542 ret = -errno;
543 goto discard_msg;
544 }
545 }
546
547 msgb_put(msg, ret);
548
549 if (ret < needed) {
550 LOGP(DLINP, LOGL_INFO,
551 "Received part of IPA message L2 data (%d/%d)\n",
552 msgb_l2len(msg), len);
553 if (!tmp_msg) {
554 ret = -EIO;
555 goto discard_msg;
556 }
557 *tmp_msg = msg;
558 return -EAGAIN;
559 }
560 }
561
562 ret = msgb_l2len(msg);
563
564 if (ret == 0) {
565 LOGP(DLINP, LOGL_INFO,
566 "Discarding IPA message without payload\n");
567 ret = -EAGAIN;
568 goto discard_msg;
569 }
570
571 if (tmp_msg)
572 *tmp_msg = NULL;
573 *rmsg = msg;
574 return ret;
575
576discard_msg:
577 if (tmp_msg)
578 *tmp_msg = NULL;
579 msgb_free(msg);
580 return ret;
581}
582
Harald Welte20725b92017-05-15 12:50:04 +0200583#endif /* SYS_SOCKET_H */
584
Harald Welte28aa9912014-08-20 22:06:04 +0200585struct msgb *ipa_msg_alloc(int headroom)
586{
587 struct msgb *nmsg;
588
589 headroom += sizeof(struct ipaccess_head);
590
Neels Hofmeyr889ab162017-09-07 20:41:12 +0200591 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "IPA Multiplex");
Harald Welte28aa9912014-08-20 22:06:04 +0200592 if (!nmsg)
593 return NULL;
594 return nmsg;
595}
Harald Welte96e2a002017-06-12 21:44:18 +0200596
597/*! @} */