more elegant definition of paths between client/router and router/server

This commit is contained in:
Quentin De Coninck 2020-07-03 15:20:20 +02:00
parent c066bc4ccd
commit 883771cd53
9 changed files with 227 additions and 85 deletions

View File

@ -1,5 +1,5 @@
leftSubnet:10.0.
rightSubnet:10.1.
path_0:10,10,4
path_1:40,30,4
path_c2r_0:10,10,4
path_c2r_1:40,30,4
topoType:MultiIf

View File

@ -1,5 +1,6 @@
leftSubnet:10.0.
rightSubnet:10.1.
path_0:100,20,4
path_1:1,20,4
path_c2r_0:100,20,4
path_c2r_1:1,20,4
path_r2s_0:10,20,10
topoType:MultiIf

5
config/topo/topo_3 Normal file
View File

@ -0,0 +1,5 @@
leftSubnet:10.0.
rightSubnet:10.1.
path_c2r_0:100,20,4
path_r2s_0:10,20,10
topoType:MultiIf

8
config/topo/topo_4 Normal file
View File

@ -0,0 +1,8 @@
leftSubnet:10.0.
rightSubnet:10.1.
path_c2r_0:100,20,4
path_c2r_1:100,20,4
path_c2r_2:100,20,4
path_r2s_0:10,20,10
path_r2s_1:10,20,10
topoType:MultiIf

8
config/topo/topo_5 Normal file
View File

@ -0,0 +1,8 @@
leftSubnet:10.0.
rightSubnet:10.1.
path_c2r_0:100,20,4
path_c2r_1:100,20,4
path_r2s_0:10,20,10
path_r2s_1:10,20,10
path_r2s_2:10,20,10
topoType:MultiIf

View File

@ -179,24 +179,24 @@ class Experiment(object):
self.topo.command_to(self.topo_config.client, "{} link set dev {} priority {}".format(
Experiment.IP_BIN, self.topo_config.get_client_interface(0), priority_path_0))
self.topo.command_to(self.topo_config.router, "{} link set dev {} priority {}".format(
Experiment.IP_BIN, self.topo_config.get_router_interface_to_switch(0), priority_path_0))
Experiment.IP_BIN, self.topo_config.get_router_interface_to_client_switch(0), priority_path_0))
self.topo.command_to(self.topo_config.client, "{} link set dev {} priority {}".format(
Experiment.IP_BIN, self.topo_config.get_client_interface(1), priority_path_1))
self.topo.command_to(self.topo_config.router, "{} link set dev {} priority {}".format(
Experiment.IP_BIN, self.topo_config.get_router_interface_to_switch(1), priority_path_1))
Experiment.IP_BIN, self.topo_config.get_router_interface_to_client_switch(1), priority_path_1))
backup_path_0 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH_0)
if int(backup_path_0) > 0:
self.topo.command_to(self.topo_config.client,
self.topo_config.interface_backup_command(self.topo_config.get_client_interface(0)))
self.topo.command_to(self.topo_config.router,
self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_switch(0)))
self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_client_switch(0)))
backup_path_1 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH_1)
if int(backup_path_1) > 0:
self.topo.command_to(self.topo_config.client,
self.topo_config.interface_backup_command(self.topo_config.get_client_interface(1)))
self.topo.command_to(self.topo_config.router,
self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_switch(1)))
self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_client_switch(1)))
def run_userspace_path_manager(self):
"""
@ -364,7 +364,7 @@ class Experiment(object):
count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT)
for i in range(0, self.topo_config.client_interface_count()):
cmd = self.ping_command(self.topo_config.get_client_ip(i),
self.topo_config.get_server_ip(), n=count)
self.topo_config.get_server_ip(interface_index=0), n=count)
logging.info(cmd)
self.topo.command_to(self.topo_config.client, cmd)

View File

@ -17,12 +17,22 @@ class NetemAt(object):
return "netem at {} ({}) will be {}".format(self.at, self.delta, self.cmd)
def get_bandwidth_delay_product_divided_by_mtu(delay, bandwidth):
"""
With delay in ms, bandwidth in Mbps
"""
rtt = 2 * float(delay)
bandwidth_delay_product = (float(bandwidth) * 125000.0) * (rtt / 1000.0)
return int(math.ceil(bandwidth_delay_product * 1.0 / 1500.0))
class LinkCharacteristics(object):
"""
Network characteristics associated to a link
Attributes:
id the identifier of the link
link_type type of the link
delay the one-way delay introduced by the link in ms
queue_size the size of the link buffer, in packets
bandwidth the bandwidth of the link in Mbps
@ -31,8 +41,9 @@ class LinkCharacteristics(object):
netem_at list of NetemAt instances applicable to the link
backup integer indicating if this link is a backup one or not (useful for MPTCP)
"""
def __init__(self, id, delay, queue_size, bandwidth, loss, backup=False):
def __init__(self, id, link_type, delay, queue_size, bandwidth, loss, backup=0):
self.id = id
self.link_type = link_type
self.delay = delay
self.queue_size = queue_size
self.bandwidth = bandwidth
@ -45,10 +56,7 @@ class LinkCharacteristics(object):
"""
Get the bandwidth-delay product in terms of packets (hence, dividing by the MTU)
"""
rtt = 2 * float(self.delay)
""" Since bandwidth is in Mbps and rtt in ms """
bandwidth_delay_product = (float(self.bandwidth) * 125000.0) * (rtt / 1000.0)
return int(math.ceil(bandwidth_delay_product * 1.0 / 1500.0))
return get_bandwidth_delay_product_divided_by_mtu(self.delay, self.bandwidth)
def buffer_size(self):
"""
@ -116,6 +124,8 @@ class LinkCharacteristics(object):
Notably used by BottleneckLink
"""
return {
"link_id": self.id,
"link_type": self.link_type,
"bw": float(self.bandwidth),
"delay": "{}ms".format(self.delay),
"loss": float(self.loss),
@ -124,13 +134,14 @@ class LinkCharacteristics(object):
def __str__(self):
return """
Link type: {}
Link id: {}
Delay: {}
Queue Size: {}
Bandwidth: {}
Loss: {}
Backup: {}
""".format(self.id, self.delay, self.queue_size, self.bandwidth, self.loss, self.backup) + \
""".format(self.link_type, self.id, self.delay, self.queue_size, self.bandwidth, self.loss, self.backup) + \
"".join(["\t {} \n".format(n) for n in self.netem_at])
@ -139,13 +150,11 @@ class TopoParameter(Parameter):
RIGHT_SUBNET = "rightSubnet"
NETEM_AT = "netem_at_"
CHANGE_NETEM = "changeNetem"
SERVER_PATHS = "serverPaths"
DEFAULT_PARAMETERS = {
LEFT_SUBNET: "10.1.",
RIGHT_SUBNET: "10.2.",
CHANGE_NETEM: "false",
SERVER_PATHS: "1",
}
def __init__(self, parameter_filename):
@ -184,34 +193,63 @@ class TopoParameter(Parameter):
logging.info(self.link_characteristics[link_id].netem_at)
def parse_link_id_and_type(self, key):
"""
The key of a path must have the following format:
path_{link_type}_{ID}
Note that several links can have the same ID, several links can have the same
link_type, but the tuple (link_type, ID) is unique.
"""
_, link_type, link_id = key.split("_")
return link_type, int(link_id)
def parse_link_characteristics(self, value):
"""
The format of a link characteristic is one of the following:
- "{delay},{queue_size},{bandwidth},{loss_perc},{is_backup}"
- "{delay},{queue_size},{bandwidth},{loss_perc}"
- "{delay},{queue_size},{bandwidth}"
- "{delay},{bandwidth}"
When not specified, default values are the following:
- queue_size: get_bandwidth_delay_product_divided_by_mtu(delay, bandwidth)
- loss_perc: 0
- is_backup: 0
Return
delay, bandwidth, queue_size, loss_perc, is_backup
"""
loss_perc, is_backup = 0.0, 0
c = value.split(",")
if len(c) == 2:
delay, bw = float(c[0]), float(c[1])
return delay, bw, get_bandwidth_delay_product_divided_by_mtu(delay, bw), loss_perc, is_backup
if len(c) == 3:
return float(c[0]), float(c[2]), int(c[1]), loss_perc, is_backup
if len(c) == 4:
return float(c[0]), float(c[2]), int(c[1]), float(c[3]), is_backup
if len(c) == 5:
return float(c[0]), float(c[2]), int(c[1]), float(c[3]), int(c[4])
raise ValueError("Invalid link characteristics: {}".format(value))
def load_link_characteristics(self):
"""
CAUTION: the path_i in config file is not taken into account. Hence place them in
increasing order in the topo parameter file!
Load the path characteristics
"""
i = 0
for k in sorted(self.parameters):
# TODO FIXME rewrite this function
if k.startswith("path"):
tab = self.parameters[k].split(",")
bup = False
loss = "0.0"
if len(tab) == 5:
loss = tab[3]
bup = tab[4].lower() == 'true'
if len(tab) == 4:
try:
loss = float(tab[3])
loss = tab[3]
except ValueError:
bup = tab[3].lower() == 'true'
if len(tab) == 3 or len(tab) == 4 or len(tab) == 5:
path = LinkCharacteristics(i, tab[0],
tab[1], tab[2], loss, bup)
self.link_characteristics.append(path)
i = i + 1
try:
link_type, link_id = self.parse_link_id_and_type(k)
delay, bw, queue_size, loss_perc, is_backup = self.parse_link_characteristics(
self.parameters[k])
except ValueError as e:
logging.error("Ignored path {}: {}".format(k, e))
else:
logging.warning("Ignored path {}".format(self.parameters[k]))
path = LinkCharacteristics(link_id, link_type, delay, bw,
queue_size, loss_perc, backup=is_backup)
self.link_characteristics.append(path)
def __str__(self):
s = "{}".format(super(TopoParameter, self).__str__())
@ -248,7 +286,8 @@ class BottleneckLink(object):
topo_builder.add_link(self.bs2, self.bs3)
def get_bs_name(self, index):
return "{}_{}_{}".format(BottleneckLink.BOTTLENECK_SWITCH_NAME_PREFIX, self.link_characteristics.id, index)
return "{}_{}_{}_{}".format(BottleneckLink.BOTTLENECK_SWITCH_NAME_PREFIX,
self.link_characteristics.link_type, self.link_characteristics.id, index)
def reinit_variables(self):
# Required to retrieve actual nodes
@ -534,15 +573,33 @@ class TopoConfig(object):
"""
raise NotImplementedError()
def server_interface_count(self):
"""
Return the number of server's interfaces, without lo
"""
raise NotImplementedError()
def get_client_interface(self, client_index, interface_index):
"""
Return the interface with index `interface_index` of the client with index `client_index`
"""
raise NotImplementedError()
def get_router_interface_to_switch(self, index):
def get_server_interface(self, server_index, interface_index):
"""
Return the router's interface to switch with index `index`
Return the interface with index `interface_index` of the server with index `server_index`
"""
raise NotImplementedError()
def get_router_interface_to_client_switch(self, index):
"""
Return the router's interface to client's switch with index `index`
"""
raise NotImplementedError()
def get_router_interface_to_server_switch(self, index):
"""
Return the router's interface to server's switch with index `index`
"""
raise NotImplementedError()

View File

@ -11,41 +11,86 @@ class MultiInterfaceTopo(Topo):
self.client = self.add_client()
self.server = self.add_server()
self.router = self.add_router()
self.client_switches = []
self.server_switches = []
for l in self.topo_parameter.link_characteristics:
self.client_switches.append(self.add_client_side_switch(l))
self.add_link(self.client,self.client_switches[-1])
self.server_switches.append(self.add_router_side_switch(l))
self.add_bottleneck_link(self.client_switches[-1], self.server_switches[-1], link_characteristics=l)
self.add_link(self.server_switches[-1],self.router)
self.c2r_client_switches = []
self.c2r_router_switches = []
self.r2s_router_switches = []
self.r2s_server_switches = []
for i in range(int(self.topo_parameter.get(TopoParameter.SERVER_PATHS))):
# Add client - router links
for l in self.get_client_to_router_links():
self.c2r_client_switches.append(self.add_c2r_client_side_switch(l))
self.add_link(self.client, self.c2r_client_switches[-1])
self.c2r_router_switches.append(self.add_c2r_router_side_switch(l))
self.add_bottleneck_link(self.c2r_client_switches[-1], self.c2r_router_switches[-1], link_characteristics=l)
self.add_link(self.c2r_router_switches[-1], self.router)
# Special case: if there is no specified link between router and server, directly connect them!
if len(self.get_router_to_server_links()) > 0:
for l in self.get_router_to_server_links():
self.r2s_router_switches.append(self.add_r2s_router_side_switch(l))
self.add_link(self.router, self.r2s_router_switches[-1])
self.r2s_server_switches.append(self.add_r2s_server_side_switch(l))
self.add_bottleneck_link(self.r2s_router_switches[-1], self.r2s_server_switches[-1], link_characteristics=l)
self.add_link(self.r2s_server_switches[-1], self.server)
else:
self.add_link(self.router, self.server)
def add_client_side_switch(self, link):
return self.add_switch("{}{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id))
def get_client_to_router_links(self):
return [l for l in self.topo_parameter.link_characteristics if l.link_type == "c2r"]
def add_router_side_switch(self, link):
return self.add_switch("{}{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id + 1))
def get_router_to_server_links(self):
return [l for l in self.topo_parameter.link_characteristics if l.link_type == "r2s"]
def add_c2r_client_side_switch(self, link):
return self.add_switch("{}c2r{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id))
def add_c2r_router_side_switch(self, link):
return self.add_switch("{}c2r{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id + 1))
def add_r2s_router_side_switch(self, link):
return self.add_switch("{}r2s{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id))
def add_r2s_server_side_switch(self, link):
return self.add_switch("{}r2s{}".format(MultiInterfaceTopo.SWITCH_NAME_PREFIX, 2 * link.id + 1))
def __str__(self):
s = "Simple multiple interface topolgy \n"
s = "Simple multiple interface topology \n"
i = 0
n = len(self.topo_parameter.link_characteristics)
for p in self.topo_parameter.link_characteristics:
if i == n // 2:
if n % 2 == 0:
s = s + "c r-----s\n"
s = s + "|--sw----sw--|\n"
nc = len(self.get_client_to_router_links())
ns = len(self.get_router_to_server_links())
m = max(nc, ns)
skipped = 0
for i in range(0, m):
if i == m // 2:
if m % 2 == 0:
s = s + "c r--sw---bl---sw--s\n"
s = s + " \-sw---bl---sw-/\n"
else:
s = s + "c--sw----sw--r-----s\n"
s = s + "c--sw---bl---sw--r--sw---bl---sw--s\n"
else:
s = s + "|--sw----sw--|\n"
i = i + 1
if i < m // 2:
if (nc == m and ns + skipped == m) or (ns == m and nc + skipped == m):
s = s + " /-sw---bl---sw-\ /-sw---bl---sw-\ \n"
elif nc == m:
s = s + " /-sw---bl---sw-\ \n"
skipped += 1
else:
s = s + " /-sw---bl---sw-\ \n"
skipped += 1
else:
if (nc == m and ns + skipped == m) or (ns == m and nc + skipped == m):
s = s + " \-sw---bl---sw-/ \-sw---bl---sw-/ \n"
elif nc == m:
s = s + " \-sw---bl---sw-/ \n"
skipped += 1
else:
s = s + " \-sw---bl---sw-/ \n"
skipped += 1
return s
class MultiInterfaceConfig(TopoConfig):
NAME = "MultiIf"
@ -53,7 +98,7 @@ class MultiInterfaceConfig(TopoConfig):
super(MultiInterfaceConfig, self).__init__(topo, param)
def configure_routing(self):
for i, l in enumerate(self.topo.client_switches):
for i, l in enumerate(self.topo.c2r_client_switches):
cmd = self.add_table_route_command(self.get_client_ip(i), i)
self.topo.command_to(self.client, cmd)
@ -66,7 +111,7 @@ class MultiInterfaceConfig(TopoConfig):
i)
self.topo.command_to(self.client, cmd)
for i in range(int(self.topo.topo_parameter.get(TopoParameter.SERVER_PATHS))):
for i, l in enumerate(self.topo.r2s_server_switches):
cmd = self.add_table_route_command(self.get_server_ip(i), i)
self.topo.command_to(self.server, cmd)
@ -96,7 +141,7 @@ class MultiInterfaceConfig(TopoConfig):
netmask = "255.255.255.0"
links = self.topo.get_link_characteristics()
for i, l in enumerate(self.topo.client_switches):
for i, l in enumerate(self.topo.c2r_client_switches):
cmd = self.interface_up_command(self.get_client_interface(0, i), self.get_client_ip(i), netmask)
self.topo.command_to(self.client, cmd)
client_interface_mac = self.client.intf(self.get_client_interface(0, i)).MAC()
@ -106,23 +151,38 @@ class MultiInterfaceConfig(TopoConfig):
cmd = self.interface_backup_command(self.get_client_interface(0, i))
self.topo.command_to(self.client, cmd)
for i, l in enumerate(self.topo.server_switches):
cmd = self.interface_up_command(self.get_router_interface_to_switch(i),
for i, l in enumerate(self.topo.c2r_router_switches):
cmd = self.interface_up_command(self.get_router_interface_to_client_switch(i),
self.get_router_ip_to_client_switch(i), netmask)
self.topo.command_to(self.router, cmd)
router_interface_mac = self.router.intf(self.get_router_interface_to_switch(i)).MAC()
router_interface_mac = self.router.intf(self.get_router_interface_to_client_switch(i)).MAC()
self.topo.command_to(self.client, "arp -s {} {}".format(
self.get_router_ip_to_client_switch(i), router_interface_mac))
for i in range(int(self.topo.topo_parameter.get(TopoParameter.SERVER_PATHS))):
cmd = self.interface_up_command(self.get_router_interface_to_server(i),
if len(self.topo.r2s_router_switches) == 0:
# Case no server param is specified
cmd = self.interface_up_command(self.get_router_interface_to_server_switch(0),
self.get_router_ip_to_server_switch(0), netmask)
self.topo.command_to(self.router, cmd)
router_interface_mac = self.router.intf(self.get_router_interface_to_server_switch(0)).MAC()
self.topo.command_to(self.server, "arp -s {} {}".format(
self.get_router_ip_to_server_switch(0), router_interface_mac))
cmd = self.interface_up_command(self.get_server_interface(0, 0), self.get_server_ip(0), netmask)
self.topo.command_to(self.server, cmd)
server_interface_mac = self.server.intf(self.get_server_interface(0, 0)).MAC()
self.topo.command_to(self.router, "arp -s {} {}".format(
self.get_server_ip(0), server_interface_mac))
for i, l in enumerate(self.topo.r2s_router_switches):
cmd = self.interface_up_command(self.get_router_interface_to_server_switch(i),
self.get_router_ip_to_server_switch(i), netmask)
self.topo.command_to(self.router, cmd)
router_interface_mac = self.router.intf(self.get_router_interface_to_server(i)).MAC()
router_interface_mac = self.router.intf(self.get_router_interface_to_server_switch(i)).MAC()
self.topo.command_to(self.server, "arp -s {} {}".format(
self.get_router_ip_to_server_switch(i), router_interface_mac))
for i in range(int(self.topo.topo_parameter.get(TopoParameter.SERVER_PATHS))):
for i, l in enumerate(self.topo.r2s_server_switches):
cmd = self.interface_up_command(self.get_server_interface(0, i), self.get_server_ip(i), netmask)
self.topo.command_to(self.server, cmd)
server_interface_mac = self.server.intf(self.get_server_interface(0, i)).MAC()
@ -148,15 +208,18 @@ class MultiInterfaceConfig(TopoConfig):
return "{}{}.0/24".format(self.param.get(TopoParameter.RIGHT_SUBNET), interface_index)
def client_interface_count(self):
return len(self.topo.client_switches)
return len(self.topo.c2r_client_switches)
def get_router_interface_to_server(self, switch_index):
return self.get_router_interface_to_switch(len(self.topo.server_switches) + switch_index)
def server_interface_count(self):
return len(self.topo.r2s_server_switches)
def get_router_interface_to_server_switch(self, switch_index):
return self.get_router_interface_to_client_switch(len(self.topo.c2r_router_switches) + switch_index)
def get_client_interface(self, client_index, interface_index):
return "{}-eth{}".format(self.topo.get_client_name(client_index), interface_index)
def get_router_interface_to_switch(self, interface_index):
def get_router_interface_to_client_switch(self, interface_index):
return "{}-eth{}".format(self.topo.get_router_name(0), interface_index)
def get_server_interface(self, server_index, interface_index):

View File

@ -158,10 +158,10 @@ class MultiInterfaceCongConfig(TopoConfig):
self.topo.command_to(self.router, "arp -s " + self.getCongClientIP(i) + " " + congClientIntfMac)
cmd = self.interface_up_command(
self.get_router_interface_to_switch(i),
self.get_router_interface_to_client_switch(i),
self.getRouterIPSwitch(i), netmask)
self.topo.command_to(self.router, cmd)
routerIntfMac = self.router.intf(self.get_router_interface_to_switch(i)).MAC()
routerIntfMac = self.router.intf(self.get_router_interface_to_client_switch(i)).MAC()
self.topo.command_to(self.client, "arp -s " + self.getRouterIPSwitch(i) + " " + routerIntfMac)
# Don't forget the congestion client
self.topo.command_to(self.cong_clients[i], "arp -s " + self.getRouterIPSwitch(i) + " " + routerIntfMac)
@ -240,10 +240,10 @@ class MultiInterfaceCongConfig(TopoConfig):
return len(self.topo.switch)
def get_router_interface_to_server(self):
return self.get_router_interface_to_switch(len(self.topo.switch))
return self.get_router_interface_to_client_switch(len(self.topo.switch))
def getRouterInterfaceCongServer(self, congID):
return self.get_router_interface_to_switch(len(self.topo.switch) + 1 + congID)
return self.get_router_interface_to_client_switch(len(self.topo.switch) + 1 + congID)
def get_client_interface(self, interfaceID):
return Topo.CLIENT_NAME + "-eth" + str(interfaceID)
@ -251,7 +251,7 @@ class MultiInterfaceCongConfig(TopoConfig):
def getCongClientInterface(self, interfaceID):
return MultiInterfaceCongConfig.congClientName + str(interfaceID) + "-eth0"
def get_router_interface_to_switch(self, interfaceID):
def get_router_interface_to_client_switch(self, interfaceID):
return Topo.ROUTER_NAME + "-eth" + str(interfaceID)
def get_server_interface(self):