blob: 42c3b9496e24e37136f47c1ecb5512ababf7222e [file] [log] [blame]
Harald Welte283c7fd2015-12-21 23:35:56 +01001/* Minimal implementation of RFC 3868 - SCCP User Adaptation Layer */
2
3/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <stdint.h>
22#include <errno.h>
23#include <unistd.h>
24#include <string.h>
25
26#include <osmocom/core/utils.h>
27#include <osmocom/core/linuxlist.h>
28#include <osmocom/core/write_queue.h>
29#include <osmocom/core/logging.h>
30#include <osmocom/core/timer.h>
31
32#include <osmocom/netif/stream.h>
33#include <osmocom/sigtran/xua_msg.h>
34
35#include "sccp_sap.h"
36#include "proto_sua.h"
37#include "sua.h"
38
39#define SUA_MSGB_SIZE 1500
40
41/* Appendix C.4 of Q.714 (all in milliseconds) */
42#define CONNECTION_TIMER ( 1 * 60 * 100)
43#define TX_INACT_TIMER ( 7 * 60 * 100) /* RFC 3868 Ch. 8. */
44#define RX_INACT_TIMER (15 * 60 * 100) /* RFC 3868 Ch. 8. */
45#define RELEASE_TIMER ( 10 * 100)
46#define RELEASE_REP_TIMER ( 10 * 100)
47#define INT_TIMER ( 1 * 60 * 100)
48#define GUARD_TIMER (23 * 60 * 100)
49#define RESET_TIMER ( 10 * 100)
50
51static int DSUA = -1;
52
53struct osmo_sua_user {
54 /* global list of SUA users? */
55 struct llist_head list;
56 /* set if we are a server */
57 struct osmo_stream_srv_link *server;
58 struct osmo_stream_cli *client;
59 struct llist_head links;
60 /* user call-back function in case of incoming primitives */
61 osmo_prim_cb prim_cb;
62};
63
Harald Welte89607042015-12-21 23:51:08 +010064struct osmo_sua_link {
Harald Welte283c7fd2015-12-21 23:35:56 +010065 /* list of SUA links per sua_user */
66 struct llist_head list;
67 /* sua user to which we belong */
68 struct osmo_sua_user *user;
69 /* local list of (SCCP) connections in this link */
70 struct llist_head connections;
71 /* next connection local reference */
72 uint32_t next_id;
73 int is_server;
74 void *data;
75};
76
77enum sua_connection_state {
78 S_IDLE,
79 S_CONN_PEND_IN,
80 S_CONN_PEND_OUT,
81 S_ACTIVE,
82 S_DISCONN_PEND,
83 S_RESET_IN,
84 S_RESET_OUT,
85 S_BOTHWAY_RESET,
86 S_WAIT_CONN_CONF,
87};
88
89static const struct value_string conn_state_names[] = {
90 { S_IDLE, "IDLE" },
91 { S_CONN_PEND_IN, "CONN_PEND_IN" },
92 { S_CONN_PEND_OUT, "CONN_PEND_OUT" },
93 { S_ACTIVE, "ACTIVE" },
94 { S_DISCONN_PEND, "DISCONN_PEND" },
95 { S_RESET_IN, "RESET_IN" },
96 { S_RESET_OUT, "RESET_OUT" },
97 { S_BOTHWAY_RESET, "BOTHWAY_RESET" },
98 { S_WAIT_CONN_CONF, "WAIT_CONN_CONF" },
99 { 0, NULL }
100};
101
102struct sua_connection {
103 struct llist_head list;
Harald Welte89607042015-12-21 23:51:08 +0100104 struct osmo_sua_link *link;
Harald Welte283c7fd2015-12-21 23:35:56 +0100105 struct osmo_sccp_addr calling_addr;
106 struct osmo_sccp_addr called_addr;
107 uint32_t conn_id;
108 uint32_t remote_ref;
109 enum sua_connection_state state;
110 struct osmo_timer_list timer;
111 /* inactivity timers */
112 struct osmo_timer_list tias;
113 struct osmo_timer_list tiar;
114};
115
116
117/***********************************************************************
118 * Message encoding helper functions
119 ***********************************************************************/
120
121#define XUA_HDR(class, type) ((struct xua_common_hdr) { .spare = 0, .msg_class = (class), .msg_type = (type) })
122
123static int msgb_t16l16vp_put(struct msgb *msg, uint16_t tag, uint16_t len, const uint8_t *data)
124{
125 uint8_t *cur;
126 unsigned int rest;
127 unsigned int tlv_len = 4 + len + (4 - (len % 4));
128
129 if (msgb_tailroom(msg) < tlv_len)
130 return -ENOMEM;
131
132 /* tag */
133 msgb_put_u16(msg, tag);
134 /* length */
135 msgb_put_u16(msg, len + 4);
136 /* value */
137 cur = msgb_put(msg, len);
138 memcpy(cur, data, len);
139 /* padding */
140 rest = (4 - (len % 4)) & 0x3;
141 if (rest > 0) {
142 cur = msgb_put(msg, rest);
143 memset(cur, 0, rest);
144 }
145
146 return 0;
147}
148
149static int msgb_t16l16vp_put_u32(struct msgb *msg, uint16_t tag, uint32_t val)
150{
151 uint32_t val_n = htonl(val);
152
153 return msgb_t16l16vp_put(msg, tag, sizeof(val_n), (uint8_t *)&val_n);
154}
155
156static int xua_msg_add_u32(struct xua_msg *xua, uint16_t iei, uint32_t val)
157{
158 uint32_t val_n = htonl(val);
159 return xua_msg_add_data(xua, iei, sizeof(val_n), (uint8_t *) &val_n);
160}
161
162static uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei)
163{
164 struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
165 uint32_t rc = 0;
166 if (part)
167 rc = ntohl(*(uint32_t *)part->dat);
168 return rc;
169}
170
171static int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct osmo_sccp_addr *addr)
172{
173 struct msgb *tmp = msgb_alloc(128, "SCCP Address");
174 int rc;
175
176 if (!tmp)
177 return -ENOMEM;
178
179 msgb_put_u16(tmp, 2); /* route on SSN + PC */
180 msgb_put_u16(tmp, 7); /* always put all addresses on SCCP side */
181
182 if (addr->presence & OSMO_SCCP_ADDR_T_GT) {
183 /* FIXME */
184 }
185 if (addr->presence & OSMO_SCCP_ADDR_T_PC) {
186 msgb_t16l16vp_put_u32(tmp, SUA_IEI_PC, addr->pc);
187 }
188 if (addr->presence & OSMO_SCCP_ADDR_T_SSN) {
189 msgb_t16l16vp_put_u32(tmp, SUA_IEI_SSN, addr->ssn);
190 }
191 if (addr->presence & OSMO_SCCP_ADDR_T_IPv4) {
192 /* FIXME: IPv4 address */
193 } else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6) {
194 /* FIXME: IPv6 address */
195 }
196 rc = xua_msg_add_data(xua, iei, msgb_length(tmp), tmp->data);
197 msgb_free(tmp);
198
199 return rc;
200}
201
202
203/***********************************************************************
204 * SUA Link and Connection handling
205 ***********************************************************************/
206
Harald Welte89607042015-12-21 23:51:08 +0100207static struct osmo_sua_link *sua_link_new(struct osmo_sua_user *user, int is_server)
Harald Welte283c7fd2015-12-21 23:35:56 +0100208{
Harald Welte89607042015-12-21 23:51:08 +0100209 struct osmo_sua_link *link;
Harald Welte283c7fd2015-12-21 23:35:56 +0100210
Harald Welte89607042015-12-21 23:51:08 +0100211 link = talloc_zero(user, struct osmo_sua_link);
Harald Welte283c7fd2015-12-21 23:35:56 +0100212 if (!link)
213 return NULL;
214
215 link->user = user;
216 link->is_server = is_server;
217 INIT_LLIST_HEAD(&link->connections);
218
219 llist_add_tail(&link->list, &user->links);
220
221 return link;
222}
223
224static void conn_destroy(struct sua_connection *conn);
225
Harald Welte89607042015-12-21 23:51:08 +0100226static void sua_link_destroy(struct osmo_sua_link *link)
Harald Welte283c7fd2015-12-21 23:35:56 +0100227{
228 struct sua_connection *conn;
229
230 llist_for_each_entry(conn, &link->connections, list)
231 conn_destroy(conn);
232
233 llist_del(&link->list);
234
235 /* FIXME: do we need to cleanup the sccp link? */
236
237 talloc_free(link);
238}
239
Harald Welte89607042015-12-21 23:51:08 +0100240static int sua_link_send(struct osmo_sua_link *link, struct msgb *msg)
Harald Welte283c7fd2015-12-21 23:35:56 +0100241{
242 msgb_sctp_ppid(msg) = SUA_PPID;
243
244 if (link->is_server)
245 osmo_stream_srv_send(link->data, msg);
246 else
247 osmo_stream_cli_send(link->data, msg);
248
249 return 0;
250}
251
Harald Welte89607042015-12-21 23:51:08 +0100252static struct sua_connection *conn_find_by_id(struct osmo_sua_link *link, uint32_t id)
Harald Welte283c7fd2015-12-21 23:35:56 +0100253{
254 struct sua_connection *conn;
255
256 llist_for_each_entry(conn, &link->connections, list) {
257 if (conn->conn_id == id)
258 return conn;
259 }
260 return NULL;
261}
262
263static void tx_inact_tmr_cb(void *data)
264{
265 struct sua_connection *conn = data;
266 struct xua_msg *xua = xua_msg_alloc();
267 struct msgb *outmsg;
268
269 /* encode + send the CLDT */
270 xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COIT);
271 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
272 xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, 2);
273 xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
274 xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &conn->called_addr);
275 /* optional: sequence number; credit (both class 3 only) */
276
277 outmsg = xua_to_msg(1, xua);
278 xua_msg_free(xua);
279
280 sua_link_send(conn->link, outmsg);
281}
282
283static void rx_inact_tmr_cb(void *data)
284{
285 struct sua_connection *conn = data;
286
287 /* FIXME: release connection */
288 /* Send N-DISCONNECT.ind to local user */
289 /* Send RLSD to peer */
290 /* enter disconnect pending state with release timer pending */
291}
292
293
Harald Welte89607042015-12-21 23:51:08 +0100294static struct sua_connection *conn_create_id(struct osmo_sua_link *link, uint32_t conn_id)
Harald Welte283c7fd2015-12-21 23:35:56 +0100295{
296 struct sua_connection *conn = talloc_zero(link, struct sua_connection);
297
298 conn->conn_id = conn_id;
299 conn->link = link;
300 conn->state = S_IDLE;
301
302 llist_add_tail(&conn->list, &link->connections);
303
304 conn->tias.cb = tx_inact_tmr_cb;
305 conn->tias.data = conn;
306 conn->tiar.cb = rx_inact_tmr_cb;
307 conn->tiar.data = conn;
308
309 return conn;
310}
311
Harald Welte89607042015-12-21 23:51:08 +0100312static struct sua_connection *conn_create(struct osmo_sua_link *link)
Harald Welte283c7fd2015-12-21 23:35:56 +0100313{
314 uint32_t conn_id;
315
316 do {
317 conn_id = link->next_id++;
318 } while (conn_find_by_id(link, conn_id));
319
320 return conn_create_id(link, conn_id);
321}
322
323static void conn_destroy(struct sua_connection *conn)
324{
325 /* FIXME: do some cleanup; inform user? */
326 osmo_timer_del(&conn->tias);
327 osmo_timer_del(&conn->tiar);
328 llist_del(&conn->list);
329 talloc_free(conn);
330}
331
332static void conn_state_set(struct sua_connection *conn,
333 enum sua_connection_state state)
334{
335 DEBUGP(DSUA, "(%u) state chg %s->", conn->conn_id,
336 get_value_string(conn_state_names, conn->state));
337 DEBUGPC(DSUA, "%s\n",
338 get_value_string(conn_state_names, state));
339 conn->state = state;
340}
341
342static void conn_restart_tx_inact_timer(struct sua_connection *conn)
343{
344 osmo_timer_schedule(&conn->tias, TX_INACT_TIMER / 100,
345 (TX_INACT_TIMER % 100) * 10);
346}
347
348static void conn_restart_rx_inact_timer(struct sua_connection *conn)
349{
350 osmo_timer_schedule(&conn->tiar, RX_INACT_TIMER / 100,
351 (RX_INACT_TIMER % 100) * 10);
352}
353
354static void conn_start_inact_timers(struct sua_connection *conn)
355{
356 conn_restart_tx_inact_timer(conn);
357 conn_restart_rx_inact_timer(conn);
358}
359
360
361static struct msgb *sua_msgb_alloc(void)
362{
363 return msgb_alloc(SUA_MSGB_SIZE, "SUA Primitive");
364}
365
366
367/***********************************************************************
368 * Handling of messages from the User SAP
369 ***********************************************************************/
370
371/* user program sends us a N-CONNNECT.req to initiate a new connection */
Harald Welte89607042015-12-21 23:51:08 +0100372static int sua_connect_req(struct osmo_sua_link *link, struct osmo_scu_prim *prim)
Harald Welte283c7fd2015-12-21 23:35:56 +0100373{
374 struct osmo_scu_connect_param *par = &prim->u.connect;
375 struct xua_msg *xua = xua_msg_alloc();
376 struct sua_connection *conn;
377 struct msgb *outmsg;
378
379 if (par->sccp_class != 2) {
380 LOGP(DSUA, LOGL_ERROR, "N-CONNECT.req for unsupported "
381 "SCCP class %u\n", par->sccp_class);
382 /* FIXME: Send primitive to user */
383 return -EINVAL;
384 }
385
386 conn = conn_create_id(link, par->conn_id);
387 if (!conn) {
388 /* FIXME: Send primitive to user */
389 return -EINVAL;
390 }
391
392 memcpy(&conn->called_addr, &par->called_addr,
393 sizeof(conn->called_addr));
394 memcpy(&conn->calling_addr, &par->calling_addr,
395 sizeof(conn->calling_addr));
396
397 /* encode + send the CLDT */
398 xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE);
399 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
400 xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, par->sccp_class);
401 xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
402 xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
403 xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, 0); /* FIXME */
404 /* sequence number */
405 if (par->calling_addr.presence)
406 xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
407 /* optional: hop count; importance; priority; credit */
408 if (msgb_l2(prim->oph.msg))
409 xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
410 msgb_l2(prim->oph.msg));
411
412 outmsg = xua_to_msg(1, xua);
413 xua_msg_free(xua);
414
415 /* FIXME: Start CONNECTION_TIMER */
416 conn_state_set(conn, S_CONN_PEND_OUT);
417
418 return sua_link_send(link, outmsg);
419}
420
421/* user program sends us a N-CONNNECT.resp, presumably against a
422 * N-CONNECT.ind */
Harald Welte89607042015-12-21 23:51:08 +0100423static int sua_connect_resp(struct osmo_sua_link *link, struct osmo_scu_prim *prim)
Harald Welte283c7fd2015-12-21 23:35:56 +0100424{
425 struct osmo_scu_connect_param *par = &prim->u.connect;
426 struct xua_msg *xua = xua_msg_alloc();
427 struct sua_connection *conn;
428 struct msgb *outmsg;
429
430 /* check if we already know a connection for this conn_id */
431 conn = conn_find_by_id(link, par->conn_id);
432 if (!conn) {
433 LOGP(DSUA, LOGL_ERROR, "N-CONNECT.resp for unknown "
434 "connection ID %u\n", par->conn_id);
435 /* FIXME: Send primitive to user */
436 return -ENODEV;
437 }
438
439 if (conn->state != S_CONN_PEND_IN) {
440 LOGP(DSUA, LOGL_ERROR, "N-CONNECT.resp in wrong state %s\n",
441 get_value_string(conn_state_names, conn->state));
442 /* FIXME: Send primitive to user */
443 return -EINVAL;
444 }
445
446 /* encode + send the COAK message */
447 xua = xua_msg_alloc();
448 xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK);
449 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
450 xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, par->sccp_class);
451 xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
452 xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
453 xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, 0); /* FIXME */
454 /* sequence number */
455 if (par->calling_addr.presence)
456 xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
457 /* optional: hop count; importance; priority */
458 /* FIXME: destination address will be present in case the CORE
459 * message conveys the source address parameter */
460 if (par->called_addr.presence)
461 xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
462 if (msgb_l2(prim->oph.msg))
463 xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
464 msgb_l2(prim->oph.msg));
465
466 outmsg = xua_to_msg(1, xua);
467 xua_msg_free(xua);
468
469 conn_state_set(conn, S_ACTIVE);
470 conn_start_inact_timers(conn);
471
472 return sua_link_send(link, outmsg);
473}
474
475/* user wants to send connection-oriented data */
Harald Welte89607042015-12-21 23:51:08 +0100476static int sua_data_req(struct osmo_sua_link *link, struct osmo_scu_prim *prim)
Harald Welte283c7fd2015-12-21 23:35:56 +0100477{
478 struct osmo_scu_data_param *par = &prim->u.data;
479 struct xua_msg *xua;
480 struct sua_connection *conn;
481 struct msgb *outmsg;
482
483 /* check if we know about this conncetion, and obtain reference */
484 conn = conn_find_by_id(link, par->conn_id);
485 if (!conn) {
486 LOGP(DSUA, LOGL_ERROR, "N-DATA.req for unknown "
487 "connection ID %u\n", par->conn_id);
488 /* FIXME: Send primitive to user */
489 return -ENODEV;
490 }
491
492 if (conn->state != S_ACTIVE) {
493 LOGP(DSUA, LOGL_ERROR, "N-DATA.req in wrong state %s\n",
494 get_value_string(conn_state_names, conn->state));
495 /* FIXME: Send primitive to user */
496 return -EINVAL;
497 }
498
499 conn_restart_tx_inact_timer(conn);
500
501 /* encode + send the CODT message */
502 xua = xua_msg_alloc();
503 xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT);
504 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
505 /* Sequence number only in expedited data */
506 xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
507 /* optional: priority; correlation id */
508 xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
509 msgb_l2(prim->oph.msg));
510
511 outmsg = xua_to_msg(1, xua);
512 xua_msg_free(xua);
513
514 return sua_link_send(link, outmsg);
515}
516
517/* user wants to disconnect a connection */
Harald Welte89607042015-12-21 23:51:08 +0100518static int sua_disconnect_req(struct osmo_sua_link *link, struct osmo_scu_prim *prim)
Harald Welte283c7fd2015-12-21 23:35:56 +0100519{
520 struct osmo_scu_disconn_param *par = &prim->u.disconnect;
521 struct xua_msg *xua;
522 struct sua_connection *conn;
523 struct msgb *outmsg;
524
525 /* resolve reference of connection */
526 conn = conn_find_by_id(link, par->conn_id);
527 if (!conn) {
528 LOGP(DSUA, LOGL_ERROR, "N-DISCONNECT.resp for unknown "
529 "connection ID %u\n", par->conn_id);
530 /* FIXME: Send primitive to user */
531 return -ENODEV;
532 }
533
534 /* encode + send the RELRE */
535 xua = xua_msg_alloc();
536 xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE);
537 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
538 xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
539 xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
540 xua_msg_add_u32(xua, SUA_IEI_CAUSE, par->cause);
541 /* optional: importance */
542 if (msgb_l2(prim->oph.msg))
543 xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
544 msgb_l2(prim->oph.msg));
545
546 outmsg = xua_to_msg(1, xua);
547 xua_msg_free(xua);
548
549 conn_state_set(conn, S_DISCONN_PEND);
550
551 return sua_link_send(link, outmsg);
552}
553
554/* user wants to send connectionless data */
Harald Welte89607042015-12-21 23:51:08 +0100555static int sua_unitdata_req(struct osmo_sua_link *link, struct osmo_scu_prim *prim)
Harald Welte283c7fd2015-12-21 23:35:56 +0100556{
557 struct osmo_scu_unitdata_param *par = &prim->u.unitdata;
558 struct xua_msg *xua = xua_msg_alloc();
559 struct msgb *outmsg;
560
561 /* encode + send the CLDT */
562 xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
563 xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
564 xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, 0);
565 xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
566 xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
567 xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, par->in_sequence_control);
568 /* optional: importance, ... correlation id? */
569 xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
570 msgb_l2(prim->oph.msg));
571
572 outmsg = xua_to_msg(1, xua);
573 xua_msg_free(xua);
574
575 return sua_link_send(link, outmsg);
576}
577
578/* user hands us a SCCP-USER SAP primitive down into the stack */
Harald Welte89607042015-12-21 23:51:08 +0100579int osmo_sua_user_link_down(struct osmo_sua_link *link, struct osmo_prim_hdr *oph)
Harald Welte283c7fd2015-12-21 23:35:56 +0100580{
581 struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
582 struct msgb *msg = prim->oph.msg;
583 int rc = 0;
584
585 LOGP(DSUA, LOGL_DEBUG, "Received SCCP User Primitive (%s)\n",
586 osmo_sccp_prim_name(&prim->oph));
587
588 switch (OSMO_PRIM_HDR(&prim->oph)) {
589 case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST):
590 rc = sua_connect_req(link, prim);
591 break;
592 case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_RESPONSE):
593 rc = sua_connect_resp(link, prim);
594 break;
595 case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST):
596 rc = sua_data_req(link, prim);
597 break;
598 case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_REQUEST):
599 rc = sua_disconnect_req(link, prim);
600 break;
601 case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST):
602 rc = sua_unitdata_req(link, prim);
603 break;
604 default:
605 rc = -1;
606 }
607
608 if (rc != 1)
609 msgb_free(msg);
610
611 return rc;
612}
613
614
615/***********************************************************************
616 * Mandatory IE checking
617 ***********************************************************************/
618
619#define MAND_IES(msgt, ies) [msgt] = (ies)
620
621static const uint16_t cldt_mand_ies[] = {
622 SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_ADDR,
623 SUA_IEI_DEST_ADDR, SUA_IEI_SEQ_CTRL, SUA_IEI_DATA, 0
624};
625
626static const uint16_t cldr_mand_ies[] = {
627 SUA_IEI_ROUTE_CTX, SUA_IEI_CAUSE, SUA_IEI_SRC_ADDR,
628 SUA_IEI_DEST_ADDR, 0
629};
630
631static const uint16_t codt_mand_ies[] = {
632 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_DATA, 0
633};
634
635static const uint16_t coda_mand_ies[] = {
636 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, 0
637};
638
639static const uint16_t core_mand_ies[] = {
640 SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
641 SUA_IEI_DEST_ADDR, SUA_IEI_SEQ_CTRL, 0
642};
643
644static const uint16_t coak_mand_ies[] = {
645 SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_DEST_REF,
646 SUA_IEI_SRC_REF, SUA_IEI_SEQ_CTRL, 0
647};
648
649static const uint16_t coref_mand_ies[] = {
650 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0
651};
652
653static const uint16_t relre_mand_ies[] = {
654 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF,
655 SUA_IEI_CAUSE, 0
656};
657
658static const uint16_t relco_mand_ies[] = {
659 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0
660};
661
662static const uint16_t resre_mand_ies[] = {
663 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF,
664 SUA_IEI_CAUSE, 0
665};
666
667static const uint16_t resco_mand_ies[] = {
668 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0
669};
670
671static const uint16_t coerr_mand_ies[] = {
672 SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0
673};
674
675static const uint16_t coit_mand_ies[] = {
676 SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
677 SUA_IEI_DEST_REF, 0
678};
679
680static const uint16_t *mand_ies_cl[256] = {
681 MAND_IES(SUA_CL_CLDT, cldt_mand_ies),
682 MAND_IES(SUA_CL_CLDR, cldr_mand_ies),
683};
684
685static const uint16_t *mand_ies_co[256] = {
686 MAND_IES(SUA_CO_CODT, codt_mand_ies),
687 MAND_IES(SUA_CO_CODA, coda_mand_ies),
688 MAND_IES(SUA_CO_CORE, core_mand_ies),
689 MAND_IES(SUA_CO_COAK, coak_mand_ies),
690 MAND_IES(SUA_CO_COREF, coref_mand_ies),
691 MAND_IES(SUA_CO_RELRE, relre_mand_ies),
692 MAND_IES(SUA_CO_RELCO, relco_mand_ies),
693 MAND_IES(SUA_CO_RESRE, resre_mand_ies),
694 MAND_IES(SUA_CO_RESCO, resco_mand_ies),
695 MAND_IES(SUA_CO_COERR, coerr_mand_ies),
696 MAND_IES(SUA_CO_COIT, coit_mand_ies),
697};
698
699static int check_all_mand_ies(const uint16_t **mand_ies, struct xua_msg *xua)
700{
701 uint8_t msg_type = xua->hdr.msg_type;
702 const uint16_t *ies = mand_ies[msg_type];
703 uint16_t ie;
704
705 for (ie = *ies; ie; ie = *ies++) {
706 if (!xua_msg_find_tag(xua, ie)) {
707 LOGP(DSUA, LOGL_ERROR, "SUA Message %u:%u should "
708 "contain IE 0x%04x, but doesn't\n",
709 xua->hdr.msg_class, msg_type, ie);
710 return 0;
711 }
712 }
713
714 return 1;
715}
716
717
718/***********************************************************************
719 * Receiving SUA messsages from SCTP
720 ***********************************************************************/
721
722static int sua_parse_addr(struct osmo_sccp_addr *out,
723 struct xua_msg *xua,
724 uint16_t iei)
725{
726 const struct xua_msg_part *param = xua_msg_find_tag(xua, iei);
727
728 if (!param)
729 return -ENODEV;
730
731 /* FIXME */
732 return 0;
733}
734
Harald Welte89607042015-12-21 23:51:08 +0100735static int sua_rx_cldt(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +0100736{
737 struct osmo_scu_prim *prim;
738 struct osmo_scu_unitdata_param *param;
739 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
740 struct msgb *upmsg = sua_msgb_alloc();
741 uint32_t protocol_class;
742
743 /* fill primitive */
744 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
745 param = &prim->u.unitdata;
746 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
747 OSMO_SCU_PRIM_N_UNITDATA,
748 PRIM_OP_INDICATION, upmsg);
749 sua_parse_addr(&param->called_addr, xua, SUA_IEI_DEST_ADDR);
750 sua_parse_addr(&param->calling_addr, xua, SUA_IEI_SRC_ADDR);
751 param->in_sequence_control = xua_msg_get_u32(xua, SUA_IEI_SEQ_CTRL);
752 protocol_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
753 param->return_option = protocol_class & 0x80;
754 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
755
756 /* copy data */
757 upmsg->l2h = msgb_put(upmsg, data_ie->len);
758 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
759
760 /* send to user SAP */
761 link->user->prim_cb(&prim->oph, link);
762
763 return 0;
764}
765
766
767/* connectioness messages received from socket */
Harald Welte89607042015-12-21 23:51:08 +0100768static int sua_rx_cl(struct osmo_sua_link *link,
Harald Welte283c7fd2015-12-21 23:35:56 +0100769 struct xua_msg *xua, struct msgb *msg)
770{
771 int rc = -1;
772
773 if (!check_all_mand_ies(mand_ies_cl, xua))
774 return -1;
775
776 switch (xua->hdr.msg_type) {
777 case SUA_CL_CLDT:
778 rc = sua_rx_cldt(link, xua);
779 break;
780 case SUA_CL_CLDR:
781 default:
782 break;
783 }
784
785 return rc;
786}
787
788/* RFC 3868 3.3.3 / SCCP CR */
Harald Welte89607042015-12-21 23:51:08 +0100789static int sua_rx_core(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +0100790{
791 struct osmo_scu_prim *prim;
792 struct osmo_scu_connect_param *param;
793 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
794 struct msgb *upmsg;
795 struct sua_connection *conn;
796 uint8_t *cur;
797
798 /* fill conn */
799 conn = conn_create(link);
800 sua_parse_addr(&conn->called_addr, xua, SUA_IEI_DEST_ADDR);
801 sua_parse_addr(&conn->calling_addr, xua, SUA_IEI_SRC_ADDR);
802 conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
803
804 /* fill primitive */
805 upmsg = sua_msgb_alloc();
806 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
807 param = &prim->u.connect;
808 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
809 OSMO_SCU_PRIM_N_CONNECT,
810 PRIM_OP_INDICATION, upmsg);
811 param->conn_id = conn->conn_id;
812 memcpy(&param->called_addr, &conn->called_addr,
813 sizeof(param->called_addr));
814 memcpy(&param->calling_addr, &conn->calling_addr,
815 sizeof(param->calling_addr));
816 //param->in_sequence_control;
817 param->sccp_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS) & 3;
818 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
819
820 if (data_ie) {
821 /* copy data */
822 upmsg->l2h = msgb_put(upmsg, data_ie->len);
823 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
824 }
825
826 conn_state_set(conn, S_CONN_PEND_IN);
827
828 /* send to user SAP */
829 link->user->prim_cb(&prim->oph, link);
830
831 return 0;
832}
833
834/* RFC 3868 3.3.4 / SCCP CC */
Harald Welte89607042015-12-21 23:51:08 +0100835static int sua_rx_coak(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +0100836{
837 struct osmo_scu_prim *prim;
838 struct sua_connection *conn;
839 struct osmo_scu_connect_param *param;
840 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
841 struct msgb *upmsg;
842 uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
843
844 /* resolve conn */
845 conn = conn_find_by_id(link, conn_id);
846 if (!conn) {
847 LOGP(DSUA, LOGL_ERROR, "COAK for unknwon reference %u\n",
848 conn_id);
849 /* FIXME: error message? */
850 return -1;
851 }
852 conn_restart_rx_inact_timer(conn);
853
854 if (conn->state != S_CONN_PEND_OUT) {
855 LOGP(DSUA, LOGL_ERROR, "COAK in wrong state %s\n",
856 get_value_string(conn_state_names, conn->state));
857 /* FIXME: error message? */
858 return -EINVAL;
859 }
860
861 /* track remote reference */
862 conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
863
864 /* fill primitive */
865 upmsg = sua_msgb_alloc();
866 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
867 param = &prim->u.connect;
868 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
869 OSMO_SCU_PRIM_N_CONNECT,
870 PRIM_OP_CONFIRM, upmsg);
871 param->conn_id = conn->conn_id;
872 memcpy(&param->called_addr, &conn->called_addr,
873 sizeof(param->called_addr));
874 memcpy(&param->calling_addr, &conn->calling_addr,
875 sizeof(param->calling_addr));
876 //param->in_sequence_control;
877 param->sccp_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS) & 3;
878 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
879
880 if (data_ie) {
881 /* copy data */
882 upmsg->l2h = msgb_put(upmsg, data_ie->len);
883 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
884 }
885
886 conn_state_set(conn, S_ACTIVE);
887 conn_start_inact_timers(conn);
888
889 /* send to user SAP */
890 link->user->prim_cb(&prim->oph, link);
891
892 return 0;
893}
894
895/* RFC 3868 3.3.5 / SCCP CREF */
Harald Welte89607042015-12-21 23:51:08 +0100896static int sua_rx_coref(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +0100897{
898 struct osmo_scu_prim *prim;
899 struct sua_connection *conn;
900 struct osmo_scu_connect_param *param;
901 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
902 struct msgb *upmsg;
903 uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
904 uint32_t cause;
905
906 /* resolve conn */
907 conn = conn_find_by_id(link, conn_id);
908 if (!conn) {
909 LOGP(DSUA, LOGL_ERROR, "COREF for unknwon reference %u\n",
910 conn_id);
911 /* FIXME: error message? */
912 return -1;
913 }
914 conn_restart_rx_inact_timer(conn);
915
916 /* fill primitive */
917 upmsg = sua_msgb_alloc();
918 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
919 param = &prim->u.connect;
920 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
921 OSMO_SCU_PRIM_N_DISCONNECT,
922 PRIM_OP_INDICATION, upmsg);
923 param->conn_id = conn_id;
924 memcpy(&param->called_addr, &conn->called_addr,
925 sizeof(param->called_addr));
926 memcpy(&param->calling_addr, &conn->calling_addr,
927 sizeof(param->calling_addr));
928 //param->in_sequence_control;
929 cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE);
930 /* optional: src addr */
931 /* optional: dest addr */
932 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
933 if (data_ie) {
934 /* copy data */
935 upmsg->l2h = msgb_put(upmsg, data_ie->len);
936 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
937 }
938
939 /* send to user SAP */
940 link->user->prim_cb(&prim->oph, link);
941
942 conn_state_set(conn, S_IDLE);
943 conn_destroy(conn);
944
945 return 0;
946}
947
948/* RFC 3868 3.3.6 / SCCP RLSD */
Harald Welte89607042015-12-21 23:51:08 +0100949static int sua_rx_relre(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +0100950{
951 struct osmo_scu_prim *prim;
952 struct sua_connection *conn;
953 struct osmo_scu_connect_param *param;
954 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
955 struct msgb *upmsg;
956 uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
957 uint32_t cause;
958
959 /* resolve conn */
960 conn = conn_find_by_id(link, conn_id);
961 if (!conn) {
962 LOGP(DSUA, LOGL_ERROR, "RELRE for unknwon reference %u\n",
963 conn_id);
964 /* FIXME: error message? */
965 return -1;
966 }
967
968 /* fill primitive */
969 upmsg = sua_msgb_alloc();
970 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
971 param = &prim->u.connect;
972 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
973 OSMO_SCU_PRIM_N_DISCONNECT,
974 PRIM_OP_INDICATION, upmsg); /* what primitive? */
975
976 param->conn_id = conn_id;
977 /* source reference */
978 cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE);
979 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
980 if (data_ie) {
981 /* copy data */
982 upmsg->l2h = msgb_put(upmsg, data_ie->len);
983 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
984 }
985
986 memcpy(&param->called_addr, &conn->called_addr,
987 sizeof(param->called_addr));
988 memcpy(&param->calling_addr, &conn->calling_addr,
989 sizeof(param->calling_addr));
990
991 /* send to user SAP */
992 link->user->prim_cb(&prim->oph, link);
993
994 conn_state_set(conn, S_IDLE);
995 conn_destroy(conn);
996
997 return 0;
998}
999
1000/* RFC 3868 3.3.7 / SCCP RLC */
Harald Welte89607042015-12-21 23:51:08 +01001001static int sua_rx_relco(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +01001002{
1003 struct osmo_scu_prim *prim;
1004 struct sua_connection *conn;
1005 struct osmo_scu_connect_param *param;
1006 struct msgb *upmsg;
1007 uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
1008
1009 /* resolve conn */
1010 conn = conn_find_by_id(link, conn_id);
1011 if (!conn) {
1012 LOGP(DSUA, LOGL_ERROR, "RELCO for unknwon reference %u\n",
1013 conn_id);
1014 /* FIXME: error message? */
1015 return -1;
1016 }
1017 conn_restart_rx_inact_timer(conn);
1018
1019 /* fill primitive */
1020 upmsg = sua_msgb_alloc();
1021 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
1022 param = &prim->u.connect;
1023 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
1024 OSMO_SCU_PRIM_N_DISCONNECT,
1025 PRIM_OP_CONFIRM, upmsg); /* what primitive? */
1026
1027 param->conn_id = conn_id;
1028 /* source reference */
1029 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
1030
1031 memcpy(&param->called_addr, &conn->called_addr,
1032 sizeof(param->called_addr));
1033 memcpy(&param->calling_addr, &conn->calling_addr,
1034 sizeof(param->calling_addr));
1035
1036 /* send to user SAP */
1037 link->user->prim_cb(&prim->oph, link);
1038
1039 conn_destroy(conn);
1040
1041 return 0;
1042
1043}
1044
1045/* RFC3868 3.3.1 / SCCP DT1 */
Harald Welte89607042015-12-21 23:51:08 +01001046static int sua_rx_codt(struct osmo_sua_link *link, struct xua_msg *xua)
Harald Welte283c7fd2015-12-21 23:35:56 +01001047{
1048 struct osmo_scu_prim *prim;
1049 struct sua_connection *conn;
1050 struct osmo_scu_data_param *param;
1051 struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
1052 struct msgb *upmsg;
1053 uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
1054 uint8_t *cur;
1055
1056 /* resolve conn */
1057 conn = conn_find_by_id(link, conn_id);
1058 if (!conn) {
1059 LOGP(DSUA, LOGL_ERROR, "DT1 for unknwon reference %u\n",
1060 conn_id);
1061 /* FIXME: error message? */
1062 return -1;
1063 }
1064
1065 if (conn->state != S_ACTIVE) {
1066 LOGP(DSUA, LOGL_ERROR, "DT1 in invalid state %s\n",
1067 get_value_string(conn_state_names, conn->state));
1068 /* FIXME: error message? */
1069 return -1;
1070 }
1071
1072 conn_restart_rx_inact_timer(conn);
1073
1074 /* fill primitive */
1075 upmsg = sua_msgb_alloc();
1076 prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
1077 param = &prim->u.data;
1078 osmo_prim_init(&prim->oph, SCCP_SAP_USER,
1079 OSMO_SCU_PRIM_N_DATA,
1080 PRIM_OP_INDICATION, upmsg);
1081 param->conn_id = conn_id;
1082 param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
1083
1084 /* copy data */
1085 upmsg->l2h = msgb_put(upmsg, data_ie->len);
1086 memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
1087
1088 /* send to user SAP */
1089 link->user->prim_cb(&prim->oph, link);
1090
1091 return 0;
1092}
1093
1094
1095/* connection-oriented messages received from socket */
Harald Welte89607042015-12-21 23:51:08 +01001096static int sua_rx_co(struct osmo_sua_link *link,
Harald Welte283c7fd2015-12-21 23:35:56 +01001097 struct xua_msg *xua, struct msgb *msg)
1098{
1099 int rc = -1;
1100
1101 if (!check_all_mand_ies(mand_ies_co, xua))
1102 return -1;
1103
1104 switch (xua->hdr.msg_type) {
1105 case SUA_CO_CORE:
1106 rc = sua_rx_core(link, xua);
1107 break;
1108 case SUA_CO_COAK:
1109 rc = sua_rx_coak(link, xua);
1110 break;
1111 case SUA_CO_COREF:
1112 rc = sua_rx_coref(link, xua);
1113 break;
1114 case SUA_CO_RELRE:
1115 rc = sua_rx_relre(link, xua);
1116 break;
1117 case SUA_CO_RELCO:
1118 rc = sua_rx_relco(link, xua);
1119 break;
1120 case SUA_CO_CODT:
1121 rc = sua_rx_codt(link, xua);
1122 break;
1123 case SUA_CO_RESCO:
1124 case SUA_CO_RESRE:
1125 case SUA_CO_CODA:
1126 case SUA_CO_COERR:
1127 case SUA_CO_COIT:
1128 /* FIXME */
1129 default:
1130 break;
1131 }
1132
1133 return rc;
1134}
1135
1136/* process SUA message received from socket */
Harald Welte89607042015-12-21 23:51:08 +01001137static int sua_rx_msg(struct osmo_sua_link *link, struct msgb *msg)
Harald Welte283c7fd2015-12-21 23:35:56 +01001138{
1139 struct xua_msg *xua;
1140 int rc = -1;
1141
1142 xua = xua_from_msg(1, msgb_length(msg), msg->data);
1143 if (!xua) {
1144 LOGP(DSUA, LOGL_ERROR, "Unable to parse incoming "
1145 "SUA message\n");
1146 return -EIO;
1147 }
1148
1149 LOGP(DSUA, LOGL_DEBUG, "Received SUA Message (%u:%u)\n",
1150 xua->hdr.msg_class, xua->hdr.msg_type);
1151
1152 switch (xua->hdr.msg_class) {
1153 case SUA_MSGC_CL:
1154 rc = sua_rx_cl(link, xua, msg);
1155 break;
1156 case SUA_MSGC_CO:
1157 rc = sua_rx_co(link, xua, msg);
1158 break;
1159 case SUA_MSGC_MGMT:
1160 case SUA_MSGC_SNM:
1161 case SUA_MSGC_ASPSM:
1162 case SUA_MSGC_ASPTM:
1163 case SUA_MSGC_RKM:
1164 /* FIXME */
1165 default:
1166 break;
1167 }
1168
1169 xua_msg_free(xua);
1170
1171 return rc;
1172}
1173
1174/***********************************************************************
1175 * libosmonetif integration
1176 ***********************************************************************/
1177
1178#include <osmocom/netif/stream.h>
1179#include <netinet/sctp.h>
1180
1181/* netif code tells us we can read something from the socket */
1182static int sua_srv_conn_cb(struct osmo_stream_srv *conn)
1183{
1184 struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
Harald Welte89607042015-12-21 23:51:08 +01001185 struct osmo_sua_link *link = osmo_stream_srv_get_data(conn);
Harald Welte283c7fd2015-12-21 23:35:56 +01001186 struct msgb *msg = msgb_alloc(SUA_MSGB_SIZE, "SUA Server Rx");
1187 struct sctp_sndrcvinfo sinfo;
1188 unsigned int ppid;
1189 int flags = 0;
1190 int rc;
1191
1192 if (!msg)
1193 return -ENOMEM;
1194
1195 /* read SUA message from socket and process it */
1196 rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
1197 NULL, NULL, &sinfo, &flags);
1198 if (rc < 0) {
1199 close(ofd->fd);
1200 osmo_fd_unregister(ofd);
1201 ofd->fd = -1;
1202 return rc;
1203 } else if (rc == 0) {
1204 close(ofd->fd);
1205 osmo_fd_unregister(ofd);
1206 ofd->fd = -1;
1207 } else {
1208 msgb_put(msg, rc);
1209 }
1210
1211 if (flags & MSG_NOTIFICATION) {
1212 msgb_free(msg);
1213 return 0;
1214 }
1215
1216 ppid = ntohl(sinfo.sinfo_ppid);
1217 msgb_sctp_ppid(msg) = ppid;
1218 msgb_sctp_stream(msg) = ntohl(sinfo.sinfo_stream);
1219 msg->dst = link;
1220
1221 switch (ppid) {
1222 case SUA_PPID:
1223 rc = sua_rx_msg(link, msg);
1224 break;
1225 default:
1226 LOGP(DSUA, LOGL_NOTICE, "SCTP chunk for unknown PPID %u "
1227 "received\n", ppid);
1228 rc = 0;
1229 break;
1230 }
1231
1232 msgb_free(msg);
1233 return rc;
1234}
1235
1236static int sua_srv_conn_closed_cb(struct osmo_stream_srv *srv)
1237{
Harald Welte89607042015-12-21 23:51:08 +01001238 struct osmo_sua_link *sual = osmo_stream_srv_get_data(srv);
Harald Welte283c7fd2015-12-21 23:35:56 +01001239 struct sua_connection *conn;
1240
1241 LOGP(DSUA, LOGL_INFO, "SCTP connection closed\n");
1242
1243 /* remove from per-user list of sua links */
1244 llist_del(&sual->list);
1245
1246 llist_for_each_entry(conn, &sual->connections, list) {
1247 /* FIXME: send RELEASE request */
1248 }
1249 talloc_free(sual);
1250 osmo_stream_srv_set_data(srv, NULL);
1251
1252 return 0;
1253}
1254
1255static int sua_accept_cb(struct osmo_stream_srv_link *link, int fd)
1256{
1257 struct osmo_sua_user *user = osmo_stream_srv_link_get_data(link);
1258 struct osmo_stream_srv *srv;
Harald Welte89607042015-12-21 23:51:08 +01001259 struct osmo_sua_link *sual;
Harald Welte283c7fd2015-12-21 23:35:56 +01001260
1261 LOGP(DSUA, LOGL_INFO, "New SCTP connection accepted\n");
1262
1263 srv = osmo_stream_srv_create(user, link, fd,
1264 sua_srv_conn_cb,
1265 sua_srv_conn_closed_cb, NULL);
1266 if (!srv)
1267 close(fd);
1268
1269 /* create new SUA link and connect both data structures */
1270 sual = sua_link_new(user, 1);
1271 if (!sual) {
1272 osmo_stream_srv_destroy(srv);
1273 return -1;
1274 }
1275 sual->data = srv;
1276 osmo_stream_srv_set_data(srv, sual);
1277
1278 return 0;
1279}
1280
Harald Welte89607042015-12-21 23:51:08 +01001281int osmo_sua_server_listen(struct osmo_sua_user *user, const char *hostname, uint16_t port)
Harald Welte283c7fd2015-12-21 23:35:56 +01001282{
1283 int rc;
1284
1285 if (user->server)
1286 osmo_stream_srv_link_close(user->server);
1287 else {
1288 user->server = osmo_stream_srv_link_create(user);
1289 osmo_stream_srv_link_set_data(user->server, user);
1290 osmo_stream_srv_link_set_accept_cb(user->server, sua_accept_cb);
1291 }
1292
1293 osmo_stream_srv_link_set_addr(user->server, hostname);
1294 osmo_stream_srv_link_set_port(user->server, port);
1295 osmo_stream_srv_link_set_proto(user->server, IPPROTO_SCTP);
1296
1297 rc = osmo_stream_srv_link_open(user->server);
1298 if (rc < 0) {
1299 osmo_stream_srv_link_destroy(user->server);
1300 user->server = NULL;
1301 return rc;
1302 }
1303
1304 return 0;
1305}
1306
1307/* netif code tells us we can read something from the socket */
1308static int sua_cli_conn_cb(struct osmo_stream_cli *conn)
1309{
1310 struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
Harald Welte89607042015-12-21 23:51:08 +01001311 struct osmo_sua_link *link = osmo_stream_cli_get_data(conn);
Harald Welte283c7fd2015-12-21 23:35:56 +01001312 struct msgb *msg = msgb_alloc(SUA_MSGB_SIZE, "SUA Client Rx");
1313 struct sctp_sndrcvinfo sinfo;
1314 unsigned int ppid;
1315 int flags = 0;
1316 int rc;
1317
1318 if (!msg)
1319 return -ENOMEM;
1320
1321 /* read SUA message from socket and process it */
1322 rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
1323 NULL, NULL, &sinfo, &flags);
1324 if (rc < 0) {
1325 close(ofd->fd);
1326 osmo_fd_unregister(ofd);
1327 ofd->fd = -1;
1328 return rc;
1329 } else if (rc == 0) {
1330 close(ofd->fd);
1331 osmo_fd_unregister(ofd);
1332 ofd->fd = -1;
1333 } else {
1334 msgb_put(msg, rc);
1335 }
1336
1337 if (flags & MSG_NOTIFICATION) {
1338 msgb_free(msg);
1339 return 0;
1340 }
1341
1342 ppid = ntohl(sinfo.sinfo_ppid);
1343 msgb_sctp_ppid(msg) = ppid;
1344 msgb_sctp_stream(msg) = ntohl(sinfo.sinfo_stream);
1345 msg->dst = link;
1346
1347 switch (ppid) {
1348 case SUA_PPID:
1349 rc = sua_rx_msg(link, msg);
1350 break;
1351 default:
1352 LOGP(DSUA, LOGL_NOTICE, "SCTP chunk for unknown PPID %u "
1353 "received\n", ppid);
1354 rc = 0;
1355 break;
1356 }
1357
1358 msgb_free(msg);
1359 return rc;
1360}
1361
Harald Welte89607042015-12-21 23:51:08 +01001362int osmo_sua_client_connect(struct osmo_sua_user *user, const char *hostname, uint16_t port)
Harald Welte283c7fd2015-12-21 23:35:56 +01001363{
1364 struct osmo_stream_cli *cli;
Harald Welte89607042015-12-21 23:51:08 +01001365 struct osmo_sua_link *sual;
Harald Welte283c7fd2015-12-21 23:35:56 +01001366 int rc;
1367
1368 cli = osmo_stream_cli_create(user);
1369 if (!cli)
1370 return -1;
1371 osmo_stream_cli_set_addr(cli, hostname);
1372 osmo_stream_cli_set_port(cli, port);
1373 osmo_stream_cli_set_proto(cli, IPPROTO_SCTP);
1374 osmo_stream_cli_set_reconnect_timeout(cli, 5);
1375 osmo_stream_cli_set_read_cb(cli, sua_cli_conn_cb);
1376
1377 /* create SUA link and associate it with stream_cli */
1378 sual = sua_link_new(user, 0);
1379 if (!sual) {
1380 osmo_stream_cli_destroy(cli);
1381 return -1;
1382 }
1383 sual->data = cli;
1384 osmo_stream_cli_set_data(cli, sual);
1385
1386 rc = osmo_stream_cli_open(cli);
1387 if (rc < 0) {
1388 sua_link_destroy(sual);
1389 osmo_stream_cli_destroy(cli);
1390 return rc;
1391 }
1392 user->client = cli;
1393
1394 return 0;
1395}
1396
Harald Welte89607042015-12-21 23:51:08 +01001397struct osmo_sua_link *osmo_sua_client_get_link(struct osmo_sua_user *user)
Harald Welte283c7fd2015-12-21 23:35:56 +01001398{
1399 return osmo_stream_cli_get_data(user->client);
1400}
1401
1402static LLIST_HEAD(sua_users);
1403
1404struct osmo_sua_user *osmo_sua_user_create(void *ctx, osmo_prim_cb prim_cb)
1405{
1406 struct osmo_sua_user *user = talloc_zero(ctx, struct osmo_sua_user);
1407
1408 user->prim_cb = prim_cb;
1409 INIT_LLIST_HEAD(&user->links);
1410
1411 llist_add_tail(&user->list, &sua_users);
1412
1413 return user;
1414}
1415
1416void osmo_sua_user_destroy(struct osmo_sua_user *user)
1417{
Harald Welte89607042015-12-21 23:51:08 +01001418 struct osmo_sua_link *link;
Harald Welte283c7fd2015-12-21 23:35:56 +01001419
1420 llist_del(&user->list);
1421
1422 llist_for_each_entry(link, &user->links, list)
1423 sua_link_destroy(link);
1424
1425 talloc_free(user);
1426}
1427
1428void osmo_sua_set_log_area(int area)
1429{
1430 DSUA = area;
1431}