189 lines
5.4 KiB
C
189 lines
5.4 KiB
C
/* (C) 2018-2019 by sysmocom s.f.m.c. GmbH
|
|
* All Rights Reserved
|
|
*
|
|
* Author: Harald Welte, Philipp Maier
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <osmocom/msc/sgs_iface.h>
|
|
#include <osmocom/msc/debug.h>
|
|
#include <osmocom/msc/sgs_server.h>
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/socket.h>
|
|
#include <osmocom/core/select.h>
|
|
#include <osmocom/netif/stream.h>
|
|
#include <netinet/sctp.h>
|
|
|
|
#define LOGSGC(sgc, lvl, fmt, args...) \
|
|
LOGP(DSGS, lvl, "%s: " fmt, (sgc)->sockname, ## args)
|
|
|
|
/* call-back when data arrives on SGs */
|
|
static int sgs_conn_readable_cb(struct osmo_stream_srv *conn)
|
|
{
|
|
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
|
struct sgs_connection *sgc = osmo_stream_srv_get_data(conn);
|
|
struct msgb *msg = gsm29118_msgb_alloc();
|
|
struct sctp_sndrcvinfo sinfo;
|
|
int flags = 0;
|
|
int rc = 0;
|
|
|
|
/* we cannot use osmo_stream_srv_recv() here, as we might get some out-of-band info from
|
|
* SCTP. FIXME: add something like osmo_stream_srv_recv_sctp() to libosmo-netif and use
|
|
* it here as well as in libosmo-sigtran */
|
|
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg), NULL, NULL, &sinfo, &flags);
|
|
if (rc < 0) {
|
|
osmo_stream_srv_destroy(conn);
|
|
rc = -EBADF;
|
|
goto out;
|
|
} else if (rc == 0) {
|
|
osmo_stream_srv_destroy(conn);
|
|
rc = -EBADF;
|
|
goto out;
|
|
} else {
|
|
msgb_put(msg, rc);
|
|
}
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
|
union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
osmo_stream_srv_destroy(conn);
|
|
rc = -EBADF;
|
|
break;
|
|
case SCTP_ASSOC_CHANGE:
|
|
/* FIXME: do we have to notify the SGs code about this? */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
/* set l2 header, as that's what we use in SGs code */
|
|
msg->l2h = msgb_data(msg);
|
|
|
|
if (msgb_sctp_ppid(msg) != 0) {
|
|
LOGSGC(sgc, LOGL_NOTICE, "Ignoring SCTP PPID %ld (spec violation)\n", msgb_sctp_ppid(msg));
|
|
msgb_free(msg);
|
|
return 0;
|
|
}
|
|
|
|
/* handle message */
|
|
sgs_iface_rx(sgc, msg);
|
|
|
|
return 0;
|
|
out:
|
|
msgb_free(msg);
|
|
return rc;
|
|
}
|
|
|
|
/* call-back when new connection is closed ed on SGs */
|
|
static int sgs_conn_closed_cb(struct osmo_stream_srv *conn)
|
|
{
|
|
struct sgs_connection *sgc = osmo_stream_srv_get_data(conn);
|
|
|
|
LOGSGC(sgc, LOGL_NOTICE, "Connection lost\n");
|
|
if (sgc->mme) {
|
|
/* unlink ourselves from the MME context */
|
|
if (sgc->mme->conn == sgc)
|
|
sgc->mme->conn = NULL;
|
|
}
|
|
llist_del(&sgc->entry);
|
|
return 0;
|
|
}
|
|
|
|
/* call-back when new connection is accept() ed on SGs */
|
|
static int sgs_accept_cb(struct osmo_stream_srv_link *link, int fd)
|
|
{
|
|
struct sgs_state *sgs = osmo_stream_srv_link_get_data(link);
|
|
struct sgs_connection *sgc = talloc_zero(link, struct sgs_connection);
|
|
OSMO_ASSERT(sgc);
|
|
sgc->sgs = sgs;
|
|
osmo_sock_get_name_buf(sgc->sockname, sizeof(sgc->sockname), fd);
|
|
sgc->srv = osmo_stream_srv_create(sgc, link, fd, sgs_conn_readable_cb, sgs_conn_closed_cb, sgc);
|
|
if (!sgc->srv) {
|
|
talloc_free(sgc);
|
|
return -1;
|
|
}
|
|
LOGSGC(sgc, LOGL_INFO, "Accepted new SGs connection\n");
|
|
llist_add_tail(&sgc->entry, &sgs->conn_list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct sgs_state *sgs_state_alloc(void *ctx)
|
|
{
|
|
struct sgs_state *sgs = talloc_zero(ctx, struct sgs_state);
|
|
|
|
INIT_LLIST_HEAD(&sgs->mme_list);
|
|
INIT_LLIST_HEAD(&sgs->conn_list);
|
|
|
|
memcpy(sgs->cfg.timer, sgs_state_timer_defaults, sizeof(sgs->cfg.timer));
|
|
memcpy(sgs->cfg.counter, sgs_state_counter_defaults, sizeof(sgs->cfg.counter));
|
|
sgs->cfg.local_port = SGS_PORT_DEFAULT;
|
|
osmo_strlcpy(sgs->cfg.local_addr, DEFAULT_SGS_SERVER_IP, sizeof(sgs->cfg.local_addr));
|
|
osmo_strlcpy(sgs->cfg.vlr_name, DEFAULT_SGS_SERVER_VLR_NAME, sizeof(sgs->cfg.vlr_name));
|
|
|
|
return sgs;
|
|
}
|
|
|
|
/*! allocate SGs new sgs state
|
|
* \param[in] ctx talloc context
|
|
* \returns returns allocated sgs state, NULL in case of error. */
|
|
struct sgs_state *sgs_server_alloc(void *ctx)
|
|
{
|
|
struct sgs_state *sgs;
|
|
struct osmo_stream_srv_link *link;
|
|
|
|
sgs = sgs_state_alloc(ctx);
|
|
if (!sgs)
|
|
return NULL;
|
|
|
|
sgs->srv_link = link = osmo_stream_srv_link_create(ctx);
|
|
if (!sgs->srv_link)
|
|
return NULL;
|
|
|
|
osmo_stream_srv_link_set_nodelay(link, true);
|
|
osmo_stream_srv_link_set_addr(link, sgs->cfg.local_addr);
|
|
osmo_stream_srv_link_set_port(link, sgs->cfg.local_port);
|
|
osmo_stream_srv_link_set_proto(link, IPPROTO_SCTP);
|
|
osmo_stream_srv_link_set_data(link, sgs);
|
|
osmo_stream_srv_link_set_accept_cb(link, sgs_accept_cb);
|
|
|
|
return sgs;
|
|
}
|
|
|
|
/*! (re)open SGs interface (SCTP)
|
|
* \param[in] sgs associated sgs state
|
|
* \returns 0 in case of success, -EINVAL in case of error. */
|
|
int sgs_server_open(struct sgs_state *sgs)
|
|
{
|
|
int rc;
|
|
struct osmo_fd *ofd = osmo_stream_srv_link_get_ofd(sgs->srv_link);
|
|
|
|
rc = osmo_stream_srv_link_open(sgs->srv_link);
|
|
if (rc < 0) {
|
|
LOGP(DSGS, LOGL_ERROR, "SGs socket cannot be opened: %s\n", strerror(errno));
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOGP(DSGS, LOGL_NOTICE, "SGs socket bound to %s\n", osmo_sock_get_name2(ofd->fd));
|
|
return 0;
|
|
}
|