blob: 1dcc30300d16076120dd36c882f18e2ce41fbbcf [file] [log] [blame]
Alexander Couzens6a161492020-07-12 13:45:50 +02001/*! \file gprs_ns2_udp.c
2 * NS-over-UDP implementation.
3 * GPRS Networks Service (NS) messages on the Gb interface.
4 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
5 * as well as its successor 3GPP TS 48.016 */
6
7/* (C) 2020 sysmocom - s.f.m.c. GmbH
8 * Author: Alexander Couzens <lynxis@fe80.eu>
9 *
10 * All Rights Reserved
11 *
12 * SPDX-License-Identifier: GPL-2.0+
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 *
27 */
28
29#include <errno.h>
30
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +010031#include <osmocom/core/osmo_io.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020032#include <osmocom/core/select.h>
33#include <osmocom/core/sockaddr_str.h>
34#include <osmocom/core/socket.h>
35#include <osmocom/gprs/gprs_ns2.h>
36
37#include "common_vty.h"
38#include "gprs_ns2_internal.h"
39
40
41static void free_bind(struct gprs_ns2_vc_bind *bind);
42
43
44struct gprs_ns2_vc_driver vc_driver_ip = {
45 .name = "GB UDP IPv4/IPv6",
46 .free_bind = free_bind,
47};
48
49struct priv_bind {
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +010050 struct osmo_io_fd *iofd;
Alexander Couzens6a161492020-07-12 13:45:50 +020051 struct osmo_sockaddr addr;
52 int dscp;
Harald Welted99e4ee2021-04-28 19:57:12 +020053 uint8_t priority;
Alexander Couzens6a161492020-07-12 13:45:50 +020054};
55
56struct priv_vc {
57 struct osmo_sockaddr remote;
58};
59
60/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
61static void free_bind(struct gprs_ns2_vc_bind *bind)
62{
63 struct priv_bind *priv;
64
65 if (!bind)
66 return;
67
Alexander Couzens55bc8692021-01-18 18:39:57 +010068 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
69
Alexander Couzens6a161492020-07-12 13:45:50 +020070 priv = bind->priv;
71
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +010072 osmo_iofd_free(priv->iofd);
73 priv->iofd = NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +020074 talloc_free(priv);
75}
76
77static void free_vc(struct gprs_ns2_vc *nsvc)
78{
Alexander Couzensea377242021-01-17 16:51:55 +010079 if (!nsvc)
80 return;
81
Alexander Couzens6a161492020-07-12 13:45:50 +020082 if (!nsvc->priv)
83 return;
84
Alexander Couzens55bc8692021-01-18 18:39:57 +010085 OSMO_ASSERT(gprs_ns2_is_ip_bind(nsvc->bind));
Alexander Couzens6a161492020-07-12 13:45:50 +020086 talloc_free(nsvc->priv);
87 nsvc->priv = NULL;
88}
89
Alexander Couzens22f34712020-10-02 02:34:39 +020090static void dump_vty(const struct gprs_ns2_vc_bind *bind,
Alexander Couzens75b61882021-03-21 16:18:17 +010091 struct vty *vty, bool stats)
Alexander Couzens22f34712020-10-02 02:34:39 +020092{
93 struct priv_bind *priv;
94 struct gprs_ns2_vc *nsvc;
95 struct osmo_sockaddr_str sockstr = {};
96 unsigned long nsvcs = 0;
97
98 if (!bind)
99 return;
100
101 priv = bind->priv;
102 if (osmo_sockaddr_str_from_sockaddr(&sockstr, &priv->addr.u.sas))
103 strcpy(sockstr.ip, "invalid");
104
105 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
106 nsvcs++;
107 }
108
Harald Welted99e4ee2021-04-28 19:57:12 +0200109 vty_out(vty, "UDP bind: %s:%d DSCP: %d Priority: %u%s", sockstr.ip, sockstr.port,
110 priv->dscp, priv->priority, VTY_NEWLINE);
Alexander Couzensc4704762021-02-08 23:13:12 +0100111 vty_out(vty, " IP-SNS signalling weight: %u data weight: %u%s",
112 bind->sns_sig_weight, bind->sns_data_weight, VTY_NEWLINE);
Alexander Couzens8bd63b62021-03-21 17:45:22 +0100113 vty_out(vty, " %lu NS-VC:%s", nsvcs, VTY_NEWLINE);
Alexander Couzens22f34712020-10-02 02:34:39 +0200114
115 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Alexander Couzens75b61882021-03-21 16:18:17 +0100116 ns2_vty_dump_nsvc(vty, nsvc, stats);
Alexander Couzens22f34712020-10-02 02:34:39 +0200117 }
118}
119
120
Harald Welte5bef2cc2020-09-18 22:33:24 +0200121/*! Find a NS-VC by its remote socket address.
122 * \param[in] bind in which to search
Pau Espin Pedrolf6ef9ba2023-04-27 17:50:10 +0200123 * \param[in] rem_addr remote peer socket address to search
Alexander Couzens38b19e82020-09-23 23:56:37 +0200124 * \returns NS-VC matching sockaddr; NULL if none found */
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700125struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind,
Pau Espin Pedrolf6ef9ba2023-04-27 17:50:10 +0200126 const struct osmo_sockaddr *rem_addr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200127{
128 struct gprs_ns2_vc *nsvc;
129 struct priv_vc *vcpriv;
130
Alexander Couzens55bc8692021-01-18 18:39:57 +0100131 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
132
Alexander Couzens6a161492020-07-12 13:45:50 +0200133 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
134 vcpriv = nsvc->priv;
Pau Espin Pedrolf6ef9ba2023-04-27 17:50:10 +0200135 if (vcpriv->remote.u.sa.sa_family != rem_addr->u.sa.sa_family)
Alexander Couzens6a161492020-07-12 13:45:50 +0200136 continue;
Pau Espin Pedrolf6ef9ba2023-04-27 17:50:10 +0200137 if (osmo_sockaddr_cmp(&vcpriv->remote, rem_addr))
Alexander Couzens6a161492020-07-12 13:45:50 +0200138 continue;
139
Alexander Couzens38b19e82020-09-23 23:56:37 +0200140 return nsvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200141 }
142
Alexander Couzens38b19e82020-09-23 23:56:37 +0200143 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200144}
145
146static inline int nsip_sendmsg(struct gprs_ns2_vc_bind *bind,
147 struct msgb *msg,
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100148 const struct osmo_sockaddr *dest)
Alexander Couzens6a161492020-07-12 13:45:50 +0200149{
Alexander Couzens6a161492020-07-12 13:45:50 +0200150 struct priv_bind *priv = bind->priv;
151
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100152 return osmo_iofd_sendto_msgb(priv->iofd, msg, 0, dest);
Alexander Couzens6a161492020-07-12 13:45:50 +0200153}
154
Harald Welte5bef2cc2020-09-18 22:33:24 +0200155/*! send the msg and free it afterwards.
156 * \param nsvc NS-VC on which the message shall be sent
157 * \param msg message to be sent
158 * \return number of bytes transmitted; negative on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200159static int nsip_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
160{
161 int rc;
162 struct gprs_ns2_vc_bind *bind = nsvc->bind;
163 struct priv_vc *priv = nsvc->priv;
164
165 rc = nsip_sendmsg(bind, msg, &priv->remote);
166
167 return rc;
168}
169
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100170static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200171{
172 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
173 if (!priv)
174 return NULL;
175
176 nsvc->priv = priv;
177 priv->remote = *remote;
178
179 return priv;
180}
181
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100182static void handle_nsip_recvfrom(struct osmo_io_fd *iofd, int error, struct msgb *msg,
183 const struct osmo_sockaddr *saddr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200184{
Alexander Couzenscce88282020-10-26 00:25:50 +0100185 int rc = 0;
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100186 struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200187 struct gprs_ns2_vc *nsvc;
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100188
Alexander Couzens6a161492020-07-12 13:45:50 +0200189 struct msgb *reject;
190
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100191 msg->l2h = msgb_data(msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200192
193 /* check if a vc is available */
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100194 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, saddr);
Alexander Couzens38b19e82020-09-23 23:56:37 +0200195 if (!nsvc) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200196 /* VC not found */
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100197 rc = ns2_create_vc(bind, msg, saddr, "newconnection", &reject, &nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200198 switch (rc) {
Alexander Couzensba5a9922021-01-25 16:03:23 +0100199 case NS2_CS_FOUND:
Alexander Couzens6a161492020-07-12 13:45:50 +0200200 break;
Alexander Couzensba5a9922021-01-25 16:03:23 +0100201 case NS2_CS_ERROR:
202 case NS2_CS_SKIPPED:
Alexander Couzens6a161492020-07-12 13:45:50 +0200203 rc = 0;
Alexander Couzens13010122020-09-24 00:54:51 +0200204 goto out;
Alexander Couzensba5a9922021-01-25 16:03:23 +0100205 case NS2_CS_REJECTED:
Alexander Couzens6a161492020-07-12 13:45:50 +0200206 /* nsip_sendmsg will free reject */
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100207 rc = nsip_sendmsg(bind, reject, saddr);
Alexander Couzens772ca612020-09-24 16:19:02 +0200208 goto out;
Alexander Couzensba5a9922021-01-25 16:03:23 +0100209 case NS2_CS_CREATED:
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100210 ns2_driver_alloc_vc(bind, nsvc, saddr);
Alexander Couzens20ed5912021-08-06 18:07:26 +0200211 /* only start the fsm for non SNS. SNS will take care of its own */
212 if (nsvc->nse->dialect != GPRS_NS2_DIALECT_SNS)
213 ns2_vc_fsm_start(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200214 break;
215 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200216 }
217
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100218 ns2_recv_vc(nsvc, msg);
219 return;
Alexander Couzenscce88282020-10-26 00:25:50 +0100220
Alexander Couzens13010122020-09-24 00:54:51 +0200221out:
Alexander Couzens6a161492020-07-12 13:45:50 +0200222 msgb_free(msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200223}
224
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100225static void handle_nsip_sendto(struct osmo_io_fd *iofd, int res,
Daniel Willmann3ed87722023-06-06 11:34:14 +0200226 struct msgb *msg,
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100227 const struct osmo_sockaddr *daddr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200228{
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100229 struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
230 struct gprs_ns2_vc *nsvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200231
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100232 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, daddr);
233 if (!nsvc)
234 return;
Alexander Couzens6a161492020-07-12 13:45:50 +0200235
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100236 if (OSMO_LIKELY(res >= 0)) {
237 RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
238 RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, res);
239 } else {
240 RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
241 RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, msgb_length(msg));
242 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200243}
244
Alexander Couzens4f608452020-10-11 18:41:24 +0200245/*! Find NS bind for a given socket address
246 * \param[in] nsi NS instance
247 * \param[in] sockaddr socket address to search for
248 * \return
249 */
250struct gprs_ns2_vc_bind *gprs_ns2_ip_bind_by_sockaddr(struct gprs_ns2_inst *nsi,
251 const struct osmo_sockaddr *sockaddr)
252{
253 struct gprs_ns2_vc_bind *bind;
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200254 const struct osmo_sockaddr *local;
Alexander Couzens4f608452020-10-11 18:41:24 +0200255
256 OSMO_ASSERT(nsi);
257 OSMO_ASSERT(sockaddr);
258
259 llist_for_each_entry(bind, &nsi->binding, list) {
260 if (!gprs_ns2_is_ip_bind(bind))
261 continue;
262
263 local = gprs_ns2_ip_bind_sockaddr(bind);
264 if (!osmo_sockaddr_cmp(sockaddr, local))
265 return bind;
266 }
267
268 return NULL;
269}
270
Harald Welte5bef2cc2020-09-18 22:33:24 +0200271/*! Bind to an IPv4/IPv6 address
272 * \param[in] nsi NS Instance in which to create the NSVC
273 * \param[in] local the local address to bind to
274 * \param[in] dscp the DSCP/TOS bits used for transmitted data
Alexander Couzensc80a8742021-02-03 11:27:52 +0100275 * \param[out] result pointer to the created bind or if a bind with the name exists return the bind.
276 * \return 0 on success; negative on error. -EALREADY returned in case a bind with the name exists */
Alexander Couzens6a161492020-07-12 13:45:50 +0200277int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100278 const char *name,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700279 const struct osmo_sockaddr *local,
Alexander Couzens6a161492020-07-12 13:45:50 +0200280 int dscp,
281 struct gprs_ns2_vc_bind **result)
282{
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200283 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200284 struct priv_bind *priv;
285 int rc;
286
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100287 struct osmo_io_ops ioops = {
288 .sendto_cb = &handle_nsip_sendto,
289 .recvfrom_cb = &handle_nsip_recvfrom,
290 };
291
Harald Weltec3aa8f92021-01-31 11:41:34 +0100292 if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6)
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100293 return -EINVAL;
294
Harald Weltec96d7162021-04-27 21:56:25 +0200295 if (dscp < 0 || dscp > 63)
296 return -EINVAL;
297
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200298 bind = gprs_ns2_ip_bind_by_sockaddr(nsi, local);
299 if (bind) {
Alexander Couzensc80a8742021-02-03 11:27:52 +0100300 if (result)
301 *result = bind;
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200302 return -EBUSY;
303 }
304
Harald Weltec3aa8f92021-01-31 11:41:34 +0100305 rc = ns2_bind_alloc(nsi, name, &bind);
306 if (rc < 0)
307 return rc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200308
309 bind->driver = &vc_driver_ip;
Alexander Couzensaac90162020-11-19 02:44:04 +0100310 bind->ll = GPRS_NS2_LL_UDP;
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100311 /* expect 100 mbit at least.
312 * TODO: ask the network layer about the speed. But would require
313 * notification on change. */
314 bind->transfer_capability = 100;
Alexander Couzens6a161492020-07-12 13:45:50 +0200315 bind->send_vc = nsip_vc_sendmsg;
316 bind->free_vc = free_vc;
Alexander Couzens22f34712020-10-02 02:34:39 +0200317 bind->dump_vty = dump_vty;
Alexander Couzens6a161492020-07-12 13:45:50 +0200318
319 priv = bind->priv = talloc_zero(bind, struct priv_bind);
320 if (!priv) {
Harald Weltec3aa8f92021-01-31 11:41:34 +0100321 gprs_ns2_free_bind(bind);
Harald Weltebdfb8b92021-01-31 11:44:57 +0100322 return -ENOMEM;
Alexander Couzens6a161492020-07-12 13:45:50 +0200323 }
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100324
Alexander Couzens6a161492020-07-12 13:45:50 +0200325 priv->addr = *local;
Harald Welte485b3f72021-04-28 19:45:34 +0200326 priv->dscp = dscp;
Alexander Couzens6a161492020-07-12 13:45:50 +0200327
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100328 rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
Alexander Couzens6a161492020-07-12 13:45:50 +0200329 local, NULL,
Harald Welte485b3f72021-04-28 19:45:34 +0200330 OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(priv->dscp));
Alexander Couzens6a161492020-07-12 13:45:50 +0200331 if (rc < 0) {
Harald Weltec3aa8f92021-01-31 11:41:34 +0100332 gprs_ns2_free_bind(bind);
Alexander Couzens6a161492020-07-12 13:45:50 +0200333 return rc;
334 }
335
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100336 priv->iofd = osmo_iofd_setup(bind, rc, "NS bind", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops, bind);
337 osmo_iofd_register(priv->iofd, rc);
338 osmo_iofd_set_alloc_info(priv->iofd, 4096, 128);
Daniel Willmann3236fdf2023-08-29 16:24:56 +0200339 osmo_iofd_set_txqueue_max_length(priv->iofd, nsi->txqueue_max_length);
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100340
Alexander Couzens4f1128f2021-01-20 17:42:48 +0100341 /* IPv4: max fragmented payload can be (13 bit) * 8 byte => 65535.
342 * IPv6: max payload can be 65535 (RFC 2460).
343 * UDP header = 8 byte */
344 bind->mtu = 65535 - 8;
Alexander Couzens6a161492020-07-12 13:45:50 +0200345 if (result)
346 *result = bind;
347
348 return 0;
349}
350
Harald Welte5bef2cc2020-09-18 22:33:24 +0200351/*! Create new NS-VC to a given remote address
352 * \param[in] bind the bind we want to connect
353 * \param[in] nse NS entity to be used for the new NS-VC
354 * \param[in] remote remote address to connect to
355 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100356struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
357 struct gprs_ns2_nse *nse,
358 const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200359{
360 struct gprs_ns2_vc *nsvc;
Alexander Couzensd7948062021-06-03 20:35:47 +0200361 const struct osmo_sockaddr *local;
Alexander Couzens6a161492020-07-12 13:45:50 +0200362 struct priv_vc *priv;
Alexander Couzensd923cff2020-12-01 01:03:52 +0100363 enum gprs_ns2_vc_mode vc_mode;
Alexander Couzensd7948062021-06-03 20:35:47 +0200364 char idbuf[256], tmp[INET6_ADDRSTRLEN + 8];
Alexander Couzens6a161492020-07-12 13:45:50 +0200365
Alexander Couzens55bc8692021-01-18 18:39:57 +0100366 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
367
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100368 vc_mode = ns2_dialect_to_vc_mode(nse->dialect);
Alexander Couzensd923cff2020-12-01 01:03:52 +0100369 if ((int) vc_mode == -1) {
Harald Weltef2949742021-01-20 14:54:14 +0100370 LOGNSE(nse, LOGL_ERROR, "Can not derive vc mode from dialect %d. Maybe libosmocore is too old.\n",
371 nse->dialect);
Alexander Couzensd923cff2020-12-01 01:03:52 +0100372 return NULL;
373 }
374
Alexander Couzens7bb39e32021-02-16 23:06:53 +0100375 /* duplicate */
376 if (gprs_ns2_nsvc_by_sockaddr_bind(bind, remote))
377 return NULL;
378
Alexander Couzensd7948062021-06-03 20:35:47 +0200379 local = gprs_ns2_ip_bind_sockaddr(bind);
380 osmo_sockaddr_to_str_buf(tmp, sizeof(tmp), local);
381 snprintf(idbuf, sizeof(idbuf), "NSE%05u-NSVC-%s-%s-%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
382 tmp, osmo_sockaddr_to_str(remote));
383 osmo_identifier_sanitize_buf(idbuf, NULL, '_');
384
Harald Welte603f4042020-11-29 17:39:19 +0100385 nsvc = ns2_vc_alloc(bind, nse, true, vc_mode, idbuf);
Alexander Couzensc06aa712020-11-18 23:56:45 +0100386 if (!nsvc)
387 return NULL;
388
Alexander Couzens6a161492020-07-12 13:45:50 +0200389 nsvc->priv = talloc_zero(bind, struct priv_vc);
390 if (!nsvc->priv) {
391 gprs_ns2_free_nsvc(nsvc);
392 return NULL;
393 }
394
395 priv = nsvc->priv;
396 priv->remote = *remote;
397
Alexander Couzens6a161492020-07-12 13:45:50 +0200398 return nsvc;
399}
400
Alexander Couzens979f5f52020-10-11 21:01:48 +0200401/*! Return the socket address of the local peer of a NS-VC.
402 * \param[in] nsvc NS-VC whose local peer we want to know
403 * \return address of the local peer; NULL in case of error */
404const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
405{
406 struct priv_bind *priv;
407
Alexander Couzens979f5f52020-10-11 21:01:48 +0200408 if (nsvc->bind->driver != &vc_driver_ip)
409 return NULL;
410
411 priv = nsvc->bind->priv;
412 return &priv->addr;
413}
414
Harald Welte5bef2cc2020-09-18 22:33:24 +0200415/*! Return the socket address of the remote peer of a NS-VC.
416 * \param[in] nsvc NS-VC whose remote peer we want to know
417 * \return address of the remote peer; NULL in case of error */
Alexander Couzensd33512b2020-10-11 21:42:11 +0200418const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200419{
420 struct priv_vc *priv;
421
Alexander Couzensaac90162020-11-19 02:44:04 +0100422 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzens6a161492020-07-12 13:45:50 +0200423 return NULL;
424
425 priv = nsvc->priv;
426 return &priv->remote;
427}
428
Alexander Couzensd420ea92020-10-12 01:11:05 +0200429/*! Compare the NS-VC with the given parameter
430 * \param[in] nsvc NS-VC to compare with
431 * \param[in] local The local address
432 * \param[in] remote The remote address
433 * \param[in] nsvci NS-VCI will only be used if the NS-VC in BLOCKRESET mode otherwise NS-VCI isn't applicable.
434 * \return true if the NS-VC has the same properties as given
435 */
436bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
437 const struct osmo_sockaddr *local,
438 const struct osmo_sockaddr *remote,
439 uint16_t nsvci)
440{
441 struct priv_vc *vpriv;
442 struct priv_bind *bpriv;
443
Alexander Couzensaac90162020-11-19 02:44:04 +0100444 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzensd420ea92020-10-12 01:11:05 +0200445 return false;
446
447 vpriv = nsvc->priv;
448 bpriv = nsvc->bind->priv;
449
450 if (osmo_sockaddr_cmp(local, &bpriv->addr))
451 return false;
452
453 if (osmo_sockaddr_cmp(remote, &vpriv->remote))
454 return false;
455
Alexander Couzens138b96f2021-01-25 16:23:29 +0100456 if (nsvc->mode == GPRS_NS2_VC_MODE_BLOCKRESET)
Alexander Couzensd420ea92020-10-12 01:11:05 +0200457 if (nsvc->nsvci != nsvci)
458 return false;
459
460 return true;
461}
462
Harald Welte5bef2cc2020-09-18 22:33:24 +0200463/*! Return the locally bound socket address of the bind.
464 * \param[in] bind The bind whose local address we want to know
465 * \return address of the local bind */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200466const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200467{
468 struct priv_bind *priv;
Alexander Couzens55bc8692021-01-18 18:39:57 +0100469 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
Alexander Couzens6a161492020-07-12 13:45:50 +0200470
471 priv = bind->priv;
472 return &priv->addr;
473}
474
Harald Welte5bef2cc2020-09-18 22:33:24 +0200475/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200476int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
477{
478 return (bind->driver == &vc_driver_ip);
479}
480
Harald Welte5bef2cc2020-09-18 22:33:24 +0200481/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200482int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
483{
484 struct priv_bind *priv;
485 int rc = 0;
486
Harald Welte628f5342021-04-28 19:41:07 +0200487 if (dscp < 0 || dscp > 63)
488 return -EINVAL;
489
Alexander Couzens55bc8692021-01-18 18:39:57 +0100490 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
Alexander Couzens6a161492020-07-12 13:45:50 +0200491 priv = bind->priv;
492
493 if (dscp != priv->dscp) {
494 priv->dscp = dscp;
495
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100496 rc = osmo_sock_set_dscp(osmo_iofd_get_fd(priv->iofd), dscp);
Harald Weltef2949742021-01-20 14:54:14 +0100497 if (rc < 0) {
Harald Welte628f5342021-04-28 19:41:07 +0200498 LOGBIND(bind, LOGL_ERROR, "Failed to set the DSCP to %u with ret(%d) errno(%d)\n",
Harald Weltef2949742021-01-20 14:54:14 +0100499 dscp, rc, errno);
500 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200501 }
502
503 return rc;
504}
Alexander Couzense769f522020-12-07 07:37:07 +0100505
Harald Welted99e4ee2021-04-28 19:57:12 +0200506/*! Set the socket priority of the given bind. */
507int gprs_ns2_ip_bind_set_priority(struct gprs_ns2_vc_bind *bind, uint8_t priority)
508{
509 struct priv_bind *priv;
510 int rc = 0;
511
512 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
513 priv = bind->priv;
514
515 if (priority != priv->priority) {
516 priv->priority = priority;
517
Daniel Willmannfa3a9ce2022-11-21 19:27:18 +0100518 rc = osmo_sock_set_priority(osmo_iofd_get_fd(priv->iofd), priority);
Harald Welted99e4ee2021-04-28 19:57:12 +0200519 if (rc < 0) {
520 LOGBIND(bind, LOGL_ERROR, "Failed to set the priority to %u with ret(%d) errno(%d)\n",
521 priority, rc, errno);
522 }
523 }
524
525 return rc;
526}
527
528
Alexander Couzense769f522020-12-07 07:37:07 +0100529/*! Count UDP binds compatible with remote */
530int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote)
531{
532 struct gprs_ns2_vc_bind *bind;
533 const struct osmo_sockaddr *sa;
534 int count = 0;
535
536 llist_for_each_entry(bind, &nsi->binding, list) {
537 if (!gprs_ns2_is_ip_bind(bind))
538 continue;
539
540 sa = gprs_ns2_ip_bind_sockaddr(bind);
541 if (!sa)
542 continue;
543
544 if (sa->u.sa.sa_family == remote->u.sa.sa_family)
545 count++;
546 }
547
548 return count;
549}
550
551/* return the matching bind by index */
552struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
553 struct osmo_sockaddr *remote,
554 int index)
555{
556 struct gprs_ns2_vc_bind *bind;
557 const struct osmo_sockaddr *sa;
558 int i = 0;
559
560 llist_for_each_entry(bind, &nsi->binding, list) {
561 if (!gprs_ns2_is_ip_bind(bind))
562 continue;
563
564 sa = gprs_ns2_ip_bind_sockaddr(bind);
565 if (!sa)
566 continue;
567
568 if (sa->u.sa.sa_family == remote->u.sa.sa_family) {
569 if (index == i)
570 return bind;
571 i++;
572 }
573 }
574
575 return NULL;
576}
Alexander Couzensc4704762021-02-08 23:13:12 +0100577
Daniel Willmann3236fdf2023-08-29 16:24:56 +0200578void ns2_ip_set_txqueue_max_length(struct gprs_ns2_vc_bind *bind, unsigned int max_length)
579{
580 struct priv_bind *priv = bind->priv;
581 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
582
583 osmo_iofd_set_txqueue_max_length(priv->iofd, max_length);
584}
585
Alexander Couzensc4704762021-02-08 23:13:12 +0100586/*! set the signalling and data weight for this bind
587 * \param[in] bind
588 * \param[in] signalling the signalling weight
589 * \param[in] data the data weight
590 */
591void gprs_ns2_ip_bind_set_sns_weight(struct gprs_ns2_vc_bind *bind, uint8_t signalling, uint8_t data)
592{
593 OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
594 bind->sns_sig_weight = signalling;
595 bind->sns_data_weight = data;
596 ns2_sns_update_weights(bind);
597}