Last active
April 16, 2016 23:40
-
-
Save smihir/2d5446314a431b953760 to your computer and use it in GitHub Desktop.
cs640 assignment 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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