blob: 779e12c11af1ed2d2c5391f3db877aa3af6c672c [file] [log] [blame]
Harald Welte0df904d2018-12-03 11:00:04 +01001/* (C) 2018-2019 by sysmocom s.f.m.c. GmbH
2 * All Rights Reserved
3 *
4 * Author: Harald Welte, Philipp Maier
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
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010021#include <errno.h>
22
Harald Welte0df904d2018-12-03 11:00:04 +010023#include <osmocom/msc/sgs_iface.h>
24#include <osmocom/msc/debug.h>
25#include <osmocom/msc/sgs_server.h>
26#include <osmocom/core/utils.h>
27#include <osmocom/core/socket.h>
28#include <osmocom/core/select.h>
29#include <osmocom/netif/stream.h>
30#include <netinet/sctp.h>
31
32#define LOGSGC(sgc, lvl, fmt, args...) \
33 LOGP(DSGS, lvl, "%s: " fmt, (sgc)->sockname, ## args)
34
35/* call-back when data arrives on SGs */
36static int sgs_conn_readable_cb(struct osmo_stream_srv *conn)
37{
38 struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
39 struct sgs_connection *sgc = osmo_stream_srv_get_data(conn);
40 struct msgb *msg = gsm29118_msgb_alloc();
41 struct sctp_sndrcvinfo sinfo;
42 int flags = 0;
Vadim Yanitskiy48a24cd2019-05-10 21:18:05 +070043 int rc = 0;
Harald Welte0df904d2018-12-03 11:00:04 +010044
45 /* we cannot use osmo_stream_srv_recv() here, as we might get some out-of-band info from
46 * SCTP. FIXME: add something like osmo_stream_srv_recv_sctp() to libosmo-netif and use
47 * it here as well as in libosmo-sigtran */
48 rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg), NULL, NULL, &sinfo, &flags);
49 if (rc < 0) {
50 osmo_stream_srv_destroy(conn);
51 rc = -EBADF;
52 goto out;
53 } else if (rc == 0) {
54 osmo_stream_srv_destroy(conn);
55 rc = -EBADF;
56 goto out;
57 } else {
58 msgb_put(msg, rc);
59 }
60
61 if (flags & MSG_NOTIFICATION) {
62 union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
63
64 switch (notif->sn_header.sn_type) {
65 case SCTP_SHUTDOWN_EVENT:
66 osmo_stream_srv_destroy(conn);
67 rc = -EBADF;
68 break;
69 case SCTP_ASSOC_CHANGE:
70 /* FIXME: do we have to notify the SGs code about this? */
71 break;
72 default:
73 break;
74 }
Harald Welte0df904d2018-12-03 11:00:04 +010075 goto out;
76 }
77
78 /* set l2 header, as that's what we use in SGs code */
79 msg->l2h = msgb_data(msg);
80
81 if (msgb_sctp_ppid(msg) != 0) {
82 LOGSGC(sgc, LOGL_NOTICE, "Ignoring SCTP PPID %ld (spec violation)\n", msgb_sctp_ppid(msg));
83 msgb_free(msg);
84 return 0;
85 }
86
87 /* handle message */
88 sgs_iface_rx(sgc, msg);
89
90 return 0;
91out:
92 msgb_free(msg);
93 return rc;
94}
95
96/* call-back when new connection is closed ed on SGs */
97static int sgs_conn_closed_cb(struct osmo_stream_srv *conn)
98{
99 struct sgs_connection *sgc = osmo_stream_srv_get_data(conn);
100
101 LOGSGC(sgc, LOGL_NOTICE, "Connection lost\n");
102 if (sgc->mme) {
103 /* unlink ourselves from the MME context */
104 if (sgc->mme->conn == sgc)
105 sgc->mme->conn = NULL;
106 }
107 llist_del(&sgc->entry);
108 return 0;
109}
110
111/* call-back when new connection is accept() ed on SGs */
112static int sgs_accept_cb(struct osmo_stream_srv_link *link, int fd)
113{
114 struct sgs_state *sgs = osmo_stream_srv_link_get_data(link);
115 struct sgs_connection *sgc = talloc_zero(link, struct sgs_connection);
116 OSMO_ASSERT(sgc);
117 sgc->sgs = sgs;
118 osmo_sock_get_name_buf(sgc->sockname, sizeof(sgc->sockname), fd);
119 sgc->srv = osmo_stream_srv_create(sgc, link, fd, sgs_conn_readable_cb, sgs_conn_closed_cb, sgc);
120 if (!sgc->srv) {
121 talloc_free(sgc);
122 return -1;
123 }
124 LOGSGC(sgc, LOGL_INFO, "Accepted new SGs connection\n");
125 llist_add_tail(&sgc->entry, &sgs->conn_list);
126
127 return 0;
128}
129
130static struct sgs_state *sgs_state_alloc(void *ctx)
131{
132 struct sgs_state *sgs = talloc_zero(ctx, struct sgs_state);
133
134 INIT_LLIST_HEAD(&sgs->mme_list);
135 INIT_LLIST_HEAD(&sgs->conn_list);
136
137 memcpy(sgs->cfg.timer, sgs_state_timer_defaults, sizeof(sgs->cfg.timer));
138 memcpy(sgs->cfg.counter, sgs_state_counter_defaults, sizeof(sgs->cfg.counter));
139 sgs->cfg.local_port = SGS_PORT_DEFAULT;
140 osmo_strlcpy(sgs->cfg.local_addr, DEFAULT_SGS_SERVER_IP, sizeof(sgs->cfg.local_addr));
141 osmo_strlcpy(sgs->cfg.vlr_name, DEFAULT_SGS_SERVER_VLR_NAME, sizeof(sgs->cfg.vlr_name));
142
143 return sgs;
144}
145
146/*! allocate SGs new sgs state
147 * \param[in] ctx talloc context
148 * \returns returns allocated sgs state, NULL in case of error. */
149struct sgs_state *sgs_server_alloc(void *ctx)
150{
151 struct sgs_state *sgs;
152 struct osmo_stream_srv_link *link;
153
154 sgs = sgs_state_alloc(ctx);
155 if (!sgs)
156 return NULL;
157
158 sgs->srv_link = link = osmo_stream_srv_link_create(ctx);
159 if (!sgs->srv_link)
160 return NULL;
161
162 osmo_stream_srv_link_set_nodelay(link, true);
163 osmo_stream_srv_link_set_addr(link, sgs->cfg.local_addr);
164 osmo_stream_srv_link_set_port(link, sgs->cfg.local_port);
165 osmo_stream_srv_link_set_proto(link, IPPROTO_SCTP);
166 osmo_stream_srv_link_set_data(link, sgs);
167 osmo_stream_srv_link_set_accept_cb(link, sgs_accept_cb);
168
169 return sgs;
170}
171
172/*! (re)open SGs interface (SCTP)
173 * \param[in] sgs associated sgs state
174 * \returns 0 in case of success, -EINVAL in case of error. */
175int sgs_server_open(struct sgs_state *sgs)
176{
177 int rc;
178 struct osmo_fd *ofd = osmo_stream_srv_link_get_ofd(sgs->srv_link);
179
180 rc = osmo_stream_srv_link_open(sgs->srv_link);
181 if (rc < 0) {
182 LOGP(DSGS, LOGL_ERROR, "SGs socket cannot be opened: %s\n", strerror(errno));
183 return -EINVAL;
184 }
185
186 LOGP(DSGS, LOGL_NOTICE, "SGs socket bound to %s\n", osmo_sock_get_name2(ofd->fd));
187 return 0;
188}