diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index d9feae28aa1b21cc1ae35ff4585b13e010eadc8e..b3ef406e69298fc785e975cb488fb3833de5d36b 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -476,6 +476,17 @@ public class Ethernet extends BasePacket { sb.append("\nnw_proto: "); sb.append(p.getProtocol()); } + else if (pkt instanceof IPv6) { + IPv6 p = (IPv6) pkt; + sb.append("\nnw_src: "); + sb.append(p.getSourceAddress().toString()); + sb.append("\nnw_dst: "); + sb.append(p.getDestinationAddress().toString()); + sb.append("\nnw_tclass: "); + sb.append(p.getTrafficClass()); + sb.append("\nnw_proto: "); + sb.append(p.getNextHeader().toString()); + } else if (pkt instanceof DHCP) { sb.append("\ndhcp packet"); } @@ -488,9 +499,8 @@ public class Ethernet extends BasePacket { else if (pkt instanceof BPDU) { sb.append("\nbpdu packet"); } - else sb.append("\nunknwon packet"); + else sb.append("\nunknown packet"); return sb.toString(); } - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/packet/IPv6.java b/src/main/java/net/floodlightcontroller/packet/IPv6.java index d8af941018bd2ad2f8ae0b37f02d0cee7522d29c..e15b45e024fd9a4ecdf6aa9f9e1915a457ab8e5a 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPv6.java +++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java @@ -1,255 +1,304 @@ +/** + * Copyright 2015, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ package net.floodlightcontroller.packet; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; /** * @author Jacob Chappell (jacob.chappell@uky.edu) + * @edited Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu */ public class IPv6 extends BasePacket { - public static Map<IpProtocol, Class<? extends IPacket>> nextHeaderClassMap; - - static { - nextHeaderClassMap = new HashMap<IpProtocol, Class<? extends IPacket>>(); - // TODO: Add ICMPv6, IPv6 Options, etc.. - nextHeaderClassMap.put(IpProtocol.TCP, TCP.class); - nextHeaderClassMap.put(IpProtocol.UDP, UDP.class); - } - - public static final int HEADER_LENGTH = 40; - - protected byte version; - protected byte trafficClass; - protected int flowLabel; - protected short payloadLength; - protected IpProtocol nextHeader; - protected byte hopLimit; - protected IPv6Address sourceAddress; - protected IPv6Address destinationAddress; - - public IPv6() { - super(); - this.version = 6; - nextHeader = IpProtocol.NONE; - sourceAddress = IPv6Address.NONE; - destinationAddress = IPv6Address.NONE; - } - - public byte getVersion() { - return version; - } - - public IPv6 setVersion(byte version) { - this.version = version; - return this; - } - - public byte getTrafficClass() { - return trafficClass; - } - - public IPv6 setTrafficClass(byte trafficClass) { - this.trafficClass = trafficClass; - return this; - } - - public int getFlowLabel() { - return flowLabel; - } - - public IPv6 setFlowLabel(int flowLabel) { - this.flowLabel = flowLabel; - return this; - } - - public short getPayloadLength() { - return payloadLength; - } - - public IPv6 setPayloadLength(short payloadLength) { - this.payloadLength = payloadLength; - return this; - } - - public IpProtocol getNextHeader() { - return nextHeader; - } - - public IPv6 setNextHeader(IpProtocol nextHeader) { - this.nextHeader = nextHeader; - return this; - } - - public byte getHopLimit() { - return hopLimit; - } - - public IPv6 setHopLimit(byte hopLimit) { - this.hopLimit = hopLimit; - return this; - } - - public IPv6Address getSourceAddress() { - return sourceAddress; - } - - public IPv6 setSourceAddress(IPv6Address sourceAddress) { - this.sourceAddress = sourceAddress; - return this; - } - - public IPv6Address getDestinationAddress() { - return destinationAddress; - } - - public IPv6 setDestinationAddress(IPv6Address destinationAddress) { - this.destinationAddress = destinationAddress; - return this; - } - - @Override - public byte[] serialize() { - // Get the raw bytes of the payload we encapsulate. - byte[] payloadData = null; - if (this.payload != null) { - this.payload.setParent(this); - payloadData = this.payload.serialize(); - } - // Update our internal payload length. - this.payloadLength = (short) ((payloadData != null) ? payloadData.length : 0); - // Create a byte buffer to hold the IPv6 packet structure. - byte[] data = new byte[HEADER_LENGTH + this.payloadLength]; - ByteBuffer bb = ByteBuffer.wrap(data); - // Add header fields to the byte buffer in the correct order. - // Fear not the bit magic that must occur. - bb.put((byte) (((this.version & 0xF) << 4) | - ((this.trafficClass & 0xF0) >>> 4))); - bb.put((byte) (((this.trafficClass & 0xF) << 4) | - ((this.flowLabel & 0xF0000) >>> 16))); - bb.putShort((short) (this.flowLabel & 0xFFFF)); - bb.putShort(this.payloadLength); - bb.put((byte) this.nextHeader.getIpProtocolNumber()); - bb.put(this.hopLimit); - bb.put(this.sourceAddress.getBytes()); - bb.put(this.destinationAddress.getBytes()); - // Add the payload to the byte buffer, if necessary. - if (payloadData != null) - bb.put(payloadData); - // We're done! Return the data. - return data; - } - - @Override - public IPacket deserialize(byte[] data, int offset, int length) - throws PacketParsingException { - // Wrap the data in a byte buffer for easier retrieval. - ByteBuffer bb = ByteBuffer.wrap(data, offset, length); - // Retrieve values from IPv6 header. - byte firstByte = bb.get(); - byte secondByte = bb.get(); - this.version = (byte) ((firstByte & 0xF0) >>> 4); - if (this.version != 6) { - throw new PacketParsingException( - "Invalid version for IPv6 packet: " + - this.version); - } - this.trafficClass = (byte) (((firstByte & 0xF) << 4) | - ((secondByte & 0xF0) >>> 4)); - this.flowLabel = ((secondByte & 0xF) << 16) | - (bb.getShort() & 0xFFFF); - this.payloadLength = bb.getShort(); - this.nextHeader = IpProtocol.of(bb.get()); // TODO: U8.f()? - this.hopLimit = bb.get(); - byte[] sourceAddress = new byte[16]; - bb.get(sourceAddress, 0, 16); - byte[] destinationAddress = new byte[16]; - bb.get(destinationAddress, 0, 16); - this.sourceAddress = IPv6Address.of(sourceAddress); - this.destinationAddress = IPv6Address.of(destinationAddress); - // Retrieve the payload, if possible. - IPacket payload; - if (IPv6.nextHeaderClassMap.containsKey(this.nextHeader)) { - Class<? extends IPacket> clazz = IPv6.nextHeaderClassMap.get(this.nextHeader); - try { - payload = clazz.newInstance(); - } catch (Exception e) { - throw new RuntimeException("Error parsing payload for IPv6 packet", e); - } - } else { - payload = new Data(); - } - // Deserialize as much of the payload as we can (hopefully all of it). - this.payload = payload.deserialize(data, bb.position(), - Math.min(this.payloadLength, bb.limit() - bb.position())); - this.payload.setParent(this); - // We're done! - return this; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime - * result - + ((destinationAddress == null) ? 0 : destinationAddress - .hashCode()); - result = prime * result + flowLabel; - result = prime * result + hopLimit; - result = prime * result - + ((nextHeader == null) ? 0 : nextHeader.hashCode()); - result = prime * result + payloadLength; - result = prime * result - + ((sourceAddress == null) ? 0 : sourceAddress.hashCode()); - result = prime * result + trafficClass; - result = prime * result + version; - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (!(obj instanceof IPv6)) - return false; - IPv6 other = (IPv6) obj; - if (destinationAddress == null) { - if (other.destinationAddress != null) - return false; - } else if (!destinationAddress.equals(other.destinationAddress)) - return false; - if (flowLabel != other.flowLabel) - return false; - if (hopLimit != other.hopLimit) - return false; - if (nextHeader == null) { - if (other.nextHeader != null) - return false; - } else if (!nextHeader.equals(other.nextHeader)) - return false; - if (payloadLength != other.payloadLength) - return false; - if (sourceAddress == null) { - if (other.sourceAddress != null) - return false; - } else if (!sourceAddress.equals(other.sourceAddress)) - return false; - if (trafficClass != other.trafficClass) - return false; - if (version != other.version) - return false; - return true; - } + public static Map<IpProtocol, Class<? extends IPacket>> nextHeaderClassMap; + + static { + nextHeaderClassMap = new HashMap<IpProtocol, Class<? extends IPacket>>(); + // TODO: Add ICMPv6, IPv6 Options, etc.. + nextHeaderClassMap.put(IpProtocol.TCP, TCP.class); + nextHeaderClassMap.put(IpProtocol.UDP, UDP.class); + } + + public static final int HEADER_LENGTH = 40; + + protected byte version; + protected byte trafficClass; + protected int flowLabel; + protected short payloadLength; + protected IpProtocol nextHeader; + protected byte hopLimit; + protected IPv6Address sourceAddress; + protected IPv6Address destinationAddress; + + public IPv6() { + super(); + this.version = 6; + nextHeader = IpProtocol.NONE; + sourceAddress = IPv6Address.NONE; + destinationAddress = IPv6Address.NONE; + } + + public byte getVersion() { + return version; + } + + public IPv6 setVersion(byte version) { + this.version = version; + return this; + } + + public byte getTrafficClass() { + return trafficClass; + } + + public IPv6 setTrafficClass(byte trafficClass) { + this.trafficClass = trafficClass; + return this; + } + + public int getFlowLabel() { + return flowLabel; + } + + public IPv6 setFlowLabel(int flowLabel) { + this.flowLabel = flowLabel; + return this; + } + + public short getPayloadLength() { + return payloadLength; + } + + public IPv6 setPayloadLength(short payloadLength) { + this.payloadLength = payloadLength; + return this; + } + + public IpProtocol getNextHeader() { + return nextHeader; + } + + public IPv6 setNextHeader(IpProtocol nextHeader) { + this.nextHeader = nextHeader; + return this; + } + + public byte getHopLimit() { + return hopLimit; + } + + public IPv6 setHopLimit(byte hopLimit) { + this.hopLimit = hopLimit; + return this; + } + + public IPv6Address getSourceAddress() { + return sourceAddress; + } + + public IPv6 setSourceAddress(IPv6Address sourceAddress) { + this.sourceAddress = sourceAddress; + return this; + } + + public IPv6Address getDestinationAddress() { + return destinationAddress; + } + + public IPv6 setDestinationAddress(IPv6Address destinationAddress) { + this.destinationAddress = destinationAddress; + return this; + } + + @Override + public byte[] serialize() { + // Get the raw bytes of the payload we encapsulate. + byte[] payloadData = null; + if (this.payload != null) { + this.payload.setParent(this); + payloadData = this.payload.serialize(); + /* + * If we forgot to include the IpProtocol before serializing, + * try to ascertain what it is from the payload. If it's not + * a payload type we know about, we'll throw an exception. + */ + if ((this.nextHeader == null || this.nextHeader.equals(IpProtocol.NONE))) { + if (IPv6.nextHeaderClassMap.containsValue(this.payload.getClass())) { + Set<Map.Entry<IpProtocol, Class<? extends IPacket>>> entries = IPv6.nextHeaderClassMap.entrySet(); + for (Map.Entry<IpProtocol, Class<? extends IPacket>> m : entries) { + if (m.getValue().equals(this.payload.getClass())) { + this.setNextHeader(m.getKey()); + log.warn("Setting previously unset IPv6 'next header' to {} as detected by payload {}", m.getKey(), this.getPayload().getClass().toString()); + break; + } + } + } else if (this.payload instanceof Data) { + /* we're good -- there shouldn't be an IpProtocol set it it's just data */ + } else { + throw new IllegalArgumentException("IpProtocol is unset in IPv6 packet " + this.toString() + ". Unable to determine payload type to set for payload " + this.getPayload().getClass().toString()); + } + } + } + // Update our internal payload length. + this.payloadLength = (short) ((payloadData != null) ? payloadData.length : 0); + // Create a byte buffer to hold the IPv6 packet structure. + byte[] data = new byte[HEADER_LENGTH + this.payloadLength]; + ByteBuffer bb = ByteBuffer.wrap(data); + // Add header fields to the byte buffer in the correct order. + // Fear not the bit magic that must occur. + bb.put((byte) (((this.version & 0xF) << 4) | + ((this.trafficClass & 0xF0) >>> 4))); + bb.put((byte) (((this.trafficClass & 0xF) << 4) | + ((this.flowLabel & 0xF0000) >>> 16))); + bb.putShort((short) (this.flowLabel & 0xFFFF)); + bb.putShort(this.payloadLength); + bb.put((byte) this.nextHeader.getIpProtocolNumber()); + bb.put(this.hopLimit); + bb.put(this.sourceAddress.getBytes()); + bb.put(this.destinationAddress.getBytes()); + // Add the payload to the byte buffer, if necessary. + if (payloadData != null) + bb.put(payloadData); + // We're done! Return the data. + return data; + } + + @Override + public String toString() { + return "IPv6 [version=" + version + ", trafficClass=" + trafficClass + + ", flowLabel=" + flowLabel + ", payloadLength=" + + payloadLength + ", nextHeader=" + nextHeader + ", hopLimit=" + + hopLimit + ", sourceAddress=" + sourceAddress + + ", destinationAddress=" + destinationAddress + ", parent=" + + parent + ", payload=" + payload + "]"; + } + + @Override + public IPacket deserialize(byte[] data, int offset, int length) + throws PacketParsingException { + // Wrap the data in a byte buffer for easier retrieval. + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + // Retrieve values from IPv6 header. + byte firstByte = bb.get(); + byte secondByte = bb.get(); + this.version = (byte) ((firstByte & 0xF0) >>> 4); + if (this.version != 6) { + throw new PacketParsingException( + "Invalid version for IPv6 packet: " + + this.version); + } + this.trafficClass = (byte) (((firstByte & 0xF) << 4) | + ((secondByte & 0xF0) >>> 4)); + this.flowLabel = ((secondByte & 0xF) << 16) | + (bb.getShort() & 0xFFFF); + this.payloadLength = bb.getShort(); + this.nextHeader = IpProtocol.of(bb.get()); + this.hopLimit = bb.get(); + byte[] sourceAddress = new byte[16]; + bb.get(sourceAddress, 0, 16); + byte[] destinationAddress = new byte[16]; + bb.get(destinationAddress, 0, 16); + this.sourceAddress = IPv6Address.of(sourceAddress); + this.destinationAddress = IPv6Address.of(destinationAddress); + // Retrieve the payload, if possible. + IPacket payload; + if (IPv6.nextHeaderClassMap.containsKey(this.nextHeader)) { + Class<? extends IPacket> clazz = IPv6.nextHeaderClassMap.get(this.nextHeader); + try { + payload = clazz.newInstance(); + } catch (Exception e) { + throw new RuntimeException("Error parsing payload for IPv6 packet", e); + } + } else { + payload = new Data(); + } + // Deserialize as much of the payload as we can (hopefully all of it). + this.payload = payload.deserialize(data, bb.position(), + Math.min(this.payloadLength, bb.limit() - bb.position())); + this.payload.setParent(this); + // We're done! + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime + * result + + ((destinationAddress == null) ? 0 : destinationAddress + .hashCode()); + result = prime * result + flowLabel; + result = prime * result + hopLimit; + result = prime * result + + ((nextHeader == null) ? 0 : nextHeader.hashCode()); + result = prime * result + payloadLength; + result = prime * result + + ((sourceAddress == null) ? 0 : sourceAddress.hashCode()); + result = prime * result + trafficClass; + result = prime * result + version; + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof IPv6)) + return false; + IPv6 other = (IPv6) obj; + if (destinationAddress == null) { + if (other.destinationAddress != null) + return false; + } else if (!destinationAddress.equals(other.destinationAddress)) + return false; + if (flowLabel != other.flowLabel) + return false; + if (hopLimit != other.hopLimit) + return false; + if (nextHeader == null) { + if (other.nextHeader != null) + return false; + } else if (!nextHeader.equals(other.nextHeader)) + return false; + if (payloadLength != other.payloadLength) + return false; + if (sourceAddress == null) { + if (other.sourceAddress != null) + return false; + } else if (!sourceAddress.equals(other.sourceAddress)) + return false; + if (trafficClass != other.trafficClass) + return false; + if (version != other.version) + return false; + return true; + } } diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 748516618cc16b45ed2feddfe99642668ca6a7b7..0bb86f00d0031948b33f5ef21382f50d09694aa3 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -461,7 +461,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertNotSame(d1.getDeviceKey(), d2.getDeviceKey()); assertEquals(MockEntityClassifier.testEC, d2.getEntityClass()); assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d2.getVlanId()); - assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses()); + assertArrayEquals(new IPv4Address[] { }, d2.getIPv4Addresses()); assertEquals(2, deviceManager.getAllDevices().size()); verify(mockListener); @@ -2517,7 +2517,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); Device d1 = deviceManager.learnDeviceByEntity(e1); - assertArrayEquals(new Integer[0], d1.getIPv4Addresses()); + assertArrayEquals(new IPv4Address[0], d1.getIPv4Addresses()); Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); @@ -3252,7 +3252,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Query for Device2. Make sure we only have the more recent AP // Query for the Device1. Make sure we have the two IPs we stored. d = getSingleDeviceFromDeviceManager(2L); - assertArrayEquals(new Integer[0], d.getIPv4Addresses()); + assertArrayEquals(new IPv4Address[0], d.getIPv4Addresses()); assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d.getVlanId()); swp = new SwitchPort(DatapathId.of(4L), OFPort.of(5)); assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints()); diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 595a69d20eb968d5358b83bf48b0ef9851c1c08c..51f1faefb541b21acefbe0151385aa1a7277ad9f 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -45,6 +45,7 @@ import net.floodlightcontroller.packet.Data; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; @@ -96,12 +97,17 @@ public class ForwardingTest extends FloodlightTestCase { protected MockThreadPoolService threadPool; protected IOFSwitch sw1, sw2; protected OFFeaturesReply swFeatures; - protected IDevice srcDevice, dstDevice1, dstDevice2; + protected IDevice srcDevice, dstDevice1, dstDevice2; /* reuse for IPv4 and IPv6 */ protected OFPacketIn packetIn; + protected OFPacketIn packetInIPv6; protected OFPacketOut packetOut; + protected OFPacketOut packetOutIPv6; protected OFPacketOut packetOutFlooded; + protected OFPacketOut packetOutFloodedIPv6; protected IPacket testPacket; + protected IPacket testPacketIPv6; protected byte[] testPacketSerialized; + protected byte[] testPacketSerializedIPv6; protected int expected_wildcards; protected Date currentDate; private MockSyncService mockSyncService; @@ -115,25 +121,6 @@ public class ForwardingTest extends FloodlightTestCase { cntx = new FloodlightContext(); // Module loader setup - /* - Collection<Class<? extends IFloodlightModule>> mods = new ArrayList<Class<? extends IFloodlightModule>>(); - Collection<IFloodlightService> mockedServices = new ArrayList<IFloodlightService>(); - mods.add(Forwarding.class); - routingEngine = createMock(IRoutingService.class); - topology = createMock(ITopologyService.class); - mockedServices.add(routingEngine); - mockedServices.add(topology); - FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader(); - fml.setupModules(mods, mockedServices); - mockFloodlightProvider = - (MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class); - deviceManager = - (MockDeviceManager) fml.getModuleByName(MockDeviceManager.class); - threadPool = - (MockThreadPoolService) fml.getModuleByName(MockThreadPoolService.class); - forwarding = - (Forwarding) fml.getModuleByName(Forwarding.class); - */ mockFloodlightProvider = getMockFloodlightProvider(); forwarding = new Forwarding(); threadPool = new MockThreadPoolService(); @@ -143,7 +130,6 @@ public class ForwardingTest extends FloodlightTestCase { mockSyncService = new MockSyncService(); DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); - FloodlightModuleContext fmc = new FloodlightModuleContext(); fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider); @@ -211,12 +197,27 @@ public class ForwardingTest extends FloodlightTestCase { .setDestinationPort((short) 5001) .setPayload(new Data(new byte[] {0x01})))); - + testPacketIPv6 = new Ethernet() + .setDestinationMACAddress("00:11:22:33:44:55") + .setSourceMACAddress("00:44:33:22:11:00") + .setEtherType(EthType.IPv6) + .setPayload( + new IPv6() + .setHopLimit((byte) 128) + .setSourceAddress(IPv6Address.of(1, 1)) + .setDestinationAddress(IPv6Address.of(2, 2)) + .setNextHeader(IpProtocol.UDP) + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); currentDate = new Date(); // Mock Packet-in testPacketSerialized = testPacket.serialize(); + testPacketSerializedIPv6 = testPacketIPv6.serialize(); + packetIn = factory.buildPacketIn() .setMatch(factory.buildMatch() .setExact(MatchField.IN_PORT, OFPort.of(1)) @@ -232,6 +233,22 @@ public class ForwardingTest extends FloodlightTestCase { .setBufferId(OFBufferId.NO_BUFFER) .setData(testPacketSerialized) .setReason(OFPacketInReason.NO_MATCH) + .build(); + packetInIPv6 = factory.buildPacketIn() + .setMatch(factory.buildMatch() + .setExact(MatchField.IN_PORT, OFPort.of(1)) + .setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00")) + .setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55")) + .setExact(MatchField.ETH_TYPE, EthType.IPv6) + .setExact(MatchField.IPV6_SRC, IPv6Address.of(1, 1)) + .setExact(MatchField.IPV6_DST, IPv6Address.of(2, 2)) + .setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, TransportPort.of(5000)) + .setExact(MatchField.UDP_DST, TransportPort.of(5001)) + .build()) + .setBufferId(OFBufferId.NO_BUFFER) + .setData(testPacketSerializedIPv6) + .setReason(OFPacketInReason.NO_MATCH) .build(); // Mock Packet-out @@ -244,6 +261,13 @@ public class ForwardingTest extends FloodlightTestCase { .setData(testPacketSerialized) .setXid(15) .build(); + packetOutIPv6 = factory.buildPacketOut() + .setBufferId(this.packetInIPv6.getBufferId()) + .setActions(poactions) + .setInPort(OFPort.of(1)) + .setData(testPacketSerializedIPv6) + .setXid(15) + .build(); // Mock Packet-out with OFPP_FLOOD action poactions = new ArrayList<OFAction>(); @@ -255,11 +279,25 @@ public class ForwardingTest extends FloodlightTestCase { .setActions(poactions) .setData(testPacketSerialized) .build(); - + packetOutFloodedIPv6 = factory.buildPacketOut() + .setBufferId(this.packetInIPv6.getBufferId()) + .setInPort(packetInIPv6.getMatch().get(MatchField.IN_PORT)) + .setXid(17) + .setActions(poactions) + .setData(testPacketSerializedIPv6) + .build(); + } + + void removeDeviceFromContext() { IFloodlightProviderService.bcStore. - put(cntx, - IFloodlightProviderService.CONTEXT_PI_PAYLOAD, - (Ethernet)testPacket); + remove(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + IFloodlightProviderService.bcStore. + remove(cntx, + IDeviceService.CONTEXT_SRC_DEVICE); + IFloodlightProviderService.bcStore. + remove(cntx, + IDeviceService.CONTEXT_DST_DEVICE); } enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 }; @@ -313,6 +351,68 @@ public class ForwardingTest extends FloodlightTestCase { dstDevice2); } verify(topology); + + IFloodlightProviderService.bcStore. + put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet)testPacket); + } + + public void learnDevicesIPv6(DestDeviceToLearn destDeviceToLearn) { + // Build src and dest devices + MacAddress dataLayerSource = ((Ethernet)testPacketIPv6).getSourceMACAddress(); + MacAddress dataLayerDest = + ((Ethernet)testPacketIPv6).getDestinationMACAddress(); + IPv6Address networkSource = + ((IPv6)((Ethernet)testPacketIPv6).getPayload()). + getSourceAddress(); + IPv6Address networkDest = + ((IPv6)((Ethernet)testPacketIPv6).getPayload()). + getDestinationAddress(); + + reset(topology); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + replay(topology); + + srcDevice = + deviceManager.learnEntity(dataLayerSource, VlanVid.ZERO, + IPv4Address.NONE, networkSource, + DatapathId.of(1), OFPort.of(1)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_SRC_DEVICE, + srcDevice); + if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) { + dstDevice1 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + IPv4Address.NONE, networkDest, + DatapathId.of(2), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice1); + } + if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) { + dstDevice2 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + IPv4Address.NONE, networkDest, + DatapathId.of(1), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice2); + } + verify(topology); + + IFloodlightProviderService.bcStore. + put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet)testPacketIPv6); } @Test @@ -381,6 +481,78 @@ public class ForwardingTest extends FloodlightTestCase { OFMessage m = wc2.getValue(); assert (m instanceof OFFlowMod); assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardMultiSwitchPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.DEVICE1); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(2L)); + List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3))); + route.setPath(nptList); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetInIPv6.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(action.getPort()) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L << 52)) + .setPriority(1) + .build(); + OFFlowMod fm2 = fm1.createBuilder().build(); + + sw1.write(capture(wc1)); + expectLastCall().anyTimes(); + sw2.write(capture(wc2)); + expectLastCall().anyTimes(); + + reset(topology); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))).andReturn(true).anyTimes(); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod. + assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod. + + List<OFMessage> msglist = wc1.getValues(); + + for (OFMessage m: msglist) { + if (m instanceof OFFlowMod) + assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m)); + else if (m instanceof OFPacketOut) { + assertTrue(OFMessageUtils.equalsIgnoreXid(packetOutIPv6, m)); + } + } + + OFMessage m = wc2.getValue(); + assert (m instanceof OFFlowMod); + assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2)); + + removeDeviceFromContext(); } @Test @@ -433,9 +605,66 @@ public class ForwardingTest extends FloodlightTestCase { assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1)); assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOut)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardSingleSwitchPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.DEVICE2); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetInIPv6.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(OFPort.of(3)) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L<< 52)) + .setPriority(1) + .build(); + + // Record expected packet-outs/flow-mods + sw1.write(capture(wc1)); + expectLastCall().once(); + sw1.write(capture(wc2)); + expectLastCall().once(); + + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(wc2.hasCaptured()); + + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1)); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOutIPv6)); + + removeDeviceFromContext(); } /*TODO OFMessageDamper broken due to XID variability in OFMessages... need to fix @Test */ + /*TODO make an IPv6 test for this once OFMessageDamper fixed */ public void testFlowModDampening() throws Exception { learnDevices(DestDeviceToLearn.DEVICE2); @@ -488,6 +717,8 @@ public class ForwardingTest extends FloodlightTestCase { forwarding.receive(sw1, this.packetIn, cntx); forwarding.receive(sw1, this.packetIn, cntx); verify(sw1, routingEngine); + + removeDeviceFromContext(); } @Test @@ -517,6 +748,38 @@ public class ForwardingTest extends FloodlightTestCase { assertTrue(wc1.hasCaptured()); assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFlooded)); + + removeDeviceFromContext(); } -} + @Test + public void testForwardNoPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.NONE); + + // Set no destination attachment point or route + // expect no Flow-mod but expect the packet to be flooded + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + + // Reset mocks, trigger the packet in, and validate results + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), + OFPort.of(anyShort()))) + .andReturn(true) + .anyTimes(); + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) + .andReturn(true).anyTimes(); + // Reset XID to expected (dependent on prior unit tests) + sw1.write(capture(wc1)); + expectLastCall().once(); + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFloodedIPv6)); + + removeDeviceFromContext(); + } +} \ No newline at end of file