blob: d93b66d51773c71c824e7ac229ee2dcd412b2f56 [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,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100301 const char *name,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700302 const struct osmo_sockaddr *local,
Alexander Couzens6a161492020-07-12 13:45:50 +0200303 int dscp,
304 struct gprs_ns2_vc_bind **result)
305{
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200306 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200307 struct priv_bind *priv;
308 int rc;
309
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100310 if (!name)
311 return -EINVAL;
312
313 if (gprs_ns2_bind_by_name(nsi, name))
314 return -EALREADY;
315
Alexander Couzens6090b1d2020-10-11 18:41:37 +0200316 bind = gprs_ns2_ip_bind_by_sockaddr(nsi, local);
317 if (bind) {
318 *result = bind;
319 return -EBUSY;
320 }
321
322 bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
Alexander Couzens6a161492020-07-12 13:45:50 +0200323 if (!bind)
324 return -ENOSPC;
325
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100326 bind->name = talloc_strdup(bind, name);
327 if (!bind->name) {
328 talloc_free(bind);
329 return -ENOSPC;
330 }
331
Alexander Couzens6a161492020-07-12 13:45:50 +0200332 if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6) {
333 talloc_free(bind);
334 return -EINVAL;
335 }
336
337 bind->driver = &vc_driver_ip;
Alexander Couzensaac90162020-11-19 02:44:04 +0100338 bind->ll = GPRS_NS2_LL_UDP;
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100339 /* expect 100 mbit at least.
340 * TODO: ask the network layer about the speed. But would require
341 * notification on change. */
342 bind->transfer_capability = 100;
Alexander Couzens6a161492020-07-12 13:45:50 +0200343 bind->send_vc = nsip_vc_sendmsg;
344 bind->free_vc = free_vc;
Alexander Couzens22f34712020-10-02 02:34:39 +0200345 bind->dump_vty = dump_vty;
Alexander Couzens6a161492020-07-12 13:45:50 +0200346 bind->nsi = nsi;
347
348 priv = bind->priv = talloc_zero(bind, struct priv_bind);
349 if (!priv) {
350 talloc_free(bind);
351 return -ENOSPC;
352 }
353 priv->fd.cb = nsip_fd_cb;
354 priv->fd.data = bind;
355 priv->addr = *local;
356 INIT_LLIST_HEAD(&bind->nsvc);
357
Alexander Couzens6a161492020-07-12 13:45:50 +0200358 rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_DGRAM, IPPROTO_UDP,
359 local, NULL,
360 OSMO_SOCK_F_BIND);
361 if (rc < 0) {
362 talloc_free(priv);
363 talloc_free(bind);
364 return rc;
365 }
366
367 if (dscp > 0) {
368 priv->dscp = dscp;
369
370 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
371 &dscp, sizeof(dscp));
372 if (rc < 0)
373 LOGP(DLNS, LOGL_ERROR,
374 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
375 dscp, rc, errno);
376 }
377
Pau Espin Pedrolaa83d412020-10-09 14:20:13 +0200378 llist_add(&bind->list, &nsi->binding);
Alexander Couzens6a161492020-07-12 13:45:50 +0200379 if (result)
380 *result = bind;
381
382 return 0;
383}
384
Harald Welte5bef2cc2020-09-18 22:33:24 +0200385/*! Create new NS-VC to a given remote address
386 * \param[in] bind the bind we want to connect
387 * \param[in] nse NS entity to be used for the new NS-VC
388 * \param[in] remote remote address to connect to
389 * \return pointer to newly-allocated and connected NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200390struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
391 struct gprs_ns2_nse *nse,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700392 const struct osmo_sockaddr *remote)
Alexander Couzens6a161492020-07-12 13:45:50 +0200393{
394 struct gprs_ns2_vc *nsvc;
395 struct priv_vc *priv;
Alexander Couzensd923cff2020-12-01 01:03:52 +0100396 enum gprs_ns2_vc_mode vc_mode;
Harald Welte603f4042020-11-29 17:39:19 +0100397 char *sockaddr_str;
398 char idbuf[64];
Alexander Couzens6a161492020-07-12 13:45:50 +0200399
Alexander Couzensd923cff2020-12-01 01:03:52 +0100400 vc_mode = gprs_ns2_dialect_to_vc_mode(nse->dialect);
401 if ((int) vc_mode == -1) {
402 LOGP(DLNS, LOGL_ERROR, "Can not derive vc mode from dialect %d. Maybe libosmocore is too old.\n",
403 nse->dialect);
404 return NULL;
405 }
406
Harald Welte603f4042020-11-29 17:39:19 +0100407 sockaddr_str = (char *)osmo_sockaddr_to_str(remote);
408 osmo_identifier_sanitize_buf(sockaddr_str, NULL, '_');
409 snprintf(idbuf, sizeof(idbuf), "%s-NSE%05u-remote-%s", gprs_ns2_lltype_str(nse->ll),
410 nse->nsei, sockaddr_str);
411 nsvc = ns2_vc_alloc(bind, nse, true, vc_mode, idbuf);
Alexander Couzensc06aa712020-11-18 23:56:45 +0100412 if (!nsvc)
413 return NULL;
414
Alexander Couzens6a161492020-07-12 13:45:50 +0200415 nsvc->priv = talloc_zero(bind, struct priv_vc);
416 if (!nsvc->priv) {
417 gprs_ns2_free_nsvc(nsvc);
418 return NULL;
419 }
420
421 priv = nsvc->priv;
422 priv->remote = *remote;
423
Alexander Couzens6a161492020-07-12 13:45:50 +0200424 return nsvc;
425}
426
Alexander Couzens979f5f52020-10-11 21:01:48 +0200427/*! Return the socket address of the local peer of a NS-VC.
428 * \param[in] nsvc NS-VC whose local peer we want to know
429 * \return address of the local peer; NULL in case of error */
430const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
431{
432 struct priv_bind *priv;
433
Alexander Couzens979f5f52020-10-11 21:01:48 +0200434 if (nsvc->bind->driver != &vc_driver_ip)
435 return NULL;
436
437 priv = nsvc->bind->priv;
438 return &priv->addr;
439}
440
Harald Welte5bef2cc2020-09-18 22:33:24 +0200441/*! Return the socket address of the remote peer of a NS-VC.
442 * \param[in] nsvc NS-VC whose remote peer we want to know
443 * \return address of the remote peer; NULL in case of error */
Alexander Couzensd33512b2020-10-11 21:42:11 +0200444const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200445{
446 struct priv_vc *priv;
447
Alexander Couzensaac90162020-11-19 02:44:04 +0100448 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzens6a161492020-07-12 13:45:50 +0200449 return NULL;
450
451 priv = nsvc->priv;
452 return &priv->remote;
453}
454
Alexander Couzensd420ea92020-10-12 01:11:05 +0200455/*! Compare the NS-VC with the given parameter
456 * \param[in] nsvc NS-VC to compare with
457 * \param[in] local The local address
458 * \param[in] remote The remote address
459 * \param[in] nsvci NS-VCI will only be used if the NS-VC in BLOCKRESET mode otherwise NS-VCI isn't applicable.
460 * \return true if the NS-VC has the same properties as given
461 */
462bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
463 const struct osmo_sockaddr *local,
464 const struct osmo_sockaddr *remote,
465 uint16_t nsvci)
466{
467 struct priv_vc *vpriv;
468 struct priv_bind *bpriv;
469
Alexander Couzensaac90162020-11-19 02:44:04 +0100470 if (nsvc->bind->driver != &vc_driver_ip)
Alexander Couzensd420ea92020-10-12 01:11:05 +0200471 return false;
472
473 vpriv = nsvc->priv;
474 bpriv = nsvc->bind->priv;
475
476 if (osmo_sockaddr_cmp(local, &bpriv->addr))
477 return false;
478
479 if (osmo_sockaddr_cmp(remote, &vpriv->remote))
480 return false;
481
482 if (nsvc->mode == NS2_VC_MODE_BLOCKRESET)
483 if (nsvc->nsvci != nsvci)
484 return false;
485
486 return true;
487}
488
Harald Welte5bef2cc2020-09-18 22:33:24 +0200489/*! Return the locally bound socket address of the bind.
490 * \param[in] bind The bind whose local address we want to know
491 * \return address of the local bind */
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200492const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200493{
494 struct priv_bind *priv;
495
496 priv = bind->priv;
497 return &priv->addr;
498}
499
Harald Welte5bef2cc2020-09-18 22:33:24 +0200500/*! Is the given bind an IP bind? */
Alexander Couzens6a161492020-07-12 13:45:50 +0200501int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
502{
503 return (bind->driver == &vc_driver_ip);
504}
505
Harald Welte5bef2cc2020-09-18 22:33:24 +0200506/*! Set the DSCP (TOS) bit value of the given bind. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200507int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
508{
509 struct priv_bind *priv;
510 int rc = 0;
511
512 priv = bind->priv;
513
514 if (dscp != priv->dscp) {
515 priv->dscp = dscp;
516
517 rc = setsockopt(priv->fd.fd, IPPROTO_IP, IP_TOS,
518 &dscp, sizeof(dscp));
519 if (rc < 0)
520 LOGP(DLNS, LOGL_ERROR,
521 "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
522 dscp, rc, errno);
523 }
524
525 return rc;
526}
Alexander Couzense769f522020-12-07 07:37:07 +0100527
528/*! Count UDP binds compatible with remote */
529int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote)
530{
531 struct gprs_ns2_vc_bind *bind;
532 const struct osmo_sockaddr *sa;
533 int count = 0;
534
535 llist_for_each_entry(bind, &nsi->binding, list) {
536 if (!gprs_ns2_is_ip_bind(bind))
537 continue;
538
539 sa = gprs_ns2_ip_bind_sockaddr(bind);
540 if (!sa)
541 continue;
542
543 if (sa->u.sa.sa_family == remote->u.sa.sa_family)
544 count++;
545 }
546
547 return count;
548}
549
550/* return the matching bind by index */
551struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
552 struct osmo_sockaddr *remote,
553 int index)
554{
555 struct gprs_ns2_vc_bind *bind;
556 const struct osmo_sockaddr *sa;
557 int i = 0;
558
559 llist_for_each_entry(bind, &nsi->binding, list) {
560 if (!gprs_ns2_is_ip_bind(bind))
561 continue;
562
563 sa = gprs_ns2_ip_bind_sockaddr(bind);
564 if (!sa)
565 continue;
566
567 if (sa->u.sa.sa_family == remote->u.sa.sa_family) {
568 if (index == i)
569 return bind;
570 i++;
571 }
572 }
573
574 return NULL;
575}