blob: 6e41fd988b2c1d2d994f3906465ecaaac6884fae [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
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100101/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format. */
Harald Weltee3919962014-08-20 22:28:23 +0200102int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
Harald Welte28aa9912014-08-20 22:06:04 +0200103{
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100104 return ipa_ccm_idtag_parse_off(dec, buf, len, 1);
Harald Welte5a7740d2018-08-01 17:29:48 +0200105}
106
Pau Espin Pedroldeeab472019-03-27 17:33:17 +0100107/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format.
108 * WARNING: This function can only parse correctly IPA CCM ID GET/REQUEST
109 * messages, and only when len_offset is passed value of 1.
110 * \param[out] dec Caller-provided/allocated output structure for parsed payload
111 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
112 * \param[in] len Length of \a buf in octets
113 * \param[in] len_offset Offset from end of len field to start of value (ommiting tag). Must be 1!
114 * \returns 0 on success; negative on error
115 */
Harald Welte5a7740d2018-08-01 17:29:48 +0200116int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
117{
Harald Welte28aa9912014-08-20 22:06:04 +0200118 uint8_t t_len;
119 uint8_t t_tag;
120 uint8_t *cur = buf;
121
122 memset(dec, 0, sizeof(*dec));
123
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200124 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
Harald Welte28aa9912014-08-20 22:06:04 +0200125 while (len >= 2) {
126 len -= 2;
127 t_len = *cur++;
128 t_tag = *cur++;
129
Harald Welte5a7740d2018-08-01 17:29:48 +0200130 if (t_len < len_offset) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200131 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte5a7740d2018-08-01 17:29:48 +0200132 LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
133 return -EINVAL;
134 }
135
Harald Welte7869baf2018-07-31 20:25:48 +0200136 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200137 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200138 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 +0200139 return -EINVAL;
140 }
141
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200142 LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte7869baf2018-07-31 20:25:48 +0200143
Oliver Smith27f7b0d2019-03-25 13:45:57 +0100144 dec->lv[t_tag].len = t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200145 dec->lv[t_tag].val = cur;
146
Harald Welte5a7740d2018-08-01 17:29:48 +0200147 cur += t_len - len_offset;
148 len -= t_len - len_offset;
Harald Welte7869baf2018-07-31 20:25:48 +0200149 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200150 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200151 return 0;
152}
153
154/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format.
155 * The odd payload format of those messages is structured as follows:
156 * * 8bit length value (length of payload *and tag*)
157 * * 8bit tag value
158 * * optional, variable-length payload
159 * \param[out] dec Caller-provided/allocated output structure for parsed payload
160 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
161 * \param[in] len Length of \a buf in octets
162 * \returns 0 on success; negative on error */
163int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
164{
165 uint8_t t_len;
166 uint8_t t_tag;
167 const uint8_t *cur = buf;
168
169 memset(dec, 0, sizeof(*dec));
170
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200171 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
Harald Welte7869baf2018-07-31 20:25:48 +0200172 while (len >= 2) {
173 len -= 2;
174 t_len = *cur++;
175 t_tag = *cur++;
176
Harald Welte28aa9912014-08-20 22:06:04 +0200177 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200178 LOGPC(DLMI, LOGL_DEBUG, "\n");
Max9b4d0652016-11-15 19:21:23 +0100179 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
Harald Welte28aa9912014-08-20 22:06:04 +0200180 return -EINVAL;
181 }
182
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200183 LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte28aa9912014-08-20 22:06:04 +0200184
Harald Welte7869baf2018-07-31 20:25:48 +0200185 dec->lv[t_tag].len = t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200186 dec->lv[t_tag].val = cur;
187
Harald Welte7869baf2018-07-31 20:25:48 +0200188 cur += t_len-1;
189 len -= t_len-1;
190 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200191 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200192 return 0;
193}
194
195/*! Parse the payload part of an IPA CCM ID RESP, return \ref tlv_parsed format.
196 * The odd payload format of those messages is structured as follows:
197 * * 16bit length value (length of payload *and tag*)
198 * * 8bit tag value
199 * * optional, variable-length payload
200 * \param[out] dec Caller-provided/allocated output structure for parsed payload
201 * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message
202 * \param[in] len Length of \a buf in octets
203 * \returns 0 on success; negative on error */
204int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len)
205{
206 uint8_t t_len;
207 uint8_t t_tag;
208 const uint8_t *cur = buf;
209
210 memset(dec, 0, sizeof(*dec));
211
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200212 LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_RESP: ");
Harald Welte7869baf2018-07-31 20:25:48 +0200213 while (len >= 3) {
214 len -= 3;
Pau Espin Pedrol3cb68512019-03-27 17:45:00 +0100215 t_len = osmo_load16be(cur);
216 cur += 2;
Harald Welte7869baf2018-07-31 20:25:48 +0200217 t_tag = *cur++;
218
219 if (t_len > len + 1) {
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200220 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte7869baf2018-07-31 20:25:48 +0200221 LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
222 return -EINVAL;
223 }
224
225 DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
226
227 dec->lv[t_tag].len = t_len-1;
228 dec->lv[t_tag].val = cur;
229
230 cur += t_len-1;
231 len -= t_len-1;
Harald Welte28aa9912014-08-20 22:06:04 +0200232 }
Pau Espin Pedrol9cc661b2020-08-26 13:29:47 +0200233 LOGPC(DLMI, LOGL_DEBUG, "\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200234 return 0;
235}
236
Harald Weltee3919962014-08-20 22:28:23 +0200237int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
Harald Welte28aa9912014-08-20 22:06:04 +0200238{
239 unsigned long ul;
240 char *endptr;
241 const char *nptr;
242
243 nptr = str;
244 ul = strtoul(nptr, &endptr, 10);
245 if (endptr <= nptr)
246 return -EINVAL;
247 unit_data->site_id = ul & 0xffff;
248
249 if (*endptr++ != '/')
250 return -EINVAL;
251
252 nptr = endptr;
253 ul = strtoul(nptr, &endptr, 10);
254 if (endptr <= nptr)
255 return -EINVAL;
256 unit_data->bts_id = ul & 0xffff;
257
258 if (*endptr++ != '/')
259 return -EINVAL;
260
261 nptr = endptr;
262 ul = strtoul(nptr, &endptr, 10);
263 if (endptr <= nptr)
264 return -EINVAL;
265 unit_data->trx_id = ul & 0xffff;
266
267 return 0;
268}
269
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100270/*! Fill ud struct from tp structure.
271 * \param[in,out] ud ipaccess_unit to fill
272 * \param[in] tp the decoded TLV structure from eg. ID_RESP message
273 * \returns zero on success, negative on error
274 *
275 * This function expects parameter ud's fields to be initialized to zero if not yet set.
276 * Existing incoming string pointer fields are expected to be allocated using
277 * talloc and will be deallocated as such if replaced with the content of tp.
278 **/
Harald Weltee3919962014-08-20 22:28:23 +0200279int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
Harald Welte28aa9912014-08-20 22:06:04 +0200280 const struct tlv_parsed *tp)
281{
282 int rc = 0;
283
284 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100285 osmo_talloc_replace_string(ud, &ud->serno,
286 (char *)TLVP_VAL(tp, IPAC_IDTAG_SERNR));
Harald Welte28aa9912014-08-20 22:06:04 +0200287
288 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100289 osmo_talloc_replace_string(ud, &ud->unit_name,
290 (char *)TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
Harald Welte28aa9912014-08-20 22:06:04 +0200291
292 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100293 osmo_talloc_replace_string(ud, &ud->location1,
294 (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
Harald Welte28aa9912014-08-20 22:06:04 +0200295
296 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100297 osmo_talloc_replace_string(ud, &ud->location2,
298 (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
Harald Welte28aa9912014-08-20 22:06:04 +0200299
300 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100301 osmo_talloc_replace_string(ud, &ud->equipvers,
302 (char *)TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
Harald Welte28aa9912014-08-20 22:06:04 +0200303
304 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
Pau Espin Pedrolca540be2023-12-12 17:22:39 +0100305 osmo_talloc_replace_string(ud, &ud->swversion,
306 (char *)TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
Harald Welte28aa9912014-08-20 22:06:04 +0200307
308 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
309 rc = osmo_macaddr_parse(ud->mac_addr, (char *)
310 TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
311 if (rc < 0)
312 goto out;
313 }
314
315 if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
Harald Weltee3919962014-08-20 22:28:23 +0200316 rc = ipa_parse_unitid((char *)
Harald Welte28aa9912014-08-20 22:06:04 +0200317 TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
318
319out:
320 return rc;
321}
322
Harald Welte7bc88bb2017-04-15 19:05:33 +0200323#define IPA_STRING_MAX 64
324
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200325/*! Generate IPA CCM ID RESP based on list of IEs
Harald Welte7bc88bb2017-04-15 19:05:33 +0200326 * \param[in] dev Descriptor describing identity data for response
327 * \param[in] ies_req List of IEIs to include in response
328 * \param[in] num_ies_req Number of IEIs in \a ies_req
329 * \returns Message buffer with IPA CCM ID RESP */
330struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev,
331 const uint8_t *ies_req, unsigned int num_ies_req)
332{
333 struct msgb *msg = ipa_msg_alloc(16);
334 char str[IPA_STRING_MAX];
335 unsigned int i;
336
337 if (!msg)
338 return NULL;
339
340 *msgb_put(msg, 1) = IPAC_MSGT_ID_RESP;
341
342 for (i = 0; i < num_ies_req; i++) {
343 uint8_t *tag;
344
345 str[0] = '\0';
346 switch (ies_req[i]) {
347 case IPAC_IDTAG_UNIT:
348 snprintf(str, sizeof(str), "%u/%u/%u",
349 dev->site_id, dev->bts_id, dev->trx_id);
350 break;
351 case IPAC_IDTAG_MACADDR:
352 snprintf(str, sizeof(str),
353 "%02x:%02x:%02x:%02x:%02x:%02x",
354 dev->mac_addr[0], dev->mac_addr[1],
355 dev->mac_addr[2], dev->mac_addr[3],
356 dev->mac_addr[4], dev->mac_addr[5]);
357 break;
358 case IPAC_IDTAG_LOCATION1:
359 if (dev->location1)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200360 osmo_strlcpy(str, dev->location1, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200361 break;
362 case IPAC_IDTAG_LOCATION2:
363 if (dev->location2)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200364 osmo_strlcpy(str, dev->location2, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200365 break;
366 case IPAC_IDTAG_EQUIPVERS:
367 if (dev->equipvers)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200368 osmo_strlcpy(str, dev->equipvers, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200369 break;
370 case IPAC_IDTAG_SWVERSION:
371 if (dev->swversion)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200372 osmo_strlcpy(str, dev->swversion, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200373 break;
374 case IPAC_IDTAG_UNITNAME:
375 if (dev->unit_name) {
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200376 snprintf(str, sizeof(str), "%s", dev->unit_name);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200377 } else {
378 snprintf(str, sizeof(str),
379 "%02x-%02x-%02x-%02x-%02x-%02x",
380 dev->mac_addr[0], dev->mac_addr[1],
381 dev->mac_addr[2], dev->mac_addr[3],
382 dev->mac_addr[4], dev->mac_addr[5]);
383 }
384 break;
385 case IPAC_IDTAG_SERNR:
386 if (dev->serno)
Neels Hofmeyrebe4ef72018-07-26 17:12:07 +0200387 osmo_strlcpy(str, dev->serno, sizeof(str));
Harald Welte7bc88bb2017-04-15 19:05:33 +0200388 break;
389 default:
390 LOGP(DLINP, LOGL_NOTICE,
391 "Unknown ipaccess tag 0x%02x\n", ies_req[i]);
392 msgb_free(msg);
393 return NULL;
394 }
Harald Welte7bc88bb2017-04-15 19:05:33 +0200395
396 LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", ies_req[i], str);
397 tag = msgb_put(msg, 3 + strlen(str) + 1);
398 tag[0] = 0x00;
399 tag[1] = 1 + strlen(str) + 1;
Harald Weltea5458422021-04-29 18:39:52 +0200400 tag[2] = ies_req[i];
Harald Welte7bc88bb2017-04-15 19:05:33 +0200401 memcpy(tag + 3, str, strlen(str) + 1);
402 }
403 ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
404 return msg;
405}
406
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200407/*! Generate IPA CCM ID RESP based on requets payload
Harald Welte7bc88bb2017-04-15 19:05:33 +0200408 * \param[in] dev Descriptor describing identity data for response
409 * \param[in] data Payload of the IPA CCM ID GET request
410 * \param[in] len Length of \a data in octets
411 * \returns Message buffer with IPA CCM ID RESP */
412struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
413 const uint8_t *data, unsigned int len)
414{
415 uint8_t ies[len/2];
416 unsigned int num_ies = 0;
417 const uint8_t *cur = data;
418
Harald Welte8a4895c2017-04-27 10:25:10 +0200419 memset(ies, 0, sizeof(ies));
420
Harald Welte7bc88bb2017-04-15 19:05:33 +0200421 /* build a array of the IEIs */
422 while (len >= 2) {
423 uint8_t t_len, t_tag;
Harald Welteb189b5f2021-04-29 18:38:48 +0200424 len -= 2; /* subtract the length of the two bytes read below */
Harald Welte7bc88bb2017-04-15 19:05:33 +0200425 t_len = *cur++;
426 t_tag = *cur++;
427
Harald Welteb189b5f2021-04-29 18:38:48 +0200428 /* as the 'tag' is included in the length of t_len, this cannot happen */
429 if (t_len == 0)
430 break;
431
Harald Welte7bc88bb2017-04-15 19:05:33 +0200432 if (t_len > len + 1) {
Thorsten Alteholz5a9dbf82018-04-08 19:13:25 +0200433 LOGP(DLINP, LOGL_ERROR, "IPA CCM tag 0x%02x does not fit\n", t_tag);
Harald Welte7bc88bb2017-04-15 19:05:33 +0200434 break;
435 }
436
437 ies[num_ies++] = t_tag;
438
Harald Welteb189b5f2021-04-29 18:38:48 +0200439 /* we need to subtract one from t_len to account for the tag */
440 cur += t_len - 1;
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200441 /* prevent any unsigned integer underflow due to somebody sending us
442 * messages with wrong length values */
443 if (len <= t_len)
Harald Welte0b2c0ec2018-04-16 22:53:48 +0200444 len = 0;
Harald Welte539272d2021-04-29 15:52:38 +0200445 else
Harald Welteb189b5f2021-04-29 18:38:48 +0200446 len -= t_len - 1;
Harald Welte7bc88bb2017-04-15 19:05:33 +0200447 }
448 return ipa_ccm_make_id_resp(dev, ies, num_ies);
449}
450
Harald Weltee3919962014-08-20 22:28:23 +0200451int ipa_send(int fd, const void *msg, size_t msglen)
Harald Welte28aa9912014-08-20 22:06:04 +0200452{
453 int ret;
454
455 ret = write(fd, msg, msglen);
456 if (ret < 0)
Jacob Erlbecka6be2242014-12-22 10:58:46 +0100457 return -errno;
Harald Welte28aa9912014-08-20 22:06:04 +0200458 if (ret < msglen) {
Harald Weltee3919962014-08-20 22:28:23 +0200459 LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
Harald Welte28aa9912014-08-20 22:06:04 +0200460 return -EIO;
461 }
462 return ret;
463}
464
Harald Weltee3919962014-08-20 22:28:23 +0200465int ipa_ccm_send_pong(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200466{
Harald Weltee3919962014-08-20 22:28:23 +0200467 return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200468}
469
Harald Weltee3919962014-08-20 22:28:23 +0200470int ipa_ccm_send_id_ack(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200471{
Harald Weltee3919962014-08-20 22:28:23 +0200472 return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200473}
474
Harald Weltee3919962014-08-20 22:28:23 +0200475int ipa_ccm_send_id_req(int fd)
Harald Welte28aa9912014-08-20 22:06:04 +0200476{
Harald Weltee3919962014-08-20 22:28:23 +0200477 return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
Harald Welte28aa9912014-08-20 22:06:04 +0200478}
479
480/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200481int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200482{
483 uint8_t msg_type = *(msg->l2h);
484 int ret;
485
486 switch (msg_type) {
487 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200488 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200489 if (ret < 0) {
Vadim Yanitskiy403dfbc2023-07-05 00:54:06 +0700490 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
Harald Welte28aa9912014-08-20 22:06:04 +0200491 "message. Reason: %s\n", strerror(errno));
492 break;
493 }
494 ret = 1;
495 break;
496 case IPAC_MSGT_PONG:
497 DEBUGP(DLMI, "PONG!\n");
498 ret = 1;
499 break;
500 case IPAC_MSGT_ID_ACK:
501 DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
Harald Weltee3919962014-08-20 22:28:23 +0200502 ret = ipa_ccm_send_id_ack(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200503 if (ret < 0) {
504 LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
505 "message. Reason: %s\n", strerror(errno));
506 break;
507 }
508 ret = 1;
509 break;
510 default:
511 /* This is not an IPA PING, PONG or ID_ACK message */
512 ret = 0;
513 break;
514 }
515 return ret;
516}
517
518/* base handling of the ip.access protocol */
Harald Weltee3919962014-08-20 22:28:23 +0200519int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
Harald Welte28aa9912014-08-20 22:06:04 +0200520{
521 uint8_t msg_type = *(msg->l2h);
522 int ret = 0;
523
524 switch (msg_type) {
525 case IPAC_MSGT_PING:
Harald Weltee3919962014-08-20 22:28:23 +0200526 ret = ipa_ccm_send_pong(bfd->fd);
Harald Welte28aa9912014-08-20 22:06:04 +0200527 if (ret < 0) {
528 LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
529 "message. Reason: %s\n", strerror(errno));
530 }
531 break;
532 case IPAC_MSGT_PONG:
533 DEBUGP(DLMI, "PONG!\n");
534 break;
535 case IPAC_MSGT_ID_ACK:
536 DEBUGP(DLMI, "ID_ACK\n");
537 break;
538 }
539 return ret;
540}
541
542
Harald Weltee3919962014-08-20 22:28:23 +0200543void ipa_prepend_header_ext(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200544{
545 struct ipaccess_head_ext *hh_ext;
546
547 /* prepend the osmo ip.access header extension */
548 hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
549 hh_ext->proto = proto;
550}
551
Harald Weltee3919962014-08-20 22:28:23 +0200552void ipa_prepend_header(struct msgb *msg, int proto)
Harald Welte28aa9912014-08-20 22:06:04 +0200553{
554 struct ipaccess_head *hh;
555
556 /* prepend the ip.access header */
557 hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
Harald Welte95871da2017-05-15 12:11:36 +0200558 hh->len = osmo_htons(msg->len - sizeof(*hh));
Harald Welte28aa9912014-08-20 22:06:04 +0200559 hh->proto = proto;
560}
561
Harald Welte20725b92017-05-15 12:50:04 +0200562#ifdef HAVE_SYS_SOCKET_H
563#include <sys/socket.h>
564
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200565/*! Read one ipa message from socket fd without caching not fully received
566 * messages. See \ref ipa_msg_recv_buffered for further information.
567 */
Harald Welte28aa9912014-08-20 22:06:04 +0200568int ipa_msg_recv(int fd, struct msgb **rmsg)
569{
570 int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
571 if (rc < 0) {
572 errno = -rc;
573 rc = -1;
574 }
575 return rc;
576}
577
Pau Espin Pedrol8a757d22018-08-22 14:12:01 +0200578/*! Read one ipa message from socket fd or store part if still not fully received.
579 * \param[in] fd The fd for the socket to read from.
580 * \param[out] rmsg internally allocated msgb containing a fully received ipa message.
581 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
582 *
583 * As ipa can run on top of stream based protocols such as TCP, there's the
584 * possibility that such lower layers split ipa messages in several low level
585 * packets. If a low layer packet is received containing several ipa frames,
586 * this function will pull from the socket and return only the first one
587 * available in the stream. As the socket will remain with data, it will
588 * trigger again during next select() and then this function will fetch the
589 * next ipa message, and so on.
590 *
591 * \returns -EAGAIN and allocated tmp_msg if message was not yet fully
592 * received. Other negative values indicate an error and cached msgb will be
593 * freed. 0 if socket is found dead. Positive value indicating l2 msgb len and
594 * rmsg pointing to internally allocated msgb containing the ipa frame on
595 * scucess.
596 */
Harald Welte28aa9912014-08-20 22:06:04 +0200597int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
598{
599 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
600 struct ipaccess_head *hh;
601 int len, ret;
602 int needed;
603
604 if (msg == NULL) {
605 msg = ipa_msg_alloc(0);
606 if (msg == NULL) {
607 ret = -ENOMEM;
608 goto discard_msg;
609 }
610 msg->l1h = msg->tail;
611 }
612
613 if (msg->l2h == NULL) {
614 /* first read our 3-byte header */
615 needed = sizeof(*hh) - msg->len;
616 ret = recv(fd, msg->tail, needed, 0);
617 if (ret == 0)
618 goto discard_msg;
619
620 if (ret < 0) {
621 if (errno == EAGAIN || errno == EINTR)
622 ret = 0;
623 else {
624 ret = -errno;
625 goto discard_msg;
626 }
627 }
628
629 msgb_put(msg, ret);
630
631 if (ret < needed) {
632 if (msg->len == 0) {
633 ret = -EAGAIN;
634 goto discard_msg;
635 }
636
637 LOGP(DLINP, LOGL_INFO,
Harald Weltef196a022014-08-21 09:42:03 +0200638 "Received part of IPA message header (%d/%zu)\n",
Harald Welte28aa9912014-08-20 22:06:04 +0200639 msg->len, sizeof(*hh));
640 if (!tmp_msg) {
641 ret = -EIO;
642 goto discard_msg;
643 }
644 *tmp_msg = msg;
645 return -EAGAIN;
646 }
647
648 msg->l2h = msg->tail;
649 }
650
651 hh = (struct ipaccess_head *) msg->data;
652
653 /* then read the length as specified in header */
Harald Welte95871da2017-05-15 12:11:36 +0200654 len = osmo_ntohs(hh->len);
Harald Welte28aa9912014-08-20 22:06:04 +0200655
656 if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
657 LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
658 "received %d bytes\n", len, msg->len);
659 ret = -EIO;
660 goto discard_msg;
661 }
662
663 needed = len - msgb_l2len(msg);
664
665 if (needed > 0) {
666 ret = recv(fd, msg->tail, needed, 0);
667
668 if (ret == 0)
669 goto discard_msg;
670
671 if (ret < 0) {
672 if (errno == EAGAIN || errno == EINTR)
673 ret = 0;
674 else {
675 ret = -errno;
676 goto discard_msg;
677 }
678 }
679
680 msgb_put(msg, ret);
681
682 if (ret < needed) {
683 LOGP(DLINP, LOGL_INFO,
684 "Received part of IPA message L2 data (%d/%d)\n",
685 msgb_l2len(msg), len);
686 if (!tmp_msg) {
687 ret = -EIO;
688 goto discard_msg;
689 }
690 *tmp_msg = msg;
691 return -EAGAIN;
692 }
693 }
694
695 ret = msgb_l2len(msg);
696
697 if (ret == 0) {
698 LOGP(DLINP, LOGL_INFO,
699 "Discarding IPA message without payload\n");
700 ret = -EAGAIN;
701 goto discard_msg;
702 }
703
704 if (tmp_msg)
705 *tmp_msg = NULL;
706 *rmsg = msg;
707 return ret;
708
709discard_msg:
710 if (tmp_msg)
711 *tmp_msg = NULL;
712 msgb_free(msg);
713 return ret;
714}
715
Harald Welte20725b92017-05-15 12:50:04 +0200716#endif /* SYS_SOCKET_H */
717
Harald Welte28aa9912014-08-20 22:06:04 +0200718struct msgb *ipa_msg_alloc(int headroom)
719{
720 struct msgb *nmsg;
721
722 headroom += sizeof(struct ipaccess_head);
723
Neels Hofmeyr889ab162017-09-07 20:41:12 +0200724 nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "IPA Multiplex");
Harald Welte28aa9912014-08-20 22:06:04 +0200725 if (!nmsg)
726 return NULL;
727 return nmsg;
728}
Harald Welte96e2a002017-06-12 21:44:18 +0200729
730/*! @} */