pythonsdnmininetiperfpox

Mininet topology is not completing the iperf command from the 2 hosts separated by 2 switches


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)

Solution

  • EDIT:

    I solved this by flooding IP_packets directed to h3, to all ports except the in_port