Projects
osmocom:latest
osmo-uecups
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 5
View file
osmo-uecups.spec
Deleted
@@ -1,85 +0,0 @@ -# -# spec file for package osmo-uecups -# -# Copyright (c) 2020, Martin Hauke <mardnh@gmx.de> -# -# All modifications and additions to the file contributed by third parties -# remain the property of their copyright owners, unless otherwise agreed -# upon. The license for this file, and modifications and additions to the -# file, is the same license as for the pristine package itself (unless the -# license for the pristine package is not an Open Source License, in which -# case the license is the MIT License). An "Open Source License" is a -# license that conforms to the Open Source Definition (Version 1.9) -# published by the Open Source Initiative. - -Name: osmo-uecups -Requires: osmocom-latest -Version: 0.2.2 -Release: 0 -Summary: OsmoUECUPS: UE/MME/SGW side GTP user plane daemon with control plane separation -License: GPL-2.0-or-later -Group: Productivity/Telephony/Utilities -URL: https://osmocom.org/projects/osmo-uecups -Source: osmo-uecups_0.2.2.tar.xz -Source1: rpmlintrc -BuildRequires: autoconf -BuildRequires: autoconf-archive -BuildRequires: automake -BuildRequires: libtool -BuildRequires: pkgconfig -BuildRequires: pkgconfig(libosmocore) >= 1.9.0 -BuildRequires: pkgconfig(libosmovty) >= 1.9.0 -BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0 -BuildRequires: pkgconfig(jansson) -BuildRequires: pkgconfig(libnl-route-3.0) -BuildRequires: lksctp-tools-devel -#BuildRequires: systemd-rpm-macros - -%description -OsmoUECUPS: UE/MME/SGW side GTP user plane daemon with control plane -separation. - -%prep -%setup -n osmo-uecups -q - -%build -echo "%{version}" >.tarball-version -autoreconf -fiv -%configure \ - --docdir="%{_docdir}/%{name}" \ - --with-systemdsystemunitdir=%{_unitdir} -make %{?_smp_mflags} - -%install -%make_install - -%check -### FIXME - no checks atm -#make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) - -### FIXME - no service file atm -#%%preun -#%%service_del_preun %{name}.service -#% -#%%postun -#%%service_del_postun %{name}.service -#% -#%%pre -#%%service_add_pre %{name}.service -#% -#%%post -#%%service_add_post %{name}.service - -%files -### FIXME - no licence files included atm -#%%license XXX -%doc README.md -%dir %{_docdir}/%{name} -%dir %{_docdir}/%{name}/examples -%{_docdir}/%{name}/examples/osmo-uecups-daemon.cfg -%{_bindir}/osmo-uecups-daemon -%dir %{_sysconfdir}/osmocom -%config(noreplace) %{_sysconfdir}/osmocom/osmo-uecups-daemon.cfg -#%%{_unitdir}/osmo-uecups.service - -%changelog
View file
osmo-uecups_0.2.2.tar.xz/contrib/osmo-uecups.spec.in
Deleted
@@ -1,84 +0,0 @@ -# -# spec file for package osmo-uecups -# -# Copyright (c) 2020, Martin Hauke <mardnh@gmx.de> -# -# All modifications and additions to the file contributed by third parties -# remain the property of their copyright owners, unless otherwise agreed -# upon. The license for this file, and modifications and additions to the -# file, is the same license as for the pristine package itself (unless the -# license for the pristine package is not an Open Source License, in which -# case the license is the MIT License). An "Open Source License" is a -# license that conforms to the Open Source Definition (Version 1.9) -# published by the Open Source Initiative. - -Name: osmo-uecups -Requires: osmocom-latest -Version: @VERSION@ -Release: 0 -Summary: OsmoUECUPS: UE/MME/SGW side GTP user plane daemon with control plane separation -License: GPL-2.0-or-later -Group: Productivity/Telephony/Utilities -URL: https://osmocom.org/projects/osmo-uecups -Source: %{name}-%{version}.tar.xz -BuildRequires: autoconf -BuildRequires: autoconf-archive -BuildRequires: automake -BuildRequires: libtool -BuildRequires: pkgconfig -BuildRequires: pkgconfig(libosmocore) >= 1.9.0 -BuildRequires: pkgconfig(libosmovty) >= 1.9.0 -BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0 -BuildRequires: pkgconfig(jansson) -BuildRequires: pkgconfig(libnl-route-3.0) -BuildRequires: lksctp-tools-devel -#BuildRequires: systemd-rpm-macros - -%description -OsmoUECUPS: UE/MME/SGW side GTP user plane daemon with control plane -separation. - -%prep -%setup -q - -%build -echo "%{version}" >.tarball-version -autoreconf -fiv -%configure \ - --docdir="%{_docdir}/%{name}" \ - --with-systemdsystemunitdir=%{_unitdir} -make %{?_smp_mflags} - -%install -%make_install - -%check -### FIXME - no checks atm -#make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) - -### FIXME - no service file atm -#%%preun -#%%service_del_preun %{name}.service -#% -#%%postun -#%%service_del_postun %{name}.service -#% -#%%pre -#%%service_add_pre %{name}.service -#% -#%%post -#%%service_add_post %{name}.service - -%files -### FIXME - no licence files included atm -#%%license XXX -%doc README.md -%dir %{_docdir}/%{name} -%dir %{_docdir}/%{name}/examples -%{_docdir}/%{name}/examples/osmo-uecups-daemon.cfg -%{_bindir}/osmo-uecups-daemon -%dir %{_sysconfdir}/osmocom -%config(noreplace) %{_sysconfdir}/osmocom/osmo-uecups-daemon.cfg -#%%{_unitdir}/osmo-uecups.service - -%changelog
View file
osmo-uecups_0.2.2.dsc -> osmo-uecups_0.3.0.dsc
Changed
@@ -2,18 +2,18 @@ Source: osmo-uecups Binary: osmo-uecups Architecture: any -Version: 0.2.2 +Version: 0.3.0 Maintainer: Osmocom team <openbsc@lists.osmocom.org> Homepage: https://osmocom.org/projects/osmo-uecups Standards-Version: 3.9.8 Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups -Build-Depends: debhelper (>= 10), dh-autoreconf, autotools-dev, autoconf, automake, libtool, pkg-config, libjansson-dev, libnl-route-3-dev, libosmocore-dev (>= 1.9.0), libosmo-netif-dev (>= 1.4.0), libsctp-dev, osmo-gsm-manuals-dev (>= 1.5.0) +Build-Depends: debhelper (>= 10), dh-autoreconf, autotools-dev, autoconf, automake, libtool, pkg-config, libjansson-dev, libnl-route-3-dev, libosmocore-dev (>= 1.12.0), libosmo-netif-dev (>= 1.7.0), libsctp-dev, osmo-gsm-manuals-dev (>= 1.7.0) Package-List: osmo-uecups deb net extra arch=any Checksums-Sha1: - 1b56f3ef960fa56d34490d8dd0625ada847f0922 29228 osmo-uecups_0.2.2.tar.xz + 2644af69c49df2516f1be85ecabf96128fcde23a 31268 osmo-uecups_0.3.0.tar.xz Checksums-Sha256: - 20ae887d5c70a21d7daa80bf106dfc0396058124c39728cdbae3ddad95fd9adc 29228 osmo-uecups_0.2.2.tar.xz + 3724ac7e011473bbfe821226c1dfafc128a47052ce288e0e4a849fd5adb9e2ba 31268 osmo-uecups_0.3.0.tar.xz Files: - 683719f9716f4919fdfbbe5d4b78e895 29228 osmo-uecups_0.2.2.tar.xz + 56f1ee569cc6f6af820922728318d7f6 31268 osmo-uecups_0.3.0.tar.xz
View file
osmo-uecups_0.2.2.tar.xz/.tarball-version -> osmo-uecups_0.3.0.tar.xz/.tarball-version
Changed
@@ -1 +1 @@ -0.2.2 +0.3.0
View file
osmo-uecups_0.2.2.tar.xz/Makefile.am -> osmo-uecups_0.3.0.tar.xz/Makefile.am
Changed
@@ -10,7 +10,7 @@ $(NULL) BUILT_SOURCES = $(top_srcdir)/.version -EXTRA_DIST = git-version-gen .version contrib/osmo-uecups.spec.in +EXTRA_DIST = git-version-gen .version @RELMAKE@
View file
osmo-uecups_0.2.2.tar.xz/README.md -> osmo-uecups_0.3.0.tar.xz/README.md
Changed
@@ -23,7 +23,7 @@ -------- The official homepage of the project is -https://osmocom.org/projects/osmo-ggsn/wiki/osmo-uecups +https://osmocom.org/projects/osmo-uecups/wiki GIT Repository -------------- @@ -137,7 +137,7 @@ Our coding standards are described at https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards -We us a gerrit based patch submission/review process for managing +We use a Gerrit based patch submission/review process for managing contributions. Please see https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for more details
View file
osmo-uecups_0.2.2.tar.xz/configure.ac -> osmo-uecups_0.3.0.tar.xz/configure.ac
Changed
@@ -26,9 +26,9 @@ fi PKG_PROG_PKG_CONFIG(0.20) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) -PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0) -PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.12.0) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.12.0) +PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.7.0) PKG_CHECK_MODULES(LIBJANSSON, jansson) PKG_CHECK_MODULES(LIBNLROUTE3, libnl-route-3.0) @@ -109,5 +109,4 @@ daemon/Makefile doc/Makefile doc/examples/Makefile - contrib/osmo-uecups.spec )
View file
osmo-uecups_0.2.2.tar.xz/contrib/jenkins.sh -> osmo-uecups_0.3.0.tar.xz/contrib/jenkins.sh
Changed
@@ -18,9 +18,8 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.hc") -osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false -osmo-build-dep.sh libosmo-abis "" ac_cv_path_DOXYGEN=false -osmo-build-dep.sh libosmo-netif "" ac_cv_path_DOXYGEN=false +osmo-build-dep.sh libosmocore "" --disable-doxygen +osmo-build-dep.sh libosmo-netif "" --disable-doxygen export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib"
View file
osmo-uecups_0.2.2.tar.xz/daemon/cups_client.c -> osmo-uecups_0.3.0.tar.xz/daemon/cups_client.c
Changed
@@ -15,6 +15,7 @@ #include <osmocom/core/exec.h> #include "internal.h" +#include "gtp.h" #include <netinet/sctp.h> @@ -202,6 +203,58 @@ return 0; } +static int parse_ext_hdr_pdu_session_container(struct gtp1u_exthdr_pdu_sess_container *out, json_t *jpdu_sess_cont) +{ + json_t *jpdu_type, *jqfi; + const char *str_pdu_type; + + if (!json_is_object(jpdu_sess_cont)) + return -EINVAL; + + memset(out, 0, sizeof(*out)); + + out->enabled = true; + + jpdu_type = json_object_get(jpdu_sess_cont, "pdu_type"); + if (!json_is_string(jpdu_type)) + return -EINVAL; + str_pdu_type = json_string_value(jpdu_type); + if (!strcmp(str_pdu_type, "ul_pdu_sess_info")) + out->pdu_type = GTP1_EXTHDR_PDU_TYPE_UL_PDU_SESSION_INFORMATION; + else if (!strcmp(str_pdu_type, "dl_pdu_sess_info")) + out->pdu_type = GTP1_EXTHDR_PDU_TYPE_DL_PDU_SESSION_INFORMATION; + else + return -EINVAL; + + jqfi = json_object_get(jpdu_sess_cont, "qfi"); + if (!json_is_integer(jqfi)) + return -EINVAL; + out->qos_flow_identifier = json_number_value(jqfi); + + return 0; +} +static int parse_ext_hdr(struct gtp1u_exthdrs *out, json_t *jexthdr) +{ + json_t *jseq_num, *jn_pdu_num, *jpdu_sess_cont; + int rc = 0; + + if (!json_is_object(jexthdr)) + return -EINVAL; + + jseq_num = json_object_get(jexthdr, "sequence_number"); + if (jseq_num) + out->seq_num_enabled = true; + + jn_pdu_num = json_object_get(jexthdr, "n_pdu_number"); + if (jn_pdu_num) + out->n_pdu_num_enabled = true; + + jpdu_sess_cont = json_object_get(jexthdr, "pdu_session_container"); + if (jpdu_sess_cont) + rc = parse_ext_hdr_pdu_session_container(&out->pdu_sess_container, jpdu_sess_cont); + + return rc; +} static int parse_create_tun(struct gtp_tunnel_params *out, json_t *ctun) { @@ -209,6 +262,7 @@ json_t *jrx_teid, *jtx_teid; json_t *jtun_dev_name, *jtun_netns_name; json_t *juser_addr, *juser_addr_type; + json_t *jgtp_ext_hdr; 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"}}' */ @@ -257,6 +311,13 @@ out->tun_netns_name = talloc_strdup(out, json_string_value(jtun_netns_name)); } + jgtp_ext_hdr = json_object_get(ctun, "gtp_ext_hdr"); + if (jgtp_ext_hdr) { + rc = parse_ext_hdr(&out->exthdr, jgtp_ext_hdr); + if (rc < 0) + return rc; + } + return 0; } @@ -491,105 +552,126 @@ return 0; } + +static void cups_client_free(struct cups_client *cc); + /* control/user plane separation per-client read cb */ -static int cups_client_read_cb(struct osmo_stream_srv *conn) +static int cups_client_read_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg) { - 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; + int flags; - /* 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); + flags = msgb_sctp_msg_flags(msg); + LOGCC(cc, LOGL_DEBUG, "read %d bytes (flags=0x%x)\n", res, flags); - if (flags & MSG_NOTIFICATION) { + if (flags & OSMO_STREAM_SCTP_MSG_FLAGS_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; + goto free_client; default: - break; + msgb_free(msg); + return 0; } - goto out; } - - LOGCC(cc, LOGL_DEBUG, "Rx '%s'\n", msgb_data(msg)); + if (res <= 0) + goto free_client; /* 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; + LOGCC(cc, LOGL_ERROR, "Error decoding JSON (%s)\n", jerr.text); + msgb_free(msg); + return -1; } /* Dispatch */ - rc = cups_client_handle_json(cc, jroot); + cups_client_handle_json(cc, jroot); json_decref(jroot); msgb_free(msg); return 0; -out: + +free_client: + LOGCC(cc, LOGL_NOTICE, "UECUPS connection lost\n"); msgb_free(msg); - return rc; + cups_client_free(cc); + return -1; } 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; + + if (!cc) /* already being destroyed in cups_client_free() */ + return 0; + + LOGCC(cc, LOGL_INFO, "UECUPS connection lost\n"); + cups_client_free(cc); + return 0; +} + +static struct cups_client *cups_client_alloc(struct gtp_daemon *d, struct osmo_stream_srv_link *link, int fd) +{ + struct cups_client *cc; + + cc = talloc_zero(d, struct cups_client); + if (!cc) + return NULL; + + cc->d = d; + osmo_sock_get_name_buf(cc->sockname, sizeof(cc->sockname), fd); + cc->srv = osmo_stream_srv_create2(cc, link, fd, cc); + if (!cc->srv) { + talloc_free(cc); + return NULL; + } + osmo_stream_srv_set_read_cb(cc->srv, cups_client_read_cb); + osmo_stream_srv_set_closed_cb(cc->srv, cups_client_closed_cb); + + llist_add_tail(&cc->list, &d->cups_clients); + return cc; +} + +static void cups_client_free(struct cups_client *cc) +{ struct subprocess *p, *p2; + if (!cc) + return; + + LOGCC(cc, LOGL_DEBUG, "free()\n"); + /* 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) { + llist_for_each_entry_safe(p, p2, &cc->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; + if (cc->srv) { + osmo_stream_srv_set_data(cc->srv, NULL); + osmo_stream_srv_destroy(cc->srv); + } + talloc_free(cc); } - /* 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); + cc = cups_client_alloc(d, link, fd); 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; } @@ -607,6 +689,7 @@ 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_set_msgb_alloc_info(srv_link, CUPS_MSGB_SIZE, 0); osmo_stream_srv_link_open(srv_link); return srv_link; }
View file
osmo-uecups_0.2.2.tar.xz/daemon/gtp.h -> osmo-uecups_0.3.0.tar.xz/daemon/gtp.h
Changed
@@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once #include <stdint.h> +#include <osmocom/core/endian.h> /* General GTP protocol related definitions. */ @@ -31,3 +32,60 @@ #define GTP1_F_SEQ 0x02 #define GTP1_F_EXTHDR 0x04 #define GTP1_F_MASK 0x07 + + +/* + * 5GC GTP Header (16byte) + * o Flags(1byte) : 0x34 + * o Message Type(1byte) : T-PDU (0xff) + * o Length(2byte) : 36 + * o TEID(4byte) : 0x00000001 + * o Sequence Number(2byte) : 0x0000 + * o N PDU Number(1byte) : 0x00 + * o Next extension header type(4byte): PDU Session container(1byte) : (0x85) + * o Extension header(4byte) + * - Extension HEader Length(1byte) : 1 + * - PDU Session Container(2byte) + * ; PDU Type : UL PDU SESSION INFORMATION (1) + * ; QoS Flow Identifier (QFI) : 1 + * - Next extension header type : No more extension headers (0x00) + */ + +#define GTP1_EXTHDR_UDP_PORT 0x40 +#define GTP1_EXTHDR_PDU_SESSION_CONTAINER 0x85 +#define GTP1_EXTHDR_PDCP_NUMBER 0xc0 +#define GTP1_EXTHDR_NO_MORE_EXTENSION_HEADERS 0x0 + +#define GTP1_EXTHDR_PDU_TYPE_DL_PDU_SESSION_INFORMATION 0 +#define GTP1_EXTHDR_PDU_TYPE_UL_PDU_SESSION_INFORMATION 1 + +struct gtp1_exthdr { + uint16_t sequence_number; + uint8_t n_pdu_number; + struct { +#if OSMO_IS_LITTLE_ENDIAN + uint8_t type; + uint8_t len; + union { + struct { /* TODO: make sure order of fields is correct or swapped */ + uint8_t spare1:4, + pdu_type:4; + uint8_t qos_flow_identifier:6, + reflective_qos_indicator:1, + paging_policy_presence:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t type; + uint8_t len; + union { + struct { + uint8_t spare1:4, + pdu_type:4; + uint8_t paging_policy_presence:1, reflective_qos_indicator:1, qos_flow_identifier:6; +#endif + }; + uint16_t udp_port; + uint16_t pdcp_number; + }; + } __attribute__ ((packed)) array8; +} __attribute__ ((packed));
View file
osmo-uecups_0.2.2.tar.xz/daemon/gtp_endpoint.c -> osmo-uecups_0.3.0.tar.xz/daemon/gtp_endpoint.c
Changed
@@ -1,4 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +/*pthread.h pthread_setname_np(): */ +#define _GNU_SOURCE + #include <stdint.h> #include <stdbool.h> #include <stdlib.h> @@ -23,70 +27,132 @@ #define LOGEP(ep, lvl, fmt, args ...) \ LOGP(DEP, lvl, "%s: " fmt, (ep)->name, ## args) +/* LOGEP "No Cancel": Use within the pthread which can be pthread_cancel()ed, in + * order to avoid exiting with the logging mutex held and causing a deadlock afterwards. */ +#define LOGEP_NC(ep, lvl, fmt, args ...) \ + do { \ + int _old_cancelst_unused; \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_old_cancelst_unused); \ + LOGEP(ep, lvl, fmt, ## args); \ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &_old_cancelst_unused); \ + } while (0) + /*********************************************************************** * GTP Endpoint (UDP socket) ***********************************************************************/ -/* one thread for reading from each GTP/UDP socket (GTP decapsulation -> tun) */ +static void handle_gtp1u(struct gtp_endpoint *ep, const uint8_t *buffer, unsigned int nread) +{ + struct gtp_daemon *d = ep->d; + struct gtp_tunnel *t; + const struct gtp1_header *gtph; + const uint8_t *payload; + int rc, outfd; + uint32_t teid; + uint16_t gtp_len; + + if (nread < sizeof(*gtph)) { + LOGEP_NC(ep, LOGL_NOTICE, "Short read: %u < %lu\n", nread, sizeof(*gtph)); + return; + } + gtph = (struct gtp1_header *)buffer; + + /* check GTP header contents */ + if ((gtph->flags & 0xf0) != 0x30) { + LOGEP_NC(ep, LOGL_NOTICE, "Unexpected GTP Flags: 0x%02x\n", gtph->flags); + return; + } + if (gtph->type != GTP_TPDU) { + LOGEP_NC(ep, LOGL_NOTICE, "Unexpected GTP Message Type: 0x%02x\n", gtph->type); + return; + } + + gtp_len = ntohs(gtph->length); + if (sizeof(*gtph)+gtp_len > nread) { + LOGEP_NC(ep, LOGL_NOTICE, "Short GTP Message: %lu < len=%u\n", + sizeof(*gtph)+gtp_len, nread); + return; + } + teid = ntohl(gtph->tid); + + payload = buffer + sizeof(*gtph); + if (gtph->flags & GTP1_F_MASK) { + const struct gtp1_exthdr *exthdr = (const struct gtp1_exthdr *)payload; + if (gtp_len < 4) { + LOGEP_NC(ep, LOGL_NOTICE, "Short GTP Message according to flags 0x%02x: %lu < len=%u\n", + gtph->flags, sizeof(*gtph) + gtp_len, nread); + return; + } + gtp_len -= 4; + payload += 4; + const uint8_t *it = &exthdr->array0.type; + while (*it != 0) { + unsigned int ext_len; + if (gtp_len < 1) { + LOGEP_NC(ep, LOGL_NOTICE, "Short GTP Message according to flags 0x%02x: %lu < len=%u\n", + gtph->flags, sizeof(*gtph) + gtp_len, nread); + return; + } + ext_len = 1 + 1 + it1 + 1; + if (gtp_len < ext_len) { + LOGEP_NC(ep, LOGL_NOTICE, "Short GTP Message according to flags 0x%02x: %lu < len=%u\n", + gtph->flags, sizeof(*gtph) + gtp_len, nread); + return; + } + gtp_len -= ext_len; + payload += ext_len; + it = payload - 1; + } + } + + /* 2) look-up tunnel based on TEID */ + pthread_rwlock_rdlock(&d->rwlock); + t = _gtp_tunnel_find_r(d, teid, ep); + if (!t) { + pthread_rwlock_unlock(&d->rwlock); + LOGEP_NC(ep, LOGL_NOTICE, "Unable to find tunnel for TEID=0x%08x\n", teid); + return; + } + outfd = t->tun_dev->fd; + pthread_rwlock_unlock(&d->rwlock); + + /* 3) write to TUN device */ + rc = write(outfd, payload, gtp_len); + if (rc < gtp_len) { + LOGEP_NC(ep, LOGL_FATAL, "Error writing to tun device %s\n", strerror(errno)); + exit(1); + } +} + +/* One thread for reading from each GTP/UDP socket (GTP decapsulation -> tun) + * IMPORTANT!: Since this thread is cancellable (deferred type): + * - All osmo logging functions in this thread must be called with PTHREAD_CANCEL_DISABLE set, + * otherwise the thread could be cancelled while holding the libosmocore logging mutex, hence causing + * deadlock with main (or other) thread. + * - Within pthread_rwlock_*(&d->rwlock) mutual exclusion zone, if we ever do any call considered + * a cancellation point (see "man pthreads"), then make sure to do the call protected with + * PTHREAD_CANCEL_DISABLE set, otherwise we may leave the d->rwlock held forever and cause a deadlock + * with main (or other) thread. + */ static void *gtp_endpoint_thread(void *arg) { struct gtp_endpoint *ep = (struct gtp_endpoint *)arg; - struct gtp_daemon *d = ep->d; + char thread_name16; + uint8_t buffersizeof(struct gtp1_header) + sizeof(struct gtp1_exthdr) + MAX_UDP_PACKET; - uint8_t bufferMAX_UDP_PACKET+sizeof(struct gtp1_header); + snprintf(thread_name, sizeof(thread_name), "RxGtpu%s", ep->name); + pthread_setname_np(pthread_self(), thread_name); while (1) { - struct gtp_tunnel *t; - const struct gtp1_header *gtph; - int rc, nread, outfd; - uint32_t teid; + int rc; /* 1) read GTP packet from UDP socket */ rc = recvfrom(ep->fd, buffer, sizeof(buffer), 0, (struct sockaddr *)NULL, 0); if (rc < 0) { - LOGEP(ep, LOGL_FATAL, "Error reading from UDP socket: %s\n", strerror(errno)); - exit(1); - } - nread = rc; - if (nread < sizeof(*gtph)) { - LOGEP(ep, LOGL_NOTICE, "Short read: %d < %lu\n", nread, sizeof(*gtph)); - continue; - } - gtph = (struct gtp1_header *)buffer; - - /* check GTP heaader contents */ - if (gtph->flags != 0x30) { - LOGEP(ep, LOGL_NOTICE, "Unexpected GTP Flags: 0x%02x\n", gtph->flags); - continue; - } - if (gtph->type != GTP_TPDU) { - LOGEP(ep, LOGL_NOTICE, "Unexpected GTP Message Type: 0x%02x\n", gtph->type); - continue; - } - if (sizeof(*gtph)+ntohs(gtph->length) > nread) { - LOGEP(ep, LOGL_NOTICE, "Shotr GTP Message: %lu < len=%d\n", - sizeof(*gtph)+ntohs(gtph->length), nread); - continue; - } - teid = ntohl(gtph->tid); - - /* 2) look-up tunnel based on TEID */ - pthread_rwlock_rdlock(&d->rwlock); - t = _gtp_tunnel_find_r(d, teid, ep); - if (!t) { - pthread_rwlock_unlock(&d->rwlock); - LOGEP(ep, LOGL_NOTICE, "Unable to find tunnel for TEID=0x%08x\n", teid); - continue; - } - outfd = t->tun_dev->fd; - pthread_rwlock_unlock(&d->rwlock); - - /* 3) write to TUN device */ - rc = write(outfd, buffer+sizeof(*gtph), ntohs(gtph->length)); - if (rc < nread-sizeof(struct gtp1_header)) { - LOGEP(ep, LOGL_FATAL, "Error writing to tun device %s\n", strerror(errno)); + LOGEP_NC(ep, LOGL_FATAL, "Error reading from UDP socket: %s\n", strerror(errno)); exit(1); } + handle_gtp1u(ep, buffer, rc); } }
View file
osmo-uecups_0.2.2.tar.xz/daemon/gtp_tunnel.c -> osmo-uecups_0.3.0.tar.xz/daemon/gtp_tunnel.c
Changed
@@ -56,6 +56,7 @@ t->rx_teid = cpars->rx_teid; t->tx_teid = cpars->tx_teid; + memcpy(&t->exthdr, &cpars->exthdr, sizeof(t->exthdr)); memcpy(&t->user_addr, &cpars->user_addr, sizeof(t->user_addr)); memcpy(&t->remote_udp, &cpars->remote_udp, sizeof(t->remote_udp));
View file
osmo-uecups_0.2.2.tar.xz/daemon/internal.h -> osmo-uecups_0.3.0.tar.xz/daemon/internal.h
Changed
@@ -181,6 +181,18 @@ * this is what happens when IP arrives on the tun device */ +struct gtp1u_exthdr_pdu_sess_container { + bool enabled; + uint8_t pdu_type; /* GTP1_EXTHDR_PDU_TYPE_* */ + uint8_t qos_flow_identifier; +}; + +struct gtp1u_exthdrs { + bool seq_num_enabled; + bool n_pdu_num_enabled; + struct gtp1u_exthdr_pdu_sess_container pdu_sess_container; +}; + struct gtp_tunnel { /* entry in global list / hash table */ struct llist_head list; @@ -205,6 +217,9 @@ /* Remote UDP IP/Port*/ struct sockaddr_storage remote_udp; + /* GTP Extension Header */ + struct gtp1u_exthdrs exthdr; + /* TODO: Filter */ }; @@ -219,6 +234,9 @@ uint32_t rx_teid; uint32_t tx_teid; + /* GTPv1U Extension Headers: */ + struct gtp1u_exthdrs exthdr; + /* end user address */ struct sockaddr_storage user_addr;
View file
osmo-uecups_0.2.2.tar.xz/daemon/tun_device.c -> osmo-uecups_0.3.0.tar.xz/daemon/tun_device.c
Changed
@@ -1,4 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +/*pthread.h pthread_setname_np(): */ +#define _GNU_SOURCE + #include <unistd.h> #include <stdint.h> #include <stdbool.h> @@ -42,6 +46,16 @@ #define LOGTUN(tun, lvl, fmt, args ...) \ LOGP(DTUN, lvl, "%s: " fmt, (tun)->devname, ## args) +/* LOGTUN "No Cancel": Use within the pthread which can be pthread_cancel()ed, in + * order to avoid exiting with the logging mutex held and causing a deadlock afterwards. */ +#define LOGTUN_NC(ep, lvl, fmt, args ...) \ + do { \ + int _old_cancelst_unused; \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_old_cancelst_unused); \ + LOGTUN(tun, lvl, fmt, ## args); \ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &_old_cancelst_unused); \ + } while (0) + /* extracted information from a packet */ struct pkt_info { struct sockaddr_storage saddr; @@ -128,50 +142,117 @@ OSMO_ASSERT(rc == 0); } -/* one thread for reading from each TUN device (TUN -> GTP encapsulation) */ -static void *tun_device_thread(void *arg) +/* Note: This function is called with d->rwlock locked, and it's responsible of unlocking it before returning. */ +static int tx_gtp1u_pkt(struct gtp_tunnel *t, uint8_t *base_buffer, const uint8_t *payload, unsigned int payload_len) { - struct tun_device *tun = (struct tun_device *)arg; - struct gtp_daemon *d = tun->d; - - uint8_t base_bufferMAX_UDP_PACKET+sizeof(struct gtp1_header); - struct gtp1_header *gtph = (struct gtp1_header *)base_buffer; - uint8_t *buffer = base_buffer + sizeof(struct gtp1_header); - + struct gtp1_header *gtph; + unsigned int head_len = payload - base_buffer; + unsigned int hdr_len_needed; + unsigned int opt_hdr_len_needed = 0; struct sockaddr_storage daddr; - int old_cancelst_unused; + int outfd = t->gtp_ep->fd; + int rc; + uint8_t flags; + +#define GTP1_F_NPDU 0x01 +#define GTP1_F_SEQ 0x02 +#define GTP1_F_EXTHDR 0x04 +#define GTP1_F_MASK 0x07 + + flags = 0x30; /* Version */ + + if (t->exthdr.seq_num_enabled) + flags |= GTP1_F_SEQ; + + if (t->exthdr.n_pdu_num_enabled) + flags |= GTP1_F_NPDU; + + if (t->exthdr.pdu_sess_container.enabled) { + flags |= GTP1_F_EXTHDR; + opt_hdr_len_needed += 4; /* Extra Header struct */ + } + /* Make sure the Next Extension Header Type is counted: */ + if (flags & GTP1_F_MASK) + opt_hdr_len_needed += 4; + + hdr_len_needed = sizeof(struct gtp1_header) + opt_hdr_len_needed; + OSMO_ASSERT(hdr_len_needed < head_len); + gtph = (struct gtp1_header *)(payload - hdr_len_needed); /* initialize the fixed part of the GTP header */ - gtph->flags = 0x30; + gtph->flags = flags; gtph->type = GTP_TPDU; + gtph->length = htons(opt_hdr_len_needed + payload_len); + gtph->tid = htonl(t->tx_teid); + + if (flags & GTP1_F_MASK) { + struct gtp1_exthdr *exthdr = (struct gtp1_exthdr *)(((uint8_t *)gtph) + sizeof(*gtph)); + exthdr->sequence_number = htons(0); /* TODO: increment sequence_number in "t". */ + exthdr->n_pdu_number = 0; /* TODO: increment n_pdu_number in "t". */ + if (t->exthdr.pdu_sess_container.enabled) { + exthdr->array0.type = GTP1_EXTHDR_PDU_SESSION_CONTAINER; + exthdr->array0.len = 1; + exthdr->array0.spare1 = 0; + exthdr->array0.pdu_type = t->exthdr.pdu_sess_container.pdu_type; + exthdr->array0.qos_flow_identifier = t->exthdr.pdu_sess_container.qos_flow_identifier; + exthdr->array0.reflective_qos_indicator = 0; + exthdr->array0.paging_policy_presence = 0; + exthdr->array1.type = 0; /* No extension headers */ + } else { + exthdr->array0.type = 0; /* No extension headers */ + } + } + + memcpy(&daddr, &t->remote_udp, sizeof(daddr)); + pthread_rwlock_unlock(&t->d->rwlock); + + /* 4) write to GTP/UDP socket */ + rc = sendto(outfd, gtph, hdr_len_needed + payload_len, 0, + (struct sockaddr *)&daddr, sizeof(daddr)); + return rc; +} + +/* One thread for reading from each TUN device (TUN -> GTP encapsulation) + * IMPORTANT!: Since this thread is cancellable (deferred type): + * - All osmo logging functions in this thread must be called with PTHREAD_CANCEL_DISABLE set, + * otherwise the thread could be cancelled while holding the libosmocore logging mutex, hence causing + * deadlock with main (or other) thread. + * - Within pthread_rwlock_*(&d->rwlock) mutual exclusion zone, if we ever do any call considered + * a cancellation point (see "man pthreads"), then make sure to do the call protected with + * PTHREAD_CANCEL_DISABLE set, otherwise we may leave the d->rwlock held forever and cause a deadlock + * with main (or other) thread. + */ +static void *tun_device_thread(void *arg) +{ + struct tun_device *tun = (struct tun_device *)arg; + struct gtp_daemon *d = tun->d; + char thread_name16; + /* Make sure "buffer" below ends up aligned to 4byte so that it can access struct iphdr in a 4-byte aligned way. */ + const size_t payload_off_4byte_aligned = ((sizeof(struct gtp1_header) + sizeof(struct gtp1_exthdr)) + 3) & (~0x3); + uint8_t base_bufferpayload_off_4byte_aligned + MAX_UDP_PACKET; 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. */ + + snprintf(thread_name, sizeof(thread_name), "Rx%s", tun->devname); + pthread_setname_np(pthread_self(), thread_name); while (1) { struct gtp_tunnel *t; struct pkt_info pinfo; - int rc, nread, outfd; + int rc, nread; + uint8_t *buffer = base_buffer + payload_off_4byte_aligned; /* 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)); + LOGTUN_NC(tun, LOGL_FATAL, "Error readingfrom tun device: %s\n", strerror(errno)); exit(1); } nread = rc; - gtph->length = htons(nread); 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); + LOGTUN_NC(tun, LOGL_NOTICE, "Error parsing IP packet: %s\n", osmo_hexdump(buffer, nread)); continue; } @@ -189,22 +270,13 @@ 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); + LOGTUN_NC(tun, LOGL_NOTICE, "No tunnel found for source address %s:%s\n", host, port); continue; } - outfd = t->gtp_ep->fd; - memcpy(&daddr, &t->remote_udp, sizeof(daddr)); - gtph->tid = htonl(t->tx_teid); - pthread_rwlock_unlock(&d->rwlock); - - /* 4) write to GTP/UDP socket */ - rc = sendto(outfd, base_buffer, nread+sizeof(*gtph), 0, - (struct sockaddr *)&daddr, sizeof(daddr)); + rc = tx_gtp1u_pkt(t, base_buffer, buffer, nread); + /* pthread_rwlock_unlock() was called inside tx_gtp1u_pkt(). */ if (rc < 0) { - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelst_unused); - LOGTUN(tun, LOGL_FATAL, "Error Writing to UDP socket: %s\n", strerror(errno)); + LOGTUN_NC(tun, LOGL_FATAL, "Error Writing to UDP socket: %s\n", strerror(errno)); exit(1); } }
View file
osmo-uecups_0.2.2.tar.xz/debian/changelog -> osmo-uecups_0.3.0.tar.xz/debian/changelog
Changed
@@ -1,3 +1,32 @@ +osmo-uecups (0.3.0) unstable; urgency=medium + + Pau Espin Pedrol + * .gitignore: Add contrib/osmo-uecups.spec + * jenkins.sh: Skip building unneeded libosmo-abis + * ttcn3: UECUPS_Types: Add templates for existing json commands + * Introduce GTPv1U QFI support + * tun_device: Fix Tx uninitialized gtpu ext hdr fields + * tun_device: Make sure struct iphdr access is 4-byte aligned + * Set thread names + * cups_client: Log conn lost before tear down subprocesses + * cups_client: Add cups_client_alloc/free functions + * cups_client: Fix memleak during conn closed + * cups_client: Use new iofd stream_srv APIs + * gtp_endpoint: Avoid deadlocks logging while thread is cancelled + * tun_device: Introduce helper macro LOGTUN_NC() + * cosmetic: Improve comments on cancellable thread safety + + Oliver Smith + * contrib: remove rpm spec file + * contrib/jenkins: libosmo-abis after libosmo-netif + + Vadim Yanitskiy + * README.md: cosmetic: fix a typo + * README.md: fix wrong project's homepage URL + * doc/examples: populate the config file example + + -- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 03 Dec 2025 20:06:03 +0100 + osmo-uecups (0.2.2) unstable; urgency=medium Harald Welte
View file
osmo-uecups_0.2.2.tar.xz/debian/control -> osmo-uecups_0.3.0.tar.xz/debian/control
Changed
@@ -11,10 +11,10 @@ pkg-config, libjansson-dev, libnl-route-3-dev, - libosmocore-dev (>= 1.9.0), - libosmo-netif-dev (>= 1.4.0), + libosmocore-dev (>= 1.12.0), + libosmo-netif-dev (>= 1.7.0), libsctp-dev, - osmo-gsm-manuals-dev (>= 1.5.0) + osmo-gsm-manuals-dev (>= 1.7.0) Standards-Version: 3.9.8 Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-uecups
View file
osmo-uecups_0.3.0.tar.xz/doc/examples/osmo-uecups-daemon.cfg
Added
@@ -0,0 +1,14 @@ +log stderr + logging filter all 1 + logging color 1 + logging print file basename last + logging print category-hex 0 + logging print category 1 + logging timestamp 0 + logging level set-all info +! +line vty + no login +! +uecups + local-ip 127.0.0.1
View file
osmo-uecups_0.2.2.tar.xz/ttcn3/UECUPS_Types.ttcn -> osmo-uecups_0.3.0.tar.xz/ttcn3/UECUPS_Types.ttcn
Changed
@@ -22,23 +22,37 @@ uint16_t Port }; +const charstring UECUPS_GtpExtHdr_PduSessContainer_Type_ul_pdu_sess_info := "ul_pdu_sess_info"; +const charstring UECUPS_GtpExtHdr_PduSessContainer_Type_dl_pdu_sess_info := "dl_pdu_sess_info"; +type record UECUPS_GtpExtHdr_PduSessContainer { + charstring pdu_type, /* ("ul_pdu_sess_info"|"dl_pdu_sess_info") */ + uint32_t qfi +}; + +type record UECUPS_GtpExtHdr { + boolean sequence_number optional, + boolean n_pdu_number optional, + UECUPS_GtpExtHdr_PduSessContainer pdu_session_container optional +}; + /* Create a new GTP-U tunnel in the user plane */ type record UECUPS_CreateTun { /* TEID in transmit + receive direction */ - uint32_t tx_teid, - uint32_t rx_teid, + uint32_t tx_teid, + uint32_t rx_teid, /* user address (allocated inside the tunnel) */ - UECUPS_AddrType user_addr_type, - OCT4_16n user_addr, + UECUPS_AddrType user_addr_type, + OCT4_16n user_addr, /* GTP endpoint (UDP IP/Port tuples) */ - UECUPS_SockAddr local_gtp_ep, - UECUPS_SockAddr remote_gtp_ep, + UECUPS_SockAddr local_gtp_ep, + UECUPS_SockAddr remote_gtp_ep, /* TUN device */ - charstring tun_dev_name, - charstring tun_netns_name optional + charstring tun_dev_name, + charstring tun_netns_name optional, + UECUPS_GtpExtHdr gtp_ext_hdr optional }; type record UECUPS_CreateTunRes { @@ -128,5 +142,64 @@ Port := Port } +template (value) UECUPS_GtpExtHdr_PduSessContainer +ts_UECUPS_GtpExtHdr_PduSessContainer(template (value) charstring pdu_type, + template (value) uint32_t qfi) +:= { + pdu_type := pdu_type, + qfi := qfi +}; + +template (value) UECUPS_GtpExtHdr +ts_UECUPS_GtpExtHdr(template (omit) boolean sequence_number := omit, + template (omit) boolean n_pdu_number := omit, + template (omit) UECUPS_GtpExtHdr_PduSessContainer pdu_session_container := omit) +:= { + sequence_number := sequence_number, + n_pdu_number := n_pdu_number, + pdu_session_container := pdu_session_container +}; + +template (value) UECUPS_CreateTun +ts_UECUPS_CreateTun(template (value) uint32_t tx_teid, + template (value) uint32_t rx_teid, + template (value) UECUPS_AddrType user_addr_type, + template (value) OCT4_16n user_addr, + template (value) UECUPS_SockAddr local_gtp_ep, + template (value) UECUPS_SockAddr remote_gtp_ep, + template (value) charstring tun_dev_name := "tun", + template (omit) charstring tun_netns_name := omit, + template (omit) UECUPS_GtpExtHdr gtp_ext_hdr := omit) +:= { + tx_teid := tx_teid, + rx_teid := rx_teid, + user_addr_type := user_addr_type, + user_addr := user_addr, + local_gtp_ep := local_gtp_ep, + remote_gtp_ep := remote_gtp_ep, + tun_dev_name := tun_dev_name, + tun_netns_name := tun_netns_name, + gtp_ext_hdr := gtp_ext_hdr +}; + +template (value) UECUPS_DestroyTun +ts_UECUPS_DestroyTun(template (value) UECUPS_SockAddr local_gtp_ep, + template (value) uint32_t rx_teid) +:= { + local_gtp_ep := local_gtp_ep, + rx_teid := rx_teid +}; + +template (value) UECUPS_StartProgram +ts_UECUPS_StartProgram(template (value) charstring command, + template (omit) charstring_list environment := omit, + template (value) charstring run_as_user := "root", + template (omit) charstring tun_netns_name := omit) +:= { + command := command, + environment := environment, + run_as_user := run_as_user, + tun_netns_name := tun_netns_name +}; } with { encode "JSON" };
View file
rpmlintrc
Deleted
@@ -1,5 +0,0 @@ -# Don't abort the build when finding a library that depends on a package with -# a specific version. This is intentional for nightly builds, we don't want -# libraries from different build dates to be mixed as they might have ABI -# incompatibilities. -setBadness('shlib-fixed-dependency', 0)
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
.