blob: d423c262c9c5144d86b90589fcfcdd18b30daf69 [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{
Harald Welte5a7740d2018-08-01 17:29:48 +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
Harald Welte5a7740d2018-08-01 17:29:48 +0200119 if (t_len < len_offset) {
120 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
121 return -EINVAL;
122 }
123
Harald Welte7869baf2018-07-31 20:25:48 +0200124 if (t_len > len + 1) {
125 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Holger Hans Peter Freytherf558ed42015-06-02 15:52:06 +0200126 return -EINVAL;
127 }
128
Harald Welte7869baf2018-07-31 20:25:48 +0200129 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
130
Harald Welte5a7740d2018-08-01 17:29:48 +0200131 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200132 dec->lv[t_tag].val = cur;
133
Harald Welte5a7740d2018-08-01 17:29:48 +0200134 cur += t_len - len_offset;
135 len -= t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200136 }
137 return 0;
138}
139
140/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format.
141 * The odd payload format of those messages is structured as follows:
142 * * 8bit length value (length of payload *and tag*)
143 * * 8bit tag value
144 * * optional, variable-length payload
145 * \param[out] dec Caller-provided/allocated output structure for parsed payload
146 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
147 * \param[in] len Length of \a buf in octets
148 * \returns 0 on success; negative on error */
149int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
150{
151 uint8_t t_len;
152 uint8_t t_tag;
153 const uint8_t *cur = buf;
154
155 memset(dec, 0, sizeof(*dec));
156
157 while (len >= 2) {
158 len -= 2;
159 t_len = *cur++;
160 t_tag = *cur++;
161
Harald Welte28aa9912014-08-20 22:06:04 +0200162 if (t_len > len + 1) {
Max9b4d0652016-11-15 19:21:23 +0100163 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Harald Welte28aa9912014-08-20 22:06:04 +0200164 return -EINVAL;
165 }
166
Harald Weltee3919962014-08-20 22:28:23 +0200167 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200168
Harald Welte7869baf2018-07-31 20:25:48 +0200169 dec->lv[t_tag].len = t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200170 dec->lv[t_tag].val = cur;
171
Harald Welte7869baf2018-07-31 20:25:48 +0200172 cur += t_len-1;
173 len -= t_len-1;
174 }
175 return 0;
176}
177
178/*! Parse the payload part of an IPA CCM ID RESP, return \ref tlv_parsed format.
179 * The odd payload format of those messages is structured as follows:
180 * * 16bit length value (length of payload *and tag*)
181 * * 8bit tag value
182 * * optional, variable-length payload
183 * \param[out] dec Caller-provided/allocated output structure for parsed payload
184 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
185 * \param[in] len Length of \a buf in octets
186 * \returns 0 on success; negative on error */
187int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
188{
189 uint8_t t_len;
190 uint8_t t_tag;
191 const uint8_t *cur = buf;
192
193 memset(dec, 0, sizeof(*dec));
194
195 while (len >= 3) {
196 len -= 3;
197 t_len = *cur++ << 8;
198 t_len += *cur++;
199 t_tag = *cur++;
200
201 if (t_len > len + 1) {
202 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
203 return -EINVAL;
204 }
205
206 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
207
208 dec->lv[t_tag].len = t_len-1;
209 dec->lv[t_tag].val = cur;
210
211 cur += t_len-1;
212 len -= t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200213 }
214 return 0;
215}
216
Harald Weltee3919962014-08-20 22:28:23 +0200217int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200218{
219 unsigned long ul;
220 char *endptr;
221 const char *nptr;
222
223 nptr = str;
224 ul = strtoul(nptr, &endptr, 10);
225 if (endptr <= nptr)
226 return -EINVAL;
227 unit_data->site_id = ul & 0xffff;
228
229 if (*endptr++ != '/')
230 return -EINVAL;
231
232 nptr = endptr;
233 ul = strtoul(nptr, &endptr, 10);
234 if (endptr <= nptr)
235 return -EINVAL;
236 unit_data->bts_id = ul & 0xffff;
237
238 if (*endptr++ != '/')
239 return -EINVAL;
240
241 nptr = endptr;
242 ul = strtoul(nptr, &endptr, 10);
243 if (endptr <= nptr)
244 return -EINVAL;
245 unit_data->trx_id = ul & 0xffff;
246
247 return 0;
248}
249
Harald Weltee3919962014-08-20 22:28:23 +0200250int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200251 const struct tlv_parsed *tp)
252{
253 int rc = 0;
254
255 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
256 ud->serno = talloc_strdup(ud, (char *)
257 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
258
259 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
260 ud->unit_name = talloc_strdup(ud, (char *)
261 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
262
263 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
264 ud->location1 = talloc_strdup(ud, (char *)
265 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
266
267 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
268 ud->location2 = talloc_strdup(ud, (char *)
269 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
270
271 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
272 ud->equipvers = talloc_strdup(ud, (char *)
273 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
274
275 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
276 ud->swversion = talloc_strdup(ud, (char *)
277 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
278
279 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
280 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
281 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
282 if (rc < 0)
283 goto out;
284 }
285
286 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200287 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200288 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
289
290out:
291 return rc;
292}
293
Harald Welte7bc88bb2017-04-15 19:05:33 +0200294#define IPA_STRING_MAX 64
295
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200296/*! Generate IPA CCM ID RESP based on list of IEs
Harald Welte7bc88bb2017-04-15 19:05:33 +0200297 * \param[in] dev Descriptor describing identity data for response
298 * \param[in] ies_req List of IEIs to include in response
299 * \param[in] num_ies_req Number of IEIs in \a ies_req
300 * \returns Message buffer with IPA CCM ID RESP */
301struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev,
302 const uint8_t *ies_req, unsigned int num_ies_req)
303{
304 struct msgb *msg = ipa_msg_alloc(16);
305 char str[IPA_STRING_MAX];
306 unsigned int i;
307
308 if (!msg)
309 return NULL;
310
311 *msgb_put(msg, 1) = IPAC_MSGT_ID_RESP;
312
313 for (i = 0; i < num_ies_req; i++) {
314 uint8_t *tag;
315
316 str[0] = '\0';
317 switch (ies_req[i]) {
318 case IPAC_IDTAG_UNIT:
319 snprintf(str, sizeof(str), "%u/%u/%u",
320 dev->site_id, dev->bts_id, dev->trx_id);
321 break;
322 case IPAC_IDTAG_MACADDR:
323 snprintf(str, sizeof(str),
324 "%02x:%02x:%02x:%02x:%02x:%02x",
325 dev->mac_addr[0], dev->mac_addr[1],
326 dev->mac_addr[2], dev->mac_addr[3],
327 dev->mac_addr[4], dev->mac_addr[5]);
328 break;
329 case IPAC_IDTAG_LOCATION1:
330 if (dev->location1)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200331 osmo_strlcpy(str, dev->location1, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200332 break;
333 case IPAC_IDTAG_LOCATION2:
334 if (dev->location2)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200335 osmo_strlcpy(str, dev->location2, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200336 break;
337 case IPAC_IDTAG_EQUIPVERS:
338 if (dev->equipvers)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200339 osmo_strlcpy(str, dev->equipvers, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200340 break;
341 case IPAC_IDTAG_SWVERSION:
342 if (dev->swversion)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200343 osmo_strlcpy(str, dev->swversion, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200344 break;
345 case IPAC_IDTAG_UNITNAME:
346 if (dev->unit_name) {
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200347 snprintf(str, sizeof(str), "%s", dev->unit_name);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200348 } else {
349 snprintf(str, sizeof(str),
350 "%02x-%02x-%02x-%02x-%02x-%02x",
351 dev->mac_addr[0], dev->mac_addr[1],
352 dev->mac_addr[2], dev->mac_addr[3],
353 dev->mac_addr[4], dev->mac_addr[5]);
354 }
355 break;
356 case IPAC_IDTAG_SERNR:
357 if (dev->serno)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200358 osmo_strlcpy(str, dev->serno, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200359 break;
360 default:
361 LOGP(DLINP, LOGL_NOTICE,
362 "Unknown ipaccess tag 0x%02x\n", ies_req[i]);
363 msgb_free(msg);
364 return NULL;
365 }
Harald Welte7bc88bb2017-04-15 19:05:33 +0200366
367 LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", ies_req[i], str);
368 tag = msgb_put(msg, 3 + strlen(str) + 1);
369 tag[0] = 0x00;
370 tag[1] = 1 + strlen(str) + 1;
371 tag[2] = ies_req[1];
372 memcpy(tag + 3, str, strlen(str) + 1);
373 }
374 ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
375 return msg;
376}
377
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200378/*! Generate IPA CCM ID RESP based on requets payload
Harald Welte7bc88bb2017-04-15 19:05:33 +0200379 * \param[in] dev Descriptor describing identity data for response
380 * \param[in] data Payload of the IPA CCM ID GET request
381 * \param[in] len Length of \a data in octets
382 * \returns Message buffer with IPA CCM ID RESP */
383struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
384 const uint8_t *data, unsigned int len)
385{
386 uint8_t ies[len/2];
387 unsigned int num_ies = 0;
388 const uint8_t *cur = data;
389
Harald Welte8a4895c2017-04-27 10:25:10 +0200390 memset(ies, 0, sizeof(ies));
391
Harald Welte7bc88bb2017-04-15 19:05:33 +0200392 /* build a array of the IEIs */
393 while (len >= 2) {
394 uint8_t t_len, t_tag;
395 len -= 2;
396 t_len = *cur++;
397 t_tag = *cur++;
398
399 if (t_len > len + 1) {
Thorsten Alteholz5a9dbf82018-04-08 19:13:25 +0200400 LOGP(DLINP, LOGL_ERROR, "IPA CCM tag 0x%02x does not fit\n", t_tag);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200401 break;
402 }
403
404 ies[num_ies++] = t_tag;
405
406 cur += t_len;
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200407 /* prevent any unsigned integer underflow due to somebody sending us
408 * messages with wrong length values */
409 if (len <= t_len)
410 len -= t_len;
411 else
412 len = 0;
Harald Welte7bc88bb2017-04-15 19:05:33 +0200413 }
414 return ipa_ccm_make_id_resp(dev, ies, num_ies);
415}
416
Harald Weltee3919962014-08-20 22:28:23 +0200417int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200418{
419 int ret;
420
421 ret = write(fd, msg, msglen);
422 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100423 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200424 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200425 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200426 return -EIO;
427 }
428 return ret;
429}
430
Harald Weltee3919962014-08-20 22:28:23 +0200431int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200432{
Harald Weltee3919962014-08-20 22:28:23 +0200433 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200434}
435
Harald Weltee3919962014-08-20 22:28:23 +0200436int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200437{
Harald Weltee3919962014-08-20 22:28:23 +0200438 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200439}
440
Harald Weltee3919962014-08-20 22:28:23 +0200441int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200442{
Harald Weltee3919962014-08-20 22:28:23 +0200443 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200444}
445
446/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200447int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200448{
449 uint8_t msg_type = *(msg->l2h);
450 int ret;
451
452 switch (msg_type) {
453 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200454 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200455 if (ret < 0) {
456 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
457 "message. Reason: %s\n", strerror(errno));
458 break;
459 }
460 ret = 1;
461 break;
462 case IPAC_MSGT_PONG:
463 DEBUGP(DLMI, "PONG!\n");
464 ret = 1;
465 break;
466 case IPAC_MSGT_ID_ACK:
467 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200468 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200469 if (ret < 0) {
470 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
471 "message. Reason: %s\n", strerror(errno));
472 break;
473 }
474 ret = 1;
475 break;
476 default:
477 /* This is not an IPA PING, PONG or ID_ACK message */
478 ret = 0;
479 break;
480 }
481 return ret;
482}
483
484/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200485int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200486{
487 uint8_t msg_type = *(msg->l2h);
488 int ret = 0;
489
490 switch (msg_type) {
491 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200492 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200493 if (ret < 0) {
494 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
495 "message. Reason: %s\n", strerror(errno));
496 }
497 break;
498 case IPAC_MSGT_PONG:
499 DEBUGP(DLMI, "PONG!\n");
500 break;
501 case IPAC_MSGT_ID_ACK:
502 DEBUGP(DLMI, "ID_ACK\n");
503 break;
504 }
505 return ret;
506}
507
508
Harald Weltee3919962014-08-20 22:28:23 +0200509void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200510{
511 struct ipaccess_head_ext *hh_ext;
512
513 /* prepend the osmo ip.access header extension */
514 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
515 hh_ext->proto = proto;
516}
517
Harald Weltee3919962014-08-20 22:28:23 +0200518void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200519{
520 struct ipaccess_head *hh;
521
522 /* prepend the ip.access header */
523 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
Harald Welte95871da2017-05-15 12:11:36 +0200524 hh->len = osmo_htons(msg->len - sizeof(*hh));
Harald Welte28aa9912014-08-20 22:06:04 +0200525 hh->proto = proto;
526}
527
Harald Welte20725b92017-05-15 12:50:04 +0200528#ifdef HAVE_SYS_SOCKET_H
529#include <sys/socket.h>
530
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200531/*! Read one ipa message from socket fd without caching not fully received
532 * messages. See \ref ipa_msg_recv_buffered for further information.
533 */
Harald Welte28aa9912014-08-20 22:06:04 +0200534int ipa_msg_recv(int fd, struct msgb **rmsg)
535{
536 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
537 if (rc < 0) {
538 errno = -rc;
539 rc = -1;
540 }
541 return rc;
542}
543
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200544/*! Read one ipa message from socket fd or store part if still not fully received.
545 * \param[in] fd The fd for the socket to read from.
546 * \param[out] rmsg internally allocated msgb containing a fully received ipa message.
547 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
548 *
549 * As ipa can run on top of stream based protocols such as TCP, there's the
550 * possibility that such lower layers split ipa messages in several low level
551 * packets. If a low layer packet is received containing several ipa frames,
552 * this function will pull from the socket and return only the first one
553 * available in the stream. As the socket will remain with data, it will
554 * trigger again during next select() and then this function will fetch the
555 * next ipa message, and so on.
556 *
557 * \returns -EAGAIN and allocated tmp_msg if message was not yet fully
558 * received. Other negative values indicate an error and cached msgb will be
559 * freed. 0 if socket is found dead. Positive value indicating l2 msgb len and
560 * rmsg pointing to internally allocated msgb containing the ipa frame on
561 * scucess.
562 */
Harald Welte28aa9912014-08-20 22:06:04 +0200563int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
564{
565 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
566 struct ipaccess_head *hh;
567 int len, ret;
568 int needed;
569
570 if (msg == NULL) {
571 msg = ipa_msg_alloc(0);
572 if (msg == NULL) {
573 ret = -ENOMEM;
574 goto discard_msg;
575 }
576 msg->l1h = msg->tail;
577 }
578
579 if (msg->l2h == NULL) {
580 /* first read our 3-byte header */
581 needed = sizeof(*hh) - msg->len;
582 ret = recv(fd, msg->tail, needed, 0);
583 if (ret == 0)
584 goto discard_msg;
585
586 if (ret < 0) {
587 if (errno == EAGAIN || errno == EINTR)
588 ret = 0;
589 else {
590 ret = -errno;
591 goto discard_msg;
592 }
593 }
594
595 msgb_put(msg, ret);
596
597 if (ret < needed) {
598 if (msg->len == 0) {
599 ret = -EAGAIN;
600 goto discard_msg;
601 }
602
603 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200604 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200605 msg->len, sizeof(*hh));
606 if (!tmp_msg) {
607 ret = -EIO;
608 goto discard_msg;
609 }
610 *tmp_msg = msg;
611 return -EAGAIN;
612 }
613
614 msg->l2h = msg->tail;
615 }
616
617 hh = (struct ipaccess_head *) msg->data;
618
619 /* then read the length as specified in header */
Harald Welte95871da2017-05-15 12:11:36 +0200620 len = osmo_ntohs(hh->len);
Harald Welte28aa9912014-08-20 22:06:04 +0200621
622 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
623 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
624 "received %d bytes\n", len, msg->len);
625 ret = -EIO;
626 goto discard_msg;
627 }
628
629 needed = len - msgb_l2len(msg);
630
631 if (needed > 0) {
632 ret = recv(fd, msg->tail, needed, 0);
633
634 if (ret == 0)
635 goto discard_msg;
636
637 if (ret < 0) {
638 if (errno == EAGAIN || errno == EINTR)
639 ret = 0;
640 else {
641 ret = -errno;
642 goto discard_msg;
643 }
644 }
645
646 msgb_put(msg, ret);
647
648 if (ret < needed) {
649 LOGP(DLINP, LOGL_INFO,
650 "Received part of IPA message L2 data (%d/%d)\n",
651 msgb_l2len(msg), len);
652 if (!tmp_msg) {
653 ret = -EIO;
654 goto discard_msg;
655 }
656 *tmp_msg = msg;
657 return -EAGAIN;
658 }
659 }
660
661 ret = msgb_l2len(msg);
662
663 if (ret == 0) {
664 LOGP(DLINP, LOGL_INFO,
665 "Discarding IPA message without payload\n");
666 ret = -EAGAIN;
667 goto discard_msg;
668 }
669
670 if (tmp_msg)
671 *tmp_msg = NULL;
672 *rmsg = msg;
673 return ret;
674
675discard_msg:
676 if (tmp_msg)
677 *tmp_msg = NULL;
678 msgb_free(msg);
679 return ret;
680}
681
Harald Welte20725b92017-05-15 12:50:04 +0200682#endif /* SYS_SOCKET_H */
683
Harald Welte28aa9912014-08-20 22:06:04 +0200684struct msgb *ipa_msg_alloc(int headroom)
685{
686 struct msgb *nmsg;
687
688 headroom += sizeof(struct ipaccess_head);
689
Neels Hofmeyr889ab162017-09-07 20:41:12 +0200690 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "IPA Multiplex");
Harald Welte28aa9912014-08-20 22:06:04 +0200691 if (!nmsg)
692 return NULL;
693 return nmsg;
694}
Harald Welte96e2a002017-06-12 21:44:18 +0200695
696/*! @} */