blob: 93e34df270abe8753d57e7a0971c031272e13778 [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>
arehbein2c59d122023-04-11 13:30:29 +020033#include <inttypes.h>
Harald Welte28aa9912014-08-20 22:06:04 +020034
Holger Hans Peter Freytheree6652b2015-11-09 16:21:19 +000035#include <sys/types.h>
Harald Welte28aa9912014-08-20 22:06:04 +020036
Harald Welte95871da2017-05-15 12:11:36 +020037#include <osmocom/core/byteswap.h>
Harald Welte28aa9912014-08-20 22:06:04 +020038#include <osmocom/core/msgb.h>
39#include <osmocom/core/talloc.h>
40#include <osmocom/core/logging.h>
41#include <osmocom/core/macaddr.h>
42#include <osmocom/core/select.h>
43
44#include <osmocom/gsm/tlv.h>
45#include <osmocom/gsm/protocol/ipaccess.h>
Harald Weltee3919962014-08-20 22:28:23 +020046#include <osmocom/gsm/ipa.h>
Harald Welte28aa9912014-08-20 22:06:04 +020047
Harald Welte96e2a002017-06-12 21:44:18 +020048/*! \addtogroup ipa
49 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020050 * IPA Multiplex utility routines
Harald Welte96e2a002017-06-12 21:44:18 +020051 */
52
Harald Welte28aa9912014-08-20 22:06:04 +020053#define IPA_ALLOC_SIZE 1200
54
55/*
56 * Common propietary IPA messages:
57 * - PONG: in reply to PING.
58 * - ID_REQUEST: first messages once OML has been established.
59 * - ID_ACK: in reply to ID_ACK.
60 */
61static const uint8_t ipa_pong_msg[] = {
62 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
63};
64
65static const uint8_t ipa_id_ack_msg[] = {
66 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
67};
68
69static const uint8_t ipa_id_req_msg[] = {
70 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
71 0x01, IPAC_IDTAG_UNIT,
72 0x01, IPAC_IDTAG_MACADDR,
73 0x01, IPAC_IDTAG_LOCATION1,
74 0x01, IPAC_IDTAG_LOCATION2,
75 0x01, IPAC_IDTAG_EQUIPVERS,
76 0x01, IPAC_IDTAG_SWVERSION,
77 0x01, IPAC_IDTAG_UNITNAME,
78 0x01, IPAC_IDTAG_SERNR,
79};
80
81
82static const char *idtag_names[] = {
83 [IPAC_IDTAG_SERNR] = "Serial_Number",
84 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
85 [IPAC_IDTAG_LOCATION1] = "Location_1",
86 [IPAC_IDTAG_LOCATION2] = "Location_2",
87 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
88 [IPAC_IDTAG_SWVERSION] = "Software_Version",
89 [IPAC_IDTAG_IPADDR] = "IP_Address",
90 [IPAC_IDTAG_MACADDR] = "MAC_Address",
91 [IPAC_IDTAG_UNIT] = "Unit_ID",
92};
93
Harald Weltee3919962014-08-20 22:28:23 +020094const char *ipa_ccm_idtag_name(uint8_t tag)
Harald Welte28aa9912014-08-20 22:06:04 +020095{
96 if (tag >= ARRAY_SIZE(idtag_names))
97 return "unknown";
98
99 return idtag_names[tag];
100}
101
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100102/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format. */
Harald Weltee3919962014-08-20 22:28:23 +0200103int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
Harald Welte28aa9912014-08-20 22:06:04 +0200104{
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100105 return ipa_ccm_idtag_parse_off(dec, buf, len, 1);
Harald Welte5a7740d2018-08-01 17:29:48 +0200106}
107
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100108/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format.
109 * WARNING: This function can only parse correctly IPA CCM ID GET/REQUEST
110 * messages, and only when len_offset is passed value of 1.
111 * \param[out] dec Caller-provided/allocated output structure for parsed payload
112 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
113 * \param[in] len Length of \a buf in octets
114 * \param[in] len_offset Offset from end of len field to start of value (ommiting tag). Must be 1!
115 * \returns 0 on success; negative on error
116 */
Harald Welte5a7740d2018-08-01 17:29:48 +0200117int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
118{
Harald Welte28aa9912014-08-20 22:06:04 +0200119 uint8_t t_len;
120 uint8_t t_tag;
121 uint8_t *cur = buf;
122
123 memset(dec, 0, sizeof(*dec));
124
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200125 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
Harald Welte28aa9912014-08-20 22:06:04 +0200126 while (len >= 2) {
127 len -= 2;
128 t_len = *cur++;
129 t_tag = *cur++;
130
Harald Welte5a7740d2018-08-01 17:29:48 +0200131 if (t_len < len_offset) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200132 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte5a7740d2018-08-01 17:29:48 +0200133 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
134 return -EINVAL;
135 }
136
Harald Welte7869baf2018-07-31 20:25:48 +0200137 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200138 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200139 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 +0200140 return -EINVAL;
141 }
142
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200143 LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte7869baf2018-07-31 20:25:48 +0200144
Oliver Smith27f7b0d2019-03-25 13:45:57 +0100145 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200146 dec->lv[t_tag].val = cur;
147
Harald Welte5a7740d2018-08-01 17:29:48 +0200148 cur += t_len - len_offset;
149 len -= t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200150 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200151 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200152 return 0;
153}
154
155/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format.
156 * The odd payload format of those messages is structured as follows:
157 * * 8bit length value (length of payload *and tag*)
158 * * 8bit tag value
159 * * optional, variable-length payload
160 * \param[out] dec Caller-provided/allocated output structure for parsed payload
161 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
162 * \param[in] len Length of \a buf in octets
163 * \returns 0 on success; negative on error */
164int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
165{
166 uint8_t t_len;
167 uint8_t t_tag;
168 const uint8_t *cur = buf;
169
170 memset(dec, 0, sizeof(*dec));
171
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200172 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
Harald Welte7869baf2018-07-31 20:25:48 +0200173 while (len >= 2) {
174 len -= 2;
175 t_len = *cur++;
176 t_tag = *cur++;
177
Harald Welte28aa9912014-08-20 22:06:04 +0200178 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200179 LOGPC(DLMI, LOGL_DEBUG, "\n");
Max9b4d0652016-11-15 19:21:23 +0100180 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Harald Welte28aa9912014-08-20 22:06:04 +0200181 return -EINVAL;
182 }
183
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200184 LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200185
Harald Welte7869baf2018-07-31 20:25:48 +0200186 dec->lv[t_tag].len = t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200187 dec->lv[t_tag].val = cur;
188
Harald Welte7869baf2018-07-31 20:25:48 +0200189 cur += t_len-1;
190 len -= t_len-1;
191 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200192 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200193 return 0;
194}
195
196/*! Parse the payload part of an IPA CCM ID RESP, return \ref tlv_parsed format.
197 * The odd payload format of those messages is structured as follows:
198 * * 16bit length value (length of payload *and tag*)
199 * * 8bit tag value
200 * * optional, variable-length payload
201 * \param[out] dec Caller-provided/allocated output structure for parsed payload
202 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
203 * \param[in] len Length of \a buf in octets
204 * \returns 0 on success; negative on error */
205int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
206{
207 uint8_t t_len;
208 uint8_t t_tag;
209 const uint8_t *cur = buf;
210
211 memset(dec, 0, sizeof(*dec));
212
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200213 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_RESP: ");
Harald Welte7869baf2018-07-31 20:25:48 +0200214 while (len >= 3) {
215 len -= 3;
Pau Espin Pedrol3cb68512019-03-27 17:45:00 +0100216 t_len = osmo_load16be(cur);
217 cur += 2;
Harald Welte7869baf2018-07-31 20:25:48 +0200218 t_tag = *cur++;
219
220 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200221 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200222 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
223 return -EINVAL;
224 }
225
226 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
227
228 dec->lv[t_tag].len = t_len-1;
229 dec->lv[t_tag].val = cur;
230
231 cur += t_len-1;
232 len -= t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200233 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200234 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200235 return 0;
236}
237
Harald Weltee3919962014-08-20 22:28:23 +0200238int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200239{
240 unsigned long ul;
241 char *endptr;
242 const char *nptr;
243
244 nptr = str;
245 ul = strtoul(nptr, &endptr, 10);
246 if (endptr <= nptr)
247 return -EINVAL;
248 unit_data->site_id = ul & 0xffff;
249
250 if (*endptr++ != '/')
251 return -EINVAL;
252
253 nptr = endptr;
254 ul = strtoul(nptr, &endptr, 10);
255 if (endptr <= nptr)
256 return -EINVAL;
257 unit_data->bts_id = ul & 0xffff;
258
259 if (*endptr++ != '/')
260 return -EINVAL;
261
262 nptr = endptr;
263 ul = strtoul(nptr, &endptr, 10);
264 if (endptr <= nptr)
265 return -EINVAL;
266 unit_data->trx_id = ul & 0xffff;
267
268 return 0;
269}
270
Harald Weltee3919962014-08-20 22:28:23 +0200271int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200272 const struct tlv_parsed *tp)
273{
274 int rc = 0;
275
276 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
277 ud->serno = talloc_strdup(ud, (char *)
278 TLVP_VAL(tp, IPAC_IDTAG_SERNR));
279
280 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
281 ud->unit_name = talloc_strdup(ud, (char *)
282 TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
283
284 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
285 ud->location1 = talloc_strdup(ud, (char *)
286 TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
287
288 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
289 ud->location2 = talloc_strdup(ud, (char *)
290 TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
291
292 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
293 ud->equipvers = talloc_strdup(ud, (char *)
294 TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
295
296 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
297 ud->swversion = talloc_strdup(ud, (char *)
298 TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
299
300 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
301 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
302 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
303 if (rc < 0)
304 goto out;
305 }
306
307 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200308 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200309 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
310
311out:
312 return rc;
313}
314
Harald Welte7bc88bb2017-04-15 19:05:33 +0200315#define IPA_STRING_MAX 64
316
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200317/*! Generate IPA CCM ID RESP based on list of IEs
Harald Welte7bc88bb2017-04-15 19:05:33 +0200318 * \param[in] dev Descriptor describing identity data for response
319 * \param[in] ies_req List of IEIs to include in response
320 * \param[in] num_ies_req Number of IEIs in \a ies_req
321 * \returns Message buffer with IPA CCM ID RESP */
322struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev,
323 const uint8_t *ies_req, unsigned int num_ies_req)
324{
325 struct msgb *msg = ipa_msg_alloc(16);
326 char str[IPA_STRING_MAX];
327 unsigned int i;
328
329 if (!msg)
330 return NULL;
331
332 *msgb_put(msg, 1) = IPAC_MSGT_ID_RESP;
333
334 for (i = 0; i < num_ies_req; i++) {
335 uint8_t *tag;
336
337 str[0] = '\0';
338 switch (ies_req[i]) {
339 case IPAC_IDTAG_UNIT:
340 snprintf(str, sizeof(str), "%u/%u/%u",
341 dev->site_id, dev->bts_id, dev->trx_id);
342 break;
343 case IPAC_IDTAG_MACADDR:
344 snprintf(str, sizeof(str),
345 "%02x:%02x:%02x:%02x:%02x:%02x",
346 dev->mac_addr[0], dev->mac_addr[1],
347 dev->mac_addr[2], dev->mac_addr[3],
348 dev->mac_addr[4], dev->mac_addr[5]);
349 break;
350 case IPAC_IDTAG_LOCATION1:
351 if (dev->location1)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200352 osmo_strlcpy(str, dev->location1, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200353 break;
354 case IPAC_IDTAG_LOCATION2:
355 if (dev->location2)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200356 osmo_strlcpy(str, dev->location2, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200357 break;
358 case IPAC_IDTAG_EQUIPVERS:
359 if (dev->equipvers)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200360 osmo_strlcpy(str, dev->equipvers, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200361 break;
362 case IPAC_IDTAG_SWVERSION:
363 if (dev->swversion)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200364 osmo_strlcpy(str, dev->swversion, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200365 break;
366 case IPAC_IDTAG_UNITNAME:
367 if (dev->unit_name) {
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200368 snprintf(str, sizeof(str), "%s", dev->unit_name);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200369 } else {
370 snprintf(str, sizeof(str),
371 "%02x-%02x-%02x-%02x-%02x-%02x",
372 dev->mac_addr[0], dev->mac_addr[1],
373 dev->mac_addr[2], dev->mac_addr[3],
374 dev->mac_addr[4], dev->mac_addr[5]);
375 }
376 break;
377 case IPAC_IDTAG_SERNR:
378 if (dev->serno)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200379 osmo_strlcpy(str, dev->serno, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200380 break;
381 default:
382 LOGP(DLINP, LOGL_NOTICE,
383 "Unknown ipaccess tag 0x%02x\n", ies_req[i]);
384 msgb_free(msg);
385 return NULL;
386 }
Harald Welte7bc88bb2017-04-15 19:05:33 +0200387
388 LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", ies_req[i], str);
389 tag = msgb_put(msg, 3 + strlen(str) + 1);
390 tag[0] = 0x00;
391 tag[1] = 1 + strlen(str) + 1;
Harald Weltea5458422021-04-29 18:39:52 +0200392 tag[2] = ies_req[i];
Harald Welte7bc88bb2017-04-15 19:05:33 +0200393 memcpy(tag + 3, str, strlen(str) + 1);
394 }
395 ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
396 return msg;
397}
398
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200399/*! Generate IPA CCM ID RESP based on requets payload
Harald Welte7bc88bb2017-04-15 19:05:33 +0200400 * \param[in] dev Descriptor describing identity data for response
401 * \param[in] data Payload of the IPA CCM ID GET request
402 * \param[in] len Length of \a data in octets
403 * \returns Message buffer with IPA CCM ID RESP */
404struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
405 const uint8_t *data, unsigned int len)
406{
407 uint8_t ies[len/2];
408 unsigned int num_ies = 0;
409 const uint8_t *cur = data;
410
Harald Welte8a4895c2017-04-27 10:25:10 +0200411 memset(ies, 0, sizeof(ies));
412
Harald Welte7bc88bb2017-04-15 19:05:33 +0200413 /* build a array of the IEIs */
414 while (len >= 2) {
415 uint8_t t_len, t_tag;
Harald Welteb189b5f2021-04-29 18:38:48 +0200416 len -= 2; /* subtract the length of the two bytes read below */
Harald Welte7bc88bb2017-04-15 19:05:33 +0200417 t_len = *cur++;
418 t_tag = *cur++;
419
Harald Welteb189b5f2021-04-29 18:38:48 +0200420 /* as the 'tag' is included in the length of t_len, this cannot happen */
421 if (t_len == 0)
422 break;
423
Harald Welte7bc88bb2017-04-15 19:05:33 +0200424 if (t_len > len + 1) {
Thorsten Alteholz5a9dbf82018-04-08 19:13:25 +0200425 LOGP(DLINP, LOGL_ERROR, "IPA CCM tag 0x%02x does not fit\n", t_tag);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200426 break;
427 }
428
429 ies[num_ies++] = t_tag;
430
Harald Welteb189b5f2021-04-29 18:38:48 +0200431 /* we need to subtract one from t_len to account for the tag */
432 cur += t_len - 1;
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200433 /* prevent any unsigned integer underflow due to somebody sending us
434 * messages with wrong length values */
435 if (len <= t_len)
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200436 len = 0;
Harald Welte539272d2021-04-29 15:52:38 +0200437 else
Harald Welteb189b5f2021-04-29 18:38:48 +0200438 len -= t_len - 1;
Harald Welte7bc88bb2017-04-15 19:05:33 +0200439 }
440 return ipa_ccm_make_id_resp(dev, ies, num_ies);
441}
442
Harald Weltee3919962014-08-20 22:28:23 +0200443int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200444{
445 int ret;
446
447 ret = write(fd, msg, msglen);
448 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100449 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200450 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200451 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200452 return -EIO;
453 }
454 return ret;
455}
456
Harald Weltee3919962014-08-20 22:28:23 +0200457int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200458{
Harald Weltee3919962014-08-20 22:28:23 +0200459 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200460}
461
Harald Weltee3919962014-08-20 22:28:23 +0200462int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200463{
Harald Weltee3919962014-08-20 22:28:23 +0200464 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200465}
466
Harald Weltee3919962014-08-20 22:28:23 +0200467int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200468{
Harald Weltee3919962014-08-20 22:28:23 +0200469 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200470}
471
472/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200473int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200474{
475 uint8_t msg_type = *(msg->l2h);
476 int ret;
477
478 switch (msg_type) {
479 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200480 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200481 if (ret < 0) {
482 LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
483 "message. Reason: %s\n", strerror(errno));
484 break;
485 }
486 ret = 1;
487 break;
488 case IPAC_MSGT_PONG:
489 DEBUGP(DLMI, "PONG!\n");
490 ret = 1;
491 break;
492 case IPAC_MSGT_ID_ACK:
493 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200494 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200495 if (ret < 0) {
496 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
497 "message. Reason: %s\n", strerror(errno));
498 break;
499 }
500 ret = 1;
501 break;
502 default:
503 /* This is not an IPA PING, PONG or ID_ACK message */
504 ret = 0;
505 break;
506 }
507 return ret;
508}
509
510/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200511int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200512{
513 uint8_t msg_type = *(msg->l2h);
514 int ret = 0;
515
516 switch (msg_type) {
517 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200518 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200519 if (ret < 0) {
520 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
521 "message. Reason: %s\n", strerror(errno));
522 }
523 break;
524 case IPAC_MSGT_PONG:
525 DEBUGP(DLMI, "PONG!\n");
526 break;
527 case IPAC_MSGT_ID_ACK:
528 DEBUGP(DLMI, "ID_ACK\n");
529 break;
530 }
531 return ret;
532}
533
534
Harald Weltee3919962014-08-20 22:28:23 +0200535void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200536{
537 struct ipaccess_head_ext *hh_ext;
538
539 /* prepend the osmo ip.access header extension */
540 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
541 hh_ext->proto = proto;
542}
543
Harald Weltee3919962014-08-20 22:28:23 +0200544void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200545{
546 struct ipaccess_head *hh;
547
548 /* prepend the ip.access header */
549 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
Harald Welte95871da2017-05-15 12:11:36 +0200550 hh->len = osmo_htons(msg->len - sizeof(*hh));
Harald Welte28aa9912014-08-20 22:06:04 +0200551 hh->proto = proto;
552}
553
Harald Welte20725b92017-05-15 12:50:04 +0200554#ifdef HAVE_SYS_SOCKET_H
555#include <sys/socket.h>
556
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200557/*! Read one ipa message from socket fd without caching not fully received
558 * messages. See \ref ipa_msg_recv_buffered for further information.
559 */
Harald Welte28aa9912014-08-20 22:06:04 +0200560int ipa_msg_recv(int fd, struct msgb **rmsg)
561{
562 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
563 if (rc < 0) {
564 errno = -rc;
565 rc = -1;
566 }
567 return rc;
568}
569
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200570/*! Read one ipa message from socket fd or store part if still not fully received.
571 * \param[in] fd The fd for the socket to read from.
572 * \param[out] rmsg internally allocated msgb containing a fully received ipa message.
573 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
574 *
575 * As ipa can run on top of stream based protocols such as TCP, there's the
576 * possibility that such lower layers split ipa messages in several low level
577 * packets. If a low layer packet is received containing several ipa frames,
578 * this function will pull from the socket and return only the first one
579 * available in the stream. As the socket will remain with data, it will
580 * trigger again during next select() and then this function will fetch the
581 * next ipa message, and so on.
582 *
583 * \returns -EAGAIN and allocated tmp_msg if message was not yet fully
584 * received. Other negative values indicate an error and cached msgb will be
585 * freed. 0 if socket is found dead. Positive value indicating l2 msgb len and
586 * rmsg pointing to internally allocated msgb containing the ipa frame on
587 * scucess.
588 */
Harald Welte28aa9912014-08-20 22:06:04 +0200589int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
590{
591 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
592 struct ipaccess_head *hh;
593 int len, ret;
594 int needed;
595
596 if (msg == NULL) {
597 msg = ipa_msg_alloc(0);
598 if (msg == NULL) {
599 ret = -ENOMEM;
600 goto discard_msg;
601 }
602 msg->l1h = msg->tail;
603 }
604
605 if (msg->l2h == NULL) {
606 /* first read our 3-byte header */
607 needed = sizeof(*hh) - msg->len;
608 ret = recv(fd, msg->tail, needed, 0);
609 if (ret == 0)
610 goto discard_msg;
611
612 if (ret < 0) {
613 if (errno == EAGAIN || errno == EINTR)
614 ret = 0;
615 else {
616 ret = -errno;
617 goto discard_msg;
618 }
619 }
620
621 msgb_put(msg, ret);
622
623 if (ret < needed) {
624 if (msg->len == 0) {
625 ret = -EAGAIN;
626 goto discard_msg;
627 }
628
629 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200630 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200631 msg->len, sizeof(*hh));
632 if (!tmp_msg) {
633 ret = -EIO;
634 goto discard_msg;
635 }
636 *tmp_msg = msg;
637 return -EAGAIN;
638 }
639
640 msg->l2h = msg->tail;
641 }
642
643 hh = (struct ipaccess_head *) msg->data;
644
645 /* then read the length as specified in header */
Harald Welte95871da2017-05-15 12:11:36 +0200646 len = osmo_ntohs(hh->len);
Harald Welte28aa9912014-08-20 22:06:04 +0200647
648 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
649 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
650 "received %d bytes\n", len, msg->len);
651 ret = -EIO;
652 goto discard_msg;
653 }
654
655 needed = len - msgb_l2len(msg);
656
657 if (needed > 0) {
658 ret = recv(fd, msg->tail, needed, 0);
659
660 if (ret == 0)
661 goto discard_msg;
662
663 if (ret < 0) {
664 if (errno == EAGAIN || errno == EINTR)
665 ret = 0;
666 else {
667 ret = -errno;
668 goto discard_msg;
669 }
670 }
671
672 msgb_put(msg, ret);
673
674 if (ret < needed) {
675 LOGP(DLINP, LOGL_INFO,
676 "Received part of IPA message L2 data (%d/%d)\n",
677 msgb_l2len(msg), len);
678 if (!tmp_msg) {
679 ret = -EIO;
680 goto discard_msg;
681 }
682 *tmp_msg = msg;
683 return -EAGAIN;
684 }
685 }
686
687 ret = msgb_l2len(msg);
688
689 if (ret == 0) {
690 LOGP(DLINP, LOGL_INFO,
691 "Discarding IPA message without payload\n");
692 ret = -EAGAIN;
693 goto discard_msg;
694 }
695
696 if (tmp_msg)
697 *tmp_msg = NULL;
698 *rmsg = msg;
699 return ret;
700
701discard_msg:
702 if (tmp_msg)
703 *tmp_msg = NULL;
704 msgb_free(msg);
705 return ret;
706}
707
Harald Welte20725b92017-05-15 12:50:04 +0200708#endif /* SYS_SOCKET_H */
709
Harald Welte28aa9912014-08-20 22:06:04 +0200710struct msgb *ipa_msg_alloc(int headroom)
711{
712 struct msgb *nmsg;
713
714 headroom += sizeof(struct ipaccess_head);
715
Neels Hofmeyr889ab162017-09-07 20:41:12 +0200716 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "IPA Multiplex");
Harald Welte28aa9912014-08-20 22:06:04 +0200717 if (!nmsg)
718 return NULL;
719 return nmsg;
720}
Harald Welte96e2a002017-06-12 21:44:18 +0200721
arehbein2c59d122023-04-11 13:30:29 +0200722/*! Segmentation callback used by libosmo-netif streaming backend
723 * See definition of `struct osmo_io_ops` for callback semantics
724 * \param[in] msg Original `struct msgb` received via osmo_io
725 * \returns The total packet length indicated by the first header,
726 * otherwise
727 * -EAGAIN, if the header has not been read yet,
728 * -ENOBUFS, if the header declares a payload too large
729 */
730int ipa_segmentation_cb(struct msgb *msg)
731{
732 const struct ipaccess_head *hh = (const struct ipaccess_head *) msg->data;
733 size_t payload_len, total_len;
734 size_t available = msgb_length(msg) + msgb_tailroom(msg);
735 if (msgb_length(msg) < sizeof(*hh)) {
736 /* Haven't even read the entire header */
737 return -EAGAIN;
738 }
739 payload_len = osmo_ntohs(hh->len);
740 total_len = sizeof(*hh) + payload_len;
741 if (OSMO_UNLIKELY(available < total_len)) {
742 LOGP(DLINP, LOGL_ERROR, "Not enough space left in message buffer. "
743 "Have %zu octets, but need %zu\n",
744 available, total_len);
745 return -ENOBUFS;
746 }
747 return total_len;
748}
749
Harald Welte96e2a002017-06-12 21:44:18 +0200750/*! @} */