Skip to content

Commit

Permalink
feat(agw): IPv6 data path support (magma#11926)
Browse files Browse the repository at this point in the history
Signed-off-by: Pruthvi Hebbani <pruthvi.hebbani@radisys.com>
  • Loading branch information
pruthvihebbani committed Jun 2, 2022
1 parent 08f8f83 commit 2eae88c
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 54 deletions.
3 changes: 3 additions & 0 deletions lte/gateway/Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
magma.vm.network "private_network", ip: "192.168.60.142", nic_type: "82540EM"
# iperf3 trfserver routable IP.
magma.vm.network "private_network", ip: "192.168.129.1", nic_type: "82540EM"
magma.vm.network "private_network", ip: "3001::10", nic_type: "82540EM"

magma.vm.provider "virtualbox" do |vb|
vb.name = "magma-dev"
Expand Down Expand Up @@ -68,6 +69,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
magma_trfserver.vm.network "private_network", ip: "192.168.60.144", nic_type: "82540EM"
# iperf3 server IP.
magma_trfserver.vm.network "private_network", ip: "192.168.129.42", nic_type: "82540EM"
magma_trfserver.vm.network "private_network", ip: "3001::2", nic_type: "82540EM"

magma_trfserver.vm.provider "virtualbox" do |vb|
vb.name = "magma-trfserver"
Expand Down Expand Up @@ -119,6 +121,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
magma_test.vm.network "private_network", ip: "192.168.60.141", nic_type: "82540EM"
# UE trfgen network
magma_test.vm.network "private_network", ip: "192.168.128.11", nic_type: "82540EM"
magma_test.vm.network "private_network", ip: "3001::3", nic_type: "82540EM"
#config.ssh.private_key_path = "~/.ssh/vagrant.key"
config.ssh.forward_agent = true

Expand Down
3 changes: 2 additions & 1 deletion lte/gateway/configs/pipelined.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ uplink_eth_port_name: eth2
uplink_gw_mac: 'ff:ff:ff:ff:ff:ff'
dp_router_enabled: true
sgi_management_iface_ip_addr: '192.168.129.1/24'

sgi_management_iface_ipv6_addr: '3001::10/64'
sgi_management_iface_ipv6_gw: '3001::2'
ovs_gtp_stats_polling_interval: 180

he_proxy_eth_mac: 'e6:8f:a2:80:80:80'
Expand Down
1 change: 1 addition & 0 deletions lte/gateway/deploy/roles/magma/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
remote_src: yes
with_items:
- config_stateless_agw.py
- config_iface_for_ipv6.py
- create_oai_certs.py
- generate_oai_config.py
- generate_dnsd_config.py
Expand Down
46 changes: 41 additions & 5 deletions lte/gateway/deploy/roles/trfserver/files/traffic_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import subprocess
import sys
import threading
import time
import traceback

import iperf3
Expand Down Expand Up @@ -516,14 +517,22 @@ def __init__(self, server, instances):
self._server = server
self._instances = instances
self._results = None
self._trfserver_ipv6 = '3001::2'
self._trfserver_ipv4 = '192.168.129.42'
self._agw_ipv6 = '3001::10'
self._agw_ip_sub = 64

self._setup_iperf3()

def _get_macs(self):
def _get_macs(self, version=4):
''' Retrieves the MAC addresses of the associated test servers, based
on the information of the instances '''
ip = pyroute2.IPRoute()
mac = ip.link('get', index=ip.link_lookup(ifname='eth2')[0])[0] \
if version == 4:
intf = 'eth2'
else:
intf = 'eth3'
mac = ip.link('get', index=ip.link_lookup(ifname=intf)[0])[0] \
.get_attr('IFLA_ADDRESS')

return (mac,) * len(self._instances)
Expand Down Expand Up @@ -559,9 +568,15 @@ def _run_iperf3(self, results_buffer, iperf):
'''
# Constructing the subprocess call
params = ('-B', iperf.bind_address, '-p', str(iperf.port), '-J')
if ipaddress.ip_address(iperf.bind_address).version == 6:
params += ('-6',)
if 'c' == iperf.role:
params = ('-c', iperf.server_hostname) + params
params += ('-b', str(iperf.bandwidth), '-t', str(iperf.duration))
# For ipv6 there is delay in configuring ipv6 address on eth3
# interface of test vm, so sleep for 5 secs
if ipaddress.ip_address(iperf.bind_address).version == 6:
time.sleep(5)
if 'udp' == iperf.protocol:
params += ('-u',)
else:
Expand All @@ -583,12 +598,26 @@ def _setup_iperf3(self):
for instance in self._instances:
if instance.is_uplink:
iperf = iperf3.Server()
iperf.bind_address = '192.168.129.42'
ip_str = ipaddress.ip_address(instance.ip)
if ip_str.version == 4:
print("Running ipv4")
iperf.bind_address = self._trfserver_ipv4
else:
print("Running ipv6")
iperf.bind_address = self._trfserver_ipv6
os.system('sudo /sbin/ip -6 route add %s/%d dev eth3' % (self._agw_ipv6, self._agw_ip_sub))
os.system('sudo /sbin/ip -6 route add %s via %s dev eth3' % (instance.ip.exploded, self._agw_ipv6))
iperf.port = TrafficTestDriver._get_port()
else:
iperf = iperf3.Client()
iperf.bandwidth = 10 ** 7 # 10 Mbps
iperf.bind_address = '192.168.129.42'
ip_str = ipaddress.ip_address(instance.ip)
if ip_str.version == 4:
iperf.bind_address = self._trfserver_ipv4
else:
iperf.bind_address = self._trfserver_ipv6
os.system('sudo /sbin/ip -6 route add %s/%d dev eth3' % (self._agw_ipv6, self._agw_ip_sub))
os.system('sudo /sbin/ip -6 route add %s via %s dev eth3' % (instance.ip.exploded, self._agw_ipv6))
iperf.duration = instance.duration
iperf.port = instance.port
iperf.protocol = 'udp' if instance.is_udp else 'tcp'
Expand All @@ -612,7 +641,14 @@ def run(self):
ports = (
iperf.port if 's' == iperf.role else 0 for iperf in self._iperfs
)
macs = self._get_macs()
# For now multiple UEs with mixed ip addresses is not supported
# so check the version of the first ip address
# TODO: Add support for handling multiple UE with mixed ipv4 and ipv6
# addresses
for iperf in self._iperfs:
ip_version = ipaddress.ip_address(iperf.bind_address).version
break
macs = self._get_macs(ip_version)

# Reshape into argument tuples
tuples = zip(ips, ports, macs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ S1_SETUP_TMR_VAL 2000
NO_OF_SCTP_IN_STREAMS 3
NO_OF_SCTP_OUT_STREAMS 3
UE_ETH_INTF eth2
UE_ETH_INTF_IPV6 eth3
VS_IP_ADDR 192.168.129.42
CFGEND
92 changes: 74 additions & 18 deletions lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,7 @@ class MagmadUtil(object):
apn_correction_cmds = Enum("apn_correction_cmds", "DISABLE ENABLE")
health_service_cmds = Enum("health_service_cmds", "DISABLE ENABLE")
ha_service_cmds = Enum("ha_service_cmds", "DISABLE ENABLE")
config_ipv6_iface_cmds = Enum("config_ipv6_iface_cmds", "DISABLE ENABLE")

def __init__(self, magmad_client):
"""
Expand Down Expand Up @@ -1215,24 +1216,46 @@ def print_redis_state(self):
mme_ueip_imsi_map_entries,
)

def enable_nat(self):
def enable_nat(self, ip_version=4):
"""Enable Nat"""
self._set_agw_nat(True)
self._validate_nated_datapath()
self.exec_command("sudo ip route del default via 192.168.129.42")
self.exec_command("sudo ip route add default via 10.0.2.2 dev eth0")
self._validate_nated_datapath(ip_version)
if ip_version == 4:
self.exec_command("sudo ip route del default via 192.168.129.42")
self.exec_command("sudo ip route add default via 10.0.2.2 dev eth0")
else:
self.exec_command("sudo ip route del default via 3001::2")
self.exec_command("sudo ip route add default via 2020::10 dev eth0")

def disable_nat(self, ip_version=4):
"""
Disable Nat
ip config details:
vm ip intf
=============================
dev 192.168.129.1 eth2
dev 3001::10 eth3
test 192.168.128.11 eth2
test 3001::3 eth3
trf 192.168.129.42 eth2
trf 3001::2 eth3
"""
if ip_version == 4:
self.exec_command("sudo ip route del default via 10.0.2.2 dev eth0")
self.exec_command(
"sudo ip addr replace 192.168.129.1/24 dev uplink_br0",
)
self.exec_command(
"sudo ip route add default via 192.168.129.42 dev uplink_br0",
)
else:
self.exec_command("sudo ip route del default via 2020::10 dev eth0")
self.exec_command("sudo ip addr replace 3001::10 dev uplink_br0")
self.exec_command("sudo ip route -A inet6 add default via 3001::2 dev uplink_br0")

def disable_nat(self):
"""Disable Nat"""
self.exec_command("sudo ip route del default via 10.0.2.2 dev eth0")
self.exec_command(
"sudo ip addr replace 192.168.129.1/24 dev uplink_br0",
)
self.exec_command(
"sudo ip route add default via 192.168.129.42 dev uplink_br0",
)
self._set_agw_nat(False)
self._validate_non_nat_datapath()
self._validate_non_nat_datapath(ip_version)

def _set_agw_nat(self, enable: bool):
mconfig_conf = (
Expand All @@ -1250,18 +1273,51 @@ def _set_agw_nat(self, enable: bool):
self.restart_sctpd()
self.restart_all_services()

def _validate_non_nat_datapath(self):
def _validate_non_nat_datapath(self, ip_version=4):
# validate SGi interface is part of uplink-bridge.
out1 = self.exec_command_output("sudo ovs-vsctl list-ports uplink_br0")
assert "eth2" in str(out1)
iface = "eth2" if ip_version == 4 else "eth3"
assert iface in str(out1)
print("NAT is disabled")

def _validate_nated_datapath(self):
def _validate_nated_datapath(self, ip_version=4):
# validate SGi interface is not part of uplink-bridge.
out1 = self.exec_command_output("sudo ovs-vsctl list-ports uplink_br0")
assert "eth2" not in str(out1)
iface = "eth2" if ip_version == 4 else "eth3"
assert iface not in str(out1)
print("NAT is enabled")

def config_ipv6_iface(self, cmd):
"""
Configure eth3 interface for ipv6 data on the access gateway
Args:
cmd: Enable or disable eth3 iface on AGW,
should be one of
enable: Enable eth3 as nat_iface, do nothing if already configured
disable: Disable eth3 as nat_iface, do nothing if already configured
"""
magtivate_cmd = "source /home/vagrant/build/python/bin/activate"
venvsudo_cmd = "sudo -E PATH=$PATH PYTHONPATH=$PYTHONPATH env"
config_ipv6_iface_script = "/usr/local/bin/config_iface_for_ipv6.py"

ret_code = self.exec_command(
magtivate_cmd
+ " && "
+ venvsudo_cmd
+ " python3 "
+ config_ipv6_iface_script
+ " "
+ cmd.name.lower(),
)

if ret_code == 0:
print("Configuration successful")
elif ret_code == 1:
print("Configuration not changed")
else:
print("Failed to configure")


class MobilityUtil(object):
"""Utility wrapper for interacting with mobilityd"""
Expand Down
14 changes: 12 additions & 2 deletions lte/gateway/python/integ_tests/s1aptests/s1ap_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(
apn_correction=MagmadUtil.apn_correction_cmds.DISABLE,
health_service=MagmadUtil.health_service_cmds.DISABLE,
federated_mode=False,
ip_version=4,
):
"""
Initialize the various classes required by the tests and setup.
Expand All @@ -78,7 +79,7 @@ def __init__(
current_time = time.strftime("%H:%M:%S", t)
print("Start time", current_time)
self._s1_util = S1ApUtil()
self._enBConfig()
self._enBConfig(ip_version)

if self._test_oai_upstream:
subscriber_client = SubscriberDbCassandra()
Expand Down Expand Up @@ -132,13 +133,14 @@ def _test_cloud(self):
def _test_oai_upstream(self):
return os.getenv("TEST_OAI_UPSTREAM") is not None

def _enBConfig(self):
def _enBConfig(self, ip_version=4):
"""Configure the eNB in S1APTester"""
# Using exaggerated prints makes the stdout easier to read.
print("************************* Enb tester config")
req = s1ap_types.FwNbConfigReq_t()
req.cellId_pr.pres = True
req.cellId_pr.cell_id = 10
req.ip_version = ip_version
assert self._s1_util.issue_cmd(s1ap_types.tfwCmd.ENB_CONFIG, req) == 0
response = self._s1_util.get_response()
assert response.msg_type == s1ap_types.tfwCmd.ENB_CONFIG_CONFIRM.value
Expand Down Expand Up @@ -405,6 +407,10 @@ def configDownlinkTest(self, *ues, **kwargs):
**kwargs,
)

def configMtuSize(self, set_mtu=False):
""" Config MTU size for DL ipv6 data """
assert self._trf_util.update_mtu_size(set_mtu)

def configUplinkTest(self, *ues, **kwargs):
""" Set up an uplink test, returning a TrafficTest object
Expand Down Expand Up @@ -626,3 +632,7 @@ def flush_arp(self):
"sudo ip neigh flush all",
)
print("magma-dev: ARP flushed")

def enable_disable_ipv6_iface(self, cmd):
"""Enable or disable eth3 (ipv6) interface as nat_iface"""
self._magmad_util.config_ipv6_iface(cmd)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Copyright 2020 The Magma Authors.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import unittest

from integ_tests.s1aptests import s1ap_wrapper
from s1ap_utils import MagmadUtil


class TestDisableIpv6Iface(unittest.TestCase):
"""Unittest: TestDisableIpv6Iface"""

def test_disable_ipv6_iface(self):
"""Delete eth3 interface as nat_iface on pipelined.yml
after testing ipv6 data testcases
"""
self._s1ap_wrapper = s1ap_wrapper.TestWrapper()

print("Disabling ipv6_iface on pipelined.yml")
cmd = MagmadUtil.config_ipv6_iface_cmds.DISABLE
self._s1ap_wrapper.enable_disable_ipv6_iface(cmd)


if __name__ == "__main__":
unittest.main()
35 changes: 35 additions & 0 deletions lte/gateway/python/integ_tests/s1aptests/test_enable_ipv6_iface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Copyright 2020 The Magma Authors.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import unittest

from integ_tests.s1aptests import s1ap_wrapper
from s1ap_utils import MagmadUtil


class TestEnableIpv6Iface(unittest.TestCase):
"""Unittest: TestEnableIpv6Iface"""

def test_enable_ipv6_iface(self):
"""Add eth3 interface as nat_iface on pipelined.yml to
test ipv6 data testcases
"""
self._s1ap_wrapper = s1ap_wrapper.TestWrapper()

print("Enabling ipv6_iface on pipelined.yml")
cmd = MagmadUtil.config_ipv6_iface_cmds.ENABLE
self._s1ap_wrapper.enable_disable_ipv6_iface(cmd)


if __name__ == "__main__":
unittest.main()

0 comments on commit 2eae88c

Please sign in to comment.