blob: 3ce05084fd8cadcf39c5608c1dd828738a6400c3 [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 */
113struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind, struct osmo_sockaddr *saddr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200114{
115 struct gprs_ns2_vc *nsvc;
116 struct priv_vc *vcpriv;
117
Alexander Couzens6a161492020-07-12 13:45:50 +0200118 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
119 vcpriv = nsvc->priv;
120 if (vcpriv->remote.u.sa.sa_family != saddr->u.sa.sa_family)
121 continue;
122 if (osmo_sockaddr_cmp(&vcpriv->remote, saddr))
123 continue;
124
Alexander Couzens38b19e82020-09-23 23:56:37 +0200125 return nsvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200126 }
127
Alexander Couzens38b19e82020-09-23 23:56:37 +0200128 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200129}
130
131static inline int nsip_sendmsg(struct gprs_ns2_vc_bind *bind,
132 struct msgb *msg,
133 struct osmo_sockaddr *dest)
134{
135 int rc;
136 struct priv_bind *priv = bind->priv;
137
138 rc = sendto(priv->fd.fd, msg->data, msg->len, 0,
139 &dest->u.sa, sizeof(*dest));
140
141 msgb_free(msg);
142
143 return rc;
144}
145
Harald Welte5bef2cc2020-09-18 22:33:24 +0200146/*! send the msg and free it afterwards.
147 * \param nsvc NS-VC on which the message shall be sent
148 * \param msg message to be sent
149 * \return number of bytes transmitted; negative on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200150static int nsip_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
151{
152 int rc;
153 struct gprs_ns2_vc_bind *bind = nsvc->bind;
154 struct priv_vc *priv = nsvc->priv;
155
156 rc = nsip_sendmsg(bind, msg, &priv->remote);
157
158 return rc;
159}
160
161/* Read a single NS-over-IP message */
162static struct msgb *read_nsip_msg(struct osmo_fd *bfd, int *error,
163 struct osmo_sockaddr *saddr)
164{
165 struct msgb *msg = gprs_ns2_msgb_alloc();
166 int ret = 0;
167 socklen_t saddr_len = sizeof(*saddr);
168
169 if (!msg) {
170 *error = -ENOMEM;
171 return NULL;
172 }
173
174 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0,
175 &saddr->u.sa, &saddr_len);
176 if (ret < 0) {
177 LOGP(DLNS, LOGL_ERROR, "recv error %s during NSIP recvfrom %s\n",
178 strerror(errno), osmo_sock_get_name2(bfd->fd));
179 msgb_free(msg);
180 *error = ret;
181 return NULL;
182 } else if (ret == 0) {
183 msgb_free(msg);
184 *error = ret;
185 return NULL;
186 }
187
188 msg->l2h = msg->data;
189 msgb_put(msg, ret);
190
191 return msg;
192}
193
194static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, struct osmo_sockaddr *remote)
195{
196 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
197 if (!priv)
198 return NULL;
199
200 nsvc->priv = priv;
201 priv->remote = *remote;
202
203 return priv;
204}
205
206static int handle_nsip_read(struct osmo_fd *bfd)
207{
208 int rc;
209 int error = 0;
210 struct gprs_ns2_vc_bind *bind = bfd->data;
211 struct osmo_sockaddr saddr;
212 struct gprs_ns2_vc *nsvc;
213 struct msgb *msg = read_nsip_msg(bfd, &error, &saddr);
214 struct msgb *reject;
215
216 if (!msg)
217 return -EINVAL;
218
219 /* check if a vc is available */
Alexander Couzens38b19e82020-09-23 23:56:37 +0200220 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &saddr);
221 if (!nsvc) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200222 /* VC not found */
223 rc = ns2_create_vc(bind, msg, "newconnection", &reject, &nsvc);
224 switch (rc) {
225 case GPRS_NS2_CS_FOUND:
Alexander Couzens6a161492020-07-12 13:45:50 +0200226 break;
227 case GPRS_NS2_CS_ERROR:
228 case GPRS_NS2_CS_SKIPPED:
229 rc = 0;
Alexander Couzens13010122020-09-24 00:54:51 +0200230 goto out;
Alexander Couzens6a161492020-07-12 13:45:50 +0200231 case GPRS_NS2_CS_REJECTED:
232 /* nsip_sendmsg will free reject */
Alexander Couzens772ca612020-09-24 16:19:02 +0200233 rc = nsip_sendmsg(bind, reject, &saddr);
234 goto out;
Alexander Couzens6a161492020-07-12 13:45:50 +0200235 case GPRS_NS2_CS_CREATED:
236 ns2_driver_alloc_vc(bind, nsvc, &saddr);
237 gprs_ns2_vc_fsm_start(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200238 break;
239 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200240 }
241
Alexander Couzens13010122020-09-24 00:54:51 +0200242 rc = ns2_recv_vc(nsvc, msg);
243out:
Alexander Couzens6a161492020-07-12 13:45:50 +0200244 msgb_free(msg);
245
246 return rc;
247}
248
249static int handle_nsip_write(struct osmo_fd *bfd)
250{
251 /* FIXME: actually send the data here instead of nsip_sendmsg() */
252 return -EIO;
253}
254
255static int nsip_fd_cb(struct osmo_fd *bfd, unsigned int what)
256{
257 int rc = 0;
258
259 if (what & OSMO_FD_READ)
260 rc = handle_nsip_read(bfd);
261 if (what & OSMO_FD_WRITE)
262 rc = handle_nsip_write(bfd);
263
264 return rc;
265}
266
Harald Welte5bef2cc2020-09-18 22:33:24 +0200267/*! Bind to an IPv4/IPv6 address
268 * \param[in] nsi NS Instance in which to create the NSVC
269 * \param[in] local the local address to bind to
270 * \param[in] dscp the DSCP/TOS bits used for transmitted data
271 * \param[out] result if set, returns the bind object
272 * \return 0 on success; negative in case of error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200273int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
274 struct osmo_sockaddr *local,
275 int dscp,
276 struct gprs_ns2_vc_bind **result)
277{
278 struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
279 struct priv_bind *priv;
280 int rc;
281
282 if (!bind)
283 return -ENOSPC;
284
285 if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6) {
286 talloc_free(bind);
287 return -EINVAL;
288 }
289
290 bind->driver = &vc_driver_ip;
291 bind->send_vc = nsip_vc_sendmsg;
292 bind->free_vc = free_vc;
Alexander Couzens22f34712020-10-02 02:34:39 +0200293 bind->dump_vty = dump_vty;
Alexander Couzens6a161492020-07-12 13:45:50 +0200294 bind->nsi = nsi;
295
296 priv = bind->priv = talloc_zero(bind, struct priv_bind);
297 if (!priv) {
298 talloc_free(bind);
299 return -ENOSPC;
300 }
301 priv->fd.cb = nsip_fd_cb;
302 priv->fd.data = bind;
303 priv->addr = *local;
304 INIT_LLIST_HEAD(&bind->nsvc);
305
306 llist_add(&bind->list, &nsi->binding);
307
308 rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_DGRAM, IPPROTO_UDP,
309 local, NULL,
310 OSMO_SOCK_F_BIND);
311 if (rc < 0) {
312 talloc_free(priv);
313 talloc_free(bind);
314 return rc;
315 }
316
317 if (dscp > 0) {
318 priv->dscp = dscp;
319
320 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
321 &dscp, sizeof(dscp));
322 if (rc < 0)
323 LOGP(DLNS, LOGL_ERROR,
324 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
325 dscp, rc, errno);
326 }
327
328 ns2_vty_bind_apply(bind);
329
330 if (result)
331 *result = bind;
332
333 return 0;
334}
335
Harald Welte5bef2cc2020-09-18 22:33:24 +0200336/*! Create new NS-VC to a given remote address
337 * \param[in] bind the bind we want to connect
338 * \param[in] nse NS entity to be used for the new NS-VC
339 * \param[in] remote remote address to connect to
340 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200341struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
342 struct gprs_ns2_nse *nse,
343 struct osmo_sockaddr *remote)
344{
345 struct gprs_ns2_vc *nsvc;
346 struct priv_vc *priv;
347
348 nsvc = ns2_vc_alloc(bind, nse, true);
349 nsvc->priv = talloc_zero(bind, struct priv_vc);
350 if (!nsvc->priv) {
351 gprs_ns2_free_nsvc(nsvc);
352 return NULL;
353 }
354
355 priv = nsvc->priv;
356 priv->remote = *remote;
357
358 nsvc->ll = GPRS_NS_LL_UDP;
359
360 return nsvc;
361}
362
Harald Welte5bef2cc2020-09-18 22:33:24 +0200363/*! Return the socket address of the remote peer of a NS-VC.
364 * \param[in] nsvc NS-VC whose remote peer we want to know
365 * \return address of the remote peer; NULL in case of error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200366struct osmo_sockaddr *gprs_ns2_ip_vc_sockaddr(struct gprs_ns2_vc *nsvc)
367{
368 struct priv_vc *priv;
369
370 if (nsvc->ll != GPRS_NS_LL_UDP)
371 return NULL;
372
373 priv = nsvc->priv;
374 return &priv->remote;
375}
376
Harald Welte5bef2cc2020-09-18 22:33:24 +0200377/*! Return the locally bound socket address of the bind.
378 * \param[in] bind The bind whose local address we want to know
379 * \return address of the local bind */
Alexander Couzens6a161492020-07-12 13:45:50 +0200380struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
381{
382 struct priv_bind *priv;
383
384 priv = bind->priv;
385 return &priv->addr;
386}
387
Harald Welte5bef2cc2020-09-18 22:33:24 +0200388/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200389int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
390{
391 return (bind->driver == &vc_driver_ip);
392}
393
Harald Welte5bef2cc2020-09-18 22:33:24 +0200394/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200395int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
396{
397 struct priv_bind *priv;
398 int rc = 0;
399
400 priv = bind->priv;
401
402 if (dscp != priv->dscp) {
403 priv->dscp = dscp;
404
405 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
406 &dscp, sizeof(dscp));
407 if (rc < 0)
408 LOGP(DLNS, LOGL_ERROR,
409 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
410 dscp, rc, errno);
411 }
412
413 return rc;
414}