Remove SGW and PGW

This commit is contained in:
Sukchan Lee 2020-08-22 13:21:37 -04:00
parent aca41f6668
commit b412e51b83
41 changed files with 7 additions and 10947 deletions

6
debian/changelog vendored
View File

@ -1,9 +1,3 @@
open5gs (2.0.0~eoan) eoan; urgency=medium
* 5G Core and EPC (Hybrid)
-- Sukchan Lee <acetcom@gmail.com> Fri, 21 Aug 2020 23:41:47 -0400
open5gs (2.0.0~focal) focal; urgency=medium
* 5G Core and EPC (Hybrid)

View File

@ -31,10 +31,8 @@ configure_file(output : 'version.h', configuration : version_conf)
subdir('mme')
subdir('hss')
subdir('sgw')
subdir('sgwc')
subdir('sgwu')
subdir('pgw')
subdir('pcrf')
subdir('nrf')

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ogs-app.h"
int app_initialize(const char *const argv[])
{
int rv;
rv = pgw_initialize();
if (rv != OGS_OK) {
ogs_error("Failed to intialize PGW");
return rv;
}
ogs_info("PGW initialize...done");
return OGS_OK;
}
void app_terminate(void)
{
pgw_terminate();
ogs_info("PGW terminate...done");
}

View File

@ -1,85 +0,0 @@
# Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
# This file is part of Open5GS.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
pgw_conf = configuration_data()
pgw_headers = ('''
net/if.h
netinet/ip.h
netinet/ip6.h
netinet/ip_icmp.h
netinet/icmp6.h
'''.split())
foreach h : pgw_headers
if cc.has_header(h)
define = 'HAVE_' + h.underscorify().to_upper()
pgw_conf.set(define, 1)
endif
endforeach
if have_func_kqueue
pgw_conf.set('HAVE_KQUEUE', 1)
endif
configure_file(output : 'pgw-config.h', configuration : pgw_conf)
libpgw_sources = files('''
pgw-ipfw.h
pgw-event.h
pgw-context.h
pgw-sm.h
pgw-gtp-path.h
pgw-s5c-build.h
pgw-s5c-handler.h
pgw-fd-path.h
pgw-gx-handler.h
pgw-ipfw.c
pgw-init.c
pgw-event.c
pgw-context.c
pgw-sm.c
pgw-gtp-path.c
pgw-s5c-build.c
pgw-s5c-handler.c
pgw-fd-path.c
pgw-gx-handler.c
'''.split())
libpgw = static_library('pgw',
sources : libpgw_sources,
link_with : libipfw,
dependencies : [libapp_dep, libdiameter_gx_dep, libgtp_dep, libipfw_dep],
install : false)
libpgw_dep = declare_dependency(
link_with : libpgw,
dependencies : [libapp_dep, libdiameter_gx_dep, libgtp_dep, libipfw_dep])
pgw_sources = files('''
app-init.c
../main.c
'''.split())
executable('open5gs-pgwd',
sources : pgw_sources,
c_args : '-DDEFAULT_CONFIG_FILENAME="@0@/pgw.yaml"'.format(open5gs_sysconfdir),
include_directories : srcinc,
dependencies : libpgw_dep,
install_rpath : libdir,
install : true)

File diff suppressed because it is too large Load Diff

View File

@ -1,301 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_CONTEXT_H
#define PGW_CONTEXT_H
#include "pgw-config.h"
#if HAVE_NET_IF_H
#include <net/if.h>
#endif
#include "ogs-gtp.h"
#include "ogs-diameter-gx.h"
#include "ogs-app.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_NUM_OF_DEV 16
#define MAX_NUM_OF_SUBNET 16
extern int __pgw_log_domain;
#undef OGS_LOG_DOMAIN
#define OGS_LOG_DOMAIN __pgw_log_domain
typedef struct pgw_context_s {
const char* diam_conf_path; /* PGW Diameter conf path */
ogs_diam_config_t *diam_config; /* PGW Diameter config */
uint32_t gtpc_port; /* Default: PGW GTP-C local port */
uint32_t gtpu_port; /* Default: PGW GTP-U local port */
const char *tun_ifname; /* Default: ogstun */
ogs_list_t gtpc_list; /* PGW GTPC IPv4 Server List */
ogs_list_t gtpc_list6; /* PGW GTPC IPv6 Server List */
ogs_sock_t *gtpc_sock; /* PGW GTPC IPv4 Socket */
ogs_sock_t *gtpc_sock6; /* PGW GTPC IPv6 Socket */
ogs_sockaddr_t *gtpc_addr; /* PGW GTPC IPv4 Address */
ogs_sockaddr_t *gtpc_addr6; /* PGW GTPC IPv6 Address */
ogs_list_t gtpu_list; /* PGW GTPU IPv4 Server List */
ogs_list_t gtpu_list6; /* PGW GTPU IPv6 Server List */
ogs_sock_t *gtpu_sock; /* PGW GTPU IPv4 Socket */
ogs_sock_t *gtpu_sock6; /* PGW GTPU IPv6 Socket */
ogs_sockaddr_t *gtpu_addr; /* PGW GTPU IPv4 Address */
ogs_sockaddr_t *gtpu_addr6; /* PGW GTPU IPv6 Address */
ogs_list_t dev_list; /* PGW Tun Device List */
ogs_list_t subnet_list; /* PGW UE Subnet List */
ogs_queue_t *queue; /* Queue for processing PGW control */
ogs_timer_mgr_t *timer_mgr; /* Timer Manager */
ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */
#define MAX_NUM_OF_DNS 2
const char *dns[MAX_NUM_OF_DNS];
const char *dns6[MAX_NUM_OF_DNS];
#define MAX_NUM_OF_P_CSCF 16
const char *p_cscf[MAX_NUM_OF_P_CSCF];
int num_of_p_cscf;
int p_cscf_index;
const char *p_cscf6[MAX_NUM_OF_P_CSCF];
int num_of_p_cscf6;
int p_cscf6_index;
ogs_list_t sgw_s5c_list; /* SGW GTPC Node List */
ogs_list_t sgw_s5u_list; /* SGW GTPU Node List */
ogs_list_t ip_pool_list;
ogs_hash_t *sess_hash; /* hash table (IMSI+APN) */
ogs_hash_t *ipv4_hash; /* hash table (IPv4 Address) */
ogs_hash_t *ipv6_hash; /* hash table (IPv6 Address) */
uint16_t mtu; /* MTU to advertise in PCO */
ogs_list_t sess_list;
} pgw_context_t;
typedef struct pgw_subnet_s pgw_subnet_t;
typedef struct pgw_ue_ip_s {
uint32_t addr[4];
bool static_ip;
/* Related Context */
pgw_subnet_t *subnet;
} pgw_ue_ip_t;
typedef struct pgw_dev_s {
ogs_lnode_t lnode;
char ifname[IFNAMSIZ];
ogs_socket_t fd;
ogs_sockaddr_t *link_local_addr;
ogs_poll_t *poll;
} pgw_dev_t;
typedef struct pgw_subnet_s {
ogs_lnode_t node;
ogs_ipsubnet_t sub; /* Subnet : cafe::0/64 */
ogs_ipsubnet_t gw; /* Gateway : cafe::1 */
char apn[OGS_MAX_APN_LEN]; /* APN : "internet", "volte", .. */
#define MAX_NUM_OF_SUBNET_RANGE 16
struct {
const char *low;
const char *high;
} range[MAX_NUM_OF_SUBNET_RANGE];
int num_of_range;
int family; /* AF_INET or AF_INET6 */
uint8_t prefixlen; /* prefixlen */
OGS_POOL(pool, pgw_ue_ip_t);
pgw_dev_t *dev; /* Related Context */
} pgw_subnet_t;
typedef struct pgw_sess_s {
ogs_lnode_t lnode;
uint32_t index; /**< An index of this node */
uint32_t pgw_s5c_teid; /* PGW-S5C-TEID is derived from INDEX */
uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is received from SGW */
char *gx_sid; /* Gx Session ID */
/* IMSI */
uint8_t imsi[OGS_MAX_IMSI_LEN];
int imsi_len;
char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1];
/* APN Configuration */
ogs_pdn_t pdn;
pgw_ue_ip_t* ipv4;
pgw_ue_ip_t* ipv6;
uint8_t hash_keybuf[OGS_MAX_IMSI_LEN+OGS_MAX_APN_LEN+1];
int hash_keylen;
ogs_tlv_octet_t ue_pco; /* Save Protocol Configuration Options from UE */
ogs_tlv_octet_t user_location_information; /* User Location Information */
ogs_tlv_octet_t ue_timezone; /* UE Timezone */
ogs_list_t bearer_list;
/* Related Context */
ogs_gtp_node_t *gnode;
} pgw_sess_t;
typedef struct pgw_bearer_s {
ogs_lnode_t lnode; /**< A node of list_t */
uint32_t index;
uint8_t ebi;
uint32_t pgw_s5u_teid; /* PGW_S5U is derived from INDEX */
uint32_t sgw_s5u_teid; /* SGW_S5U is received from SGW */
char *name; /* PCC Rule Name */
ogs_qos_t qos; /* QoS Infomration */
/* Packet Filter Identifier Generator(1~15) */
uint8_t pf_identifier;
/* Packet Filter List */
ogs_list_t pf_list;
pgw_sess_t *sess;
ogs_gtp_node_t *gnode;
} pgw_bearer_t;
typedef struct pgw_rule_s {
uint8_t proto;
ED5(uint8_t ipv4_local:1;,
uint8_t ipv4_remote:1;,
uint8_t ipv6_local:1;,
uint8_t ipv6_remote:1;,
uint8_t reserved:4;)
struct {
struct {
uint32_t addr[4];
uint32_t mask[4];
} local;
struct {
uint32_t addr[4];
uint32_t mask[4];
} remote;
} ip;
struct {
struct {
uint16_t low;
uint16_t high;
} local;
struct {
uint16_t low;
uint16_t high;
} remote;
} port;
} pgw_rule_t;
typedef struct pgw_pf_s {
ogs_lnode_t lnode;
ED3(uint8_t spare:2;,
uint8_t direction:2;,
uint8_t identifier:4;)
pgw_rule_t rule;
pgw_bearer_t *bearer;
} pgw_pf_t;
void pgw_context_init(void);
void pgw_context_final(void);
pgw_context_t *pgw_self(void);
int pgw_context_parse_config(void);
pgw_sess_t *pgw_sess_add_by_message(ogs_gtp_message_t *message);
pgw_sess_t *pgw_sess_add(
uint8_t *imsi, int imsi_len, char *apn,
uint8_t pdn_type, uint8_t ebi, ogs_paa_t *addr);
int pgw_sess_remove(pgw_sess_t *sess);
void pgw_sess_remove_all(void);
pgw_sess_t *pgw_sess_find(uint32_t index);
pgw_sess_t *pgw_sess_find_by_teid(uint32_t teid);
pgw_sess_t *pgw_sess_find_by_imsi_apn(uint8_t *imsi, int imsi_len, char *apn);
pgw_sess_t *pgw_sess_find_by_ipv4(uint32_t addr);
pgw_sess_t *pgw_sess_find_by_ipv6(uint32_t *addr6);
pgw_bearer_t *pgw_bearer_add(pgw_sess_t *sess);
int pgw_bearer_remove(pgw_bearer_t *bearer);
void pgw_bearer_remove_all(pgw_sess_t *sess);
pgw_bearer_t *pgw_bearer_find(uint32_t index);
pgw_bearer_t *pgw_bearer_find_by_pgw_s5u_teid(uint32_t pgw_s5u_teid);
pgw_bearer_t *pgw_bearer_find_by_ebi(pgw_sess_t *sess, uint8_t ebi);
pgw_bearer_t *pgw_bearer_find_by_name(pgw_sess_t *sess, char *name);
pgw_bearer_t *pgw_bearer_find_by_qci_arp(pgw_sess_t *sess,
uint8_t qci,
uint8_t priority_level,
uint8_t pre_emption_capability,
uint8_t pre_emption_vulnerability);
pgw_bearer_t *pgw_default_bearer_in_sess(pgw_sess_t *sess);
pgw_bearer_t *pgw_bearer_first(pgw_sess_t *sess);
pgw_bearer_t *pgw_bearer_next(pgw_bearer_t *bearer);
pgw_pf_t *pgw_pf_add(pgw_bearer_t *bearer, uint32_t precedence);
int pgw_pf_remove(pgw_pf_t *pf);
void pgw_pf_remove_all(pgw_bearer_t *bearer);
pgw_pf_t *pgw_pf_find_by_id(pgw_bearer_t *pgw_bearer, uint8_t id);
pgw_pf_t *pgw_pf_first(pgw_bearer_t *bearer);
pgw_pf_t *pgw_pf_next(pgw_pf_t *pf);
int pgw_ue_pool_generate(void);
pgw_ue_ip_t *pgw_ue_ip_alloc(int family, const char *apn, uint8_t *addr);
int pgw_ue_ip_free(pgw_ue_ip_t *ip);
pgw_dev_t *pgw_dev_add(const char *ifname);
int pgw_dev_remove(pgw_dev_t *dev);
void pgw_dev_remove_all(void);
pgw_dev_t *pgw_dev_find_by_ifname(const char *ifname);
pgw_dev_t *pgw_dev_first(void);
pgw_dev_t *pgw_dev_next(pgw_dev_t *dev);
pgw_subnet_t *pgw_subnet_add(
const char *ipstr, const char *mask_or_numbits,
const char *apn, const char *ifname);
pgw_subnet_t *pgw_subnet_next(pgw_subnet_t *subnet);
int pgw_subnet_remove(pgw_subnet_t *subnet);
void pgw_subnet_remove_all(void);
pgw_subnet_t *pgw_subnet_first(void);
void stats_add_session(void);
void stats_remove_session(void);
#ifdef __cplusplus
}
#endif
#endif /* PGW_CONTEXT_H */

View File

@ -1,117 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-event.h"
#include "pgw-context.h"
#if defined(HAVE_KQUEUE)
/*
* kqueue does not support TUN/TAP character device
* So, PGW should use select action in I/O multiplexing
*/
extern const ogs_pollset_actions_t ogs_select_actions;
extern ogs_pollset_actions_t ogs_pollset_actions;
extern bool ogs_pollset_actions_initialized;
static void pollset_action_setup(void)
{
ogs_pollset_actions = ogs_select_actions;
ogs_pollset_actions_initialized = true;
}
#endif
#define EVENT_POOL 32 /* FIXME : 32 */
static OGS_POOL(pool, pgw_event_t);
void pgw_event_init(void)
{
ogs_pool_init(&pool, EVENT_POOL);
#if defined(HAVE_KQUEUE)
pollset_action_setup();
#endif
pgw_self()->queue = ogs_queue_create(EVENT_POOL);
ogs_assert(pgw_self()->queue);
pgw_self()->timer_mgr = ogs_timer_mgr_create();
ogs_assert(pgw_self()->timer_mgr);
pgw_self()->pollset = ogs_pollset_create();
ogs_assert(pgw_self()->pollset);
}
void pgw_event_term(void)
{
ogs_queue_term(pgw_self()->queue);
ogs_pollset_notify(pgw_self()->pollset);
}
void pgw_event_final(void)
{
if (pgw_self()->pollset)
ogs_pollset_destroy(pgw_self()->pollset);
if (pgw_self()->timer_mgr)
ogs_timer_mgr_destroy(pgw_self()->timer_mgr);
if (pgw_self()->queue)
ogs_queue_destroy(pgw_self()->queue);
ogs_pool_final(&pool);
}
pgw_event_t *pgw_event_new(pgw_event_e id)
{
pgw_event_t *e = NULL;
ogs_pool_alloc(&pool, &e);
ogs_assert(e);
memset(e, 0, sizeof(*e));
e->id = id;
return e;
}
void pgw_event_free(pgw_event_t *e)
{
ogs_assert(e);
ogs_pool_free(&pool, e);
}
const char *pgw_event_get_name(pgw_event_t *e)
{
if (e == NULL)
return OGS_FSM_NAME_INIT_SIG;
switch (e->id) {
case OGS_FSM_ENTRY_SIG:
return OGS_FSM_NAME_ENTRY_SIG;
case OGS_FSM_EXIT_SIG:
return OGS_FSM_NAME_EXIT_SIG;
case PGW_EVT_S5C_MESSAGE:
return "PGW_EVT_S5C_MESSAGE";
case PGW_EVT_GX_MESSAGE:
return "PGW_EVT_GX_MESSAGE";
default:
break;
}
return "UNKNOWN_EVENT";
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_EVENT_H
#define PGW_EVENT_H
#include "ogs-core.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ogs_gtp_node_s ogs_gtp_node_t;
typedef struct ogs_gtp_xact_s ogs_gtp_xact_t;
typedef struct pgw_sess_s pgw_sess_t;
typedef enum {
PGW_EVT_BASE = OGS_FSM_USER_SIG,
PGW_EVT_S5C_MESSAGE,
PGW_EVT_GX_MESSAGE,
PGW_EVT_TOP,
} pgw_event_e;
typedef struct pgw_event_s {
int id;
ogs_pkbuf_t *pkbuf;
ogs_gtp_node_t *gnode;
ogs_gtp_xact_t *xact;
pgw_sess_t *sess;
} pgw_event_t;
void pgw_event_init(void);
void pgw_event_term(void);
void pgw_event_final(void);
pgw_event_t *pgw_event_new(pgw_event_e id);
void pgw_event_free(pgw_event_t *e);
const char *pgw_event_get_name(pgw_event_t *e);
#ifdef __cplusplus
}
#endif
#endif /* PGW_EVENT_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_FD_PATH_H
#define PGW_FD_PATH_H
#include "pgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct gtp_xact_s gtp_xact_t;
int pgw_fd_init(void);
void pgw_fd_final(void);
void pgw_gx_send_ccr(pgw_sess_t *sess, ogs_gtp_xact_t *xact,
uint32_t cc_request_type);
#ifdef __cplusplus
}
#endif
#endif /* PGW_FD_PATH_H */

View File

@ -1,533 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-context.h"
#if HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#if HAVE_NETINET_IP6_H
#include <netinet/ip6.h>
#endif
#if HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#if HAVE_NETINET_ICMP6_H
#include <netinet/icmp6.h>
#endif
#include "pgw-event.h"
#include "pgw-gtp-path.h"
#include "pgw-ipfw.h"
#define PGW_GTP_HANDLED 1
static int pgw_gtp_handle_multicast(ogs_pkbuf_t *recvbuf);
static int pgw_gtp_handle_slaac(pgw_sess_t *sess, ogs_pkbuf_t *recvbuf);
static int pgw_gtp_send_to_bearer(pgw_bearer_t *bearer, ogs_pkbuf_t *sendbuf);
static int pgw_gtp_send_router_advertisement(
pgw_sess_t *sess, uint8_t *ip6_dst);
static void _gtpv1_tun_recv_cb(short when, ogs_socket_t fd, void *data)
{
ogs_pkbuf_t *recvbuf = NULL;
int n;
int rv;
pgw_bearer_t *bearer = NULL;
recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN);
ogs_pkbuf_reserve(recvbuf, OGS_GTPV1U_HEADER_LEN);
ogs_pkbuf_put(recvbuf, OGS_MAX_SDU_LEN-OGS_GTPV1U_HEADER_LEN);
n = ogs_read(fd, recvbuf->data, recvbuf->len);
if (n <= 0) {
ogs_log_message(OGS_LOG_WARN, ogs_socket_errno, "ogs_read() failed");
ogs_pkbuf_free(recvbuf);
return;
}
ogs_pkbuf_trim(recvbuf, n);
/* Find the bearer by packet filter */
bearer = pgw_bearer_find_by_packet(recvbuf);
if (bearer) {
/* Unicast */
rv = pgw_gtp_send_to_bearer(bearer, recvbuf);
ogs_assert(rv == OGS_OK);
} else {
if (ogs_config()->parameter.multicast) {
rv = pgw_gtp_handle_multicast(recvbuf);
ogs_assert(rv != OGS_ERROR);
}
}
ogs_pkbuf_free(recvbuf);
}
static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
{
pgw_event_t *e = NULL;
int rv;
ssize_t size;
ogs_pkbuf_t *pkbuf = NULL;
ogs_sockaddr_t from;
ogs_gtp_node_t *gnode = NULL;
ogs_assert(fd != INVALID_SOCKET);
pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN);
ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN);
size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from);
if (size <= 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"ogs_recvfrom() failed");
ogs_pkbuf_free(pkbuf);
return;
}
ogs_pkbuf_trim(pkbuf, size);
e = pgw_event_new(PGW_EVT_S5C_MESSAGE);
gnode = ogs_gtp_node_find_by_addr(&pgw_self()->sgw_s5c_list, &from);
if (!gnode) {
gnode = ogs_gtp_node_add_by_addr(&pgw_self()->sgw_s5c_list, &from);
ogs_assert(gnode);
gnode->sock = data;
}
ogs_assert(e);
e->gnode = gnode;
e->pkbuf = pkbuf;
rv = ogs_queue_push(pgw_self()->queue, e);
if (rv != OGS_OK) {
ogs_error("ogs_queue_push() failed:%d", (int)rv);
ogs_pkbuf_free(e->pkbuf);
pgw_event_free(e);
}
}
static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
{
int rv;
ssize_t size;
ogs_pkbuf_t *pkbuf = NULL;
int len;
ogs_gtp_header_t *gtp_h = NULL;
struct ip *ip_h = NULL;
uint32_t teid;
pgw_bearer_t *bearer = NULL;
pgw_sess_t *sess = NULL;
pgw_subnet_t *subnet = NULL;
pgw_dev_t *dev = NULL;
ogs_assert(fd != INVALID_SOCKET);
pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN);
ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN);
size = ogs_recv(fd, pkbuf->data, pkbuf->len, 0);
if (size <= 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"ogs_recv() failed");
goto cleanup;
}
ogs_pkbuf_trim(pkbuf, size);
ogs_assert(pkbuf);
ogs_assert(pkbuf->len);
gtp_h = (ogs_gtp_header_t *)pkbuf->data;
teid = be32toh(gtp_h->teid);
ogs_debug("[PGW] RECV GPU-U from SGW : TEID[0x%x]", teid);
/* Remove GTP header and send packets to TUN interface */
len = ogs_gtpu_header_len(pkbuf);
if (len < 0) {
ogs_error("[DROP] Cannot decode GTPU packet");
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
goto cleanup;
}
ogs_assert(ogs_pkbuf_pull(pkbuf, len));
ip_h = (struct ip *)pkbuf->data;
ogs_assert(ip_h);
bearer = pgw_bearer_find_by_pgw_s5u_teid(teid);
if (!bearer) {
ogs_warn("[DROP] Cannot find PGW S5U bearer : TEID[0x%x]", teid);
goto cleanup;
}
sess = bearer->sess;
ogs_assert(sess);
if (ip_h->ip_v == 4 && sess->ipv4)
subnet = sess->ipv4->subnet;
else if (ip_h->ip_v == 6 && sess->ipv6)
subnet = sess->ipv6->subnet;
if (!subnet) {
ogs_log_hexdump(OGS_LOG_TRACE, pkbuf->data, pkbuf->len);
ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p",
ip_h->ip_v, sess->ipv4, sess->ipv6);
goto cleanup;
}
/* Check IPv6 */
if (ogs_config()->parameter.no_slaac == 0 && ip_h->ip_v == 6) {
rv = pgw_gtp_handle_slaac(sess, pkbuf);
if (rv == PGW_GTP_HANDLED) {
goto cleanup;
}
ogs_assert(rv == OGS_OK);
}
dev = subnet->dev;
ogs_assert(dev);
if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0)
ogs_error("ogs_write() failed");
cleanup:
ogs_pkbuf_free(pkbuf);
}
int pgw_gtp_open(void)
{
pgw_dev_t *dev = NULL;
pgw_subnet_t *subnet = NULL;
ogs_socknode_t *node = NULL;
ogs_sock_t *sock = NULL;
int rc;
ogs_list_for_each(&pgw_self()->gtpc_list, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(pgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
}
ogs_list_for_each(&pgw_self()->gtpc_list6, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(pgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
}
pgw_self()->gtpc_sock = ogs_socknode_sock_first(&pgw_self()->gtpc_list);
if (pgw_self()->gtpc_sock)
pgw_self()->gtpc_addr = &pgw_self()->gtpc_sock->local_addr;
pgw_self()->gtpc_sock6 = ogs_socknode_sock_first(&pgw_self()->gtpc_list6);
if (pgw_self()->gtpc_sock6)
pgw_self()->gtpc_addr6 = &pgw_self()->gtpc_sock6->local_addr;
ogs_assert(pgw_self()->gtpc_addr || pgw_self()->gtpc_addr6);
ogs_list_for_each(&pgw_self()->gtpu_list, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(pgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv1_u_recv_cb, sock);
}
ogs_list_for_each(&pgw_self()->gtpu_list6, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(pgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv1_u_recv_cb, sock);
}
pgw_self()->gtpu_sock = ogs_socknode_sock_first(&pgw_self()->gtpu_list);
if (pgw_self()->gtpu_sock)
pgw_self()->gtpu_addr = &pgw_self()->gtpu_sock->local_addr;
pgw_self()->gtpu_sock6 = ogs_socknode_sock_first(&pgw_self()->gtpu_list6);
if (pgw_self()->gtpu_sock6)
pgw_self()->gtpu_addr6 = &pgw_self()->gtpu_sock6->local_addr;
ogs_assert(pgw_self()->gtpu_addr || pgw_self()->gtpu_addr6);
/* NOTE : tun device can be created via following command.
*
* $ sudo ip tuntap add name ogstun mode tun
*
* Also, before running pgw, assign the one IP from IP pool of UE
* to ogstun. The IP should not be assigned to UE
*
* $ sudo ifconfig ogstun 10.45.0.1/16 up
*
*/
/* Open Tun interface */
for (dev = pgw_dev_first(); dev; dev = pgw_dev_next(dev)) {
dev->fd = ogs_tun_open(dev->ifname, IFNAMSIZ, 0);
if (dev->fd == INVALID_SOCKET) {
ogs_error("tun_open(dev:%s) failed", dev->ifname);
return OGS_ERROR;
}
dev->poll = ogs_pollset_add(pgw_self()->pollset,
OGS_POLLIN, dev->fd, _gtpv1_tun_recv_cb, NULL);
ogs_assert(dev->poll);
}
/*
* On Linux, it is possible to create a persistent tun/tap
* interface which will continue to exist even if open5gs quit,
* although this is normally not required.
* It can be useful to set up a tun/tap interface owned
* by a non-root user, so open5gs can be started without
* needing any root privileges at all.
*/
/* Set P-to-P IP address with Netmask
* Note that Linux will skip this configuration */
for (subnet = pgw_subnet_first();
subnet; subnet = pgw_subnet_next(subnet)) {
ogs_assert(subnet->dev);
rc = ogs_tun_set_ip(subnet->dev->ifname, &subnet->gw, &subnet->sub);
if (rc != OGS_OK) {
ogs_error("ogs_tun_set_ip(dev:%s) failed", subnet->dev->ifname);
return OGS_ERROR;
}
}
/* Link-Local Address for PGW_TUN */
for (dev = pgw_dev_first(); dev; dev = pgw_dev_next(dev))
dev->link_local_addr = ogs_link_local_addr_by_dev(dev->ifname);
return OGS_OK;
}
void pgw_gtp_close(void)
{
pgw_dev_t *dev = NULL;
ogs_socknode_remove_all(&pgw_self()->gtpc_list);
ogs_socknode_remove_all(&pgw_self()->gtpc_list6);
ogs_socknode_remove_all(&pgw_self()->gtpu_list);
ogs_socknode_remove_all(&pgw_self()->gtpu_list6);
for (dev = pgw_dev_first(); dev; dev = pgw_dev_next(dev)) {
ogs_pollset_remove(dev->poll);
ogs_closesocket(dev->fd);
}
}
static int pgw_gtp_handle_multicast(ogs_pkbuf_t *recvbuf)
{
int rv;
struct ip *ip_h = NULL;
struct ip6_hdr *ip6_h = NULL;
ip_h = (struct ip *)recvbuf->data;
if (ip_h->ip_v == 6) {
#if COMPILE_ERROR_IN_MAC_OS_X /* Compiler error in Mac OS X platform */
ip6_h = (struct ip6_hdr *)recvbuf->data;
if (IN6_IS_ADDR_MULTICAST(&ip6_h->ip6_dst))
#else
struct in6_addr ip6_dst;
ip6_h = (struct ip6_hdr *)recvbuf->data;
memcpy(&ip6_dst, &ip6_h->ip6_dst, sizeof(struct in6_addr));
if (IN6_IS_ADDR_MULTICAST(&ip6_dst))
#endif
{
pgw_sess_t *sess = NULL;
/* IPv6 Multicast */
ogs_list_for_each(&pgw_self()->sess_list, sess) {
if (sess->ipv6) {
/* PDN IPv6 is avaiable */
pgw_bearer_t *bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(bearer);
rv = pgw_gtp_send_to_bearer(bearer, recvbuf);
ogs_assert(rv == OGS_OK);
return PGW_GTP_HANDLED;
}
}
}
}
return OGS_OK;
}
static int pgw_gtp_handle_slaac(pgw_sess_t *sess, ogs_pkbuf_t *recvbuf)
{
int rv;
struct ip *ip_h = NULL;
ogs_assert(sess);
ogs_assert(recvbuf);
ogs_assert(recvbuf->len);
ip_h = (struct ip *)recvbuf->data;
if (ip_h->ip_v == 6) {
struct ip6_hdr *ip6_h = (struct ip6_hdr *)recvbuf->data;
if (ip6_h->ip6_nxt == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp_h =
(struct icmp6_hdr *)(recvbuf->data + sizeof(struct ip6_hdr));
if (icmp_h->icmp6_type == ND_ROUTER_SOLICIT) {
ogs_debug("[PGW] Router Solict");
if (sess->ipv6) {
rv = pgw_gtp_send_router_advertisement(
sess, ip6_h->ip6_src.s6_addr);
ogs_assert(rv == OGS_OK);
}
return PGW_GTP_HANDLED;
}
}
}
return OGS_OK;
}
static int pgw_gtp_send_to_bearer(pgw_bearer_t *bearer, ogs_pkbuf_t *sendbuf)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ogs_gtp_header_t *gtp_h = NULL;
ogs_assert(bearer);
ogs_assert(bearer->gnode);
ogs_assert(bearer->gnode->sock);
/* Add GTP-U header */
ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_HEADER_LEN));
gtp_h = (ogs_gtp_header_t *)sendbuf->data;
/* Bits 8 7 6 5 4 3 2 1
* +--+--+--+--+--+--+--+--+
* |version |PT| 1| E| S|PN|
* +--+--+--+--+--+--+--+--+
* 0 0 1 1 0 0 0 0
*/
gtp_h->flags = 0x30;
gtp_h->type = OGS_GTPU_MSGTYPE_GPDU;
gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN);
gtp_h->teid = htobe32(bearer->sgw_s5u_teid);
/* Send to SGW */
ogs_debug("[PGW] SEND GPU-U to SGW[%s] : TEID[0x%x]",
OGS_ADDR(&bearer->gnode->addr, buf),
bearer->sgw_s5u_teid);
rv = ogs_gtp_sendto(bearer->gnode, sendbuf);
return rv;
}
static int pgw_gtp_send_router_advertisement(
pgw_sess_t *sess, uint8_t *ip6_dst)
{
int rv;
ogs_pkbuf_t *pkbuf = NULL;
pgw_bearer_t *bearer = NULL;
pgw_ue_ip_t *ue_ip = NULL;
pgw_subnet_t *subnet = NULL;
pgw_dev_t *dev = NULL;
ogs_ipsubnet_t src_ipsub;
uint16_t plen = 0;
uint8_t nxt = 0;
uint8_t *p = NULL;
struct ip6_hdr *ip6_h = NULL;
struct nd_router_advert *advert_h = NULL;
struct nd_opt_prefix_info *prefix = NULL;
ogs_assert(sess);
bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(bearer);
ue_ip = sess->ipv6;
ogs_assert(ue_ip);
subnet = ue_ip->subnet;
ogs_assert(subnet);
dev = subnet->dev;
ogs_assert(dev);
pkbuf = ogs_pkbuf_alloc(NULL, OGS_GTPV1U_HEADER_LEN+200);
ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_HEADER_LEN);
ogs_pkbuf_put(pkbuf, 200);
pkbuf->len = sizeof *ip6_h + sizeof *advert_h + sizeof *prefix;
memset(pkbuf->data, 0, pkbuf->len);
p = (uint8_t *)pkbuf->data;
ip6_h = (struct ip6_hdr *)p;
advert_h = (struct nd_router_advert *)((uint8_t *)ip6_h + sizeof *ip6_h);
prefix = (struct nd_opt_prefix_info *)
((uint8_t*)advert_h + sizeof *advert_h);
rv = ogs_ipsubnet(&src_ipsub, "fe80::1", NULL);
ogs_assert(rv == OGS_OK);
if (dev->link_local_addr)
memcpy(src_ipsub.sub, dev->link_local_addr->sin6.sin6_addr.s6_addr,
sizeof src_ipsub.sub);
advert_h->nd_ra_type = ND_ROUTER_ADVERT;
advert_h->nd_ra_code = 0;
advert_h->nd_ra_curhoplimit = 64;
advert_h->nd_ra_flags_reserved = 0;
advert_h->nd_ra_router_lifetime = htobe16(64800); /* 64800s */
advert_h->nd_ra_reachable = 0;
advert_h->nd_ra_retransmit = 0;
prefix->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
prefix->nd_opt_pi_len = 4; /* 32bytes */
prefix->nd_opt_pi_prefix_len = subnet->prefixlen;
prefix->nd_opt_pi_flags_reserved =
ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO;
prefix->nd_opt_pi_valid_time = htobe32(0xffffffff); /* Infinite */
prefix->nd_opt_pi_preferred_time = htobe32(0xffffffff); /* Infinite */
memcpy(prefix->nd_opt_pi_prefix.s6_addr,
subnet->sub.sub, sizeof prefix->nd_opt_pi_prefix.s6_addr);
/* For IPv6 Pseudo-Header */
plen = htobe16(sizeof *advert_h + sizeof *prefix);
nxt = IPPROTO_ICMPV6;
memcpy(p, src_ipsub.sub, sizeof src_ipsub.sub);
p += sizeof src_ipsub.sub;
memcpy(p, ip6_dst, OGS_IPV6_LEN);
p += OGS_IPV6_LEN;
p += 2; memcpy(p, &plen, 2); p += 2;
p += 3; *p = nxt; p += 1;
advert_h->nd_ra_cksum = ogs_in_cksum((uint16_t *)pkbuf->data, pkbuf->len);
ip6_h->ip6_flow = htobe32(0x60000001);
ip6_h->ip6_plen = plen;
ip6_h->ip6_nxt = nxt; /* ICMPv6 */
ip6_h->ip6_hlim = 0xff;
memcpy(ip6_h->ip6_src.s6_addr, src_ipsub.sub, sizeof src_ipsub.sub);
memcpy(ip6_h->ip6_dst.s6_addr, ip6_dst, OGS_IPV6_LEN);
rv = pgw_gtp_send_to_bearer(bearer, pkbuf);
ogs_assert(rv == OGS_OK);
ogs_debug("[PGW] Router Advertisement");
ogs_pkbuf_free(pkbuf);
return rv;
}

View File

@ -1,37 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_GTP_PATH_H
#define PGW_GTP_PATH_H
#include "ogs-tun.h"
#include "ogs-gtp.h"
#ifdef __cplusplus
extern "C" {
#endif
int pgw_gtp_open(void);
void pgw_gtp_close(void);
#ifdef __cplusplus
}
#endif
#endif /* PGW_GTP_PATH_H */

View File

@ -1,420 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-context.h"
#include "pgw-gtp-path.h"
#include "pgw-s5c-build.h"
#include "pgw-gx-handler.h"
#include "pgw-ipfw.h"
#include "ipfw/ipfw2.h"
static void encode_traffic_flow_template(
ogs_gtp_tft_t *tft, pgw_bearer_t *bearer);
static void bearer_binding(pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message);
static void timeout(ogs_gtp_xact_t *xact, void *data)
{
pgw_sess_t *sess = data;
uint8_t type = 0;
ogs_assert(sess);
type = xact->seq[0].type;
ogs_debug("GTP Timeout : SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x] "
"Message-Type[%d]", sess->sgw_s5c_teid, sess->pgw_s5c_teid, type);
}
static uint8_t gtp_cause_from_diameter(
const uint32_t *dia_err, const uint32_t *dia_exp_err)
{
if (dia_exp_err) {
}
if (dia_err) {
switch (*dia_err) {
case OGS_DIAM_UNKNOWN_SESSION_ID:
return OGS_GTP_CAUSE_APN_ACCESS_DENIED_NO_SUBSCRIPTION;
}
}
ogs_error("Unexpected Diameter Result Code %d/%d, defaulting to severe "
"network failure",
dia_err ? *dia_err : -1, dia_exp_err ? *dia_exp_err : -1);
return OGS_GTP_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER;
}
void pgw_gx_handle_cca_initial_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message,
ogs_gtp_xact_t *xact)
{
int rv;
ogs_gtp_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
ogs_assert(sess);
ogs_assert(gx_message);
ogs_assert(xact);
ogs_debug("[PGW] Create Session Response");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
if (gx_message->result_code != ER_DIAMETER_SUCCESS) {
uint8_t cause_value = gtp_cause_from_diameter(
gx_message->err, gx_message->exp_err);
ogs_gtp_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
memset(&h, 0, sizeof(ogs_gtp_header_t));
h.type = OGS_GTP_CREATE_SESSION_RESPONSE_TYPE;
h.teid = sess->sgw_s5c_teid;
pkbuf = pgw_s5c_build_create_session_response(
h.type, sess, gx_message);
ogs_expect_or_return(pkbuf);
rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
bearer_binding(sess, gx_message);
}
void pgw_gx_handle_cca_termination_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message,
ogs_gtp_xact_t *xact)
{
int rv;
ogs_gtp_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
uint32_t sgw_s5c_teid;
ogs_assert(xact);
ogs_assert(sess);
ogs_assert(gx_message);
/* backup sgw_s5c_teid in session context */
sgw_s5c_teid = sess->sgw_s5c_teid;
ogs_debug("[PGW] Delete Session Response");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
/* Remove a pgw session */
pgw_sess_remove(sess);
if (gx_message->result_code != ER_DIAMETER_SUCCESS) {
uint8_t cause_value = gtp_cause_from_diameter(
gx_message->err, gx_message->exp_err);
ogs_gtp_send_error_message(xact, sgw_s5c_teid,
OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
memset(&h, 0, sizeof(ogs_gtp_header_t));
h.type = OGS_GTP_DELETE_SESSION_RESPONSE_TYPE;
h.teid = sgw_s5c_teid;
pkbuf = pgw_s5c_build_delete_session_response(
h.type, sess, gx_message);
ogs_expect_or_return(pkbuf);
rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
}
void pgw_gx_handle_re_auth_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message)
{
bearer_binding(sess, gx_message);
}
static void encode_traffic_flow_template(
ogs_gtp_tft_t *tft, pgw_bearer_t *bearer)
{
int i, j, len;
pgw_pf_t *pf = NULL;
ogs_assert(tft);
ogs_assert(bearer);
memset(tft, 0, sizeof(*tft));
tft->code = OGS_GTP_TFT_CODE_CREATE_NEW_TFT;
i = 0;
pf = pgw_pf_first(bearer);
while (pf) {
tft->pf[i].direction = pf->direction;
tft->pf[i].identifier = pf->identifier - 1;
tft->pf[i].precedence = i+1;
j = 0, len = 0;
if (pf->rule.proto) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE;
tft->pf[i].component[j].proto = pf->rule.proto;
j++; len += 2;
}
if (pf->rule.ipv4_local) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE;
tft->pf[i].component[j].ipv4.addr = pf->rule.ip.local.addr[0];
tft->pf[i].component[j].ipv4.mask = pf->rule.ip.local.mask[0];
j++; len += 9;
}
if (pf->rule.ipv4_remote) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE;
tft->pf[i].component[j].ipv4.addr = pf->rule.ip.remote.addr[0];
tft->pf[i].component[j].ipv4.mask = pf->rule.ip.remote.mask[0];
j++; len += 9;
}
if (pf->rule.ipv6_local) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE;
memcpy(tft->pf[i].component[j].ipv6.addr, pf->rule.ip.local.addr,
sizeof pf->rule.ip.local.addr);
tft->pf[i].component[j].ipv6.prefixlen =
contigmask((uint8_t *)pf->rule.ip.local.mask, 128);
j++; len += 18;
}
if (pf->rule.ipv6_remote) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE;
memcpy(tft->pf[i].component[j].ipv6.addr, pf->rule.ip.remote.addr,
sizeof pf->rule.ip.remote.addr);
tft->pf[i].component[j].ipv6.prefixlen =
contigmask((uint8_t *)pf->rule.ip.remote.mask, 128);
j++; len += 18;
}
if (pf->rule.port.local.low) {
if (pf->rule.port.local.low == pf->rule.port.local.high)
{
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE;
tft->pf[i].component[j].port.low = pf->rule.port.local.low;
j++; len += 3;
} else {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE;
tft->pf[i].component[j].port.low = pf->rule.port.local.low;
tft->pf[i].component[j].port.high = pf->rule.port.local.high;
j++; len += 5;
}
}
if (pf->rule.port.remote.low) {
if (pf->rule.port.remote.low == pf->rule.port.remote.high) {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE;
tft->pf[i].component[j].port.low = pf->rule.port.remote.low;
j++; len += 3;
} else {
tft->pf[i].component[j].type =
GTP_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE;
tft->pf[i].component[j].port.low = pf->rule.port.remote.low;
tft->pf[i].component[j].port.high = pf->rule.port.remote.high;
j++; len += 5;
}
}
tft->pf[i].num_of_component = j;
tft->pf[i].length = len;
i++;
pf = pgw_pf_next(pf);
}
tft->num_of_packet_filter = i;
}
static void bearer_binding(pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message)
{
int rv;
int i, j;
ogs_assert(sess);
ogs_assert(gx_message);
for (i = 0; i < gx_message->num_of_pcc_rule; i++) {
ogs_gtp_xact_t *xact = NULL;
ogs_gtp_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
pgw_bearer_t *bearer = NULL;
ogs_pcc_rule_t *pcc_rule = &gx_message->pcc_rule[i];
int bearer_created = 0;
int qos_presence = 0;
ogs_gtp_tft_t tft;
ogs_assert(pcc_rule);
if (pcc_rule->name == NULL) {
ogs_error("No PCC Rule Name");
continue;
}
if (pcc_rule->type == OGS_PCC_RULE_TYPE_INSTALL) {
bearer = pgw_bearer_find_by_qci_arp(sess,
pcc_rule->qos.qci,
pcc_rule->qos.arp.priority_level,
pcc_rule->qos.arp.pre_emption_capability,
pcc_rule->qos.arp.pre_emption_vulnerability);
if (!bearer) {
if (pcc_rule->num_of_flow == 0) {
/* TFT is mandatory in
* activate dedicated EPS bearer context request */
ogs_error("No flow in PCC Rule");
continue;
}
bearer = pgw_bearer_add(sess);
ogs_assert(bearer);
bearer->name = ogs_strdup(pcc_rule->name);
ogs_assert(bearer->name);
memcpy(&bearer->qos, &pcc_rule->qos, sizeof(ogs_qos_t));
bearer_created = 1;
} else {
ogs_assert(strcmp(bearer->name, pcc_rule->name) == 0);
if (pcc_rule->num_of_flow) {
/* We'll use always 'Create new TFT'.
* Therefore, all previous flows are removed
* and replaced by the new flow */
pgw_pf_remove_all(bearer);
}
if ((pcc_rule->qos.mbr.downlink &&
bearer->qos.mbr.downlink != pcc_rule->qos.mbr.downlink) ||
(pcc_rule->qos.mbr.uplink &&
bearer->qos.mbr.uplink != pcc_rule->qos.mbr.uplink) ||
(pcc_rule->qos.gbr.downlink &&
bearer->qos.gbr.downlink != pcc_rule->qos.gbr.downlink) ||
(pcc_rule->qos.gbr.uplink &&
bearer->qos.gbr.uplink != pcc_rule->qos.gbr.uplink)) {
/* Update QoS parameter */
memcpy(&bearer->qos, &pcc_rule->qos, sizeof(ogs_qos_t));
/* Update Bearer Request encodes updated QoS parameter */
qos_presence = 1;
}
if (pcc_rule->num_of_flow == 0 && qos_presence == 0) {
ogs_warn("No need to send 'Update Bearer Request'");
ogs_warn(" - Both QoS and TFT are same as before");
continue;
}
}
for (j = 0; j < pcc_rule->num_of_flow; j++) {
ogs_flow_t *flow = &pcc_rule->flow[j];
pgw_rule_t rule;
pgw_pf_t *pf = NULL;
ogs_expect_or_return(flow);
ogs_expect_or_return(flow->description);
rv = pgw_compile_packet_filter(&rule, flow->description);
ogs_expect_or_return(rv == OGS_OK);
pf = pgw_pf_add(bearer, pcc_rule->precedence);
ogs_expect_or_return(pf);
memcpy(&pf->rule, &rule, sizeof(pgw_rule_t));
pf->direction = flow->direction;
}
memset(&tft, 0, sizeof tft);
if (pcc_rule->num_of_flow)
encode_traffic_flow_template(&tft, bearer);
memset(&h, 0, sizeof(ogs_gtp_header_t));
if (bearer_created == 1) {
h.type = OGS_GTP_CREATE_BEARER_REQUEST_TYPE;
h.teid = sess->sgw_s5c_teid;
/* TFT is mandatory in
* activate dedicated EPS bearer context request */
ogs_assert(pcc_rule->num_of_flow);
pkbuf = pgw_s5c_build_create_bearer_request(
h.type, bearer, pcc_rule->num_of_flow ? &tft : NULL);
ogs_expect_or_return(pkbuf);
} else {
h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE;
h.teid = sess->sgw_s5c_teid;
pkbuf = pgw_s5c_build_update_bearer_request(
h.type, bearer,
OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED,
pcc_rule->num_of_flow ? &tft : NULL, qos_presence);
ogs_expect_or_return(pkbuf);
}
xact = ogs_gtp_xact_local_create(
sess->gnode, &h, pkbuf, timeout, sess);
ogs_expect_or_return(xact);
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
} else if (pcc_rule->type == OGS_PCC_RULE_TYPE_REMOVE) {
bearer = pgw_bearer_find_by_name(sess, pcc_rule->name);
if (!bearer) {
ogs_warn("No need to send 'Delete Bearer Request'");
ogs_warn(" - Bearer[Name:%s] has already been removed.",
pcc_rule->name);
return;
}
memset(&h, 0, sizeof(ogs_gtp_header_t));
h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE;
h.teid = sess->sgw_s5c_teid;
pkbuf = pgw_s5c_build_delete_bearer_request(h.type, bearer,
OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED);
ogs_expect_or_return(pkbuf);
xact = ogs_gtp_xact_local_create(
sess->gnode, &h, pkbuf, timeout, sess);
ogs_expect_or_return(xact);
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
} else {
ogs_error("Invalid Type[%d]", pcc_rule->type);
}
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_GX_HANDLER_H
#define PGW_GX_HANDLER_H
#include "pgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
void pgw_gx_handle_cca_initial_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message,
ogs_gtp_xact_t *xact);
void pgw_gx_handle_cca_termination_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message,
ogs_gtp_xact_t *xact);
void pgw_gx_handle_re_auth_request(
pgw_sess_t *sess, ogs_diam_gx_message_t *gx_message);
#ifdef __cplusplus
}
#endif
#endif /* PGW_GX_HANDLER_H */

View File

@ -1,125 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-context.h"
#include "pgw-event.h"
#include "pgw-sm.h"
#include "pgw-fd-path.h"
static ogs_thread_t *thread;
static void pgw_main(void *data);
static int initialized = 0;
int pgw_initialize()
{
int rv;
pgw_context_init();
pgw_event_init();
rv = ogs_gtp_xact_init(pgw_self()->timer_mgr, 512);
if (rv != OGS_OK) return rv;
rv = pgw_context_parse_config();
if (rv != OGS_OK) return rv;
rv = ogs_log_config_domain(
ogs_config()->logger.domain, ogs_config()->logger.level);
if (rv != OGS_OK) return rv;
rv = pgw_ue_pool_generate();
if (rv != OGS_OK) return rv;
rv = pgw_fd_init();
if (rv != 0) return OGS_ERROR;
thread = ogs_thread_create(pgw_main, NULL);
if (!thread) return OGS_ERROR;
initialized = 1;
return OGS_OK;
}
void pgw_terminate(void)
{
if (!initialized) return;
pgw_event_term();
ogs_thread_destroy(thread);
pgw_fd_final();
pgw_context_final();
ogs_gtp_xact_final();
pgw_event_final();
}
static void pgw_main(void *data)
{
ogs_fsm_t pgw_sm;
int rv;
ogs_fsm_create(&pgw_sm, pgw_state_initial, pgw_state_final);
ogs_fsm_init(&pgw_sm, 0);
for ( ;; ) {
ogs_pollset_poll(pgw_self()->pollset,
ogs_timer_mgr_next(pgw_self()->timer_mgr));
/*
* After ogs_pollset_poll(), ogs_timer_mgr_expire() must be called.
*
* The reason is why ogs_timer_mgr_next() can get the corrent value
* when ogs_timer_stop() is called internally in ogs_timer_mgr_expire().
*
* You should not use event-queue before ogs_timer_mgr_expire().
* In this case, ogs_timer_mgr_expire() does not work
* because 'if rv == OGS_DONE' statement is exiting and
* not calling ogs_timer_mgr_expire().
*/
ogs_timer_mgr_expire(pgw_self()->timer_mgr);
for ( ;; ) {
pgw_event_t *e = NULL;
rv = ogs_queue_trypop(pgw_self()->queue, (void**)&e);
ogs_assert(rv != OGS_ERROR);
if (rv == OGS_DONE)
goto done;
if (rv == OGS_RETRY)
break;
ogs_assert(e);
ogs_fsm_dispatch(&pgw_sm, e);
pgw_event_free(e);
}
}
done:
ogs_fsm_fini(&pgw_sm, 0);
ogs_fsm_delete(&pgw_sm);
}

View File

@ -1,454 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define _DEFAULT_SOURCE 1
#define _BSD_SOURCE 1
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include "pgw-ipfw.h"
#include "ipfw/ipfw2.h"
#include "ipfw/objs/include_e/netinet/ip_fw.h"
#define MAX_NUM_OF_TOKEN 32
#define MAX_NUM_OF_RULE_BUFFER 1024
void compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, void *tstate);
int pgw_compile_packet_filter(pgw_rule_t *pgw_rule, char *description)
{
pgw_rule_t zero_rule;
char *token, *dir;
char *saveptr;
int i = 2;
char *av[MAX_NUM_OF_TOKEN];
uint32_t rulebuf[MAX_NUM_OF_RULE_BUFFER];
int rbufsize;
struct ip_fw_rule *rule = (struct ip_fw_rule *)rulebuf;
int l;
ipfw_insn *cmd;
ogs_assert(pgw_rule);
rbufsize = sizeof(rulebuf);
memset(rulebuf, 0, rbufsize);
av[0] = NULL;
/* ACTION */
if (!description) { /* FIXME : OLD gcc generates uninitialized warning */
ogs_assert_if_reached();
return OGS_ERROR;
}
token = strtok_r(description, " ", &saveptr);
if (strcmp(token, "permit") != 0) {
ogs_error("Not begins with reserved keyword : 'permit'");
return OGS_ERROR;
}
av[1] = token;
/* Save DIRECTION */
dir = token = strtok_r(NULL, " ", &saveptr);
if (strcmp(token, "out") != 0) {
ogs_error("Not begins with reserved keyword : 'permit out'");
return OGS_ERROR;
}
/* ADDR */
token = strtok_r(NULL, " ", &saveptr);
while (token != NULL) {
av[i++] = token;
token = strtok_r(NULL, " ", &saveptr);
}
/* Add DIRECTION */
av[i++] = dir;
av[i] = NULL;
compile_rule(av, (uint32_t *)rule, &rbufsize, NULL);
memset(pgw_rule, 0, sizeof(pgw_rule_t));
for (l = rule->act_ofs, cmd = rule->cmd;
l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
uint32_t *a = NULL;
uint16_t *p = NULL;
switch (cmd->opcode) {
case O_PROTO:
pgw_rule->proto = cmd->arg1;
break;
case O_IP_SRC:
case O_IP_SRC_MASK:
a = ((ipfw_insn_u32 *)cmd)->d;
pgw_rule->ipv4_local = 1;
pgw_rule->ip.local.addr[0] = a[0];
if (cmd->opcode == O_IP_SRC_MASK)
pgw_rule->ip.local.mask[0] = a[1];
else
pgw_rule->ip.local.mask[0] = 0xffffffff;
break;
case O_IP_DST:
case O_IP_DST_MASK:
a = ((ipfw_insn_u32 *)cmd)->d;
pgw_rule->ipv4_remote = 1;
pgw_rule->ip.remote.addr[0] = a[0];
if (cmd->opcode == O_IP_DST_MASK)
pgw_rule->ip.remote.mask[0] = a[1];
else
pgw_rule->ip.remote.mask[0] = 0xffffffff;
break;
case O_IP6_SRC:
case O_IP6_SRC_MASK:
a = ((ipfw_insn_u32 *)cmd)->d;
pgw_rule->ipv6_local = 1;
memcpy(pgw_rule->ip.local.addr, a, OGS_IPV6_LEN);
if (cmd->opcode == O_IP6_SRC_MASK)
memcpy(pgw_rule->ip.local.mask, a+4, OGS_IPV6_LEN);
else
n2mask((struct in6_addr *)pgw_rule->ip.local.mask, 128);
break;
case O_IP6_DST:
case O_IP6_DST_MASK:
a = ((ipfw_insn_u32 *)cmd)->d;
pgw_rule->ipv6_remote = 1;
memcpy(pgw_rule->ip.remote.addr, a, OGS_IPV6_LEN);
if (cmd->opcode == O_IP6_DST_MASK)
memcpy(pgw_rule->ip.remote.mask, a+4, OGS_IPV6_LEN);
else
n2mask((struct in6_addr *)pgw_rule->ip.remote.mask, 128);
break;
case O_IP_SRCPORT:
p = ((ipfw_insn_u16 *)cmd)->ports;
pgw_rule->port.local.low = p[0];
pgw_rule->port.local.high = p[1];
break;
case O_IP_DSTPORT:
p = ((ipfw_insn_u16 *)cmd)->ports;
pgw_rule->port.remote.low = p[0];
pgw_rule->port.remote.high = p[1];
break;
}
}
memset(&zero_rule, 0, sizeof(pgw_rule_t));
if (memcmp(pgw_rule, &zero_rule, sizeof(pgw_rule_t)) == 0) {
ogs_error("Cannot find Flow-Description");
return OGS_ERROR;
}
return OGS_OK;
}
static int decode_ipv6_header(
struct ip6_hdr *ip6_h, uint8_t *proto, uint16_t *hlen)
{
int done = 0;
uint8_t *p, *jp, *endp;
uint8_t nxt; /* Next Header */
ogs_assert(ip6_h);
ogs_assert(proto);
ogs_assert(hlen);
nxt = ip6_h->ip6_nxt;
p = (uint8_t *)ip6_h + sizeof(*ip6_h);
endp = p + be16toh(ip6_h->ip6_plen);
jp = p + sizeof(struct ip6_hbh);
while (p == endp) { /* Jumbo Frame */
uint32_t jp_len = 0;
struct ip6_opt_jumbo *jumbo = NULL;
ogs_assert(nxt == 0);
jumbo = (struct ip6_opt_jumbo *)jp;
memcpy(&jp_len, jumbo->ip6oj_jumbo_len, sizeof(jp_len));
jp_len = be32toh(jp_len);
switch (jumbo->ip6oj_type) {
case IP6OPT_JUMBO:
endp = p + jp_len;
break;
case 0:
jp++;
break;
default:
jp += (sizeof(struct ip6_opt) + jp_len);
break;
}
}
while (p < endp) {
struct ip6_ext *ext = (struct ip6_ext *)p;
switch (nxt) {
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
case 135: /* mobility */
case 139: /* host identity, experimental */
case 140: /* shim6 */
case 253: /* testing, experimental */
case 254: /* testing, experimental */
p += ((ext->ip6e_len << 3) + 8);
break;
case IPPROTO_FRAGMENT:
p += sizeof(struct ip6_frag);
break;
case IPPROTO_AH:
p += ((ext->ip6e_len + 2) << 2);
break;
default: /* Upper Layer */
done = 1;
break;
}
if (done)
break;
nxt = ext->ip6e_nxt;
}
*proto = nxt;
*hlen = p - (uint8_t *)ip6_h;
return OGS_OK;
}
pgw_bearer_t *pgw_bearer_find_by_packet(ogs_pkbuf_t *pkt)
{
struct ip *ip_h = NULL;
struct ip6_hdr *ip6_h = NULL;
uint32_t *src_addr = NULL;
uint32_t *dst_addr = NULL;
int addr_len = 0;
uint8_t proto = 0;
uint16_t ip_hlen = 0;
char buf[OGS_ADDRSTRLEN];
pgw_sess_t *sess = NULL;
ogs_assert(pkt);
ogs_assert(pkt->len);
ip_h = (struct ip *)pkt->data;
if (ip_h->ip_v == 4) {
ip_h = (struct ip *)pkt->data;
ip6_h = NULL;
proto = ip_h->ip_p;
ip_hlen = (ip_h->ip_hl)*4;
src_addr = &ip_h->ip_src.s_addr;
dst_addr = &ip_h->ip_dst.s_addr;
addr_len = OGS_IPV4_LEN;
sess = pgw_sess_find_by_ipv4(dst_addr[0]);
} else if (ip_h->ip_v == 6) {
ip_h = NULL;
ip6_h = (struct ip6_hdr *)pkt->data;
decode_ipv6_header(ip6_h, &proto, &ip_hlen);
src_addr = (uint32_t *)ip6_h->ip6_src.s6_addr;
dst_addr = (uint32_t *)ip6_h->ip6_dst.s6_addr;
addr_len = OGS_IPV6_LEN;
sess = pgw_sess_find_by_ipv6(dst_addr);
} else
ogs_error("Invalid IP version = %d", ip_h->ip_v);
ogs_debug("[PGW] PROTO:%d SRC:%08x %08x %08x %08x",
proto, be32toh(src_addr[0]), be32toh(src_addr[1]),
be32toh(src_addr[2]), be32toh(src_addr[3]));
ogs_debug("[PGW] HLEN:%d DST:%08x %08x %08x %08x",
ip_hlen, be32toh(dst_addr[0]), be32toh(dst_addr[1]),
be32toh(dst_addr[2]), be32toh(dst_addr[3]));
if (sess) {
pgw_bearer_t *default_bearer = NULL;
pgw_bearer_t *bearer = NULL;
if (ip_h && sess->ipv4)
ogs_debug("[PGW] PAA IPv4:%s",
OGS_INET_NTOP(&sess->ipv4->addr, buf));
if (ip6_h && sess->ipv6)
ogs_debug("[PGW] PAA IPv6:%s",
OGS_INET6_NTOP(&sess->ipv6->addr, buf));
/* Save the default bearer */
default_bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(default_bearer);
/* Found */
ogs_debug("[PGW] Found Session : EBI[%d]", default_bearer->ebi);
bearer = pgw_bearer_next(default_bearer);
/* Find the bearer with matched */
for (; bearer; bearer = pgw_bearer_next(bearer)) {
pgw_pf_t *pf = NULL;
if (bearer->ebi == 0) {
/* Create Bearer Response is not received */
continue;
}
for (pf = pgw_pf_first(bearer); pf; pf = pgw_pf_next(pf)) {
int k;
uint32_t src_mask[4];
uint32_t dst_mask[4];
ogs_debug("DIR:%d PROTO:%d SRC:%d-%d DST:%d-%d",
pf->direction, pf->rule.proto,
pf->rule.port.local.low,
pf->rule.port.local.high,
pf->rule.port.remote.low,
pf->rule.port.remote.high);
ogs_debug("SRC:%08x %08x %08x %08x/%08x %08x %08x %08x",
be32toh(pf->rule.ip.local.addr[0]),
be32toh(pf->rule.ip.local.addr[1]),
be32toh(pf->rule.ip.local.addr[2]),
be32toh(pf->rule.ip.local.addr[3]),
be32toh(pf->rule.ip.local.mask[0]),
be32toh(pf->rule.ip.local.mask[1]),
be32toh(pf->rule.ip.local.mask[2]),
be32toh(pf->rule.ip.local.mask[3]));
ogs_debug("DST:%08x %08x %08x %08x/%08x %08x %08x %08x",
be32toh(pf->rule.ip.remote.addr[0]),
be32toh(pf->rule.ip.remote.addr[1]),
be32toh(pf->rule.ip.remote.addr[2]),
be32toh(pf->rule.ip.remote.addr[3]),
be32toh(pf->rule.ip.remote.mask[0]),
be32toh(pf->rule.ip.remote.mask[1]),
be32toh(pf->rule.ip.remote.mask[2]),
be32toh(pf->rule.ip.remote.mask[3]));
if (pf->direction != 1) {
continue;
}
for (k = 0; k < 4; k++) {
src_mask[k] = src_addr[k] & pf->rule.ip.local.mask[k];
dst_mask[k] = dst_addr[k] & pf->rule.ip.remote.mask[k];
}
if (memcmp(src_mask, pf->rule.ip.local.addr,
addr_len) == 0 &&
memcmp(dst_mask, pf->rule.ip.remote.addr,
addr_len) == 0) {
/* Protocol match */
if (pf->rule.proto == 0) { /* IP */
/* No need to match port */
break;
}
if (pf->rule.proto == proto) {
if (pf->rule.proto == IPPROTO_TCP) {
struct tcphdr *tcph =
(struct tcphdr *)
((char *)pkt->data + ip_hlen);
/* Source port */
if (pf->rule.port.local.low &&
be16toh(tcph->th_sport) <
pf->rule.port.local.low) {
continue;
}
if (pf->rule.port.local.high &&
be16toh(tcph->th_sport) >
pf->rule.port.local.high) {
continue;
}
/* Dst Port*/
if (pf->rule.port.remote.low &&
be16toh(tcph->th_dport) <
pf->rule.port.remote.low) {
continue;
}
if (pf->rule.port.remote.high &&
be16toh(tcph->th_dport) >
pf->rule.port.remote.high) {
continue;
}
/* Matched */
break;
} else if (pf->rule.proto == IPPROTO_UDP) {
struct udphdr *udph =
(struct udphdr *)
((char *)pkt->data + ip_hlen);
/* Source port */
if (pf->rule.port.local.low &&
be16toh(udph->uh_sport) <
pf->rule.port.local.low) {
continue;
}
if (pf->rule.port.local.high &&
be16toh(udph->uh_sport) >
pf->rule.port.local.high) {
continue;
}
/* Dst Port*/
if (pf->rule.port.remote.low &&
be16toh(udph->uh_dport) <
pf->rule.port.remote.low) {
continue;
}
if (pf->rule.port.remote.high &&
be16toh(udph->uh_dport) >
pf->rule.port.remote.high) {
continue;
}
/* Matched */
break;
} else {
/* No need to match port */
break;
}
}
}
}
if (pf) {
bearer = pf->bearer;
ogs_debug("Found Dedicated Bearer : EBI[%d]", bearer->ebi);
break;
}
}
return (bearer ? bearer : default_bearer);
} else {
ogs_debug("[PGW] No Session");
}
return NULL;
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_IPFW_H
#define PGW_IPFW_H
#include "pgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
int pgw_compile_packet_filter(pgw_rule_t *pf, char *description);
pgw_bearer_t *pgw_bearer_find_by_packet(ogs_pkbuf_t *pkt);
#ifdef __cplusplus
}
#endif
#endif /* PGW_IPFW_H */

View File

@ -1,583 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-context.h"
#include "pgw-s5c-build.h"
#include "ipfw/ipfw2.h"
static int16_t pgw_pco_build(uint8_t *pco_buf, ogs_gtp_tlv_pco_t *tlv_pco);
ogs_pkbuf_t *pgw_s5c_build_create_session_response(
uint8_t type, pgw_sess_t *sess,
ogs_diam_gx_message_t *gx_message)
{
int rv;
pgw_bearer_t *bearer = NULL;
ogs_gtp_message_t gtp_message;
ogs_gtp_create_session_response_t *rsp = NULL;
ogs_gtp_cause_t cause;
ogs_gtp_f_teid_t pgw_s5c_teid, pgw_s5u_teid;
ogs_gtp_ambr_t ambr;
ogs_gtp_bearer_qos_t bearer_qos;
char bearer_qos_buf[GTP_BEARER_QOS_LEN];
int len;
uint8_t pco_buf[OGS_MAX_PCO_LEN];
int16_t pco_len;
ogs_debug("[PGW] Create Session Response");
ogs_assert(sess);
bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(bearer);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
ogs_debug(" SGW_S5U_TEID[0x%x] PGW_S5U_TEID[0x%x]",
bearer->sgw_s5u_teid, bearer->pgw_s5u_teid);
rsp = &gtp_message.create_session_response;
memset(&gtp_message, 0, sizeof(ogs_gtp_message_t));
/* Set Cause */
memset(&cause, 0, sizeof(cause));
cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
rsp->cause.presence = 1;
rsp->cause.len = sizeof(cause);
rsp->cause.data = &cause;
/* Control Plane(UL) : PGW-S5C */
memset(&pgw_s5c_teid, 0, sizeof(ogs_gtp_f_teid_t));
pgw_s5c_teid.interface_type = OGS_GTP_F_TEID_S5_S8_PGW_GTP_C;
pgw_s5c_teid.teid = htobe32(sess->pgw_s5c_teid);
rv = ogs_gtp_sockaddr_to_f_teid(
pgw_self()->gtpc_addr, pgw_self()->gtpc_addr6, &pgw_s5c_teid, &len);
ogs_assert(rv == OGS_OK);
rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.
presence = 1;
rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.
data = &pgw_s5c_teid;
rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.
len = len;
/* PDN Address Allocation */
rsp->pdn_address_allocation.data = &sess->pdn.paa;
if (sess->ipv4 && sess->ipv6)
rsp->pdn_address_allocation.len = OGS_PAA_IPV4V6_LEN;
else if (sess->ipv4)
rsp->pdn_address_allocation.len = OGS_PAA_IPV4_LEN;
else if (sess->ipv6)
rsp->pdn_address_allocation.len = OGS_PAA_IPV6_LEN;
else
ogs_assert_if_reached();
rsp->pdn_address_allocation.presence = 1;
/* APN Restriction */
rsp->apn_restriction.presence = 1;
rsp->apn_restriction.u8 = OGS_GTP_APN_NO_RESTRICTION;
/* APN-AMBR
* if PCRF changes APN-AMBR, this should be included. */
if ((gx_message->pdn.ambr.uplink &&
(sess->pdn.ambr.uplink / 1000) !=
(gx_message->pdn.ambr.uplink / 1000)) ||
(gx_message->pdn.ambr.downlink &&
(sess->pdn.ambr.downlink / 1000) !=
(gx_message->pdn.ambr.downlink / 1000))) {
memset(&ambr, 0, sizeof(ogs_gtp_ambr_t));
ambr.uplink = htobe32(gx_message->pdn.ambr.uplink / 1000);
ambr.downlink = htobe32(gx_message->pdn.ambr.downlink / 1000);
rsp->aggregate_maximum_bit_rate.presence = 1;
rsp->aggregate_maximum_bit_rate.data = &ambr;
rsp->aggregate_maximum_bit_rate.len = sizeof(ambr);
}
/* PCO */
if (sess->ue_pco.presence && sess->ue_pco.len && sess->ue_pco.data) {
pco_len = pgw_pco_build(pco_buf, &sess->ue_pco);
ogs_assert(pco_len > 0);
rsp->protocol_configuration_options.presence = 1;
rsp->protocol_configuration_options.data = pco_buf;
rsp->protocol_configuration_options.len = pco_len;
}
/* Bearer EBI */
rsp->bearer_contexts_created.presence = 1;
rsp->bearer_contexts_created.eps_bearer_id.presence = 1;
rsp->bearer_contexts_created.eps_bearer_id.u8 = bearer->ebi;
/* Bearer Cause */
rsp->bearer_contexts_created.cause.presence = 1;
rsp->bearer_contexts_created.cause.len = sizeof(cause);
rsp->bearer_contexts_created.cause.data = &cause;
/* Bearer QoS
* if PCRF changes Bearer QoS, this should be included. */
if ((gx_message->pdn.qos.qci &&
sess->pdn.qos.qci != gx_message->pdn.qos.qci) ||
(gx_message->pdn.qos.arp.priority_level &&
sess->pdn.qos.arp.priority_level !=
gx_message->pdn.qos.arp.priority_level) ||
sess->pdn.qos.arp.pre_emption_capability !=
gx_message->pdn.qos.arp.pre_emption_capability ||
sess->pdn.qos.arp.pre_emption_vulnerability !=
gx_message->pdn.qos.arp.pre_emption_vulnerability) {
memset(&bearer_qos, 0, sizeof(bearer_qos));
bearer_qos.qci = gx_message->pdn.qos.qci;
bearer_qos.priority_level = gx_message->pdn.qos.arp.priority_level;
bearer_qos.pre_emption_capability =
gx_message->pdn.qos.arp.pre_emption_capability;
bearer_qos.pre_emption_vulnerability =
gx_message->pdn.qos.arp.pre_emption_vulnerability;
rsp->bearer_contexts_created.bearer_level_qos.presence = 1;
ogs_gtp_build_bearer_qos(&rsp->bearer_contexts_created.bearer_level_qos,
&bearer_qos, bearer_qos_buf, GTP_BEARER_QOS_LEN);
}
/* Data Plane(UL) : PGW-S5U */
memset(&pgw_s5u_teid, 0, sizeof(ogs_gtp_f_teid_t));
pgw_s5u_teid.interface_type = OGS_GTP_F_TEID_S5_S8_PGW_GTP_U;
pgw_s5u_teid.teid = htobe32(bearer->pgw_s5u_teid);
rv = ogs_gtp_sockaddr_to_f_teid(
pgw_self()->gtpu_addr, pgw_self()->gtpu_addr6, &pgw_s5u_teid, &len);
ogs_assert(rv == OGS_OK);
rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.presence = 1;
rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.data = &pgw_s5u_teid;
rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.len = len;
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
ogs_pkbuf_t *pgw_s5c_build_delete_session_response(
uint8_t type, pgw_sess_t *sess,
ogs_diam_gx_message_t *gx_message)
{
ogs_gtp_message_t gtp_message;
ogs_gtp_delete_session_response_t *rsp = NULL;
ogs_gtp_cause_t cause;
uint8_t pco_buf[OGS_MAX_PCO_LEN];
int16_t pco_len;
ogs_assert(gx_message);
/* prepare cause */
memset(&cause, 0, sizeof(cause));
cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
rsp = &gtp_message.delete_session_response;
memset(&gtp_message, 0, sizeof(ogs_gtp_message_t));
/* Cause */
rsp->cause.presence = 1;
rsp->cause.len = sizeof(cause);
rsp->cause.data = &cause;
/* Recovery */
/* PCO */
if (sess->ue_pco.presence && sess->ue_pco.len && sess->ue_pco.data) {
pco_len = pgw_pco_build(pco_buf, &sess->ue_pco);
ogs_assert(pco_len > 0);
rsp->protocol_configuration_options.presence = 1;
rsp->protocol_configuration_options.data = pco_buf;
rsp->protocol_configuration_options.len = pco_len;
}
/* Private Extension */
/* build */
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
ogs_pkbuf_t *pgw_s5c_build_create_bearer_request(
uint8_t type, pgw_bearer_t *bearer, ogs_gtp_tft_t *tft)
{
int rv;
pgw_sess_t *sess = NULL;
pgw_bearer_t *linked_bearer = NULL;
ogs_gtp_message_t gtp_message;
ogs_gtp_create_bearer_request_t *req = NULL;
ogs_gtp_f_teid_t pgw_s5u_teid;
ogs_gtp_bearer_qos_t bearer_qos;
char bearer_qos_buf[GTP_BEARER_QOS_LEN];
int len;
char tft_buf[OGS_GTP_MAX_TRAFFIC_FLOW_TEMPLATE];
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
linked_bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(linked_bearer);
ogs_debug("[PGW] Create Bearer Request");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
req = &gtp_message.create_bearer_request;
memset(&gtp_message, 0, sizeof(ogs_gtp_message_t));
/* Linked EBI */
req->linked_eps_bearer_id.presence = 1;
req->linked_eps_bearer_id.u8 = linked_bearer->ebi;
/* Bearer EBI */
req->bearer_contexts.presence = 1;
req->bearer_contexts.eps_bearer_id.presence = 1;
req->bearer_contexts.eps_bearer_id.u8 = bearer->ebi;
/* Data Plane(UL) : PGW_S5U */
memset(&pgw_s5u_teid, 0, sizeof(ogs_gtp_f_teid_t));
pgw_s5u_teid.interface_type = OGS_GTP_F_TEID_S5_S8_PGW_GTP_U;
pgw_s5u_teid.teid = htobe32(bearer->pgw_s5u_teid);
rv = ogs_gtp_sockaddr_to_f_teid(
pgw_self()->gtpu_addr, pgw_self()->gtpu_addr6, &pgw_s5u_teid, &len);
ogs_assert(rv == OGS_OK);
req->bearer_contexts.s5_s8_u_sgw_f_teid.presence = 1;
req->bearer_contexts.s5_s8_u_sgw_f_teid.data = &pgw_s5u_teid;
req->bearer_contexts.s5_s8_u_sgw_f_teid.len = len;
/* Bearer QoS */
memset(&bearer_qos, 0, sizeof(bearer_qos));
bearer_qos.qci = bearer->qos.qci;
bearer_qos.priority_level = bearer->qos.arp.priority_level;
bearer_qos.pre_emption_capability =
bearer->qos.arp.pre_emption_capability;
bearer_qos.pre_emption_vulnerability =
bearer->qos.arp.pre_emption_vulnerability;
bearer_qos.dl_mbr = bearer->qos.mbr.downlink;
bearer_qos.ul_mbr = bearer->qos.mbr.uplink;
bearer_qos.dl_gbr = bearer->qos.gbr.downlink;
bearer_qos.ul_gbr = bearer->qos.gbr.uplink;
req->bearer_contexts.bearer_level_qos.presence = 1;
ogs_gtp_build_bearer_qos(&req->bearer_contexts.bearer_level_qos,
&bearer_qos, bearer_qos_buf, GTP_BEARER_QOS_LEN);
/* Bearer TFT */
if (tft && tft->num_of_packet_filter) {
req->bearer_contexts.tft.presence = 1;
ogs_gtp_build_tft(&req->bearer_contexts.tft,
tft, tft_buf, OGS_GTP_MAX_TRAFFIC_FLOW_TEMPLATE);
}
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
ogs_pkbuf_t *pgw_s5c_build_update_bearer_request(
uint8_t type, pgw_bearer_t *bearer, uint8_t pti,
ogs_gtp_tft_t *tft, int qos_presence)
{
pgw_sess_t *sess = NULL;
ogs_gtp_message_t gtp_message;
ogs_gtp_update_bearer_request_t *req = NULL;
ogs_gtp_ambr_t ambr;
ogs_gtp_bearer_qos_t bearer_qos;
char bearer_qos_buf[GTP_BEARER_QOS_LEN];
char tft_buf[OGS_GTP_MAX_TRAFFIC_FLOW_TEMPLATE];
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
ogs_debug("[PGW] Update Bearer Request");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
req = &gtp_message.update_bearer_request;
memset(&gtp_message, 0, sizeof(ogs_gtp_message_t));
/* Bearer EBI */
req->bearer_contexts.presence = 1;
req->bearer_contexts.eps_bearer_id.presence = 1;
req->bearer_contexts.eps_bearer_id.u8 = bearer->ebi;
if (sess->pdn.ambr.uplink || sess->pdn.ambr.downlink) {
/*
* Ch 8.7. Aggregate Maximum Bit Rate(AMBR) in TS 29.274 V15.9.0
*
* AMBR is defined in clause 9.9.4.2 of 3GPP TS 24.301 [23],
* but it shall be encoded as shown in Figure 8.7-1 as
* Unsigned32 binary integer values in kbps (1000 bits per second).
*/
memset(&ambr, 0, sizeof(ogs_gtp_ambr_t));
ambr.uplink = htobe32(sess->pdn.ambr.uplink / 1000);
ambr.downlink = htobe32(sess->pdn.ambr.downlink / 1000);
req->aggregate_maximum_bit_rate.presence = 1;
req->aggregate_maximum_bit_rate.data = &ambr;
req->aggregate_maximum_bit_rate.len = sizeof(ambr);
}
/* PTI */
if (pti) {
req->procedure_transaction_id.presence = 1;
req->procedure_transaction_id.u8 = pti;
}
/* Bearer QoS */
if (qos_presence == 1) {
memset(&bearer_qos, 0, sizeof(bearer_qos));
bearer_qos.qci = bearer->qos.qci;
bearer_qos.priority_level = bearer->qos.arp.priority_level;
bearer_qos.pre_emption_capability =
bearer->qos.arp.pre_emption_capability;
bearer_qos.pre_emption_vulnerability =
bearer->qos.arp.pre_emption_vulnerability;
bearer_qos.dl_mbr = bearer->qos.mbr.downlink;
bearer_qos.ul_mbr = bearer->qos.mbr.uplink;
bearer_qos.dl_gbr = bearer->qos.gbr.downlink;
bearer_qos.ul_gbr = bearer->qos.gbr.uplink;
req->bearer_contexts.bearer_level_qos.presence = 1;
ogs_gtp_build_bearer_qos(&req->bearer_contexts.bearer_level_qos,
&bearer_qos, bearer_qos_buf, GTP_BEARER_QOS_LEN);
}
/* Bearer TFT */
if (tft && tft->num_of_packet_filter) {
req->bearer_contexts.tft.presence = 1;
ogs_gtp_build_tft(&req->bearer_contexts.tft,
tft, tft_buf, OGS_GTP_MAX_TRAFFIC_FLOW_TEMPLATE);
}
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
ogs_pkbuf_t *pgw_s5c_build_delete_bearer_request(
uint8_t type, pgw_bearer_t *bearer, uint8_t pti)
{
pgw_sess_t *sess = NULL;
pgw_bearer_t *linked_bearer = NULL;
ogs_gtp_message_t gtp_message;
ogs_gtp_delete_bearer_request_t *req = NULL;
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
linked_bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(linked_bearer);
ogs_debug("[PGW] Delete Bearer Request");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
req = &gtp_message.delete_bearer_request;
memset(&gtp_message, 0, sizeof(ogs_gtp_message_t));
if (bearer->ebi == linked_bearer->ebi) {
/* Linked EBI */
req->linked_eps_bearer_id.presence = 1;
req->linked_eps_bearer_id.u8 = bearer->ebi;
} else {
/* Bearer EBI */
req->eps_bearer_ids.presence = 1;
req->eps_bearer_ids.u8 = bearer->ebi;
}
if (pti) {
req->procedure_transaction_id.presence = 1;
req->procedure_transaction_id.u8 = pti;
}
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
static int16_t pgw_pco_build(uint8_t *pco_buf, ogs_gtp_tlv_pco_t *tlv_pco)
{
int rv;
ogs_pco_t ue, pgw;
ogs_pco_ipcp_t pco_ipcp;
ogs_ipsubnet_t dns_primary, dns_secondary, dns6_primary, dns6_secondary;
ogs_ipsubnet_t p_cscf, p_cscf6;
int size = 0;
int i = 0;
uint16_t mtu = 0;
ogs_assert(pco_buf);
ogs_assert(tlv_pco);
size = ogs_pco_parse(&ue, tlv_pco->data, tlv_pco->len);
ogs_assert(size);
memset(&pgw, 0, sizeof(ogs_pco_t));
pgw.ext = ue.ext;
pgw.configuration_protocol = ue.configuration_protocol;
for (i = 0; i < ue.num_of_id; i++) {
uint8_t *data = ue.ids[i].data;
switch(ue.ids[i].id) {
case OGS_PCO_ID_CHALLENGE_HANDSHAKE_AUTHENTICATION_PROTOCOL:
if (data[0] == 2) { /* Code : Response */
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = 4;
pgw.ids[pgw.num_of_id].data =
(uint8_t *)"\x03\x00\x00\x04"; /* Code : Success */
pgw.num_of_id++;
}
break;
case OGS_PCO_ID_INTERNET_PROTOCOL_CONTROL_PROTOCOL:
if (data[0] == 1) { /* Code : Configuration Request */
uint16_t len = 0;
ogs_assert(pgw_self()->dns[0] || pgw_self()->dns[1]);
memset(&pco_ipcp, 0, sizeof(ogs_pco_ipcp_t));
pco_ipcp.code = 2; /* Code : Configuration Ack */
len = 4;
/* Primary DNS Server IP Address */
if (pgw_self()->dns[0]) {
rv = ogs_ipsubnet(
&dns_primary, pgw_self()->dns[0], NULL);
ogs_assert(rv == OGS_OK);
pco_ipcp.options[0].type = 129;
pco_ipcp.options[0].len = 6;
pco_ipcp.options[0].addr = dns_primary.sub[0];
len += 6;
}
/* Secondary DNS Server IP Address */
if (pgw_self()->dns[1]) {
rv = ogs_ipsubnet(
&dns_secondary, pgw_self()->dns[1], NULL);
ogs_assert(rv == OGS_OK);
pco_ipcp.options[1].type = 131;
pco_ipcp.options[1].len = 6;
pco_ipcp.options[1].addr = dns_secondary.sub[0];
len += 6;
}
pco_ipcp.len = htobe16(len);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = len;
pgw.ids[pgw.num_of_id].data = (uint8_t *)&pco_ipcp;
pgw.num_of_id++;
}
break;
case OGS_PCO_ID_DNS_SERVER_IPV4_ADDRESS_REQUEST:
if (pgw_self()->dns[0]) {
rv = ogs_ipsubnet(
&dns_primary, pgw_self()->dns[0], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV4_LEN;
pgw.ids[pgw.num_of_id].data = dns_primary.sub;
pgw.num_of_id++;
}
if (pgw_self()->dns[1]) {
rv = ogs_ipsubnet(
&dns_secondary, pgw_self()->dns[1], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV4_LEN;
pgw.ids[pgw.num_of_id].data = dns_secondary.sub;
pgw.num_of_id++;
}
break;
case OGS_PCO_ID_DNS_SERVER_IPV6_ADDRESS_REQUEST:
if (pgw_self()->dns6[0]) {
rv = ogs_ipsubnet(
&dns6_primary, pgw_self()->dns6[0], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV6_LEN;
pgw.ids[pgw.num_of_id].data = dns6_primary.sub;
pgw.num_of_id++;
}
if (pgw_self()->dns6[1]) {
rv = ogs_ipsubnet(
&dns6_secondary, pgw_self()->dns6[1], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV6_LEN;
pgw.ids[pgw.num_of_id].data = dns6_secondary.sub;
pgw.num_of_id++;
}
break;
case OGS_PCO_ID_P_CSCF_IPV4_ADDRESS_REQUEST:
if (pgw_self()->num_of_p_cscf) {
rv = ogs_ipsubnet(&p_cscf,
pgw_self()->p_cscf[pgw_self()->p_cscf_index], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV4_LEN;
pgw.ids[pgw.num_of_id].data = p_cscf.sub;
pgw.num_of_id++;
pgw_self()->p_cscf_index++;
pgw_self()->p_cscf_index %= pgw_self()->num_of_p_cscf;
}
break;
case OGS_PCO_ID_P_CSCF_IPV6_ADDRESS_REQUEST:
if (pgw_self()->num_of_p_cscf6) {
rv = ogs_ipsubnet(&p_cscf6,
pgw_self()->p_cscf6[pgw_self()->p_cscf6_index], NULL);
ogs_assert(rv == OGS_OK);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = OGS_IPV6_LEN;
pgw.ids[pgw.num_of_id].data = p_cscf6.sub;
pgw.num_of_id++;
pgw_self()->p_cscf6_index++;
pgw_self()->p_cscf6_index %= pgw_self()->num_of_p_cscf6;
}
break;
case OGS_PCO_ID_IP_ADDRESS_ALLOCATION_VIA_NAS_SIGNALLING:
/* TODO */
break;
case OGS_PCO_ID_IPV4_LINK_MTU_REQUEST:
if (pgw_self()->mtu) {
mtu = htons(pgw_self()->mtu);
pgw.ids[pgw.num_of_id].id = ue.ids[i].id;
pgw.ids[pgw.num_of_id].len = sizeof(uint16_t);
pgw.ids[pgw.num_of_id].data = &mtu;
pgw.num_of_id++;
}
break;
case OGS_PCO_ID_MS_SUPPORTS_BCM:
/* TODO */
break;
case OGS_PCO_ID_MS_SUPPORT_LOCAL_ADDR_TFT_INDICATOR:
/* TODO */
break;
default:
ogs_warn("Unknown PCO ID:(0x%x)", ue.ids[i].id);
}
}
size = ogs_pco_build(pco_buf, OGS_MAX_PCO_LEN, &pgw);
return size;
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_S5C_BUILD_H
#define PGW_S5C_BUILD_H
#include "ogs-gtp.h"
#ifdef __cplusplus
extern "C" {
#endif
ogs_pkbuf_t *pgw_s5c_build_create_session_response(
uint8_t type, pgw_sess_t *sess,
ogs_diam_gx_message_t *gx_message);
ogs_pkbuf_t *pgw_s5c_build_delete_session_response(
uint8_t type, pgw_sess_t *sess,
ogs_diam_gx_message_t *gx_message);
ogs_pkbuf_t *pgw_s5c_build_create_bearer_request(
uint8_t type, pgw_bearer_t *bearer, ogs_gtp_tft_t *tft);
ogs_pkbuf_t *pgw_s5c_build_update_bearer_request(
uint8_t type, pgw_bearer_t *bearer, uint8_t pti,
ogs_gtp_tft_t *tft, int qos_presence);
ogs_pkbuf_t *pgw_s5c_build_delete_bearer_request(
uint8_t type, pgw_bearer_t *bearer, uint8_t pti);
#ifdef __cplusplus
}
#endif
#endif /* PGW_S5C_BUILD_H */

View File

@ -1,652 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-event.h"
#include "pgw-context.h"
#include "pgw-gtp-path.h"
#include "pgw-fd-path.h"
#include "pgw-s5c-build.h"
#include "pgw-s5c-handler.h"
#include "ipfw/ipfw2.h"
void pgw_s5c_handle_echo_request(
ogs_gtp_xact_t *xact, ogs_gtp_echo_request_t *req)
{
ogs_assert(xact);
ogs_assert(req);
ogs_debug("[PGW] Receiving Echo Request");
/* FIXME : Before implementing recovery counter correctly,
* I'll re-use the recovery value in request message */
ogs_gtp_send_echo_response(xact, req->recovery.u8, 0);
}
void pgw_s5c_handle_echo_response(
ogs_gtp_xact_t *xact, ogs_gtp_echo_response_t *req)
{
/* Not Implemented */
}
void pgw_s5c_handle_create_session_request(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_create_session_request_t *req)
{
int rv;
uint8_t cause_value = 0;
ogs_gtp_f_teid_t *sgw_s5c_teid, *sgw_s5u_teid;
ogs_gtp_node_t *sgw = NULL;
pgw_bearer_t *bearer = NULL;
ogs_gtp_bearer_qos_t bearer_qos;
ogs_gtp_ambr_t *ambr = NULL;
uint16_t decoded = 0;
ogs_assert(xact);
ogs_assert(req);
ogs_debug("[PGW] Create Session Reqeust");
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
if (sess) {
bearer = pgw_default_bearer_in_sess(sess);
ogs_assert(bearer);
}
if (!bearer) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (req->imsi.presence == 0) {
ogs_error("No IMSI");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->sender_f_teid_for_control_plane.presence == 0) {
ogs_error("No TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts_to_be_created.presence == 0) {
ogs_error("No Bearer");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts_to_be_created.bearer_level_qos.presence == 0) {
ogs_error("No EPS Bearer QoS");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts_to_be_created.s5_s8_u_sgw_f_teid.presence == 0) {
ogs_error("No TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (!ogs_diam_peer_connected()) {
ogs_error("No Diameter Peer");
cause_value = OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
/* Set IMSI */
sess->imsi_len = req->imsi.len;
memcpy(sess->imsi, req->imsi.data, sess->imsi_len);
ogs_buffer_to_bcd(sess->imsi, sess->imsi_len, sess->imsi_bcd);
/* Control Plane(DL) : SGW-S5C */
sgw_s5c_teid = req->sender_f_teid_for_control_plane.data;
ogs_assert(sgw_s5c_teid);
sess->sgw_s5c_teid = be32toh(sgw_s5c_teid->teid);
/* Control Plane(DL) : SGW-S5U */
sgw_s5u_teid = req->bearer_contexts_to_be_created.s5_s8_u_sgw_f_teid.data;
ogs_assert(sgw_s5u_teid);
bearer->sgw_s5u_teid = be32toh(sgw_s5u_teid->teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
ogs_debug(" SGW_S5U_TEID[0x%x] PGW_S5U_TEID[0x%x]",
bearer->sgw_s5u_teid, bearer->pgw_s5u_teid);
sgw = ogs_gtp_node_find_by_f_teid(&pgw_self()->sgw_s5u_list, sgw_s5u_teid);
if (!sgw) {
sgw = ogs_gtp_node_add_by_f_teid(
&pgw_self()->sgw_s5u_list, sgw_s5u_teid, pgw_self()->gtpu_port,
ogs_config()->parameter.no_ipv4,
ogs_config()->parameter.no_ipv6,
ogs_config()->parameter.prefer_ipv4);
ogs_assert(sgw);
rv = ogs_gtp_connect(
pgw_self()->gtpu_sock, pgw_self()->gtpu_sock6, sgw);
ogs_assert(rv == OGS_OK);
}
/* Setup GTP Node */
OGS_SETUP_GTP_NODE(bearer, sgw);
decoded = ogs_gtp_parse_bearer_qos(&bearer_qos,
&req->bearer_contexts_to_be_created.bearer_level_qos);
ogs_assert(req->bearer_contexts_to_be_created.bearer_level_qos.len ==
decoded);
sess->pdn.qos.qci = bearer_qos.qci;
sess->pdn.qos.arp.priority_level = bearer_qos.priority_level;
sess->pdn.qos.arp.pre_emption_capability =
bearer_qos.pre_emption_capability;
sess->pdn.qos.arp.pre_emption_vulnerability =
bearer_qos.pre_emption_vulnerability;
/* Set AMBR if available */
if (req->aggregate_maximum_bit_rate.presence) {
/*
* Ch 8.7. Aggregate Maximum Bit Rate(AMBR) in TS 29.274 V15.9.0
*
* AMBR is defined in clause 9.9.4.2 of 3GPP TS 24.301 [23],
* but it shall be encoded as shown in Figure 8.7-1 as
* Unsigned32 binary integer values in kbps (1000 bits per second).
*/
ambr = req->aggregate_maximum_bit_rate.data;
sess->pdn.ambr.downlink = be32toh(ambr->downlink) * 1000;
sess->pdn.ambr.uplink = be32toh(ambr->uplink) * 1000;
}
/* PCO */
if (req->protocol_configuration_options.presence) {
OGS_TLV_STORE_DATA(&sess->ue_pco, &req->protocol_configuration_options);
}
/* Set User Location Information */
if (req->user_location_information.presence) {
OGS_TLV_STORE_DATA(&sess->user_location_information,
&req->user_location_information);
}
/* Set UE Timezone */
if (req->ue_time_zone.presence) {
OGS_TLV_STORE_DATA(&sess->ue_timezone, &req->ue_time_zone);
}
pgw_gx_send_ccr(sess, xact,
OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST);
}
void pgw_s5c_handle_delete_session_request(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_delete_session_request_t *req)
{
uint8_t cause_value = 0;
ogs_debug("[PGW] Delete Session Request");
ogs_assert(xact);
ogs_assert(req);
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
if (!sess) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (!ogs_diam_peer_connected()) {
ogs_error("No Diameter Peer");
cause_value = OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
pgw_gx_send_ccr(sess, xact,
OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST);
}
void pgw_s5c_handle_create_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_create_bearer_response_t *rsp)
{
int rv;
ogs_gtp_f_teid_t *sgw_s5u_teid, *pgw_s5u_teid;
ogs_gtp_node_t *sgw = NULL;
pgw_bearer_t *bearer = NULL;
ogs_assert(xact);
ogs_assert(rsp);
ogs_debug("[PGW] Create Bearer Response");
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
if (!sess) {
ogs_warn("No Context in TEID");
return;
}
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
if (rsp->cause.presence) {
ogs_gtp_cause_t *cause = rsp->cause.data;
ogs_assert(cause);
if (cause->value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_warn("GTP Failed [CAUSE:%d]", cause->value);
return;
}
}
if (rsp->bearer_contexts.presence == 0) {
ogs_error("No Bearer");
return;
}
if (rsp->bearer_contexts.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
return;
}
if (rsp->bearer_contexts.s5_s8_u_pgw_f_teid.presence == 0) {
ogs_error("No PGW TEID");
return;
}
if (rsp->bearer_contexts.s5_s8_u_sgw_f_teid.presence == 0) {
ogs_error("No SGW TEID");
return;
}
/* Correlate with PGW-S5U-TEID */
pgw_s5u_teid = rsp->bearer_contexts.s5_s8_u_pgw_f_teid.data;
ogs_assert(pgw_s5u_teid);
/* Find the Bearer by PGW-S5U-TEID */
bearer = pgw_bearer_find_by_pgw_s5u_teid(be32toh(pgw_s5u_teid->teid));
ogs_assert(bearer);
/* Set EBI */
bearer->ebi = rsp->bearer_contexts.eps_bearer_id.u8;
/* Data Plane(DL) : SGW-S5U */
sgw_s5u_teid = rsp->bearer_contexts.s5_s8_u_sgw_f_teid.data;
bearer->sgw_s5u_teid = be32toh(sgw_s5u_teid->teid);
sgw = ogs_gtp_node_find_by_f_teid(&pgw_self()->sgw_s5u_list, sgw_s5u_teid);
if (!sgw) {
sgw = ogs_gtp_node_add_by_f_teid(
&pgw_self()->sgw_s5u_list, sgw_s5u_teid, pgw_self()->gtpu_port,
ogs_config()->parameter.no_ipv4,
ogs_config()->parameter.no_ipv6,
ogs_config()->parameter.prefer_ipv4);
ogs_assert(sgw);
rv = ogs_gtp_connect(
pgw_self()->gtpu_sock, pgw_self()->gtpu_sock6, sgw);
ogs_assert(rv == OGS_OK);
}
/* Setup GTP Node */
OGS_SETUP_GTP_NODE(bearer, sgw);
ogs_debug("[PGW] Create Bearer Response : SGW[0x%x] --> PGW[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
}
void pgw_s5c_handle_update_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_update_bearer_response_t *rsp)
{
int rv;
ogs_assert(xact);
ogs_assert(rsp);
ogs_debug("[PGW] Update Bearer Response");
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
if (!sess) {
ogs_warn("No Context in TEID");
return;
}
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
if (rsp->cause.presence) {
ogs_gtp_cause_t *cause = rsp->cause.data;
ogs_assert(cause);
if (cause->value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_warn("GTP Failed [CAUSE:%d]", cause->value);
return;
}
}
if (rsp->bearer_contexts.presence == 0) {
ogs_error("No Bearer");
return;
}
if (rsp->bearer_contexts.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
return;
}
ogs_debug("[PGW] Update Bearer Response : SGW[0x%x] --> PGW[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
}
void pgw_s5c_handle_delete_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_delete_bearer_response_t *rsp)
{
int rv;
pgw_bearer_t *bearer = NULL;
ogs_assert(xact);
ogs_assert(rsp);
ogs_debug("[PGW] Delete Bearer Response");
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
if (!sess) {
ogs_warn("No Context in TEID");
return;
}
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
if (rsp->cause.presence) {
ogs_gtp_cause_t *cause = rsp->cause.data;
ogs_assert(cause);
if (cause->value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_warn("GTP Failed [CAUSE:%d]", cause->value);
return;
}
}
if (rsp->bearer_contexts.presence == 0) {
ogs_error("No Bearer");
return;
}
if (rsp->bearer_contexts.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
return;
}
bearer = pgw_bearer_find_by_ebi(
sess, rsp->bearer_contexts.eps_bearer_id.u8);
ogs_assert(bearer);
ogs_debug("[PGW] Delete Bearer Response : SGW[0x%x] --> PGW[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
pgw_bearer_remove(bearer);
}
static int reconfigure_packet_filter(pgw_pf_t *pf, ogs_gtp_tft_t *tft, int i)
{
int j;
for (j = 0; j < tft->pf[i].num_of_component; j++) {
switch(tft->pf[i].component[j].type) {
case GTP_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE:
pf->rule.proto = tft->pf[i].component[j].proto;
break;
case GTP_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE:
pf->rule.ipv4_remote = 1;
pf->rule.ip.remote.addr[0] = tft->pf[i].component[j].ipv4.addr;
pf->rule.ip.remote.mask[0] = tft->pf[i].component[j].ipv4.mask;
break;
case GTP_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE:
pf->rule.ipv4_local = 1;
pf->rule.ip.local.addr[0] = tft->pf[i].component[j].ipv4.addr;
pf->rule.ip.local.mask[0] = tft->pf[i].component[j].ipv4.mask;
break;
case GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE:
pf->rule.ipv6_remote = 1;
memcpy(pf->rule.ip.remote.addr,
tft->pf[i].component[j].ipv6_mask.addr,
sizeof(pf->rule.ip.remote.addr));
memcpy(pf->rule.ip.remote.mask,
tft->pf[i].component[j].ipv6_mask.mask,
sizeof(pf->rule.ip.remote.mask));
break;
case GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE:
pf->rule.ipv6_remote = 1;
memcpy(pf->rule.ip.remote.addr,
tft->pf[i].component[j].ipv6_mask.addr,
sizeof(pf->rule.ip.remote.addr));
n2mask((struct in6_addr *)pf->rule.ip.remote.mask,
tft->pf[i].component[j].ipv6.prefixlen);
break;
case GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_TYPE:
pf->rule.ipv6_local = 1;
memcpy(pf->rule.ip.local.addr,
tft->pf[i].component[j].ipv6_mask.addr,
sizeof(pf->rule.ip.local.addr));
memcpy(pf->rule.ip.local.mask,
tft->pf[i].component[j].ipv6_mask.mask,
sizeof(pf->rule.ip.local.mask));
break;
case GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE:
pf->rule.ipv6_local = 1;
memcpy(pf->rule.ip.local.addr,
tft->pf[i].component[j].ipv6_mask.addr,
sizeof(pf->rule.ip.local.addr));
n2mask((struct in6_addr *)pf->rule.ip.local.mask,
tft->pf[i].component[j].ipv6.prefixlen);
break;
case GTP_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE:
pf->rule.port.local.low = pf->rule.port.local.high =
tft->pf[i].component[j].port.low;
break;
case GTP_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE:
pf->rule.port.remote.low = pf->rule.port.remote.high =
tft->pf[i].component[j].port.low;
break;
case GTP_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE:
pf->rule.port.local.low = tft->pf[i].component[j].port.low;
pf->rule.port.local.high = tft->pf[i].component[j].port.high;
break;
case GTP_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE:
pf->rule.port.remote.low = tft->pf[i].component[j].port.low;
pf->rule.port.remote.high = tft->pf[i].component[j].port.high;
break;
default:
ogs_error("Unknown Packet Filter Type(%d)",
tft->pf[i].component[j].type);
return OGS_ERROR;
}
}
return j;
}
void pgw_s5c_handle_bearer_resource_command(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_bearer_resource_command_t *cmd)
{
int rv;
uint8_t cause_value = 0;
int i;
ogs_gtp_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
pgw_bearer_t *bearer = NULL;
pgw_pf_t *pf = NULL;
int16_t decoded;
ogs_gtp_tft_t tft;
int tft_update = 0;
int tft_delete = 0;
int qos_update = 0;
ogs_assert(xact);
ogs_assert(sess);
ogs_assert(cmd);
ogs_debug("[PGW] Bearer Resource Command");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
if (cmd->linked_eps_bearer_id.presence == 0) {
ogs_error("No Linked EPS Bearer ID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
} else {
bearer = pgw_bearer_find_by_ebi(
sess, cmd->linked_eps_bearer_id.u8);
if (!bearer) {
ogs_error("No Context for Linked EPS Bearer ID[%d]",
cmd->linked_eps_bearer_id.u8);
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
}
if (cmd->procedure_transaction_id.presence == 0) {
ogs_error("No PTI");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cmd->traffic_aggregate_description.presence == 0) {
ogs_error("No Traffic aggregate description(TAD)");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE, cause_value);
return;
}
ogs_assert(bearer);
decoded = ogs_gtp_parse_tft(&tft, &cmd->traffic_aggregate_description);
ogs_assert(cmd->traffic_aggregate_description.len == decoded);
if (tft.code == OGS_GTP_TFT_CODE_NO_TFT_OPERATION) {
/* No operation */
} else if (tft.code == OGS_GTP_TFT_CODE_DELETE_EXISTING_TFT) {
pgw_pf_remove_all(bearer);
tft_delete = 1;
} else if (tft.code ==
OGS_GTP_TFT_CODE_REPLACE_PACKET_FILTERS_IN_EXISTING) {
for (i = 0; i < tft.num_of_packet_filter; i++) {
pf = pgw_pf_find_by_id(bearer, tft.pf[i].identifier+1);
if (pf) {
if (reconfigure_packet_filter(pf, &tft, i) < 0) {
ogs_gtp_send_error_message(
xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE,
OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TAD_OPERATION);
return;
}
}
tft_update = 1;
}
} else if (tft.code ==
OGS_GTP_TFT_CODE_ADD_PACKET_FILTERS_TO_EXISTING_TFT ||
tft.code == OGS_GTP_TFT_CODE_CREATE_NEW_TFT) {
if (tft.code == OGS_GTP_TFT_CODE_CREATE_NEW_TFT)
pgw_pf_remove_all(bearer);
for (i = 0; i < tft.num_of_packet_filter; i++) {
pf = pgw_pf_find_by_id(bearer, tft.pf[i].identifier+1);
if (!pf)
pf = pgw_pf_add(bearer, tft.pf[i].precedence);
ogs_assert(pf);
if (reconfigure_packet_filter(pf, &tft, i) < 0) {
ogs_gtp_send_error_message(
xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE,
OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TAD_OPERATION);
return;
}
tft_update = 1;
}
} else if (tft.code ==
OGS_GTP_TFT_CODE_DELETE_PACKET_FILTERS_FROM_EXISTING) {
for (i = 0; i < tft.num_of_packet_filter; i++) {
pf = pgw_pf_find_by_id(bearer, tft.pf[i].identifier+1);
if (pf)
pgw_pf_remove(pf);
if (ogs_list_count(&bearer->pf_list))
tft_update = 1;
else
tft_delete = 1;
}
}
if (cmd->flow_quality_of_service.presence) {
ogs_gtp_flow_qos_t flow_qos;
decoded = ogs_gtp_parse_flow_qos(
&flow_qos, &cmd->flow_quality_of_service);
ogs_assert(cmd->flow_quality_of_service.len == decoded);
bearer->qos.mbr.uplink = flow_qos.ul_mbr;
bearer->qos.mbr.downlink = flow_qos.dl_mbr;
bearer->qos.gbr.uplink = flow_qos.ul_gbr;
bearer->qos.gbr.downlink = flow_qos.dl_gbr;
qos_update = 1;
}
if (tft_update == 0 && tft_delete == 0 && qos_update == 0) {
/* No modification */
ogs_gtp_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE,
OGS_GTP_CAUSE_SERVICE_NOT_SUPPORTED);
return;
}
memset(&h, 0, sizeof(ogs_gtp_header_t));
h.teid = sess->sgw_s5c_teid;
if (tft_delete) {
h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE;
pkbuf = pgw_s5c_build_delete_bearer_request(
h.type, bearer, cmd->procedure_transaction_id.u8);
ogs_expect_or_return(pkbuf);
} else {
h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE;
pkbuf = pgw_s5c_build_update_bearer_request(
h.type, bearer, cmd->procedure_transaction_id.u8,
tft_update ? &tft : NULL, qos_update);
ogs_expect_or_return(pkbuf);
}
rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_S5C_HANDLER_H
#define PGW_S5C_HANDLER_H
#include "pgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
void pgw_s5c_handle_echo_request(
ogs_gtp_xact_t *xact, ogs_gtp_echo_request_t *req);
void pgw_s5c_handle_echo_response(
ogs_gtp_xact_t *xact, ogs_gtp_echo_response_t *req);
void pgw_s5c_handle_create_session_request(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_create_session_request_t *req);
void pgw_s5c_handle_delete_session_request(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_delete_session_request_t *req);
void pgw_s5c_handle_create_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_create_bearer_response_t *req);
void pgw_s5c_handle_update_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_update_bearer_response_t *req);
void pgw_s5c_handle_delete_bearer_response(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_delete_bearer_response_t *req);
void pgw_s5c_handle_bearer_resource_command(
pgw_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp_bearer_resource_command_t *cmd);
#ifdef __cplusplus
}
#endif
#endif /* PGW_S5C_HANDLER_H */

View File

@ -1,216 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pgw-sm.h"
#include "pgw-context.h"
#include "pgw-event.h"
#include "pgw-gtp-path.h"
#include "pgw-s5c-handler.h"
#include "pgw-gx-handler.h"
#include "pgw-fd-path.h"
void pgw_state_initial(ogs_fsm_t *s, pgw_event_t *e)
{
pgw_sm_debug(e);
ogs_assert(s);
OGS_FSM_TRAN(s, &pgw_state_operational);
}
void pgw_state_final(ogs_fsm_t *s, pgw_event_t *e)
{
pgw_sm_debug(e);
ogs_assert(s);
}
void pgw_state_operational(ogs_fsm_t *s, pgw_event_t *e)
{
int rv;
ogs_pkbuf_t *recvbuf = NULL;
ogs_gtp_xact_t *xact = NULL;
ogs_gtp_message_t gtp_message;
pgw_sess_t *sess = NULL;
ogs_diam_gx_message_t *gx_message = NULL;
ogs_gtp_node_t *gnode = NULL;
pgw_sm_debug(e);
ogs_assert(s);
switch (e->id) {
case OGS_FSM_ENTRY_SIG:
rv = pgw_gtp_open();
if (rv != OGS_OK)
ogs_fatal("Can't establish PGW path");
break;
case OGS_FSM_EXIT_SIG:
pgw_gtp_close();
break;
case PGW_EVT_S5C_MESSAGE:
ogs_assert(e);
recvbuf = e->pkbuf;
ogs_assert(recvbuf);
if (ogs_gtp_parse_msg(&gtp_message, recvbuf) != OGS_OK) {
ogs_error("ogs_gtp_parse_msg() failed");
ogs_pkbuf_free(recvbuf);
break;
}
/*
* 5.5.2 in spec 29.274
*
* If a peer's TEID is not available, the TEID field still shall be
* present in the header and its value shall be set to "0" in the
* following messages:
*
* - Create Session Request message on S2a/S2b/S5/S8
*
* - Create Session Request message on S4/S11, if for a given UE,
* the SGSN/MME has not yet obtained the Control TEID of the SGW.
*
* - If a node receives a message and the TEID-C in the GTPv2 header of
* the received message is not known, it shall respond with
* "Context not found" Cause in the corresponding response message
* to the sender, the TEID used in the GTPv2-C header in the response
* message shall be then set to zero.
*
* - If a node receives a request message containing protocol error,
* e.g. Mandatory IE missing, which requires the receiver to reject
* the message as specified in clause 7.7, it shall reject
* the request message. For the response message, the node should
* look up the remote peer's TEID and accordingly set the GTPv2-C
* header TEID and the message cause code. As an implementation
* option, the node may not look up the remote peer's TEID and
* set the GTPv2-C header TEID to zero in the response message.
* However in this case, the cause code shall not be set to
* "Context not found".
*/
if (gtp_message.h.teid_presence && gtp_message.h.teid != 0) {
/* Cause is not "Context not found" */
sess = pgw_sess_find_by_teid(gtp_message.h.teid);
}
if (sess) {
gnode = sess->gnode;
ogs_assert(gnode);
} else {
gnode = e->gnode;
ogs_assert(gnode);
}
rv = ogs_gtp_xact_receive(gnode, &gtp_message.h, &xact);
if (rv != OGS_OK) {
ogs_pkbuf_free(recvbuf);
break;
}
switch(gtp_message.h.type) {
case OGS_GTP_ECHO_REQUEST_TYPE:
pgw_s5c_handle_echo_request(xact, &gtp_message.echo_request);
break;
case OGS_GTP_ECHO_RESPONSE_TYPE:
pgw_s5c_handle_echo_response(xact, &gtp_message.echo_response);
break;
case OGS_GTP_CREATE_SESSION_REQUEST_TYPE:
if (gtp_message.h.teid == 0) {
ogs_expect(!sess);
sess = pgw_sess_add_by_message(&gtp_message);
if (sess)
OGS_SETUP_GTP_NODE(sess, gnode);
}
pgw_s5c_handle_create_session_request(
sess, xact, &gtp_message.create_session_request);
break;
case OGS_GTP_DELETE_SESSION_REQUEST_TYPE:
pgw_s5c_handle_delete_session_request(
sess, xact, &gtp_message.delete_session_request);
break;
case OGS_GTP_CREATE_BEARER_RESPONSE_TYPE:
pgw_s5c_handle_create_bearer_response(
sess, xact, &gtp_message.create_bearer_response);
break;
case OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE:
pgw_s5c_handle_update_bearer_response(
sess, xact, &gtp_message.update_bearer_response);
break;
case OGS_GTP_DELETE_BEARER_RESPONSE_TYPE:
pgw_s5c_handle_delete_bearer_response(
sess, xact, &gtp_message.delete_bearer_response);
break;
case OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE:
pgw_s5c_handle_bearer_resource_command(
sess, xact, &gtp_message.bearer_resource_command);
break;
default:
ogs_warn("Not implmeneted(type:%d)", gtp_message.h.type);
break;
}
ogs_pkbuf_free(recvbuf);
break;
case PGW_EVT_GX_MESSAGE:
ogs_assert(e);
recvbuf = e->pkbuf;
ogs_assert(recvbuf);
gx_message = (ogs_diam_gx_message_t *)recvbuf->data;
ogs_assert(gx_message);
sess = e->sess;
ogs_assert(sess);
switch(gx_message->cmd_code) {
case OGS_DIAM_GX_CMD_CODE_CREDIT_CONTROL:
xact = e->xact;
ogs_assert(xact);
switch(gx_message->cc_request_type) {
case OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST:
pgw_gx_handle_cca_initial_request(
sess, gx_message, xact);
break;
case OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST:
pgw_gx_handle_cca_termination_request(
sess, gx_message, xact);
break;
default:
ogs_error("Not implemented(%d)",
gx_message->cc_request_type);
break;
}
break;
case OGS_DIAM_GX_CMD_RE_AUTH:
pgw_gx_handle_re_auth_request(sess, gx_message);
break;
default:
ogs_error("Invalid type(%d)", gx_message->cmd_code);
break;
}
ogs_diam_gx_message_free(gx_message);
ogs_pkbuf_free(recvbuf);
break;
default:
ogs_error("No handler for event %s", pgw_event_get_name(e));
break;
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PGW_SM_H
#define PGW_SM_H
#include "pgw-event.h"
#ifdef __cplusplus
extern "C" {
#endif
void pgw_state_initial(ogs_fsm_t *s, pgw_event_t *e);
void pgw_state_final(ogs_fsm_t *s, pgw_event_t *e);
void pgw_state_operational(ogs_fsm_t *s, pgw_event_t *e);
void pgw_state_exception(ogs_fsm_t *s, pgw_event_t *e);
#define pgw_sm_debug(__pe) \
ogs_debug("%s(): %s", __func__, pgw_event_get_name(__pe))
#ifdef __cplusplus
}
#endif
#endif /* PGW_SM_H */

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ogs-app.h"
int app_initialize(const char *const argv[])
{
int rv;
rv = sgw_initialize();
if (rv != OGS_OK) {
ogs_error("Failed to intialize SGW");
return rv;
}
ogs_info("SGW initialize...done");
return OGS_OK;
}
void app_terminate(void)
{
sgw_terminate();
ogs_info("SGW terminate...done");
}

View File

@ -1,55 +0,0 @@
# Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
# This file is part of Open5GS.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
libsgw_sources = files('''
sgw-event.h
sgw-context.h
sgw-gtp-path.h
sgw-sm.h
sgw-s11-handler.h
sgw-s5c-handler.h
sgw-init.c
sgw-event.c
sgw-context.c
sgw-gtp-path.c
sgw-sm.c
sgw-s11-handler.c
sgw-s5c-handler.c
'''.split())
libsgw = static_library('sgw',
sources : libsgw_sources,
dependencies : [libapp_dep, libgtp_dep],
install : false)
libsgw_dep = declare_dependency(
link_with : libsgw,
dependencies : [libapp_dep, libgtp_dep])
sgw_sources = files('''
app-init.c
../main.c
'''.split())
executable('open5gs-sgwd',
sources : sgw_sources,
c_args : '-DDEFAULT_CONFIG_FILENAME="@0@/sgw.yaml"'.format(open5gs_sysconfdir),
include_directories : srcinc,
dependencies : libsgw_dep,
install_rpath : libdir,
install : true)

View File

@ -1,879 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <yaml.h>
#include "sgw-context.h"
static sgw_context_t self;
int __sgw_log_domain;
static OGS_POOL(sgw_ue_pool, sgw_ue_t);
static OGS_POOL(sgw_sess_pool, sgw_sess_t);
static OGS_POOL(sgw_bearer_pool, sgw_bearer_t);
static OGS_POOL(sgw_tunnel_pool, sgw_tunnel_t);
static int context_initialized = 0;
void sgw_context_init(void)
{
ogs_assert(context_initialized == 0);
memset(&self, 0, sizeof(sgw_context_t));
ogs_log_install_domain(&__ogs_gtp_domain, "gtp", ogs_core()->log.level);
ogs_log_install_domain(&__sgw_log_domain, "sgw", ogs_core()->log.level);
ogs_list_init(&self.gtpc_list);
ogs_list_init(&self.gtpc_list6);
ogs_list_init(&self.gtpu_list);
ogs_list_init(&self.gtpu_list6);
ogs_list_init(&self.adv_gtpu_list);
ogs_list_init(&self.adv_gtpu_list6);
self.adv_gtpu_hash = ogs_hash_make();
self.adv_gtpu_hash6 = ogs_hash_make();
ogs_gtp_node_init(512);
ogs_list_init(&self.mme_s11_list);
ogs_list_init(&self.pgw_s5c_list);
ogs_list_init(&self.enb_s1u_list);
ogs_list_init(&self.pgw_s5u_list);
ogs_pool_init(&sgw_ue_pool, ogs_config()->pool.ue);
ogs_pool_init(&sgw_sess_pool, ogs_config()->pool.sess);
ogs_pool_init(&sgw_bearer_pool, ogs_config()->pool.bearer);
ogs_pool_init(&sgw_tunnel_pool, ogs_config()->pool.tunnel);
self.imsi_ue_hash = ogs_hash_make();
ogs_list_init(&self.sgw_ue_list);
context_initialized = 1;
}
void sgw_context_final(void)
{
ogs_assert(context_initialized == 1);
sgw_ue_remove_all();
ogs_assert(self.imsi_ue_hash);
ogs_hash_destroy(self.imsi_ue_hash);
ogs_assert(self.adv_gtpu_hash);
ogs_hash_destroy(self.adv_gtpu_hash);
ogs_assert(self.adv_gtpu_hash6);
ogs_hash_destroy(self.adv_gtpu_hash6);
ogs_pool_final(&sgw_tunnel_pool);
ogs_pool_final(&sgw_bearer_pool);
ogs_pool_final(&sgw_sess_pool);
ogs_pool_final(&sgw_ue_pool);
ogs_gtp_node_remove_all(&self.mme_s11_list);
ogs_gtp_node_remove_all(&self.pgw_s5c_list);
ogs_gtp_node_remove_all(&self.enb_s1u_list);
ogs_gtp_node_remove_all(&self.pgw_s5u_list);
ogs_gtp_node_final();
context_initialized = 0;
}
sgw_context_t *sgw_self(void)
{
return &self;
}
static int sgw_context_prepare(void)
{
self.gtpc_port = OGS_GTPV2_C_UDP_PORT;
self.gtpu_port = OGS_GTPV1_U_UDP_PORT;
return OGS_OK;
}
static int sgw_context_validation(void)
{
if (ogs_list_empty(&self.gtpc_list) &&
ogs_list_empty(&self.gtpc_list6)) {
ogs_error("No sgw.gtpc in '%s'",
ogs_config()->file);
return OGS_ERROR;
}
if (ogs_list_empty(&self.gtpu_list) &&
ogs_list_empty(&self.gtpu_list6)) {
ogs_error("No sgw.gtpu in '%s'",
ogs_config()->file);
return OGS_RETRY;
}
return OGS_OK;
}
int sgw_context_parse_config(void)
{
int rv;
yaml_document_t *document = NULL;
ogs_yaml_iter_t root_iter;
document = ogs_config()->document;
ogs_assert(document);
rv = sgw_context_prepare();
if (rv != OGS_OK) return rv;
ogs_yaml_iter_init(&root_iter, document);
while (ogs_yaml_iter_next(&root_iter)) {
const char *root_key = ogs_yaml_iter_key(&root_iter);
ogs_assert(root_key);
if (!strcmp(root_key, "sgw")) {
ogs_yaml_iter_t sgw_iter;
ogs_yaml_iter_recurse(&root_iter, &sgw_iter);
while (ogs_yaml_iter_next(&sgw_iter)) {
const char *sgw_key = ogs_yaml_iter_key(&sgw_iter);
ogs_assert(sgw_key);
if (!strcmp(sgw_key, "gtpc")) {
ogs_yaml_iter_t gtpc_array, gtpc_iter;
ogs_yaml_iter_recurse(&sgw_iter, &gtpc_array);
do {
int family = AF_UNSPEC;
int i, num = 0;
const char *hostname[OGS_MAX_NUM_OF_HOSTNAME];
uint16_t port = self.gtpc_port;
const char *dev = NULL;
ogs_sockaddr_t *addr = NULL;
if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_MAPPING_NODE) {
memcpy(&gtpc_iter, &gtpc_array,
sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&gtpc_array))
break;
ogs_yaml_iter_recurse(&gtpc_array, &gtpc_iter);
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SCALAR_NODE) {
break;
} else
ogs_assert_if_reached();
while (ogs_yaml_iter_next(&gtpc_iter)) {
const char *gtpc_key =
ogs_yaml_iter_key(&gtpc_iter);
ogs_assert(gtpc_key);
if (!strcmp(gtpc_key, "family")) {
const char *v = ogs_yaml_iter_value(&gtpc_iter);
if (v) family = atoi(v);
if (family != AF_UNSPEC &&
family != AF_INET && family != AF_INET6) {
ogs_warn("Ignore family(%d) : "
"AF_UNSPEC(%d), "
"AF_INET(%d), AF_INET6(%d) ",
family, AF_UNSPEC, AF_INET, AF_INET6);
family = AF_UNSPEC;
}
} else if (!strcmp(gtpc_key, "addr") ||
!strcmp(gtpc_key, "name")) {
ogs_yaml_iter_t hostname_iter;
ogs_yaml_iter_recurse(&gtpc_iter,
&hostname_iter);
ogs_assert(ogs_yaml_iter_type(&hostname_iter) !=
YAML_MAPPING_NODE);
do {
if (ogs_yaml_iter_type(&hostname_iter) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&hostname_iter))
break;
}
ogs_assert(num <= OGS_MAX_NUM_OF_HOSTNAME);
hostname[num++] =
ogs_yaml_iter_value(&hostname_iter);
} while (
ogs_yaml_iter_type(&hostname_iter) ==
YAML_SEQUENCE_NODE);
} else if (!strcmp(gtpc_key, "port")) {
const char *v = ogs_yaml_iter_value(&gtpc_iter);
if (v) port = atoi(v);
} else if (!strcmp(gtpc_key, "dev")) {
dev = ogs_yaml_iter_value(&gtpc_iter);
} else
ogs_warn("unknown key `%s`", gtpc_key);
}
addr = NULL;
for (i = 0; i < num; i++) {
rv = ogs_addaddrinfo(&addr,
family, hostname[i], port, 0);
ogs_assert(rv == OGS_OK);
}
if (addr) {
if (ogs_config()->parameter.no_ipv4 == 0)
ogs_socknode_add(
&self.gtpc_list, AF_INET, addr);
if (ogs_config()->parameter.no_ipv6 == 0)
ogs_socknode_add(
&self.gtpc_list6, AF_INET6, addr);
ogs_freeaddrinfo(addr);
}
if (dev) {
rv = ogs_socknode_probe(
ogs_config()->parameter.no_ipv4 ?
NULL : &self.gtpc_list,
ogs_config()->parameter.no_ipv6 ?
NULL : &self.gtpc_list6,
dev, port);
ogs_assert(rv == OGS_OK);
}
} while (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SEQUENCE_NODE);
if (ogs_list_empty(&self.gtpc_list) &&
ogs_list_empty(&self.gtpc_list6)) {
rv = ogs_socknode_probe(
ogs_config()->parameter.no_ipv4 ?
NULL : &self.gtpc_list,
ogs_config()->parameter.no_ipv6 ?
NULL : &self.gtpc_list6,
NULL, self.gtpc_port);
ogs_assert(rv == OGS_OK);
}
} else if (!strcmp(sgw_key, "gtpu")) {
ogs_yaml_iter_t gtpu_array, gtpu_iter;
ogs_yaml_iter_recurse(&sgw_iter, &gtpu_array);
do {
int family = AF_UNSPEC;
int i, num = 0;
int adv_num = 0;
const char *hostname[OGS_MAX_NUM_OF_HOSTNAME];
const char *adv_hostname[OGS_MAX_NUM_OF_HOSTNAME];
uint16_t port = self.gtpu_port;
const char *dev = NULL;
ogs_sockaddr_t *addr = NULL;
ogs_sockaddr_t *adv_addr = NULL;
if (ogs_yaml_iter_type(&gtpu_array) ==
YAML_MAPPING_NODE) {
memcpy(&gtpu_iter, &gtpu_array,
sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&gtpu_array) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&gtpu_array))
break;
ogs_yaml_iter_recurse(&gtpu_array, &gtpu_iter);
} else if (ogs_yaml_iter_type(&gtpu_array) ==
YAML_SCALAR_NODE) {
break;
} else
ogs_assert_if_reached();
while (ogs_yaml_iter_next(&gtpu_iter)) {
const char *gtpu_key =
ogs_yaml_iter_key(&gtpu_iter);
ogs_assert(gtpu_key);
if (!strcmp(gtpu_key, "family")) {
const char *v = ogs_yaml_iter_value(&gtpu_iter);
if (v) family = atoi(v);
if (family != AF_UNSPEC &&
family != AF_INET && family != AF_INET6) {
ogs_warn("Ignore family(%d) : "
"AF_UNSPEC(%d), "
"AF_INET(%d), AF_INET6(%d) ",
family, AF_UNSPEC, AF_INET, AF_INET6);
family = AF_UNSPEC;
}
} else if (!strcmp(gtpu_key, "addr") ||
!strcmp(gtpu_key, "name")) {
ogs_yaml_iter_t hostname_iter;
ogs_yaml_iter_recurse(&gtpu_iter,
&hostname_iter);
ogs_assert(ogs_yaml_iter_type(&hostname_iter) !=
YAML_MAPPING_NODE);
do {
if (ogs_yaml_iter_type(&hostname_iter) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&hostname_iter))
break;
}
ogs_assert(num <= OGS_MAX_NUM_OF_HOSTNAME);
hostname[num++] =
ogs_yaml_iter_value(&hostname_iter);
} while (
ogs_yaml_iter_type(&hostname_iter) ==
YAML_SEQUENCE_NODE);
} else if (!strcmp(gtpu_key, "port")) {
const char *v = ogs_yaml_iter_value(&gtpu_iter);
if (v) port = atoi(v);
} else if (!strcmp(gtpu_key, "dev")) {
dev = ogs_yaml_iter_value(&gtpu_iter);
} else if (!strcmp(gtpu_key, "advertise_addr") ||
!strcmp(gtpu_key, "advertise_name")) {
ogs_yaml_iter_t adv_hostname_iter;
ogs_yaml_iter_recurse(&gtpu_iter,
&adv_hostname_iter);
ogs_assert(ogs_yaml_iter_type(
&adv_hostname_iter) != YAML_MAPPING_NODE);
do {
if (ogs_yaml_iter_type(
&adv_hostname_iter) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(
&adv_hostname_iter))
break;
}
ogs_assert(adv_num <=
OGS_MAX_NUM_OF_HOSTNAME);
adv_hostname[adv_num++] =
ogs_yaml_iter_value(&adv_hostname_iter);
} while (
ogs_yaml_iter_type(&adv_hostname_iter) ==
YAML_SEQUENCE_NODE);
} else
ogs_warn("unknown key `%s`", gtpu_key);
}
addr = NULL;
for (i = 0; i < num; i++) {
rv = ogs_addaddrinfo(&addr,
family, hostname[i], port, 0);
ogs_assert(rv == OGS_OK);
}
if (addr) {
if (ogs_config()->parameter.no_ipv4 == 0)
ogs_socknode_add(
&self.gtpu_list, AF_INET, addr);
if (ogs_config()->parameter.no_ipv6 == 0)
ogs_socknode_add(
&self.gtpu_list6, AF_INET6, addr);
ogs_freeaddrinfo(addr);
}
if (dev) {
rv = ogs_socknode_probe(
ogs_config()->parameter.no_ipv4 ?
NULL : &self.gtpu_list,
ogs_config()->parameter.no_ipv6 ?
NULL : &self.gtpu_list6,
dev, port);
ogs_assert(rv == OGS_OK);
}
adv_addr = NULL;
for (i = 0; i < adv_num; i++) {
rv = ogs_addaddrinfo(&adv_addr,
family, adv_hostname[i], port, 0);
ogs_assert(rv == OGS_OK);
}
if (adv_addr) {
ogs_socknode_t *node = NULL;
if (ogs_config()->parameter.no_ipv4 == 0)
ogs_socknode_add(
&self.adv_gtpu_list, AF_INET, adv_addr);
if (ogs_config()->parameter.no_ipv6 == 0)
ogs_socknode_add(
&self.adv_gtpu_list6, AF_INET6, adv_addr);
ogs_list_for_each(&self.gtpu_list, node) {
ogs_socknode_t *adv_node =
ogs_list_first(&self.adv_gtpu_list);
if (!adv_node)
continue;
ogs_hash_set(self.adv_gtpu_hash,
&node->addr->sin.sin_addr,
sizeof(node->addr->sin.sin_addr),
adv_node->addr);
}
ogs_list_for_each(&self.gtpu_list6, node) {
ogs_socknode_t *adv_node =
ogs_list_first(&self.adv_gtpu_list6);
if (!adv_node)
continue;
ogs_hash_set(self.adv_gtpu_hash6,
&node->addr->sin6.sin6_addr,
sizeof(node->addr->sin6.sin6_addr),
adv_node->addr);
}
ogs_freeaddrinfo(adv_addr);
}
} while (ogs_yaml_iter_type(&gtpu_array) ==
YAML_SEQUENCE_NODE);
if (ogs_list_empty(&self.gtpu_list) &&
ogs_list_empty(&self.gtpu_list6)) {
rv = ogs_socknode_probe(
ogs_config()->parameter.no_ipv4 ?
NULL : &self.gtpu_list,
ogs_config()->parameter.no_ipv6 ?
NULL : &self.gtpu_list6,
NULL, self.gtpu_port);
ogs_assert(rv == OGS_OK);
}
} else
ogs_warn("unknown key `%s`", sgw_key);
}
}
}
rv = sgw_context_validation();
if (rv != OGS_OK) return rv;
return OGS_OK;
}
sgw_ue_t *sgw_ue_add_by_message(ogs_gtp_message_t *message)
{
sgw_ue_t *sgw_ue = NULL;
ogs_gtp_create_session_request_t *req = &message->create_session_request;
ogs_assert(message);
req = &message->create_session_request;
if (req->imsi.presence == 0) {
ogs_error("No IMSI");
return NULL;
}
ogs_trace("sgw_ue_add_by_message() - IMSI ");
ogs_log_hexdump(OGS_LOG_TRACE, req->imsi.data, req->imsi.len);
/*
* 7.2.1 in 3GPP TS 29.274 Release 15
*
* If the new Create Session Request received by the SGW collides with
* an existing active PDN connection context (the existing PDN connection
* context is identified with the tuple [IMSI, EPS Bearer ID], where IMSI
* shall be replaced by TAC and SNR part of ME Identity for emergency
* attached UE without UICC or authenticated IMSI), this Create Session
* Request shall be treated as a request for a new session. Before creating
* the new session, the SGW should delete:
*
* - the existing PDN connection context locally, if the Create Session
* Request is received with the TEID set to zero in the header, or
* if it is received with a TEID not set to zero in the header and
* it collides with the default bearer of an existing PDN connection
* context;
* - the existing dedicated bearer context locally, if the Create Session
* Request collides with an existing dedicated bearer context and
* the message is received with a TEID not set to zero in the header.
*/
sgw_ue = sgw_ue_find_by_imsi(req->imsi.data, req->imsi.len);
if (sgw_ue)
sgw_ue_remove(sgw_ue);
sgw_ue = sgw_ue_add(req->imsi.data, req->imsi.len);
ogs_assert(sgw_ue);
return sgw_ue;
}
sgw_ue_t *sgw_ue_add(uint8_t *imsi, int imsi_len)
{
sgw_ue_t *sgw_ue = NULL;
ogs_assert(imsi);
ogs_assert(imsi_len);
ogs_pool_alloc(&sgw_ue_pool, &sgw_ue);
ogs_assert(sgw_ue);
memset(sgw_ue, 0, sizeof *sgw_ue);
sgw_ue->sgw_s11_teid = ogs_pool_index(&sgw_ue_pool, sgw_ue);
ogs_assert(sgw_ue->sgw_s11_teid > 0 &&
sgw_ue->sgw_s11_teid <= ogs_config()->pool.ue);
/* Set IMSI */
sgw_ue->imsi_len = imsi_len;
memcpy(sgw_ue->imsi, imsi, sgw_ue->imsi_len);
ogs_buffer_to_bcd(sgw_ue->imsi, sgw_ue->imsi_len, sgw_ue->imsi_bcd);
ogs_list_init(&sgw_ue->sess_list);
ogs_hash_set(self.imsi_ue_hash, sgw_ue->imsi, sgw_ue->imsi_len, sgw_ue);
ogs_list_add(&self.sgw_ue_list, sgw_ue);
return sgw_ue;
}
int sgw_ue_remove(sgw_ue_t *sgw_ue)
{
ogs_assert(sgw_ue);
ogs_list_remove(&self.sgw_ue_list, sgw_ue);
ogs_hash_set(self.imsi_ue_hash, sgw_ue->imsi, sgw_ue->imsi_len, NULL);
sgw_sess_remove_all(sgw_ue);
ogs_pool_free(&sgw_ue_pool, sgw_ue);
return OGS_OK;
}
void sgw_ue_remove_all(void)
{
sgw_ue_t *sgw_ue = NULL, *next = NULL;;
ogs_list_for_each_safe(&self.sgw_ue_list, next, sgw_ue)
sgw_ue_remove(sgw_ue);
}
sgw_ue_t *sgw_ue_find_by_imsi_bcd(char *imsi_bcd)
{
uint8_t imsi[OGS_MAX_IMSI_LEN];
int imsi_len = 0;
ogs_assert(imsi_bcd);
ogs_bcd_to_buffer(imsi_bcd, imsi, &imsi_len);
return sgw_ue_find_by_imsi(imsi, imsi_len);
}
sgw_ue_t *sgw_ue_find_by_imsi(uint8_t *imsi, int imsi_len)
{
ogs_assert(imsi && imsi_len);
return (sgw_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len);
}
sgw_ue_t *sgw_ue_find_by_teid(uint32_t teid)
{
return ogs_pool_find(&sgw_ue_pool, teid);
}
sgw_sess_t *sgw_sess_add(sgw_ue_t *sgw_ue, char *apn, uint8_t ebi)
{
sgw_sess_t *sess = NULL;
sgw_bearer_t *bearer = NULL;
ogs_assert(sgw_ue);
ogs_assert(ebi);
ogs_pool_alloc(&sgw_sess_pool, &sess);
ogs_assert(sess);
memset(sess, 0, sizeof *sess);
sess->sgw_s5c_teid =
SGW_S5C_INDEX_TO_TEID(ogs_pool_index(&sgw_sess_pool, sess));
/* Set APN */
ogs_cpystrn(sess->pdn.apn, apn, OGS_MAX_APN_LEN+1);
sess->sgw_ue = sgw_ue;
ogs_list_init(&sess->bearer_list);
bearer = sgw_bearer_add(sess);
ogs_assert(bearer);
bearer->ebi = ebi;
ogs_list_add(&sgw_ue->sess_list, sess);
return sess;
}
int sgw_sess_remove(sgw_sess_t *sess)
{
ogs_assert(sess);
ogs_assert(sess->sgw_ue);
ogs_list_remove(&sess->sgw_ue->sess_list, sess);
sgw_bearer_remove_all(sess);
ogs_pool_free(&sgw_sess_pool, sess);
return OGS_OK;
}
void sgw_sess_remove_all(sgw_ue_t *sgw_ue)
{
sgw_sess_t *sess = NULL, *next_sess = NULL;
ogs_assert(sgw_ue);
ogs_list_for_each_safe(&sgw_ue->sess_list, next_sess, sess)
sgw_sess_remove(sess);
}
sgw_sess_t* sgw_sess_find_by_teid(uint32_t teid)
{
return ogs_pool_find(&sgw_sess_pool, SGW_S5C_TEID_TO_INDEX(teid));
}
sgw_sess_t* sgw_sess_find_by_apn(sgw_ue_t *sgw_ue, char *apn)
{
sgw_sess_t *sess = NULL;
ogs_assert(sgw_ue);
sess = sgw_sess_first(sgw_ue);
while (sess) {
if (strcmp(sess->pdn.apn, apn) == 0)
return sess;
sess = sgw_sess_next(sess);
}
return NULL;
}
sgw_sess_t* sgw_sess_find_by_ebi(sgw_ue_t *sgw_ue, uint8_t ebi)
{
sgw_bearer_t *bearer = NULL;
ogs_assert(sgw_ue);
bearer = sgw_bearer_find_by_ue_ebi(sgw_ue, ebi);
if (bearer)
return bearer->sess;
return NULL;
}
sgw_sess_t* sgw_sess_first(sgw_ue_t *sgw_ue)
{
ogs_assert(sgw_ue);
return ogs_list_first(&sgw_ue->sess_list);
}
sgw_sess_t* sgw_sess_next(sgw_sess_t *sess)
{
ogs_assert(sess);
return ogs_list_next(sess);
}
sgw_bearer_t* sgw_bearer_add(sgw_sess_t *sess)
{
sgw_bearer_t *bearer = NULL;
sgw_tunnel_t *tunnel = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_assert(sess);
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
ogs_pool_alloc(&sgw_bearer_pool, &bearer);
ogs_assert(bearer);
memset(bearer, 0, sizeof *bearer);
bearer->sgw_ue = sgw_ue;
bearer->sess = sess;
ogs_list_init(&bearer->tunnel_list);
tunnel = sgw_tunnel_add(bearer, OGS_GTP_F_TEID_S1_U_SGW_GTP_U);
ogs_assert(tunnel);
tunnel = sgw_tunnel_add(bearer, OGS_GTP_F_TEID_S5_S8_SGW_GTP_U);
ogs_assert(tunnel);
ogs_list_add(&sess->bearer_list, bearer);
return bearer;
}
int sgw_bearer_remove(sgw_bearer_t *bearer)
{
int i;
ogs_assert(bearer);
ogs_assert(bearer->sess);
ogs_list_remove(&bearer->sess->bearer_list, bearer);
sgw_tunnel_remove_all(bearer);
/* Free the buffered packets */
for (i = 0; i < bearer->num_buffered_pkt; i++)
ogs_pkbuf_free(bearer->buffered_pkts[i]);
ogs_pool_free(&sgw_bearer_pool, bearer);
return OGS_OK;
}
void sgw_bearer_remove_all(sgw_sess_t *sess)
{
sgw_bearer_t *bearer = NULL, *next_bearer = NULL;
ogs_assert(sess);
ogs_list_for_each_safe(&sess->bearer_list, next_bearer, bearer)
sgw_bearer_remove(bearer);
}
sgw_bearer_t *sgw_bearer_find_by_sgw_s5u_teid(uint32_t sgw_s5u_teid)
{
return ogs_pool_find(&sgw_bearer_pool, sgw_s5u_teid);
}
sgw_bearer_t *sgw_bearer_find_by_sess_ebi(sgw_sess_t *sess, uint8_t ebi)
{
sgw_bearer_t *bearer = NULL;
ogs_assert(sess);
bearer = sgw_bearer_first(sess);
while (bearer) {
if (ebi == bearer->ebi)
return bearer;
bearer = sgw_bearer_next(bearer);
}
return NULL;
}
sgw_bearer_t *sgw_bearer_find_by_ue_ebi(sgw_ue_t *sgw_ue, uint8_t ebi)
{
sgw_sess_t *sess = NULL;
sgw_bearer_t *bearer = NULL;
ogs_assert(sgw_ue);
sess = sgw_sess_first(sgw_ue);
while (sess) {
bearer = sgw_bearer_find_by_sess_ebi(sess, ebi);
if (bearer) {
return bearer;
}
sess = sgw_sess_next(sess);
}
return NULL;
}
sgw_bearer_t *sgw_default_bearer_in_sess(sgw_sess_t *sess)
{
return sgw_bearer_first(sess);
}
sgw_bearer_t *sgw_bearer_first(sgw_sess_t *sess)
{
ogs_assert(sess);
return ogs_list_first(&sess->bearer_list);
}
sgw_bearer_t *sgw_bearer_next(sgw_bearer_t *bearer)
{
ogs_assert(bearer);
return ogs_list_next(bearer);
}
sgw_tunnel_t *sgw_tunnel_add(sgw_bearer_t *bearer, uint8_t interface_type)
{
sgw_tunnel_t *tunnel = NULL;
ogs_assert(bearer);
ogs_pool_alloc(&sgw_tunnel_pool, &tunnel);
ogs_assert(tunnel);
memset(tunnel, 0, sizeof *tunnel);
tunnel->interface_type = interface_type;
tunnel->local_teid = ogs_pool_index(&sgw_tunnel_pool, tunnel);
ogs_assert(tunnel->local_teid > 0 &&
tunnel->local_teid <= ogs_config()->pool.tunnel);
tunnel->bearer = bearer;
ogs_list_add(&bearer->tunnel_list, tunnel);
return tunnel;
}
int sgw_tunnel_remove(sgw_tunnel_t *tunnel)
{
ogs_assert(tunnel);
ogs_assert(tunnel->bearer);
ogs_list_remove(&tunnel->bearer->tunnel_list, tunnel);
ogs_pool_free(&sgw_tunnel_pool, tunnel);
return OGS_OK;
}
void sgw_tunnel_remove_all(sgw_bearer_t *bearer)
{
sgw_tunnel_t *tunnel = NULL, *next_tunnel = NULL;
ogs_assert(bearer);
ogs_list_for_each_safe(&bearer->tunnel_list, next_tunnel, tunnel)
sgw_tunnel_remove(tunnel);
}
sgw_tunnel_t *sgw_tunnel_find_by_teid(uint32_t teid)
{
return ogs_pool_find(&sgw_tunnel_pool, teid);
}
sgw_tunnel_t *sgw_tunnel_find_by_interface_type(
sgw_bearer_t *bearer, uint8_t interface_type)
{
sgw_tunnel_t *tunnel = NULL;
ogs_assert(bearer);
tunnel = sgw_tunnel_first(bearer);
while (tunnel) {
if (tunnel->interface_type == interface_type) {
return tunnel;
}
tunnel = sgw_tunnel_next(tunnel);
}
return NULL;
}
sgw_tunnel_t *sgw_s1u_tunnel_in_bearer(sgw_bearer_t *bearer)
{
ogs_assert(bearer);
return sgw_tunnel_find_by_interface_type(
bearer, OGS_GTP_F_TEID_S1_U_SGW_GTP_U);
}
sgw_tunnel_t *sgw_s5u_tunnel_in_bearer(sgw_bearer_t *bearer)
{
ogs_assert(bearer);
return sgw_tunnel_find_by_interface_type(
bearer, OGS_GTP_F_TEID_S5_S8_SGW_GTP_U);
}
sgw_tunnel_t *sgw_tunnel_first(sgw_bearer_t *bearer)
{
ogs_assert(bearer);
return ogs_list_first(&bearer->tunnel_list);
}
sgw_tunnel_t *sgw_tunnel_next(sgw_tunnel_t *tunnel)
{
ogs_assert(tunnel);
return ogs_list_next(tunnel);
}

View File

@ -1,208 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_CONTEXT_H
#define SGW_CONTEXT_H
#include "ogs-gtp.h"
#include "ogs-app.h"
#ifdef __cplusplus
extern "C" {
#endif
extern int __sgw_log_domain;
#undef OGS_LOG_DOMAIN
#define OGS_LOG_DOMAIN __sgw_log_domain
typedef struct sgw_tunnel_s sgw_tunnel_t;
typedef struct sgw_context_s {
uint32_t gtpc_port; /* Default GTPC port */
uint32_t gtpu_port; /* Default GTPU port */
ogs_list_t gtpc_list; /* SGW GTPC IPv4 Server List */
ogs_list_t gtpc_list6; /* SGW GTPC IPv6 Server List */
ogs_sock_t *gtpc_sock; /* SGW GTPC IPv4 Socket */
ogs_sock_t *gtpc_sock6; /* SGW GTPC IPv6 Socket */
ogs_sockaddr_t *gtpc_addr; /* SGW GTPC IPv4 Address */
ogs_sockaddr_t *gtpc_addr6; /* SGW GTPC IPv6 Address */
ogs_list_t gtpu_list; /* SGW GTPU IPv4 Server List */
ogs_list_t gtpu_list6; /* SGW GTPU IPv6 Server List */
ogs_list_t adv_gtpu_list; /* Advertised SGW GTPU IPv4 Server List */
ogs_list_t adv_gtpu_list6; /* Advertised SGW GTPU IPv6 Server List */
/* Hashtable (SGW GTPU : Advertised SGW GTPU) IPv4 */
ogs_hash_t *adv_gtpu_hash;
/* Hashtable (SGW GTPU : Advertised SGW GTPU) IPv6 */
ogs_hash_t *adv_gtpu_hash6;
ogs_sock_t *gtpu_sock; /* SGW GTPU IPv4 Socket */
ogs_sock_t *gtpu_sock6; /* SGW GTPU IPv6 Socket */
ogs_sockaddr_t *gtpu_addr; /* SGW GTPU IPv4 Address */
ogs_sockaddr_t *gtpu_addr6; /* SGW GTPU IPv6 Address */
ogs_queue_t *queue; /* Queue for processing SGW control */
ogs_timer_mgr_t *timer_mgr; /* Timer Manager */
ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */
ogs_list_t mme_s11_list; /* MME GTPC Node List */
ogs_list_t pgw_s5c_list; /* PGW GTPC Node List */
ogs_list_t enb_s1u_list; /* eNB GTPU Node List */
ogs_list_t pgw_s5u_list; /* PGW GTPU Node List */
ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : SGW_UE) */
ogs_list_t sgw_ue_list; /* SGW_UE List */
} sgw_context_t;
typedef struct sgw_ue_s {
ogs_lnode_t lnode;
uint32_t sgw_s11_teid; /* SGW-S11-TEID is derived from INDEX */
uint32_t mme_s11_teid; /* MME-S11-TEID is received from MME */
/* UE identity */
uint8_t imsi[OGS_MAX_IMSI_LEN];
int imsi_len;
char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1];
#define SGW_S1U_INACTIVE 0x0001
#define SGW_DL_NOTI_SENT 0x0002
#define SGW_GET_UE_STATE(__uE) ((__uE)->state)
#define SGW_SET_UE_STATE(__uE,__sTATE) ((__uE)->state |= (__sTATE))
#define SGW_RESET_UE_STATE(__uE, __sTATE) ((__uE)->state &= ~(__sTATE))
uint32_t state;
ogs_list_t sess_list;
ogs_gtp_node_t *gnode;
} sgw_ue_t;
typedef struct sgw_sess_s {
ogs_lnode_t lnode; /* A node of list_t */
/*
* SGW-S5C-TEID = INDEX | 0x80000000
* INDEX = SGW-S5C-TEID & ~0x80000000
*/
#define SGW_S5C_TEID_TO_INDEX(__iNDEX) (__iNDEX & ~0x80000000)
#define SGW_S5C_INDEX_TO_TEID(__iNDEX) (__iNDEX | 0x80000000)
uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from INDEX */
uint32_t pgw_s5c_teid; /* PGW-S5C-TEID is received from PGW */
/* APN Configuration */
ogs_pdn_t pdn;
ogs_list_t bearer_list;
/* Related Context */
ogs_gtp_node_t *gnode;
sgw_ue_t *sgw_ue;
} sgw_sess_t;
typedef struct sgw_bearer_s {
ogs_lnode_t lnode;
uint8_t ebi;
/* User-Lication-Info */
ogs_eps_tai_t tai;
ogs_e_cgi_t e_cgi;
/* Pkts which will be buffered in case of UE-IDLE */
uint32_t num_buffered_pkt;
#define MAX_NUM_OF_PACKET_BUFFER 512
ogs_pkbuf_t* buffered_pkts[MAX_NUM_OF_PACKET_BUFFER];
ogs_list_t tunnel_list;
sgw_sess_t *sess;
sgw_ue_t *sgw_ue;
} sgw_bearer_t;
typedef struct sgw_tunnel_s {
ogs_lnode_t lnode;
uint8_t interface_type;
uint32_t local_teid;
uint32_t remote_teid;
/* Related Context */
sgw_bearer_t *bearer;
ogs_gtp_node_t *gnode;
} sgw_tunnel_t;
void sgw_context_init(void);
void sgw_context_final(void);
sgw_context_t *sgw_self(void);
int sgw_context_parse_config(void);
sgw_ue_t *sgw_ue_add_by_message(ogs_gtp_message_t *message);
sgw_ue_t *sgw_ue_find_by_imsi(uint8_t *imsi, int imsi_len);
sgw_ue_t *sgw_ue_find_by_imsi_bcd(char *imsi_bcd);
sgw_ue_t *sgw_ue_find_by_teid(uint32_t teid);
sgw_ue_t *sgw_ue_add(uint8_t *imsi, int imsi_len);
int sgw_ue_remove(sgw_ue_t *sgw_ue);
void sgw_ue_remove_all(void);
sgw_sess_t *sgw_sess_add(sgw_ue_t *sgw_ue, char *apn, uint8_t ebi);
int sgw_sess_remove(sgw_sess_t *sess);
void sgw_sess_remove_all(sgw_ue_t *sgw_ue);
sgw_sess_t *sgw_sess_find_by_apn(sgw_ue_t *sgw_ue, char *apn);
sgw_sess_t *sgw_sess_find_by_ebi(sgw_ue_t *sgw_ue, uint8_t ebi);
sgw_sess_t *sgw_sess_find_by_teid(uint32_t teid);
sgw_sess_t *sgw_sess_first(sgw_ue_t *sgw_ue);
sgw_sess_t *sgw_sess_next(sgw_sess_t *sess);
sgw_bearer_t *sgw_bearer_add(sgw_sess_t *sess);
int sgw_bearer_remove(sgw_bearer_t *bearer);
void sgw_bearer_remove_all(sgw_sess_t *sess);
sgw_bearer_t *sgw_bearer_find_by_sgw_s5u_teid(
uint32_t sgw_s5u_teid);
sgw_bearer_t *sgw_bearer_find_by_sess_ebi(
sgw_sess_t *sess, uint8_t ebi);
sgw_bearer_t *sgw_bearer_find_by_ue_ebi(
sgw_ue_t *sgw_ue, uint8_t ebi);
sgw_bearer_t *sgw_default_bearer_in_sess(sgw_sess_t *sess);
sgw_bearer_t *sgw_bearer_first(sgw_sess_t *sess);
sgw_bearer_t *sgw_bearer_next(sgw_bearer_t *bearer);
sgw_tunnel_t *sgw_tunnel_add(
sgw_bearer_t *bearer, uint8_t interface_type);
int sgw_tunnel_remove(sgw_tunnel_t *tunnel);
void sgw_tunnel_remove_all(sgw_bearer_t *bearer);
sgw_tunnel_t *sgw_tunnel_find_by_teid(uint32_t teid);
sgw_tunnel_t *sgw_tunnel_find_by_interface_type(
sgw_bearer_t *bearer, uint8_t interface_type);
sgw_tunnel_t *sgw_s1u_tunnel_in_bearer(sgw_bearer_t *bearer);
sgw_tunnel_t *sgw_s5u_tunnel_in_bearer(sgw_bearer_t *bearer);
sgw_tunnel_t *sgw_tunnel_first(sgw_bearer_t *bearer);
sgw_tunnel_t *sgw_tunnel_next(sgw_tunnel_t *tunnel);
#ifdef __cplusplus
}
#endif
#endif /* SGW_CONTEXT_H */

View File

@ -1,96 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sgw-event.h"
#include "sgw-context.h"
static OGS_POOL(pool, sgw_event_t);
#define EVENT_POOL 32 /* FIXME : 32 */
void sgw_event_init(void)
{
ogs_pool_init(&pool, EVENT_POOL);
sgw_self()->queue = ogs_queue_create(EVENT_POOL);
ogs_assert(sgw_self()->queue);
sgw_self()->timer_mgr = ogs_timer_mgr_create();
ogs_assert(sgw_self()->timer_mgr);
sgw_self()->pollset = ogs_pollset_create();
ogs_assert(sgw_self()->pollset);
}
void sgw_event_term(void)
{
ogs_queue_term(sgw_self()->queue);
ogs_pollset_notify(sgw_self()->pollset);
}
void sgw_event_final(void)
{
if (sgw_self()->pollset)
ogs_pollset_destroy(sgw_self()->pollset);
if (sgw_self()->timer_mgr)
ogs_timer_mgr_destroy(sgw_self()->timer_mgr);
if (sgw_self()->queue)
ogs_queue_destroy(sgw_self()->queue);
ogs_pool_final(&pool);
}
sgw_event_t *sgw_event_new(sgw_event_e id)
{
sgw_event_t *e = NULL;
ogs_pool_alloc(&pool, &e);
ogs_assert(e);
memset(e, 0, sizeof(*e));
e->id = id;
return e;
}
void sgw_event_free(sgw_event_t *e)
{
ogs_assert(e);
ogs_pool_free(&pool, e);
}
const char *sgw_event_get_name(sgw_event_t *e)
{
if (e == NULL)
return OGS_FSM_NAME_INIT_SIG;
switch (e->id) {
case OGS_FSM_ENTRY_SIG:
return OGS_FSM_NAME_ENTRY_SIG;
case OGS_FSM_EXIT_SIG:
return OGS_FSM_NAME_EXIT_SIG;
case SGW_EVT_S11_MESSAGE:
return "SGW_EVT_S11_MESSAGE";
case SGW_EVT_S5C_MESSAGE:
return "SGW_EVT_S5C_MESSAGE";
default:
break;
}
return "UNKNOWN_EVENT";
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_EVENT_H
#define SGW_EVENT_H
#include "ogs-core.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ogs_gtp_node_s ogs_gtp_node_t;
typedef struct sgw_bearer_s sgw_bearer_t;
typedef enum {
SGW_EVT_BASE = OGS_FSM_USER_SIG,
SGW_EVT_S11_MESSAGE,
SGW_EVT_S5C_MESSAGE,
SGW_EVT_LO_DLDATA_NOTI,
SGW_EVT_TOP,
} sgw_event_e;
typedef struct sgw_event_s {
int id;
ogs_pkbuf_t *pkbuf;
ogs_gtp_node_t *gnode;
sgw_bearer_t *bearer;
} sgw_event_t;
void sgw_event_init(void);
void sgw_event_term(void);
void sgw_event_final(void);
sgw_event_t *sgw_event_new(sgw_event_e id);
void sgw_event_free(sgw_event_t *e);
const char *sgw_event_get_name(sgw_event_t *e);
#ifdef __cplusplus
}
#endif
#endif /* SGW_EVENT_H */

View File

@ -1,402 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sgw-context.h"
#include "sgw-event.h"
#include "sgw-gtp-path.h"
static ogs_pkbuf_pool_t *packet_pool = NULL;
static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data)
{
sgw_event_t *e = NULL;
int rv;
ssize_t size;
ogs_pkbuf_t *pkbuf = NULL;
ogs_sockaddr_t from;
ogs_gtp_node_t *gnode = NULL;
ogs_assert(fd != INVALID_SOCKET);
pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN);
ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN);
size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from);
if (size <= 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"ogs_recvfrom() failed");
ogs_pkbuf_free(pkbuf);
return;
}
ogs_pkbuf_trim(pkbuf, size);
/*
* 5.5.2 in spec 29.274
*
* If a peer's TEID is not available, the TEID field still shall be
* present in the header and its value shall be set to "0" in the
* following messages:
*
* - Create Session Request message on S2a/S2b/S5/S8
*
* - Create Session Request message on S4/S11, if for a given UE,
* the SGSN/MME has not yet obtained the Control TEID of the SGW.
*
* - If a node receives a message and the TEID-C in the GTPv2 header of
* the received message is not known, it shall respond with
* "Context not found" Cause in the corresponding response message
* to the sender, the TEID used in the GTPv2-C header in the response
* message shall be then set to zero.
*
* - If a node receives a request message containing protocol error,
* e.g. Mandatory IE missing, which requires the receiver to reject
* the message as specified in clause 7.7, it shall reject
* the request message. For the response message, the node should
* look up the remote peer's TEID and accordingly set the GTPv2-C
* header TEID and the message cause code. As an implementation
* option, the node may not look up the remote peer's TEID and
* set the GTPv2-C header TEID to zero in the response message.
* However in this case, the cause code shall not be set to
* "Context not found".
*/
gnode = ogs_gtp_node_find_by_addr(&sgw_self()->pgw_s5c_list, &from);
if (gnode) {
e = sgw_event_new(SGW_EVT_S5C_MESSAGE);
ogs_assert(e);
e->gnode = gnode;
} else {
e = sgw_event_new(SGW_EVT_S11_MESSAGE);
gnode = ogs_gtp_node_find_by_addr(&sgw_self()->mme_s11_list, &from);
if (!gnode) {
gnode = ogs_gtp_node_add_by_addr(&sgw_self()->mme_s11_list, &from);
ogs_assert(gnode);
gnode->sock = data;
}
ogs_assert(e);
e->gnode = gnode;
}
e->pkbuf = pkbuf;
rv = ogs_queue_push(sgw_self()->queue, e);
if (rv != OGS_OK) {
ogs_error("ogs_queue_push() failed:%d", (int)rv);
ogs_pkbuf_free(e->pkbuf);
sgw_event_free(e);
}
}
static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ssize_t size;
ogs_pkbuf_t *pkbuf = NULL;
ogs_sockaddr_t from;
ogs_gtp_header_t *gtp_h = NULL;
sgw_bearer_t *bearer = NULL;
sgw_tunnel_t *tunnel = NULL;
uint32_t teid;
int i;
ogs_assert(fd != INVALID_SOCKET);
ogs_assert(packet_pool);
pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_SDU_LEN);
ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN);
size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from);
if (size <= 0) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"ogs_recvfrom() failed");
ogs_pkbuf_free(pkbuf);
return;
}
ogs_pkbuf_trim(pkbuf, size);
gtp_h = (ogs_gtp_header_t *)pkbuf->data;
if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) {
ogs_pkbuf_t *echo_rsp;
ogs_debug("[SGW] RECV Echo Request from [%s]",
OGS_ADDR(&from, buf));
echo_rsp = ogs_gtp_handle_echo_req(pkbuf);
if (echo_rsp) {
ssize_t sent;
/* Echo reply */
ogs_debug("[SGW] SEND Echo Response to [%s]",
OGS_ADDR(&from, buf));
sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from);
if (sent < 0 || sent != echo_rsp->len) {
ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
"ogs_sendto() failed");
}
ogs_pkbuf_free(echo_rsp);
}
} else if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU ||
gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) {
teid = ntohl(gtp_h->teid);
if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU)
ogs_debug("[SGW] RECV GPU-U from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
else if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER)
ogs_debug("[SGW] RECV End Marker from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
tunnel = sgw_tunnel_find_by_teid(teid);
if (!tunnel) {
if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU)
ogs_warn("[SGW] RECV GPU-U from [%s] : No TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
else if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER)
ogs_warn("[SGW] RECV End Marker from [%s] : No TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
ogs_pkbuf_free(pkbuf);
return;
}
bearer = tunnel->bearer;
ogs_assert(bearer);
/* Convert TEID */
if (tunnel->interface_type == OGS_GTP_F_TEID_S1_U_SGW_GTP_U) {
sgw_tunnel_t *s5u_tunnel = NULL;
s5u_tunnel = sgw_s5u_tunnel_in_bearer(bearer);
ogs_assert(s5u_tunnel);
ogs_assert(s5u_tunnel->gnode);
ogs_assert(s5u_tunnel->gnode->sock);
ogs_debug("[SGW] SEND GPU-U to PGW[%s]: TEID[0x%x]",
OGS_ADDR(&s5u_tunnel->gnode->addr, buf),
s5u_tunnel->remote_teid);
gtp_h->teid = htonl(s5u_tunnel->remote_teid);
ogs_gtp_sendto(s5u_tunnel->gnode, pkbuf);
} else if (tunnel->interface_type ==
OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING ||
tunnel->interface_type ==
OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) {
sgw_tunnel_t *indirect_tunnel = NULL;
indirect_tunnel = sgw_tunnel_find_by_interface_type(bearer,
tunnel->interface_type);
ogs_assert(indirect_tunnel);
ogs_assert(indirect_tunnel->gnode);
ogs_assert(indirect_tunnel->gnode->sock);
ogs_debug("[SGW] SEND GPU-U to Indirect Tunnel[%s]: TEID[0x%x]",
OGS_ADDR(&indirect_tunnel->gnode->addr, buf),
indirect_tunnel->remote_teid);
gtp_h->teid = htonl(indirect_tunnel->remote_teid);
ogs_gtp_sendto(indirect_tunnel->gnode, pkbuf);
} else if (tunnel->interface_type == OGS_GTP_F_TEID_S5_S8_SGW_GTP_U) {
sgw_tunnel_t *s1u_tunnel = NULL;
s1u_tunnel = sgw_s1u_tunnel_in_bearer(bearer);
ogs_assert(s1u_tunnel);
if (s1u_tunnel->remote_teid) {
ogs_assert(s1u_tunnel->gnode);
ogs_assert(s1u_tunnel->gnode->sock);
ogs_debug("[SGW] SEND GPU-U to ENB[%s]: TEID[0x%x]",
OGS_ADDR(&s1u_tunnel->gnode->addr, buf),
s1u_tunnel->remote_teid);
/* If there is buffered packet, send it first */
for (i = 0; i < bearer->num_buffered_pkt; i++) {
ogs_gtp_header_t *gtp_h = NULL;
gtp_h = (ogs_gtp_header_t *)bearer->buffered_pkts[i]->data;
gtp_h->teid = htonl(s1u_tunnel->remote_teid);
ogs_gtp_sendto(s1u_tunnel->gnode, bearer->buffered_pkts[i]);
ogs_pkbuf_free(bearer->buffered_pkts[i]);
}
bearer->num_buffered_pkt = 0;
gtp_h->teid = htonl(s1u_tunnel->remote_teid);
ogs_gtp_sendto(s1u_tunnel->gnode, pkbuf);
} else {
/* S1U path is deactivated.
* Send downlink_data_notification to MME.
*
*/
sgw_ue_t *sgw_ue = NULL;
ogs_assert(bearer->sess);
ogs_assert(bearer->sess->sgw_ue);
sgw_ue = bearer->sess->sgw_ue;
ogs_debug("[SGW] S1U PATH deactivated : STATE[0x%x]",
SGW_GET_UE_STATE(sgw_ue));
if ((SGW_GET_UE_STATE(sgw_ue) & SGW_S1U_INACTIVE)) {
ogs_debug(" SGW-S1U Inactive");
if (!(SGW_GET_UE_STATE(sgw_ue) & SGW_DL_NOTI_SENT)) {
sgw_event_t *e;
ogs_debug(" EVENT DL Data Notification");
e = sgw_event_new(SGW_EVT_LO_DLDATA_NOTI);
ogs_assert(e);
e->bearer = bearer;
rv = ogs_queue_push(sgw_self()->queue, e);
if (rv != OGS_OK) {
ogs_error("ogs_queue_push() failed:%d", (int)rv);
sgw_event_free(e);
}
SGW_SET_UE_STATE(sgw_ue, SGW_DL_NOTI_SENT);
}
/* Buffer the packet */
if (bearer->num_buffered_pkt < MAX_NUM_OF_PACKET_BUFFER) {
bearer->buffered_pkts[bearer->num_buffered_pkt++] =
pkbuf;
return;
}
} else {
/* UE is S1U_ACTIVE state but there is no s1u teid */
ogs_debug("[SGW] UE is ACITVE but there is no matched "
"ENB_S1U_TEID[%d]", teid);
/* Just drop it */
}
}
}
}
ogs_pkbuf_free(pkbuf);
return;
}
int sgw_gtp_open(void)
{
ogs_socknode_t *node = NULL;
ogs_sock_t *sock = NULL;
ogs_pkbuf_config_t config;
memset(&config, 0, sizeof config);
config.cluster_8192_pool = ogs_config()->pool.packet;
packet_pool = ogs_pkbuf_pool_create(&config);
ogs_list_for_each(&sgw_self()->gtpc_list, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(sgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
}
ogs_list_for_each(&sgw_self()->gtpc_list6, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(sgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock);
}
sgw_self()->gtpc_sock = ogs_socknode_sock_first(&sgw_self()->gtpc_list);
if (sgw_self()->gtpc_sock)
sgw_self()->gtpc_addr = &sgw_self()->gtpc_sock->local_addr;
sgw_self()->gtpc_sock6 = ogs_socknode_sock_first(&sgw_self()->gtpc_list6);
if (sgw_self()->gtpc_sock6)
sgw_self()->gtpc_addr6 = &sgw_self()->gtpc_sock6->local_addr;
ogs_assert(sgw_self()->gtpc_addr || sgw_self()->gtpc_addr6);
ogs_list_for_each(&sgw_self()->gtpu_list, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(sgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv1_u_recv_cb, sock);
}
ogs_list_for_each(&sgw_self()->gtpu_list6, node) {
sock = ogs_gtp_server(node);
ogs_assert(sock);
node->poll = ogs_pollset_add(sgw_self()->pollset,
OGS_POLLIN, sock->fd, _gtpv1_u_recv_cb, sock);
}
sgw_self()->gtpu_sock = ogs_socknode_sock_first(&sgw_self()->gtpu_list);
if (sgw_self()->gtpu_sock)
sgw_self()->gtpu_addr = &sgw_self()->gtpu_sock->local_addr;
sgw_self()->gtpu_sock6 = ogs_socknode_sock_first(&sgw_self()->gtpu_list6);
if (sgw_self()->gtpu_sock6)
sgw_self()->gtpu_addr6 = &sgw_self()->gtpu_sock6->local_addr;
ogs_assert(sgw_self()->gtpu_addr || sgw_self()->gtpu_addr6);
return OGS_OK;
}
void sgw_gtp_close(void)
{
ogs_socknode_remove_all(&sgw_self()->gtpc_list);
ogs_socknode_remove_all(&sgw_self()->gtpc_list6);
ogs_socknode_remove_all(&sgw_self()->gtpu_list);
ogs_socknode_remove_all(&sgw_self()->gtpu_list6);
ogs_socknode_remove_all(&sgw_self()->adv_gtpu_list);
ogs_socknode_remove_all(&sgw_self()->adv_gtpu_list6);
ogs_pkbuf_pool_destroy(packet_pool);
}
void sgw_gtp_send_end_marker(sgw_tunnel_t *s1u_tunnel)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ogs_pkbuf_t *pkbuf = NULL;
ogs_gtp_header_t *h = NULL;
ogs_assert(s1u_tunnel);
ogs_assert(s1u_tunnel->gnode);
ogs_assert(s1u_tunnel->gnode->sock);
ogs_debug("[SGW] SEND End Marker to ENB[%s]: TEID[0x%x]",
OGS_ADDR(&s1u_tunnel->gnode->addr, buf),
s1u_tunnel->remote_teid);
pkbuf = ogs_pkbuf_alloc(NULL,
100 /* enough for END_MARKER; use smaller buffer */);
ogs_pkbuf_put(pkbuf, 100);
h = (ogs_gtp_header_t *)pkbuf->data;
memset(h, 0, OGS_GTPV1U_HEADER_LEN);
/*
* Flags
* 0x20 - Version : GTP release 99 version (1)
* 0x10 - Protocol Type : GTP (1)
*/
h->flags = 0x30;
h->type = OGS_GTPU_MSGTYPE_END_MARKER;
h->teid = htonl(s1u_tunnel->remote_teid);
rv = ogs_gtp_sendto(s1u_tunnel->gnode, pkbuf);
ogs_expect(rv == OGS_OK);
ogs_pkbuf_free(pkbuf);
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_GTP_PATH_H
#define SGW_GTP_PATH_H
#ifdef __cplusplus
extern "C" {
#endif
int sgw_gtp_open(void);
void sgw_gtp_close(void);
void sgw_gtp_send_end_marker(sgw_tunnel_t *s1u_tunnel);
#ifdef __cplusplus
}
#endif
#endif /* SGW_GTP_PATH_H */

View File

@ -1,115 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sgw-context.h"
#include "sgw-sm.h"
#include "sgw-event.h"
static ogs_thread_t *thread;
static void sgw_main(void *data);
static int initialized = 0;
int sgw_initialize()
{
int rv;
sgw_context_init();
sgw_event_init();
rv = ogs_gtp_xact_init(sgw_self()->timer_mgr, 512);
if (rv != OGS_OK) return rv;
rv = sgw_context_parse_config();
if (rv != OGS_OK) return rv;
rv = ogs_log_config_domain(
ogs_config()->logger.domain, ogs_config()->logger.level);
if (rv != OGS_OK) return rv;
thread = ogs_thread_create(sgw_main, NULL);
if (!thread) return OGS_ERROR;
initialized = 1;
return OGS_OK;
}
void sgw_terminate(void)
{
if (!initialized) return;
sgw_event_term();
ogs_thread_destroy(thread);
sgw_context_final();
ogs_gtp_xact_final();
sgw_event_final();
}
static void sgw_main(void *data)
{
ogs_fsm_t sgw_sm;
int rv;
ogs_fsm_create(&sgw_sm, sgw_state_initial, sgw_state_final);
ogs_fsm_init(&sgw_sm, 0);
for ( ;; ) {
ogs_pollset_poll(sgw_self()->pollset,
ogs_timer_mgr_next(sgw_self()->timer_mgr));
/*
* After ogs_pollset_poll(), ogs_timer_mgr_expire() must be called.
*
* The reason is why ogs_timer_mgr_next() can get the corrent value
* when ogs_timer_stop() is called internally in ogs_timer_mgr_expire().
*
* You should not use event-queue before ogs_timer_mgr_expire().
* In this case, ogs_timer_mgr_expire() does not work
* because 'if rv == OGS_DONE' statement is exiting and
* not calling ogs_timer_mgr_expire().
*/
ogs_timer_mgr_expire(sgw_self()->timer_mgr);
for ( ;; ) {
sgw_event_t *e = NULL;
rv = ogs_queue_trypop(sgw_self()->queue, (void**)&e);
ogs_assert(rv != OGS_ERROR);
if (rv == OGS_DONE)
goto done;
if (rv == OGS_RETRY)
break;
ogs_assert(e);
ogs_fsm_dispatch(&sgw_sm, e);
sgw_event_free(e);
}
}
done:
ogs_fsm_fini(&sgw_sm, 0);
ogs_fsm_delete(&sgw_sm);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_S11_HANDLER_H
#define SGW_S11_HANDLER_H
#include "sgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
void sgw_s11_handle_create_session_request(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
void sgw_s11_handle_modify_bearer_request(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_modify_bearer_request_t *req);
void sgw_s11_handle_delete_session_request(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
void sgw_s11_handle_create_bearer_response(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
void sgw_s11_handle_update_bearer_response(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
void sgw_s11_handle_delete_bearer_response(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
void sgw_s11_handle_release_access_bearers_request(
ogs_gtp_xact_t *s11_xact, sgw_ue_t *sgw_ue,
ogs_gtp_release_access_bearers_request_t *req);
void sgw_s11_handle_lo_dldata_notification(sgw_bearer_t *bearer);
void sgw_s11_handle_downlink_data_notification_ack(
ogs_gtp_xact_t *s11_xact, sgw_ue_t *sgw_ue,
ogs_gtp_downlink_data_notification_acknowledge_t *ack);
void sgw_s11_handle_create_indirect_data_forwarding_tunnel_request(
ogs_gtp_xact_t *s11_xact, sgw_ue_t *sgw_ue,
ogs_gtp_create_indirect_data_forwarding_tunnel_request_t *req);
void sgw_s11_handle_delete_indirect_data_forwarding_tunnel_request(
ogs_gtp_xact_t *s11_xact, sgw_ue_t *sgw_ue);
void sgw_s11_handle_bearer_resource_command(ogs_gtp_xact_t *s11_xact,
sgw_ue_t *sgw_ue, ogs_gtp_message_t *message);
#ifdef __cplusplus
}
#endif
#endif /* SGW_S11_HANDLER_H */

View File

@ -1,623 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sgw-event.h"
#include "sgw-context.h"
#include "sgw-gtp-path.h"
#include "sgw-s5c-handler.h"
static void timeout(ogs_gtp_xact_t *xact, void *data)
{
sgw_sess_t *sess = data;
sgw_ue_t *sgw_ue = NULL;
uint8_t type = 0;
ogs_assert(xact);
ogs_assert(sess);
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
type = xact->seq[0].type;
ogs_error("GTP Timeout : IMSI[%s] Message-Type[%d]",
sgw_ue->imsi_bcd, type);
}
void sgw_s5c_handle_create_session_response(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
int rv;
uint8_t cause_value;
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
ogs_gtp_node_t *pgw = NULL;
ogs_gtp_xact_t *s11_xact = NULL;
sgw_bearer_t *bearer = NULL;
sgw_tunnel_t *s1u_tunnel = NULL, *s5u_tunnel = NULL;
ogs_gtp_create_session_response_t *rsp = NULL;
ogs_pkbuf_t *pkbuf = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_gtp_f_teid_t *pgw_s5c_teid = NULL;
ogs_gtp_f_teid_t sgw_s11_teid;
ogs_gtp_f_teid_t *pgw_s5u_teid = NULL;
ogs_gtp_f_teid_t sgw_s1u_teid;
int len;
ogs_assert(s5c_xact);
s11_xact = s5c_xact->assoc_xact;
ogs_assert(s11_xact);
ogs_assert(message);
ogs_debug("[SGW] Create Session Response");
if (!sess) {
ogs_warn("No Context in TEID");
sess = s5c_xact->data;
ogs_assert(sess);
}
rv = ogs_gtp_xact_commit(s5c_xact);
ogs_expect(rv == OGS_OK);
rsp = &message->create_session_response;
if (rsp->cause.presence) {
ogs_gtp_cause_t *cause = rsp->cause.data;
ogs_assert(cause);
cause_value = cause->value;
if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
if (rsp->bearer_contexts_created.cause.presence) {
cause = rsp->bearer_contexts_created.cause.data;
ogs_assert(cause);
cause_value = cause->value;
} else {
ogs_error("No Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
} else {
/* Deliver PGW cause value to the MME */
ogs_warn("Cause[%d] : No Accepted", cause_value);
ogs_gtp_send_error_message(
s11_xact, sgw_ue ? sgw_ue->mme_s11_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE,
cause_value);
return;
}
} else {
ogs_error("No Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED)
ogs_warn("Cause[%d] : No Accepted", cause_value);
if (rsp->bearer_contexts_created.cause.presence == 0) {
ogs_error("No EPS Bearer Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (rsp->bearer_contexts_created.presence == 0) {
ogs_error("No Bearer");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (rsp->bearer_contexts_created.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
bearer = sgw_bearer_find_by_sess_ebi(sess,
rsp->bearer_contexts_created.eps_bearer_id.u8);
ogs_assert(bearer);
s1u_tunnel = sgw_s1u_tunnel_in_bearer(bearer);
ogs_assert(s1u_tunnel);
s5u_tunnel = sgw_s5u_tunnel_in_bearer(bearer);
ogs_assert(s5u_tunnel);
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
}
if (!bearer) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.
presence == 0) {
ogs_error("No GTP TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.presence == 0) {
ogs_error("No GTP TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(s11_xact, sgw_ue ? sgw_ue->mme_s11_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE,
cause_value);
return;
}
/* Receive Control Plane(UL) : PGW-S5C */
pgw_s5c_teid = rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.
data;
ogs_assert(pgw_s5c_teid);
sess->pgw_s5c_teid = ntohl(pgw_s5c_teid->teid);
/* Receive Data Plane(UL) : PGW-S5U */
pgw_s5u_teid = rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.data;
ogs_assert(pgw_s5u_teid);
s5u_tunnel->remote_teid = ntohl(pgw_s5u_teid->teid);
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgw_ue->mme_s11_teid, sgw_ue->sgw_s11_teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
ogs_debug(" ENB_S1U_TEID[%d] SGW_S1U_TEID[%d]",
s1u_tunnel->remote_teid, s1u_tunnel->local_teid);
ogs_debug(" SGW_S5U_TEID[%d] PGW_S5U_TEID[%d]",
s5u_tunnel->local_teid, s5u_tunnel->remote_teid);
pgw = ogs_gtp_node_find_by_f_teid(&sgw_self()->pgw_s5u_list, pgw_s5u_teid);
if (!pgw) {
pgw = ogs_gtp_node_add_by_f_teid(
&sgw_self()->pgw_s5u_list, pgw_s5u_teid, sgw_self()->gtpu_port,
ogs_config()->parameter.no_ipv4,
ogs_config()->parameter.no_ipv6,
ogs_config()->parameter.prefer_ipv4);
ogs_assert(pgw);
rv = ogs_gtp_connect(
sgw_self()->gtpu_sock, sgw_self()->gtpu_sock6, pgw);
ogs_assert(rv == OGS_OK);
}
/* Setup GTP Node */
OGS_SETUP_GTP_NODE(s5u_tunnel, pgw);
/* Send Control Plane(UL) : SGW-S11 */
memset(&sgw_s11_teid, 0, sizeof(ogs_gtp_f_teid_t));
sgw_s11_teid.interface_type = OGS_GTP_F_TEID_S11_S4_SGW_GTP_C;
sgw_s11_teid.teid = htonl(sgw_ue->sgw_s11_teid);
rv = ogs_gtp_sockaddr_to_f_teid(
sgw_self()->gtpc_addr, sgw_self()->gtpc_addr6, &sgw_s11_teid, &len);
ogs_assert(rv == OGS_OK);
rsp->sender_f_teid_for_control_plane.presence = 1;
rsp->sender_f_teid_for_control_plane.data = &sgw_s11_teid;
rsp->sender_f_teid_for_control_plane.len = len;
/* Send Data Plane(UL) : SGW-S1U */
memset(&sgw_s1u_teid, 0, sizeof(ogs_gtp_f_teid_t));
sgw_s1u_teid.interface_type = s1u_tunnel->interface_type;
sgw_s1u_teid.teid = htonl(s1u_tunnel->local_teid);
if (sgw_self()->gtpu_addr) {
addr = ogs_hash_get(sgw_self()->adv_gtpu_hash,
&sgw_self()->gtpu_addr->sin.sin_addr,
sizeof(sgw_self()->gtpu_addr->sin.sin_addr));
}
if (sgw_self()->gtpu_addr6) {
addr6 = ogs_hash_get(sgw_self()->adv_gtpu_hash6,
&sgw_self()->gtpu_addr6->sin6.sin6_addr,
sizeof(sgw_self()->gtpu_addr6->sin6.sin6_addr));
}
// Swap the SGW-S1U IP to IP to be advertised to UE
if (addr || addr6) {
rv = ogs_gtp_sockaddr_to_f_teid(addr, addr6, &sgw_s1u_teid, &len);
ogs_assert(rv == OGS_OK);
} else {
rv = ogs_gtp_sockaddr_to_f_teid(
sgw_self()->gtpu_addr, sgw_self()->gtpu_addr6, &sgw_s1u_teid, &len);
ogs_assert(rv == OGS_OK);
}
rsp->bearer_contexts_created.s1_u_enodeb_f_teid.presence = 1;
rsp->bearer_contexts_created.s1_u_enodeb_f_teid.data = &sgw_s1u_teid;
rsp->bearer_contexts_created.s1_u_enodeb_f_teid.len = len;
message->h.type = OGS_GTP_CREATE_SESSION_RESPONSE_TYPE;
message->h.teid = sgw_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(message);
ogs_expect_or_return(pkbuf);
rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
}
void sgw_s5c_handle_delete_session_response(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
int rv;
uint8_t cause_value;
ogs_gtp_xact_t *s11_xact = NULL;
ogs_gtp_delete_session_response_t *rsp = NULL;
ogs_pkbuf_t *pkbuf = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_assert(s5c_xact);
s11_xact = s5c_xact->assoc_xact;
ogs_assert(s11_xact);
ogs_assert(message);
ogs_debug("[SGW] Delete Session Response");
if (!sess) {
ogs_warn("No Context in TEID");
sess = s5c_xact->data;
ogs_assert(sess);
}
rv = ogs_gtp_xact_commit(s5c_xact);
ogs_expect(rv == OGS_OK);
rsp = &message->delete_session_response;
if (rsp->cause.presence) {
ogs_gtp_cause_t *cause = rsp->cause.data;
ogs_assert(cause);
cause_value = cause->value;
} else {
ogs_error("No Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(s11_xact, sgw_ue ? sgw_ue->mme_s11_teid : 0,
OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
/* Remove a pgw session */
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgw_ue->mme_s11_teid, sgw_ue->sgw_s11_teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
sgw_sess_remove(sess);
message->h.type = OGS_GTP_DELETE_SESSION_RESPONSE_TYPE;
message->h.teid = sgw_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(message);
ogs_expect_or_return(pkbuf);
rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
}
void sgw_s5c_handle_create_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
int rv;
uint8_t cause_value = 0;
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
ogs_gtp_node_t *pgw = NULL;
ogs_gtp_xact_t *s11_xact = NULL;
sgw_bearer_t *bearer = NULL;
sgw_tunnel_t *s1u_tunnel = NULL, *s5u_tunnel = NULL;
ogs_gtp_create_bearer_request_t *req = NULL;
ogs_pkbuf_t *pkbuf = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_gtp_f_teid_t *pgw_s5u_teid = NULL;
ogs_gtp_f_teid_t sgw_s1u_teid;
int len;
ogs_assert(s5c_xact);
ogs_assert(message);
ogs_debug("[SGW] Create Bearer Request");
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
req = &message->create_bearer_request;
if (!sess) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (req->linked_eps_bearer_id.presence == 0) {
ogs_error("No Linked EBI");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts.presence == 0) {
ogs_error("No Bearer");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts.s5_s8_u_sgw_f_teid.presence == 0) {
ogs_error("No GTP TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0,
OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value);
return;
}
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
bearer = sgw_bearer_add(sess);
ogs_assert(bearer);
s1u_tunnel = sgw_s1u_tunnel_in_bearer(bearer);
ogs_assert(s1u_tunnel);
s5u_tunnel = sgw_s5u_tunnel_in_bearer(bearer);
ogs_assert(s5u_tunnel);
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgw_ue->mme_s11_teid, sgw_ue->sgw_s11_teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
/* Receive Data Plane(UL) : PGW-S5U */
pgw_s5u_teid = req->bearer_contexts.s5_s8_u_sgw_f_teid.data;
ogs_assert(pgw_s5u_teid);
s5u_tunnel->remote_teid = ntohl(pgw_s5u_teid->teid);
pgw = ogs_gtp_node_find_by_f_teid(&sgw_self()->pgw_s5u_list, pgw_s5u_teid);
if (!pgw) {
pgw = ogs_gtp_node_add_by_f_teid(
&sgw_self()->pgw_s5u_list, pgw_s5u_teid, sgw_self()->gtpu_port,
ogs_config()->parameter.no_ipv4,
ogs_config()->parameter.no_ipv6,
ogs_config()->parameter.prefer_ipv4);
ogs_assert(pgw);
rv = ogs_gtp_connect(
sgw_self()->gtpu_sock, sgw_self()->gtpu_sock6, pgw);
ogs_assert(rv == OGS_OK);
}
/* Setup GTP Node */
OGS_SETUP_GTP_NODE(s5u_tunnel, pgw);
/* Remove S5U-F-TEID */
req->bearer_contexts.s5_s8_u_sgw_f_teid.presence = 0;
/* Send Data Plane(UL) : SGW-S1U */
memset(&sgw_s1u_teid, 0, sizeof(ogs_gtp_f_teid_t));
sgw_s1u_teid.interface_type = s1u_tunnel->interface_type;
sgw_s1u_teid.teid = htonl(s1u_tunnel->local_teid);
if (sgw_self()->gtpu_addr) {
addr = ogs_hash_get(sgw_self()->adv_gtpu_hash,
&sgw_self()->gtpu_addr->sin.sin_addr,
sizeof(sgw_self()->gtpu_addr->sin.sin_addr));
}
if (sgw_self()->gtpu_addr6) {
addr6 = ogs_hash_get(sgw_self()->adv_gtpu_hash6,
&sgw_self()->gtpu_addr6->sin6.sin6_addr,
sizeof(sgw_self()->gtpu_addr6->sin6.sin6_addr));
}
// Swap the SGW-S1U IP to IP to be advertised to UE
if (addr || addr6) {
rv = ogs_gtp_sockaddr_to_f_teid(addr, addr6, &sgw_s1u_teid, &len);
ogs_assert(rv == OGS_OK);
} else {
rv = ogs_gtp_sockaddr_to_f_teid(
sgw_self()->gtpu_addr, sgw_self()->gtpu_addr6, &sgw_s1u_teid, &len);
ogs_assert(rv == OGS_OK);
}
req->bearer_contexts.s1_u_enodeb_f_teid.presence = 1;
req->bearer_contexts.s1_u_enodeb_f_teid.data = &sgw_s1u_teid;
req->bearer_contexts.s1_u_enodeb_f_teid.len = len;
message->h.type = OGS_GTP_CREATE_BEARER_REQUEST_TYPE;
message->h.teid = sgw_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(message);
ogs_expect_or_return(pkbuf);
s11_xact = ogs_gtp_xact_local_create(
sgw_ue->gnode, &message->h, pkbuf, timeout, sess);
ogs_expect_or_return(s11_xact);
ogs_gtp_xact_associate(s5c_xact, s11_xact);
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
}
void sgw_s5c_handle_update_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
int rv;
uint8_t cause_value = 0;
ogs_gtp_xact_t *s11_xact = NULL;
ogs_gtp_update_bearer_request_t *req = NULL;
ogs_pkbuf_t *pkbuf = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_assert(s5c_xact);
ogs_assert(message);
ogs_debug("[SGW] Update Bearer Request");
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
req = &message->update_bearer_request;
if (!sess) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (req->bearer_contexts.presence == 0) {
ogs_error("No Bearer");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (req->bearer_contexts.eps_bearer_id.presence == 0) {
ogs_error("No EPS Bearer ID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0,
OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE, cause_value);
return;
}
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgw_ue->mme_s11_teid, sgw_ue->sgw_s11_teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
message->h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE;
message->h.teid = sgw_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(message);
ogs_expect_or_return(pkbuf);
s11_xact = s5c_xact->assoc_xact;
if (!s11_xact) {
s11_xact = ogs_gtp_xact_local_create(
sgw_ue->gnode, &message->h, pkbuf, timeout, sess);
ogs_expect_or_return(s11_xact);
ogs_gtp_xact_associate(s5c_xact, s11_xact);
} else {
rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
}
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
ogs_debug("[SGW] Update Bearer Request : SGW <-- PGW");
}
void sgw_s5c_handle_delete_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
int rv;
uint8_t cause_value = 0;
ogs_gtp_xact_t *s11_xact = NULL;
ogs_gtp_delete_bearer_request_t *req = NULL;
ogs_pkbuf_t *pkbuf = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_assert(s5c_xact);
ogs_assert(message);
ogs_debug("[SGW] Delete Bearer Request");
cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
req = &message->delete_bearer_request;
if (!sess) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (req->linked_eps_bearer_id.presence == 0 &&
req->eps_bearer_ids.presence == 0) {
ogs_error("No Linked EBI or EPS Bearer ID");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0,
OGS_GTP_DELETE_BEARER_RESPONSE_TYPE, cause_value);
return;
}
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgw_ue->mme_s11_teid, sgw_ue->sgw_s11_teid);
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->pgw_s5c_teid);
message->h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE;
message->h.teid = sgw_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(message);
ogs_expect_or_return(pkbuf);
s11_xact = s5c_xact->assoc_xact;
if (!s11_xact) {
s11_xact = ogs_gtp_xact_local_create(
sgw_ue->gnode, &message->h, pkbuf, timeout, sess);
ogs_expect_or_return(s11_xact);
ogs_gtp_xact_associate(s5c_xact, s11_xact);
} else {
rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
}
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
}
void sgw_s5c_handle_bearer_resource_failure_indication(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message)
{
uint8_t cause_value = 0;
ogs_gtp_xact_t *s11_xact = NULL;
ogs_gtp_bearer_resource_failure_indication_t *ind = NULL;
sgw_ue_t *sgw_ue = NULL;
ogs_assert(s5c_xact);
s11_xact = s5c_xact->assoc_xact;
ogs_assert(s11_xact);
ogs_assert(message);
ogs_debug("[SGW] Bearer Resource Failure Indication");
ind = &message->bearer_resource_failure_indication;
if (!sess) {
ogs_warn("No Context");
cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (ind->cause.presence) {
ogs_gtp_cause_t *cause = ind->cause.data;
ogs_assert(cause);
cause_value = cause->value;
} else {
ogs_error("No Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
sgw_ue = sess->sgw_ue;
ogs_assert(sgw_ue);
ogs_gtp_send_error_message(s11_xact, sgw_ue ? sgw_ue->mme_s11_teid : 0,
OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE, cause_value);
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_S5C_HANDLER_H
#define SGW_S5C_HANDLER_H
#include "sgw-context.h"
#ifdef __cplusplus
extern "C" {
#endif
void sgw_s5c_handle_create_session_response(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *gtp_message);
void sgw_s5c_handle_delete_session_response(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *gtp_message);
void sgw_s5c_handle_create_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *gtp_message);
void sgw_s5c_handle_update_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *gtp_message);
void sgw_s5c_handle_delete_bearer_request(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *gtp_message);
void sgw_s5c_handle_bearer_resource_failure_indication(ogs_gtp_xact_t *s5c_xact,
sgw_sess_t *sess, ogs_gtp_message_t *message);
#ifdef __cplusplus
}
#endif
#endif /* SGW_S5C_HANDLER_H */

View File

@ -1,261 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sgw-context.h"
#include "sgw-event.h"
#include "sgw-sm.h"
#include "sgw-gtp-path.h"
#include "sgw-s11-handler.h"
#include "sgw-s5c-handler.h"
static void sgw_handle_echo_request(
ogs_gtp_xact_t *xact, ogs_gtp_echo_request_t *req)
{
ogs_assert(xact);
ogs_assert(req);
ogs_debug("[SGW] Receiving Echo Request");
/* FIXME : Before implementing recovery counter correctly,
* I'll re-use the recovery value in request message */
ogs_gtp_send_echo_response(xact, req->recovery.u8, 0);
}
static void sgw_handle_echo_response(
ogs_gtp_xact_t *s11_xact, ogs_gtp_echo_response_t *rsp)
{
/* Not Implemented */
}
void sgw_state_initial(ogs_fsm_t *s, sgw_event_t *e)
{
sgw_sm_debug(e);
ogs_assert(s);
OGS_FSM_TRAN(s, &sgw_state_operational);
}
void sgw_state_final(ogs_fsm_t *s, sgw_event_t *e)
{
sgw_sm_debug(e);
ogs_assert(s);
}
void sgw_state_operational(ogs_fsm_t *s, sgw_event_t *e)
{
int rv;
ogs_pkbuf_t *pkbuf = NULL;
ogs_gtp_xact_t *xact = NULL;
ogs_gtp_message_t message;
sgw_ue_t *sgw_ue = NULL;
sgw_sess_t *sess = NULL;
sgw_bearer_t *bearer = NULL;
ogs_gtp_node_t *gnode = NULL;
sgw_sm_debug(e);
ogs_assert(s);
switch (e->id) {
case OGS_FSM_ENTRY_SIG:
rv = sgw_gtp_open();
if (rv != OGS_OK) {
ogs_error("Can't establish SGW path");
break;
}
break;
case OGS_FSM_EXIT_SIG:
sgw_gtp_close();
break;
case SGW_EVT_S11_MESSAGE:
ogs_assert(e);
pkbuf = e->pkbuf;
ogs_assert(pkbuf);
if (ogs_gtp_parse_msg(&message, pkbuf) != OGS_OK) {
ogs_error("ogs_gtp_parse_msg() failed");
ogs_pkbuf_free(pkbuf);
break;
}
if (message.h.teid_presence && message.h.teid != 0) {
/* Cause is not "Context not found" */
sgw_ue = sgw_ue_find_by_teid(message.h.teid);
}
if (sgw_ue) {
gnode = sgw_ue->gnode;
ogs_assert(gnode);
} else {
gnode = e->gnode;
ogs_assert(gnode);
}
rv = ogs_gtp_xact_receive(gnode, &message.h, &xact);
if (rv != OGS_OK) {
ogs_pkbuf_free(pkbuf);
break;
}
switch(message.h.type) {
case OGS_GTP_ECHO_REQUEST_TYPE:
sgw_handle_echo_request(xact, &message.echo_request);
break;
case OGS_GTP_ECHO_RESPONSE_TYPE:
sgw_handle_echo_response(xact, &message.echo_response);
break;
case OGS_GTP_CREATE_SESSION_REQUEST_TYPE:
if (message.h.teid == 0) {
ogs_expect(!sgw_ue);
sgw_ue = sgw_ue_add_by_message(&message);
if (sgw_ue)
OGS_SETUP_GTP_NODE(sgw_ue, gnode);
}
sgw_s11_handle_create_session_request(xact, sgw_ue,
&message);
break;
case OGS_GTP_MODIFY_BEARER_REQUEST_TYPE:
sgw_s11_handle_modify_bearer_request(xact, sgw_ue,
&message.modify_bearer_request);
break;
case OGS_GTP_DELETE_SESSION_REQUEST_TYPE:
sgw_s11_handle_delete_session_request(xact, sgw_ue,
&message);
break;
case OGS_GTP_CREATE_BEARER_RESPONSE_TYPE:
sgw_s11_handle_create_bearer_response(xact, sgw_ue,
&message);
break;
case OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE:
sgw_s11_handle_update_bearer_response(xact, sgw_ue,
&message);
break;
case OGS_GTP_DELETE_BEARER_RESPONSE_TYPE:
sgw_s11_handle_delete_bearer_response(xact, sgw_ue,
&message);
break;
case OGS_GTP_RELEASE_ACCESS_BEARERS_REQUEST_TYPE:
sgw_s11_handle_release_access_bearers_request(xact, sgw_ue,
&message.release_access_bearers_request);
break;
case OGS_GTP_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE_TYPE:
sgw_s11_handle_downlink_data_notification_ack(xact, sgw_ue,
&message.downlink_data_notification_acknowledge);
break;
case OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE:
sgw_s11_handle_create_indirect_data_forwarding_tunnel_request(
xact, sgw_ue,
&message.create_indirect_data_forwarding_tunnel_request);
break;
case OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE:
sgw_s11_handle_delete_indirect_data_forwarding_tunnel_request(
xact, sgw_ue);
break;
case OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE:
sgw_s11_handle_bearer_resource_command(xact, sgw_ue, &message);
break;
default:
ogs_warn("Not implemented(type:%d)", message.h.type);
break;
}
ogs_pkbuf_free(pkbuf);
break;
case SGW_EVT_S5C_MESSAGE:
ogs_assert(e);
pkbuf = e->pkbuf;
ogs_assert(pkbuf);
if (ogs_gtp_parse_msg(&message, pkbuf) != OGS_OK) {
ogs_error("ogs_gtp_parse_msg() failed");
ogs_pkbuf_free(pkbuf);
break;
}
if (message.h.teid_presence && message.h.teid != 0) {
sess = sgw_sess_find_by_teid(message.h.teid);
}
if (sess) {
gnode = sess->gnode;
ogs_assert(gnode);
} else {
gnode = e->gnode;
ogs_assert(gnode);
}
rv = ogs_gtp_xact_receive(gnode, &message.h, &xact);
if (rv != OGS_OK) {
ogs_pkbuf_free(pkbuf);
break;
}
switch(message.h.type) {
case OGS_GTP_ECHO_REQUEST_TYPE:
sgw_handle_echo_request(xact, &message.echo_request);
break;
case OGS_GTP_ECHO_RESPONSE_TYPE:
sgw_handle_echo_response(xact, &message.echo_response);
break;
case OGS_GTP_CREATE_SESSION_RESPONSE_TYPE:
sgw_s5c_handle_create_session_response(xact, sess,
&message);
break;
case OGS_GTP_DELETE_SESSION_RESPONSE_TYPE:
sgw_s5c_handle_delete_session_response(xact, sess,
&message);
break;
case OGS_GTP_CREATE_BEARER_REQUEST_TYPE:
sgw_s5c_handle_create_bearer_request(xact, sess,
&message);
break;
case OGS_GTP_UPDATE_BEARER_REQUEST_TYPE:
sgw_s5c_handle_update_bearer_request(xact, sess,
&message);
break;
case OGS_GTP_DELETE_BEARER_REQUEST_TYPE:
sgw_s5c_handle_delete_bearer_request(xact, sess,
&message);
break;
case OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE:
sgw_s5c_handle_bearer_resource_failure_indication(xact, sess,
&message);
break;
default:
ogs_warn("Not implmeneted(type:%d)", message.h.type);
break;
}
ogs_pkbuf_free(pkbuf);
break;
case SGW_EVT_LO_DLDATA_NOTI:
ogs_assert(e);
bearer = e->bearer;
ogs_assert(bearer);
sgw_s11_handle_lo_dldata_notification(bearer);
break;
default:
ogs_error("No handler for event %s", sgw_event_get_name(e));
break;
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SGW_SM_H
#define SGW_SM_H
#include "sgw-event.h"
#ifdef __cplusplus
extern "C" {
#endif
void sgw_state_initial(ogs_fsm_t *s, sgw_event_t *e);
void sgw_state_final(ogs_fsm_t *s, sgw_event_t *e);
void sgw_state_operational(ogs_fsm_t *s, sgw_event_t *e);
void sgw_state_exception(ogs_fsm_t *s, sgw_event_t *e);
#define sgw_sm_debug(__pe) \
ogs_debug("%s(): %s\n", __func__, sgw_event_get_name(__pe))
#ifdef __cplusplus
}
#endif
#endif /* !SGW_SM_H */

View File

@ -28,24 +28,13 @@ libtestepc = static_library('testepc',
sources : [libtestepc_sources],
c_args : libtestepc_cc_args,
include_directories : [libtestapp_inc, srcinc],
link_with : [libmme, libhss, libsgw, libpgw, libpcrf],
dependencies : [libtestcommon_dep,
libmme_dep,
libhss_dep,
libsgw_dep,
libpgw_dep,
libpcrf_dep],
dependencies : libtestcommon_dep,
install : false)
libtestepc_dep = declare_dependency(
link_with : libtestepc,
include_directories : [libtestapp_inc, srcinc],
dependencies : [libtestcommon_dep,
libmme_dep,
libhss_dep,
libsgw_dep,
libpgw_dep,
libpcrf_dep])
dependencies : libtestcommon_dep)
testepc_sources = files('''
../../src/main.c
@ -67,7 +56,6 @@ libtest5gc = static_library('test5gc',
sources : [libtest5gc_sources],
c_args : libtest5gc_cc_args,
include_directories : [libtestapp_inc, srcinc],
link_with : libamf,
dependencies : libtestcommon_dep,
install : false)
@ -96,24 +84,13 @@ libtestapp = static_library('testapp',
sources : [libtestapp_sources],
c_args : libtestapp_cc_args,
include_directories : [libtestapp_inc, srcinc],
link_with : [libmme, libhss, libsgw, libpgw, libpcrf],
dependencies : [libtestcommon_dep,
libmme_dep,
libhss_dep,
libsgw_dep,
libpgw_dep,
libpcrf_dep],
dependencies : libtestcommon_dep,
install : false)
libtestapp_dep = declare_dependency(
link_with : libtestapp,
include_directories : [libtestapp_inc, srcinc],
dependencies : [libtestcommon_dep,
libmme_dep,
libhss_dep,
libsgw_dep,
libpgw_dep,
libpcrf_dep])
dependencies : libtestcommon_dep)
testapp_sources = files('''
../../src/main.c

View File

@ -66,7 +66,7 @@ libtestcommon = static_library('testcomon',
libngap_dep,
libnas_eps_dep,
libnas_5gs_dep,
libdiameter_common_dep],
libdiameter_rx_dep],
install : false)
libtestcommon_dep = declare_dependency(
@ -81,4 +81,4 @@ libtestcommon_dep = declare_dependency(
libngap_dep,
libnas_eps_dep,
libnas_5gs_dep,
libdiameter_common_dep])
libdiameter_rx_dep])

View File

@ -28,6 +28,6 @@ testunit_unit_sources = files('''
testunit_unit_exe = executable('unit',
sources : testunit_unit_sources,
c_args : [testunit_core_cc_flags, sbi_cc_flags],
dependencies : [libtestepc_dep, libsbi_dep])
dependencies : [libtestapp_dep, libmme_dep, libsbi_dep])
test('unit', testunit_unit_exe, is_parallel : false, suite: 'unit')