forked from acouzens/open5gs
Remove SGW and PGW
This commit is contained in:
parent
aca41f6668
commit
b412e51b83
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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
|
@ -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 */
|
|
@ -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";
|
||||
}
|
|
@ -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
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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 = >p_message.create_session_response;
|
||||
memset(>p_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(>p_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 = >p_message.delete_session_response;
|
||||
memset(>p_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(>p_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 = >p_message.create_bearer_request;
|
||||
memset(>p_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(>p_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 = >p_message.update_bearer_request;
|
||||
memset(>p_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(>p_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 = >p_message.delete_bearer_request;
|
||||
memset(>p_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(>p_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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
216
src/pgw/pgw-sm.c
216
src/pgw/pgw-sm.c
|
@ -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(>p_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, >p_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, >p_message.echo_request);
|
||||
break;
|
||||
case OGS_GTP_ECHO_RESPONSE_TYPE:
|
||||
pgw_s5c_handle_echo_response(xact, >p_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(>p_message);
|
||||
if (sess)
|
||||
OGS_SETUP_GTP_NODE(sess, gnode);
|
||||
}
|
||||
pgw_s5c_handle_create_session_request(
|
||||
sess, xact, >p_message.create_session_request);
|
||||
break;
|
||||
case OGS_GTP_DELETE_SESSION_REQUEST_TYPE:
|
||||
pgw_s5c_handle_delete_session_request(
|
||||
sess, xact, >p_message.delete_session_request);
|
||||
break;
|
||||
case OGS_GTP_CREATE_BEARER_RESPONSE_TYPE:
|
||||
pgw_s5c_handle_create_bearer_response(
|
||||
sess, xact, >p_message.create_bearer_response);
|
||||
break;
|
||||
case OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE:
|
||||
pgw_s5c_handle_update_bearer_response(
|
||||
sess, xact, >p_message.update_bearer_response);
|
||||
break;
|
||||
case OGS_GTP_DELETE_BEARER_RESPONSE_TYPE:
|
||||
pgw_s5c_handle_delete_bearer_response(
|
||||
sess, xact, >p_message.delete_bearer_response);
|
||||
break;
|
||||
case OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE:
|
||||
pgw_s5c_handle_bearer_resource_command(
|
||||
sess, xact, >p_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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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");
|
||||
}
|
|
@ -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)
|
|
@ -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, >pc_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(>pc_array) ==
|
||||
YAML_MAPPING_NODE) {
|
||||
memcpy(>pc_iter, >pc_array,
|
||||
sizeof(ogs_yaml_iter_t));
|
||||
} else if (ogs_yaml_iter_type(>pc_array) ==
|
||||
YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(>pc_array))
|
||||
break;
|
||||
ogs_yaml_iter_recurse(>pc_array, >pc_iter);
|
||||
} else if (ogs_yaml_iter_type(>pc_array) ==
|
||||
YAML_SCALAR_NODE) {
|
||||
break;
|
||||
} else
|
||||
ogs_assert_if_reached();
|
||||
|
||||
while (ogs_yaml_iter_next(>pc_iter)) {
|
||||
const char *gtpc_key =
|
||||
ogs_yaml_iter_key(>pc_iter);
|
||||
ogs_assert(gtpc_key);
|
||||
if (!strcmp(gtpc_key, "family")) {
|
||||
const char *v = ogs_yaml_iter_value(>pc_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(>pc_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(>pc_iter);
|
||||
if (v) port = atoi(v);
|
||||
} else if (!strcmp(gtpc_key, "dev")) {
|
||||
dev = ogs_yaml_iter_value(>pc_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(>pc_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, >pu_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(>pu_array) ==
|
||||
YAML_MAPPING_NODE) {
|
||||
memcpy(>pu_iter, >pu_array,
|
||||
sizeof(ogs_yaml_iter_t));
|
||||
} else if (ogs_yaml_iter_type(>pu_array) ==
|
||||
YAML_SEQUENCE_NODE) {
|
||||
if (!ogs_yaml_iter_next(>pu_array))
|
||||
break;
|
||||
ogs_yaml_iter_recurse(>pu_array, >pu_iter);
|
||||
} else if (ogs_yaml_iter_type(>pu_array) ==
|
||||
YAML_SCALAR_NODE) {
|
||||
break;
|
||||
} else
|
||||
ogs_assert_if_reached();
|
||||
|
||||
while (ogs_yaml_iter_next(>pu_iter)) {
|
||||
const char *gtpu_key =
|
||||
ogs_yaml_iter_key(>pu_iter);
|
||||
ogs_assert(gtpu_key);
|
||||
if (!strcmp(gtpu_key, "family")) {
|
||||
const char *v = ogs_yaml_iter_value(>pu_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(>pu_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(>pu_iter);
|
||||
if (v) port = atoi(v);
|
||||
} else if (!strcmp(gtpu_key, "dev")) {
|
||||
dev = ogs_yaml_iter_value(>pu_iter);
|
||||
} else if (!strcmp(gtpu_key, "advertise_addr") ||
|
||||
!strcmp(gtpu_key, "advertise_name")) {
|
||||
ogs_yaml_iter_t adv_hostname_iter;
|
||||
ogs_yaml_iter_recurse(>pu_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(>pu_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);
|
||||
}
|
|
@ -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 */
|
|
@ -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";
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
261
src/sgw/sgw-sm.c
261
src/sgw/sgw-sm.c
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue