Skip to content

Instantly share code, notes, and snippets.

@smihir
Last active April 16, 2016 23:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smihir/2d5446314a431b953760 to your computer and use it in GitHub Desktop.
Save smihir/2d5446314a431b953760 to your computer and use it in GitHub Desktop.
cs640 assignment 1
#!/usr/bin/env python3
'''
Ethernet learning switch in Python: HW3.
Note that this file currently has the code to implement a "hub"
in it, not a learning switch. (I.e., it's currently a switch
that doesn't learn.)
'''
from switchyard.lib.address import *
from switchyard.lib.packet import *
from switchyard.lib.common import *
import time
class LearningSwitch:
def __init__(self, net):
self.lookup_table = {}
self.texpire = 24
self.net = net
self.interfaces = net.interfaces()
self.macs = [intf.ethaddr for intf in self.interfaces]
def run(self):
while True:
try:
# Don't use the timestamp in recv_packet
# it looks like the timestamp is just representing
# the packet number received while running in test mode
dev,packet = self.net.recv_packet(timeout=self.texpire/3)
except NoPackets:
self.timeout += 1
if self.timeout == 3:
self.purge_lookup()
continue
except Shutdown:
return
ts = time.time()
self.timeout = 0
self.learn(packet[0].src, dev, ts)
log_debug ("In {} received packet {} on {}".format(self.net.name, packet, dev))
if packet[0].dst in self.macs:
log_debug ("Packet intended for me")
else:
entry = self.lookup(packet[0].dst)
log_debug ("lookup on {}: {}".format(packet[0].dst, entry))
if entry:
if ts < entry[1] + self.texpire:
dst_dev = entry[0]
log_debug ("Sending packet {} to {}".format(packet, dst_dev))
self.net.send_packet(dst_dev, packet)
continue
else:
# Clear entry from lookup and Flood!!!
self.del_lookup_entry(packet[0].dst)
for intf in self.interfaces:
if dev != intf.name:
log_debug ("Flooding packet {} to {}".format(packet, intf.name))
self.net.send_packet(intf.name, packet)
def learn(self, mac_addr, interface, time):
if mac_addr == "ff:ff:ff:ff:ff:ff":
log_debug("Do not learn BCAST mac")
else:
self.lookup_table[mac_addr] = (interface, time)
def lookup(self, mac_addr):
if mac_addr in self.lookup_table:
return self.lookup_table[mac_addr]
else:
return None
def del_lookup_entry(self, mac_addr):
del self.lookup_table[mac_addr]
def purge_lookup(self):
self.lookup_table.clear()
def start(self):
self.run()
self.net.shutdown()
def switchy_main(net):
ls = LearningSwitch(net)
ls.start()
#!/usr/bin/env python
import sys
from switchyard.lib.address import *
from switchyard.lib.packet import *
from switchyard.lib.common import *
from switchyard.lib.testing import *
def mk_pkt(hwsrc, hwdst, ipsrc, ipdst, reply=False):
ether = Ethernet()
ether.src = EthAddr(hwsrc)
ether.dst = EthAddr(hwdst)
ether.ethertype = EtherType.IP
ippkt = IPv4()
ippkt.srcip = IPAddr(ipsrc)
ippkt.dstip = IPAddr(ipdst)
ippkt.protocol = IPProtocol.ICMP
ippkt.ttl = 32
icmppkt = ICMP()
if reply:
icmppkt.icmptype = ICMPType.EchoReply
else:
icmppkt.icmptype = ICMPType.EchoRequest
return ether + ippkt + icmppkt
def switch_tests():
s = Scenario("switch tests")
s.add_interface('eth0', '10:00:00:00:00:01')
s.add_interface('eth1', '10:00:00:00:00:02')
s.add_interface('eth2', '10:00:00:00:00:03')
# test case 1: a frame with broadcast destination should get sent out
# all ports except ingress
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
# test case 2: a frame with any unicast address except one assigned to learning switch
# interface should be sent out all ports except ingress
reqpkt = mk_pkt("20:00:00:00:00:01", "30:00:00:00:00:02", '192.168.1.100','172.16.42.2')
s.expect(PacketInputEvent("eth0", reqpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:01 to 30:00:00:00:00:02 should arrive on eth0")
s.expect(PacketOutputEvent("eth1", reqpkt, display=Ethernet), "Ethernet frame destined for 30:00:00:00:00:02 should be sent out on eth1")
resppkt = mk_pkt("30:00:00:00:00:02", "20:00:00:00:00:01", '172.16.42.2', '192.168.1.100', reply=True)
s.expect(PacketInputEvent("eth1", resppkt, display=Ethernet), "An Ethernet frame from 30:00:00:00:00:02 to 20:00:00:00:00:01 should arrive on eth1")
s.expect(PacketOutputEvent("eth0", resppkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:01 should be sent out on eth0")
# test case 3: a frame with dest address of one of the interfaces should
# result in nothing happening
reqpkt = mk_pkt("20:00:00:00:00:02", "10:00:00:00:00:03", '192.168.1.100','172.16.42.2')
s.expect(PacketInputEvent("eth2", reqpkt, display=Ethernet), "An Ethernet frame should arrive on eth2 with destination address the same as eth2's MAC address")
s.expect(PacketInputTimeoutEvent(1.0), "The hub should not do anything in response to a frame arriving with a destination address referring to the hub itself.")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
resppkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
resppkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
resppkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
resppkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
testpkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
testpkt = mk_pkt("30:00:00:00:00:02", "ff:ff:ff:ff:ff:ff", "172.16.42.2", "255.255.255.255")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth0 and eth2")
testpkt = mk_pkt("20:00:00:00:00:01", "ff:ff:ff:ff:ff:ff", '172.16.42.2', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth0")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "The Ethernet frame with a broadcast destination address should be forwarded out ports eth1 and eth2")
s.expect(PacketInputTimeoutEvent(10), "Silence")
reqpkt = mk_pkt("20:00:00:00:00:01", "20:00:00:00:00:02", '192.168.100.1','192.168.1.100')
s.expect(PacketInputEvent("eth0", reqpkt, display=Ethernet), "An Ethernet frame should arrive on eth0 with destination address for eth2")
s.expect(PacketOutputEvent("eth1", reqpkt, "eth2", reqpkt, display=Ethernet), "The ethernet frame should be flooded in eth1 and eth2")
reqpkt = mk_pkt("20:00:00:00:00:02", "20:00:00:00:00:01", '192.168.1.100','192.168.100.1', reply=True)
s.expect(PacketInputEvent("eth2", reqpkt, display=Ethernet), "An Ethernet frame should arrive on eth2 destined for eth0")
s.expect(PacketOutputEvent("eth0", reqpkt, display=Ethernet), "The reply ethernet frame should be sent on eth0")
testpkt = mk_pkt("20:00:00:00:00:03", "ff:ff:ff:ff:ff:ff", '192.168.1.101', '255.255.255.255')
s.expect(PacketInputEvent("eth2", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth2")
s.expect(PacketOutputEvent("eth0", testpkt, "eth1", testpkt, display=Ethernet), "The reply ethernet frame should be flooded on eth0 and eth1")
reqpkt = mk_pkt("20:00:00:00:00:01", "20:00:00:00:00:03", '192.168.100.1','192.168.1.101')
s.expect(PacketInputEvent("eth0", reqpkt, display=Ethernet), "An Ethernet frame should arrive on eth0 with destination address for eth2")
s.expect(PacketOutputEvent("eth2", reqpkt, display=Ethernet), "The ethernet frame should be sent on eth2")
testpkt = mk_pkt("20:00:00:00:00:03", "ff:ff:ff:ff:ff:ff", '192.168.1.101', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth2")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "An Ethernet frame should be flooded on eth1 and eth2")
testpkt = mk_pkt("30:00:00:00:00:02", "20:00:00:00:00:03", "172.16.42.2", "192.168.1.101")
s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame with 20:00:00:00:00:03 destination address should arrive on eth1")
s.expect(PacketOutputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame should arrive on eth0")
# test case 3: a frame with dest address of one of the interfaces should
# result in nothing happening
reqpkt = mk_pkt("ff:ff:ff:ff:ff:ff", "10:00:00:00:00:03", '255.255.255.255','172.16.42.2')
s.expect(PacketInputEvent("eth0", reqpkt, display=Ethernet), "Negative test case: packet with bcast destination on eth0")
s.expect(PacketInputTimeoutEvent(1.0), "The hub should not do anything in response to a frame arriving with a destination address referring to the hub itself.")
testpkt = mk_pkt("20:00:00:00:00:03", "ff:ff:ff:ff:ff:ff", '192.168.1.101', '255.255.255.255')
s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame with a broadcast destination address should arrive on eth2")
s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "An Ethernet frame should be flooded on eth1 and eth2")
return s
scenario = switch_tests()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment