I'm trying to complete the mininet topology exercise from this website https://github.com/mininet/openflow-tutorial/wiki/Advanced-Topology. Basically I have to create a topology like this:
h1-----s1---s2----h3
(there is also another host attached to s1 called h2)
and program a POX controller to install flows to the switches so that the pingall
and iperf
commands work.
Everything works fine except for the iperf
command which fails when it runs from h1 to h3 or from h2 to h3.
This is the code I have, and I believe the problem has to do with communicating to the right switch what to do with packets of a type different than arp or icmp, but I've been stuck too long on this problem and have decided to ask for help here.
from pox.core import core
import pox.openflow.libopenflow_01 as of
import pox.lib.packet as pkt #ipv4, arp..
import pox.lib.addresses as adr #EthAddr , IPAddr ..
log = core.getLogger()
class Tutorial (object):
def __init__ (self, connection):
# Keep track of the connection to the switch so that we can
# send it messages!
self.connection = connection
# This binds our PacketIn event listener
connection.addListeners(self)
self.dpid_table = {'10.0.1.1': 1, '10.0.2.1': 2}
# Use this table to keep track of which ethernet address is on
# which switch port (keys are MACs, values are ports).
self.ip_to_port = {'10.0.1.2':1, '10.0.1.3':2, '10.0.2.2':3}
self.mac_to_port = {}
self.routing_table = {
'10.0.1.0/24': [['10.0.1.2', 's1-eth1', '10.0.1.1', 1, '00:00:00:00:00:01'],
['10.0.1.3', 's1-eth2', '10.0.1.1', 2, '00:00:00:00:00:02']],
'10.0.2.0/24': ['10.0.2.2', 's2-eth1', '10.0.2.1', 3, '00:00:00:00:00:03'],
}
def FlowMode(self, packet_in, out_port):
print("Creating Flow...")
msg = of.ofp_flow_mod()
msg.match.in_port = packet_in.in_port
#msg_match = of.ofp_match(dl_type = pkt.ethernet.IP_TYPE, nw_proto = pkt.ipv4.IPv4)
msg.idle_timeout = 15
msg.buffer_id = packet_in.buffer_id
action = of.ofp_action_output(port = out_port)
msg.actions.append(action)
self.connection.send(msg)
print("flow created")
def act_like_router (self, packet, packet_in):
#handle ARP Requests and replies
etherPayload = packet.payload #the stripped ethFrame, contains ipv4 or arp packet
if packet.type == pkt.ethernet.ARP_TYPE:
#etherPayload is an ARP packet
src_ip = etherPayload.protosrc
dst_ip = etherPayload.protodst
if etherPayload.opcode == pkt.arp.REQUEST:
print("ARP REQUEST received by controller....\n Constructing reply...")
arp_reply = pkt.arp()
arp_reply.hwsrc = adr.EthAddr("11:12:13:14:15:16") #fake mac in response
arp_reply.hwdst = etherPayload.hwsrc
arp_reply.opcode = pkt.arp.REPLY
arp_reply.protosrc = etherPayload.protodst
arp_reply.protodst = etherPayload.protosrc
#encapsulate in ethernet frame now
ether = pkt.ethernet()
ether.type = pkt.ethernet.ARP_TYPE
ether.dst = packet.src
ether.src = packet.dst
ether.payload = arp_reply
msg = of.ofp_packet_out()
msg.data = ether.pack()
action = of.ofp_action_output(port = packet_in.in_port)
msg.actions.append(action)
self.connection.send(msg)
#send to switch, will have to implement flow instead
#self.resend_packet(ether, packet_in.in_port)
print("ARP Reply sent!")
elif etherPayload.opcode == pkt.arp.REPLY:
if src_ip not in self.arp_table:
self.arp_table[str(src_ip)] = etherPayload.hwsrc
self.mac_to_port[etherPayload.hwsrc] = packet_in.in_port
print("ARP REPLY received by controller updating tables")
print(self.mac_to_port)
print(self.arp_table)
#else:
#self.mac_to_port[etherPayload.hwsrc] = packet_in.in_port
else:
print("some other ARP OPCODE received")
elif packet.type == pkt.ethernet.IP_TYPE:
#etherPayload is an IP packet
if etherPayload.protocol == pkt.ipv4.ICMP_PROTOCOL:
icmp_packet = etherPayload.payload
if icmp_packet.type == pkt.TYPE_ECHO_REQUEST:
print("received ping request...\n creating echo_reply message")
src_ip = etherPayload.srcip
dst_ip = etherPayload.dstip
k = 0
#check if destination exist in any of the subnets
for subnet in self.routing_table.keys():
if dst_ip.inNetwork(subnet): #possible mistake here maybe turn dst_ip into IPADDR obj
k = subnet
break
if k!=0:
#host exists create and send echo reply
print('Sending reply to network ' + str(k))
#create echo fields
ech = pkt.echo() #echo contained in pkt.icmp
ech.id = icmp_packet.payload.id
ech.seq = icmp_packet.payload.seq + 1
#encapsulates in icmp
icmp_reply = pkt.icmp()
icmp_reply.type = pkt.TYPE_ECHO_REPLY #code 0
icmp_reply.payload = ech
#encapsulates in ipv4
ip_p = pkt.ipv4()
ip_p.protocol = pkt.ipv4.ICMP_PROTOCOL
ip_p.srcip = dst_ip
ip_p.dstip = src_ip
ip_p.payload = icmp_reply
#encapsulates in ethernet
eth_p = pkt.ethernet()
eth_p.type = pkt.ethernet.IP_TYPE
eth_p.src = packet.dst
eth_p.dst = packet.src
eth_p.payload = ip_p
msg = of.ofp_packet_out()
msg.data = eth_p.pack()
action = of.ofp_action_output(port = packet_in.in_port)
print("sending reply from: " + str(dst_ip) + " to: " + str(src_ip) + " using packet_in.in_port: " + str(packet_in.in_port))
msg.actions.append(action)
self.connection.send(msg)
#send to switch, will have to implement flow instead
#self.resend_packet(eth_p, packet_in.in_port)
print("echo Reply sent!")
else:
#host doesn't exist send dst unreachable
print("ICMP destination unreachable")
unr = pkt.unreach()
unr.payload = etherPayload
icmp_reply = pkt.icmp()
icmp_reply.type = pkt.TYPE_DEST_UNREACH
icmp_reply.payload = unr
ip_p = pkt.ipv4()
ip_p.srcip = dst_ip
ip_p.dstip = src_ip
ip_p.protocol = pkt.ipv4.ICMP_PROTOCOL
ip_p.payload = icmp_reply
eth_p = pkt.ethernet()
eth_p.type = pkt.ethernet.IP_TYPE
eth_p.dst = packet.src
eth_p.src = packet.dst
eth_p.payload = ip_p
msg = of.ofp_packet_out()
msg.data = eth_p.pack()
action = of.ofp_action_output(port = packet_in.in_port)
#print("sending reply from: " + str(dst_ip) + " to: " + str(src_ip) + " using packet_in.in_port: " + str(packet_in.in_port))
msg.actions.append(action)
self.connection.send(msg)
#send to switch, will have to implement flow instead
#self.resend_packet(eth_p, packet_in.in_port)
log.debug("echo Reply sent!")
#se non e` ICMP
else:
print("received some ip packet...\n handling it... ")
src_ip = etherPayload.srcip
dst_ip = etherPayload.dstip
k = 0
#check if destination exist in any of the subnets
'''
self.routing_table = {
'10.0.1.0/24': ['10.0.1.2', 's1-eth1', '10.0.1.1', 1, '00:00:00:00:00:02'],
['10.0.1.3', 's1-eth2', '10.0.1.1', 2, '00:00:00:00:00:03']
'''
for subnet in self.routing_table.keys():
if dst_ip.inNetwork(subnet):
k = subnet
break
if k!=0:
port1 = self.ip_to_port[str(dst_ip)] #get port to communicate on that subnet
if str(dst_ip) == '10.0.1.2':
#ethDest = adr.EthAddr("4e:2d:32:b9:bc:52")
ethDest = adr.EthAddr(self.routing_table[subnet][0][4])
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = port1)
packet.src = packet.dst
packet.dst = ethDest#adr.EthAddr('11:11:11:11:11:11')#ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip))
self.FlowMode(packet_in, packet_in.in_port)
elif str(dst_ip) == '10.0.1.3':
#ethDest = adr.EthAddr("22:02:eb:9c:27:2d")
ethDest = adr.EthAddr(self.routing_table[subnet][1][4])
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = port1)
packet.src = packet.dst
packet.dst = ethDest#adr.EthAddr('11:11:11:11:11:11')#ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip))
self.FlowMode(packet_in, packet_in.in_port)
elif str(dst_ip) == '10.0.2.2':
ethDest = adr.EthAddr(self.routing_table[subnet][4])
if packet_in.in_port == 1 and self.connection.dpid == 1:
outport = 3
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = outport)
packet.src = packet.dst
packet.dst = ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip) + " to switch number: " + str(connection.dpid)
+ str(etherPayload.id))
self.FlowMode(packet_in, packet_in.in_port)
elif (packet_in.in_port == 2 or packet_in.in_port == 3) and self.connection.dpid == 1:
outport = 1
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = outport)
packet.src = packet.dst
packet.dst = ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip) + " to switch number: " + str(self.connection.dpid)
+ str(etherPayload.id))
self.FlowMode(packet_in, packet_in.in_port)
elif packet_in.in_port == 1 and self.connection.dpid == 2:
outport = 3
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = outport)
packet.src = packet.dst
packet.dst = ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip) + " to switch number: " + str(self.connection.dpid)
+ str(etherPayload.id))
self.FlowMode(packet_in, packet_in.in_port)
elif(packet_in.in_port == 2 or packet_in.in_port == 3) and self.connection.dpid == 2:
outport = 1
msg = of.ofp_packet_out()
action = of.ofp_action_output(port = outport)
packet.src = packet.dst
packet.dst = ethDest
msg.data = packet.pack()
msg.actions.append(action)
self.connection.send(msg)
print("installing flow for packets for: " + str(dst_ip) + " to switch number: " + str(self.connection.dpid)
+ str(etherPayload.id))
self.FlowMode(packet_in, packet_in.in_port)
'''
router_msg = of.ofp_flow_mod()
router_msg.match = of.ofp_match.from_packet(packet)
router_msg.idle_timeout = 100
router_msg.buffer_id = packet_in.buffer_id
action = of.ofp_action_output(port = packet_in.in_port)
router_msg.actions.append(action)
self.connection.send(router_msg)
'''
#don't forget flow mode
def _handle_PacketIn (self, event):
"""
Handles packet in messages from the switch.
"""
packet = event.parsed # This is the ethernet packet.
if not packet.parsed:
log.warning("Ignoring incomplete packet")
return
packet_in = event.ofp # The actual ofp_packet_in message.
self.act_like_router(packet, packet_in)
def resend_packet(self, packet_in, out_port):
"""
Instructs the switch to resend a packet that it had sent to us.
"packet_in" is the ofp_packet_in object the switch had sent to the
controller due to a table-miss.
"""
msg = of.ofp_packet_out()
msg.data = packet_in.pack()
# Add an action to send to the specified port
action = of.ofp_action_output(port=out_port)
msg.actions.append(action)
# Send message to switch
self.connection.send(msg)
def launch ():
"""
Starts the component
"""
def start_switch (event):
log.debug("Controlling %s" % (event.connection,))
Tutorial(event.connection)
core.openflow.addListenerByName("ConnectionUp", start_switch)
EDIT:
I solved this by flooding IP_packets directed to h3, to all ports except the in_port