Changed Interface between NAS and GW to avoid the GW start function being called twice in IPv4v6 mode.

This commit is contained in:
Pedro Alvarez 2018-10-19 16:14:02 +01:00 committed by Andre Puschmann
parent 15cd8fc3b6
commit 5c29dba741
5 changed files with 175 additions and 174 deletions

View File

@ -36,6 +36,7 @@
#include <string>
#include "srslte/asn1/liblte_rrc.h"
#include "srslte/asn1/liblte_mme.h"
#include "srslte/common/interfaces_common.h"
#include "srslte/common/common.h"
#include "srslte/common/security.h"
@ -104,8 +105,7 @@ public:
class gw_interface_nas
{
public:
virtual srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str) = 0;
virtual srslte::error_t setup_if_addr6(uint8_t *ipv6_if_id, char *err_str) = 0;
virtual srslte::error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_id, char *err_str) = 0;
};
// GW interface for RRC

View File

@ -58,8 +58,7 @@ public:
void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu);
// NAS interface
srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str);
srslte::error_t setup_if_addr6(uint8_t *ipv6_if_id, char *err_str);
srslte::error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_addr, char *err_str);
// RRC interface
void add_mch_port(uint32_t lcid, uint32_t port);
@ -96,6 +95,8 @@ private:
void run_thread();
srslte::error_t init_if(char *err_str);
srslte::error_t setup_if_addr4(uint32_t ip_addr, char *err_str);
srslte::error_t setup_if_addr6(uint8_t *ipv6_if_id, char *err_str);
bool find_ipv6_addr(struct in6_addr *in6_out);
void del_ipv6_addr(struct in6_addr *in6p);

View File

@ -181,168 +181,27 @@ void gw::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu)
/*******************************************************************************
NAS interface
*******************************************************************************/
srslte::error_t gw::setup_if_addr(uint32_t ip_addr, char *err_str)
srslte::error_t gw::setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_addr, char *err_str)
{
if (ip_addr != current_ip_addr) {
if(!if_up)
{
if(init_if(err_str))
{
gw_log->error("init_if failed\n");
return(srslte::ERROR_CANT_START);
}
srslte::error_t err;
if(pdn_type == LIBLTE_MME_PDN_TYPE_IPV4 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6 ){
err = setup_if_addr4(ip_addr, err_str);
if(err!= srslte::ERROR_NONE){
return err;
}
// Setup the IP address
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip_addr);
if(0 > ioctl(sock, SIOCSIFADDR, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket address: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
ifr.ifr_netmask.sa_family = AF_INET;
const char *mask = "255.255.255.0";
if (!default_netmask) {
mask = netmask.c_str();
}
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(mask);
if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket netmask: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
current_ip_addr = ip_addr;
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
}
return(srslte::ERROR_NONE);
if(pdn_type == LIBLTE_MME_PDN_TYPE_IPV6 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6 ){
err = setup_if_addr6(ipv6_if_addr, err_str);
if(err!= srslte::ERROR_NONE){
return err;
}
}
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
return srslte::ERROR_NONE;
}
srslte::error_t gw::setup_if_addr6(uint8_t *ipv6_if_id, char *err_str)
{
struct sockaddr_in6 sai;
struct in6_ifreq ifr6;
bool match = true;
for (int i=0; i<8; i++){
if(ipv6_if_id[i] != current_if_id[i]){
match = false;
break;
}
}
if (!match) {
if (!if_up) {
if( init_if(err_str) ) {
gw_log->error("init_if failed\n");
return(srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET6, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET6;
if(inet_pton(AF_INET6, "fe80::", (void *)&sai.sin6_addr) <= 0) {
gw_log->error("Bad address\n");
return srslte::ERROR_CANT_START;
}
memcpy(&sai.sin6_addr.s6_addr[8], ipv6_if_id, 8);
if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) {
perror("SIOGIFINDEX");
return srslte::ERROR_CANT_START;
}
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
ifr6.ifr6_prefixlen = 64;
memcpy((char *) &ifr6.ifr6_addr, (char *) &sai.sin6_addr,
sizeof(struct in6_addr));
if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) {
err_str = strerror(errno);
gw_log->error("Could not set IPv6 Link local address. Error %s\n", err_str);
return srslte::ERROR_CANT_START;
}
for (int i=0; i<8; i++){
current_if_id[i] = ipv6_if_id[i];
}
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
}
return(srslte::ERROR_NONE);
}
srslte::error_t gw::init_if(char *err_str)
{
if(if_up)
{
return(srslte::ERROR_ALREADY_STARTED);
}
// Construct the TUN device
tun_fd = open("/dev/net/tun", O_RDWR);
gw_log->info("TUN file descriptor = %d\n", tun_fd);
if(0 > tun_fd)
{
err_str = strerror(errno);
gw_log->debug("Failed to open TUN device: %s\n", err_str);
return(srslte::ERROR_CANT_START);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, tundevname.c_str(), std::min(tundevname.length(), (size_t)(IFNAMSIZ-1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = 0;
if(0 > ioctl(tun_fd, TUNSETIFF, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set TUN device name: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
// Bring up the interface
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to bring up socket: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket flags: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
// Delete link-local IPv6 address.
struct in6_addr in6p;
char addr_str[INET6_ADDRSTRLEN];
if(find_ipv6_addr(&in6p)){
gw_log->debug("Found link-local IPv6 address: %s\n",inet_ntop(AF_INET6, &in6p, addr_str,INET6_ADDRSTRLEN) );
del_ipv6_addr(&in6p);
} else {
gw_log->warning("Could not find link-local IPv6 address.\n");
}
if_up = true;
return(srslte::ERROR_NONE);
}
/*******************************************************************************
@ -355,8 +214,6 @@ void gw::add_mch_port(uint32_t lcid, uint32_t port)
}
}
/********************/
/* GW Receive */
/********************/
@ -453,9 +310,156 @@ void gw::run_thread()
gw_log->info("GW IP receiver thread exiting.\n");
}
/********************/
/* NETLINK Helpers */
/********************/
/**************************/
/* TUN Interface Helpers */
/**************************/
srslte::error_t gw::init_if(char *err_str)
{
if (if_up) {
return (srslte::ERROR_ALREADY_STARTED);
}
// Construct the TUN device
tun_fd = open("/dev/net/tun", O_RDWR);
gw_log->info("TUN file descriptor = %d\n", tun_fd);
if (0 > tun_fd) {
err_str = strerror(errno);
gw_log->debug("Failed to open TUN device: %s\n", err_str);
return (srslte::ERROR_CANT_START);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, tundevname.c_str(), std::min(tundevname.length(), (size_t)(IFNAMSIZ-1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = 0;
if (0 > ioctl(tun_fd, TUNSETIFF, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set TUN device name: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
// Bring up the interface
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to bring up socket: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket flags: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
// Delete link-local IPv6 address.
struct in6_addr in6p;
char addr_str[INET6_ADDRSTRLEN];
if(find_ipv6_addr(&in6p)){
gw_log->debug("Found link-local IPv6 address: %s\n",inet_ntop(AF_INET6, &in6p, addr_str,INET6_ADDRSTRLEN) );
del_ipv6_addr(&in6p);
} else {
gw_log->warning("Could not find link-local IPv6 address.\n");
}
if_up = true;
return(srslte::ERROR_NONE);
}
srslte::error_t gw::setup_if_addr4(uint32_t ip_addr, char *err_str)
{
if (ip_addr != current_ip_addr) {
if (!if_up) {
if (init_if(err_str)) {
gw_log->error("init_if failed\n");
return (srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip_addr);
if (0 > ioctl(sock, SIOCSIFADDR, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket address: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_netmask.sa_family = AF_INET;
const char *mask = "255.255.255.0";
if (!default_netmask) {
mask = netmask.c_str();
}
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(mask);
if (0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket netmask: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
current_ip_addr = ip_addr;
}
return(srslte::ERROR_NONE);
}
srslte::error_t gw::setup_if_addr6(uint8_t *ipv6_if_id, char *err_str)
{
struct sockaddr_in6 sai;
struct in6_ifreq ifr6;
bool match = true;
for (int i=0; i<8; i++){
if(ipv6_if_id[i] != current_if_id[i]){
match = false;
break;
}
}
if (!match) {
if (!if_up) {
if( init_if(err_str) ) {
gw_log->error("init_if failed\n");
return(srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET6, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET6;
if(inet_pton(AF_INET6, "fe80::", (void *)&sai.sin6_addr) <= 0) {
gw_log->error("Bad address\n");
return srslte::ERROR_CANT_START;
}
memcpy(&sai.sin6_addr.s6_addr[8], ipv6_if_id, 8);
if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) {
perror("SIOGIFINDEX");
return srslte::ERROR_CANT_START;
}
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
ifr6.ifr6_prefixlen = 64;
memcpy((char *) &ifr6.ifr6_addr, (char *) &sai.sin6_addr,
sizeof(struct in6_addr));
if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) {
err_str = strerror(errno);
gw_log->error("Could not set IPv6 Link local address. Error %s\n", err_str);
return srslte::ERROR_CANT_START;
}
for (int i=0; i<8; i++){
current_if_id[i] = ipv6_if_id[i];
}
}
return(srslte::ERROR_NONE);
}
bool gw::find_ipv6_addr(struct in6_addr *in6_out)
{
int status, rtattrlen, fd = -1;

View File

@ -662,7 +662,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
// Setup GW
char *err_str = NULL;
if (gw->setup_if_addr(ip_addr, err_str)) {
if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV4, ip_addr, NULL, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
} else if (LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type){
@ -689,7 +689,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
act_def_eps_bearer_context_req.pdn_addr.addr[7]);
// Setup GW
char *err_str = NULL;
if (gw->setup_if_addr6(ipv6_if_id, err_str)) {
if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV6, 0, ipv6_if_id, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
} else if (LIBLTE_MME_PDN_TYPE_IPV4V6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type){
@ -734,10 +734,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
act_def_eps_bearer_context_req.pdn_addr.addr[11]);
char *err_str = NULL;
if (gw->setup_if_addr(ip_addr, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
if (gw->setup_if_addr6(ipv6_if_id, err_str)) {
if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV4V6, ip_addr, ipv6_if_id, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
} else {

View File

@ -126,8 +126,7 @@ private:
class gw_dummy : public gw_interface_nas, public gw_interface_pdcp
{
error_t setup_if_addr(uint32_t ip_addr, char *err_str) { return ERROR_NONE; }
error_t setup_if_addr6(uint8_t *ipv6_if_id, char *err_str) { return ERROR_NONE; }
error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_id, char *err_str) { return ERROR_NONE; }
void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {}
void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {}
};