blob: b64c7b0599d60bd067fef4f2ad56d2d121bf6f5e [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file gsmtap_util.c
2 * GSMTAP support code in libosmocore. */
Harald Weltee779c362010-06-29 20:51:13 +02003/*
Harald Welte93713a52017-07-12 23:43:40 +02004 * (C) 2010-2017 by Harald Welte <laforge@gnumonks.org>
Harald Weltee779c362010-06-29 20:51:13 +02005 *
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Harald Weltee779c362010-06-29 20:51:13 +020010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
Harald Weltee779c362010-06-29 20:51:13 +020020 */
21
Pau Espin Pedrol88955fb2023-01-18 18:54:00 +010022#include "config.h"
Harald Weltee779c362010-06-29 20:51:13 +020023
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010024#include <osmocom/core/gsmtap_util.h>
25#include <osmocom/core/logging.h>
26#include <osmocom/core/gsmtap.h>
27#include <osmocom/core/msgb.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020028#include <osmocom/core/talloc.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/core/select.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020030#include <osmocom/core/socket.h>
Harald Welte95871da2017-05-15 12:11:36 +020031#include <osmocom/core/byteswap.h>
arehbeinf38077e2023-10-13 17:20:15 +020032#include <osmocom/core/utils.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010033#include <osmocom/gsm/protocol/gsm_04_08.h>
34#include <osmocom/gsm/rsl.h>
Harald Weltee779c362010-06-29 20:51:13 +020035
Harald Welte33cb71a2011-05-21 18:54:32 +020036#include <sys/types.h>
Harald Weltee4764422011-05-22 12:25:57 +020037
Harald Weltee779c362010-06-29 20:51:13 +020038#include <stdio.h>
39#include <unistd.h>
40#include <stdint.h>
41#include <string.h>
42#include <errno.h>
43
Harald Welte47379ca2011-08-17 16:35:24 +020044/*! \addtogroup gsmtap
45 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020046 * GSMTAP utility routines. Encapsulates GSM messages over UDP.
47 *
48 * \file gsmtap_util.c */
Harald Welte47379ca2011-08-17 16:35:24 +020049
arehbeinf38077e2023-10-13 17:20:15 +020050/*! one gsmtap instance
51 * Until gsmtap_inst_fd() is removed from the API at some point in the future, we have to keep the first member as
52 * 'int' and the second as 'struct osmo_wqueue' (this effectively makes sure that the struct member wq.bfd.fd maintains
53 * the same memory offset from the start of the struct) to ensure that inlined static 'instances' of gsmtap_inst_fd() in
54 * old binaries keep working the way they used to even with gsmtap_inst objects obtained from newer versions of libosmocore */
arehbein1584b2a2023-10-12 00:20:17 +020055struct gsmtap_inst {
arehbeinf38077e2023-10-13 17:20:15 +020056 int osmo_io_mode; /*!< Indicates whether or not to use Osmo IO mode for message output (thus enabling use of tx queues).
57 * This field member may not be changed or moved (backwards compatibility) */
58 struct osmo_wqueue wq; /*!< the wait queue. This field member may not be changed or moved (backwards compatibility) */
59
60 struct osmo_io_fd *out; /*!< Used when osmo_io_mode is nonzero */
arehbeinf67e4582023-10-24 11:45:54 +020061 int sink_fd;
arehbein1584b2a2023-10-12 00:20:17 +020062};
63
arehbeinf38077e2023-10-13 17:20:15 +020064struct _gsmtap_inst_legacy {
65 int ofd_wq_mode;
66 struct osmo_wqueue wq;
67 struct osmo_fd sink_ofd;
68};
69osmo_static_assert(offsetof(struct gsmtap_inst, wq) == offsetof(struct _gsmtap_inst_legacy, wq),
70 gsmtap_inst_new_wq_offset_equals_legacy_wq_offset);
71
arehbein1584b2a2023-10-12 00:20:17 +020072/*! Deprecated, use gsmtap_inst_fd2() instead
73 * \param[in] gti GSMTAP instance
74 * \returns file descriptor of GSMTAP instance */
75int gsmtap_inst_fd(struct gsmtap_inst *gti)
76{
77 return gsmtap_inst_fd2(gti);
78}
79
80/*! obtain the file descriptor associated with a gsmtap instance
81 * \param[in] gti GSMTAP instance
82 * \returns file descriptor of GSMTAP instance */
83int gsmtap_inst_fd2(const struct gsmtap_inst *gti)
84{
85 return gti->wq.bfd.fd;
86}
Harald Welte47379ca2011-08-17 16:35:24 +020087
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! convert RSL channel number to GSMTAP channel type
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010089 * \param[in] rsl_chantype RSL channel type
Harald Welte47379ca2011-08-17 16:35:24 +020090 * \param[in] link_id RSL link identifier
Harald Welte67733042020-03-08 17:21:29 +010091 * \param[in] user_plane Is this voice/csd user plane (1) or signaling (0)
Harald Welte47379ca2011-08-17 16:35:24 +020092 * \returns GSMTAP channel type
93 */
Harald Welte67733042020-03-08 17:21:29 +010094uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_plane)
Harald Weltee779c362010-06-29 20:51:13 +020095{
96 uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
97
98 switch (rsl_chantype) {
99 case RSL_CHAN_Bm_ACCHs:
Vadim Yanitskiyfc02ff42021-05-26 18:19:03 +0200100 case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs:
Harald Welte67733042020-03-08 17:21:29 +0100101 if (user_plane)
102 ret = GSMTAP_CHANNEL_VOICE_F;
103 else
104 ret = GSMTAP_CHANNEL_FACCH_F;
Harald Weltee779c362010-06-29 20:51:13 +0200105 break;
106 case RSL_CHAN_Lm_ACCHs:
Vadim Yanitskiyfc02ff42021-05-26 18:19:03 +0200107 case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs:
Harald Welte67733042020-03-08 17:21:29 +0100108 if (user_plane)
109 ret = GSMTAP_CHANNEL_VOICE_H;
110 else
111 ret = GSMTAP_CHANNEL_FACCH_H;
Harald Weltee779c362010-06-29 20:51:13 +0200112 break;
113 case RSL_CHAN_SDCCH4_ACCH:
114 ret = GSMTAP_CHANNEL_SDCCH4;
115 break;
116 case RSL_CHAN_SDCCH8_ACCH:
117 ret = GSMTAP_CHANNEL_SDCCH8;
118 break;
119 case RSL_CHAN_BCCH:
120 ret = GSMTAP_CHANNEL_BCCH;
121 break;
122 case RSL_CHAN_RACH:
123 ret = GSMTAP_CHANNEL_RACH;
124 break;
125 case RSL_CHAN_PCH_AGCH:
126 /* it could also be AGCH... */
127 ret = GSMTAP_CHANNEL_PCH;
128 break;
Harald Welte3b7cd0b2017-07-27 14:12:15 +0200129 case RSL_CHAN_OSMO_PDCH:
130 ret = GSMTAP_CHANNEL_PDCH;
131 break;
Harald Welteac7fabe2020-02-26 17:24:02 +0100132 case RSL_CHAN_OSMO_CBCH4:
133 ret = GSMTAP_CHANNEL_CBCH51;
134 break;
135 case RSL_CHAN_OSMO_CBCH8:
136 ret = GSMTAP_CHANNEL_CBCH52;
137 break;
Harald Weltee779c362010-06-29 20:51:13 +0200138 }
139
140 if (link_id & 0x40)
141 ret |= GSMTAP_CHANNEL_ACCH;
142
143 return ret;
144}
145
Harald Welte67733042020-03-08 17:21:29 +0100146/*! convert RSL channel number to GSMTAP channel type
147 * \param[in] rsl_chantype RSL channel type
148 * \param[in] link_id RSL link identifier
149 * \returns GSMTAP channel type
150 */
151uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
152{
153 return chantype_rsl2gsmtap2(rsl_chantype, link_id, false);
154}
155
Harald Welte93713a52017-07-12 23:43:40 +0200156/*! convert GSMTAP channel type to RSL channel number + Link ID
157 * \param[in] gsmtap_chantype GSMTAP channel type
158 * \param[out] rsl_chantype RSL channel mumber
159 * \param[out] link_id RSL link identifier
160 */
161void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype,
162 uint8_t *link_id)
163{
164 switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) {
Harald Welte67733042020-03-08 17:21:29 +0100165 case GSMTAP_CHANNEL_FACCH_F:
166 case GSMTAP_CHANNEL_VOICE_F: // TCH/F
Harald Welte93713a52017-07-12 23:43:40 +0200167 *rsl_chantype = RSL_CHAN_Bm_ACCHs;
168 break;
Harald Welte67733042020-03-08 17:21:29 +0100169 case GSMTAP_CHANNEL_FACCH_H:
170 case GSMTAP_CHANNEL_VOICE_H: // TCH/H
Harald Welte93713a52017-07-12 23:43:40 +0200171 *rsl_chantype = RSL_CHAN_Lm_ACCHs;
172 break;
173 case GSMTAP_CHANNEL_SDCCH4: // SDCCH/4
174 *rsl_chantype = RSL_CHAN_SDCCH4_ACCH;
175 break;
176 case GSMTAP_CHANNEL_SDCCH8: // SDCCH/8
177 *rsl_chantype = RSL_CHAN_SDCCH8_ACCH;
178 break;
179 case GSMTAP_CHANNEL_BCCH: // BCCH
180 *rsl_chantype = RSL_CHAN_BCCH;
181 break;
182 case GSMTAP_CHANNEL_RACH: // RACH
183 *rsl_chantype = RSL_CHAN_RACH;
184 break;
185 case GSMTAP_CHANNEL_PCH: // PCH
186 case GSMTAP_CHANNEL_AGCH: // AGCH
187 *rsl_chantype = RSL_CHAN_PCH_AGCH;
188 break;
189 case GSMTAP_CHANNEL_PDCH:
Harald Welte3b7cd0b2017-07-27 14:12:15 +0200190 *rsl_chantype = RSL_CHAN_OSMO_PDCH;
Harald Welte93713a52017-07-12 23:43:40 +0200191 break;
192 }
193
194 *link_id = gsmtap_chantype & GSMTAP_CHANNEL_ACCH ? 0x40 : 0x00;
195}
196
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200197/*! create an arbitrary type GSMTAP message
Sylvain Munautabf66e72011-09-26 13:23:19 +0200198 * \param[in] type The GSMTAP_TYPE_xxx constant of the message to create
Harald Welte47379ca2011-08-17 16:35:24 +0200199 * \param[in] arfcn GSM ARFCN (Channel Number)
200 * \param[in] ts GSM time slot
201 * \param[in] chan_type Channel Type
202 * \param[in] ss Sub-slot
203 * \param[in] fn GSM Frame Number
204 * \param[in] signal_dbm Signal Strength (dBm)
205 * \param[in] snr Signal/Noise Ratio (SNR)
206 * \param[in] data Pointer to data buffer
207 * \param[in] len Length of \ref data
Pau Espin Pedrol9a5d90a2020-06-30 18:16:33 +0200208 * \return dynamically allocated message buffer containing data
Harald Welte47379ca2011-08-17 16:35:24 +0200209 *
210 * This function will allocate a new msgb and fill it with a GSMTAP
211 * header containing the information
212 */
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200213struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type,
Harald Weltee34a9402010-06-29 22:31:21 +0200214 uint8_t ss, uint32_t fn, int8_t signal_dbm,
Vadim Yanitskiy833e8fa2021-01-04 17:40:05 +0100215 int8_t snr, const uint8_t *data, unsigned int len)
Harald Weltee779c362010-06-29 20:51:13 +0200216{
217 struct msgb *msg;
218 struct gsmtap_hdr *gh;
219 uint8_t *dst;
220
Harald Weltee779c362010-06-29 20:51:13 +0200221 msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx");
222 if (!msg)
Harald Weltee34a9402010-06-29 22:31:21 +0200223 return NULL;
Harald Weltee779c362010-06-29 20:51:13 +0200224
225 gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
226
227 gh->version = GSMTAP_VERSION;
228 gh->hdr_len = sizeof(*gh)/4;
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200229 gh->type = type;
Harald Weltee779c362010-06-29 20:51:13 +0200230 gh->timeslot = ts;
231 gh->sub_slot = ss;
Harald Welte95871da2017-05-15 12:11:36 +0200232 gh->arfcn = osmo_htons(arfcn);
Harald Weltee779c362010-06-29 20:51:13 +0200233 gh->snr_db = snr;
234 gh->signal_dbm = signal_dbm;
Harald Welte95871da2017-05-15 12:11:36 +0200235 gh->frame_number = osmo_htonl(fn);
Harald Weltee779c362010-06-29 20:51:13 +0200236 gh->sub_type = chan_type;
237 gh->antenna_nr = 0;
238
239 dst = msgb_put(msg, len);
240 memcpy(dst, data, len);
241
Harald Weltee34a9402010-06-29 22:31:21 +0200242 return msg;
243}
244
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200245/*! create L1/L2 data and put it into GSMTAP
Sylvain Munautabf66e72011-09-26 13:23:19 +0200246 * \param[in] arfcn GSM ARFCN (Channel Number)
247 * \param[in] ts GSM time slot
248 * \param[in] chan_type Channel Type
249 * \param[in] ss Sub-slot
250 * \param[in] fn GSM Frame Number
251 * \param[in] signal_dbm Signal Strength (dBm)
252 * \param[in] snr Signal/Noise Ratio (SNR)
253 * \param[in] data Pointer to data buffer
254 * \param[in] len Length of \ref data
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200255 * \return message buffer or NULL in case of error
Sylvain Munautabf66e72011-09-26 13:23:19 +0200256 *
257 * This function will allocate a new msgb and fill it with a GSMTAP
258 * header containing the information
259 */
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200260struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
261 uint8_t ss, uint32_t fn, int8_t signal_dbm,
Vadim Yanitskiy833e8fa2021-01-04 17:40:05 +0100262 int8_t snr, const uint8_t *data, unsigned int len)
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200263{
264 return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type,
265 ss, fn, signal_dbm, snr, data, len);
266}
267
Harald Weltee4764422011-05-22 12:25:57 +0200268#ifdef HAVE_SYS_SOCKET_H
269
270#include <sys/socket.h>
271#include <netinet/in.h>
arehbeinf38077e2023-10-13 17:20:15 +0200272#include <osmocom/core/osmo_io.h>
Harald Weltee4764422011-05-22 12:25:57 +0200273
Philipp Maierce4a8652023-02-20 10:29:49 +0100274/*! Create a new (sending) GSMTAP source socket
Harald Welte47379ca2011-08-17 16:35:24 +0200275 * \param[in] host host name or IP address in string format
276 * \param[in] port UDP port number in host byte order
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200277 * \return file descriptor of the new socket
Harald Welte47379ca2011-08-17 16:35:24 +0200278 *
Maxef5d3bc2023-02-18 23:48:27 +0300279 * Opens a GSMTAP source (sending) socket, connect it to host/port and
Harald Welte47379ca2011-08-17 16:35:24 +0200280 * return resulting fd. If \a host is NULL, the destination address
281 * will be localhost. If \a port is 0, the default \ref
282 * GSMTAP_UDP_PORT will be used.
283 * */
Harald Welte33cb71a2011-05-21 18:54:32 +0200284int gsmtap_source_init_fd(const char *host, uint16_t port)
285{
286 if (port == 0)
287 port = GSMTAP_UDP_PORT;
288 if (host == NULL)
289 host = "localhost";
290
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200291 return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port,
292 OSMO_SOCK_F_CONNECT);
Harald Welte33cb71a2011-05-21 18:54:32 +0200293}
294
Maxa747d362023-02-18 23:45:42 +0300295/*! Create a new (sending) GSMTAP source socket
296 * \param[in] local_host local host name or IP address in string format
297 * \param[in] local_port local UDP port number in host byte order
298 * \param[in] rem_host remote host name or IP address in string format
299 * \param[in] rem_port remote UDP port number in host byte order
300 * \return file descriptor of the new socket
301 *
302 * Opens a GSMTAP source (sending) socket, connect it to remote host/port,
303 * bind to local host/port and return resulting fd.
304 * If \a local_host is NULL, the default address is used.
305 * If \a local_port is 0, than random unused port will be selected by OS.
306 * If \a rem_host is NULL, the destination address will be localhost.
307 * If \a rem_port is 0, the default \ref GSMTAP_UDP_PORT will be used.
308 */
309int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port)
310{
311 if (!local_host)
312 return gsmtap_source_init_fd(rem_host, rem_port);
313
314 return osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, local_host, local_port,
315 rem_host ? rem_host : "localhost", rem_port ? rem_port : GSMTAP_UDP_PORT,
316 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
317}
318
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200319/*! Add a local sink to an existing GSMTAP source and return fd
Harald Weltede6e4982012-12-06 21:25:27 +0100320 * \param[in] gsmtap_fd file descriptor of the gsmtap socket
321 * \returns file descriptor of locally bound receive socket
322 *
323 * In case the GSMTAP socket is connected to a local destination
324 * IP/port, this function creates a corresponding receiving socket
325 * bound to that destination IP + port.
326 *
327 * In case the gsmtap socket is not connected to a local IP/port, or
328 * creation of the receiving socket fails, a negative error code is
329 * returned.
330 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200331int gsmtap_source_add_sink_fd(int gsmtap_fd)
332{
333 struct sockaddr_storage ss;
334 socklen_t ss_len = sizeof(ss);
335 int rc;
336
337 rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len);
338 if (rc < 0)
339 return rc;
340
341 if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200342 rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM,
Philipp Maierb8a91622018-08-23 20:18:55 +0200343 IPPROTO_UDP,
344 OSMO_SOCK_F_BIND |
345 OSMO_SOCK_F_UDP_REUSEADDR);
Harald Welte33cb71a2011-05-21 18:54:32 +0200346 if (rc >= 0)
347 return rc;
348 }
349
350 return -ENODEV;
351}
352
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200353/*! Send a \ref msgb through a GSMTAP source
Harald Welte47379ca2011-08-17 16:35:24 +0200354 * \param[in] gti GSMTAP instance
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100355 * \param[in] msg message buffer
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200356 * \return 0 in case of success; negative in case of error
Neels Hofmeyrc9a4ce62018-02-16 01:23:29 +0100357 * NOTE: in case of nonzero return value, the *caller* must free the msg!
358 * (This enables the caller to attempt re-sending the message.)
359 * If 0 is returned, the msgb was freed by this function.
Harald Welte47379ca2011-08-17 16:35:24 +0200360 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200361int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
362{
Harald Welte13692a62011-05-22 20:06:11 +0200363 if (!gti)
364 return -ENODEV;
365
arehbeinf38077e2023-10-13 17:20:15 +0200366 if (gti->osmo_io_mode)
367 return osmo_iofd_write_msgb(gti->out, msg);
Harald Welte33cb71a2011-05-21 18:54:32 +0200368 else {
369 /* try immediate send and return error if any */
370 int rc;
371
arehbein1584b2a2023-10-12 00:20:17 +0200372 rc = write(gsmtap_inst_fd2(gti), msg->data, msg->len);
Neels Hofmeyr90539ac2018-02-16 01:24:03 +0100373 if (rc < 0) {
Harald Welte33cb71a2011-05-21 18:54:32 +0200374 return rc;
375 } else if (rc >= msg->len) {
376 msgb_free(msg);
377 return 0;
378 } else {
379 /* short write */
380 return -EIO;
381 }
382 }
383}
384
Harald Welte9e34f082021-11-25 15:35:50 +0100385/*! Send a \ref msgb through a GSMTAP source; free the message even if tx queue full.
386 * \param[in] gti GSMTAP instance
387 * \param[in] msg message buffer; always freed, caller must not reference it later.
388 * \return 0 in case of success; negative in case of error
389 */
390int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg)
391{
392 int rc;
393 rc = gsmtap_sendmsg(gti, msg);
394 if (rc < 0)
395 msgb_free(msg);
396 return rc;
397}
398
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200399/*! send an arbitrary type through GSMTAP.
Sylvain Munautabf66e72011-09-26 13:23:19 +0200400 * See \ref gsmtap_makemsg_ex for arguments
401 */
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200402int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts,
Harald Welte33cb71a2011-05-21 18:54:32 +0200403 uint8_t chan_type, uint8_t ss, uint32_t fn,
Vadim Yanitskiy833e8fa2021-01-04 17:40:05 +0100404 int8_t signal_dbm, int8_t snr, const uint8_t *data,
Harald Welte33cb71a2011-05-21 18:54:32 +0200405 unsigned int len)
Harald Weltee34a9402010-06-29 22:31:21 +0200406{
407 struct msgb *msg;
Neels Hofmeyra4952aa2018-02-16 01:26:00 +0100408 int rc;
Harald Weltee34a9402010-06-29 22:31:21 +0200409
Harald Welte13692a62011-05-22 20:06:11 +0200410 if (!gti)
411 return -ENODEV;
412
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200413 msg = gsmtap_makemsg_ex(type, arfcn, ts, chan_type, ss, fn, signal_dbm,
Harald Weltee34a9402010-06-29 22:31:21 +0200414 snr, data, len);
415 if (!msg)
416 return -ENOMEM;
417
Neels Hofmeyra4952aa2018-02-16 01:26:00 +0100418 rc = gsmtap_sendmsg(gti, msg);
419 if (rc)
420 msgb_free(msg);
421 return rc;
Harald Weltee779c362010-06-29 20:51:13 +0200422}
423
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200424/*! send a message from L1/L2 through GSMTAP.
Sylvain Munautabf66e72011-09-26 13:23:19 +0200425 * See \ref gsmtap_makemsg for arguments
426 */
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200427int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
428 uint8_t chan_type, uint8_t ss, uint32_t fn,
Vadim Yanitskiy833e8fa2021-01-04 17:40:05 +0100429 int8_t signal_dbm, int8_t snr, const uint8_t *data,
Sylvain Munaut15ae7152011-09-26 13:05:07 +0200430 unsigned int len)
431{
432 return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn,
433 signal_dbm, snr, data, len);
434}
435
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200436/*! Add a local sink to an existing GSMTAP source and return fd
Vadim Yanitskiy2f65bb12019-03-25 15:57:09 +0700437 * \param[in] gti existing GSMTAP source
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200438 * \returns file descriptor of locally bound receive socket
439 *
440 * In case the GSMTAP socket is connected to a local destination
441 * IP/port, this function creates a corresponding receiving socket
442 * bound to that destination IP + port.
443 *
444 * In case the gsmtap socket is not connected to a local IP/port, or
445 * creation of the receiving socket fails, a negative error code is
446 * returned.
447 *
448 * The file descriptor of the receiving socket is automatically added
449 * to the libosmocore select() handling.
450 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200451int gsmtap_source_add_sink(struct gsmtap_inst *gti)
Harald Welted58ba462011-04-27 10:57:49 +0200452{
arehbeinf67e4582023-10-24 11:45:54 +0200453 return gti->sink_fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd2(gti));
Harald Welte33cb71a2011-05-21 18:54:32 +0200454}
Harald Welted58ba462011-04-27 10:57:49 +0200455
arehbeinf38077e2023-10-13 17:20:15 +0200456/* Registered in Osmo IO as a no-op to set the write callback. */
457static void gsmtap_ops_noop_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
458{
459}
460
461static struct osmo_io_ops gsmtap_ops = { .write_cb = gsmtap_ops_noop_cb };
Harald Welte47379ca2011-08-17 16:35:24 +0200462
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200463/*! Open GSMTAP source socket, connect and register osmo_fd
Maxedb5f332023-02-25 16:33:52 +0300464 * \param[in] local_host IP address in string format
465 * \param[in] local_port UDP port number in host byte order
466 * \param[in] rem_host host name or IP address in string format
467 * \param[in] rem_port UDP port number in host byte order
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100468 * \param[in] ofd_wq_mode Register \ref osmo_wqueue (1) or not (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200469 * \return callee-allocated \ref gsmtap_inst
Harald Welte47379ca2011-08-17 16:35:24 +0200470 *
Maxa747d362023-02-18 23:45:42 +0300471 * Open GSMTAP source (sending) socket, connect it to remote host/port,
472 * bind it local host/port,
Harald Welte47379ca2011-08-17 16:35:24 +0200473 * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
Vadim Yanitskiy2f65bb12019-03-25 15:57:09 +0700474 * registration.
475 */
Maxa747d362023-02-18 23:45:42 +0300476struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port,
477 const char *rem_host, uint16_t rem_port, int ofd_wq_mode)
Harald Welte33cb71a2011-05-21 18:54:32 +0200478{
479 struct gsmtap_inst *gti;
arehbeinf38077e2023-10-13 17:20:15 +0200480 int fd;
Harald Welted58ba462011-04-27 10:57:49 +0200481
Maxa747d362023-02-18 23:45:42 +0300482 fd = gsmtap_source_init_fd2(local_host, local_port, rem_host, rem_port);
Harald Welte33cb71a2011-05-21 18:54:32 +0200483 if (fd < 0)
484 return NULL;
485
486 gti = talloc_zero(NULL, struct gsmtap_inst);
arehbeinf38077e2023-10-13 17:20:15 +0200487 gti->osmo_io_mode = ofd_wq_mode;
488 /* Still using the wq member for its 'fd' field only, since we are keeping it for now, anyways */
Harald Welte33cb71a2011-05-21 18:54:32 +0200489 gti->wq.bfd.fd = fd;
arehbeinf67e4582023-10-24 11:45:54 +0200490 gti->sink_fd = -1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200491
492 if (ofd_wq_mode) {
arehbeinf38077e2023-10-13 17:20:15 +0200493 gti->out = osmo_iofd_setup(gti, gti->wq.bfd.fd, "gsmtap_inst.io_fd", OSMO_IO_FD_MODE_READ_WRITE, &gsmtap_ops, NULL);
arehbein90d79f42023-11-17 16:00:57 +0100494 if (gti->out == NULL)
495 goto err_cleanup;
496 if (osmo_iofd_register(gti->out, gti->wq.bfd.fd) < 0)
497 goto err_cleanup;
498
arehbeinf38077e2023-10-13 17:20:15 +0200499 /* osmo write queue previously used was set up with value of 64 */
500 osmo_iofd_set_txqueue_max_length(gti->out, 64);
Harald Welte33cb71a2011-05-21 18:54:32 +0200501 }
502
503 return gti;
arehbein90d79f42023-11-17 16:00:57 +0100504
505err_cleanup:
506 talloc_free(gti);
507 close(fd);
508 return NULL;
Harald Welted58ba462011-04-27 10:57:49 +0200509}
510
Maxa747d362023-02-18 23:45:42 +0300511/*! Open GSMTAP source socket, connect and register osmo_fd
512 * \param[in] host host name or IP address in string format
513 * \param[in] port UDP port number in host byte order
514 * \param[in] ofd_wq_mode Register \ref osmo_wqueue (1) or not (0)
515 * \return callee-allocated \ref gsmtap_inst
516 *
517 * Open GSMTAP source (sending) socket, connect it to host/port,
518 * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
519 * registration.
520 */
521struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
522 int ofd_wq_mode)
523{
524 return gsmtap_source_init2(NULL, 0, host, port, ofd_wq_mode);
525}
526
Vadim Yanitskiy2f4186a2021-12-29 21:58:19 +0600527void gsmtap_source_free(struct gsmtap_inst *gti)
528{
Harald Welte3ef61202023-06-28 20:04:16 +0200529 if (!gti)
530 return;
531
arehbeinf38077e2023-10-13 17:20:15 +0200532 if (gti->osmo_io_mode) {
533 osmo_iofd_free(gti->out);
Vadim Yanitskiy2f4186a2021-12-29 21:58:19 +0600534
arehbeinf67e4582023-10-24 11:45:54 +0200535 if (gti->sink_fd != -1) {
536 close(gti->sink_fd);
537 gti->sink_fd = -1;
Vadim Yanitskiy2f4186a2021-12-29 21:58:19 +0600538 }
arehbeinf67e4582023-10-24 11:45:54 +0200539
Vadim Yanitskiy2f4186a2021-12-29 21:58:19 +0600540 }
541
Vadim Yanitskiy2f4186a2021-12-29 21:58:19 +0600542 talloc_free(gti);
543}
544
Harald Weltee4764422011-05-22 12:25:57 +0200545#endif /* HAVE_SYS_SOCKET_H */
Harald Weltede6e4982012-12-06 21:25:27 +0100546
Harald Welteaa3ba462017-07-13 00:01:02 +0200547const struct value_string gsmtap_gsm_channel_names[] = {
548 { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" },
549 { GSMTAP_CHANNEL_BCCH, "BCCH" },
550 { GSMTAP_CHANNEL_CCCH, "CCCH" },
551 { GSMTAP_CHANNEL_RACH, "RACH" },
552 { GSMTAP_CHANNEL_AGCH, "AGCH" },
553 { GSMTAP_CHANNEL_PCH, "PCH" },
554 { GSMTAP_CHANNEL_SDCCH, "SDCCH" },
555 { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" },
556 { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" },
Harald Welte67733042020-03-08 17:21:29 +0100557 { GSMTAP_CHANNEL_FACCH_F, "FACCH/F" },
558 { GSMTAP_CHANNEL_FACCH_H, "FACCH/H" },
Harald Welteaa3ba462017-07-13 00:01:02 +0200559 { GSMTAP_CHANNEL_PACCH, "PACCH" },
560 { GSMTAP_CHANNEL_CBCH52, "CBCH" },
561 { GSMTAP_CHANNEL_PDCH, "PDCH" } ,
562 { GSMTAP_CHANNEL_PTCCH, "PTTCH" },
563 { GSMTAP_CHANNEL_CBCH51, "CBCH" },
564 { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH, "LSACCH" },
565 { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH4, "SACCH/4" },
566 { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH8, "SACCH/8" },
Harald Welte67733042020-03-08 17:21:29 +0100567 { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_F, "SACCH/F" },
568 { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_H, "SACCH/H" },
569 { GSMTAP_CHANNEL_VOICE_F, "TCH/F" },
570 { GSMTAP_CHANNEL_VOICE_H, "TCH/H" },
Harald Welteaa3ba462017-07-13 00:01:02 +0200571 { 0, NULL }
572};
573
574/* for debugging */
575const struct value_string gsmtap_type_names[] = {
576 { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" },
577 { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" },
578 { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" },
579 { GSMTAP_TYPE_SIM, "SIM Card" },
580 { GSMTAP_TYPE_TETRA_I1, "TETRA V+D" },
581 { GSMTAP_TYPE_TETRA_I1_BURST, "TETRA bursts" },
582 { GSMTAP_TYPE_WMX_BURST, "WiMAX burst" },
Vadim Yanitskiy0a60e422023-03-11 03:40:52 +0700583 { GSMTAP_TYPE_GB_LLC, "GPRS Gb LLC" },
584 { GSMTAP_TYPE_GB_SNDCP, "GPRS Gb SNDCP" },
Harald Welteaa3ba462017-07-13 00:01:02 +0200585 { GSMTAP_TYPE_GMR1_UM, "GMR-1 air interfeace (MES-MS<->GTS)"},
586 { GSMTAP_TYPE_UMTS_RLC_MAC, "UMTS RLC/MAC" },
587 { GSMTAP_TYPE_UMTS_RRC, "UMTS RRC" },
588 { GSMTAP_TYPE_LTE_RRC, "LTE RRC" },
589 { GSMTAP_TYPE_LTE_MAC, "LTE MAC" },
590 { GSMTAP_TYPE_LTE_MAC_FRAMED, "LTE MAC with context hdr" },
591 { GSMTAP_TYPE_OSMOCORE_LOG, "libosmocore logging" },
592 { GSMTAP_TYPE_QC_DIAG, "Qualcomm DIAG" },
Vadim Yanitskiy0a60e422023-03-11 03:40:52 +0700593 { GSMTAP_TYPE_LTE_NAS, "LTE Non-Access Stratum" },
594 { GSMTAP_TYPE_E1T1, "E1/T1 lines" },
Harald Welte5e3aaf92023-03-09 18:04:24 +0100595 { GSMTAP_TYPE_GSM_RLP, "GSM Radio Link Protocol" },
Harald Welteaa3ba462017-07-13 00:01:02 +0200596 { 0, NULL }
597};
598
Harald Weltede6e4982012-12-06 21:25:27 +0100599/*! @} */