blob: b923e81805da09786f42062e22fa873761838146 [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
31#include <osmocom/core/select.h>
32#include <osmocom/core/sockaddr_str.h>
33#include <osmocom/core/socket.h>
34#include <osmocom/gprs/gprs_ns2.h>
35
36#include "common_vty.h"
37#include "gprs_ns2_internal.h"
38
39
40static void free_bind(struct gprs_ns2_vc_bind *bind);
41
42
43struct gprs_ns2_vc_driver vc_driver_ip = {
44 .name = "GB UDP IPv4/IPv6",
45 .free_bind = free_bind,
46};
47
48struct priv_bind {
49 struct osmo_fd fd;
50 struct osmo_sockaddr addr;
51 int dscp;
52};
53
54struct priv_vc {
55 struct osmo_sockaddr remote;
56};
57
58/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
59static void free_bind(struct gprs_ns2_vc_bind *bind)
60{
61 struct priv_bind *priv;
62
63 if (!bind)
64 return;
65
66 priv = bind->priv;
67
68 osmo_fd_close(&priv->fd);
69 talloc_free(priv);
70}
71
72static void free_vc(struct gprs_ns2_vc *nsvc)
73{
74 if (!nsvc->priv)
75 return;
76
77 talloc_free(nsvc->priv);
78 nsvc->priv = NULL;
79}
80
Alexander Couzens22f34712020-10-02 02:34:39 +020081static void dump_vty(const struct gprs_ns2_vc_bind *bind,
82 struct vty *vty, bool _stats)
83{
84 struct priv_bind *priv;
85 struct gprs_ns2_vc *nsvc;
86 struct osmo_sockaddr_str sockstr = {};
87 unsigned long nsvcs = 0;
88
89 if (!bind)
90 return;
91
92 priv = bind->priv;
93 if (osmo_sockaddr_str_from_sockaddr(&sockstr, &priv->addr.u.sas))
94 strcpy(sockstr.ip, "invalid");
95
96 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
97 nsvcs++;
98 }
99
Harald Welte1e72df02020-12-01 18:20:25 +0100100 vty_out(vty, "UDP bind: %s:%d DSCP: %d%s", sockstr.ip, sockstr.port, priv->dscp, VTY_NEWLINE);
Alexander Couzens22f34712020-10-02 02:34:39 +0200101 vty_out(vty, " %lu NS-VC: %s", nsvcs, VTY_NEWLINE);
102
103 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100104 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens22f34712020-10-02 02:34:39 +0200105 }
106}
107
108
Harald Welte5bef2cc2020-09-18 22:33:24 +0200109/*! Find a NS-VC by its remote socket address.
110 * \param[in] bind in which to search
111 * \param[in] saddr remote peer socket adddress to search
Alexander Couzens38b19e82020-09-23 23:56:37 +0200112 * \returns NS-VC matching sockaddr; NULL if none found */
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700113struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind,
114 const struct osmo_sockaddr *saddr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200115{
116 struct gprs_ns2_vc *nsvc;
117 struct priv_vc *vcpriv;
118
Alexander Couzens6a161492020-07-12 13:45:50 +0200119 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
120 vcpriv = nsvc->priv;
121 if (vcpriv->remote.u.sa.sa_family != saddr->u.sa.sa_family)
122 continue;
123 if (osmo_sockaddr_cmp(&vcpriv->remote, saddr))
124 continue;
125
Alexander Couzens38b19e82020-09-23 23:56:37 +0200126 return nsvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200127 }
128
Alexander Couzens38b19e82020-09-23 23:56:37 +0200129 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200130}
131
132static inline int nsip_sendmsg(struct gprs_ns2_vc_bind *bind,
133 struct msgb *msg,
134 struct osmo_sockaddr *dest)
135{
136 int rc;
137 struct priv_bind *priv = bind->priv;
138
139 rc = sendto(priv->fd.fd, msg->data, msg->len, 0,
140 &dest->u.sa, sizeof(*dest));
141
142 msgb_free(msg);
143
144 return rc;
145}
146
Harald Welte5bef2cc2020-09-18 22:33:24 +0200147/*! send the msg and free it afterwards.
148 * \param nsvc NS-VC on which the message shall be sent
149 * \param msg message to be sent
150 * \return number of bytes transmitted; negative on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200151static int nsip_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
152{
153 int rc;
154 struct gprs_ns2_vc_bind *bind = nsvc->bind;
155 struct priv_vc *priv = nsvc->priv;
156
157 rc = nsip_sendmsg(bind, msg, &priv->remote);
158
159 return rc;
160}
161
162/* Read a single NS-over-IP message */
163static struct msgb *read_nsip_msg(struct osmo_fd *bfd, int *error,
164 struct osmo_sockaddr *saddr)
165{
166 struct msgb *msg = gprs_ns2_msgb_alloc();
167 int ret = 0;
168 socklen_t saddr_len = sizeof(*saddr);
169
170 if (!msg) {
171 *error = -ENOMEM;
172 return NULL;
173 }
174
175 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0,
176 &saddr->u.sa, &saddr_len);
177 if (ret < 0) {
178 LOGP(DLNS, LOGL_ERROR, "recv error %s during NSIP recvfrom %s\n",
179 strerror(errno), osmo_sock_get_name2(bfd->fd));
180 msgb_free(msg);
181 *error = ret;
182 return NULL;
183 } else if (ret == 0) {
184 msgb_free(msg);
185 *error = ret;
186 return NULL;
187 }
188
189 msg->l2h = msg->data;
190 msgb_put(msg, ret);
191
192 return msg;
193}
194
195static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, struct osmo_sockaddr *remote)
196{
197 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
198 if (!priv)
199 return NULL;
200
201 nsvc->priv = priv;
202 priv->remote = *remote;
203
204 return priv;
205}
206
207static int handle_nsip_read(struct osmo_fd *bfd)
208{
Alexander Couzenscce88282020-10-26 00:25:50 +0100209 int rc = 0;
Alexander Couzens6a161492020-07-12 13:45:50 +0200210 int error = 0;
211 struct gprs_ns2_vc_bind *bind = bfd->data;
212 struct osmo_sockaddr saddr;
213 struct gprs_ns2_vc *nsvc;
214 struct msgb *msg = read_nsip_msg(bfd, &error, &saddr);
215 struct msgb *reject;
216
217 if (!msg)
218 return -EINVAL;
219
220 /* check if a vc is available */
Alexander Couzens38b19e82020-09-23 23:56:37 +0200221 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &saddr);
222 if (!nsvc) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200223 /* VC not found */
224 rc = ns2_create_vc(bind, msg, "newconnection", &reject, &nsvc);
225 switch (rc) {
226 case GPRS_NS2_CS_FOUND:
Alexander Couzens6a161492020-07-12 13:45:50 +0200227 break;
228 case GPRS_NS2_CS_ERROR:
229 case GPRS_NS2_CS_SKIPPED:
230 rc = 0;
Alexander Couzens13010122020-09-24 00:54:51 +0200231 goto out;
Alexander Couzens6a161492020-07-12 13:45:50 +0200232 case GPRS_NS2_CS_REJECTED:
233 /* nsip_sendmsg will free reject */
Alexander Couzens772ca612020-09-24 16:19:02 +0200234 rc = nsip_sendmsg(bind, reject, &saddr);
235 goto out;
Alexander Couzens6a161492020-07-12 13:45:50 +0200236 case GPRS_NS2_CS_CREATED:
237 ns2_driver_alloc_vc(bind, nsvc, &saddr);
238 gprs_ns2_vc_fsm_start(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200239 break;
240 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200241 }
242
Alexander Couzenscce88282020-10-26 00:25:50 +0100243 return ns2_recv_vc(nsvc, msg);
244
Alexander Couzens13010122020-09-24 00:54:51 +0200245out:
Alexander Couzens6a161492020-07-12 13:45:50 +0200246 msgb_free(msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200247 return rc;
248}
249
250static int handle_nsip_write(struct osmo_fd *bfd)
251{
252 /* FIXME: actually send the data here instead of nsip_sendmsg() */
253 return -EIO;
254}
255
256static int nsip_fd_cb(struct osmo_fd *bfd, unsigned int what)
257{
258 int rc = 0;
259
260 if (what & OSMO_FD_READ)
261 rc = handle_nsip_read(bfd);
262 if (what & OSMO_FD_WRITE)
263 rc = handle_nsip_write(bfd);
264
265 return rc;
266}
267
Alexander Couzens4f608452020-10-11 18:41:24 +0200268/*! Find NS bind for a given socket address
269 * \param[in] nsi NS instance
270 * \param[in] sockaddr socket address to search for
271 * \return
272 */
273struct gprs_ns2_vc_bind *gprs_ns2_ip_bind_by_sockaddr(struct gprs_ns2_inst *nsi,
274 const struct osmo_sockaddr *sockaddr)
275{
276 struct gprs_ns2_vc_bind *bind;
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200277 const struct osmo_sockaddr *local;
Alexander Couzens4f608452020-10-11 18:41:24 +0200278
279 OSMO_ASSERT(nsi);
280 OSMO_ASSERT(sockaddr);
281
282 llist_for_each_entry(bind, &nsi->binding, list) {
283 if (!gprs_ns2_is_ip_bind(bind))
284 continue;
285
286 local = gprs_ns2_ip_bind_sockaddr(bind);
287 if (!osmo_sockaddr_cmp(sockaddr, local))
288 return bind;
289 }
290
291 return NULL;
292}
293
Harald Welte5bef2cc2020-09-18 22:33:24 +0200294/*! Bind to an IPv4/IPv6 address
295 * \param[in] nsi NS Instance in which to create the NSVC
296 * \param[in] local the local address to bind to
297 * \param[in] dscp the DSCP/TOS bits used for transmitted data
298 * \param[out] result if set, returns the bind object
299 * \return 0 on success; negative in case of error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200300int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700301 const struct osmo_sockaddr *local,
Alexander Couzens6a161492020-07-12 13:45:50 +0200302 int dscp,
303 struct gprs_ns2_vc_bind **result)
304{
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200305 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200306 struct priv_bind *priv;
307 int rc;
308
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200309 bind = gprs_ns2_ip_bind_by_sockaddr(nsi, local);
310 if (bind) {
311 *result = bind;
312 return -EBUSY;
313 }
314
315 bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
Alexander Couzens6a161492020-07-12 13:45:50 +0200316 if (!bind)
317 return -ENOSPC;
318
319 if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6) {
320 talloc_free(bind);
321 return -EINVAL;
322 }
323
324 bind->driver = &vc_driver_ip;
Alexander Couzensaac90162020-11-19 02:44:04 +0100325 bind->ll = GPRS_NS2_LL_UDP;
Alexander Couzens6a161492020-07-12 13:45:50 +0200326 bind->send_vc = nsip_vc_sendmsg;
327 bind->free_vc = free_vc;
Alexander Couzens22f34712020-10-02 02:34:39 +0200328 bind->dump_vty = dump_vty;
Alexander Couzens6a161492020-07-12 13:45:50 +0200329 bind->nsi = nsi;
330
331 priv = bind->priv = talloc_zero(bind, struct priv_bind);
332 if (!priv) {
333 talloc_free(bind);
334 return -ENOSPC;
335 }
336 priv->fd.cb = nsip_fd_cb;
337 priv->fd.data = bind;
338 priv->addr = *local;
339 INIT_LLIST_HEAD(&bind->nsvc);
340
Alexander Couzens6a161492020-07-12 13:45:50 +0200341 rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_DGRAM, IPPROTO_UDP,
342 local, NULL,
343 OSMO_SOCK_F_BIND);
344 if (rc < 0) {
345 talloc_free(priv);
346 talloc_free(bind);
347 return rc;
348 }
349
350 if (dscp > 0) {
351 priv->dscp = dscp;
352
353 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
354 &dscp, sizeof(dscp));
355 if (rc < 0)
356 LOGP(DLNS, LOGL_ERROR,
357 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
358 dscp, rc, errno);
359 }
360
Pau Espin Pedrolaa83d412020-10-09 14:20:13 +0200361 llist_add(&bind->list, &nsi->binding);
Alexander Couzens6a161492020-07-12 13:45:50 +0200362 if (result)
363 *result = bind;
364
365 return 0;
366}
367
Harald Welte5bef2cc2020-09-18 22:33:24 +0200368/*! Create new NS-VC to a given remote address
369 * \param[in] bind the bind we want to connect
370 * \param[in] nse NS entity to be used for the new NS-VC
371 * \param[in] remote remote address to connect to
372 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200373struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
374 struct gprs_ns2_nse *nse,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700375 const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200376{
377 struct gprs_ns2_vc *nsvc;
378 struct priv_vc *priv;
Alexander Couzensd923cff2020-12-01 01:03:52 +0100379 enum gprs_ns2_vc_mode vc_mode;
Alexander Couzens6a161492020-07-12 13:45:50 +0200380
Alexander Couzensd923cff2020-12-01 01:03:52 +0100381 vc_mode = gprs_ns2_dialect_to_vc_mode(nse->dialect);
382 if ((int) vc_mode == -1) {
383 LOGP(DLNS, LOGL_ERROR, "Can not derive vc mode from dialect %d. Maybe libosmocore is too old.\n",
384 nse->dialect);
385 return NULL;
386 }
387
388 nsvc = ns2_vc_alloc(bind, nse, true, vc_mode);
Alexander Couzensc06aa712020-11-18 23:56:45 +0100389 if (!nsvc)
390 return NULL;
391
Alexander Couzens6a161492020-07-12 13:45:50 +0200392 nsvc->priv = talloc_zero(bind, struct priv_vc);
393 if (!nsvc->priv) {
394 gprs_ns2_free_nsvc(nsvc);
395 return NULL;
396 }
397
398 priv = nsvc->priv;
399 priv->remote = *remote;
400
Alexander Couzens6a161492020-07-12 13:45:50 +0200401 return nsvc;
402}
403
Alexander Couzens979f5f52020-10-11 21:01:48 +0200404/*! Return the socket address of the local peer of a NS-VC.
405 * \param[in] nsvc NS-VC whose local peer we want to know
406 * \return address of the local peer; NULL in case of error */
407const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
408{
409 struct priv_bind *priv;
410
Alexander Couzens979f5f52020-10-11 21:01:48 +0200411 if (nsvc->bind->driver != &vc_driver_ip)
412 return NULL;
413
414 priv = nsvc->bind->priv;
415 return &priv->addr;
416}
417
Harald Welte5bef2cc2020-09-18 22:33:24 +0200418/*! Return the socket address of the remote peer of a NS-VC.
419 * \param[in] nsvc NS-VC whose remote peer we want to know
420 * \return address of the remote peer; NULL in case of error */
Alexander Couzensd33512b2020-10-11 21:42:11 +0200421const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200422{
423 struct priv_vc *priv;
424
Alexander Couzensaac90162020-11-19 02:44:04 +0100425 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzens6a161492020-07-12 13:45:50 +0200426 return NULL;
427
428 priv = nsvc->priv;
429 return &priv->remote;
430}
431
Alexander Couzensd420ea92020-10-12 01:11:05 +0200432/*! Compare the NS-VC with the given parameter
433 * \param[in] nsvc NS-VC to compare with
434 * \param[in] local The local address
435 * \param[in] remote The remote address
436 * \param[in] nsvci NS-VCI will only be used if the NS-VC in BLOCKRESET mode otherwise NS-VCI isn't applicable.
437 * \return true if the NS-VC has the same properties as given
438 */
439bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
440 const struct osmo_sockaddr *local,
441 const struct osmo_sockaddr *remote,
442 uint16_t nsvci)
443{
444 struct priv_vc *vpriv;
445 struct priv_bind *bpriv;
446
Alexander Couzensaac90162020-11-19 02:44:04 +0100447 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzensd420ea92020-10-12 01:11:05 +0200448 return false;
449
450 vpriv = nsvc->priv;
451 bpriv = nsvc->bind->priv;
452
453 if (osmo_sockaddr_cmp(local, &bpriv->addr))
454 return false;
455
456 if (osmo_sockaddr_cmp(remote, &vpriv->remote))
457 return false;
458
459 if (nsvc->mode == NS2_VC_MODE_BLOCKRESET)
460 if (nsvc->nsvci != nsvci)
461 return false;
462
463 return true;
464}
465
Harald Welte5bef2cc2020-09-18 22:33:24 +0200466/*! Return the locally bound socket address of the bind.
467 * \param[in] bind The bind whose local address we want to know
468 * \return address of the local bind */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200469const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200470{
471 struct priv_bind *priv;
472
473 priv = bind->priv;
474 return &priv->addr;
475}
476
Harald Welte5bef2cc2020-09-18 22:33:24 +0200477/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200478int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
479{
480 return (bind->driver == &vc_driver_ip);
481}
482
Harald Welte5bef2cc2020-09-18 22:33:24 +0200483/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200484int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
485{
486 struct priv_bind *priv;
487 int rc = 0;
488
489 priv = bind->priv;
490
491 if (dscp != priv->dscp) {
492 priv->dscp = dscp;
493
494 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
495 &dscp, sizeof(dscp));
496 if (rc < 0)
497 LOGP(DLNS, LOGL_ERROR,
498 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
499 dscp, rc, errno);
500 }
501
502 return rc;
503}