├── .gitignore ├── src ├── lib │ ├── pktbuf.h │ ├── helper.h │ ├── pktbuf.c │ ├── logger.c │ └── logger.h ├── node.h ├── netstack │ ├── ether.h │ ├── ether.c │ ├── arp.h │ └── arp.c ├── config.h ├── stats.h ├── node.c ├── gtp_process.h ├── config.c ├── main.c └── stats.c ├── gtp_config.example.ini ├── README.md ├── LICENSE └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.o* 3 | 4 | gtp_config.ini 5 | gtp_config.ini.* 6 | 7 | DevDocs/ 8 | .vscode/ 9 | -------------------------------------------------------------------------------- /src/lib/pktbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef __MBUF_H_ 2 | #define __MBUF_H_ 3 | 4 | #include 5 | 6 | #define NB_MBUF (8192 * 2) 7 | #define MBUF_BUFFER_LEN (2048) 8 | #define MBUF_SIZE (MBUF_BUFFER_LEN + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) 9 | 10 | int 11 | mbuf_init(void); 12 | 13 | struct rte_mbuf *get_mbuf(void); 14 | 15 | #endif /* __MBUF_H_ */ 16 | -------------------------------------------------------------------------------- /gtp_config.example.ini: -------------------------------------------------------------------------------- 1 | [Global] 2 | disp_stats = 0 3 | 4 | # Config for dpdk interface (port) #0 5 | [INTF_0] 6 | # upf ip (to RAN) 7 | ipv4 = 10.20.1.22 8 | type = GTPU 9 | 10 | # Config for dpdk interface (port) #1 11 | [INTF_1] 12 | # on behalf of UE 13 | ipv4 = 172.16.0.1 14 | type = GTPU 15 | 16 | [TUNNEL_0] 17 | teid_in = 100 18 | teid_out = 200 19 | ue_ipv4 = 172.16.0.1 20 | ran_ipv4 = 10.20.1.31 21 | 22 | # (optional) Set static arp table 23 | ; [ARP_0] 24 | ; ipv4 = 13.7.1.2 25 | ; mac = 3c:fd:fe:7a:6c:29 26 | 27 | ; [ARP_1] 28 | ; ipv4 = 13.7.1.3 29 | ; mac = 3c:fd:fe:7a:6c:2a 30 | -------------------------------------------------------------------------------- /src/lib/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef __HELPER_H_ 2 | #define __HELPER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static __rte_always_inline void 9 | print_rte_ipv4(rte_be32_t addr4) 10 | { 11 | struct in_addr addr = {.s_addr = addr4}; 12 | printf("%s", inet_ntoa(addr)); 13 | } 14 | 15 | static __rte_always_inline void 16 | print_rte_ipv4_dbg( 17 | #ifndef DEBUG 18 | __attribute__((unused)) 19 | #endif 20 | rte_be32_t addr4) 21 | { 22 | #ifdef DEBUG 23 | print_rte_ipv4(addr4); 24 | #endif 25 | } 26 | 27 | #endif /* __HELPER_H_ */ 28 | -------------------------------------------------------------------------------- /src/node.h: -------------------------------------------------------------------------------- 1 | #ifndef __NODE__ 2 | #define __NODE__ 3 | 4 | #include "config.h" 5 | 6 | /* DEFINES */ 7 | #define RTE_TEST_TX_DESC_DEFAULT (4096) 8 | #define RTE_TEST_RX_DESC_DEFAULT (1024) 9 | 10 | /* STRUCTURES */ 11 | typedef struct numa_info_s { 12 | struct rte_mempool *tx[GTP_MAX_LCORECOUNT]; 13 | struct rte_mempool *rx[GTP_MAX_LCORECOUNT]; 14 | 15 | uint32_t lcoreAvail; 16 | uint32_t intfAvail; 17 | uint32_t lcoreUsed; 18 | uint32_t intfUsed; 19 | 20 | uint8_t lcoreTotal; 21 | uint8_t intfTotal; 22 | uint8_t lcoreInUse; 23 | uint8_t intfInUse; 24 | } numa_info_t; 25 | 26 | /* FUNCTION DECLARATION */ 27 | int32_t populate_node_info(void); 28 | int32_t node_interface_setup(void); 29 | 30 | #endif /* __NODE__ */ 31 | -------------------------------------------------------------------------------- /src/netstack/ether.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ether.h 3 | * ref: https://github.com/rajneshrat/dpdk-tcpipstack 4 | */ 5 | #ifndef __EHTER_H_ 6 | #define __EHTER_H_ 7 | 8 | #include 9 | #include 10 | 11 | #define MAX_INTERFACES 10 12 | 13 | typedef struct interface_s { 14 | uint8_t port; 15 | unsigned char hw_addr[RTE_ETHER_ADDR_LEN]; 16 | uint32_t ipv4_addr; // host format (before htonl) 17 | struct interface_s *next; 18 | } interface_t; 19 | 20 | /** 21 | * @param address e.g. {"192", "168", "0", "1"} 22 | */ 23 | uint32_t int_addr_from_char(unsigned char *address, uint8_t order); 24 | 25 | void add_interface(interface_t *iface); 26 | void set_interface_hw(uint8_t port, uint8_t *mac_addr); 27 | 28 | #endif /* __EHTER_H_ */ 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dpdk_gtp_gateway 2 | DPDK implementation of GTPv1 user plane gateway. 3 | 4 | ### Features 5 | - High speed GTP-U packet encapsulation and decapsulation 6 | - Proxy ARP on behalf of UE IPs 7 | - Packet statistics update on stdout 8 | - Node socket aware memory config 9 | 10 | # Build and Run 11 | Environment 12 | - OS: Ubuntu 18.04 LTS 13 | - DPDK: 19.11.2 LTS 14 | 15 | Copy and edit config 16 | ```bash 17 | cp gtp_config.example.ini gtp_config.ini 18 | ``` 19 | 20 | Make and run the program with EAL parameters 21 | ```bash 22 | make 23 | sudo ./build/gtpgw -l 0,1,2 -n 4 24 | ``` 25 | 26 | ### References 27 | - [vipinpv85/GTP_PKT_DECODE](https://github.com/vipinpv85/GTP_PKT_DECODE) 28 | - [rajneshrat/dpdk-tcpipstack](https://github.com/rajneshrat/dpdk-tcpipstack) 29 | -------------------------------------------------------------------------------- /src/lib/pktbuf.c: -------------------------------------------------------------------------------- 1 | #include "pktbuf.h" 2 | 3 | #include 4 | 5 | #include "logger.h" 6 | 7 | static struct rte_mempool * pktmbuf_pool = NULL; 8 | 9 | int 10 | mbuf_init(void) 11 | { 12 | if (pktmbuf_pool) 13 | return -1; 14 | 15 | pktmbuf_pool = rte_mempool_create("mbuf_pool", NB_MBUF, 16 | MBUF_SIZE, 32, 17 | sizeof(struct rte_pktmbuf_pool_private), 18 | rte_pktmbuf_pool_init, NULL, 19 | rte_pktmbuf_init, NULL, 20 | SOCKET_ID_ANY, // rte_socket_id(), 21 | 0); 22 | 23 | if (!pktmbuf_pool) { 24 | logger(LOG_LIB, L_CRITICAL, "mbuf_init failed\n"); 25 | } 26 | 27 | return pktmbuf_pool ? 0 : -1; 28 | } 29 | 30 | struct rte_mbuf* 31 | get_mbuf(void) 32 | { 33 | struct rte_mbuf* buf; 34 | assert(unlikely(pktmbuf_pool != NULL)); 35 | 36 | if (unlikely((buf = rte_pktmbuf_alloc(pktmbuf_pool)) == NULL)) 37 | return NULL; 38 | else 39 | return buf; 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Vipin Varghese 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/netstack/ether.c: -------------------------------------------------------------------------------- 1 | /** 2 | * ether.c 3 | * ref: https://github.com/rajneshrat/dpdk-tcpipstack 4 | */ 5 | #include "ether.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "arp.h" 12 | 13 | interface_t *iface_list = NULL; 14 | interface_t *port_iface_map[MAX_INTERFACES] = {0}; 15 | 16 | uint32_t 17 | int_addr_from_char(unsigned char *address, uint8_t order) 18 | { 19 | uint32_t i, ip_add = 0; 20 | 21 | for (i = 0; i < 4; i++) { 22 | ip_add = ip_add << 8; 23 | ip_add |= order ? address[3 - i] : address[i]; 24 | } 25 | 26 | return ip_add; 27 | } 28 | 29 | void 30 | add_interface(interface_t *iface) 31 | { 32 | interface_t *ptr = malloc(sizeof(interface_t)); 33 | 34 | memcpy(ptr, iface, sizeof(interface_t)); 35 | ptr->next = NULL; 36 | 37 | if (iface_list == NULL) { 38 | iface_list = ptr; 39 | } else { 40 | iface_list->next = ptr; 41 | } 42 | 43 | if (ptr->port + 1 < MAX_INTERFACES) { 44 | port_iface_map[ptr->port] = ptr; 45 | } else { 46 | printf("ERROR :: interface number more than max\n"); 47 | } 48 | 49 | arp_add_mac(ptr->ipv4_addr, ptr->hw_addr, 1); 50 | } 51 | -------------------------------------------------------------------------------- /src/lib/logger.c: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define LOG_FILE "dpdkgtpv1.log" 8 | 9 | struct LoggerFeature *enable_feature; 10 | 11 | void 12 | logger_init(void) 13 | { 14 | enable_feature = calloc(LOG_ALL_Features, sizeof(struct LoggerFeature)); 15 | memset(enable_feature, 0, sizeof(struct LoggerFeature)); 16 | 17 | logger_enable_trace(LOG_APP, L_ALL); 18 | logger_enable_trace(LOG_ARP, L_ALL); 19 | // logger_enable_trace(LOG_ETHER, L_ALL); 20 | logger_enable_trace(LOG_GTP, L_ALL); 21 | // logger_enable_trace(LOG_LIB, L_ALL); 22 | } 23 | 24 | void 25 | logger_enable_trace(Feature feature, TraceLevel level) 26 | { 27 | enable_feature[feature].enable = 1; 28 | enable_feature[feature].level = level; 29 | } 30 | 31 | void 32 | logger_s(Feature feature, TraceLevel level, const char *format, ...) 33 | { 34 | va_list(arglist); 35 | 36 | if (enable_feature[feature].enable && enable_feature[feature].level >= level) { 37 | // FILE *fd = fopen(LOG_FILE, "a"); 38 | FILE *fd = stdout; 39 | va_start(arglist, format); 40 | 41 | // vfprintf(fd, "Log feature %d ---- ", feature); 42 | vfprintf(fd, format, arglist); 43 | // fprintf(fd, "\n"); 44 | 45 | va_end(arglist); 46 | // fclose(fd); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/lib/logger.h: -------------------------------------------------------------------------------- 1 | /** 2 | * logger.h 3 | * ref: https://github.com/rajneshrat/dpdk-tcpipstack 4 | */ 5 | #ifndef __LOGGER_H_ 6 | #define __LOGGER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef DEBUG 13 | #define printf_dbg(fmt, ...) printf(fmt, ##__VA_ARGS__) 14 | #else 15 | #define printf_dbg(fmt, ...) 16 | #endif 17 | 18 | typedef enum { 19 | LOG_APP, 20 | LOG_ARP, 21 | LOG_ETHER, 22 | LOG_GTP, 23 | LOG_LIB, 24 | LOG_ALL_Features, 25 | } Feature; 26 | 27 | typedef enum { 28 | L_CRITICAL, 29 | L_WARN, 30 | L_INFO, 31 | L_DEBUG, 32 | L_ALL, 33 | } TraceLevel; 34 | 35 | struct LoggerFeature { 36 | TraceLevel level; 37 | uint8_t enable; 38 | }; 39 | 40 | extern struct LoggerFeature *enable_feature; 41 | void logger_init(void); 42 | void logger_enable_trace(Feature feature, TraceLevel level); 43 | 44 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 45 | 46 | #define logger(_feature, _level, ...) \ 47 | if (enable_feature[_feature].enable && enable_feature[_feature].level >= _level) { \ 48 | printf("[Log] %s:%d(%s) :: ", __FILENAME__, __LINE__, __func__); \ 49 | logger_s(_feature, _level, __VA_ARGS__); \ 50 | } 51 | 52 | void logger_s(Feature feature, TraceLevel level, const char *format, ...); 53 | 54 | #endif /* __LOGGER_H_ */ 55 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "netstack/arp.h" 28 | 29 | /* DEFINES */ 30 | #define GTP_CFG_FILE "gtp_config.ini" 31 | #define GTP_CFG_MAX_KEYLEN 15 32 | #define GTP_CFG_TAG_INTF "INTF_" 33 | #define GTP_CFG_MAX_PORTS 10 34 | #define GTP_CFG_TAG_TUNNEL "TUNNEL_" 35 | #define GTP_CFG_MAX_TUNNELS 100 36 | #define GTP_CFG_TAG_ARP "ARP_" 37 | #define GTP_CFG_MAX_ARPS 100 38 | 39 | #define GTP_MAX_NUMANODE 4 40 | #define GTP_MAX_LCORECOUNT 32 41 | #define GTP_MAX_INTFCOUNT 4 42 | 43 | #define CFG_VAL_GTPU 0x01 44 | 45 | #define STRCPY(x, y) strcpy((char *)x, (const char *)y) 46 | #define STRCMP(x, y) strcmp((const char *)x, (const char *)y) 47 | #define STRNCMP(x, y, n) strncmp((const char *)x, (const char *)y, n) 48 | 49 | typedef struct confg_gtp_port_s { 50 | uint8_t port_num; 51 | // char ipv4[INET_ADDRSTRLEN]; 52 | uint32_t ipv4; // host format (before htonl) 53 | uint8_t gtp_type; 54 | uint8_t pkt_index; 55 | } confg_gtp_port_t; 56 | 57 | typedef struct confg_gtp_tunnel_s { 58 | uint8_t id; 59 | uint32_t teid_in; 60 | uint32_t teid_out; 61 | uint32_t ue_ipv4; // host format (before htonl) 62 | uint32_t ran_ipv4; // host format (before htonl) 63 | } confg_gtp_tunnel_t; 64 | 65 | typedef struct app_confg_s { 66 | uint8_t disp_stats; 67 | 68 | uint8_t gtp_port_count; 69 | confg_gtp_port_t gtp_ports[GTP_CFG_MAX_PORTS]; 70 | struct rte_hash *gtp_port_hash; // [port_num] = *gtp_port 71 | 72 | uint8_t gtp_tunnel_count; 73 | confg_gtp_tunnel_t gtp_tunnels[GTP_CFG_MAX_TUNNELS]; 74 | struct rte_hash *teid_in_hash; // [teid_in] = *gtp_tunnel 75 | struct rte_hash *ue_ipv4_hash; // [ue_ipv4] = *gtp_tunnel 76 | 77 | uint8_t static_arp_count; 78 | arp_entry_t static_arps[GTP_CFG_MAX_ARPS]; 79 | } app_confg_t; 80 | 81 | int32_t load_config(void); 82 | 83 | #endif /*__CONFIG_H__*/ 84 | -------------------------------------------------------------------------------- /src/netstack/arp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * arp.h - arp data structure 3 | * TODO: thread safe 4 | */ 5 | #ifndef __ARP_H_ 6 | #define __ARP_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "logger.h" 14 | 15 | #define MAX_ARP_ENTRIES 8192 16 | #define MAX_EGRESS_Q_IP_ENTRIES 8192 17 | 18 | #define HW_TYPE_ETHERNET 1 19 | #define SW_TYPE_IPV4 0x0800 20 | #define PR_LEN_IPV4 4 21 | 22 | typedef enum { 23 | ARP_REQ = 1, 24 | ARP_REPLY, 25 | RARP_REQ, 26 | RARP_REPLY, 27 | } arp_type_t; 28 | 29 | // See also arp_state_str[] in arp.c 30 | typedef enum { 31 | ARP_STATE_ANY = 0, 32 | ARP_STATE_INCOMPLETE, 33 | // states below are valid for arp_get_mac() 34 | ARP_STATE_REACHABLE, 35 | ARP_STATE_PERMANENT, 36 | } arp_state_t; 37 | 38 | struct arp { 39 | uint16_t hw_type; 40 | uint16_t pr_type; 41 | uint8_t hw_len; 42 | uint8_t pr_len; 43 | uint16_t opcode; 44 | unsigned char src_hw_add[RTE_ETHER_ADDR_LEN]; 45 | unsigned char src_pr_add[PR_LEN_IPV4]; 46 | unsigned char dst_hw_add[RTE_ETHER_ADDR_LEN]; 47 | unsigned char dst_pr_add[PR_LEN_IPV4]; 48 | } __attribute__((__packed__)) __attribute__((aligned(2))); 49 | 50 | typedef struct arp_entry_s { 51 | uint32_t ipv4_addr; // host format (before htonl) 52 | unsigned char mac_addr[RTE_ETHER_ADDR_LEN]; 53 | arp_state_t state; 54 | } arp_entry_t; 55 | 56 | int arp_init(int with_locks); 57 | int arp_terminate(void); 58 | 59 | int arp_in(struct rte_mbuf *mbuf); 60 | int arp_send_request(uint32_t dst_ip_addr, uint8_t port); 61 | 62 | /** 63 | * @return 64 | * - 0 if sent successfully 65 | * - A negative number if error occurred 66 | */ 67 | int arp_send_reply(uint32_t src_ip_addr, unsigned char *dst_hw_addr, 68 | unsigned char *dst_pr_add); 69 | 70 | int arp_get_mac(uint32_t ipv4_addr, unsigned char *mac_addr); 71 | 72 | /** 73 | * Add an IPv4-MAC pair into arp table. 74 | * If there is an arp entry with same IP existed, the mac addr will be updated. 75 | * 76 | * @return 77 | * - 0 if added successfully 78 | * - A negative number if error occurred 79 | */ 80 | int arp_add_mac(uint32_t ipv4_addr, unsigned char *mac_addr, int permanent); 81 | 82 | int arp_queue_egress_pkt(uint32_t ipv4_addr, struct rte_mbuf *m); 83 | 84 | void arp_print_table(TraceLevel trace_level); 85 | void print_ipv4(uint32_t ip_addr, TraceLevel trace_level); 86 | void print_mac(unsigned char *mac_addr, TraceLevel trace_level); 87 | 88 | #endif /* __ARP_H_ */ 89 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # BSD LICENSE 2 | # 3 | # Copyright(c) 2010-2014 Intel Corporation. All rights reserved. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above copyright 13 | # notice, this list of conditions and the following disclaimer in 14 | # the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of Intel Corporation nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | ####################################################### 33 | # Uncomment the following line for debugging each packet 34 | # DEFINE_FLAGS += -DDEBUG 35 | 36 | ####################################################### 37 | 38 | ifeq ($(RTE_SDK),) 39 | $(error "Please define RTE_SDK environment variable") 40 | endif 41 | 42 | # Default target, can be overriden by command line or environment 43 | RTE_TARGET ?= x86_64-native-linuxapp-gcc 44 | 45 | include $(RTE_SDK)/mk/rte.vars.mk 46 | 47 | # Binary name 48 | APP = gtpgw 49 | 50 | # All source are stored in SRCS-y 51 | SRC_DIR = $(abspath $(shell pwd)/..)/src 52 | LIB_DIR = $(SRC_DIR)/lib 53 | 54 | CFLAGS += -I${LIB_DIR} 55 | LDFLAGS += -L$(LIB_DIR) 56 | 57 | SRCS-y := $(LIB_DIR)/logger.c $(LIB_DIR)/pktbuf.c \ 58 | $(SRC_DIR)/netstack/arp.c $(SRC_DIR)/netstack/ether.c \ 59 | $(SRC_DIR)/node.c $(SRC_DIR)/stats.c $(SRC_DIR)/config.c \ 60 | $(SRC_DIR)/main.c 61 | 62 | CFLAGS += -std=gnu99 -g -O0 $(DEFINE_FLAGS) 63 | # CFLAGS += -std=gnu99 -g -O3 $(DEFINE_FLAGS) 64 | # CFLAGS += -std=gnu99 -Ofast $(DEFINE_FLAGS) 65 | CFLAGS += $(WERROR_FLAGS) 66 | 67 | include $(RTE_SDK)/mk/rte.extapp.mk 68 | -------------------------------------------------------------------------------- /src/stats.h: -------------------------------------------------------------------------------- 1 | #ifndef __STATS_H_ 2 | #define __STATS_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | #include "node.h" 9 | 10 | #include 11 | #include 12 | 13 | #define STATS_CLR_SCREEN printf("\033[2J") 14 | //#define STATS_ROW(x) printf("\033[x;1H") 15 | //#define STATS_ROW_COL(x,y) printf("\033[x;yH") 16 | #define STATS_ROW(x) "\033[x;1H" 17 | #define STATS_ROW_COL(x,y) "\033[x;yH" 18 | #define STATS_POS_OFFSET 2 19 | 20 | #define RESET "\033[0m" 21 | #define BLACK "\033[30m" /* Black */ 22 | #define RED "\033[31m" /* Red */ 23 | #define GREEN "\033[32m" /* Green */ 24 | #define YELLOW "\033[33m" /* Yellow */ 25 | #define BLUE "\033[34m" /* Blue */ 26 | #define MAGENTA "\033[35m" /* Magenta */ 27 | #define CYAN "\033[36m" /* Cyan */ 28 | #define WHITE "\033[37m" /* White */ 29 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 30 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 31 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 32 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 33 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 34 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 35 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 36 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 37 | 38 | typedef enum { 39 | LINK_SPEED = 4, 40 | NUMA_SOCKET, 41 | LINK_SPEED_STATE, 42 | PKTS_PER_SEC_RX, 43 | PKTS_PER_SEC_TX, 44 | MB_RX, 45 | MB_TX, 46 | PKT_INFO, 47 | DST_MAC, 48 | SRC_MAC, 49 | BS_SRC_IP, 50 | BS_DST_IP, 51 | GTP_TYPE_VER, 52 | GTP_TEID, 53 | UE_SRC_IP, 54 | UE_DST_IP, 55 | UE_IP_PROTO, 56 | GTPU_TX_IPV4, 57 | GTPU_TX_IPV6, 58 | GTPU_RX_IPV4, 59 | GTPU_RX_IPV6, 60 | SURICATA_QUEUE_RX, 61 | SURICATA_QUEUE_TX, 62 | } statsDisplayPos; 63 | 64 | typedef struct pkt_stats_s { 65 | uint64_t rx_gptu_ipv4; 66 | uint64_t rx_gptu_ipv6; 67 | uint64_t tx_gptu; 68 | uint64_t encap_err; 69 | uint64_t decap_err; 70 | 71 | uint64_t non_ipv4; 72 | uint64_t non_udp; 73 | uint64_t non_gtp; 74 | uint64_t non_gtpVer; 75 | uint64_t ipFrag; 76 | uint64_t ipCsumErr; 77 | uint64_t udpCsumErr; 78 | 79 | uint64_t dropped; 80 | 81 | uint64_t rxPkts; 82 | uint64_t txPkts; 83 | uint64_t rxBytes; 84 | uint64_t txBytes; 85 | uint64_t rxMissed; 86 | uint64_t rxErr; 87 | uint64_t txErr; 88 | uint64_t rxNoMbuff; 89 | } pkt_stats_t; /* per interface */ 90 | 91 | void get_link_stats(__attribute__((unused)) struct rte_timer *t, 92 | __attribute__((unused)) void *arg); 93 | void get_process_stats(__attribute__((unused)) struct rte_timer *t, 94 | __attribute__((unused)) void *arg); 95 | 96 | void set_stats_timer(void); 97 | void show_static_display(void); 98 | void sig_extra_stats(int signo); 99 | void sig_config(int signo); 100 | 101 | #endif /* __STATS_H__ */ 102 | 103 | -------------------------------------------------------------------------------- /src/node.c: -------------------------------------------------------------------------------- 1 | #include "node.h" 2 | 3 | #include 4 | 5 | #include "pktbuf.h" 6 | 7 | /* GLOBALS */ 8 | numa_info_t numa_node_info[GTP_MAX_NUMANODE]; 9 | 10 | /* EXTERN */ 11 | extern app_confg_t app_config; 12 | 13 | static const struct rte_eth_conf portConf = { 14 | .rxmode = { 15 | // .offloads = DEV_RX_OFFLOAD_CHECKSUM, 16 | .split_hdr_size = 0, /**< Header Split disabled */ 17 | // .hw_vlan_filter = 0, /**< VLAN filtering disabled */ 18 | // .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ 19 | // .hw_strip_crc = 0, /**< CRC stripped by hardware */ 20 | }, 21 | .txmode = { 22 | .mq_mode = ETH_MQ_TX_NONE, 23 | .offloads = DEV_TX_OFFLOAD_IPV4_CKSUM | 24 | DEV_TX_OFFLOAD_UDP_CKSUM, 25 | }, 26 | }; 27 | 28 | int32_t 29 | populate_node_info(void) 30 | { 31 | int32_t i = 0, socketId = -1, lcoreIndex = 0, enable = 0; 32 | struct rte_eth_dev_info devInfo; 33 | struct rte_ether_addr addr; 34 | 35 | /* fetch total lcore count under DPDK */ 36 | uint32_t lc; 37 | RTE_LCORE_FOREACH(lc) { 38 | socketId = rte_lcore_to_socket_id(lc); 39 | lcoreIndex = rte_lcore_index(lc); 40 | enable = rte_lcore_is_enabled(lc); 41 | 42 | printf("\n Logical %d Physical %d Socket %d enabled %d", lcoreIndex, lc, socketId, enable); 43 | 44 | if (likely(enable)) { 45 | /* classify the lcore info per NUMA node */ 46 | numa_node_info[socketId].lcoreAvail = numa_node_info[socketId].lcoreAvail | (1 << lcoreIndex); 47 | numa_node_info[socketId].lcoreTotal += 1; 48 | } else { 49 | rte_panic("\nERROR: Lcore %d Socket %d not enabled\n", lcoreIndex, socketId); 50 | exit(EXIT_FAILURE); 51 | } 52 | } 53 | printf("\n"); 54 | 55 | /* Create mempool per numa node based on interface available */ 56 | uint8_t portCount = rte_eth_dev_count_avail(); 57 | for (i = 0; i < portCount; i++) { 58 | rte_eth_dev_info_get(i, &devInfo); 59 | rte_eth_macaddr_get(i, &addr); 60 | 61 | if (rte_hash_lookup(app_config.gtp_port_hash, &i) >= 0) { 62 | printf("\n [Interface %d *GTPGW*]", i); 63 | } else { 64 | printf("\n [Interface %d]", i); 65 | } 66 | 67 | printf("\n - Driver: %s", devInfo.driver_name); 68 | printf("\n - If index: %d", devInfo.if_index); 69 | printf("\n - MAC: %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 70 | " %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, 71 | addr.addr_bytes[0], addr.addr_bytes[1], 72 | addr.addr_bytes[2], addr.addr_bytes[3], 73 | addr.addr_bytes[4], addr.addr_bytes[5]); 74 | 75 | const struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(devInfo.device); 76 | if (pci_dev) { 77 | printf("\n - PCI INFO "); 78 | printf("\n -- ADDR - domain:bus:devid:function %x:%x:%x:%x", 79 | pci_dev->addr.domain, 80 | pci_dev->addr.bus, 81 | pci_dev->addr.devid, 82 | pci_dev->addr.function); 83 | printf("\n == PCI ID - vendor:device:sub-vendor:sub-device %x:%x:%x:%x", 84 | pci_dev->id.vendor_id, 85 | pci_dev->id.device_id, 86 | pci_dev->id.subsystem_vendor_id, 87 | pci_dev->id.subsystem_device_id); 88 | printf("\n -- numa node: %d", devInfo.device->numa_node); 89 | } 90 | 91 | socketId = (devInfo.device->numa_node == -1) ? 0 : devInfo.device->numa_node; 92 | numa_node_info[socketId].intfAvail = numa_node_info[socketId].intfAvail | (1 << i); 93 | numa_node_info[socketId].intfTotal += 1; 94 | printf("\n"); 95 | } 96 | 97 | /* allocate mempool for numa which has NIC interfaces */ 98 | for (i = 0; i < GTP_MAX_NUMANODE; i++) { 99 | if (likely(numa_node_info[i].intfAvail)) { 100 | /* ToDo: per interface */ 101 | uint8_t portIndex = 0; 102 | char mempoolName[25]; 103 | 104 | /* create mempool for TX */ 105 | sprintf(mempoolName, "mbuf_pool-%d-%d-tx", i, portIndex); 106 | numa_node_info[i].tx[portIndex] = rte_mempool_create( 107 | mempoolName, NB_MBUF, 108 | MBUF_SIZE, 64, 109 | sizeof(struct rte_pktmbuf_pool_private), 110 | rte_pktmbuf_pool_init, NULL, 111 | rte_pktmbuf_init, NULL, 112 | i, /*SOCKET_ID_ANY*/ 113 | 0 /*MEMPOOL_F_SP_PUT*/); 114 | if (unlikely(numa_node_info[i].tx[portIndex] == NULL)) { 115 | rte_panic("\n ERROR: failed to get mem-pool for tx on node %d intf %d\n", i, portIndex); 116 | exit(EXIT_FAILURE); 117 | } 118 | 119 | /* create mempool for RX */ 120 | sprintf(mempoolName, "mbuf_pool-%d-%d-rx", i, portIndex); 121 | numa_node_info[i].rx[portIndex] = rte_mempool_create( 122 | mempoolName, NB_MBUF, 123 | MBUF_SIZE, 64, 124 | sizeof(struct rte_pktmbuf_pool_private), 125 | rte_pktmbuf_pool_init, NULL, 126 | rte_pktmbuf_init, NULL, 127 | i, /*SOCKET_ID_ANY*/ 128 | 0 /*MEMPOOL_F_SP_PUT*/); 129 | if (unlikely(numa_node_info[i].rx[portIndex] == NULL)) { 130 | rte_panic("\n ERROR: failed to get mem-pool for rx on node %d intf %d\n", i, portIndex); 131 | exit(EXIT_FAILURE); 132 | } 133 | } 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | int32_t 140 | node_interface_setup(void) 141 | { 142 | uint8_t portIndex = 0, portCount = rte_eth_dev_count_avail(); 143 | int32_t ret = 0, socket_id = -1; 144 | 145 | for (portIndex = 0; portIndex < portCount; portIndex++) { 146 | /* fetch the socket Id to which the port the mapped */ 147 | for (ret = 0; ret < GTP_MAX_NUMANODE; ret++) { 148 | if (numa_node_info[ret].intfTotal) { 149 | if (numa_node_info[ret].intfAvail & (1 << portIndex)) { 150 | socket_id = ret; 151 | break; 152 | } 153 | } 154 | } 155 | 156 | ret = rte_eth_dev_configure(portIndex, 1, 1, &portConf); 157 | if (unlikely(ret < 0)) { 158 | rte_panic("ERROR: Dev Configure\n"); 159 | return -1; 160 | } 161 | 162 | ret = rte_eth_rx_queue_setup(portIndex, 0, RTE_TEST_RX_DESC_DEFAULT, 163 | 0, NULL, numa_node_info[socket_id].rx[0]); 164 | if (unlikely(ret < 0)) { 165 | rte_panic("ERROR: Rx Queue Setup\n"); 166 | return -2; 167 | } 168 | 169 | ret = rte_eth_tx_queue_setup(portIndex, 0, RTE_TEST_TX_DESC_DEFAULT, 170 | 0, NULL); 171 | if (unlikely(ret < 0)) { 172 | rte_panic("ERROR: Tx Queue Setup\n"); 173 | return -3; 174 | } 175 | 176 | rte_eth_promiscuous_enable(portIndex); 177 | rte_eth_dev_start(portIndex); 178 | } 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /src/gtp_process.h: -------------------------------------------------------------------------------- 1 | #ifndef __GTP_PROCESS__ 2 | #define __GTP_PROCESS__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "logger.h" 11 | #include "helper.h" 12 | #include "stats.h" 13 | #include "netstack/arp.h" 14 | #include "netstack/ether.h" 15 | 16 | /* EXTERN */ 17 | extern app_confg_t app_config; 18 | extern interface_t *iface_list; 19 | extern interface_t *port_iface_map[MAX_INTERFACES]; 20 | extern pkt_stats_t port_pkt_stats[GTP_CFG_MAX_PORTS]; 21 | 22 | /* DEFINES */ 23 | #define GTP1U_PORT 2152 24 | #define GTP_TPDU 255 25 | 26 | // According to 3GPP TS 29.060 27 | typedef struct gtpv1_header { 28 | uint8_t flags; 29 | uint8_t type; 30 | uint16_t length; 31 | uint32_t teid; 32 | } __attribute__ ((packed)) gtpv1_t; 33 | 34 | #define GTP1_F_NPDU 0x01 35 | #define GTP1_F_SEQ 0x02 36 | #define GTP1_F_EXTHDR 0x04 37 | #define GTP1_F_MASK 0x07 38 | 39 | /* FUNCTION DEFS */ 40 | static __rte_always_inline void 41 | gtpv1_set_header(gtpv1_t *gtp_hdr, uint16_t payload_len, uint32_t teid); 42 | 43 | /* FUNCTIONS */ 44 | static __rte_always_inline int32_t 45 | process_gtpv1(struct rte_mbuf *m, uint8_t port, gtpv1_t *rx_gtp_hdr) 46 | { 47 | int32_t ret; 48 | struct rte_ipv4_hdr *inner_ip_hdr = (struct rte_ipv4_hdr *)((char *)(rx_gtp_hdr + 1)); 49 | print_rte_ipv4_dbg(inner_ip_hdr->src_addr); 50 | printf_dbg(" -> "); 51 | print_rte_ipv4_dbg(inner_ip_hdr->dst_addr); 52 | 53 | if (unlikely((inner_ip_hdr->version_ihl & 0x40) != 0x40)) { 54 | port_pkt_stats[port].rx_gptu_ipv6 += 1; 55 | } else { 56 | port_pkt_stats[port].rx_gptu_ipv4 += 1; 57 | } 58 | 59 | // Check whether there is a matched tunnel 60 | uint32_t teid_in = ntohl(rx_gtp_hdr->teid); 61 | if (unlikely(rte_hash_lookup(app_config.teid_in_hash, &teid_in) < 0)) { 62 | printf(" ERR(No matched tunnel found with teid_in: %d) ", teid_in); 63 | port_pkt_stats[port].dropped += 1; 64 | return 0; 65 | } 66 | 67 | // Outer header removal 68 | const int outer_hdr_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + 69 | sizeof(struct rte_udp_hdr) + sizeof(gtpv1_t); 70 | rte_pktmbuf_adj(m, (uint16_t)outer_hdr_len); 71 | 72 | // Send to another port 73 | // TODO: follow routing table 74 | uint16_t out_port = port ^ 1; 75 | 76 | // Prepend ethernet header 77 | struct rte_ether_hdr *eth_hdr = (struct rte_ether_hdr *) 78 | rte_pktmbuf_prepend(m, (uint16_t)sizeof(struct rte_ether_hdr)); 79 | 80 | eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); 81 | rte_ether_addr_copy( 82 | (const struct rte_ether_addr *)port_iface_map[out_port]->hw_addr, 83 | (struct rte_ether_addr *)eth_hdr->s_addr.addr_bytes); 84 | 85 | ret = arp_get_mac(inner_ip_hdr->dst_addr, eth_hdr->d_addr.addr_bytes); 86 | if (unlikely(ret != 1)) { 87 | printf(" ERR(Inner dst ip not found in arp table: "); 88 | print_rte_ipv4(inner_ip_hdr->dst_addr); 89 | printf(") "); 90 | 91 | // TODO: queue the packet and wait for arp reply instead of dropping it 92 | port_pkt_stats[port].dropped += 1; 93 | 94 | arp_send_request(inner_ip_hdr->dst_addr, out_port); 95 | return 0; 96 | } 97 | 98 | // Transmit 99 | printf_dbg(" [decap TX#%d]", out_port); 100 | ret = rte_eth_tx_burst(out_port, 0, &m, 1); 101 | if (likely(ret == 1)) { 102 | return 1; 103 | } else { 104 | printf_dbg(" ERR(rte_eth_tx_burst=%d) ", ret); 105 | return 0; 106 | } 107 | } 108 | 109 | static __rte_always_inline int32_t 110 | process_ipv4(struct rte_mbuf *m, uint8_t port, struct rte_ipv4_hdr *rx_ip_hdr) 111 | { 112 | int32_t ret; 113 | confg_gtp_tunnel_t *gtp_tunnel; 114 | 115 | if (unlikely(rte_hash_lookup_data(app_config.ue_ipv4_hash, 116 | &rx_ip_hdr->dst_addr, (void **)>p_tunnel) < 0)) { 117 | printf(" ERR(No matched tunnel found by ue_ipv4: "); 118 | print_rte_ipv4(rx_ip_hdr->dst_addr); 119 | printf(") "); 120 | port_pkt_stats[port].dropped += 1; 121 | return 0; 122 | } 123 | 124 | // Outer header removal 125 | rte_pktmbuf_adj(m, (uint16_t)sizeof(struct rte_ether_hdr)); 126 | 127 | // Outer header creation 128 | const int outer_hdr_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + 129 | sizeof(struct rte_udp_hdr) + sizeof(gtpv1_t); 130 | struct rte_ether_hdr *eth_hdr = (struct rte_ether_hdr *) 131 | rte_pktmbuf_prepend(m, (uint16_t)outer_hdr_len); 132 | 133 | // Send to another port 134 | // TODO: follow routing table 135 | // TODO: fix for the odd first port number 136 | uint16_t out_port = port ^ 1; 137 | interface_t *out_iface = port_iface_map[out_port]; 138 | 139 | struct rte_ipv4_hdr *ip_hdr = (struct rte_ipv4_hdr *)((char *)(eth_hdr + 1)); 140 | struct rte_udp_hdr *udp_hdr = (struct rte_udp_hdr *)((char *)(ip_hdr + 1)); 141 | gtpv1_t *gtp1_hdr = (gtpv1_t *)((char *)(udp_hdr + 1)); 142 | 143 | // Ethernet header 144 | eth_hdr->ether_type = 0x8; // IPv4 145 | rte_ether_addr_copy( 146 | (const struct rte_ether_addr *)out_iface->hw_addr, 147 | (struct rte_ether_addr *)eth_hdr->s_addr.addr_bytes); 148 | 149 | ret = arp_get_mac(gtp_tunnel->ran_ipv4, eth_hdr->d_addr.addr_bytes); 150 | if (unlikely(ret != 1)) { 151 | printf(" ERR(Dst ip not found in arp table: "); 152 | print_rte_ipv4(gtp_tunnel->ran_ipv4); 153 | printf(") "); 154 | 155 | // TODO: queue the packet and wait for arp reply instead of dropping it 156 | port_pkt_stats[port].dropped += 1; 157 | 158 | arp_send_request(gtp_tunnel->ran_ipv4, out_port); 159 | return 0; 160 | } 161 | 162 | // IP header 163 | ip_hdr->version_ihl = RTE_IPV4_VHL_DEF; 164 | ip_hdr->total_length = rte_cpu_to_be_16(m->pkt_len 165 | - sizeof(struct rte_ether_hdr)); 166 | ip_hdr->time_to_live = IPDEFTTL; 167 | ip_hdr->next_proto_id = IPPROTO_UDP; 168 | ip_hdr->src_addr = out_iface->ipv4_addr; 169 | ip_hdr->dst_addr = gtp_tunnel->ran_ipv4; 170 | ip_hdr->hdr_checksum = 0; 171 | 172 | // UDP header 173 | udp_hdr->src_port = 0x6808; // htons(2152) 174 | udp_hdr->dst_port = 0x6808; // htons(2152) 175 | udp_hdr->dgram_len = rte_cpu_to_be_16(m->pkt_len 176 | - sizeof(struct rte_ether_hdr) 177 | - sizeof(struct rte_ipv4_hdr)); 178 | udp_hdr->dgram_cksum = 0; 179 | 180 | // GTP header 181 | uint16_t payload_len = m->pkt_len - sizeof(struct rte_ether_hdr) 182 | - sizeof(struct rte_ipv4_hdr) 183 | - sizeof(struct rte_udp_hdr); 184 | gtpv1_set_header(gtp1_hdr, payload_len, gtp_tunnel->teid_out); 185 | 186 | // Checksum offloads 187 | m->l2_len = sizeof(struct rte_ether_hdr); 188 | m->l3_len = sizeof(struct rte_ipv4_hdr); 189 | m->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM; 190 | 191 | // Transmit 192 | printf_dbg(" [encap TX#%d]", out_port); 193 | ret = rte_eth_tx_burst(out_port, 0, &m, 1); 194 | if (likely(ret == 1)) { 195 | port_pkt_stats[out_port].tx_gptu += 1; 196 | return 1; 197 | } else { 198 | printf_dbg(" ERR(rte_eth_tx_burst=%d) ", ret); 199 | return 0; 200 | } 201 | } 202 | 203 | static __rte_always_inline void 204 | gtpv1_set_header(gtpv1_t *gtp1_hdr, uint16_t payload_len, uint32_t teid) 205 | { 206 | /* Bits 8 7 6 5 4 3 2 1 207 | * +--+--+--+--+--+--+--+--+ 208 | * |version |PT| 0| E| S|PN| 209 | * +--+--+--+--+--+--+--+--+ 210 | * 0 0 1 1 0 0 0 0 211 | */ 212 | gtp1_hdr->flags = 0x30; // v1, GTP-non-prime 213 | gtp1_hdr->type = GTP_TPDU; 214 | gtp1_hdr->length = htons(payload_len); 215 | gtp1_hdr->teid = htonl(teid); 216 | 217 | /* TODO: Suppport for extension header, sequence number and N-PDU. 218 | * Update the length field if any of them is available. 219 | */ 220 | } 221 | 222 | #endif /*__GTP_PROCESS__*/ 223 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | app_confg_t app_config = {0}; 13 | 14 | static inline int 15 | get_int(const char *string) 16 | { 17 | int i, stringLength = strlen(string); 18 | 19 | for (i = 0; i < stringLength; i++) { 20 | if ((isdigit(string[i]) == 0)) 21 | return -1; 22 | } 23 | 24 | return atoi(string); 25 | } 26 | 27 | // "xx:xx:xx:xx:xx:xx" to unsigned char hw_addr[RTE_ETHER_ADDR_LEN] 28 | static inline void 29 | cvt_mac(const char *string, unsigned char *hw_addr) 30 | { 31 | int i, idx = -3; 32 | 33 | for (i = 0; i < 6; i++) { 34 | idx += 3; 35 | hw_addr[i] = (int)strtol(&string[idx], NULL, 16); 36 | } 37 | } 38 | 39 | static void 40 | init_config_hash(int with_locks) 41 | { 42 | struct rte_hash_parameters params = {0}; 43 | 44 | // Initialize gtp_port_hash 45 | params.name = "gtp_port_hash"; 46 | params.entries = GTP_CFG_MAX_PORTS; 47 | params.key_len = sizeof(uint8_t); 48 | params.hash_func = rte_jhash; 49 | params.hash_func_init_val = 0; 50 | params.socket_id = rte_socket_id(); 51 | if (with_locks) { 52 | params.extra_flag = 53 | RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT 54 | | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY; 55 | } else { 56 | params.extra_flag = 0; 57 | } 58 | 59 | assert(rte_hash_find_existing(params.name) == NULL); 60 | app_config.gtp_port_hash = rte_hash_create(¶ms); 61 | assert((intptr_t)app_config.gtp_port_hash > 0); 62 | 63 | // Initialize teid_in_hash 64 | params.name = "teid_in_hash"; 65 | params.entries = GTP_CFG_MAX_TUNNELS; 66 | params.key_len = sizeof(uint32_t); 67 | params.hash_func = rte_jhash; 68 | params.hash_func_init_val = 0; 69 | params.socket_id = rte_socket_id(); 70 | if (with_locks) { 71 | params.extra_flag = 72 | RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT 73 | | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY; 74 | } else { 75 | params.extra_flag = 0; 76 | } 77 | 78 | assert(rte_hash_find_existing(params.name) == NULL); 79 | app_config.teid_in_hash = rte_hash_create(¶ms); 80 | assert((intptr_t)app_config.teid_in_hash > 0); 81 | 82 | // Initialize ue_ipv4_hash 83 | memset(¶ms, 0, sizeof(struct rte_hash_parameters)); 84 | params.name = "ue_ipv4_hash"; 85 | params.entries = GTP_CFG_MAX_TUNNELS; 86 | params.key_len = sizeof(uint32_t); 87 | params.hash_func = rte_jhash; 88 | params.hash_func_init_val = 0; 89 | params.socket_id = rte_socket_id(); 90 | if (with_locks) { 91 | params.extra_flag = 92 | RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT 93 | | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY; 94 | } else { 95 | params.extra_flag = 0; 96 | } 97 | 98 | assert(rte_hash_find_existing(params.name) == NULL); 99 | app_config.ue_ipv4_hash = rte_hash_create(¶ms); 100 | assert((intptr_t)app_config.ue_ipv4_hash > 0); 101 | } 102 | 103 | static int 104 | load_global_entries(struct rte_cfgfile *file) 105 | { 106 | const char *section_name = "Global"; 107 | int32_t j = 0, ret = -1; 108 | struct rte_cfgfile_entry entries[32]; 109 | 110 | ret = rte_cfgfile_section_entries(file, section_name, entries, 32); 111 | 112 | for (j = 0; j < ret; j++) { 113 | printf("\n %15s : %-15s", entries[j].name, entries[j].value); 114 | 115 | switch (strlen(entries[j].name)) { 116 | case 10: 117 | if (STRCMP("disp_stats", entries[j].name) == 0) { 118 | app_config.disp_stats = STRCMP("1", entries[j].value) == 0; 119 | } 120 | break; 121 | 122 | default: 123 | printf("\n ERROR: unexpected entry %s with value %s\n", 124 | entries[j].name, entries[j].value); 125 | fflush(stdout); 126 | return -1; 127 | } /* update per entry */ 128 | } /* iterate entries */ 129 | 130 | return 0; 131 | } 132 | 133 | static int 134 | load_intf_entries(struct rte_cfgfile *file, const char *section_name, int32_t intf_idx) 135 | { 136 | int32_t j = 0, port_num, ret = -1; 137 | struct rte_cfgfile_entry entries[32]; 138 | 139 | ret = rte_cfgfile_section_entries(file, section_name, entries, 32); 140 | port_num = get_int(section_name + strlen(GTP_CFG_TAG_INTF)); 141 | app_config.gtp_ports[intf_idx].port_num = port_num; 142 | 143 | for (j = 0; j < ret; j++) { 144 | printf("\n %15s : %-15s", entries[j].name, entries[j].value); 145 | 146 | if (STRCMP("ipv4", entries[j].name) == 0) { 147 | app_config.gtp_ports[intf_idx].ipv4 = inet_addr(entries[j].value); 148 | } else if (STRCMP("type", entries[j].name) == 0) { 149 | app_config.gtp_ports[intf_idx].gtp_type = 150 | (STRCMP("GTPU", entries[j].value) == 0) ? CFG_VAL_GTPU : 0xff; 151 | // } else if (STRCMP("index", entries[j].name) == 0) { 152 | // app_config.gtp_ports[intf_idx].pkt_index = atoi(entries[j].value); 153 | } else { 154 | printf("\n ERROR: unexpected entry %s with value %s\n", 155 | entries[j].name, entries[j].value); 156 | fflush(stdout); 157 | return -1; 158 | } 159 | } /* iterate entries */ 160 | 161 | // Add to hash 162 | ret = rte_hash_add_key_data(app_config.gtp_port_hash, 163 | &app_config.gtp_ports[intf_idx].port_num, 164 | &app_config.gtp_ports[intf_idx]); 165 | assert(ret == 0); 166 | 167 | return 0; 168 | } 169 | 170 | static int 171 | load_tunnel_entries(struct rte_cfgfile *file, const char *section_name) 172 | { 173 | int32_t j = 0, idx, ret = -1; 174 | struct rte_cfgfile_entry entries[32]; 175 | confg_gtp_tunnel_t *gtp_tunnel; 176 | 177 | ret = rte_cfgfile_section_entries(file, section_name, entries, 32); 178 | idx = get_int(section_name + strlen(GTP_CFG_TAG_TUNNEL)); 179 | gtp_tunnel = &app_config.gtp_tunnels[idx]; 180 | gtp_tunnel->id = idx; 181 | 182 | for (j = 0; j < ret; j++) { 183 | printf("\n %15s : %-15s", entries[j].name, entries[j].value); 184 | 185 | if (STRCMP("teid_in", entries[j].name) == 0) { 186 | gtp_tunnel->teid_in = atoi(entries[j].value); 187 | } else if (STRCMP("teid_out", entries[j].name) == 0) { 188 | gtp_tunnel->teid_out = atoi(entries[j].value); 189 | } else if (STRCMP("ue_ipv4", entries[j].name) == 0) { 190 | gtp_tunnel->ue_ipv4 = inet_addr(entries[j].value); 191 | } else if (STRCMP("ran_ipv4", entries[j].name) == 0) { 192 | gtp_tunnel->ran_ipv4 = inet_addr(entries[j].value); 193 | } else { 194 | printf("\n ERROR: unexpected entry %s with value %s\n", 195 | entries[j].name, entries[j].value); 196 | fflush(stdout); 197 | return -1; 198 | } 199 | } /* iterate entries */ 200 | 201 | // Add tunnel pointer to hashes 202 | ret = rte_hash_add_key_data(app_config.teid_in_hash, >p_tunnel->teid_in, gtp_tunnel); 203 | assert(ret == 0); 204 | ret = rte_hash_add_key_data(app_config.ue_ipv4_hash, >p_tunnel->ue_ipv4, gtp_tunnel); 205 | assert(ret == 0); 206 | 207 | return 0; 208 | } 209 | 210 | static int 211 | load_arp_entries(struct rte_cfgfile *file, const char *section_name) 212 | { 213 | int32_t j = 0, idx, ret = -1; 214 | struct rte_cfgfile_entry entries[32]; 215 | arp_entry_t *static_arp; 216 | 217 | ret = rte_cfgfile_section_entries(file, section_name, entries, 32); 218 | idx = get_int(section_name + strlen(GTP_CFG_TAG_ARP)); 219 | static_arp = &app_config.static_arps[idx]; 220 | 221 | for (j = 0; j < ret; j++) { 222 | printf("\n %15s : %-15s", entries[j].name, entries[j].value); 223 | 224 | if (STRCMP("ipv4", entries[j].name) == 0) { 225 | static_arp->ipv4_addr = inet_addr(entries[j].value); 226 | } else if (STRCMP("mac", entries[j].name) == 0) { 227 | cvt_mac(entries[j].value, static_arp->mac_addr); 228 | } else { 229 | printf("\n ERROR: unexpected entry %s with value %s\n", 230 | entries[j].name, entries[j].value); 231 | fflush(stdout); 232 | return -1; 233 | } 234 | } /* iterate entries */ 235 | 236 | return 0; 237 | } 238 | 239 | int32_t 240 | load_config(void) 241 | { 242 | struct rte_cfgfile *file = NULL; 243 | int32_t i = 0, intf_idx = 0, ret; 244 | char **section_names = NULL; 245 | 246 | init_config_hash(0); 247 | 248 | file = rte_cfgfile_load(GTP_CFG_FILE, 0); 249 | if (file == NULL) { 250 | printf("Cannot load configuration profile %s\n", GTP_CFG_FILE); 251 | return -1; 252 | } 253 | 254 | printf("\n Loading config entries:"); 255 | 256 | int32_t intf_count = rte_cfgfile_num_sections(file, GTP_CFG_TAG_INTF, strlen(GTP_CFG_TAG_INTF)); 257 | assert(intf_count <= GTP_CFG_MAX_PORTS); 258 | app_config.gtp_port_count = intf_count; 259 | 260 | int32_t tunnel_count = rte_cfgfile_num_sections(file, GTP_CFG_TAG_TUNNEL, strlen(GTP_CFG_TAG_TUNNEL)); 261 | assert(tunnel_count <= GTP_CFG_MAX_TUNNELS); 262 | app_config.gtp_tunnel_count = tunnel_count; 263 | 264 | int32_t arp_count = rte_cfgfile_num_sections(file, GTP_CFG_TAG_ARP, strlen(GTP_CFG_TAG_ARP)); 265 | assert(arp_count <= GTP_CFG_MAX_ARPS); 266 | app_config.static_arp_count = arp_count; 267 | 268 | const int32_t section_count = 1 + intf_count + tunnel_count + arp_count; // "Global" + ... 269 | section_names = malloc(section_count * sizeof(char *)); 270 | for (i = 0; i < section_count; i++) 271 | section_names[i] = malloc(GTP_CFG_MAX_KEYLEN + 1); 272 | 273 | rte_cfgfile_sections(file, section_names, section_count); 274 | 275 | for (i = 0; i < section_count; i++) { 276 | printf("\n\n [%s]", section_names[i]); 277 | printf("\n --------------------------------"); 278 | 279 | if (STRCMP("Global", section_names[i]) == 0) { 280 | ret = load_global_entries(file); 281 | assert(ret == 0); 282 | } else if (STRNCMP(GTP_CFG_TAG_INTF, section_names[i], strlen(GTP_CFG_TAG_INTF)) == 0) { 283 | ret = load_intf_entries(file, section_names[i], intf_idx++); 284 | assert(ret == 0); 285 | } else if (STRNCMP(GTP_CFG_TAG_TUNNEL, section_names[i], strlen(GTP_CFG_TAG_TUNNEL)) == 0) { 286 | ret = load_tunnel_entries(file, section_names[i]); 287 | assert(ret == 0); 288 | } else if (STRNCMP(GTP_CFG_TAG_ARP, section_names[i], strlen(GTP_CFG_TAG_ARP)) == 0) { 289 | ret = load_arp_entries(file, section_names[i]); 290 | assert(ret == 0); 291 | } 292 | } /* per section */ 293 | 294 | ret = rte_cfgfile_close(file); 295 | assert(ret == 0); 296 | 297 | printf("\n\n"); 298 | fflush(stdout); 299 | return 0; 300 | } 301 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "logger.h" 6 | #include "pktbuf.h" 7 | #include "netstack/arp.h" 8 | #include "netstack/ether.h" 9 | 10 | #include "config.h" 11 | #include "node.h" 12 | #include "stats.h" 13 | #include "gtp_process.h" 14 | 15 | /* DEFINES */ 16 | #define MAX_RX_BURST_COUNT 8 17 | #define PREFETCH_OFFSET 4 18 | 19 | /* GLOBALS */ 20 | volatile uint8_t keep_running = 1; 21 | 22 | /* EXTERN */ 23 | extern app_confg_t app_config; 24 | extern numa_info_t numa_node_info[GTP_MAX_NUMANODE]; 25 | extern pkt_stats_t port_pkt_stats[GTP_CFG_MAX_PORTS]; 26 | 27 | static void sigint_handler(__attribute__((unused)) int signo); 28 | static int add_interfaces(void); 29 | static int add_static_arp(void); 30 | static __rte_always_inline int pkt_handler(void *arg); 31 | static __rte_always_inline void process_pkt_mbuf(struct rte_mbuf *m, uint8_t port); 32 | 33 | int 34 | main(int argc, char **argv) 35 | { 36 | int32_t i; 37 | int32_t ret; 38 | 39 | logger_init(); 40 | 41 | // Initialize DPDK EAL 42 | ret = rte_eal_init(argc, argv); 43 | if (ret < 0) { 44 | printf("\n ERROR: cannot init EAL\n"); 45 | return -2; 46 | } 47 | 48 | // Check Huge pages for memory buffers 49 | ret = rte_eal_has_hugepages(); 50 | if (ret < 0) { 51 | rte_panic("\n ERROR: no Huge Page\n"); 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | // Load ini config file 56 | ret = load_config(); 57 | if (ret < 0) { 58 | printf("\n ERROR: failed to load config\n"); 59 | return -1; 60 | } 61 | 62 | // Create packet buffer pool 63 | ret = mbuf_init(); 64 | assert(ret == 0); 65 | 66 | ret = populate_node_info(); 67 | if (ret < 0) { 68 | rte_panic("\n ERROR: in populating NUMA node Info\n"); 69 | exit(EXIT_FAILURE); 70 | } 71 | printf("\n"); 72 | 73 | // Init ARP table 74 | ret = arp_init(0); 75 | assert(ret == 0); 76 | 77 | // Add interface info to interface and arp table 78 | ret = add_interfaces(); 79 | assert(ret == 0); 80 | 81 | // Add static arp 82 | ret = add_static_arp(); 83 | assert(ret == 0); 84 | 85 | // Set interface options and queues 86 | if (node_interface_setup() < 0) { 87 | rte_panic("ERROR: interface setup Failed\n"); 88 | exit(EXIT_FAILURE); 89 | } 90 | 91 | // Launch thread lcores 92 | uint32_t lcore = rte_get_next_lcore(-1, 0, 0); 93 | for (i = 0; i < app_config.gtp_port_count; i++) { 94 | // Skip the first lcore 95 | lcore = rte_get_next_lcore(lcore, 0, 0); 96 | // printf("Starting packet handler %d at lcore %d", i, lcore); 97 | rte_eal_remote_launch(pkt_handler, (void *)&app_config.gtp_ports[i].port_num, lcore); 98 | } 99 | 100 | // Register signals 101 | signal(SIGINT, sigint_handler); 102 | signal(SIGUSR1, sig_extra_stats); 103 | signal(SIGUSR2, sig_config); 104 | 105 | // Show stats 106 | printf("\n DISP_STATS=%s\n", app_config.disp_stats ? "ON" : "OFF"); 107 | if (app_config.disp_stats) { 108 | set_stats_timer(); 109 | rte_delay_ms(1000); 110 | show_static_display(); 111 | } 112 | 113 | while (keep_running) { 114 | rte_delay_ms(500); 115 | if (app_config.disp_stats) { 116 | show_static_display(); 117 | } 118 | rte_timer_manage(); 119 | } 120 | 121 | // Free resources 122 | printf("\n\nCleaning...\n"); 123 | arp_terminate(); 124 | printf("Done.\n"); 125 | return 0; 126 | } 127 | 128 | static void 129 | sigint_handler(__attribute__((unused)) int signo) 130 | { 131 | keep_running = 0; 132 | } 133 | 134 | static int 135 | add_interfaces(void) 136 | { 137 | int32_t i; 138 | uint16_t avail_dev_count = rte_eth_dev_count_avail(); 139 | struct rte_ether_addr addr; 140 | 141 | // Check interfaces in app configs 142 | if (app_config.gtp_port_count == 0 || app_config.gtp_port_count % 2 != 0) { 143 | logger(LOG_APP, L_CRITICAL, 144 | "Number of interface in config (%d) should be even and larger than zero\n", 145 | app_config.gtp_port_count, avail_dev_count); 146 | return -1; 147 | } else if (app_config.gtp_port_count > avail_dev_count) { 148 | logger(LOG_APP, L_CRITICAL, 149 | "Number of interface in config (%d) > avail dpdk eth devices (%d), abort.\n", 150 | app_config.gtp_port_count, avail_dev_count); 151 | return -1; 152 | } 153 | 154 | for (i = 0; i < app_config.gtp_port_count; i++) { 155 | if (app_config.gtp_ports[i].port_num >= avail_dev_count) { 156 | logger(LOG_APP, L_CRITICAL, 157 | "Interface index #%d in config >= avail dpdk eth devices (%d), abort.\n", 158 | app_config.gtp_ports[i].port_num, avail_dev_count); 159 | return -1; 160 | } 161 | } 162 | 163 | if (app_config.gtp_port_count != avail_dev_count) { 164 | logger(LOG_APP, L_WARN, 165 | "Notice: number of interface in config (%d) != avail dpdk eth devices (%d)\n", 166 | app_config.gtp_port_count, avail_dev_count); 167 | } 168 | 169 | // Add interface 170 | for (i = 0; i < app_config.gtp_port_count; i++) { 171 | confg_gtp_port_t *port_config = &app_config.gtp_ports[i]; 172 | interface_t iface; 173 | 174 | rte_eth_macaddr_get(port_config->port_num, &addr); 175 | 176 | iface.port = port_config->port_num; 177 | iface.ipv4_addr = port_config->ipv4; 178 | memcpy(iface.hw_addr, addr.addr_bytes, sizeof(iface.hw_addr)); 179 | 180 | add_interface(&iface); 181 | } 182 | 183 | return 0; 184 | } 185 | 186 | static int 187 | add_static_arp(void) 188 | { 189 | int32_t i, ret; 190 | arp_entry_t *arp_entry; 191 | 192 | for (i = 0; i < app_config.static_arp_count; i++) { 193 | arp_entry = &app_config.static_arps[i]; 194 | ret = arp_add_mac(arp_entry->ipv4_addr, arp_entry->mac_addr, 1); 195 | if (ret != 0) 196 | return -1; 197 | } 198 | 199 | return 0; 200 | } 201 | 202 | static __rte_always_inline int 203 | pkt_handler(void *arg) 204 | { 205 | uint8_t port = *((uint8_t *)arg); 206 | int32_t j, nb_rx; 207 | unsigned lcore_id, socket_id; 208 | struct rte_mbuf *ptr[MAX_RX_BURST_COUNT], *m = NULL; 209 | 210 | lcore_id = rte_lcore_id(); 211 | socket_id = rte_lcore_to_socket_id(lcore_id); 212 | 213 | // TODO: if mempool is per port ignore the below 214 | // mbuf_pool_tx = numa_node_info[socket_id].tx[0]; 215 | // mbuf_pool_rx = numa_node_info[socket_id].rx[port]; 216 | 217 | printf("\n Launched handler for port %d on socket %d \n", port, socket_id); 218 | fflush(stdout); 219 | 220 | while (1) { 221 | // Fetch MAX Burst RX packets 222 | nb_rx = rte_eth_rx_burst(port, 0, ptr, MAX_RX_BURST_COUNT); 223 | 224 | if (likely(nb_rx)) { 225 | // rte_pktmbuf_dump(stdout, ptr[0], 64); 226 | 227 | // Prefetch packets for pipeline 228 | for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++) { 229 | rte_prefetch0(rte_pktmbuf_mtod(ptr[j], void *)); 230 | } 231 | 232 | // Prefetch others packets and process packets 233 | for (j = 0; j < nb_rx - PREFETCH_OFFSET; j++) { 234 | m = ptr[j]; 235 | rte_prefetch0(rte_pktmbuf_mtod(ptr[j + PREFETCH_OFFSET], void *)); 236 | process_pkt_mbuf(m, port); 237 | } 238 | 239 | // Process remaining packets 240 | for (; j < nb_rx; j++) { 241 | m = ptr[j]; 242 | process_pkt_mbuf(m, port); 243 | } 244 | } 245 | } 246 | 247 | return 0; 248 | } 249 | 250 | static __rte_always_inline void 251 | process_pkt_mbuf(struct rte_mbuf *m, uint8_t port) 252 | { 253 | struct rte_ether_hdr *eth_hdr = NULL; 254 | struct rte_ipv4_hdr *ip_hdr = NULL; 255 | struct rte_udp_hdr *udp_hdr = NULL; 256 | gtpv1_t *gtp1_hdr = NULL; 257 | 258 | eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); 259 | printf_dbg("\n [RX] Port#%u ", m->port); 260 | printf_dbg("Ether(type:0x%x dmac: %x:%x:%x:%x:%x:%x) ", 261 | eth_hdr->ether_type, 262 | eth_hdr->d_addr.addr_bytes[0], eth_hdr->d_addr.addr_bytes[1], 263 | eth_hdr->d_addr.addr_bytes[2], eth_hdr->d_addr.addr_bytes[3], 264 | eth_hdr->d_addr.addr_bytes[4], eth_hdr->d_addr.addr_bytes[5]); 265 | 266 | // printf_dbg("smac: %x:%x:%x:%x:%x:%x) ", 267 | // eth_hdr->s_addr.addr_bytes[0], eth_hdr->s_addr.addr_bytes[1], 268 | // eth_hdr->s_addr.addr_bytes[2], eth_hdr->s_addr.addr_bytes[3], 269 | // eth_hdr->s_addr.addr_bytes[4], eth_hdr->s_addr.addr_bytes[5]); 270 | 271 | // Test: forward all non-gtpu packets 272 | // int fwd_port = 1; 273 | // int ret = rte_eth_tx_burst(fwd_port, 0, &m, 1); 274 | // printf(" fwd to port#%d ret=%d\n", fwd_port, ret); 275 | // assert(likely(ret == 1)); 276 | // return; 277 | 278 | // Ether type: IPv4 (rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4) = 0x8) 279 | if (likely(eth_hdr->ether_type == 0x8)) { 280 | ip_hdr = (struct rte_ipv4_hdr *)((char *)(eth_hdr + 1)); 281 | printf_dbg(" IPv4("); 282 | print_rte_ipv4_dbg(ip_hdr->src_addr); 283 | printf_dbg(" -> "); 284 | print_rte_ipv4_dbg(ip_hdr->dst_addr); 285 | printf_dbg(") "); 286 | 287 | // Check IP is fragmented 288 | if (unlikely(rte_ipv4_frag_pkt_is_fragmented(ip_hdr))) { 289 | port_pkt_stats[port].ipFrag += 1; 290 | goto out_flush; 291 | } 292 | 293 | // Check for UDP 294 | // printf(" protocol: %x ", ip_hdr->next_proto_id); 295 | if (likely(ip_hdr->next_proto_id == 0x11)) { 296 | udp_hdr = (struct rte_udp_hdr *)((char *)(ip_hdr + 1)); 297 | printf_dbg(" UDP(port src:%d dst:%d) ", 298 | rte_cpu_to_be_16(udp_hdr->src_port), 299 | rte_cpu_to_be_16(udp_hdr->dst_port)); 300 | 301 | /* GTPU LTE carries V1 only 2152 (htons(2152) = 0x6808) */ 302 | if (likely(udp_hdr->src_port == 0x6808 || 303 | udp_hdr->dst_port == 0x6808)) { 304 | gtp1_hdr = (gtpv1_t *)((char *)(udp_hdr + 1)); 305 | printf_dbg(" GTP-U(type:0x%x, teid:%d) ", gtp1_hdr->type, ntohl(gtp1_hdr->teid)); 306 | 307 | // Check if gtp version is 1 308 | if (unlikely(gtp1_hdr->flags >> 5 != 1)) { 309 | printf(" NonGTPVer(gtp1_hdr->ver:%d)\n", gtp1_hdr->flags >> 5); 310 | port_pkt_stats[port].non_gtpVer += 1; 311 | goto out_flush; 312 | } 313 | 314 | // Check if msg type is PDU 315 | if (unlikely(gtp1_hdr->type != 0xff)) { 316 | printf(" DROP(gtp1_hdr->type:%d)\n", gtp1_hdr->type); 317 | port_pkt_stats[port].dropped += 1; 318 | goto out_flush; 319 | } 320 | 321 | // GTP decap 322 | if (likely(process_gtpv1(m, port, gtp1_hdr) > 0)) { 323 | return; 324 | } else { 325 | printf_dbg(" ERR(decap failed)\n"); 326 | port_pkt_stats[port].decap_err += 1; 327 | goto out_flush; 328 | } 329 | } else { 330 | port_pkt_stats[port].non_gtp += 1; 331 | } /* (unlikely(udp_hdr->src|dst_port != 2123)) */ 332 | } else { 333 | port_pkt_stats[port].non_udp += 1; 334 | } /* (unlikely(ip_hdr->next_proto_id != 0x11)) */ 335 | 336 | // GTP encap 337 | if (likely(process_ipv4(m, port, ip_hdr) > 0)) { 338 | return; 339 | } else { 340 | printf_dbg(" ERR(encap failed)\n"); 341 | port_pkt_stats[port].encap_err += 1; 342 | goto out_flush; 343 | } 344 | 345 | } else { 346 | port_pkt_stats[port].non_ipv4 += 1; 347 | 348 | // Ether type: ARP 349 | if (unlikely(eth_hdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP))) { 350 | arp_in(m); 351 | goto out_flush; 352 | } 353 | } /* (likely(eth_hdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))) */ 354 | 355 | out_flush: 356 | fflush(stdout); 357 | rte_pktmbuf_free(m); 358 | } 359 | -------------------------------------------------------------------------------- /src/stats.c: -------------------------------------------------------------------------------- 1 | #include "stats.h" 2 | 3 | /* GLOBAL */ 4 | pkt_stats_t port_pkt_stats[GTP_CFG_MAX_PORTS] = {0}; 5 | 6 | static struct rte_timer fetchStats; 7 | static struct rte_timer displayStats; 8 | 9 | uint8_t doStatsDisplay = 1; 10 | 11 | /* EXTERN */ 12 | extern app_confg_t app_config; 13 | extern numa_info_t numa_node_info[GTP_MAX_NUMANODE]; 14 | 15 | void 16 | sig_extra_stats(__attribute__((unused)) int signo) 17 | { 18 | int32_t i = 0, ports = rte_eth_dev_count_avail(); 19 | 20 | doStatsDisplay = 0; 21 | 22 | /* clear screen */ 23 | STATS_CLR_SCREEN; 24 | 25 | printf(YELLOW "\033[2;1H INTF " RESET); 26 | printf("\033[3;1H"); 27 | printf(BLUE "*************************************************" RESET); 28 | printf("\033[10;5H"); 29 | printf(YELLOW " ------------- TX PKT BUFF DETAILS --------" RESET); 30 | printf("\033[11;1H"); 31 | printf(" + Type:"); 32 | printf("\033[12;1H"); 33 | printf(" + Ver:"); 34 | printf("\033[13;1H"); 35 | printf(" + Index:"); 36 | printf("\033[18;1H"); 37 | printf(YELLOW " NUMA " RESET); 38 | printf("\033[19;1H"); 39 | printf(BLUE "*************************************************" RESET); 40 | printf("\033[20;1H"); 41 | printf(" + LCORE in Use: "); 42 | printf("\033[21;1H"); 43 | printf(" + INTF in Use: "); 44 | printf("\033[30;1H"); 45 | printf(BLUE "*************************************************" RESET); 46 | 47 | for (; i < ports; i++) { 48 | printf("\033[2;%dH", (15 + 10 * i)); 49 | printf(" %8u ", i); 50 | printf("\033[11;%dH", (15 + 10 * i)); 51 | printf(" %8u ", app_config.gtp_ports[i].gtp_type); 52 | printf("\033[13;%dH", (15 + 10 * i)); 53 | printf(" %8u ", app_config.gtp_ports[i].pkt_index); // not used 54 | } 55 | 56 | for (i = 0; i < GTP_MAX_NUMANODE; i++) { 57 | printf("\033[18;%dH", (15 + 10 * i)); 58 | printf(" %8u ", i); 59 | printf("\033[20;%dH", (15 + 10 * i)); 60 | printf(" %8u ", numa_node_info[i].lcoreUsed); 61 | printf("\033[21;%dH", (15 + 10 * i)); 62 | printf(" %8u ", numa_node_info[i].intfUsed); 63 | } 64 | 65 | fflush(stdout); 66 | rte_delay_ms(10000); 67 | 68 | show_static_display(); 69 | 70 | doStatsDisplay = 1; 71 | return; 72 | } 73 | 74 | void 75 | sig_config(__attribute__((unused)) int signo) 76 | { 77 | } 78 | 79 | void get_link_stats(__attribute__((unused)) struct rte_timer *t, 80 | __attribute__((unused)) void *arg) { 81 | int32_t i, ports = rte_eth_dev_count_avail(); 82 | static uint64_t rx_currStat[GTP_CFG_MAX_PORTS] = {0}; 83 | static uint64_t tx_currStat[GTP_CFG_MAX_PORTS] = {0}; 84 | // static uint64_t rx_prevStat[GTP_CFG_MAX_PORTS] = {0}; 85 | // static uint64_t tx_prevStat[GTP_CFG_MAX_PORTS] = {0}; 86 | 87 | /* get link status for DPDK ports */ 88 | struct rte_eth_stats stats; 89 | 90 | for (i = 0; i < ports; i++) { 91 | /* ToDo: use numa info to identify the ports */ 92 | if (likely(rte_eth_stats_get(i, &stats) == 0)) { 93 | rx_currStat[i] = stats.ipackets; 94 | tx_currStat[i] = stats.opackets; 95 | 96 | // port_pkt_stats[i].rxPkts = (rx_currStat[i] - rx_prevStat[i]); 97 | // port_pkt_stats[i].txPkts = (tx_currStat[i] - tx_prevStat[i]); 98 | port_pkt_stats[i].rxPkts = rx_currStat[i]; 99 | port_pkt_stats[i].txPkts = tx_currStat[i]; 100 | 101 | // rx_prevStat[i] = stats.ipackets; 102 | // tx_prevStat[i] = stats.opackets; 103 | 104 | port_pkt_stats[i].rxBytes = stats.ibytes / (1024 * 1024); 105 | port_pkt_stats[i].txBytes = stats.obytes / (1024 * 1024); 106 | port_pkt_stats[i].rxMissed = stats.imissed; 107 | port_pkt_stats[i].rxErr = stats.ierrors; 108 | port_pkt_stats[i].txErr = stats.oerrors; 109 | port_pkt_stats[i].rxNoMbuff = stats.rx_nombuf; 110 | } 111 | } 112 | 113 | return; 114 | } 115 | 116 | void get_process_stats(__attribute__((unused)) struct rte_timer *t, 117 | __attribute__((unused)) void *arg) { 118 | int32_t i, ports = rte_eth_dev_count_avail(); 119 | 120 | if (likely(doStatsDisplay)) { 121 | for (i = 0; i < ports; i++) { 122 | /* Display calculated stats */ 123 | 124 | /*NUMA_SOCKET*/ 125 | printf("\033[4;%dH", (15 + 10 * i)); 126 | //printf("%-8d |", ); 127 | 128 | /*PKTS_PER_SEC_RX*/ 129 | printf("\033[5;%dH", (15 + 10 * i)); 130 | printf(" %-12lu ", port_pkt_stats[i].rxPkts); 131 | 132 | /*PKTS_PER_SEC_TX*/ 133 | printf("\033[6;%dH", (15 + 10 * i)); 134 | printf(" %-12lu ", port_pkt_stats[i].txPkts); 135 | 136 | /*MB_RX*/ 137 | printf("\033[7;%dH", (15 + 10 * i)); 138 | printf(" %-12lu ", port_pkt_stats[i].rxBytes); 139 | 140 | /*MB_TX*/ 141 | printf("\033[8;%dH", (15 + 10 * i)); 142 | printf(" %-12lu ", port_pkt_stats[i].txBytes); 143 | 144 | /* INTF STATS */ 145 | /* Drop */ 146 | printf("\033[11;%dH", (15 + 10 * i)); 147 | printf(" %-12lu ", port_pkt_stats[i].dropped); 148 | 149 | /* RX miss */ 150 | printf("\033[12;%dH", (15 + 10 * i)); 151 | printf(" %-12lu ", port_pkt_stats[i].rxMissed); 152 | 153 | /* RX err */ 154 | printf("\033[13;%dH", (15 + 10 * i)); 155 | printf(" %-12lu ", port_pkt_stats[i].rxErr); 156 | 157 | /* RX no mbuf */ 158 | printf("\033[14;%dH", (15 + 10 * i)); 159 | printf(" %-12lu ", port_pkt_stats[i].rxNoMbuff); 160 | 161 | /* TX err */ 162 | printf("\033[15;%dH", (15 + 10 * i)); 163 | printf(" %-12lu ", port_pkt_stats[i].txErr); 164 | 165 | /*GTPU_RX_IPV4*/ 166 | printf("\033[18;%dH", (15 + 10 * i)); 167 | printf(" %-12lu ", port_pkt_stats[i].rx_gptu_ipv4); 168 | 169 | /*GTPU_RX_IPV6*/ 170 | printf("\033[19;%dH", (15 + 10 * i)); 171 | printf(" %-12lu ", port_pkt_stats[i].rx_gptu_ipv6); 172 | 173 | /*ERR NON IPV4*/ 174 | printf("\033[22;%dH", (15 + 10 * i)); 175 | printf(" %-12lu ", port_pkt_stats[i].non_ipv4); 176 | 177 | /*ERR NON UDP*/ 178 | printf("\033[23;%dH", (15 + 10 * i)); 179 | printf(" %-12lu ", port_pkt_stats[i].non_udp); 180 | 181 | /*ERR NON GTP*/ 182 | printf("\033[24;%dH", (15 + 10 * i)); 183 | printf(" %-12lu ", port_pkt_stats[i].non_gtp); 184 | 185 | /*ERR GTP ver*/ 186 | printf("\033[25;%dH", (15 + 10 * i)); 187 | printf(" %-12lu ", port_pkt_stats[i].non_gtpVer); 188 | 189 | /*ERR IP FRAG*/ 190 | printf("\033[26;%dH", (15 + 10 * i)); 191 | printf(" %-12lu ", port_pkt_stats[i].ipFrag); 192 | 193 | /*ERR IP CSUM*/ 194 | printf("\033[27;%dH", (15 + 10 * i)); 195 | printf(" %-12lu ", port_pkt_stats[i].ipCsumErr); 196 | 197 | /*ERR UDP CSUM*/ 198 | printf("\033[28;%dH", (15 + 10 * i)); 199 | printf(" %-12lu ", port_pkt_stats[i].udpCsumErr); 200 | 201 | /*TX GTPU*/ 202 | printf("\033[31;%dH", (15 + 10 * i)); 203 | printf(" %-12lu ", port_pkt_stats[i].tx_gptu); 204 | 205 | /*ENCAP ERR*/ 206 | printf("\033[32;%dH", (15 + 10 * i)); 207 | printf(" %-12lu ", port_pkt_stats[i].encap_err); 208 | 209 | /*DECAP ERR*/ 210 | printf("\033[33;%dH", (15 + 10 * i)); 211 | printf(" %-12lu ", port_pkt_stats[i].decap_err); 212 | } 213 | } 214 | 215 | fflush(stdout); 216 | return; 217 | } 218 | 219 | void 220 | show_static_display(void) 221 | { 222 | struct rte_eth_link link; 223 | int32_t i, ports = rte_eth_dev_count_avail(); 224 | 225 | /* clear screen */ 226 | STATS_CLR_SCREEN; 227 | 228 | /* stats header */ 229 | printf("\033[2;1H"); 230 | printf(" %-10s | ", "Cat|Intf"); 231 | printf("\033[3;1H"); 232 | printf(BLUE "======================================================" RESET); 233 | 234 | /*NUMA_SOCKET*/ 235 | /*LINK_SPEED_STATE*/ 236 | printf("\033[4;1H"); 237 | printf(BLUE " %-10s | ", "Speed-Dup"); 238 | 239 | /*PKTS_PER_SEC_RX*/ 240 | printf("\033[5;1H"); 241 | printf(BLUE " %-10s | ", "RX pkts/s"); 242 | 243 | /*PKTS_PER_SEC_TX*/ 244 | printf("\033[6;1H"); 245 | printf(BLUE " %-10s | ", "TX pkts/s"); 246 | 247 | /*MB_RX*/ 248 | printf("\033[7;1H"); 249 | printf(BLUE " %-10s | ", "RX MB"); 250 | 251 | /*MB_TX*/ 252 | printf("\033[8;1H"); 253 | printf(BLUE " %-10s | " RESET, "TX MB"); 254 | 255 | /*PKT_INFO*/ 256 | printf("\033[10;1H"); 257 | printf(CYAN " %-25s " RESET, "---------- INTF STATS ----------"); 258 | 259 | /* Dropped */ 260 | printf("\033[11;1H"); 261 | printf(RED " %-10s | ", "DROP"); 262 | 263 | /* RX miss*/ 264 | printf("\033[12;1H"); 265 | printf(RED " %-10s | ", "RX MISS"); 266 | 267 | /* RX Err */ 268 | printf("\033[13;1H"); 269 | printf(RED " %-10s | ", "RX ERR"); 270 | 271 | /* RX no Mbuf */ 272 | printf("\033[14;1H"); 273 | printf(RED " %-10s | " RESET, "RX no MBUF"); 274 | 275 | /* TX Err */ 276 | printf("\033[15;1H"); 277 | printf(RED " %-10s | " RESET, "TX ERR"); 278 | 279 | printf("\033[17;1H"); 280 | printf(CYAN " %-25s " RESET, "------- GTP PKT STATS -------"); 281 | 282 | /*GTPU_RX_IPV4*/ 283 | printf("\033[18;1H"); 284 | printf(YELLOW " %-10s | ", "RX V1U-4"); 285 | 286 | /*GTPU_RX_IPV6*/ 287 | printf("\033[19;1H"); 288 | printf(YELLOW " %-10s | " RESET, "RX V1U-6"); 289 | 290 | printf("\033[21;1H"); 291 | printf(CYAN " %-25s " RESET, "-------- PKT ERR STATS --------"); 292 | 293 | /*NON IPv4*/ 294 | printf("\033[22;1H"); 295 | printf(MAGENTA " %-10s | ", "NON IPv4"); 296 | 297 | /*NON UDP*/ 298 | printf("\033[23;1H"); 299 | printf(MAGENTA " %-10s | ", "NON UDP"); 300 | 301 | /*NON GTP*/ 302 | printf("\033[24;1H"); 303 | printf(MAGENTA " %-10s | ", "NON GTP"); 304 | 305 | /*NON GTP VER*/ 306 | printf("\033[25;1H"); 307 | printf(MAGENTA " %-10s | ", "GTP E_VER"); 308 | 309 | /*IP FRAG*/ 310 | printf("\033[26;1H"); 311 | printf(MAGENTA " %-10s | ", "IP FRAG"); 312 | 313 | /*IP CHECKSUM*/ 314 | printf("\033[27;1H"); 315 | printf(MAGENTA " %-10s | ", "IP CSUM"); 316 | 317 | /*UDP CHECKSUM*/ 318 | printf("\033[28;1H"); 319 | printf(MAGENTA " %-10s | " RESET, "UDP CSUM"); 320 | 321 | printf("\033[30;1H"); 322 | printf(CYAN " %-25s " RESET, "-------- GTP PROC STATS --------"); 323 | 324 | /*TX GTPU*/ 325 | printf("\033[31;1H"); 326 | printf(BOLDRED " %-10s | ", "TX GTPU"); 327 | 328 | /*ENCAP ERR*/ 329 | printf("\033[32;1H"); 330 | printf(BOLDRED " %-10s | ", "ENCAP ERR"); 331 | 332 | /*DECAP ERR*/ 333 | printf("\033[33;1H"); 334 | printf(BOLDRED " %-10s | ", "DECAP ERR"); 335 | 336 | /* fetch port info and display */ 337 | for (i = 0; i < ports; i++) { 338 | rte_eth_link_get_nowait(i, &link); 339 | 340 | /* DPDK port id - up|down */ 341 | printf("\033[2;%dH", (15 + 10 * i)); 342 | if (link.link_status) 343 | printf(" %d-" GREEN "up" RESET, i); 344 | else 345 | printf(" %d-" RED "down" RESET, i); 346 | 347 | /*LINK_SPEED_STATE*/ 348 | printf("\033[4;%dH", (15 + 10 * i)); 349 | printf(" %5d-%-2s ", 350 | ((link.link_speed == ETH_SPEED_NUM_10M) ? 10 : 351 | (link.link_speed == ETH_SPEED_NUM_100M) ? 100 : 352 | (link.link_speed == ETH_SPEED_NUM_1G) ? 1000 : 353 | (link.link_speed == ETH_SPEED_NUM_10G) ? 10000 : 0), 354 | ((link.link_duplex == ETH_LINK_HALF_DUPLEX) ? "HD" : "FD")); 355 | } 356 | 357 | fflush(stdout); 358 | return; 359 | } 360 | 361 | void 362 | set_stats_timer(void) 363 | { 364 | int32_t lcoreId = rte_get_master_lcore(); 365 | 366 | rte_timer_subsystem_init(); 367 | 368 | /* initialize the stats fetch and display timers */ 369 | rte_timer_init(&fetchStats); 370 | rte_timer_init(&displayStats); 371 | 372 | /* periodic reload for every `period` sec for stats fetch and display */ 373 | uint64_t hz = rte_get_timer_hz(); 374 | double period = 0.5; 375 | rte_timer_reset(&fetchStats, hz * period, PERIODICAL, lcoreId, get_link_stats, NULL); 376 | rte_timer_reset(&displayStats, hz * period, PERIODICAL, lcoreId, get_process_stats, NULL); 377 | 378 | return; 379 | } 380 | -------------------------------------------------------------------------------- /src/netstack/arp.c: -------------------------------------------------------------------------------- 1 | #include "arp.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "pktbuf.h" 16 | #include "ether.h" 17 | 18 | /* EXTERN */ 19 | extern interface_t *iface_list; 20 | extern interface_t *port_iface_map[MAX_INTERFACES]; 21 | 22 | /* GLOBALS */ 23 | static const char *arp_state_str[] = {"FREE", "PENDING", "RESOLVED", "PERMANENT"}; 24 | static struct rte_hash *arp_table = NULL; // [uint32_t ipv4_addr] = (arp_entry_t *arp_entry) 25 | static struct rte_hash *arp_pkt_egqs; // pkt egress queues: [uint32_t ipv4_addr] = (struct rte_ring *pkt_egq) 26 | 27 | static __rte_always_inline int arp_send_reply_inplace(struct rte_mbuf *m, uint32_t src_ip_addr, struct arp *arp_hdr); 28 | static __rte_always_inline int arp_send(struct rte_mbuf *mbuf, uint8_t port); 29 | static __rte_always_inline int arp_add(uint32_t ipv4_addr, unsigned char *mac_addr, arp_state_t state); 30 | static __rte_always_inline int arp_update(uint32_t ipv4_addr, unsigned char *mac_addr, arp_state_t prev_state, arp_state_t new_state); 31 | 32 | int 33 | arp_init(int with_locks) 34 | { 35 | // Create arp table 36 | struct rte_hash_parameters params = {0}; 37 | 38 | params.name = "arp_table"; 39 | params.entries = MAX_ARP_ENTRIES; 40 | params.key_len = sizeof(uint32_t); 41 | params.hash_func = rte_jhash; 42 | params.hash_func_init_val = 0; 43 | params.socket_id = rte_socket_id(); 44 | params.extra_flag = with_locks ? 45 | RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT | 46 | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY : 0; 47 | 48 | assert(rte_hash_find_existing(params.name) == NULL); 49 | arp_table = rte_hash_create(¶ms); 50 | if ((intptr_t)arp_table <= 0) return -1; 51 | 52 | // Create arp_pkt_egqs 53 | params.name = "arp_pkt_egqs"; 54 | params.entries = MAX_EGRESS_Q_IP_ENTRIES; 55 | params.key_len = sizeof(uint32_t); 56 | params.hash_func = rte_jhash; 57 | params.hash_func_init_val = 0; 58 | params.socket_id = rte_socket_id(); 59 | params.extra_flag = with_locks ? 60 | RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT | 61 | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY : 0; 62 | 63 | assert(rte_hash_find_existing(params.name) == NULL); 64 | arp_pkt_egqs = rte_hash_create(¶ms); 65 | if ((intptr_t)arp_pkt_egqs <= 0) return -1; 66 | 67 | return 0; 68 | } 69 | 70 | int 71 | arp_terminate(void) 72 | { 73 | uint32_t *ipv4_addr, iter; 74 | arp_entry_t *arp_entry; 75 | struct rte_ring *pkt_egq; 76 | 77 | // Free egress pkt queue 78 | iter = 0; 79 | while (rte_hash_iterate(arp_pkt_egqs, (void *)&ipv4_addr, (void **)&pkt_egq, &iter) >= 0) { 80 | rte_ring_free(pkt_egq); 81 | } 82 | 83 | // Free the arp table 84 | iter = 0; 85 | while (rte_hash_iterate(arp_table, (void *)&ipv4_addr, (void **)&arp_entry, &iter) >= 0) { 86 | free(arp_entry); 87 | } 88 | 89 | rte_hash_free(arp_table); 90 | logger(LOG_ARP, L_INFO, "ARP table freed.\n"); 91 | return 0; 92 | } 93 | 94 | int 95 | arp_in(struct rte_mbuf *mbuf) 96 | { 97 | int ret; 98 | assert(mbuf->buf_len >= sizeof(struct arp)); 99 | assert(rte_pktmbuf_data_len(mbuf) >= (sizeof(struct arp) + sizeof(struct rte_ether_hdr))); 100 | 101 | struct arp *arp_hdr = 102 | (struct arp *)(rte_pktmbuf_mtod(mbuf, unsigned char *) + sizeof(struct rte_ether_hdr)); 103 | uint32_t ip_addr_from = int_addr_from_char(arp_hdr->src_pr_add, 1); 104 | 105 | switch (ntohs(arp_hdr->opcode)) { 106 | case ARP_REQ: { 107 | uint32_t ip_addr_to = int_addr_from_char(arp_hdr->dst_pr_add, 1); 108 | 109 | logger_s(LOG_ARP, L_INFO, "\n"); 110 | logger(LOG_ARP, L_INFO, "[ARP Request] Who has "); 111 | print_ipv4(ip_addr_to, L_DEBUG); 112 | logger_s(LOG_ARP, L_INFO, " Tell "); 113 | print_ipv4(ip_addr_from, L_DEBUG); 114 | logger_s(LOG_ARP, L_DEBUG, "\n"); 115 | 116 | // arp_send_reply(ip_addr_to, arp_hdr->src_hw_add, arp_hdr->src_pr_add); 117 | ret = arp_send_reply_inplace(mbuf, ip_addr_to, arp_hdr); 118 | if (unlikely(ret != 1)) { 119 | rte_pktmbuf_free(mbuf); 120 | } 121 | 122 | arp_add_mac(ip_addr_from, arp_hdr->src_hw_add, 0); 123 | 124 | arp_print_table(L_DEBUG); 125 | logger_s(LOG_ARP, L_DEBUG, "\n"); 126 | break; 127 | } 128 | case ARP_REPLY: { 129 | logger_s(LOG_ARP, L_INFO, "\n"); 130 | logger(LOG_ARP, L_INFO, "[ARP Reply] "); 131 | print_ipv4(ip_addr_from, L_DEBUG); 132 | logger_s(LOG_ARP, L_INFO, " is at "); 133 | print_mac(arp_hdr->src_hw_add, L_DEBUG); 134 | logger_s(LOG_ARP, L_DEBUG, "\n"); 135 | 136 | // Check if dst mac is hosted 137 | interface_t *iface = iface_list; 138 | while (iface && memcmp(&arp_hdr->dst_hw_add, &iface->hw_addr, RTE_ETHER_ADDR_LEN)) { 139 | iface = iface->next; 140 | } 141 | 142 | if (unlikely(iface == NULL)) { 143 | logger(LOG_ARP, L_INFO, "ARP reply ignored, mac not hosted\n"); 144 | rte_pktmbuf_free(mbuf); 145 | return -1; 146 | } 147 | 148 | ret = arp_update(ip_addr_from, arp_hdr->src_hw_add, ARP_STATE_INCOMPLETE, ARP_STATE_REACHABLE); 149 | assert(ret == 0); 150 | 151 | arp_print_table(L_DEBUG); 152 | logger_s(LOG_ARP, L_DEBUG, "\n"); 153 | rte_pktmbuf_free(mbuf); 154 | break; 155 | } 156 | default: { 157 | assert(0); 158 | } 159 | } 160 | 161 | fflush(stdout); 162 | return 0; 163 | } 164 | 165 | int 166 | arp_send_request(uint32_t dst_ip_addr, uint8_t port) 167 | { 168 | unsigned char dest_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 169 | interface_t *iface = port_iface_map[port]; 170 | 171 | if (unlikely(iface == NULL)) { 172 | logger(LOG_ARP, L_CRITICAL, 173 | "ARP request failed, port(%d) not in interface list\n", 174 | port); 175 | return 0; 176 | } 177 | 178 | logger_s(LOG_ARP, L_DEBUG, "\n"); 179 | logger(LOG_ARP, L_DEBUG, " Who has "); 180 | print_ipv4(dst_ip_addr, L_DEBUG); 181 | logger_s(LOG_ARP, L_INFO, " Tell "); 182 | print_ipv4(iface->ipv4_addr, L_DEBUG); 183 | 184 | struct rte_mbuf *mbuf = get_mbuf(); 185 | assert(likely(mbuf != NULL)); 186 | 187 | struct arp *arp_req = (struct arp *)rte_pktmbuf_prepend(mbuf, sizeof(struct arp)); 188 | arp_req->hw_type = htons(HW_TYPE_ETHERNET); 189 | arp_req->pr_type = htons(SW_TYPE_IPV4); 190 | arp_req->hw_len = RTE_ETHER_ADDR_LEN; 191 | arp_req->pr_len = PR_LEN_IPV4; 192 | arp_req->opcode = htons(1); 193 | rte_memcpy(arp_req->src_hw_add, iface->hw_addr, RTE_ETHER_ADDR_LEN); 194 | rte_memcpy(arp_req->dst_hw_add, dest_mac, RTE_ETHER_ADDR_LEN); 195 | rte_memcpy(arp_req->src_pr_add, &iface->ipv4_addr, PR_LEN_IPV4); 196 | rte_memcpy(arp_req->dst_pr_add, &dst_ip_addr, PR_LEN_IPV4); 197 | 198 | int ret = arp_send(mbuf, iface->port); 199 | if (ret == 0) { 200 | ret = arp_add(dst_ip_addr, NULL, ARP_STATE_INCOMPLETE); 201 | return ret == 0 ? 0 : -1; 202 | } else { 203 | rte_pktmbuf_free(mbuf); 204 | return -1; 205 | } 206 | } 207 | 208 | // Faster 209 | static __rte_always_inline int 210 | arp_send_reply_inplace(struct rte_mbuf *m, uint32_t src_ip_addr, struct arp *arp_hdr) 211 | { 212 | interface_t *iface = iface_list; 213 | while (iface && src_ip_addr != iface->ipv4_addr) { 214 | iface = iface->next; 215 | } 216 | 217 | if (unlikely(iface == NULL)) { 218 | logger(LOG_ARP, L_INFO, "ARP request failed, address not hosted\n"); 219 | return -1; 220 | } 221 | 222 | struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); 223 | 224 | // Switch src and dst data and set bonding MAC 225 | rte_ether_addr_copy(ð_hdr->s_addr, ð_hdr->d_addr); 226 | rte_ether_addr_copy((struct rte_ether_addr *)&iface->hw_addr, ð_hdr->s_addr); 227 | 228 | arp_hdr->opcode = rte_cpu_to_be_16(RTE_ARP_OP_REPLY); 229 | rte_memcpy(arp_hdr->dst_hw_add, arp_hdr->src_hw_add, RTE_ETHER_ADDR_LEN); 230 | rte_memcpy(arp_hdr->src_hw_add, iface->hw_addr, RTE_ETHER_ADDR_LEN); 231 | rte_memcpy(arp_hdr->src_pr_add, &src_ip_addr, PR_LEN_IPV4); 232 | rte_memcpy(arp_hdr->dst_pr_add, arp_hdr->src_pr_add, PR_LEN_IPV4); 233 | 234 | logger(LOG_ARP, L_DEBUG, " "); 235 | print_ipv4(src_ip_addr, L_DEBUG); 236 | logger_s(LOG_ARP, L_DEBUG, " is at "); 237 | print_mac(iface->hw_addr, L_DEBUG); 238 | 239 | // TODO: fix the below, port should be dfrom routing 240 | logger_s(LOG_ARP, L_DEBUG, " [TX#%d]", iface->port); 241 | const int queue_id = 0; 242 | const int ret = rte_eth_tx_burst(iface->port, queue_id, &m, 1); 243 | if (unlikely(ret != 1)) { 244 | logger_s(LOG_ARP, L_CRITICAL, " ERR(rte_eth_tx_burst=%d)\n", ret); 245 | return -1; 246 | } else { 247 | logger_s(LOG_ARP, L_DEBUG, "\n"); 248 | return 0; 249 | } 250 | } 251 | 252 | int 253 | arp_send_reply(uint32_t src_ip_addr, unsigned char *dst_hw_addr, 254 | unsigned char *dst_pr_add) 255 | { 256 | interface_t *iface = iface_list; 257 | while (iface && src_ip_addr != iface->ipv4_addr) { 258 | iface = iface->next; 259 | } 260 | 261 | if (unlikely(iface == NULL)) { 262 | logger(LOG_ARP, L_INFO, "ARP request failed, address not hosted\n"); 263 | return -1; 264 | } 265 | 266 | struct rte_mbuf *mbuf = get_mbuf(); 267 | assert(likely(mbuf != NULL)); 268 | 269 | struct arp *arp_reply = (struct arp *)rte_pktmbuf_prepend(mbuf, sizeof(struct arp)); 270 | arp_reply->hw_type = htons(HW_TYPE_ETHERNET); 271 | arp_reply->pr_type = htons(SW_TYPE_IPV4); 272 | arp_reply->hw_len = RTE_ETHER_ADDR_LEN; 273 | arp_reply->pr_len = PR_LEN_IPV4; 274 | arp_reply->opcode = htons(2); 275 | rte_memcpy(arp_reply->src_hw_add, iface->hw_addr, RTE_ETHER_ADDR_LEN); 276 | rte_memcpy(arp_reply->dst_hw_add, dst_hw_addr, RTE_ETHER_ADDR_LEN); 277 | rte_memcpy(arp_reply->src_pr_add, &src_ip_addr, PR_LEN_IPV4); 278 | rte_memcpy(arp_reply->dst_pr_add, dst_pr_add, PR_LEN_IPV4); 279 | 280 | logger(LOG_ARP, L_DEBUG, " "); 281 | print_ipv4(src_ip_addr, L_DEBUG); 282 | logger_s(LOG_ARP, L_DEBUG, " is at "); 283 | print_mac(iface->hw_addr, L_DEBUG); 284 | 285 | int ret = arp_send(mbuf, iface->port); 286 | if (likely(ret == 0)) { 287 | return 0; 288 | } else { 289 | rte_pktmbuf_free(mbuf); 290 | return -1; 291 | } 292 | } 293 | 294 | static __rte_always_inline int 295 | arp_send(struct rte_mbuf *mbuf, uint8_t port) 296 | { 297 | int i; 298 | struct arp *arp_hdr = (struct arp *)rte_pktmbuf_mtod(mbuf, struct arp *); 299 | struct rte_ether_hdr *eth = 300 | (struct rte_ether_hdr *)rte_pktmbuf_prepend(mbuf, sizeof(struct rte_ether_hdr)); 301 | 302 | eth->ether_type = htons(RTE_ETHER_TYPE_ARP); 303 | 304 | if (arp_hdr->opcode == ntohs(ARP_REQ)) { 305 | rte_memcpy(ð->s_addr.addr_bytes[0], arp_hdr->src_hw_add, sizeof(arp_hdr->src_hw_add)); 306 | for (i = 0; i < 6; i++) { 307 | eth->d_addr.addr_bytes[i] = 0xff; 308 | } 309 | } else if (arp_hdr->opcode == ntohs(ARP_REPLY)) { 310 | rte_memcpy(ð->s_addr.addr_bytes[0], arp_hdr->src_hw_add, sizeof(arp_hdr->src_hw_add)); 311 | rte_memcpy(ð->d_addr.addr_bytes[0], arp_hdr->dst_hw_add, sizeof(arp_hdr->dst_hw_add)); 312 | } else { 313 | logger(LOG_ARP, L_CRITICAL, "Invalid opcode %d", arp_hdr->opcode); 314 | return -1; 315 | } 316 | 317 | // TODO: fix the below, port should be dfrom routing 318 | logger_s(LOG_ARP, L_DEBUG, " [TX#%d]", port); 319 | const int queue_id = 0; 320 | const int ret = rte_eth_tx_burst(port, queue_id, &mbuf, 1); 321 | if (unlikely(ret != 1)) { 322 | logger_s(LOG_ARP, L_CRITICAL, " ERR(rte_eth_tx_burst=%d)\n", ret); 323 | return -1; 324 | } else { 325 | logger_s(LOG_ARP, L_DEBUG, "\n"); 326 | return 0; 327 | } 328 | } 329 | 330 | /** 331 | * 332 | * 333 | * Sample code: 334 | * uint32_t ip = (192 << 24 | 168 << 16 | 0 << 1 | 2); // 192.168.0.1 335 | * unsigned char mac[6] = {0x3c, 0xfd, 0xfe, 0x7a, 0x6c, 0x29}; // 3c:fd:fe:7a:6c:29 336 | * arp_add_mac(htonl(ip), mac); 337 | */ 338 | int 339 | arp_get_mac(uint32_t ipv4_addr, unsigned char *mac_addr) 340 | { 341 | // printf("Getting mac for "); 342 | // print_ipv4(ipv4_addr, L_ALL); 343 | 344 | arp_entry_t *arp_entry; 345 | int ret = rte_hash_lookup_data(arp_table, (const void *)&ipv4_addr, (void **)&arp_entry); 346 | 347 | // REACHABLE or PERMANENT 348 | if (likely(ret >= 0 && arp_entry->state >= ARP_STATE_REACHABLE)) { 349 | rte_memcpy(mac_addr, arp_entry->mac_addr, RTE_ETHER_ADDR_LEN); 350 | // printf(": mac found "); 351 | // print_mac(mac_addr, L_ALL); 352 | // printf("\n"); 353 | return 1; 354 | } else { 355 | // printf(": no mac found%d\n", ret); 356 | return 0; 357 | } 358 | } 359 | 360 | int 361 | arp_add_mac(uint32_t ipv4_addr, unsigned char *mac_addr, int permanent) 362 | { 363 | logger(LOG_ARP, L_INFO, "Adding to arp table: IP "); 364 | print_ipv4(ipv4_addr, L_INFO); 365 | logger_s(LOG_ARP, L_INFO, " MAC "); 366 | print_mac(mac_addr, L_INFO); 367 | logger_s(LOG_ARP, L_INFO, "\n"); 368 | 369 | return arp_add(ipv4_addr, mac_addr, permanent ? ARP_STATE_PERMANENT : ARP_STATE_REACHABLE); 370 | } 371 | 372 | static __rte_always_inline int 373 | arp_update(uint32_t ipv4_addr, unsigned char *mac_addr, 374 | arp_state_t prev_state, arp_state_t new_state) 375 | { 376 | arp_entry_t *arp_entry; 377 | int ret = rte_hash_lookup_data(arp_table, (const void *)&ipv4_addr, (void **)&arp_entry); 378 | 379 | if (ret >= 0 && (arp_entry->state == prev_state || prev_state == ARP_STATE_ANY)) { 380 | rte_memcpy(mac_addr, arp_entry->mac_addr, RTE_ETHER_ADDR_LEN); 381 | arp_entry->state = new_state; 382 | return 0; 383 | } else { 384 | return -1; 385 | } 386 | } 387 | 388 | static __rte_always_inline int 389 | arp_add(uint32_t ipv4_addr, unsigned char *mac_addr, arp_state_t state) 390 | { 391 | arp_entry_t *arp_entry = malloc(sizeof(arp_entry_t)); 392 | arp_entry->state = state; 393 | arp_entry->ipv4_addr = ipv4_addr; 394 | if (mac_addr) { 395 | rte_memcpy(arp_entry->mac_addr, mac_addr, RTE_ETHER_ADDR_LEN); 396 | } 397 | 398 | int ret = rte_hash_add_key_data(arp_table, (const void *)&ipv4_addr, (void *)arp_entry); 399 | return ret == 0 ? 0 : -1; 400 | } 401 | 402 | int 403 | arp_queue_egress_pkt(uint32_t ipv4_addr, struct rte_mbuf *m) 404 | { 405 | // TODO 406 | return 0; 407 | } 408 | 409 | void 410 | arp_print_table(TraceLevel trace_level) 411 | { 412 | uint32_t *ipv4_addr, iter = 0; 413 | arp_entry_t *arp_entry; 414 | 415 | logger(LOG_ARP, trace_level, "{ARP Table}\n"); 416 | logger(LOG_ARP, trace_level, "There are %d entries in total:", rte_hash_count(arp_table)); 417 | logger_s(LOG_ARP, trace_level, "\n"); 418 | 419 | while (rte_hash_iterate(arp_table, (void *)&ipv4_addr, (void **)&arp_entry, &iter) >= 0) { 420 | logger(LOG_ARP, trace_level, " - IP = "); 421 | print_ipv4(*ipv4_addr, trace_level); // arp_entry->ipv4_addr 422 | logger_s(LOG_ARP, trace_level, "\n"); 423 | 424 | logger(LOG_ARP, trace_level, " MAC = "); 425 | print_mac(arp_entry->mac_addr, trace_level); 426 | logger_s(LOG_ARP, trace_level, "\n"); 427 | 428 | logger(LOG_ARP, trace_level, " STATE = "); 429 | logger(LOG_ARP, trace_level, "%s", arp_state_str[arp_entry->state]); 430 | logger_s(LOG_ARP, trace_level, "\n"); 431 | } 432 | } 433 | 434 | void 435 | print_ipv4(uint32_t ip_addr, TraceLevel trace_level) 436 | { 437 | int i; 438 | uint8_t ip; 439 | ip_addr = htonl(ip_addr); 440 | 441 | for (i = 0; i < 4; i++) { 442 | ip = ip_addr >> 24; 443 | ip_addr = ip_addr << 8; 444 | logger_s(LOG_ARP, trace_level, "%u", ip); 445 | if (i != 3) { 446 | logger_s(LOG_ARP, trace_level, "."); 447 | } 448 | } 449 | } 450 | 451 | void 452 | print_mac(unsigned char *mac_addr, TraceLevel trace_level) 453 | { 454 | int i; 455 | for (i = 0; i < RTE_ETHER_ADDR_LEN - 1; i++) { 456 | logger_s(LOG_ARP, trace_level, "%x:", mac_addr[i]); 457 | } 458 | logger_s(LOG_ARP, trace_level, "%x", mac_addr[i]); 459 | } 460 | --------------------------------------------------------------------------------