blob: 7f826677f1299516a465a5f7225673bbe4352a6a [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
100 vty_out(vty, "UDP bind: %s:%d dcsp: %d%s", sockstr.ip, sockstr.port, priv->dscp, VTY_NEWLINE);
101 vty_out(vty, " %lu NS-VC: %s", nsvcs, VTY_NEWLINE);
102
103 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
104 vty_out(vty, " %s%s", gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
105 }
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 ns2_vty_bind_apply(bind);
363
364 if (result)
365 *result = bind;
366
367 return 0;
368}
369
Harald Welte5bef2cc2020-09-18 22:33:24 +0200370/*! Create new NS-VC to a given remote address
371 * \param[in] bind the bind we want to connect
372 * \param[in] nse NS entity to be used for the new NS-VC
373 * \param[in] remote remote address to connect to
374 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200375struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
376 struct gprs_ns2_nse *nse,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700377 const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200378{
379 struct gprs_ns2_vc *nsvc;
380 struct priv_vc *priv;
381
382 nsvc = ns2_vc_alloc(bind, nse, true);
Alexander Couzensc06aa712020-11-18 23:56:45 +0100383 if (!nsvc)
384 return NULL;
385
Alexander Couzens6a161492020-07-12 13:45:50 +0200386 nsvc->priv = talloc_zero(bind, struct priv_vc);
387 if (!nsvc->priv) {
388 gprs_ns2_free_nsvc(nsvc);
389 return NULL;
390 }
391
392 priv = nsvc->priv;
393 priv->remote = *remote;
394
Alexander Couzens6a161492020-07-12 13:45:50 +0200395 return nsvc;
396}
397
Alexander Couzens979f5f52020-10-11 21:01:48 +0200398/*! Return the socket address of the local peer of a NS-VC.
399 * \param[in] nsvc NS-VC whose local peer we want to know
400 * \return address of the local peer; NULL in case of error */
401const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
402{
403 struct priv_bind *priv;
404
Alexander Couzens979f5f52020-10-11 21:01:48 +0200405 if (nsvc->bind->driver != &vc_driver_ip)
406 return NULL;
407
408 priv = nsvc->bind->priv;
409 return &priv->addr;
410}
411
Harald Welte5bef2cc2020-09-18 22:33:24 +0200412/*! Return the socket address of the remote peer of a NS-VC.
413 * \param[in] nsvc NS-VC whose remote peer we want to know
414 * \return address of the remote peer; NULL in case of error */
Alexander Couzensd33512b2020-10-11 21:42:11 +0200415const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200416{
417 struct priv_vc *priv;
418
Alexander Couzensaac90162020-11-19 02:44:04 +0100419 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzens6a161492020-07-12 13:45:50 +0200420 return NULL;
421
422 priv = nsvc->priv;
423 return &priv->remote;
424}
425
Alexander Couzensd420ea92020-10-12 01:11:05 +0200426/*! Compare the NS-VC with the given parameter
427 * \param[in] nsvc NS-VC to compare with
428 * \param[in] local The local address
429 * \param[in] remote The remote address
430 * \param[in] nsvci NS-VCI will only be used if the NS-VC in BLOCKRESET mode otherwise NS-VCI isn't applicable.
431 * \return true if the NS-VC has the same properties as given
432 */
433bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
434 const struct osmo_sockaddr *local,
435 const struct osmo_sockaddr *remote,
436 uint16_t nsvci)
437{
438 struct priv_vc *vpriv;
439 struct priv_bind *bpriv;
440
Alexander Couzensaac90162020-11-19 02:44:04 +0100441 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzensd420ea92020-10-12 01:11:05 +0200442 return false;
443
444 vpriv = nsvc->priv;
445 bpriv = nsvc->bind->priv;
446
447 if (osmo_sockaddr_cmp(local, &bpriv->addr))
448 return false;
449
450 if (osmo_sockaddr_cmp(remote, &vpriv->remote))
451 return false;
452
453 if (nsvc->mode == NS2_VC_MODE_BLOCKRESET)
454 if (nsvc->nsvci != nsvci)
455 return false;
456
457 return true;
458}
459
Harald Welte5bef2cc2020-09-18 22:33:24 +0200460/*! Return the locally bound socket address of the bind.
461 * \param[in] bind The bind whose local address we want to know
462 * \return address of the local bind */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200463const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200464{
465 struct priv_bind *priv;
466
467 priv = bind->priv;
468 return &priv->addr;
469}
470
Harald Welte5bef2cc2020-09-18 22:33:24 +0200471/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200472int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
473{
474 return (bind->driver == &vc_driver_ip);
475}
476
Harald Welte5bef2cc2020-09-18 22:33:24 +0200477/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200478int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
479{
480 struct priv_bind *priv;
481 int rc = 0;
482
483 priv = bind->priv;
484
485 if (dscp != priv->dscp) {
486 priv->dscp = dscp;
487
488 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
489 &dscp, sizeof(dscp));
490 if (rc < 0)
491 LOGP(DLNS, LOGL_ERROR,
492 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
493 dscp, rc, errno);
494 }
495
496 return rc;
497}