From 4b9e499ea70bcfa6391e0d56d4f0600a98ddd599 Mon Sep 17 00:00:00 2001 From: James Park Date: Tue, 18 Apr 2017 10:22:55 +0900 Subject: [PATCH] Add tuntap interface to PGW --- lib/core/include/core_net.h | 5 ++ lib/core/src/unix/net_lib.c | 58 +++++++++++++++++++- lib/gtp/gtp_path.h | 19 +++++++ src/mme/mme_context.c | 6 ++- src/pgw/Makefile.am | 2 +- src/pgw/pgw_context.c | 64 ++++++++++++++++++++-- src/pgw/pgw_context.h | 4 ++ src/pgw/pgw_path.c | 105 ++++++++++++++++++++++++++++++++++++ src/sgw/sgw_context.c | 12 +++-- 9 files changed, 263 insertions(+), 12 deletions(-) diff --git a/lib/core/include/core_net.h b/lib/core/include/core_net.h index b09ef9ac8..663bb0d4c 100644 --- a/lib/core/include/core_net.h +++ b/lib/core/include/core_net.h @@ -266,6 +266,11 @@ CORE_DECLARE(int) net_unregister_link(net_link_t *net_link); */ CORE_DECLARE(int) net_fds_read_run(long timeout); +/** TunTap interface */ +CORE_DECLARE(int) net_tuntap_open(net_link_t **net_link, char *tuntap_dev_name, + int is_tap); + +CORE_DECLARE(int) net_tuntap_close(net_link_t *net_link); #ifdef __cplusplus } diff --git a/lib/core/src/unix/net_lib.c b/lib/core/src/unix/net_lib.c index 81e89fbb3..caf34a515 100644 --- a/lib/core/src/unix/net_lib.c +++ b/lib/core/src/unix/net_lib.c @@ -13,6 +13,7 @@ #if LINUX == 1 #include +#include #endif #define NET_FD_TYPE_SOCK 0 @@ -1170,6 +1171,53 @@ int net_raw_open(net_link_t **net_link, int proto) return 0; } +/** Create tuntap socket */ +int net_tuntap_open(net_link_t **net_link, char *tuntap_dev_name, + int is_tap) +{ + int rc,sock; + net_link_t *new_link = NULL; + char *dev = "/dev/net/tun"; + struct ifreq ifr; + int flags = IFF_NO_PI; + + sock = open(dev, O_RDWR); + if (sock < 0) + { + d_error("Can not open %s",dev); + return -1; + } + + pool_alloc_node(&link_pool, &new_link); + d_assert(new_link != NULL, return -1,"No link pool is availabe\n"); + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = (is_tap ? (flags | IFF_TAP) : (flags | IFF_TUN)); + strncpy(ifr.ifr_name, tuntap_dev_name, IFNAMSIZ); + + rc = ioctl(sock, TUNSETIFF, (void *)&ifr); + if (rc < 0) + { + d_error("iotcl error(dev:%s flags = %d)", tuntap_dev_name, flags); + goto cleanup; + } + + /* Save socket descriptor */ + new_link->fd = sock; + /* Save the interface name */ + strncpy(new_link->ifname, tuntap_dev_name, IFNAMSIZ); + + *net_link = new_link; + return 0; + +cleanup: + pool_free_node(&link_pool, new_link); + close(sock); + return -1; +} + + int net_link_open(net_link_t **net_link, char *device, int proto) { int sock,ioctl_sock; @@ -1313,6 +1361,14 @@ int net_raw_close(net_link_t *net_link) return 0; } +int net_tuntap_close(net_link_t *net_link) +{ + d_assert(net_link,return -1, "net_link is NULL\n"); + close(net_link->fd); + pool_free_node(&link_pool, net_link); + return 0; +} + int net_link_write(net_link_t *net_link, char *buf, int len) { d_assert(net_link && buf, return -1, "Invalid params\n"); @@ -1360,7 +1416,7 @@ int net_link_read(net_link_t *net_link, char *buffer, int size, int timeout) rc = recvfrom(net_link->fd, buffer, size, 0, &remote_addr, &addr_len); #else - rc = recvfrom(net_link->fd, buffer, size, 0, NULL, NULL); + rc = read(net_link->fd, buffer, size); #endif } else /* Timeout */ diff --git a/lib/gtp/gtp_path.h b/lib/gtp/gtp_path.h index 069684ded..efe4ae227 100644 --- a/lib/gtp/gtp_path.h +++ b/lib/gtp/gtp_path.h @@ -15,6 +15,25 @@ extern "C" { #define GTP_COMPARE_NODE(__id1, __id2) \ (((__id1)->addr) == ((__id2)->addr) && ((__id1)->port) == ((__id2)->port)) +/* GTP-U message type, defined in 3GPP TS 29.281 Release 11 */ +#define GTPU_MSGTYPE_ECHO_REQ 1 +#define GTPU_MSGTYPE_ECHO_RSP 2 +#define GTPU_MSGTYPE_ERR_IND 26 +#define GTPU_MSGTYPE_SUPP_EXTHDR_NOTI 31 +#define GTPU_MSGTYPE_END_MARKER 254 +#define GTPU_MSGTYPE_GPDU 255 + +/* GTU-U flags */ +#define GTPU_FLAGS_PN 0x1 +#define GTPU_FLAGS_S 0x2 + +typedef struct _gtp_header +{ + c_uint8_t flags; + c_uint8_t type; + c_uint16_t length; + c_uint32_t teid; +} gtp_header_t; /** * This structure represents the commonalities of GTP node such as MME, SGW, * PGW gateway. Some of members may not be used by the specific type of node */ diff --git a/src/mme/mme_context.c b/src/mme/mme_context.c index 6a352f822..fcd86302e 100644 --- a/src/mme/mme_context.c +++ b/src/mme/mme_context.c @@ -17,6 +17,8 @@ #define MIN_EPS_BEARER_ID 5 #define MAX_EPS_BEARER_ID 15 +#define DEFAULT_IP_ADDR "127.0.0.1" + static mme_context_t self; pool_declare(mme_sgw_pool, mme_sgw_t, MAX_NUM_OF_SGW); @@ -46,7 +48,7 @@ status_t mme_context_init() index_init(&mme_bearer_pool, MAX_NUM_OF_UE_BEARER); pool_init(&mme_pdn_pool, MAX_NUM_OF_UE_PDN); - self.mme_addr = inet_addr("127.0.0.1"); + self.mme_addr = inet_addr(DEFAULT_IP_ADDR); self.mme_ue_s1ap_id_hash = hash_make(); @@ -60,7 +62,7 @@ status_t mme_context_init() self.s11_port = GTPV2_C_UDP_PORT; /* FIXME : It should be removed */ - sgw->gnode.addr = inet_addr("127.0.0.1"); + sgw->gnode.addr = inet_addr(DEFAULT_IP_ADDR); sgw->gnode.port = GTPV2_C_UDP_PORT+1; /* MCC : 001, MNC : 01 */ diff --git a/src/pgw/Makefile.am b/src/pgw/Makefile.am index 3113287f3..0fa8cc677 100644 --- a/src/pgw/Makefile.am +++ b/src/pgw/Makefile.am @@ -24,7 +24,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/lib/gtp AM_CFLAGS = \ - -Wall -Werror + -Wall -Werror @OSCPPFLAGS@ MAINTAINERCLEANFILES = Makefile.in MOSTLYCLEANFILES = core *.stackdump diff --git a/src/pgw/pgw_context.c b/src/pgw/pgw_context.c index 5a15824fb..9c136a6c3 100644 --- a/src/pgw/pgw_context.c +++ b/src/pgw/pgw_context.c @@ -8,6 +8,10 @@ #include "pgw_context.h" +#include + +#define DEFAULT_IP_ADDR "127.0.0.1" + static pgw_context_t self; index_declare(pgw_sess_pool, pgw_sess_t, MAX_NUM_OF_UE); @@ -30,12 +34,12 @@ status_t pgw_context_init() pool_init(&pgw_pdn_pool, MAX_NUM_OF_UE_PDN); - self.pgw_addr = inet_addr("127.0.0.1"); + self.pgw_addr = inet_addr(DEFAULT_IP_ADDR); self.s5c_addr = self.pgw_addr; self.s5c_port = GTPV2_C_UDP_PORT + 3; /* FIXME : It shoud be removed */ - self.s5c_node.addr = inet_addr("127.0.0.1"); + self.s5c_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s5c_node.port = GTPV2_C_UDP_PORT + 2; list_init(&self.s5c_node.local_list); list_init(&self.s5c_node.remote_list); @@ -43,7 +47,7 @@ status_t pgw_context_init() self.s5u_addr = self.pgw_addr; self.s5u_port = GTPV1_U_UDP_PORT + 1; /* FIXME : It shoud be removed */ - self.s5u_node.addr = inet_addr("127.0.0.1"); + self.s5u_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s5u_node.port = GTPV1_U_UDP_PORT; self.primary_dns_addr = inet_addr("8.8.8.8"); @@ -319,3 +323,57 @@ pgw_bearer_t* pgw_bearer_next(pgw_bearer_t *bearer) return list_next(bearer); } +pgw_bearer_t* pgw_bearer_find_by_packet(pkbuf_t *pkt) +{ + pgw_bearer_t *bearer = NULL; + pgw_sess_t *iter_session = NULL; + pgw_bearer_t *iter_bearer = NULL; + pdn_t *iter_pdn = NULL; + struct iphdr *iph = NULL; + + d_assert(pkt, return NULL, "pkt is NULL"); + + iph = (struct iphdr *)pkt->payload; + + /* FIXME : Only support IPV4 */ + if (iph->version != 4) /* IPv4 */ + { + return NULL; + } + + /* FIXME: Need API to find the bearer with packet filter */ + /* Iterate session */ + for (iter_session = pgw_sess_first(); iter_session ; + iter_session = pgw_sess_next(iter_session)) + { + /* Iterate bearer in session */ + for (iter_bearer = pgw_bearer_first(iter_session); + iter_bearer; + iter_bearer = pgw_bearer_next(iter_bearer)) + { + /* Iterate pdn in session */ + for (iter_pdn = pgw_pdn_first(iter_bearer->sess); + iter_pdn; + iter_pdn = pgw_pdn_next(iter_pdn)) + { + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + + d_trace(3,"Src_IP(%s) in Pkt : PAA(%s) in PDN\n", + INET_NTOP(iph->saddr,buf1), + INET_NTOP(iter_pdn->paa.ipv4_addr, buf2)); + + if (iph->saddr == iter_pdn->paa.ipv4_addr) + { + /* Found */ + bearer = iter_bearer; + break; + } + + } + + } + } + + return bearer; +} diff --git a/src/pgw/pgw_context.h b/src/pgw/pgw_context.h index 8ef195299..f56c4cc6d 100644 --- a/src/pgw/pgw_context.h +++ b/src/pgw/pgw_context.h @@ -35,6 +35,8 @@ typedef struct _pgw_context_t { c_uint32_t primary_dns_addr; c_uint32_t secondary_dns_addr; + net_link_t* tun_link; /* PGW Tun Interace for U-plane */ + list_t sess_list; } pgw_context_t; @@ -99,6 +101,8 @@ CORE_DECLARE(pgw_bearer_t*) pgw_bearer_find_by_id( CORE_DECLARE(pgw_bearer_t*) pgw_bearer_first(pgw_sess_t *sess); CORE_DECLARE(pgw_bearer_t*) pgw_bearer_next(pgw_bearer_t *bearer); +CORE_DECLARE(pgw_bearer_t*) pgw_bearer_find_by_packet(pkbuf_t *pkt); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/pgw/pgw_path.c b/src/pgw/pgw_path.c index acb56f774..c1e965f31 100644 --- a/src/pgw/pgw_path.c +++ b/src/pgw/pgw_path.c @@ -10,6 +10,68 @@ #include "pgw_event.h" #include "pgw_path.h" +#if LINUX == 1 +static int _gtpv1_tun_recv_cb(net_link_t *net_link, void *data) +{ + pkbuf_t *recvbuf = NULL; + int n; + status_t rv; + pgw_bearer_t *bearer = NULL; + + recvbuf = pkbuf_alloc(sizeof(gtp_header_t), MAX_SDU_LEN); + d_assert(recvbuf, return -1, "pkbuf_alloc error"); + + n = net_link_read(net_link, recvbuf->payload, recvbuf->len, 0); + if (n <= 0) + { + pkbuf_free(recvbuf); + return -1; + } + + /* Find the bearer by packet filter */ + bearer = pgw_bearer_find_by_packet(recvbuf); + if (bearer) + { + gtp_header_t *gtp_h = NULL; + gtp_node_t gnode; + + /* Add GTP-U header */ + rv = pkbuf_header(recvbuf, sizeof(gtp_header_t)); + if (rv != CORE_OK) + { + d_error("pkbuf_header error"); + pkbuf_free(recvbuf); + return -1; + } + + /* 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 = GTPU_MSGTYPE_GPDU; + gtp_h->length = n; + gtp_h->teid = bearer->sgw_s5u_teid; + + /* Send to SGW */ + gnode.addr = bearer->sgw_s5u_addr; + gnode.port = GTPV1_U_UDP_PORT; + + rv = gtp_send(pgw_self()->s5u_sock, &gnode, recvbuf); + } + else + { + d_error("Can not find bearer"); + } + + pkbuf_free(recvbuf); + return 0; + +} +#endif + static int _gtpv2_c_recv_cb(net_sock_t *sock, void *data) { event_t e; @@ -93,6 +155,44 @@ status_t pgw_path_open() return rv; } +#if LINUX == 1 + { + int rc; + + /* FIXME : dev_name should be configured */ + char *tun_dev_name = "pgwtun"; + + /* NOTE : tun device can be created via following command. + * + * $ sudo ip tuntap add name pgwtun mode tun + * + * Also, before running pgw, assign the one IP from IP pool of UE + * to pgwtun. The IP should not be assigned to UE + * + * $ sudo ifconfig pgwtun 45.45.0.1/16 up + * + */ + + /* Open Tun interface */ + rc = net_tuntap_open(&pgw_self()->tun_link, tun_dev_name, 0); + if (rc != 0) + { + d_error("Can not open tun(dev : %s)",tun_dev_name); + return CORE_ERROR; + } + + rc = net_register_link(pgw_self()->tun_link, _gtpv1_tun_recv_cb, NULL); + if (rc != 0) + { + d_error("Can not register tun(dev : %s)",tun_dev_name); + net_tuntap_close(pgw_self()->tun_link); + return CORE_ERROR; + } + + } +#endif + + return CORE_OK; } @@ -114,6 +214,11 @@ status_t pgw_path_close() return rv; } +#if LINUX == 1 + net_unregister_link(pgw_self()->tun_link); + net_link_close(pgw_self()->tun_link); +#endif + return CORE_OK; } diff --git a/src/sgw/sgw_context.c b/src/sgw/sgw_context.c index 6d4a0f96c..08452f192 100644 --- a/src/sgw/sgw_context.c +++ b/src/sgw/sgw_context.c @@ -9,6 +9,8 @@ #include "sgw_context.h" +#define DEFAULT_IP_ADDR "127.0.0.1" + static sgw_context_t self; index_declare(sgw_sess_pool, sgw_sess_t, MAX_NUM_OF_UE); @@ -27,12 +29,12 @@ status_t sgw_context_init() index_init(&sgw_bearer_pool, MAX_NUM_OF_UE_BEARER); list_init(&self.sess_list); - self.sgw_addr = inet_addr("127.0.0.1"); + self.sgw_addr = inet_addr(DEFAULT_IP_ADDR); self.s11_addr = self.sgw_addr; self.s11_port = GTPV2_C_UDP_PORT + 1; /* FIXME : It shoud be removed */ - self.s11_node.addr = inet_addr("127.0.0.1"); + self.s11_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s11_node.port = GTPV2_C_UDP_PORT; list_init(&self.s11_node.local_list); list_init(&self.s11_node.remote_list); @@ -40,7 +42,7 @@ status_t sgw_context_init() self.s5c_addr = self.sgw_addr; self.s5c_port = GTPV2_C_UDP_PORT + 2; /* FIXME : It shoud be removed */ - self.s5c_node.addr = inet_addr("127.0.0.1"); + self.s5c_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s5c_node.port = GTPV2_C_UDP_PORT + 3; list_init(&self.s5c_node.local_list); list_init(&self.s5c_node.remote_list); @@ -48,13 +50,13 @@ status_t sgw_context_init() self.s1u_addr = self.sgw_addr; self.s1u_port = GTPV1_U_UDP_PORT; /* FIXME : It shoud be removed */ - self.s1u_node.addr = inet_addr("127.0.0.1"); + self.s1u_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s1u_node.port = GTPV1_U_UDP_PORT; self.s5u_addr = self.sgw_addr; self.s5u_port = GTPV1_U_UDP_PORT; /* FIXME : It shoud be removed */ - self.s5u_node.addr = inet_addr("127.0.0.1"); + self.s5u_node.addr = inet_addr(DEFAULT_IP_ADDR); self.s5u_node.port = GTPV1_U_UDP_PORT + 1; context_initialized = 1;