blob: 9aca80b20d338d9af9b6ea1a782402fdd772b2a2 [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{
209 int rc;
210 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 Couzens13010122020-09-24 00:54:51 +0200243 rc = ns2_recv_vc(nsvc, msg);
244out:
Alexander Couzens6a161492020-07-12 13:45:50 +0200245 msgb_free(msg);
246
247 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;
325 bind->send_vc = nsip_vc_sendmsg;
326 bind->free_vc = free_vc;
Alexander Couzens22f34712020-10-02 02:34:39 +0200327 bind->dump_vty = dump_vty;
Alexander Couzens6a161492020-07-12 13:45:50 +0200328 bind->nsi = nsi;
329
330 priv = bind->priv = talloc_zero(bind, struct priv_bind);
331 if (!priv) {
332 talloc_free(bind);
333 return -ENOSPC;
334 }
335 priv->fd.cb = nsip_fd_cb;
336 priv->fd.data = bind;
337 priv->addr = *local;
338 INIT_LLIST_HEAD(&bind->nsvc);
339
Alexander Couzens6a161492020-07-12 13:45:50 +0200340 rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_DGRAM, IPPROTO_UDP,
341 local, NULL,
342 OSMO_SOCK_F_BIND);
343 if (rc < 0) {
344 talloc_free(priv);
345 talloc_free(bind);
346 return rc;
347 }
348
349 if (dscp > 0) {
350 priv->dscp = dscp;
351
352 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
353 &dscp, sizeof(dscp));
354 if (rc < 0)
355 LOGP(DLNS, LOGL_ERROR,
356 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
357 dscp, rc, errno);
358 }
359
Pau Espin Pedrolaa83d412020-10-09 14:20:13 +0200360 llist_add(&bind->list, &nsi->binding);
Alexander Couzens6a161492020-07-12 13:45:50 +0200361 ns2_vty_bind_apply(bind);
362
363 if (result)
364 *result = bind;
365
366 return 0;
367}
368
Harald Welte5bef2cc2020-09-18 22:33:24 +0200369/*! Create new NS-VC to a given remote address
370 * \param[in] bind the bind we want to connect
371 * \param[in] nse NS entity to be used for the new NS-VC
372 * \param[in] remote remote address to connect to
373 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200374struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
375 struct gprs_ns2_nse *nse,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700376 const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200377{
378 struct gprs_ns2_vc *nsvc;
379 struct priv_vc *priv;
380
381 nsvc = ns2_vc_alloc(bind, nse, true);
382 nsvc->priv = talloc_zero(bind, struct priv_vc);
383 if (!nsvc->priv) {
384 gprs_ns2_free_nsvc(nsvc);
385 return NULL;
386 }
387
388 priv = nsvc->priv;
389 priv->remote = *remote;
390
391 nsvc->ll = GPRS_NS_LL_UDP;
392
393 return nsvc;
394}
395
Harald Welte5bef2cc2020-09-18 22:33:24 +0200396/*! Return the socket address of the remote peer of a NS-VC.
397 * \param[in] nsvc NS-VC whose remote peer we want to know
398 * \return address of the remote peer; NULL in case of error */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200399const struct osmo_sockaddr *gprs_ns2_ip_vc_sockaddr(struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200400{
401 struct priv_vc *priv;
402
403 if (nsvc->ll != GPRS_NS_LL_UDP)
404 return NULL;
405
406 priv = nsvc->priv;
407 return &priv->remote;
408}
409
Harald Welte5bef2cc2020-09-18 22:33:24 +0200410/*! Return the locally bound socket address of the bind.
411 * \param[in] bind The bind whose local address we want to know
412 * \return address of the local bind */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200413const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200414{
415 struct priv_bind *priv;
416
417 priv = bind->priv;
418 return &priv->addr;
419}
420
Harald Welte5bef2cc2020-09-18 22:33:24 +0200421/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200422int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
423{
424 return (bind->driver == &vc_driver_ip);
425}
426
Harald Welte5bef2cc2020-09-18 22:33:24 +0200427/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200428int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
429{
430 struct priv_bind *priv;
431 int rc = 0;
432
433 priv = bind->priv;
434
435 if (dscp != priv->dscp) {
436 priv->dscp = dscp;
437
438 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
439 &dscp, sizeof(dscp));
440 if (rc < 0)
441 LOGP(DLNS, LOGL_ERROR,
442 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
443 dscp, rc, errno);
444 }
445
446 return rc;
447}