blob: 28c42cbb52663129e943e1cb5aa72a7310fcad5a [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;
43 int rc;
44
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 }
75 rc = 0;
76 goto out;
77 }
78
79 /* set l2 header, as that's what we use in SGs code */
80 msg->l2h = msgb_data(msg);
81
82 if (msgb_sctp_ppid(msg) != 0) {
83 LOGSGC(sgc, LOGL_NOTICE, "Ignoring SCTP PPID %ld (spec violation)\n", msgb_sctp_ppid(msg));
84 msgb_free(msg);
85 return 0;
86 }
87
88 /* handle message */
89 sgs_iface_rx(sgc, msg);
90
91 return 0;
92out:
93 msgb_free(msg);
94 return rc;
95}
96
97/* call-back when new connection is closed ed on SGs */
98static int sgs_conn_closed_cb(struct osmo_stream_srv *conn)
99{
100 struct sgs_connection *sgc = osmo_stream_srv_get_data(conn);
101
102 LOGSGC(sgc, LOGL_NOTICE, "Connection lost\n");
103 if (sgc->mme) {
104 /* unlink ourselves from the MME context */
105 if (sgc->mme->conn == sgc)
106 sgc->mme->conn = NULL;
107 }
108 llist_del(&sgc->entry);
109 return 0;
110}
111
112/* call-back when new connection is accept() ed on SGs */
113static int sgs_accept_cb(struct osmo_stream_srv_link *link, int fd)
114{
115 struct sgs_state *sgs = osmo_stream_srv_link_get_data(link);
116 struct sgs_connection *sgc = talloc_zero(link, struct sgs_connection);
117 OSMO_ASSERT(sgc);
118 sgc->sgs = sgs;
119 osmo_sock_get_name_buf(sgc->sockname, sizeof(sgc->sockname), fd);
120 sgc->srv = osmo_stream_srv_create(sgc, link, fd, sgs_conn_readable_cb, sgs_conn_closed_cb, sgc);
121 if (!sgc->srv) {
122 talloc_free(sgc);
123 return -1;
124 }
125 LOGSGC(sgc, LOGL_INFO, "Accepted new SGs connection\n");
126 llist_add_tail(&sgc->entry, &sgs->conn_list);
127
128 return 0;
129}
130
131static struct sgs_state *sgs_state_alloc(void *ctx)
132{
133 struct sgs_state *sgs = talloc_zero(ctx, struct sgs_state);
134
135 INIT_LLIST_HEAD(&sgs->mme_list);
136 INIT_LLIST_HEAD(&sgs->conn_list);
137
138 memcpy(sgs->cfg.timer, sgs_state_timer_defaults, sizeof(sgs->cfg.timer));
139 memcpy(sgs->cfg.counter, sgs_state_counter_defaults, sizeof(sgs->cfg.counter));
140 sgs->cfg.local_port = SGS_PORT_DEFAULT;
141 osmo_strlcpy(sgs->cfg.local_addr, DEFAULT_SGS_SERVER_IP, sizeof(sgs->cfg.local_addr));
142 osmo_strlcpy(sgs->cfg.vlr_name, DEFAULT_SGS_SERVER_VLR_NAME, sizeof(sgs->cfg.vlr_name));
143
144 return sgs;
145}
146
147/*! allocate SGs new sgs state
148 * \param[in] ctx talloc context
149 * \returns returns allocated sgs state, NULL in case of error. */
150struct sgs_state *sgs_server_alloc(void *ctx)
151{
152 struct sgs_state *sgs;
153 struct osmo_stream_srv_link *link;
154
155 sgs = sgs_state_alloc(ctx);
156 if (!sgs)
157 return NULL;
158
159 sgs->srv_link = link = osmo_stream_srv_link_create(ctx);
160 if (!sgs->srv_link)
161 return NULL;
162
163 osmo_stream_srv_link_set_nodelay(link, true);
164 osmo_stream_srv_link_set_addr(link, sgs->cfg.local_addr);
165 osmo_stream_srv_link_set_port(link, sgs->cfg.local_port);
166 osmo_stream_srv_link_set_proto(link, IPPROTO_SCTP);
167 osmo_stream_srv_link_set_data(link, sgs);
168 osmo_stream_srv_link_set_accept_cb(link, sgs_accept_cb);
169
170 return sgs;
171}
172
173/*! (re)open SGs interface (SCTP)
174 * \param[in] sgs associated sgs state
175 * \returns 0 in case of success, -EINVAL in case of error. */
176int sgs_server_open(struct sgs_state *sgs)
177{
178 int rc;
179 struct osmo_fd *ofd = osmo_stream_srv_link_get_ofd(sgs->srv_link);
180
181 rc = osmo_stream_srv_link_open(sgs->srv_link);
182 if (rc < 0) {
183 LOGP(DSGS, LOGL_ERROR, "SGs socket cannot be opened: %s\n", strerror(errno));
184 return -EINVAL;
185 }
186
187 LOGP(DSGS, LOGL_NOTICE, "SGs socket bound to %s\n", osmo_sock_get_name2(ofd->fd));
188 return 0;
189}