Projects
osmocom:latest
osmo-uecups
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 2
View file
osmo-uecups_0.1.4.dsc -> osmo-uecups_0.2.0.dsc
Changed
@@ -2,18 +2,18 @@ Source: osmo-uecups Binary: osmo-uecups Architecture: any -Version: 0.1.4 +Version: 0.2.0 Maintainer: Harald Welte <laforge@osmocom.org> Homepage: https://osmocom.org/projects/osmo-uecups Standards-Version: 3.9.8 -Vcs-Browser: https://git.osmocom.org/osmo-uecups -Vcs-Git: git://git.osmocom.org/osmo-uecups.git -Build-Depends: debhelper (>= 9), dh-autoreconf, autotools-dev, autoconf, autoconf-archive, automake, libtool, pkg-config, libjansson-dev, libnl-route-3-dev, libosmocore-dev (>= 1.1.0), libosmo-netif-dev, libsctp-dev, osmo-gsm-manuals-dev +Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups +Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups +Build-Depends: debhelper (>= 9), dh-autoreconf, autotools-dev, autoconf, automake, libtool, pkg-config, libjansson-dev, libnl-route-3-dev, libosmocore-dev (>= 1.7.0), libosmo-netif-dev (>= 1.2.0), libsctp-dev, osmo-gsm-manuals-dev (>= 1.3.0) Package-List: osmo-uecups deb net extra arch=any Checksums-Sha1: - c6aeef775fdda61264cdb2ad00a61fe0252315ac 24820 osmo-uecups_0.1.4.tar.xz + 77306a9b01a6aa28b457695a40b0925d1e9bdabd 26876 osmo-uecups_0.2.0.tar.xz Checksums-Sha256: - 1d274267b3b7eff28685378f6c4b7a381ca1e1935e14f63e26404ba7649e881b 24820 osmo-uecups_0.1.4.tar.xz + 3d5d3c6f2a696c6b628e069da41da727d312c896f4c9ba7fdc6c56ba4bc722c3 26876 osmo-uecups_0.2.0.tar.xz Files: - 9bf103bc1e191aa723dd657b367abd5a 24820 osmo-uecups_0.1.4.tar.xz + a577448bb7ef43c46b2fcad209b7e780 26876 osmo-uecups_0.2.0.tar.xz
View file
osmo-uecups_0.1.4.tar.xz/.tarball-version -> osmo-uecups_0.2.0.tar.xz/.tarball-version
Changed
@@ -1 +1 @@ -0.1.4 \ No newline at end of file +0.2.0 \ No newline at end of file
View file
osmo-uecups_0.1.4.tar.xz/README.md -> osmo-uecups_0.2.0.tar.xz/README.md
Changed
@@ -30,9 +30,9 @@ You can clone from the official osmo-bts.git repository using - git clone git://git.osmocom.org/osmo-uecups.git + git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups -There is a cgit interface at httsp://git.osmocom.org/osmo-uecups/ +There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups> Documentation ------------- @@ -62,5 +62,5 @@ https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for more details -The current patch queue for osmo-bts can be seen at +The current patch queue for osmo-uecups can be seen at https://gerrit.osmocom.org/#/q/project:osmo-uecups+status:open
View file
osmo-uecups_0.1.4.tar.xz/configure.ac -> osmo-uecups_0.2.0.tar.xz/configure.ac
Changed
@@ -26,9 +26,9 @@ fi PKG_PROG_PKG_CONFIG(0.20) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0) -PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0) -PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.7.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0) +PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0) PKG_CHECK_MODULES(LIBJANSSON, jansson) PKG_CHECK_MODULES(LIBNLROUTE3, libnl-route-3.0)
View file
osmo-uecups_0.1.4.tar.xz/daemon/Makefile.am -> osmo-uecups_0.2.0.tar.xz/daemon/Makefile.am
Changed
@@ -36,10 +36,12 @@ $(NULL) osmo_uecups_daemon_SOURCES = \ + cups_client.c \ utility.c \ netdev.c \ netns.c \ tun_device.c \ + gtp_daemon.c \ gtp_endpoint.c \ gtp_tunnel.c \ daemon_vty.c \
View file
osmo-uecups_0.2.0.tar.xz/daemon/cups_client.c
Added
@@ -0,0 +1,612 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <unistd.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <pthread.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/exec.h> + +#include "internal.h" + +#include <netinet/sctp.h> + +/*********************************************************************** + * Client (Control/User Plane Separation) Socket + ***********************************************************************/ + +#define CUPS_MSGB_SIZE 1024 + +#define LOGCC(cc, lvl, fmt, args ...) \ + LOGP(DUECUPS, lvl, "%s: " fmt, (cc)->sockname, ## args) + +struct subprocess { + /* member in daemon->cups_clients */ + struct llist_head list; + /* pointer to the client that started us */ + struct cups_client *cups_client; + /* PID of the process */ + pid_t pid; +}; + +static json_t *gen_uecups_term_ind(pid_t pid, int status); + +/* kill the specified subprocess and forget about it */ +static void subprocess_destroy(struct subprocess *p, int signal) +{ + LOGCC(p->cups_client, LOGL_DEBUG, "Kill subprocess pid %llu with signal %u\n", + (unsigned long long)p->pid, signal); + kill(p->pid, signal); + llist_del(&p->list); + talloc_free(p); +} + +static struct subprocess *subprocess_by_pid(struct gtp_daemon *d, pid_t pid) +{ + struct subprocess *sproc; + llist_for_each_entry(sproc, &d->subprocesses, list) { + if (sproc->pid == pid) + return sproc; + } + return NULL; +} + +void child_terminated(struct gtp_daemon *d, int pid, int status) +{ + struct subprocess *sproc; + json_t *jterm_ind; + + LOGP(DUECUPS, LOGL_DEBUG, "SIGCHLD receive from pid %u; status=%d\n", pid, status); + + sproc = subprocess_by_pid(d, pid); + if (!sproc) { + LOGP(DUECUPS, LOGL_NOTICE, "subprocess %u terminated (status=%d) but we don't know it?\n", + pid, status); + return; + } + + /* generate prog_term_ind towards control plane */ + jterm_ind = gen_uecups_term_ind(pid, status); + if (!jterm_ind) + return; + + cups_client_tx_json(sproc->cups_client, jterm_ind); + + llist_del(&sproc->list); + talloc_free(sproc); +} + +/* Send JSON to a given client/connection */ +int cups_client_tx_json(struct cups_client *cc, json_t *jtx) +{ + struct msgb *msg = msgb_alloc(CUPS_MSGB_SIZE, "Tx JSON"); + char *json_str = json_dumps(jtx, JSON_SORT_KEYS); + char *out; + int json_strlen; + + json_decref(jtx); + if (!json_str) { + LOGCC(cc, LOGL_ERROR, "Error encoding JSON\n"); + return 0; + } + json_strlen = strlen(json_str); + + LOGCC(cc, LOGL_DEBUG, "JSON Tx '%s'\n", json_str); + + if (json_strlen > msgb_tailroom(msg)) { + LOGCC(cc, LOGL_ERROR, "Not enough room for JSON in msgb\n"); + free(json_str); + return 0; + } + + out = (char *)msgb_put(msg, json_strlen); + memcpy(out, json_str, json_strlen); + free(json_str); + osmo_stream_srv_send(cc->srv, msg); + + return 0; +} + +json_t *gen_uecups_result(const char *name, const char *res) +{ + json_t *jres = json_object(); + json_t *jret = json_object(); + + json_object_set_new(jres, "result", json_string(res)); + json_object_set_new(jret, name, jres); + + return jret; +} + +static int parse_ep(struct sockaddr_storage *out, json_t *in) +{ + json_t *jaddr_type, *jport, *jip; + const char *addr_type, *ip; + uint8_t buf16; + + /* {"addr_type":"IPV4","ip":"31323334","Port":2152} */ + + if (!json_is_object(in)) + return -EINVAL; + + jaddr_type = json_object_get(in, "addr_type"); + jport = json_object_get(in, "Port"); + jip = json_object_get(in, "ip"); + + if (!jaddr_type || !jport || !jip) + return -EINVAL; + + if (!json_is_string(jaddr_type) || !json_is_integer(jport) || !json_is_string(jip)) + return -EINVAL; + + addr_type = json_string_value(jaddr_type); + ip = json_string_value(jip); + + memset(out, 0, sizeof(*out)); + + if (!strcmp(addr_type, "IPV4")) { + struct sockaddr_in *sin = (struct sockaddr_in *) out; + if (osmo_hexparse(ip, buf, sizeof(buf)) != 4) + return -EINVAL; + memcpy(&sin->sin_addr, buf, 4); + sin->sin_family = AF_INET; + sin->sin_port = htons(json_integer_value(jport)); + } else if (!strcmp(addr_type, "IPV6")) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) out; + if (osmo_hexparse(ip, buf, sizeof(buf)) != 16) + return -EINVAL; + memcpy(&sin6->sin6_addr, buf, 16); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(json_integer_value(jport)); + } else + return -EINVAL; + + return 0; +} + +static int parse_eua(struct sockaddr_storage *out, json_t *jip, json_t *jaddr_type) +{ + const char *addr_type, *ip; + uint8_t buf16; + + if (!json_is_string(jip) || !json_is_string(jaddr_type)) + return -EINVAL; + + addr_type = json_string_value(jaddr_type); + ip = json_string_value(jip); + + memset(out, 0, sizeof(*out)); + + if (!strcmp(addr_type, "IPV4")) { + struct sockaddr_in *sin = (struct sockaddr_in *) out; + if (osmo_hexparse(ip, buf, sizeof(buf)) != 4) + return -EINVAL; + memcpy(&sin->sin_addr, buf, 4); + sin->sin_family = AF_INET; + } else if (!strcmp(addr_type, "IPV6")) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) out; + if (osmo_hexparse(ip, buf, sizeof(buf)) != 16) + return -EINVAL; + memcpy(&sin6->sin6_addr, buf, 16); + sin6->sin6_family = AF_INET6; + } else + return -EINVAL; + + return 0; +} + + +static int parse_create_tun(struct gtp_tunnel_params *out, json_t *ctun) +{ + json_t *jlocal_gtp_ep, *jremote_gtp_ep; + json_t *jrx_teid, *jtx_teid; + json_t *jtun_dev_name, *jtun_netns_name; + json_t *juser_addr, *juser_addr_type; + int rc; + + /* '{"create_tun":{"tx_teid":1234,"rx_teid":5678,"user_addr_type":"IPV4","user_addr":"21222324","local_gtp_ep":{"addr_type":"IPV4","ip":"31323334","Port":2152},"remote_gtp_ep":{"addr_type":"IPV4","ip":"41424344","Port":2152},"tun_dev_name":"tun23","tun_netns_name":"foo"}}' */ + + if (!json_is_object(ctun)) + return -EINVAL; + + /* mandatory IEs */ + jlocal_gtp_ep = json_object_get(ctun, "local_gtp_ep"); + jremote_gtp_ep = json_object_get(ctun, "remote_gtp_ep"); + jrx_teid = json_object_get(ctun, "rx_teid"); + jtx_teid = json_object_get(ctun, "tx_teid"); + jtun_dev_name = json_object_get(ctun, "tun_dev_name"); + juser_addr = json_object_get(ctun, "user_addr"); + juser_addr_type = json_object_get(ctun, "user_addr_type"); + + if (!jlocal_gtp_ep || !jremote_gtp_ep || !jrx_teid || !jtx_teid || !jtun_dev_name || + !juser_addr || !juser_addr_type) + return -EINVAL; + if (!json_is_object(jlocal_gtp_ep) || !json_is_object(jremote_gtp_ep) || + !json_is_integer(jrx_teid) || !json_is_integer(jtx_teid) || + !json_is_string(jtun_dev_name) || + !json_is_string(juser_addr) || !json_is_string(juser_addr_type)) + return -EINVAL; + + memset(out, 0, sizeof(*out)); + + rc = parse_ep(&out->local_udp, jlocal_gtp_ep); + if (rc < 0) + return rc; + rc = parse_ep(&out->remote_udp, jremote_gtp_ep); + if (rc < 0) + return rc; + rc = parse_eua(&out->user_addr, juser_addr, juser_addr_type); + if (rc < 0) + return rc; + out->rx_teid = json_integer_value(jrx_teid); + out->tx_teid = json_integer_value(jtx_teid); + out->tun_name = talloc_strdup(out, json_string_value(jtun_dev_name)); + + /* optional IEs */ + jtun_netns_name = json_object_get(ctun, "tun_netns_name"); + if (jtun_netns_name) { + if (!json_is_string(jtun_netns_name)) + return -EINVAL; + out->tun_netns_name = talloc_strdup(out, json_string_value(jtun_netns_name)); + } + + return 0; +} + + +static int cups_client_handle_create_tun(struct cups_client *cc, json_t *ctun) +{ + int rc; + struct gtp_tunnel_params *tpars = talloc_zero(cc, struct gtp_tunnel_params); + struct gtp_tunnel *t; + + rc = parse_create_tun(tpars, ctun); + if (rc < 0) { + talloc_free(tpars); + return rc; + } + + t = gtp_tunnel_alloc(g_daemon, tpars); + if (!t) { + LOGCC(cc, LOGL_NOTICE, "Failed to allocate tunnel\n"); + cups_client_tx_json(cc, gen_uecups_result("create_tun_res", "ERR_NOT_FOUND")); + } else { + cups_client_tx_json(cc, gen_uecups_result("create_tun_res", "OK")); + } + + talloc_free(tpars); + return 0; +} + +static int cups_client_handle_destroy_tun(struct cups_client *cc, json_t *dtun) +{ + struct sockaddr_storage local_ep_addr; + json_t *jlocal_gtp_ep, *jrx_teid; + uint32_t rx_teid; + int rc; + + jlocal_gtp_ep = json_object_get(dtun, "local_gtp_ep"); + jrx_teid = json_object_get(dtun, "rx_teid"); + + if (!jlocal_gtp_ep || !jrx_teid) + return -EINVAL; + + if (!json_is_object(jlocal_gtp_ep) || !json_is_integer(jrx_teid)) + return -EINVAL; + + rc = parse_ep(&local_ep_addr, jlocal_gtp_ep); + if (rc < 0) + return rc; + rx_teid = json_integer_value(jrx_teid); + + rc = gtp_tunnel_destroy(g_daemon, &local_ep_addr, rx_teid); + if (rc < 0) { + LOGCC(cc, LOGL_NOTICE, "Failed to destroy tunnel\n"); + cups_client_tx_json(cc, gen_uecups_result("destroy_tun_res", "ERR_NOT_FOUND")); + } else { + cups_client_tx_json(cc, gen_uecups_result("destroy_tun_res", "OK")); + } + + return 0; +} + +static json_t *gen_uecups_term_ind(pid_t pid, int status) +{ + json_t *jterm = json_object(); + json_t *jret = json_object(); + + json_object_set_new(jterm, "pid", json_integer(pid)); + json_object_set_new(jterm, "exit_code", json_integer(status)); + + json_object_set_new(jret, "program_term_ind", jterm); + + return jret; +} + +static json_t *gen_uecups_start_res(pid_t pid, const char *result) +{ + json_t *ret = gen_uecups_result("start_program_res", result); + json_object_set_new(json_object_get(ret, "start_program_res"), "pid", json_integer(pid)); + + return ret; +} + +static int cups_client_handle_start_program(struct cups_client *cc, json_t *sprog) +{ + json_t *juser, *jcmd, *jenv, *jnetns, *jres; + struct gtp_daemon *d = cc->d; + const char *cmd, *user; + char **addl_env = NULL; + sigset_t oldmask; + int nsfd = -1, rc; + + juser = json_object_get(sprog, "run_as_user"); + jcmd = json_object_get(sprog, "command"); + jenv = json_object_get(sprog, "environment"); + jnetns = json_object_get(sprog, "tun_netns_name"); + + /* mandatory parts */ + if (!juser || !jcmd) + return -EINVAL; + if (!json_is_string(juser) || !json_is_string(jcmd)) + return -EINVAL; + + /* optional parts */ + if (jenv && !json_is_array(jenv)) + return -EINVAL; + if (jnetns && !json_is_string(jnetns)) + return -EINVAL; + + cmd = json_string_value(jcmd); + user = json_string_value(juser); + if (jnetns) { + struct tun_device *tun = tun_device_find_netns(d, json_string_value(jnetns)); + if (!tun) + return -ENODEV; + nsfd = tun->netns_fd; + } + + /* build environment */ + if (jenv) { + json_t *j; + int i; + addl_env = talloc_zero_array(cc, char *, json_array_size(jenv)+1); + if (!addl_env) + return -ENOMEM; + json_array_foreach(jenv, i, j) { + addl_envi = talloc_strdup(addl_env, json_string_value(j)); + } + } + + if (jnetns) { + rc = switch_ns(nsfd, &oldmask); + if (rc < 0) { + talloc_free(addl_env); + return -EIO; + } + } + + rc = osmo_system_nowait2(cmd, osmo_environment_whitelist, addl_env, user); + + if (jnetns) { + OSMO_ASSERT(restore_ns(&oldmask) == 0); + } + + talloc_free(addl_env); + + if (rc > 0) { + /* create a record about the subprocess we started, so we can notify the + * client that crated it upon termination */ + struct subprocess *sproc = talloc_zero(cc, struct subprocess); + if (!sproc) + return -ENOMEM; + + sproc->cups_client = cc; + sproc->pid = rc; + llist_add_tail(&sproc->list, &d->subprocesses); + jres = gen_uecups_start_res(sproc->pid, "OK"); + } else { + jres = gen_uecups_start_res(0, "ERR_INVALID_DATA"); + } + + cups_client_tx_json(cc, jres); + + return 0; +} + +static int cups_client_handle_reset_all_state(struct cups_client *cc, json_t *sprog) +{ + struct gtp_daemon *d = cc->d; + struct gtp_tunnel *t, *t2; + struct subprocess *p, *p2; + json_t *jres; + + LOGCC(cc, LOGL_DEBUG, "Destroying all tunnels\n"); + pthread_rwlock_wrlock(&d->rwlock); + llist_for_each_entry_safe(t, t2, &d->gtp_tunnels, list) { + _gtp_tunnel_destroy(t); + } + pthread_rwlock_unlock(&d->rwlock); + + /* no locking needed as this list is only used by main thread */ + LOGCC(cc, LOGL_DEBUG, "Destroying all subprocesses\n"); + llist_for_each_entry_safe(p, p2, &d->subprocesses, list) { + subprocess_destroy(p, SIGKILL); + } + + if (d->reset_all_state_tun_remaining == 0) { + jres = gen_uecups_result("reset_all_state_res", "OK"); + cups_client_tx_json(cc, jres); + } else { + cc->reset_all_state_res_pending = true; + } + + return 0; +} + +static int cups_client_handle_json(struct cups_client *cc, json_t *jroot) +{ + void *iter; + const char *key; + json_t *cmd; + int rc; + + if (!json_is_object(jroot)) + return -EINVAL; + + iter = json_object_iter(jroot); + key = json_object_iter_key(iter); + cmd = json_object_iter_value(iter); + if (!iter || !key || !cmd) + return -EINVAL; + + if (!strcmp(key, "create_tun")) { + rc = cups_client_handle_create_tun(cc, cmd); + } else if (!strcmp(key, "destroy_tun")) { + rc = cups_client_handle_destroy_tun(cc, cmd); + } else if (!strcmp(key, "start_program")) { + rc = cups_client_handle_start_program(cc, cmd); + } else if (!strcmp(key, "reset_all_state")) { + rc = cups_client_handle_reset_all_state(cc, cmd); + } else { + LOGCC(cc, LOGL_NOTICE, "Unknown command '%s' received\n", key); + return -EINVAL; + } + + if (rc < 0) { + LOGCC(cc, LOGL_NOTICE, "Error %d handling '%s' command\n", rc, key); + char buf64; + snprintf(buf, sizeof(buf), "%s_res", key); + cups_client_tx_json(cc, gen_uecups_result(buf, "ERR_INVALID_DATA")); + return -EINVAL; + } + + return 0; +} + +/* control/user plane separation per-client read cb */ +static int cups_client_read_cb(struct osmo_stream_srv *conn) +{ + struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn); + struct cups_client *cc = osmo_stream_srv_get_data(conn); + struct msgb *msg = msgb_alloc(CUPS_MSGB_SIZE, "Rx JSON"); + struct sctp_sndrcvinfo sinfo; + json_error_t jerr; + json_t *jroot; + int flags = 0; + int rc = 0; + + /* Read message from socket */ + /* 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 and osmo-msc */ + rc = sctp_recvmsg(ofd->fd, msg->tail, msgb_tailroom(msg), NULL, NULL, &sinfo, &flags); + if (rc <= 0) { + osmo_stream_srv_destroy(conn); + rc = -1; + 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; + goto out; + default: + break; + } + goto out; + } + + LOGCC(cc, LOGL_DEBUG, "Rx '%s'\n", msgb_data(msg)); + + /* Parse the JSON */ + jroot = json_loadb((const char *) msgb_data(msg), msgb_length(msg), 0, &jerr); + if (!jroot) { + LOGCC(cc, LOGL_ERROR, "Error decoding JSON (%s)", jerr.text); + rc = -1; + goto out; + } + + /* Dispatch */ + rc = cups_client_handle_json(cc, jroot); + + json_decref(jroot); + msgb_free(msg); + + return 0; +out: + msgb_free(msg); + return rc; +} + +static int cups_client_closed_cb(struct osmo_stream_srv *conn) +{ + struct cups_client *cc = osmo_stream_srv_get_data(conn); + struct gtp_daemon *d = cc->d; + struct subprocess *p, *p2; + + /* kill + forget about all subprocesses of this client */ + /* We need no locking here as the subprocess list is only used from the main thread */ + llist_for_each_entry_safe(p, p2, &d->subprocesses, list) { + if (p->cups_client == cc) + subprocess_destroy(p, SIGKILL); + } + + LOGCC(cc, LOGL_INFO, "UECUPS connection lost\n"); + llist_del(&cc->list); + return 0; +} + + +/* the control/user plane separation server bind/accept fd */ +static int cups_accept_cb(struct osmo_stream_srv_link *link, int fd) +{ + struct gtp_daemon *d = osmo_stream_srv_link_get_data(link); + struct cups_client *cc; + + cc = talloc_zero(d, struct cups_client); + if (!cc) + return -1; + + cc->d = d; + osmo_sock_get_name_buf(cc->sockname, sizeof(cc->sockname), fd); + cc->srv = osmo_stream_srv_create(cc, link, fd, cups_client_read_cb, cups_client_closed_cb, cc); + if (!cc->srv) { + talloc_free(cc); + return -1; + } + LOGCC(cc, LOGL_INFO, "Accepted new UECUPS connection\n"); + + llist_add_tail(&cc->list, &d->cups_clients); + + return 0; +} + +struct osmo_stream_srv_link *cups_srv_link_create(struct gtp_daemon *d) +{ + struct osmo_stream_srv_link *srv_link; + srv_link = osmo_stream_srv_link_create(g_daemon); + if (!srv_link) + return NULL; + + /* UECUPS socket for control from control plane side */ + osmo_stream_srv_link_set_nodelay(srv_link, true); + osmo_stream_srv_link_set_addr(srv_link, g_daemon->cfg.cups_local_ip); + osmo_stream_srv_link_set_port(srv_link, g_daemon->cfg.cups_local_port); + osmo_stream_srv_link_set_proto(srv_link, IPPROTO_SCTP); + osmo_stream_srv_link_set_data(srv_link, g_daemon); + osmo_stream_srv_link_set_accept_cb(srv_link, cups_accept_cb); + osmo_stream_srv_link_open(srv_link); + return srv_link; +}
View file
osmo-uecups_0.1.4.tar.xz/daemon/daemon_vty.c -> osmo-uecups_0.2.0.tar.xz/daemon/daemon_vty.c
Changed
@@ -99,7 +99,7 @@ vty_out(vty, "Cannot destrory non-existant TUN%s", VTY_NEWLINE); return CMD_WARNING; } - _tun_device_deref_destroy(tun); + _tun_device_deref_release(tun); pthread_rwlock_unlock(&g_daemon->rwlock); return CMD_SUCCESS;
View file
osmo-uecups_0.2.0.tar.xz/daemon/gtp_daemon.c
Added
@@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <unistd.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> + +#include <pthread.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +#include "internal.h" + + +/*********************************************************************** + * GTP Daemon + ***********************************************************************/ + +#ifndef OSMO_VTY_PORT_UECUPS +#define OSMO_VTY_PORT_UECUPS 4268 +#endif + +struct gtp_daemon *g_daemon; + +static void gtp_daemon_itq_read_cb(struct osmo_it_q *q, struct llist_head *item) +{ + struct gtp_daemon *d = (struct gtp_daemon *)q->data; + struct gtp_daemon_itq_msg *itq_msg = container_of(item, struct gtp_daemon_itq_msg, list); + + LOGP(DTUN, LOGL_DEBUG, "Rx new itq message from %s\n", + itq_msg->tun_released.tun->devname); + + _tun_device_destroy(itq_msg->tun_released.tun); + if (d->reset_all_state_tun_remaining > 0) { + d->reset_all_state_tun_remaining--; + if (d->reset_all_state_tun_remaining == 0) { + struct cups_client *cc; + llist_for_each_entry(cc, &d->cups_clients, list) { + json_t *jres; + if (!cc->reset_all_state_res_pending) + continue; + cc->reset_all_state_res_pending = false; + jres = gen_uecups_result("reset_all_state_res", "OK"); + cups_client_tx_json(cc, jres); + } + } + } +} + +struct gtp_daemon *gtp_daemon_alloc(void *ctx) +{ + struct gtp_daemon *d = talloc_zero(ctx, struct gtp_daemon); + if (!d) + return NULL; + + INIT_LLIST_HEAD(&d->gtp_endpoints); + INIT_LLIST_HEAD(&d->tun_devices); + INIT_LLIST_HEAD(&d->gtp_tunnels); + INIT_LLIST_HEAD(&d->subprocesses); + pthread_rwlock_init(&d->rwlock, NULL); + d->main_thread = pthread_self(); + + d->itq = osmo_it_q_alloc(d, "itq", 4096, gtp_daemon_itq_read_cb, d); + osmo_fd_register(&d->itq->event_ofd); + + INIT_LLIST_HEAD(&d->cups_clients); + + d->cfg.cups_local_ip = talloc_strdup(d, "localhost"); + d->cfg.cups_local_port = UECUPS_SCTP_PORT; + + return d; +}
View file
osmo-uecups_0.1.4.tar.xz/daemon/internal.h -> osmo-uecups_0.2.0.tar.xz/daemon/internal.h
Changed
@@ -1,13 +1,22 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once +#include "netns.h" + #include <stdint.h> #include <stdbool.h> #include <pthread.h> #include <sys/socket.h> + +#include <jansson.h> + #include <osmocom/core/linuxlist.h> #include <osmocom/core/write_queue.h> +#include <osmocom/core/it_q.h> #include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> + +#include <osmocom/netif/stream.h> struct nl_sock; struct osmo_stream_srv_link; @@ -84,6 +93,13 @@ /*********************************************************************** * TUN Device ***********************************************************************/ +/* Message sent tun thread -> main thread through osmo_itq */ +struct gtp_daemon_itq_msg { + struct llist_head list; + struct { + struct tun_device *tun; + } tun_released; /* tun became stopped and can be freed */ +}; struct tun_device { /* entry in global list */ @@ -110,6 +126,9 @@ /* the thread handling Rx from the tun fd */ pthread_t thread; + + /* Used to store messages to be sent to main thread, since tun thread doesn't allocate through talloc */ + struct gtp_daemon_itq_msg itq_msg; }; struct tun_device * @@ -121,13 +140,33 @@ struct tun_device * _tun_device_find(struct gtp_daemon *d, const char *devname); -void _tun_device_deref_destroy(struct tun_device *tun); +void _tun_device_destroy(struct tun_device *tun); bool _tun_device_release(struct tun_device *tun); +void _tun_device_deref_release(struct tun_device *tun); bool tun_device_release(struct tun_device *tun); +/*********************************************************************** + * Client (Control/User Plane Separation) Socket + ***********************************************************************/ + +struct cups_client { + /* member in daemon->cups_clients */ + struct llist_head list; + /* back-pointer to daemon */ + struct gtp_daemon *d; + /* client socket */ + struct osmo_stream_srv *srv; + char socknameOSMO_SOCK_NAME_MAXLEN; + bool reset_all_state_res_pending; +}; + +struct osmo_stream_srv_link *cups_srv_link_create(struct gtp_daemon *d); +void child_terminated(struct gtp_daemon *d, int pid, int status); +json_t *gen_uecups_result(const char *name, const char *res); +int cups_client_tx_json(struct cups_client *cc, json_t *jtx); /*********************************************************************** * GTP Tunnel @@ -222,6 +261,12 @@ struct osmo_stream_srv_link *cups_link; struct osmo_signalfd *signalfd; + /* inter-thread queue between main thread and workers, pass struct gtp_daemon_itq_msg: */ + struct osmo_it_q *itq; + + /* Number of tunnels in progrress of being released: */ + unsigned int reset_all_state_tun_remaining; + struct { char *cups_local_ip; uint16_t cups_local_port; @@ -229,4 +274,6 @@ }; extern struct gtp_daemon *g_daemon; +struct gtp_daemon *gtp_daemon_alloc(void *ctx); + int gtpud_vty_init(void);
View file
osmo-uecups_0.1.4.tar.xz/daemon/main.c -> osmo-uecups_0.2.0.tar.xz/daemon/main.c
Changed
@@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#define _GNU_SOURCE +#include <getopt.h> #include <unistd.h> #include <stdint.h> #include <stdbool.h> @@ -22,7 +24,6 @@ #include <osmocom/core/stats.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/socket.h> -#include <osmocom/core/exec.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> #include <osmocom/vty/stats.h> @@ -31,7 +32,6 @@ #include <osmocom/vty/misc.h> #include <osmocom/netif/stream.h> -#include <netinet/sctp.h> #include <jansson.h> @@ -39,327 +39,13 @@ #include "netns.h" #include "gtp.h" -/*********************************************************************** - * Client (Contol/User Plane Separation) Socket - ***********************************************************************/ +static void *g_tall_ctx; +static char *g_config_file = "osmo-uecups-daemon.cfg"; +static int g_daemonize; +extern struct vty_app_info g_vty_info; #include <pwd.h> -#define CUPS_MSGB_SIZE 1024 - -#define LOGCC(cc, lvl, fmt, args ...) \ - LOGP(DUECUPS, lvl, "%s: " fmt, (cc)->sockname, ## args) - -struct cups_client { - /* member in daemon->cups_clients */ - struct llist_head list; - /* back-pointer to daemon */ - struct gtp_daemon *d; - /* client socket */ - struct osmo_stream_srv *srv; - char socknameOSMO_SOCK_NAME_MAXLEN; -}; - -struct subprocess { - /* member in daemon->cups_clients */ - struct llist_head list; - /* pointer to the client that started us */ - struct cups_client *cups_client; - /* PID of the process */ - pid_t pid; -}; - -/* kill the specified subprocess and forget about it */ -static void subprocess_destroy(struct subprocess *p, int signal) -{ - kill(p->pid, signal); - llist_del(&p->list); - talloc_free(p); -} - -/* Send JSON to a given client/connection */ -static int cups_client_tx_json(struct cups_client *cc, json_t *jtx) -{ - struct msgb *msg = msgb_alloc(CUPS_MSGB_SIZE, "Tx JSON"); - char *json_str = json_dumps(jtx, JSON_SORT_KEYS); - char *out; - int json_strlen; - - json_decref(jtx); - if (!json_str) { - LOGCC(cc, LOGL_ERROR, "Error encoding JSON\n"); - return 0; - } - json_strlen = strlen(json_str); - - LOGCC(cc, LOGL_DEBUG, "JSON Tx '%s'\n", json_str); - - if (json_strlen > msgb_tailroom(msg)) { - LOGCC(cc, LOGL_ERROR, "Not enough room for JSON in msgb\n"); - free(json_str); - return 0; - } - - out = (char *)msgb_put(msg, json_strlen); - memcpy(out, json_str, json_strlen); - free(json_str); - osmo_stream_srv_send(cc->srv, msg); - - return 0; -} - -static json_t *gen_uecups_result(const char *name, const char *res) -{ - json_t *jres = json_object(); - json_t *jret = json_object(); - - json_object_set_new(jres, "result", json_string(res)); - json_object_set_new(jret, name, jres); - - return jret; -} - -static int parse_ep(struct sockaddr_storage *out, json_t *in) -{ - json_t *jaddr_type, *jport, *jip; - const char *addr_type, *ip; - uint8_t buf16; - - /* {"addr_type":"IPV4","ip":"31323334","Port":2152} */ - - if (!json_is_object(in)) - return -EINVAL; - - jaddr_type = json_object_get(in, "addr_type"); - jport = json_object_get(in, "Port"); - jip = json_object_get(in, "ip"); - - if (!jaddr_type || !jport || !jip) - return -EINVAL; - - if (!json_is_string(jaddr_type) || !json_is_integer(jport) || !json_is_string(jip)) - return -EINVAL; - - addr_type = json_string_value(jaddr_type); - ip = json_string_value(jip); - - memset(out, 0, sizeof(*out)); - - if (!strcmp(addr_type, "IPV4")) { - struct sockaddr_in *sin = (struct sockaddr_in *) out; - if (osmo_hexparse(ip, buf, sizeof(buf)) != 4) - return -EINVAL; - memcpy(&sin->sin_addr, buf, 4); - sin->sin_family = AF_INET; - sin->sin_port = htons(json_integer_value(jport)); - } else if (!strcmp(addr_type, "IPV6")) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) out; - if (osmo_hexparse(ip, buf, sizeof(buf)) != 16) - return -EINVAL; - memcpy(&sin6->sin6_addr, buf, 16); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(json_integer_value(jport)); - } else - return -EINVAL; - - return 0; -} - -static int parse_eua(struct sockaddr_storage *out, json_t *jip, json_t *jaddr_type) -{ - const char *addr_type, *ip; - uint8_t buf16; - - if (!json_is_string(jip) || !json_is_string(jaddr_type)) - return -EINVAL; - - addr_type = json_string_value(jaddr_type); - ip = json_string_value(jip); - - memset(out, 0, sizeof(*out)); - - if (!strcmp(addr_type, "IPV4")) { - struct sockaddr_in *sin = (struct sockaddr_in *) out; - if (osmo_hexparse(ip, buf, sizeof(buf)) != 4) - return -EINVAL; - memcpy(&sin->sin_addr, buf, 4); - sin->sin_family = AF_INET; - } else if (!strcmp(addr_type, "IPV6")) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) out; - if (osmo_hexparse(ip, buf, sizeof(buf)) != 16) - return -EINVAL; - memcpy(&sin6->sin6_addr, buf, 16); - sin6->sin6_family = AF_INET6; - } else - return -EINVAL; - - return 0; -} - - -static int parse_create_tun(struct gtp_tunnel_params *out, json_t *ctun) -{ - json_t *jlocal_gtp_ep, *jremote_gtp_ep; - json_t *jrx_teid, *jtx_teid; - json_t *jtun_dev_name, *jtun_netns_name; - json_t *juser_addr, *juser_addr_type; - int rc; - - /* '{"create_tun":{"tx_teid":1234,"rx_teid":5678,"user_addr_type":"IPV4","user_addr":"21222324","local_gtp_ep":{"addr_type":"IPV4","ip":"31323334","Port":2152},"remote_gtp_ep":{"addr_type":"IPV4","ip":"41424344","Port":2152},"tun_dev_name":"tun23","tun_netns_name":"foo"}}' */ - - if (!json_is_object(ctun)) - return -EINVAL; - - /* mandatory IEs */ - jlocal_gtp_ep = json_object_get(ctun, "local_gtp_ep"); - jremote_gtp_ep = json_object_get(ctun, "remote_gtp_ep"); - jrx_teid = json_object_get(ctun, "rx_teid"); - jtx_teid = json_object_get(ctun, "tx_teid"); - jtun_dev_name = json_object_get(ctun, "tun_dev_name"); - juser_addr = json_object_get(ctun, "user_addr"); - juser_addr_type = json_object_get(ctun, "user_addr_type"); - - if (!jlocal_gtp_ep || !jremote_gtp_ep || !jrx_teid || !jtx_teid || !jtun_dev_name || - !juser_addr || !juser_addr_type) - return -EINVAL; - if (!json_is_object(jlocal_gtp_ep) || !json_is_object(jremote_gtp_ep) || - !json_is_integer(jrx_teid) || !json_is_integer(jtx_teid) || - !json_is_string(jtun_dev_name) || - !json_is_string(juser_addr) || !json_is_string(juser_addr_type)) - return -EINVAL; - - memset(out, 0, sizeof(*out)); - - rc = parse_ep(&out->local_udp, jlocal_gtp_ep); - if (rc < 0) - return rc; - rc = parse_ep(&out->remote_udp, jremote_gtp_ep); - if (rc < 0) - return rc; - rc = parse_eua(&out->user_addr, juser_addr, juser_addr_type); - if (rc < 0) - return rc; - out->rx_teid = json_integer_value(jrx_teid); - out->tx_teid = json_integer_value(jtx_teid); - out->tun_name = talloc_strdup(out, json_string_value(jtun_dev_name)); - - /* optional IEs */ - jtun_netns_name = json_object_get(ctun, "tun_netns_name"); - if (jtun_netns_name) { - if (!json_is_string(jtun_netns_name)) - return -EINVAL; - out->tun_netns_name = talloc_strdup(out, json_string_value(jtun_netns_name)); - } - - return 0; -} - - -static int cups_client_handle_create_tun(struct cups_client *cc, json_t *ctun) -{ - int rc; - struct gtp_tunnel_params *tpars = talloc_zero(cc, struct gtp_tunnel_params); - struct gtp_tunnel *t; - - rc = parse_create_tun(tpars, ctun); - if (rc < 0) { - talloc_free(tpars); - return rc; - } - - t = gtp_tunnel_alloc(g_daemon, tpars); - if (!t) { - LOGCC(cc, LOGL_NOTICE, "Failed to allocate tunnel\n"); - cups_client_tx_json(cc, gen_uecups_result("create_tun_res", "ERR_NOT_FOUND")); - } else { - cups_client_tx_json(cc, gen_uecups_result("create_tun_res", "OK")); - } - - talloc_free(tpars); - return 0; -} - -static int cups_client_handle_destroy_tun(struct cups_client *cc, json_t *dtun) -{ - struct sockaddr_storage local_ep_addr; - json_t *jlocal_gtp_ep, *jrx_teid; - uint32_t rx_teid; - int rc; - - jlocal_gtp_ep = json_object_get(dtun, "local_gtp_ep"); - jrx_teid = json_object_get(dtun, "rx_teid"); - - if (!jlocal_gtp_ep || !jrx_teid) - return -EINVAL; - - if (!json_is_object(jlocal_gtp_ep) || !json_is_integer(jrx_teid)) - return -EINVAL; - - rc = parse_ep(&local_ep_addr, jlocal_gtp_ep); - if (rc < 0) - return rc; - rx_teid = json_integer_value(jrx_teid); - - rc = gtp_tunnel_destroy(g_daemon, &local_ep_addr, rx_teid); - if (rc < 0) { - LOGCC(cc, LOGL_NOTICE, "Failed to destroy tunnel\n"); - cups_client_tx_json(cc, gen_uecups_result("destroy_tun_res", "ERR_NOT_FOUND")); - } else { - cups_client_tx_json(cc, gen_uecups_result("destroy_tun_res", "OK")); - } - - return 0; -} - -static json_t *gen_uecups_term_ind(pid_t pid, int status) -{ - json_t *jterm = json_object(); - json_t *jret = json_object(); - - json_object_set_new(jterm, "pid", json_integer(pid)); - json_object_set_new(jterm, "exit_code", json_integer(status)); - - json_object_set_new(jret, "program_term_ind", jterm); - - return jret; -} - - -static struct subprocess *subprocess_by_pid(struct gtp_daemon *d, pid_t pid) -{ - struct subprocess *sproc; - llist_for_each_entry(sproc, &d->subprocesses, list) { - if (sproc->pid == pid) - return sproc; - } - return NULL; -} - -static void child_terminated(struct gtp_daemon *d, int pid, int status) -{ - struct subprocess *sproc; - json_t *jterm_ind; - - LOGP(DUECUPS, LOGL_DEBUG, "SIGCHLD receive from pid %u; status=%d\n", pid, status); - - sproc = subprocess_by_pid(d, pid); - if (!sproc) { - LOGP(DUECUPS, LOGL_NOTICE, "subprocess %u terminated (status=%d) but we don't know it?\n", - pid, status); - return; - } - - /* generate prog_term_ind towards control plane */ - jterm_ind = gen_uecups_term_ind(pid, status); - if (!jterm_ind) - return; - - cups_client_tx_json(sproc->cups_client, jterm_ind); - - llist_del(&sproc->list); - talloc_free(sproc); -} - static void sigchild_cb(struct osmo_signalfd *osfd, const struct signalfd_siginfo *fdsi) { struct gtp_daemon *d = osfd->data; @@ -367,10 +53,8 @@ OSMO_ASSERT(fdsi->ssi_signo == SIGCHLD); - child_terminated(d, fdsi->ssi_pid, fdsi->ssi_status); - /* it is known that classic signals coalesce: If you get multiple signals of the - * same type before a process is scheduled, the subsequent signaals are dropped. This + * same type before a process is scheduled, the subsequent signals are dropped. This * makes sense for SIGINT or something like this, but for SIGCHLD carrying the PID of * the terminated process, it doesn't really. Linux had the chance to fix this when * introducing signalfd() - but the developers decided not to fix it. So the signalfd_siginfo @@ -382,277 +66,6 @@ } -static json_t *gen_uecups_start_res(pid_t pid, const char *result) -{ - json_t *ret = gen_uecups_result("start_program_res", result); - json_object_set_new(json_object_get(ret, "start_program_res"), "pid", json_integer(pid)); - - return ret; -} - -static int cups_client_handle_start_program(struct cups_client *cc, json_t *sprog) -{ - json_t *juser, *jcmd, *jenv, *jnetns, *jres; - struct gtp_daemon *d = cc->d; - const char *cmd, *user; - char **addl_env = NULL; - sigset_t oldmask; - int nsfd = -1, rc; - - juser = json_object_get(sprog, "run_as_user"); - jcmd = json_object_get(sprog, "command"); - jenv = json_object_get(sprog, "environment"); - jnetns = json_object_get(sprog, "tun_netns_name"); - - /* mandatory parts */ - if (!juser || !jcmd) - return -EINVAL; - if (!json_is_string(juser) || !json_is_string(jcmd)) - return -EINVAL; - - /* optional parts */ - if (jenv && !json_is_array(jenv)) - return -EINVAL; - if (jnetns && !json_is_string(jnetns)) - return -EINVAL; - - cmd = json_string_value(jcmd); - user = json_string_value(juser); - if (jnetns) { - struct tun_device *tun = tun_device_find_netns(d, json_string_value(jnetns)); - if (!tun) - return -ENODEV; - nsfd = tun->netns_fd; - } - - /* build environment */ - if (jenv) { - json_t *j; - int i; - addl_env = talloc_zero_array(cc, char *, json_array_size(jenv)+1); - if (!addl_env) - return -ENOMEM; - json_array_foreach(jenv, i, j) { - addl_envi = talloc_strdup(addl_env, json_string_value(j)); - } - } - - if (jnetns) { - rc = switch_ns(nsfd, &oldmask); - if (rc < 0) { - talloc_free(addl_env); - return -EIO; - } - } - - rc = osmo_system_nowait2(cmd, osmo_environment_whitelist, addl_env, user); - - if (jnetns) { - OSMO_ASSERT(restore_ns(&oldmask) == 0); - } - - talloc_free(addl_env); - - if (rc > 0) { - /* create a record about the subprocess we started, so we can notify the - * client that crated it upon termination */ - struct subprocess *sproc = talloc_zero(cc, struct subprocess); - if (!sproc) - return -ENOMEM; - - sproc->cups_client = cc; - sproc->pid = rc; - llist_add_tail(&sproc->list, &d->subprocesses); - jres = gen_uecups_start_res(sproc->pid, "OK"); - } else { - jres = gen_uecups_start_res(0, "ERR_INVALID_DATA"); - } - - cups_client_tx_json(cc, jres); - - return 0; -} - -static int cups_client_handle_reset_all_state(struct cups_client *cc, json_t *sprog) -{ - struct gtp_daemon *d = cc->d; - struct gtp_tunnel *t, *t2; - struct subprocess *p, *p2; - json_t *jres; - - pthread_rwlock_wrlock(&d->rwlock); - llist_for_each_entry_safe(t, t2, &d->gtp_tunnels, list) { - _gtp_tunnel_destroy(t); - } - pthread_rwlock_unlock(&d->rwlock); - - /* no locking needed as this list is only used by main thread */ - llist_for_each_entry_safe(p, p2, &d->subprocesses, list) { - subprocess_destroy(p, SIGKILL); - } - - jres = gen_uecups_result("reset_all_state_res", "OK"); - cups_client_tx_json(cc, jres); - - return 0; -} - -static int cups_client_handle_json(struct cups_client *cc, json_t *jroot) -{ - void *iter; - const char *key; - json_t *cmd; - int rc; - - if (!json_is_object(jroot)) - return -EINVAL; - - iter = json_object_iter(jroot); - key = json_object_iter_key(iter); - cmd = json_object_iter_value(iter); - if (!iter || !key || !cmd) - return -EINVAL; - - if (!strcmp(key, "create_tun")) { - rc = cups_client_handle_create_tun(cc, cmd); - } else if (!strcmp(key, "destroy_tun")) { - rc = cups_client_handle_destroy_tun(cc, cmd); - } else if (!strcmp(key, "start_program")) { - rc = cups_client_handle_start_program(cc, cmd); - } else if (!strcmp(key, "reset_all_state")) { - rc = cups_client_handle_reset_all_state(cc, cmd); - } else { - LOGCC(cc, LOGL_NOTICE, "Unknown command '%s' received\n", key); - return -EINVAL; - } - - if (rc < 0) { - LOGCC(cc, LOGL_NOTICE, "Error %d handling '%s' command\n", rc, key); - char buf64; - snprintf(buf, sizeof(buf), "%s_res", key); - cups_client_tx_json(cc, gen_uecups_result(buf, "ERR_INVALID_DATA")); - return -EINVAL; - } - - return 0; -} - -/* control/user plane separation per-client read cb */ -static int cups_client_read_cb(struct osmo_stream_srv *conn) -{ - struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn); - struct cups_client *cc = osmo_stream_srv_get_data(conn); - struct msgb *msg = msgb_alloc(CUPS_MSGB_SIZE, "Rx JSON"); - struct sctp_sndrcvinfo sinfo; - json_error_t jerr; - json_t *jroot; - int flags = 0; - int rc = 0; - - /* Read message from socket */ - /* 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 and osmo-msc */ - rc = sctp_recvmsg(ofd->fd, msg->tail, msgb_tailroom(msg), NULL, NULL, &sinfo,&flags); - if (rc <= 0) { - osmo_stream_srv_destroy(conn); - rc = -1; - 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; - goto out; - default: - break; - } - goto out; - } - - LOGCC(cc, LOGL_DEBUG, "Rx '%s'\n", msgb_data(msg)); - - /* Parse the JSON */ - jroot = json_loadb((const char *) msgb_data(msg), msgb_length(msg), 0, &jerr); - if (!jroot) { - LOGCC(cc, LOGL_ERROR, "Error decoding JSON (%s)", jerr.text); - rc = -1; - goto out; - } - - /* Dispatch */ - rc = cups_client_handle_json(cc, jroot); - - json_decref(jroot); - msgb_free(msg); - - return 0; -out: - msgb_free(msg); - return rc; -} - -static int cups_client_closed_cb(struct osmo_stream_srv *conn) -{ - struct cups_client *cc = osmo_stream_srv_get_data(conn); - struct gtp_daemon *d = cc->d; - struct subprocess *p, *p2; - - /* kill + forget about all subprocesses of this client */ - /* We need no locking here as the subprocess list is only used from the main thread */ - llist_for_each_entry_safe(p, p2, &d->subprocesses, list) { - if (p->cups_client == cc) - subprocess_destroy(p, SIGKILL); - } - - LOGCC(cc, LOGL_INFO, "UECUPS connection lost\n"); - llist_del(&cc->list); - return 0; -} - - -/* the control/user plane separation server bind/accept fd */ -static int cups_accept_cb(struct osmo_stream_srv_link *link, int fd) -{ - struct gtp_daemon *d = osmo_stream_srv_link_get_data(link); - struct cups_client *cc; - - cc = talloc_zero(d, struct cups_client); - if (!cc) - return -1; - - cc->d = d; - osmo_sock_get_name_buf(cc->sockname, sizeof(cc->sockname), fd); - cc->srv = osmo_stream_srv_create(cc, link, fd, cups_client_read_cb, cups_client_closed_cb, cc); - if (!cc->srv) { - talloc_free(cc); - return -1; - } - LOGCC(cc, LOGL_INFO, "Accepted new UECUPS connection\n"); - - llist_add_tail(&cc->list, &d->cups_clients); - - return 0; -} - -/*********************************************************************** - * GTP Daemon - ***********************************************************************/ - -#ifndef OSMO_VTY_PORT_UECUPS -#define OSMO_VTY_PORT_UECUPS 4268 -#endif - -static void *g_tall_ctx; -struct gtp_daemon *g_daemon; -static int g_daemonize; -static char *g_config_file = "osmo-uecups-daemon.cfg"; -extern struct vty_app_info g_vty_info; - static void signal_cb(struct osmo_signalfd *osfd, const struct signalfd_siginfo *fdsi) { switch (fdsi->ssi_signo) { @@ -667,27 +80,6 @@ } } -static struct gtp_daemon *gtp_daemon_alloc(void *ctx) -{ - struct gtp_daemon *d = talloc_zero(ctx, struct gtp_daemon); - if (!d) - return NULL; - - INIT_LLIST_HEAD(&d->gtp_endpoints); - INIT_LLIST_HEAD(&d->tun_devices); - INIT_LLIST_HEAD(&d->gtp_tunnels); - INIT_LLIST_HEAD(&d->subprocesses); - pthread_rwlock_init(&d->rwlock, NULL); - d->main_thread = pthread_self(); - - INIT_LLIST_HEAD(&d->cups_clients); - - d->cfg.cups_local_ip = talloc_strdup(d, "localhost"); - d->cfg.cups_local_port = UECUPS_SCTP_PORT; - - return d; -} - static const struct log_info_cat log_categories = { DTUN = { .name ="DTUN", @@ -717,6 +109,98 @@ .num_cat = ARRAY_SIZE(log_categories), }; +static void print_help() +{ + printf("Some useful options:\n"); + printf(" -h --help is printing this text.\n"); + printf(" -c --config-file filename The config file to use.\n"); + printf(" -s --disable-color\n"); + printf(" -D --daemonize Fork the process into a background daemon\n"); + printf(" -V --version Print the version number\n"); + + printf("\nVTY reference generation:\n"); + printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"); + printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n"); +} + +static void handle_long_options(const char *prog_name, const int long_option) +{ + static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT; + + switch (long_option) { + case 1: + vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg); + if (vty_ref_mode < 0) { + fprintf(stderr, "%s: Unknown VTY reference generation " + "mode '%s'\n", prog_name, optarg); + exit(2); + } + break; + case 2: + fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n", + get_value_string(vty_ref_gen_mode_names, vty_ref_mode), + get_value_string(vty_ref_gen_mode_desc, vty_ref_mode)); + vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode); + exit(0); + default: + fprintf(stderr, "%s: error parsing cmdline options\n", prog_name); + exit(2); + } +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static int long_option = 0; + static struct option long_options = { + {"help", 0, 0, 'h'}, + {"config-file", 1, 0, 'c'}, + {"daemonize", 0, 0, 'D'}, + {"version", 0, 0, 'V'}, + {"disable-color", 0, 0, 's'}, + {"vty-ref-mode", 1, &long_option, 1}, + {"vty-ref-xml", 0, &long_option, 2}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + break; + case 0: + handle_long_options(argv0, long_option); + break; + case 'c': + g_config_file = talloc_strdup(g_tall_ctx, optarg); + break; + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'V': + print_version(1); + exit(0); + break; + case 'D': + g_daemonize = 1; + break; + default: + /* ignore */ + break; + }; + } + if (argc > optind) { + fprintf(stderr, "Unsupported positional arguments on command line\n"); + exit(2); + } +} + int main(int argc, char **argv) { int rc; @@ -726,6 +210,7 @@ osmo_init_ignore_signals(); osmo_init_logging2(g_tall_ctx, &log_info); + log_enable_multithread(); g_daemon = gtp_daemon_alloc(g_tall_ctx); OSMO_ASSERT(g_daemon); @@ -739,6 +224,8 @@ rate_ctr_init(g_tall_ctx); gtpud_vty_init(); + handle_options(argc, argv); + init_netns(); rc = vty_read_config_file(g_config_file, NULL); @@ -751,22 +238,14 @@ if (rc < 0) exit(1); - g_daemon->cups_link = osmo_stream_srv_link_create(g_daemon); + /* UECUPS socket for control from control plane side */ + g_daemon->cups_link = cups_srv_link_create(g_daemon); if (!g_daemon->cups_link) { fprintf(stderr, "Failed to create CUPS socket %s:%u (%s)\n", g_daemon->cfg.cups_local_ip, g_daemon->cfg.cups_local_port, strerror(errno)); exit(1); } - /* UECUPS socket for control from control plane side */ - osmo_stream_srv_link_set_nodelay(g_daemon->cups_link, true); - osmo_stream_srv_link_set_addr(g_daemon->cups_link, g_daemon->cfg.cups_local_ip); - osmo_stream_srv_link_set_port(g_daemon->cups_link, g_daemon->cfg.cups_local_port); - osmo_stream_srv_link_set_proto(g_daemon->cups_link, IPPROTO_SCTP); - osmo_stream_srv_link_set_data(g_daemon->cups_link, g_daemon); - osmo_stream_srv_link_set_accept_cb(g_daemon->cups_link, cups_accept_cb); - osmo_stream_srv_link_open(g_daemon->cups_link); - /* block SIGCHLD via normal delivery; redirect it to signalfd */ sigset_t sigset; sigemptyset(&sigset);
View file
osmo-uecups_0.1.4.tar.xz/daemon/tun_device.c -> osmo-uecups_0.2.0.tar.xz/daemon/tun_device.c
Changed
@@ -120,6 +120,14 @@ return 0; } +static void tun_device_pthread_cleanup_routine(void *data) +{ + struct tun_device *tun = data; + LOGTUN(tun, LOGL_DEBUG, "pthread_cleanup\n"); + int rc = osmo_it_q_enqueue(tun->d->itq, &tun->itq_msg, list); + OSMO_ASSERT(rc == 0); +} + /* one thread for reading from each TUN device (TUN -> GTP encapsulation) */ static void *tun_device_thread(void *arg) { @@ -131,11 +139,18 @@ uint8_t *buffer = base_buffer + sizeof(struct gtp1_header); struct sockaddr_storage daddr; + int old_cancelst_unused; /* initialize the fixed part of the GTP header */ gtph->flags = 0x30; gtph->type = GTP_TPDU; + pthread_cleanup_push(tun_device_pthread_cleanup_routine, tun); + /* IMPORTANT!: All logging functions in this function block must be called with + * PTHREAD_CANCEL_DISABLE set, otherwise the thread could be cancelled while + * holding the logging mutex, hence causing deadlock with main (or other) + * thread. */ + while (1) { struct gtp_tunnel *t; struct pkt_info pinfo; @@ -144,6 +159,7 @@ /* 1) read from tun */ rc = read(tun->fd, buffer, MAX_UDP_PACKET); if (rc < 0) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused); LOGTUN(tun, LOGL_FATAL, "Error readingfrom tun device: %s\n", strerror(errno)); exit(1); } @@ -152,8 +168,10 @@ rc = parse_pkt(&pinfo, buffer, nread); if (rc < 0) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused); LOGTUN(tun, LOGL_NOTICE, "Error parsing IP packet: %s\n", osmo_hexdump(buffer, nread)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelst_unused); continue; } @@ -171,7 +189,9 @@ getnameinfo((const struct sockaddr *)&pinfo.saddr, sizeof(pinfo.saddr), host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused); LOGTUN(tun, LOGL_NOTICE, "No tunnel found for source address %s:%s\n", host, port); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelst_unused); continue; } outfd = t->gtp_ep->fd; @@ -183,10 +203,12 @@ rc = sendto(outfd, base_buffer, nread+sizeof(*gtph), 0, (struct sockaddr *)&daddr, sizeof(daddr)); if (rc < 0) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused); LOGTUN(tun, LOGL_FATAL, "Error Writing to UDP socket: %s\n", strerror(errno)); exit(1); } } + pthread_cleanup_pop(1); } static int tun_open(int flags, const char *name) @@ -290,13 +312,15 @@ if (tun->netns_name) { rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET); if (rc < 0) - LOGTUN(tun, LOGL_ERROR, "Cannot add IPv4 default route\n"); + LOGTUN(tun, LOGL_ERROR, "Cannot add IPv4 default route " + "(rc=%d): %s\n", rc, nl_geterror(rc)); else LOGTUN(tun, LOGL_INFO, "Added IPv4 default route\n"); rc = netdev_add_defaultroute(tun->nl, tun->ifindex, AF_INET6); if (rc < 0) - LOGTUN(tun, LOGL_ERROR, "Cannot add IPv6 default route\n"); + LOGTUN(tun, LOGL_ERROR, "Cannot add IPv6 default route " + "(rc=%d): %s\n", rc, nl_geterror(rc)); else LOGTUN(tun, LOGL_INFO, "Added IPv6 default route\n"); } @@ -374,24 +398,24 @@ return tun; } -/* UNLOCKED hard/forced destroy; caller must make sure references are cleaned up */ -static void _tun_device_destroy(struct tun_device *tun) +/* UNLOCKED hard/forced destroy; caller must make sure references are cleaned + * up, and tun thread is stopped beforehand by calling + * _tun_device_{deref_}release */ +void _tun_device_destroy(struct tun_device *tun) { /* talloc is not thread safe, all alloc/free must come from main thread */ ASSERT_MAIN_THREAD(tun->d); + LOGTUN(tun, LOGL_INFO, "Destroying\n"); - pthread_cancel(tun->thread); - llist_del(&tun->list); if (tun->netns_name) close(tun->netns_fd); close(tun->fd); nl_socket_free(tun->nl); - LOGTUN(tun, LOGL_INFO, "Destroying\n"); talloc_free(tun); } -/* UNLOCKED remove all objects referencing this tun and then destroy */ -void _tun_device_deref_destroy(struct tun_device *tun) +/* UNLOCKED remove all objects referencing this tun and then start async tun release procedure */ +void _tun_device_deref_release(struct tun_device *tun) { struct gtp_daemon *d = tun->d; char *devname = talloc_strdup(d, tun->devname); @@ -410,12 +434,12 @@ * check if the tun can still be found in the list */ tun2 = _tun_device_find(d, devname); if (tun2 && tun2 == tun) - _tun_device_destroy(tun2); + _tun_device_release(tun2); talloc_free(devname); } -/* UNLOCKED release a reference; destroy if refcount drops to 0 */ +/* UNLOCKED release a reference; start async tun release procedure if refcount drops to 0 */ bool _tun_device_release(struct tun_device *tun) { bool released = false; @@ -425,10 +449,17 @@ tun->use_count--; if (tun->use_count == 0) { - _tun_device_destroy(tun); + LOGTUN(tun, LOGL_INFO, "Releasing\n"); + llist_del(&tun->list); + tun->itq_msg.tun_released.tun = tun; + tun->d->reset_all_state_tun_remaining++; + /* We cancel the thread: the pthread_cleanup routing will send a message + * back to us (main thread) when finally cancelled. */ + pthread_cancel(tun->thread); released = true; - } else + } else { LOGTUN(tun, LOGL_DEBUG, "Release; new use_count=%lu\n", tun->use_count); + } return released; }
View file
osmo-uecups_0.1.4.tar.xz/debian/changelog -> osmo-uecups_0.2.0.tar.xz/debian/changelog
Changed
@@ -1,3 +1,26 @@ +osmo-uecups (0.2.0) unstable; urgency=medium + + Pau Espin Pedrol + * debian/control: Drop dependency on autoconf-archive + * main.c: Fix typo in comment + * main: Remove duplicate call to child_terminated() + * main.c: Improve logging + * Fix use-after-free by tun thread after tun obj destroyed + * Add optarg support + * Move several objects outside of main.c + * tun_device: Avoid deadlocks logging while thread is cancelled + * .gitignore: Add configure~ + + Vadim Yanitskiy + * daemon: enable multithread logging in main() + * daemon: log more details when netdev_add_defaultroute() fails + + Harald Welte + * update git URLs (git -> https; gitea) + * README.md: Fix copy+paste mistke + + -- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 12:24:31 +0200 + osmo-uecups (0.1.4) unstable; urgency=medium Harald Welte
View file
osmo-uecups_0.1.4.tar.xz/debian/control -> osmo-uecups_0.2.0.tar.xz/debian/control
Changed
@@ -6,19 +6,18 @@ dh-autoreconf, autotools-dev, autoconf, - autoconf-archive, automake, libtool, pkg-config, libjansson-dev, libnl-route-3-dev, - libosmocore-dev (>= 1.1.0), - libosmo-netif-dev, + libosmocore-dev (>= 1.7.0), + libosmo-netif-dev (>= 1.2.0), libsctp-dev, - osmo-gsm-manuals-dev + osmo-gsm-manuals-dev (>= 1.3.0) Standards-Version: 3.9.8 -Vcs-Git: git://git.osmocom.org/osmo-uecups.git -Vcs-Browser: https://git.osmocom.org/osmo-uecups +Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups +Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups Homepage: https://osmocom.org/projects/osmo-uecups Package: osmo-uecups
View file
osmo-uecups_0.1.4.tar.xz/debian/copyright -> osmo-uecups_0.2.0.tar.xz/debian/copyright
Changed
@@ -1,6 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: osmo-uecups -Source: git://git.osmocom.org/osmo-uecups +Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups Files: * Copyright: 2019-2020 Harald Welte <laforge@osmocom.org>
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.