/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
*
* 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 org.sdnplatform.forwarding;
import static org.easymock.EasyMock.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.easymock.Capture;
import org.easymock.CaptureType;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFType;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionDataLayerDestination;
import org.openflow.protocol.action.OFActionDataLayerSource;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.action.OFActionStripVirtualLan;
import org.openflow.protocol.action.OFActionVirtualLanIdentifier;
import org.openflow.util.HexString;
import org.openflow.util.U8;
import org.sdnplatform.IBetterOFSwitch;
import org.sdnplatform.addressspace.BetterEntityClass;
import org.sdnplatform.addressspace.IAddressSpaceManagerService;
import org.sdnplatform.core.ListenerContext;
import org.sdnplatform.core.IControllerService;
import org.sdnplatform.core.IOFSwitch;
import org.sdnplatform.core.module.ModuleContext;
import org.sdnplatform.core.test.MockControllerProvider;
import org.sdnplatform.core.test.MockThreadPoolService;
import org.sdnplatform.core.util.AppCookie;
import org.sdnplatform.counter.CounterStore;
import org.sdnplatform.counter.ICounterStoreService;
import org.sdnplatform.devicemanager.IDevice;
import org.sdnplatform.devicemanager.IDeviceService;
import org.sdnplatform.devicemanager.IEntityClass;
import org.sdnplatform.devicemanager.IEntityClassifierService;
import org.sdnplatform.devicemanager.SwitchPort;
import org.sdnplatform.devicemanager.IDeviceService.DeviceField;
import org.sdnplatform.devicemanager.internal.DefaultEntityClassifier;
import org.sdnplatform.devicemanager.internal.Entity;
import org.sdnplatform.devicemanager.test.MockDeviceManager;
import org.sdnplatform.flowcache.BetterFlowCache;
import org.sdnplatform.flowcache.IFlowCacheService;
import org.sdnplatform.flowcache.IFlowReconcileService;
import org.sdnplatform.flowcache.OFMatchReconcile;
import org.sdnplatform.flowcache.OFMatchReconcile.ReconcileAction;
import org.sdnplatform.forwarding.Forwarding;
import org.sdnplatform.forwarding.IRewriteService;
import org.sdnplatform.netvirt.core.VNS;
import org.sdnplatform.netvirt.core.NetVirtExplainPacket;
import org.sdnplatform.netvirt.core.VNSInterface;
import org.sdnplatform.netvirt.manager.INetVirtManagerService;
import org.sdnplatform.packet.ARP;
import org.sdnplatform.packet.Data;
import org.sdnplatform.packet.Ethernet;
import org.sdnplatform.packet.IPacket;
import org.sdnplatform.packet.IPv4;
import org.sdnplatform.packet.UDP;
import org.sdnplatform.restserver.IRestApiService;
import org.sdnplatform.routing.ForwardingBase;
import org.sdnplatform.routing.IRoutingDecision;
import org.sdnplatform.routing.IRoutingService;
import org.sdnplatform.routing.Route;
import org.sdnplatform.storage.IStorageSourceService;
import org.sdnplatform.storage.memory.MemoryStorageSource;
import org.sdnplatform.threadpool.IThreadPoolService;
import org.sdnplatform.topology.IBetterTopologyService;
import org.sdnplatform.topology.ITopologyService;
import org.sdnplatform.topology.NodePortTuple;
import org.sdnplatform.tunnelmanager.ITunnelManagerService;
import org.sdnplatform.util.OFMessageDamper;
import org.sdnplatform.util.TimedCache;
import org.sdnplatform.vendor.OFActionTunnelDstIP;
import static org.junit.Assert.*;
// Don't extend SDN PlatformTestCase or TestCase, use
// @Test, @Before annotations!
public class ForwardingTest {
protected MockControllerProvider mockControllerProvider;
protected ListenerContext cntx;
protected MockThreadPoolService threadPool;
protected MockDeviceManager deviceManager;
protected IRoutingService routingEngine;
protected ITopologyService topology;
protected IBetterTopologyService bettertopology;
protected ITunnelManagerService tunnelManager;
protected Forwarding forwarding;
protected IFlowReconcileService flowReconcileMgr;
protected IRewriteService rewriteService;
protected IRestApiService restApi;
protected IAddressSpaceManagerService addressSpaceMgr;
protected IOFSwitch sw1, sw2, sw3; // swithes for use in multi-action packet out for netVirt-broadcast
protected Capture<OFMessage> wc1, wc2, wc3; // Capture writes to switches
protected Capture<ListenerContext> fc1, fc2, fc3; // Capture writes to switches
//protected OFMessageSafeOutStream out1, out2, out3;
protected IDevice srcDevice, dstDevice1, dstDevice2;
protected ArrayList<IDevice> dstDevices;
protected IDevice device1, device2, device3, device4, device5; // devices added for multi-action packet out for netVirt-broadcast
protected OFPacketIn packetIn;
protected OFPacketIn multicastPacketIn; // added for multi-action packet out for netVirt-broadcast
protected OFPacketIn secondMulticastPacketIn;
protected OFPacketIn packetInUnknownDest;
protected OFPacketIn broadcastPacketIn;
protected OFPacketOut packetOut;
protected IPacket testPacket;
protected IPacket testMulticastPacket; // added for multi-action packet out for netVirt-broadcast
protected IPacket testSecondMulticastPacket;
protected byte[] testPacketSerialized;
protected byte[] testSecondMulticastPacketSerialized;
protected byte[] testMulticastPacketSerialized; // added for multi-action packet out for netVirt-broadcast
protected IPacket testPacketUnknownDest;
protected byte[] testPacketUnknownDestSerialized;
protected IPacket testBroadcastPacket;
protected byte[] testBroadcastPacketSerialized;
protected IRoutingDecision decision;
protected int fastWildcards;
protected int expected_wildcards;
protected Date currentDate;
protected int DIRECT_LINK = 1;
protected int MULTIHOP_LINK = 2;
protected int TUNNEL_LINK = 3;
@Before
public void setUp() throws Exception {
// Mock context
cntx = new ListenerContext();
mockControllerProvider = new MockControllerProvider();
forwarding = new Forwarding();
threadPool = new MockThreadPoolService();
deviceManager = new MockDeviceManager();
routingEngine = createMock(IRoutingService.class);
topology = createMock(ITopologyService.class);
bettertopology = createMock(IBetterTopologyService.class);
//topology = new BetterTopologyManager();
tunnelManager = createMock(ITunnelManagerService.class);
flowReconcileMgr = createMock(IFlowReconcileService.class);
rewriteService = createNiceMock(IRewriteService.class);
restApi = createNiceMock(IRestApiService.class);
addressSpaceMgr = createMock(IAddressSpaceManagerService.class);
DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
BetterFlowCache betterFlowCacheMgr = new BetterFlowCache();
betterFlowCacheMgr.setAppName("netVirt");
ModuleContext fmc = new ModuleContext();
fmc.addService(IControllerService.class,
mockControllerProvider);
fmc.addService(ITopologyService.class, topology);
fmc.addService(IBetterTopologyService.class, bettertopology);
fmc.addService(IThreadPoolService.class, threadPool);
fmc.addService(IRoutingService.class, routingEngine);
fmc.addService(ICounterStoreService.class, new CounterStore());
fmc.addService(IDeviceService.class, deviceManager);
fmc.addService(IFlowCacheService.class, betterFlowCacheMgr);
fmc.addService(ITunnelManagerService.class, tunnelManager);
fmc.addService(IFlowReconcileService.class, flowReconcileMgr);
fmc.addService(IRewriteService.class, rewriteService);
fmc.addService(IRestApiService.class, restApi);
fmc.addService(IAddressSpaceManagerService.class, addressSpaceMgr);
fmc.addService(IEntityClassifierService.class, entityClassifier);
fmc.addService(IStorageSourceService.class, new MemoryStorageSource());
deviceManager.init(fmc);
betterFlowCacheMgr.init(fmc);
threadPool.init(fmc);
forwarding.init(fmc);
entityClassifier.init(fmc);
threadPool.startUp(fmc);
deviceManager.startUp(fmc);
betterFlowCacheMgr.startUp(fmc);
forwarding.startUp(fmc);
entityClassifier.startUp(fmc);
// Mock tunnel service
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong())).andReturn(null).anyTimes();
expect(tunnelManager.getTunnelLoopbackPort(EasyMock.anyLong())).andReturn(null).anyTimes();
expect(tunnelManager.isTunnelSubnet(EasyMock.anyInt())).andReturn(false).anyTimes();
replay(tunnelManager);
// Mock switches
sw1 = EasyMock.createMock(IOFSwitch.class);
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(sw1.getStringId()).andReturn("00:00:00:00:00:00:00:01").anyTimes();
expect(sw1.attributeEquals(IBetterOFSwitch.SUPPORTS_NX_TTL_DECREMENT, true))
.andReturn(true).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
sw2 = EasyMock.createMock(IOFSwitch.class);
expect(sw2.getId()).andReturn(2L).anyTimes();
expect(sw2.getStringId()).andReturn("00:00:00:00:00:00:00:02").anyTimes();
expect(sw2.attributeEquals(IBetterOFSwitch.SUPPORTS_NX_TTL_DECREMENT, true))
.andReturn(true).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
sw3 = EasyMock.createMock(IOFSwitch.class);
expect(sw3.getId()).andReturn(3L).anyTimes();
expect(sw3.getStringId()).andReturn("00:00:00:00:00:00:00:03").anyTimes();
expect(sw3.attributeEquals(IBetterOFSwitch.SUPPORTS_NX_TTL_DECREMENT, true))
.andReturn(true).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes(); //switch 3 belongs to cluster 3. as it is on its own.
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes(); //switch 3 belongs to cluster 3. as it is on its own.
wc1 = new Capture<OFMessage>(CaptureType.ALL); //capture message on switch 1.
wc2 = new Capture<OFMessage>(CaptureType.ALL);
wc3 = new Capture<OFMessage>(CaptureType.ALL);
fc1 = new Capture<ListenerContext>(CaptureType.ALL);
fc2 = new Capture<ListenerContext>(CaptureType.ALL);
fc3 = new Capture<ListenerContext>(CaptureType.ALL);
//fastWilcards mocked as this constant
fastWildcards = OFMatch.OFPFW_IN_PORT | OFMatch.OFPFW_NW_PROTO | OFMatch.OFPFW_TP_SRC
| OFMatch.OFPFW_TP_DST | OFMatch.OFPFW_NW_SRC_ALL | OFMatch.OFPFW_NW_DST_ALL
| OFMatch.OFPFW_NW_TOS;
// for netVirt-broadcast multi-action packet
expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(false).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
expect(sw2.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw2.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw2.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(false).anyTimes();
expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
expect(sw3.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw3.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw3.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(false).anyTimes();
expect(sw3.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw3.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
// Load the switch map
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
switches.put(2L, sw2);
switches.put(3L, sw3);
mockControllerProvider.setSwitches(switches);
// Build test packet
testPacket = new Ethernet()
.setDestinationMACAddress("00:11:22:33:44:55")
.setSourceMACAddress("00:44:33:22:11:00")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.1.1")
.setDestinationAddress("192.168.1.2")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketSerialized = testPacket.serialize();
// Build test packet for multi-action netVirt-broadcast
testMulticastPacket = new Ethernet()
.setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
.setSourceMACAddress("00:11:33:55:77:03")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.10.3")
.setDestinationAddress("192.168.255.255")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testMulticastPacketSerialized = testMulticastPacket.serialize();
// Build test packet for multi-action netVirt-broadcast
testSecondMulticastPacket = new Ethernet()
.setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
.setSourceMACAddress("00:11:33:55:77:01")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.10.1")
.setDestinationAddress("192.168.255.255")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testSecondMulticastPacketSerialized = testSecondMulticastPacket.serialize();
testPacketUnknownDest = new Ethernet()
.setDestinationMACAddress("00:11:22:33:44:66")
.setSourceMACAddress("00:44:33:22:11:00")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.1.1")
.setDestinationAddress("192.168.1.3")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketUnknownDestSerialized = testPacketUnknownDest.serialize();
testBroadcastPacket = new Ethernet()
.setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
.setSourceMACAddress("00:11:22:33:44:66")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.1.1")
.setDestinationAddress("192.168.1.3")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testBroadcastPacketSerialized = testBroadcastPacket.serialize();
// Build src and dest devices
byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress();
byte[] dataLayerDest = ((Ethernet)testPacket).getDestinationMACAddress();
int networkSource = ((IPv4)((Ethernet)testPacket).getPayload()).getSourceAddress();
int networkDest = ((IPv4)((Ethernet)testPacket).getPayload()).getDestinationAddress();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort()))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort(),
EasyMock.anyBoolean()))
.andReturn(true).anyTimes();
replay(topology);
currentDate = new Date();
srcDevice =
deviceManager.learnEntity(Ethernet.toLong(dataLayerSource),
null, networkSource,
1L, 1);
dstDevice1 =
deviceManager.learnEntity(Ethernet.toLong(dataLayerDest),
null, networkDest,
2L, 3);
// Mock Packet-in
packetIn = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized.length);
packetInUnknownDest = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketUnknownDestSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketUnknownDestSerialized.length);
// Mock Packet-in multi-action netVirt-broadcast
multicastPacketIn = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testMulticastPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testMulticastPacketSerialized.length);
// second multicast packet in
secondMulticastPacketIn = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testSecondMulticastPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testSecondMulticastPacketSerialized.length);
broadcastPacketIn = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testBroadcastPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testBroadcastPacketSerialized.length);
// Mock Packet-out
packetOut =
(OFPacketOut) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut.setBufferId(this.packetIn.getBufferId())
.setInPort(this.packetIn.getInPort());
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(new OFActionOutput((short)3, (short) 0xffff));
packetOut.setActions(poactions)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
expected_wildcards = fastWildcards;
expected_wildcards &= ~OFMatch.OFPFW_IN_PORT & ~OFMatch.OFPFW_DL_VLAN &
~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_DL_DST;
expected_wildcards &= ~OFMatch.OFPFW_DL_TYPE; // & ~OFMatch.OFPFW_NW_SRC_MASK & ~OFMatch.OFPFW_NW_DST_MASK;
// Mock decision
decision = createMock(IRoutingDecision.class);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(1L, (short)1)).atLeastOnce();
dstDevices = new ArrayList<IDevice>();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
IRoutingDecision.rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, decision);
IDeviceService.fcStore.
put(cntx,
IDeviceService.CONTEXT_SRC_DEVICE,
srcDevice);
// set decision.getRoutingAction() based on test case
// Set SRC_IFACCES in context
List<VNSInterface> srcIfaces = new ArrayList<VNSInterface>();
VNS netVirt = new VNS("default");
srcIfaces.add(new VNSInterface("testSrcIface1", netVirt, null, null));
INetVirtManagerService.bcStore.put(cntx, INetVirtManagerService.CONTEXT_SRC_IFACES, srcIfaces);
// Mock default behavior for getSwitchPortVlanMode
expect(rewriteService.getSwitchPortVlanMode(anyObject(SwitchPort.class),
anyObject(String.class),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
replay(rewriteService);
}
@After
public void tearDown() {
verify(tunnelManager);
}
/*
* Verifies that ofm is a PacketOut that has ports.size() output actions,
* involving the ports specified in the action. It also verifies that the
* packet date matches packetData. If packetData is null we simply ignore
* it
*/
protected void assertPacketOut(OFMessage ofm, byte[] packetData,
Short[] ports) {
assertNotNull(ofm);
assertNotNull(ports);
assertEquals(true, ofm instanceof OFPacketOut);
OFPacketOut ofpo = (OFPacketOut) ofm;
List<OFAction> actions = ofpo.getActions();
assertEquals(ports.length, actions.size());
HashSet<Short> packetOutPorts = new HashSet<Short>();
for (OFAction action: actions) {
assertEquals(true, action instanceof OFActionOutput);
OFActionOutput a = (OFActionOutput)action;
packetOutPorts.add(a.getPort());
}
Arrays.sort(ports);
Short[] packetOutPortsArray =
packetOutPorts.toArray(new Short[0]);
Arrays.sort(packetOutPortsArray);
assertArrayEquals(ports, packetOutPortsArray);
if (packetData != null) {
// a mismatch here usually indicates that something
// went wrong with rewriting.
assertArrayEquals(packetData, ofpo.getPacketData());
}
}
@Test
public void testForwardMultiSwitchPath() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
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 OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod)
assertEquals(m, fm1);
}
OFMessage m = wc2.getValue();
assertTrue(m.equals(fm2));
}
/**
* In this scenario, the packet-in is from an internal switch port,
* however the internal switch-port is not part of the route. In this
* case, we will setup the correct flow-mod, but we will not send
* packet-out.
* @throws Exception
*/
@Test
public void testNoPacketOutWithFlowMod() throws Exception {
// Mock decision
reset(decision);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
dstDevices = new ArrayList<IDevice>();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(2L, (short)1)).atLeastOnce();
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods.
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
fm2.getMatch().setInputPort((short)10);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)1, true)).andReturn(false).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)1, 1L, (short)1, true)).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)10));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
wc1.reset(); wc2.reset();
// Packet-in is on switch 2, inPort = 1
forwarding.receive(sw2, this.packetIn, cntx);
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
assertTrue(wc1.hasCaptured()); // wc1 should get flowmod.
assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod, and no packet-out.
List<OFMessage> msgList = wc1.getValues();
assertTrue (msgList.size() == 1);
for(OFMessage m: msgList) {
assertTrue(m.equals(fm1));
}
msgList = wc2.getValues();
assertTrue (msgList.size() == 1);
for(OFMessage m: msgList) {
assertTrue(m.equals(fm2));
}
}
/**
* In this scenario, the packet-in is from an internal switch port,
* and the internal switch port is part of the route. In this case,
* there should be a flow-mod on both switches on the path, and
* packet-out on the second switch.
* @throws Exception
*/
@Test
public void testPacketOutWithFlowModOnIntermedaiteSwitch() throws Exception {
// Mock decision
reset(decision);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
dstDevices = new ArrayList<IDevice>();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(2L, (short)1)).atLeastOnce();
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods.
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)1, true)).andReturn(false).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)1, 1L, (short)1, true)).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
wc1.reset(); wc2.reset();
// Packet-in is on switch 2, inPort = 1
forwarding.receive(sw2, this.packetIn, cntx);
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
assertTrue(wc1.hasCaptured()); // wc1 should get flowmod.
assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod, and no packet-out.
List<OFMessage> msgList = wc1.getValues();
assertTrue (msgList.size() == 1);
for(OFMessage m: msgList) {
assertTrue(m.equals(fm1));
}
msgList = wc2.getValues();
assertTrue (msgList.size() == 2);
for(OFMessage m: msgList) {
if (m instanceof OFFlowMod)
assertTrue(m.equals(fm2));
else if (m instanceof OFPacketOut)
assertEquals(m, packetOut);
}
}
/**
* This test checks to see if the flow-mods are NOT installed if the
* packet-in comes in on a wrong attachment point port. If the packet-in
* switch port is an attachment point port and is not present on the
* route returned by topology, then the flow-mod will not be installed,
* and packet out will not be sent.
* @throws Exception
*/
@Test
public void testIgnoreFlowModOnIncorrectPacketInPort() throws Exception {
// Mock the sdnplatform provider service
IControllerService bcp = createMock(IControllerService.class);
forwarding.setControllerProvider(bcp);
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testPacket);
expect(bcp.getOFMessageFactory()).andReturn(mockControllerProvider.getOFMessageFactory()).anyTimes();
expect(bcp.injectOfMessage(sw1, getFakeArpPi(this.packetIn,
(Ethernet)testPacket, null, null))).andReturn(true);
expectLastCall().times(2);
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
switches.put(2L, sw2);
switches.put(3L, sw3);
expect(bcp.getSwitches()).andReturn(switches).anyTimes();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isBroadcastDomainPort(1L, (short)1, true))
.andReturn(false).anyTimes();
expect(topology.getAllowedIncomingBroadcastPort(1L, (short)1, true))
.andReturn(new NodePortTuple(1L, (short)1)).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)10));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology, bcp);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
verify(bcp);
// No flow-mod or packet-out was received on either switch.
assertFalse(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
}
private void setupDevice2() {
byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress();
byte[] dataLayerDest =
((Ethernet)testPacket).getDestinationMACAddress();
int networkSource =
((IPv4)((Ethernet)testPacket).getPayload()).
getSourceAddress();
int networkDest =
((IPv4)((Ethernet)testPacket).getPayload()).
getDestinationAddress();
deviceManager.startUp(null);
srcDevice =
deviceManager.learnEntity(Ethernet.toLong(dataLayerSource),
null, networkSource,
1L, 1);
dstDevice2 =
deviceManager.learnEntity(Ethernet.toLong(dataLayerDest),
null, networkDest,
1L, 3);
}
@Test
public void testForwardSingleSwitchPath() throws Exception {
setupDevice2();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as local and Mock route
dstDevices.add(dstDevice2);
Route route = new Route(1L, 1L);
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice2.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)3)).anyTimes();
replay(sw1, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, routingEngine, decision);
}
@Test
public void testFlowDampening() throws Exception {
int timeout = 40;
int timeToSleep = 50;
forwarding.setMessageDamper(
new OFMessageDamper(100,
EnumSet.of(OFType.FLOW_MOD),
timeout));
setupDevice2();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as local and Mock route
dstDevices.add(dstDevice2);
Route route = new Route(1L, 1L);
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice2.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// WE CANNOT USE MULTIPLE SWITCHES BECAUSE EASYMOCK DOESN'T ALLOW
// US TO OVERWRITE hashCode() and equals()
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
expectLastCall().times(2);
sw1.write(packetOut, cntx);
expectLastCall().times(4);
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)3)).anyTimes();
replay(sw1, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
forwarding.receive(sw1, this.packetIn, cntx);
forwarding.receive(sw1, this.packetIn, cntx);
Thread.sleep(timeToSleep);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, routingEngine, decision);
}
@Test
public void testForwardWithNoOfppTable() throws Exception {
setupDevice2();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
reset(topology);
// Set OFPP_FLOOD not supported
resetToNice(sw1);
//expect(sw1.getOutputStream()).andReturn(out1).anyTimes();
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(false).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
// Set destination as local and Mock route
dstDevices.add(dstDevice2);
Route route = new Route(1L, 1L);
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice2.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
int wc = expected_wildcards & ~OFMatch.OFPFW_NW_SRC_MASK
& ~OFMatch.OFPFW_NW_DST_MASK;
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wc))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Set expected packet-out
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(new OFActionOutput((short) 3, (short) 0xffff));
packetOut.setActions(poactions)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
// Reset mocks, trigger the packet in, and validate results
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
replay(sw1, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, routingEngine, decision);
}
@Test
public void testForwardWithNoL3Match() throws Exception {
setupDevice2();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set OFPP_FLOOD not supported
resetToNice(sw1);
reset(topology);
//expect(sw1.getOutputStream()).andReturn(out1).anyTimes();
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(false).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
// Set destination as local and Mock route
dstDevices.add(dstDevice2);
Route route = new Route(1L, 1L);
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice2.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(fastWildcards &
~OFMatch.OFPFW_IN_PORT & ~OFMatch.OFPFW_DL_VLAN &
~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_DL_DST))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
// Reset mocks, trigger the packet in, and validate results
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
replay(sw1, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, routingEngine, decision);
}
@Test
public void testForwardWithFastWildcardsAll() throws Exception {
doTestForwardWithFastWildcard(OFMatch.OFPFW_ALL);
}
@Test
public void testForwardWithFastWildcardsNone() throws Exception {
doTestForwardWithFastWildcard(0);
}
protected void doTestForwardWithFastWildcard(int wildcards) throws IOException {
setupDevice2();
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set OFPP_FLOOD not supported
resetToNice(sw1);
reset(topology);
//expect(sw1.getOutputStream()).andReturn(out1).anyTimes();
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(wildcards).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
// Set destination as local and Mock route
dstDevices.add(dstDevice2);
Route route = new Route(1L, 1L);
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice2.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wildcards &
~OFMatch.OFPFW_IN_PORT & ~OFMatch.OFPFW_DL_VLAN &
~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_DL_DST &
~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_SRC_MASK & ~OFMatch.OFPFW_NW_DST_MASK))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
// Reset mocks, trigger the packet in, and validate results
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true)).andReturn(new NodePortTuple(1, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
replay(sw1, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, routingEngine, decision);
}
@Test
public void testForwardNoPath() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
// Set no destination attachment point or route
// expect no Flow-mod or packet out
reset(sw1, sw2, routingEngine);
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, sw2, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision);
}
@Test
public void testForwardWithHints() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(new Integer(expected_wildcards & ~OFMatch.OFPFW_IN_PORT)).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = new OFFlowMod();
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards & ~OFMatch.OFPFW_IN_PORT))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1);
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
sw2.write(fm2, cntx);
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision);
}
@Test
public void testForwardOrFloodNoPath() throws Exception {
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacket);
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).atLeastOnce();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(2L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)3, false)).andReturn(true).anyTimes();
// Set no destination attachment point or route
// expect no Flow-mod
// expect packet-out action to be multi-action packet-out
// Even though we have other other switches in the openflow domain,
// we are giving only one switch to see if the broadcast happens there.
Set<Long> switches = new HashSet<Long>();
switches.add(1L);
switches.add(2L);
expect(topology.getSwitchesInOpenflowDomain(1L, false)).andReturn(switches).anyTimes();
List<Short> ports = Arrays.asList(new Short[] { (short)6, (short)7, (short)100 });
HashSet<Short> portSet = new HashSet<Short>(ports);
expect (sw1.getEnabledPortNumbers()).andReturn(ports).anyTimes();
expect (sw2.getEnabledPortNumbers()).andReturn(ports).anyTimes();
expect (topology.getPorts(1L)).andReturn(portSet).anyTimes();
expect (topology.getPorts(2L)).andReturn(portSet).anyTimes();
expect (topology.getPorts(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getPortsWithLinks(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect (topology.getBroadcastPorts(EasyMock.anyLong(), EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.isConsistent(1L, (short)1, 1L, (short)1, false)).andReturn(true).anyTimes();
// Record expected packet-outs/flow-mods
sw1.write(capture(wc1), capture(fc1));
expectLastCall().once();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().once();
// TODO: verify correct broadcast cache behavior
sw1.updateBroadcastCache(anyLong(), eq((short)1));
expectLastCall().andReturn(false).once();
// Tunnel port number is set to 100 in all the switches.
// However, the tunnel itself is not active -- hence tunnel
// is not knwon to topology.
reset(tunnelManager);
// Mock tunnel service
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong()))
.andReturn((short)100).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, topology, tunnelManager, decision);
assertPacketOut(wc1.getValue(), this.testPacketSerialized,
new Short[] { 6, 7 } );
}
@Test
public void testForwardOrFloodNoPathNoOfppFlood() throws Exception {
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacket);
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).atLeastOnce();
// Set OFPP_FLOOD not supported
resetToNice(sw1);
reset(topology);
//expect(sw1.getOutputStream()).andReturn(out1).anyTimes();
expect(sw1.getId()).andReturn(1L).anyTimes();
Set<Long> switches = new HashSet<Long>();
switches.add(1L);
switches.add(2L);
expect(topology.getSwitchesInOpenflowDomain(1L, false)).andReturn(switches).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)3, false)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(anyLong(), anyShort())).andReturn(true).anyTimes();
expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(fastWildcards).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_REQUIRES_L3_MATCH)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(false).anyTimes();
// Set no destination attachment point or route
// expect no Flow-mod
// expect packet-out action to be multi-action packet out
List<Short> ports = Arrays.asList(new Short[] { (short)6, (short)7, (short)100 });
expect (sw1.getEnabledPortNumbers()).andReturn(ports).anyTimes();
expect (sw2.getEnabledPortNumbers()).andReturn(ports).anyTimes();
HashSet<Short> portSet = new HashSet<Short>(ports);
expect (topology.getPorts(1L)).andReturn(portSet).anyTimes();
expect (topology.getPorts(2L)).andReturn(portSet).anyTimes();
expect (topology.getPorts(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getPortsWithLinks(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getBroadcastPorts(EasyMock.anyLong(), EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.isConsistent(1L, (short)1, 1L, (short)1, false)).andReturn(true).anyTimes();
// Record expected packet-outs/flow-mods
sw1.write(capture(wc1), capture(fc1));
sw2.write(capture(wc2), capture(fc2));
// Tunnel port number is set to 100 in all the switches.
// However, the tunnel itself is not active -- hence tunnel
// is not knwon to topology.
reset(tunnelManager);
// Mock tunnel service
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong()))
.andReturn((short)100).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, topology, tunnelManager, decision);
assertPacketOut(wc1.getValue(), this.testPacketSerialized,
new Short[] { 6, 7 } );
assertPacketOut(wc2.getValue(), this.testPacketSerialized,
new Short[] { 6, 7 } );
}
@Test
public void testForwardOrFloodWithPath() throws Exception {
Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
Capture<ListenerContext> bc1 = new Capture<ListenerContext>(CaptureType.ALL);
Capture<ListenerContext> bc2 = new Capture<ListenerContext>(CaptureType.ALL);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacket);
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(AppCookie.makeCookie(2, 0))
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1);
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
sw1.write(capture(wc1), capture(bc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(bc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isBroadcastDomainPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(false).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision);
List<OFMessage> msglist = wc1.getValues();
for (OFMessage m: msglist) {
if (m instanceof OFFlowMod)
assertTrue(m.equals(fm1));
else if (m instanceof OFPacketOut)
assertTrue(m.equals(packetOut));
}
OFMessage m = wc2.getValue();
assertTrue(m.equals(fm2));
}
@Test
public void testFakeArpFromAHost() throws Exception {
IControllerService bcp = createMock(IControllerService.class);
forwarding.setControllerProvider(bcp);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacketUnknownDest);
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(bcp.getOFMessageFactory()).andReturn(mockControllerProvider.getOFMessageFactory()).anyTimes();
expect(bcp.injectOfMessage(sw1, getFakeArpPi(packetInUnknownDest, (Ethernet)testPacketUnknownDest, null, null))).andReturn(true);
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
expect(bcp.getSwitches()).andReturn(switches).anyTimes();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isBroadcastDomainPort(1L, (short)1, true)).andReturn(false).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, decision, bcp, topology);
forwarding.receive(sw1, this.packetInUnknownDest, cntx);
verify(sw1);
verify(bcp);
verify(decision);
}
@Test
public void testFakeArpFromABroadcastDomain() throws Exception {
IControllerService bcp = createMock(IControllerService.class);
forwarding.setControllerProvider(bcp);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacketUnknownDest);
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(bcp.getOFMessageFactory()).andReturn(mockControllerProvider.getOFMessageFactory()).anyTimes();
expect(bcp.injectOfMessage(sw2, getFakeArpPi(packetInUnknownDest, (Ethernet)testPacketUnknownDest, null, null))).andReturn(true);
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
switches.put(2L, sw2);
switches.put(3L, sw3);
expect(bcp.getSwitches()).andReturn(switches).anyTimes();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isBroadcastDomainPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getAllowedIncomingBroadcastPort(1L, (short)1, true)).
andReturn(new NodePortTuple(2L, (short)1)).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, sw2, decision, bcp, topology);
forwarding.receive(sw1, this.packetInUnknownDest, cntx);
verify(sw1, sw2);
verify(bcp);
verify(decision);
}
/**
* Creates a fake ARP PacketIn message
*/
private OFPacketIn getFakeArpPi(OFPacketIn pi, Ethernet eth,
Integer targetIpInput,
Short packetInPortInput) {
IPv4 payload = (IPv4)eth.getPayload();
int targetIp = payload.getDestinationAddress();
if (targetIpInput != null) {
targetIp = targetIpInput.intValue();
}
short packetInPort = pi.getInPort();
if (packetInPortInput != null) {
packetInPort = packetInPortInput.shortValue();
}
IPacket arpRequest = new Ethernet()
.setSourceMACAddress(eth.getSourceMACAddress())
.setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(
new ARP()
.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setHardwareAddressLength((byte) 6)
.setProtocolAddressLength((byte) 4)
.setOpCode(ARP.OP_REQUEST)
.setSenderHardwareAddress(eth.getSourceMACAddress())
.setSenderProtocolAddress(0)
.setTargetHardwareAddress(new byte[] { 0, 0, 0, 0, 0, 0} )
.setTargetProtocolAddress(targetIp));
byte[] arpRequestSerialized = arpRequest.serialize();
OFPacketIn fakePi =
(OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN);
fakePi.setInPort(packetInPort);
fakePi.setBufferId(OFPacketOut.BUFFER_ID_NONE);
fakePi.setReason(OFPacketInReason.NO_MATCH);
fakePi.setPacketData(arpRequestSerialized);
fakePi.setTotalLength((short) arpRequestSerialized.length);
fakePi.setLength(OFPacketIn.MINIMUM_LENGTH);
return fakePi;
}
protected void setupMulticastTest(IEntityClass addressSpace)
throws Exception {
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
expect(sw1.updateBroadcastCache(anyLong(), anyShort()))
.andReturn(false).anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
expect(sw2.updateBroadcastCache(anyLong(), anyShort()))
.andReturn(false).anyTimes();
sw3.write(capture(wc3), capture(fc3));
expectLastCall().anyTimes();
expect(sw3.updateBroadcastCache(anyLong(), anyShort()))
.andReturn(false).anyTimes();
deviceManager.startUp(null);
if (addressSpace != null) {
reset(topology);
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(true).anyTimes();
topology.addListener(deviceManager);
expectLastCall().once();
IEntityClassifierService ecs =
createMock(IEntityClassifierService.class);
expect(ecs.classifyEntity(anyObject(Entity.class)))
.andReturn(addressSpace).anyTimes();
expect(ecs.getKeyFields())
.andReturn(EnumSet.of(DeviceField.VLAN, DeviceField.MAC,
DeviceField.SWITCH, DeviceField.PORT))
.anyTimes();
ecs.addListener(deviceManager);
expectLastCall().anyTimes();
replay(ecs, topology);
deviceManager.setEntityClassifier(ecs);
}
//setup 5 devices based on three switches. for multi-action
// netVirt-broadcast
device1 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:01"),
null,
IPv4.toIPv4Address("192.168.10.1"),
1L, 3);
device2 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:02"),
null,
IPv4.toIPv4Address("192.168.10.2"),
1L, 4);
device3 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:03"),
null,
IPv4.toIPv4Address("192.168.10.3"),
2L, 3);
device4 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:04"),
null,
IPv4.toIPv4Address("192.168.10.4"),
3L, 3);
device5 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:05"),
null,
IPv4.toIPv4Address("192.168.10.5"),
3L, 4);
}
@Test
public void testMulticast() throws Exception {
setupMulticastTest(null);
ArrayList<IDevice> dstDevices = new ArrayList<IDevice>();
dstDevices.add(device1);
dstDevices.add(device2);
dstDevices.add(device3);
dstDevices.add(device4);
dstDevices.add(device5);
// we alternate in handing in an empty interface list and a
// null pointer
List<SwitchPort> multicastIfaces = new ArrayList<SwitchPort>();
// TEST 1: Send a packet from device 3; (switch 2, port #3)
// Verifies the multi-action packet output.
IRoutingDecision mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).atLeastOnce();
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(2L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(multicastIfaces).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testMulticastPacket);
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)4)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(3L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(3L, (short)4)).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(true).anyTimes();
expect(topology.getOpenflowDomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getOpenflowDomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getOpenflowDomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.inSameL2Domain(1L, 1L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(1L, 2L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(2L, 1L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(2L, 2L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(1L, 3L)).andReturn(false).anyTimes();
expect(topology.inSameL2Domain(2L, 3L)).andReturn(false).anyTimes();
expect(topology.inSameL2Domain(3L, 1L)).andReturn(false).anyTimes();
expect(topology.inSameL2Domain(3L, 2L)).andReturn(false).anyTimes();
expect(topology.inSameL2Domain(3L, 3L)).andReturn(true).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(1L, (short)3, 1L, (short)3, true)).andReturn(new NodePortTuple(1L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(1L, (short)3, 2L, (short)3, true)).andReturn(new NodePortTuple(2L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(1L, (short)3, 1L, (short)4, true)).andReturn(new NodePortTuple(1L, (short)4)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)3, true)).andReturn(new NodePortTuple(1L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)4, true)).andReturn(new NodePortTuple(1L, (short)4)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 2L, (short)3, true)).andReturn(new NodePortTuple(2L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 3L, (short)3, true)).andReturn(new NodePortTuple(3L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 3L, (short)4, true)).andReturn(new NodePortTuple(3L, (short)4)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(3L, (short)3, 3L, (short)3, true)).andReturn(new NodePortTuple(3L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(3L, (short)3, 3L, (short)4, true)).andReturn(new NodePortTuple(3L, (short)4)).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)3, 1L, (short)3, true)).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)3, 2L, (short)3, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)3, 1L, (short)4, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)3, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)4, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 2L, (short)3, true)).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 3L, (short)3, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 3L, (short)4, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(3L, (short)3, 3L, (short)3, true)).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(3L, (short)3, 3L, (short)4, true)).andReturn(false).anyTimes();
replay(sw1, sw2, sw3, mydecision, topology);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision);
assertTrue(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
assertPacketOut(wc1.getValue(), this.testMulticastPacketSerialized,
new Short[] { 3,4});
///////////////////////////////////////////////////////////////////////
// TEST 2. Send the same packet again and test if it is not broadcast.
///////////////////////////////////////////////////////////////////////
wc1.reset(); wc2.reset(); wc3.reset();
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testMulticastPacket);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
assertFalse(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
///////////////////////////////////////////////////////////////////////
// TEST 3. Send a packet from a different cluster.
///////////////////////////////////////////////////////////////////////
wc1.reset(); wc2.reset(); wc3.reset();
mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).atLeastOnce(); // for tunnel check
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(3L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(null).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testMulticastPacket);
replay (mydecision);
forwarding.processPacketInMessage(sw3, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision);
assertFalse(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertTrue(wc3.hasCaptured());
assertPacketOut(wc3.getValue(), this.testMulticastPacketSerialized,
new Short[] { 4});
///////////////////////////////////////////////////////////////////////
// TEST 4. Send a different packet.
///////////////////////////////////////////////////////////////////////
wc1.reset(); wc2.reset(); wc3.reset();
mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device1).anyTimes(); // for tunnel check
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(1L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(null).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testSecondMulticastPacket);
replay (mydecision);
forwarding.processPacketInMessage(sw1, this.secondMulticastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
assertPacketOut(wc1.getValue(), this.testSecondMulticastPacketSerialized,
new Short[] { 4 });
assertPacketOut(wc2.getValue(), this.testSecondMulticastPacketSerialized,
new Short[] { 3 });
///////////////////////////////////////////////////////////////////////
// TEST 5. Wait 6 seconds; and then try again.
///////////////////////////////////////////////////////////////////////
// clear out broadcast cache
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
wc1.reset(); wc2.reset(); wc3.reset();
mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).anyTimes(); // for tunnel check
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(2L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(multicastIfaces).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testMulticastPacket);
replay (mydecision);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision);
assertFalse(wc3.hasCaptured());
assertFalse(wc2.hasCaptured());
assertTrue(wc1.hasCaptured());
assertPacketOut(wc1.getValue(), this.testMulticastPacketSerialized,
new Short[] { 3,4});
}
/*
* Test VLAN rewrite, Src and Dst Mac rewrite.
* Focus is on VLAN rewrite.
*/
@Test
public void testMulticastWithRewrites() throws Exception {
Short vlan = 1;
String addressSpaceName = "MyAddressSpace";
BetterEntityClass addressSpace = new BetterEntityClass(addressSpaceName, vlan);
setupMulticastTest(addressSpace);
ArrayList<IDevice> dstDevices = new ArrayList<IDevice>();
dstDevices.add(device1);
dstDevices.add(device2);
dstDevices.add(device3);
dstDevices.add(device4);
dstDevices.add(device5);
List<SwitchPort> multicastIfaces = new ArrayList<SwitchPort>();
multicastIfaces.add(new SwitchPort(2L, 4));
multicastIfaces.add(new SwitchPort(1L, 5));
SwitchPort swp1x3 = new SwitchPort(1L, 3);
SwitchPort swp1x5 = new SwitchPort(1L, 5);
IRoutingDecision mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).anyTimes();
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(2L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(multicastIfaces).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(true).anyTimes();
expect(topology.getOpenflowDomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getOpenflowDomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getOpenflowDomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getOutgoingSwitchPort(2L, (short)3, 2L, (short)4, true))
.andReturn(new NodePortTuple(2L, (short)4)).anyTimes();
expect(topology.getOutgoingSwitchPort(2L, (short)3, 1L, (short)5, true))
.andReturn(new NodePortTuple(1L, (short)5)).anyTimes();
// Setting up expectations for isAttachmetPointPort. All ports are
// attachment point ports, except switch 2, port 4 which we'll toggle
// using an answer object
expect(topology.isAttachmentPointPort(not(eq(2L)), not(eq((short)4)), eq(false)))
.andReturn(true).anyTimes();
class MyAnswer implements IAnswer<Boolean> {
Boolean value;
@Override
public Boolean answer() {
return this.value;
}
}
MyAnswer myAnswer = new MyAnswer();
myAnswer.value = true;
expect(topology.isAttachmentPointPort(eq(2L), eq((short)4), eq(false)))
.andAnswer(myAnswer).anyTimes();
expect(topology.inSameL2Domain(1L, 2L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(2L, 2L)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(3L, 2L)).andReturn(false).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)3, true)).andReturn(new NodePortTuple(1L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 2L, (short)3, true)).andReturn(new NodePortTuple(2L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)4, true)).andReturn(new NodePortTuple(1L, (short)4)).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)3, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 2L, (short)3, true)).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)4, true)).andReturn(false).anyTimes();
// TEST 1: Send a packet from device 3; (switch 2, port #3)
// Verifies the multi-action packet output.
// Transport vlan set. All ports tag ==> we expect rewritten VLAN
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testMulticastPacket.clone());
Ethernet contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
replay(sw1, sw2, sw3, mydecision, topology, rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision, rewriteService);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
Ethernet ethWithVlanSet = (Ethernet)testMulticastPacket.clone();
ethWithVlanSet.setVlanID(vlan);
assertPacketOut(wc1.getValue(), ethWithVlanSet.serialize(),
new Short[] { 3, 4, 5});
assertPacketOut(wc2.getValue(), ethWithVlanSet.serialize(),
new Short[] { 4 });
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
// TEST 2: Same as TEST 1 but no port tags. However, switch 2, port 4
// is an internal port now ==> we expect tagging on it but not on
// other ports
wc1.reset(); wc2.reset(); wc3.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
SwitchPort swp2x4 = new SwitchPort(2L, 4);
expect(rewriteService
.getSwitchPortVlanMode(eq(swp2x4),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
myAnswer.value = false;
replay(rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, rewriteService, mydecision);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
ethWithVlanSet = (Ethernet)testMulticastPacket.clone();
ethWithVlanSet.setVlanID(vlan);
assertPacketOut(wc1.getValue(), this.testMulticastPacketSerialized,
new Short[] { 3, 4, 5});
assertPacketOut(wc2.getValue(), ethWithVlanSet.serialize(),
new Short[] { 4 });
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
myAnswer.value = true;
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
//
// TEST 3: same as TEST 1 but only switch 1, port 3,5 have
// the vlan tagged
// and we rewrite the source MAC
//
wc1.reset(); wc2.reset(); wc3.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(42L).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
SwitchPort otherSwp = and(not(eq(swp1x3)), not(eq(swp1x5)));
expect(rewriteService
.getSwitchPortVlanMode(otherSwp,
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(eq(swp1x3),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(eq(swp1x5),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
replay(rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision, rewriteService);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
Ethernet ethRewrittenSrcMac = (Ethernet)testMulticastPacket.clone();
assertEquals(ethRewrittenSrcMac, testMulticastPacket);
ethRewrittenSrcMac.setSourceMACAddress(Ethernet.toByteArray(42L));
ethWithVlanSet = (Ethernet) ethRewrittenSrcMac.clone();
ethWithVlanSet.setVlanID(vlan);
List<OFMessage> ofms = wc1.getValues();
assertEquals(2, ofms.size());
for (OFMessage ofm: ofms) {
assertEquals(true, ofm instanceof OFPacketOut);
OFPacketOut ofpo = (OFPacketOut)ofm;
// This is a hack to get the right capture to the right
// assertPacketOut call...
if (ofpo.getActions().size() == 1) {
assertPacketOut(ofm, ethRewrittenSrcMac.serialize(),
new Short[] { 4 });
} else {
assertPacketOut(ofm, ethWithVlanSet.serialize(),
new Short[] { 3, 5 });
}
}
assertPacketOut(wc2.getValue(), ethRewrittenSrcMac.serialize(),
new Short[] { 4 });
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
//
// TEST 4: same as TEST 1 but now switch 1, port 3,5 have
// the vlan untagged and we rewrite dst MAC
//
wc1.reset(); wc2.reset(); wc3.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(1L).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
otherSwp = and(not(eq(swp1x3)), not(eq(swp1x5)));
expect(rewriteService
.getSwitchPortVlanMode(otherSwp,
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(eq(swp1x3),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(eq(swp1x5),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
replay(rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision, rewriteService);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
Ethernet ethRewrittenDstMac = (Ethernet)testMulticastPacket.clone();
assertEquals(ethRewrittenDstMac, testMulticastPacket);
ethRewrittenDstMac.setDestinationMACAddress(Ethernet.toByteArray(1L));
ethWithVlanSet = (Ethernet)ethRewrittenDstMac.clone();
ethWithVlanSet.setVlanID(vlan);
ofms = wc1.getValues();
assertEquals(2, ofms.size());
for (OFMessage ofm: ofms) {
assertEquals(true, ofm instanceof OFPacketOut);
OFPacketOut ofpo = (OFPacketOut)ofm;
// This is a hack to get the right capture to the right
// assertPacketOut call...
if (ofpo.getActions().size() == 1) {
assertPacketOut(ofm, ethWithVlanSet.serialize(),
new Short[] { 4 });
} else {
assertPacketOut(ofm, ethRewrittenDstMac.serialize(),
new Short[] { 3, 5 });
}
}
assertPacketOut(wc2.getValue(), ethWithVlanSet.serialize(),
new Short[] { 4 });
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
// TEST 5a: Same as TEST 1
// However, we decrement the TTL by 1
wc1.reset(); wc2.reset(); wc3.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(1).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testMulticastPacket.clone());
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
replay(rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision, rewriteService);
assertTrue(wc1.hasCaptured());
assertTrue(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
ethWithVlanSet = (Ethernet)testMulticastPacket.clone();
ethWithVlanSet.setVlanID(vlan);
IPv4 ip = (IPv4)ethWithVlanSet.getPayload();
short newTtl = U8.f(ip.getTtl());
newTtl -= (short)1;
ip.setTtl(U8.t(newTtl));
ip.resetChecksum();
assertPacketOut(wc1.getValue(), ethWithVlanSet.serialize(),
new Short[] { 3, 4, 5});
assertPacketOut(wc2.getValue(), ethWithVlanSet.serialize(),
new Short[] { 4 });
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
// TEST 5b: Same as TEST 1
// However, we decrement the TTL by 255 ==> Packet should be dropped
wc1.reset(); wc2.reset(); wc3.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(255).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testMulticastPacket.clone());
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
replay(rewriteService);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
verify(sw1, sw2, sw3, mydecision, rewriteService);
assertFalse(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
forwarding.broadcastCache = new TimedCache<Long>(100, 5*1000);
// Make sure we didn't change the PI_PAYLOAD in the context
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testMulticastPacket, contextEth);
}
@Test
public void testBroadcastLoopSuppression() throws Exception {
SwitchPort spt = new SwitchPort(1L, (short)3);
int expectedPktHashCode = ForwardingBase.prime2 * 1 + ((Ethernet) testBroadcastPacket).hashCode();
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
deviceManager.startUp(null);
// setup 2 devices based on one sw1
device1 =
deviceManager.learnEntity(HexString.toLong("00:11:22:33:44:66"),
null,
IPv4.toIPv4Address("192.168.10.1"),
1L, 3);
device2 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:02"),
null,
IPv4.toIPv4Address("192.168.10.2"),
1L, 4);
// TEST 1: Send a packet from device 1; (switch 1, port #3)
// Verifies the packet output.
IRoutingDecision mydecision = createMock(IRoutingDecision.class);
ArrayList<IDevice> dstDevices = new ArrayList<IDevice>();
expect(mydecision.getSourceDevice()).andReturn(device1).anyTimes();
expect(mydecision.getSourcePort()).andReturn(spt).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).anyTimes();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)3, false)).andReturn(true).anyTimes();
Capture<Long> pktHashCode = new Capture<Long>(CaptureType.ALL);
Capture<Short> inPortCapture = new Capture<Short>(CaptureType.ALL);
sw1.updateBroadcastCache(captureLong(pktHashCode), capture(inPortCapture));
expectLastCall().andReturn(false)
.andReturn(true)
.anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testBroadcastPacket);
// Even though we have other other switches in the openflow domain,
// we are giving only one switch to see if the broadcast happens there.
Set<Long> switches = new HashSet<Long>();
switches.add(1L);
switches.add(2L);
expect(topology.getSwitchesInOpenflowDomain(1L, false)).andReturn(switches).anyTimes();
List<Short> ports = Arrays.asList(new Short[] { (short)6, (short)7, (short)100 });
expect (sw1.getEnabledPortNumbers()).andReturn(ports).anyTimes();
HashSet<Short> portSet = new HashSet<Short>(ports);
expect (topology.getPorts(1L)).andReturn(portSet).anyTimes();
expect (topology.getPorts(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getPortsWithLinks(EasyMock.anyLong())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect (topology.getBroadcastPorts(EasyMock.anyLong(), EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(new HashSet<Short>()).anyTimes();
expect (topology.isConsistent(1L, (short)3, 1L, (short)3, false)).andReturn(true).anyTimes();
// Tunnel port number is set to 100 in all the switches.
// However, the tunnel itself is not active -- hence tunnel
// is not knwon to topology.
reset(tunnelManager);
// Mock tunnel service
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong()))
.andReturn((short)100).anyTimes();
replay(sw1, mydecision, topology, tunnelManager);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
verify(sw1, mydecision, topology, tunnelManager);
assertTrue(wc1.hasCaptured());
assertTrue(expectedPktHashCode == pktHashCode.getValue().intValue());
assertTrue(this.broadcastPacketIn.getInPort() == inPortCapture.getValue().shortValue());
assertPacketOut(wc1.getValue(), this.testBroadcastPacketSerialized,
new Short[] { 6, 7});
//assertTrue(actions.get(0) instanceof OFActionOutput);
//OFActionOutput action = (OFActionOutput)actions.get(0);
//assertTrue(action.getPort() == OFPort.OFPP_FLOOD.getValue());
///////////////////////////////////////////////////////////////////////
// TEST 2. Send the same packet again and test if it is suppressed.
///////////////////////////////////////////////////////////////////////
wc1.reset();
pktHashCode.reset();
inPortCapture.reset();
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testBroadcastPacket);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
assertFalse(wc1.hasCaptured());
assertTrue(expectedPktHashCode == pktHashCode.getValue().intValue());
assertTrue(this.broadcastPacketIn.getInPort() == inPortCapture.getValue().shortValue());
}
@Test
public void testBroadcastDrop() throws Exception {
// Mock decision
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)this.testBroadcastPacket);
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).atLeastOnce();
int pktHashCode = ((Ethernet)testBroadcastPacket).hashCode();
expect(sw1.updateBroadcastCache(new Long(pktHashCode), this.broadcastPacketIn.getInPort())).andReturn(false).anyTimes();
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)1, true)).andReturn(false).atLeastOnce();
// Reset mocks, trigger the packet in, and validate results
replay(sw1, topology, decision);
forwarding.receive(sw1, this.broadcastPacketIn, cntx);
verify(sw1, decision);
assertFalse(wc1.hasCaptured());
}
// We get a broadcast packet on switch port (2,1) from host h1.
// Switchport (2,1) is a broadcast domain port.
// h1 on switch-port (1,1) - which is a non-broadcast domain port.
@Test
public void testBroadcastDropOnInconsistentPort() throws Exception {
// Mock decision
// Mock decision
decision = createMock(IRoutingDecision.class);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(2L, (short)1)).atLeastOnce();
dstDevices = new ArrayList<IDevice>();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
IRoutingDecision.rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, decision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)this.testBroadcastPacket);
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).atLeastOnce();
expect(sw2.updateBroadcastCache(anyLong(),
eq(this.broadcastPacketIn.getInPort())))
.andReturn(false).anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isIncomingBroadcastAllowed(2L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.isConsistent(1L, (short)1, 2L, (short)1, true)).andReturn(false).anyTimes();
// Reset mocks, trigger the packet in, and validate results
replay(sw2, topology, decision);
forwarding.receive(sw2, this.broadcastPacketIn, cntx);
verify(sw2, decision);
assertFalse(wc2.hasCaptured());
}
@Test
public void testMulticastForwardWithInterfaces() throws Exception {
setupMulticastTest(null);
ArrayList<IDevice> dstDevices = new ArrayList<IDevice>();
dstDevices.add(device1);
dstDevices.add(device2);
dstDevices.add(device3);
List<SwitchPort> multicastIfaces = new ArrayList<SwitchPort>();
// TEST 1: Send a packet from device 3; (switch 2, port #3)
// Verifies the multi-action packet output.
// There are no attachments on switch 3, hence switch 3 should not receive any packetouts.
IRoutingDecision mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).anyTimes();
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(2L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(multicastIfaces).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testMulticastPacket);
reset(topology);
expect(topology.isIncomingBroadcastAllowed(EasyMock.anyLong(), EasyMock.anyShort(), EasyMock.anyBoolean())).andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(2L, (short)3, 3L, (short)5, true)).andReturn(new NodePortTuple(3L, (short)7)).anyTimes();
expect(topology.getOutgoingSwitchPort(2L, (short)3, 3L, (short)6, true)).andReturn(new NodePortTuple(3L, (short)8)).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(1L).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)4)).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)3)).andReturn(true).anyTimes();
expect(topology.inSameL2Domain(EasyMock.anyLong(), EasyMock.anyLong())).andReturn(true).anyTimes();
expect(topology.getOpenflowDomainId(EasyMock.anyLong(), EasyMock.anyBoolean())).andReturn(1L).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)3, true)).andReturn(new NodePortTuple(1L, (short)3)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 1L, (short)4, true)).andReturn(new NodePortTuple(1L, (short)4)).anyTimes();
expect(topology.getAllowedOutgoingBroadcastPort(2L, (short)3, 2L, (short)3, true)).andReturn(new NodePortTuple(2L, (short)3)).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)3, true)).andReturn(false).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 2L, (short)3, true)).andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(2L, (short)3, 1L, (short)4, true)).andReturn(false).anyTimes();
replay(sw1, sw2, sw3, mydecision, topology);
forwarding.processPacketInMessage(sw2, this.multicastPacketIn, mydecision, cntx);
assertTrue(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertFalse(wc3.hasCaptured());
assertPacketOut(wc1.getValue(), this.testMulticastPacketSerialized,
new Short[] { 3, 4 });
///////////////////////////////////////////////////////////////////////
// TEST 2. Send a different packet from device 3.
// Now, add switch 3, port 5 and 6.
// to the multicast interfaces
// which will be returned by the routing decision.
// In addition, the outgoing switch port for switch 3, port 5 and 6
// is set to ports 7 and 8
// Verify that switch3 receives a packet out.
///////////////////////////////////////////////////////////////////////
wc1.reset(); wc2.reset(); wc3.reset();
multicastIfaces.add(new SwitchPort(3L, 5));
multicastIfaces.add(new SwitchPort(3L, 6));
mydecision = createMock(IRoutingDecision.class);
expect(mydecision.getSourceDevice()).andReturn(device3).anyTimes(); // for tunnel check
expect(mydecision.getSourcePort()).andReturn(new SwitchPort(2L, (short)3)).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.MULTICAST).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getMulticastInterfaces()).andReturn(multicastIfaces).anyTimes();
IRoutingDecision.rtStore.put(cntx, "decision", mydecision);
IControllerService.bcStore.put(cntx, IControllerService.CONTEXT_PI_PAYLOAD, (Ethernet)testSecondMulticastPacket);
replay (mydecision);
forwarding.processPacketInMessage(sw1, this.secondMulticastPacketIn, mydecision, cntx);
assertTrue(wc1.hasCaptured());
assertFalse(wc2.hasCaptured());
assertTrue(wc3.hasCaptured());
assertPacketOut(wc1.getValue(), this.testSecondMulticastPacketSerialized,
new Short[] { 3, 4 });
assertPacketOut(wc3.getValue(), this.testSecondMulticastPacketSerialized,
new Short[] { 7, 8 });
}
@Test
public void testBroadcastWithRewrites() throws Exception {
Short vlan = 42;
SwitchPort spt = new SwitchPort(1L, (short)3);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
deviceManager.startUp(null);
// setup 2 devices based on one sw1
String addressSpaceName = "MyAddressSpace";
BetterEntityClass addressSpace = new BetterEntityClass(addressSpaceName, vlan);
reset(topology);
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(true).anyTimes();
topology.addListener(deviceManager);
expectLastCall().once();
IEntityClassifierService ecs =
createMock(IEntityClassifierService.class);
expect(ecs.classifyEntity(anyObject(Entity.class)))
.andReturn(addressSpace).anyTimes();
expect(ecs.getKeyFields())
.andReturn(EnumSet.of(DeviceField.VLAN, DeviceField.MAC,
DeviceField.SWITCH, DeviceField.PORT))
.anyTimes();
ecs.addListener(deviceManager);
expectLastCall().anyTimes();
replay(ecs, topology);
deviceManager.setEntityClassifier(ecs);
device1 =
deviceManager.learnEntity(HexString.toLong("00:11:22:33:44:66"),
null,
IPv4.toIPv4Address("192.168.10.1"),
1L, 3);
device2 =
deviceManager.learnEntity(HexString.toLong("00:11:33:55:77:02"),
null,
IPv4.toIPv4Address("192.168.10.2"),
1L, 4);
Set<Short> ports = new HashSet<Short>(Arrays.asList(new Short[] {
(short)6,
(short)7,
(short)8,
(short)9,
(short)10
}));
expect (sw1.getEnabledPortNumbers()).andReturn(ports).anyTimes();
// link ports. these will be excluded from the BC
HashSet<Short> linkPorts = new HashSet<Short>();
linkPorts.add((short) 8);
linkPorts.add((short) 9);
linkPorts.add((short) 10);
reset(topology);
expect(topology.getPorts(1L)).andReturn(ports).anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect (topology.getPortsWithLinks(EasyMock.anyLong()))
.andReturn(linkPorts).anyTimes();
// broadcast ports. these will be included in the BC
HashSet<Short> bcPorts = new HashSet<Short>();
bcPorts.add((short) 10);
expect (topology.getBroadcastPorts(EasyMock.anyLong(),
EasyMock.anyLong(),
EasyMock.anyShort(),
EasyMock.anyBoolean()))
.andReturn(bcPorts).anyTimes();
expect (topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect (topology.isConsistent(1L, (short)3, 1L, (short)3, false)).andReturn(true).anyTimes();
// Setting up expectations for isAttachmetPointPort.
// Port 7 is always an attachment point port and we toggle
// whether ports 6 and 10 are
class MyAnswer implements IAnswer<Boolean> {
Boolean value;
@Override
public Boolean answer() {
return this.value;
}
}
MyAnswer myAnswer = new MyAnswer();
myAnswer.value = true;
expect(topology.isAttachmentPointPort(anyLong(), not(eq((short)7)), eq(false)))
.andAnswer(myAnswer).anyTimes();
expect(topology.isAttachmentPointPort(anyLong(), eq((short)7), eq(false)))
.andReturn(true).anyTimes();
IRoutingDecision mydecision = createMock(IRoutingDecision.class);
ArrayList<IDevice> dstDevices = new ArrayList<IDevice>();
expect(mydecision.getSourceDevice()).andReturn(device1).anyTimes();
expect(mydecision.getSourcePort()).andReturn(spt).anyTimes();
expect(mydecision.getDestinationDevices()).andReturn(dstDevices).anyTimes();
expect(mydecision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD).anyTimes();
Set<Long> switches = new HashSet<Long>();
switches.add(1L);
expect(topology.getSwitchesInOpenflowDomain(1L, false)).andReturn(switches).anyTimes();
expect(topology.isIncomingBroadcastAllowed(1L, (short)3, false)).andReturn(true).anyTimes();
expect(sw1.updateBroadcastCache(anyLong(), anyShort()))
.andReturn(false).anyTimes();
byte[] origPktData = testBroadcastPacketSerialized.clone();
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testBroadcastPacket.clone());
Ethernet contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testBroadcastPacket, contextEth);
// TEST 1: Send a packet from device 1; (switch 1, port #3)
// Verifies the packet output.
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
// Tunnel port number is set to 100 in all the switches.
// However, the tunnel itself is not active -- hence tunnel
// is not knwon to topology.
reset(tunnelManager);
// Mock tunnel service
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong()))
.andReturn((short)100).anyTimes();
replay(sw1, mydecision, topology, tunnelManager, rewriteService);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
verify(rewriteService, tunnelManager, topology);
assertArrayEquals(origPktData, testBroadcastPacketSerialized);
assertTrue(wc1.hasCaptured());
Ethernet ethWithVlanSet = (Ethernet)testBroadcastPacket.clone();
ethWithVlanSet.setVlanID(vlan);
assertPacketOut(wc1.getValue(), ethWithVlanSet.serialize(),
new Short[] { 6, 7, 10});
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testBroadcastPacket, contextEth);
//
// TEST 2a: Send a packet from device 1; (switch 1, port #3)
// All untagged. Do TTL decrement (by 1)
wc1.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(1).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
replay(rewriteService);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
verify(rewriteService);
assertTrue(wc1.hasCaptured());
Ethernet ethWithTtlChanged = (Ethernet) this.testBroadcastPacket.clone();
IPv4 ip = (IPv4) ethWithTtlChanged.getPayload();
short newTtl = U8.f(ip.getTtl());
newTtl -= (short)1;
ip.setTtl(U8.t(newTtl));
ip.resetChecksum();
assertPacketOut(wc1.getValue(), ethWithTtlChanged.serialize(),
new Short[] { 6, 7, 10});
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testBroadcastPacket, contextEth);
//
// TEST 2b: Send a packet from device 1; (switch 1, port #3)
// All untagged. Do TTL decrement (by 255). Packet should be dropped
wc1.reset();
resetToDefault(rewriteService);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(null).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(255).atLeastOnce();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
replay(rewriteService);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
verify(rewriteService);
assertEquals(false, wc1.hasCaptured());
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testBroadcastPacket, contextEth);
List<OFMessage> ofms;
//
// TEST 3: Send a packet from device 1; (switch 1, port #3)
// ports 6 is tagged, 7 and 10 are native and thus untagged
// In addition: rewrite MAC addresses
resetToDefault(rewriteService);
wc1.reset();
SwitchPort swp1x6 = new SwitchPort(1L, 6);
expect(rewriteService.getFinalIngressDstMac(cntx))
.andReturn(1L).atLeastOnce();
expect(rewriteService.getFinalEgressSrcMac(cntx))
.andReturn(42L).atLeastOnce();
expect(rewriteService.getTtlDecrement(cntx))
.andReturn(null).atLeastOnce();
SwitchPort otherSwp = not(eq(swp1x6));
expect(rewriteService
.getSwitchPortVlanMode(otherSwp,
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(eq(swp1x6),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
expect(rewriteService
.getSwitchPortVlanMode(anyObject(SwitchPort.class),
eq(addressSpaceName),
anyShort(),
anyBoolean()))
.andReturn(vlan).anyTimes();
replay(rewriteService);
forwarding.processPacketInMessage(sw1, this.broadcastPacketIn, mydecision, cntx);
verify(rewriteService);
assertArrayEquals(origPktData, testBroadcastPacketSerialized);
assertTrue(wc1.hasCaptured());
Ethernet ethRewrittenMac = (Ethernet)testBroadcastPacket.clone();
assertEquals(ethRewrittenMac, testBroadcastPacket);
ethRewrittenMac.setDestinationMACAddress(Ethernet.toByteArray(1L));
ethRewrittenMac.setSourceMACAddress(Ethernet.toByteArray(42L));
ethWithVlanSet = (Ethernet)ethRewrittenMac.clone();
ethWithVlanSet.setVlanID(vlan);
ofms = wc1.getValues();
assertEquals(2, ofms.size());
for (OFMessage ofm: ofms) {
assertEquals(true, ofm instanceof OFPacketOut);
OFPacketOut ofpo = (OFPacketOut)ofm;
// This is a hack to get the right capture to the right
// assertPacketOut call...
if (ofpo.getActions().size() == 2) {
assertPacketOut(ofm, ethRewrittenMac.serialize(),
new Short[] { 7, 10 });
} else {
assertPacketOut(ofm, ethWithVlanSet.serialize(),
new Short[] { 6 });
}
}
contextEth = IControllerService.bcStore
.get(cntx, IControllerService.CONTEXT_PI_PAYLOAD);
assertEquals(testBroadcastPacket, contextEth);
}
class EgressPortConfig {
Short transportVlan;
Short egressVlan;
boolean expectedReturn;
EgressPortConfig(Short tVlan, Short eVlan, boolean expectedReturn) {
this.transportVlan = tVlan;
this.egressVlan = eVlan;
this.expectedReturn = expectedReturn;
}
}
/**
* Test vlan tagging for packetOut.
* transportVlan is set.
* egressVlan is set too.
* packetOut should be sent to the switch.
* @throws IOException
*/
@Test
public void testPushPacketOutToEgressPort1() throws IOException {
EgressPortConfig epCfg = new EgressPortConfig((short)1, (short)2, true);
internalPushPacketOutToEgressPortTest(epCfg);
}
/**
* Test vlan tagging for packetOut
* transportVlan is set.
* egressVlan is null.
* packetOut should be dropped.
* @throws IOException
*/
@Test
public void testPushPacketOutToEgressPort2() throws IOException {
EgressPortConfig epCfg = new EgressPortConfig((short)1, null, false);
internalPushPacketOutToEgressPortTest(epCfg);
}
/**
* Test vlan tagging for packetOut
* transportVlan is null.
* packetOut should be sent without vlan.
* @throws IOException
*/
@Test
public void testPushPacketOutToEgressPort3() throws IOException {
EgressPortConfig epCfg = new EgressPortConfig(null, null, true);
internalPushPacketOutToEgressPortTest(epCfg);
}
/**
* Test vlan tagging for packetOut
* transportVlan is Untagged.
* egressVlan is Untagged
* packetOut should be sent without vlan.
* @throws IOException
*/
@Test
public void testPushPacketOutToEgressPort4() throws IOException {
EgressPortConfig epCfg = new EgressPortConfig((short)-1, (short)-1, true);
internalPushPacketOutToEgressPortTest(epCfg);
}
protected void internalPushPacketOutToEgressPortTest(
EgressPortConfig epConfig)
throws IOException {
String addressSpace = "foobar";
Short vlan = -1;
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
resetToDefault(rewriteService);
if (epConfig.transportVlan != null) {
expect(rewriteService.getSwitchPortVlanMode(
EasyMock.anyObject(SwitchPort.class),
eq(addressSpace),
eq(vlan),
eq(true)))
.andReturn(epConfig.egressVlan).atLeastOnce();
} else {
expect(rewriteService.getSwitchPortVlanMode(
EasyMock.anyObject(SwitchPort.class),
eq(addressSpace),
eq(vlan),
eq(true)))
.andReturn(Ethernet.VLAN_UNTAGGED).atLeastOnce();
}
replay(addressSpaceMgr, rewriteService, sw1);
SwitchPort swp = new SwitchPort(1L, (short)1);
boolean rtCode = forwarding.pushPacketOutToEgressPort(
(Ethernet)this.testPacket,
(short)2,
swp,
false,
addressSpace,
vlan.shortValue(),
cntx,
false);
verify(addressSpaceMgr, rewriteService, sw1);
assertEquals(epConfig.expectedReturn, rtCode);
// If push succeeds, check the packet capture.
if (epConfig.expectedReturn) {
assertTrue(wc1.hasCaptured());
Ethernet ethWithVlanSet = (Ethernet)testPacket.clone();
// Set egressVlan if it is expected.
if (epConfig.egressVlan != null && epConfig.egressVlan != Ethernet.VLAN_UNTAGGED) {
ethWithVlanSet.setVlanID(epConfig.egressVlan);
}
assertPacketOut(wc1.getValue(), ethWithVlanSet.serialize(),
new Short[] { 1 });
}
}
/**
* Test that the route is annotated in the explain packet
* @throws Exception
*/
@Test
public void testRouteAnnotationOfExplainPacket() throws Exception {
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)2));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// Set up the context to indicate that it is an explain packet
NetVirtExplainPacket.ExplainStore.put(cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT, NetVirtExplainPacket.VAL_EXPLAIN_PKT);
NetVirtExplainPacket.ExplainPktRoute epr = new NetVirtExplainPacket.ExplainPktRoute();
NetVirtExplainPacket.ExplainRouteStore.put(cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_ROUTE, epr);
// Set up Mock decision
decision = createMock(IRoutingDecision.class);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(1L, (short)1)).atLeastOnce();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
IRoutingDecision.rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, decision);
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
// Start the replay
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology);
// Call the action function
forwarding.receive(sw1, this.packetIn, cntx);
// Verify that all the replays that were setup did happen
verify(sw1, sw2, routingEngine, decision);
// Finally get the route from the context and verify it
assertTrue(epr.numClusters == 1);
assertTrue(epr.oc.get(0).route.getPath().size() == 4);
assertTrue(epr.oc.get(0).route.getPath().get(2).getPortId() == 1);
assertTrue(epr.oc.get(0).route.getPath().get(1).getPortId() == 2);
// The 2 in equal (2) below represents dpid 00:00:00:00:00:00:00:02 of sw2
assertTrue(epr.oc.get(0).route.getPath().get(2).getNodeId() == 2L);
}
/**
* Verify that route is NOT annotated for non-explain packet
* @throws Exception
*/
@Test
public void testNoRouteAnnotationOfNonExplainPacket() throws Exception {
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)2));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, true)).andReturn(route).atLeastOnce();
// DONT set up the context to indicate that it is an explain packet, but set up its route store
NetVirtExplainPacket.ExplainPktRoute epr = new NetVirtExplainPacket.ExplainPktRoute();
epr.numClusters = -1;
NetVirtExplainPacket.ExplainRouteStore.put(cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_ROUTE, epr);
// Set up Mock decision
decision = createMock(IRoutingDecision.class);
expect(decision.getSourceDevice()).andReturn(srcDevice).atLeastOnce();
expect(decision.getSourcePort()).andReturn(new SwitchPort(1L, (short)1)).atLeastOnce();
expect(decision.getDestinationDevices()).andReturn(dstDevices).atLeastOnce();
IRoutingDecision.rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, decision);
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(expected_wildcards).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Mock topology
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true)).andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, true)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
// Expected Flow-mods
OFActionOutput action = new OFActionOutput((short)2, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1)
.setWildcards(expected_wildcards);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm2.setFlags((short)0); // set flow-mod-removal flag on src switch only
((OFActionOutput)(fm2.getActions().get(0))).setPort((short)3);
((OFActionOutput)packetOut.getActions().get(0)).setPort((short)2);
// Record expected packet-outs/flow-mods
sw2.write(fm2, cntx);
sw1.write(fm1, cntx);
sw1.write(packetOut, cntx);
// Start the replay
replay(sw1, sw2, routingEngine, decision, topology);
// Call the action function
forwarding.receive(sw1, this.packetIn, cntx);
// Verify that all the replays that were setup did happen
verify(sw1, sw2, routingEngine, decision);
// Verify that route is NOT annotated in non-explain packet
// Check that the numClusters is still -1
assertTrue(epr.numClusters == -1);
}
@Test
public void testMacRewriteAction() {
IPacket[] packets = new IPacket[] { testPacket, testMulticastPacket,
testPacketUnknownDest,
testBroadcastPacket };
for (IPacket pkt: packets) {
Ethernet eth = (Ethernet)pkt.clone();
OFAction action = null;
Long curMac = Ethernet.toLong(eth.getDestinationMACAddress());
Long newMac = curMac + 1;
byte[] newMacArray = Ethernet.toByteArray(newMac);
// Dest Mac
assertNull(forwarding.getDstMacRewriteAction(eth.getDestinationMAC().toLong(),
curMac));
assertNull(forwarding.getDstMacRewriteAction(eth.getDestinationMAC().toLong(),
null));
action = forwarding.getDstMacRewriteAction(eth.getDestinationMAC().toLong(),
newMac);
assertEquals(true, action instanceof OFActionDataLayerDestination);
OFActionDataLayerDestination dstMacAction =
(OFActionDataLayerDestination)action;
assertArrayEquals(newMacArray, dstMacAction.getDataLayerAddress());
assertEquals(eth, pkt); // make sure the packet is unchanged
// Source Mac
curMac = Ethernet.toLong(eth.getSourceMACAddress());
newMac = curMac + 1;
newMacArray = Ethernet.toByteArray(newMac);
assertNull(forwarding.getSrcMacRewriteAction(eth.getSourceMAC().toLong(),
curMac));
assertNull(forwarding.getSrcMacRewriteAction(eth.getSourceMAC().toLong(),
null));
action = forwarding.getSrcMacRewriteAction(eth.getSourceMAC().toLong(),
newMac);
assertEquals(true, action instanceof OFActionDataLayerSource);
OFActionDataLayerSource srcMacAction =
(OFActionDataLayerSource)action;
assertArrayEquals(newMacArray, srcMacAction.getDataLayerAddress());
assertEquals(eth, pkt); // make sure the packet is unchanged
}
}
@Test
public void testDecrementTtl() {
IPacket[] packets = new IPacket[] { testPacket, testMulticastPacket,
testPacketUnknownDest,
testBroadcastPacket };
for (IPacket pkt: packets) {
Ethernet eth = (Ethernet)pkt.clone();
boolean notExpired;
IPv4 ip = (IPv4)eth.getPayload();
ip.setChecksum((short)0x1234);
// Make sure the original TTL is as we expect. Just to calibrate
assertEquals("Default TTL for IP packets is different than " +
"expected. Please adjust test", (byte)0x80, ip.getTtl());
notExpired = forwarding.decrementTtl(eth, 1);
assertEquals(true, notExpired);
ip = (IPv4)eth.getPayload();
assertEquals((byte)0x7F, ip.getTtl());
assertEquals(0, ip.getChecksum());
notExpired = forwarding.decrementTtl(eth, 3);
assertEquals(true, notExpired);
ip = (IPv4)eth.getPayload();
assertEquals((byte)0x7C, ip.getTtl());
assertEquals(0, ip.getChecksum());
notExpired = forwarding.decrementTtl(eth, 124);
assertEquals(false, notExpired);
ip = (IPv4)eth.getPayload();
assertEquals(0, ip.getTtl());
assertEquals(0, ip.getChecksum());
// get a fresh clone
eth = (Ethernet)pkt.clone();
notExpired = forwarding.decrementTtl(eth, 200);
assertEquals(false, notExpired);
ip = (IPv4)eth.getPayload();
assertEquals(0, ip.getTtl());
assertEquals(0, ip.getChecksum());
// get a fresh clone
eth = (Ethernet)pkt.clone();
notExpired = forwarding.decrementTtl(eth, 2000);
assertEquals(false, notExpired);
ip = (IPv4)eth.getPayload();
assertEquals(0, ip.getTtl());
assertEquals(0, ip.getChecksum());
}
IPacket nonIpPacket = new Ethernet()
.setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
.setSourceMACAddress("00:11:22:33:44:66")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload( new ARP() );
Ethernet eth = (Ethernet)nonIpPacket;
boolean notExpired = forwarding.decrementTtl(eth, 3);
assertEquals(true, notExpired);
notExpired = forwarding.decrementTtl(eth, 0xFF);
assertEquals(true, notExpired);
}
protected void assertExpectedVlanAction(OFAction action,
Short origVlan, Short newVlan) {
String msg = origVlan.toString() + "-->" + newVlan.toString();
if (origVlan.equals(newVlan))
assertNull(msg, action);
else if (newVlan.equals(Ethernet.VLAN_UNTAGGED)) {
assertEquals(msg, true, action instanceof OFActionStripVirtualLan);
}
else {
assertEquals(msg, true,
action instanceof OFActionVirtualLanIdentifier);
OFActionVirtualLanIdentifier vlanAction =
(OFActionVirtualLanIdentifier)action;
assertEquals(newVlan.shortValue(),
vlanAction.getVirtualLanIdentifier());
}
}
@Test
public void testVlanRewriteRule() {
IPacket[] packets = new IPacket[] { testPacket, testMulticastPacket,
testPacketUnknownDest,
testBroadcastPacket };
Short[] vlans = new Short[] { Ethernet.VLAN_UNTAGGED, 1, 23, 42 };
for (IPacket pkt: packets) {
for (Short vlan: vlans) {
Ethernet orig = (Ethernet)pkt.clone();
orig.setVlanID(vlan);
Ethernet eth = (Ethernet)pkt.clone();
eth.setVlanID(vlan);
assertEquals(orig, eth);
for (Short newVlan: vlans) {
OFAction action;
action = forwarding.getVlanRewriteAction(eth.getVlanID(), newVlan);
assertExpectedVlanAction(action, vlan, newVlan);
assertEquals(orig, eth);
}
assertNull(forwarding.getVlanRewriteAction(eth.getVlanID(), null));
}
}
}
/**
* Tests if the flow mods and the packet-outs for routing through a
* tunnel port have the correct tunnel destination actions in them.
* @throws Exception
*/
@Test
public void testForwardMultiSwitchPathWithTunnelPorts() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
int wc = expected_wildcards & ~OFMatch.OFPFW_NW_SRC_MASK
& ~OFMatch.OFPFW_NW_DST_MASK;
OFFlowMod fm2 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm2.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wc))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Expected Flow-mods
match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
actions = new ArrayList<OFAction>();
// Add tunnel action first
OFActionTunnelDstIP tunnelDstAction =
new OFActionTunnelDstIP(100);
actions.add(tunnelDstAction);
int tunnelActionLength = tunnelDstAction.getLengthU();
// Then add output action
action = new OFActionOutput((short)3, (short)0xffff);
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wc))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH+tunnelActionLength);
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
// Reset the packetout.
packetOut =
(OFPacketOut) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut.setBufferId(this.packetIn.getBufferId())
.setInPort(this.packetIn.getInPort());
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(tunnelDstAction);
poactions.add(new OFActionOutput((short)3, (short) 0xffff));
packetOut.setActions(poactions)
.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH + tunnelActionLength))
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, false)).andReturn(route).anyTimes();
// Mock tunnel service
reset(tunnelManager);
expect(tunnelManager.getTunnelLoopbackPort(EasyMock.anyLong())).andReturn(null).anyTimes();
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(1L)).andReturn(new Short((short)3)).anyTimes();
expect(tunnelManager.getTunnelPortNumber(2L)).andReturn(new Short((short)1)).anyTimes();
expect(tunnelManager.getTunnelIPAddr(2L)).andReturn(new Integer(100)).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision, topology, tunnelManager);
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 OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod) {
assertEquals(m, fm1);
}
}
OFMessage m = wc2.getValue();
assertTrue(m.equals(fm2));
}
/**
* This test simulates traffic that's already tunneled. The tunneled
* traffic in its first and last hop will be written with srcIP, dstIP,
* and eth-type fields, without wildcarding them.
* @throws Exception
*/
@Test
public void testForwardMultiSwitchPathForTunnelTraffic() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
// Since the packets are the first and last hops of the switch
// the wildcard will have the network source and destination masks.
int wc = expected_wildcards & ~OFMatch.OFPFW_NW_SRC_MASK
& ~OFMatch.OFPFW_NW_DST_MASK;
OFFlowMod fm2 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm2.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wc))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Expected Flow-mods
match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
actions = new ArrayList<OFAction>();
// Then add output action
action = new OFActionOutput((short)3, (short)0xffff);
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(wc))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
// Reset the packetout.
packetOut =
(OFPacketOut) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut.setBufferId(this.packetIn.getBufferId())
.setInPort(this.packetIn.getInPort());
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(new OFActionOutput((short)3, (short) 0xffff));
packetOut.setActions(poactions)
.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH))
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, false)).andReturn(route).anyTimes();
// Mock tunnel service
reset(tunnelManager);
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.getTunnelLoopbackPort(1L)).andReturn(null).once();
expect(tunnelManager.getTunnelLoopbackPort(2L)).andReturn(null).once();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(1L)).andReturn(new Short((short)100)).anyTimes();
expect(tunnelManager.getTunnelPortNumber(2L)).andReturn(new Short((short)100)).anyTimes();
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision, topology, tunnelManager);
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 OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod) {
assertEquals(m, fm1);
}
}
OFMessage m = wc2.getValue();
assertTrue(m.equals(fm2));
}
/**
* Test set-up that is common to testTunnelTraffic* unit tests.
* @throws Exception
*/
private void testDetectTunnelTrafficCommon() throws Exception {
// Mock decision
expect(decision.getRoutingAction()).andReturn(IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision.getWildcards()).andReturn(null).atLeastOnce();
expect(decision.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as sw2 and Mock route
dstDevices.add(dstDevice1);
// Expected Flow-mods
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, false)).andReturn(true).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 2, (short)3, false)).andReturn(new NodePortTuple(2, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())).andReturn(true).anyTimes();
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)3));
route.getPath().add(new NodePortTuple(2L, (short)1));
route.getPath().add(new NodePortTuple(2L, (short)3));
long cookie = forwarding.getHashByMac(dstDevice1.getMACAddress());
expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3, cookie, false)).andReturn(route).anyTimes();
// Mock tunnel service
reset(tunnelManager);
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(1L)).andReturn(new Short((short)100)).anyTimes();
expect(tunnelManager.getTunnelPortNumber(2L)).andReturn(new Short((short)100)).anyTimes();
reset(bettertopology);
}
/**
* This test is to ensure that the tunneled traffic (from one tun-loopback
* port to another is identified correctly when the source of the traffic
* is from the tunnel port.
*/
@Test
public void testDetectTunnelTrafficSource() throws Exception {
testDetectTunnelTrafficCommon();
expect(tunnelManager.getTunnelLoopbackPort(1L)).andReturn(new Short((short)1)).once();
expect(tunnelManager.getTunnelLoopbackPort(2L)).andReturn(null).once();
expect(tunnelManager.getSwitchDpid(new Integer(-1062731518))).andReturn(new Long(2L)).anyTimes();
bettertopology.detectTunnelSource(1L, 2L);
expectLastCall().once();
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
}
/**
* This test is to ensure that the tunneled traffic (from one tun-loopback
* port to another is identified correctly when the destination of the traffic
* is from the tunnel port.
*/
@Test
public void testDetectTunnelTrafficDestination() throws Exception {
testDetectTunnelTrafficCommon();
expect(tunnelManager.getTunnelLoopbackPort(1L)).andReturn(null).once();
expect(tunnelManager.getTunnelLoopbackPort(2L)).andReturn(new Short((short)3)).once();
expect(tunnelManager.getSwitchDpid(new Integer(-1062731519))).andReturn(new Long(1L)).anyTimes();
bettertopology.detectTunnelDestination(1L, 2L);
expectLastCall().once();
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
}
/**
* This test is to ensure that the tunneled traffic (from one tun-loopback
* port to another is identified correctly when the source and destination
* of the traffic is from the tunnel port. In this case, the entire
* tunnel path is through the L2 domain, thus we don't have to add it to
* the detection queue as we establish both the flow-mods.
*/
@Test
public void testDetectTunnelTrafficSourceAndDestination() throws Exception {
testDetectTunnelTrafficCommon();
expect(tunnelManager.getTunnelLoopbackPort(1L)).andReturn(new Short((short)1)).once();
expect(tunnelManager.getTunnelLoopbackPort(2L)).andReturn(new Short((short)3)).once();
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
forwarding.receive(sw1, this.packetIn, cntx);
verify(sw1, sw2, routingEngine, decision, topology, tunnelManager, bettertopology);
}
private void
setupNOFViaTunnelToOFSwTest(short packetInPort, short packetOutPort,
boolean flowModProg, boolean reconcileTest) {
/*
* Setup devices corresponding to H1, H2, L3 NOF TOR, TEP1, TEP2
*/
device1 = deviceManager.learnEntity(HexString.toLong("00:00:00:00:00:01"),
null,
IPv4.toIPv4Address("10.0.0.1"),
1L, 3);
device2 = deviceManager.learnEntity(HexString.toLong("00:00:00:00:00:02"),
null,
IPv4.toIPv4Address("10.0.0.2"),
2L, 3);
device3 = deviceManager.learnEntity(HexString.toLong("00:00:00:00:00:03"),
null,
IPv4.toIPv4Address("192.168.0.3"),
1L, Integer.valueOf(packetInPort));
device4 = deviceManager.learnEntity(HexString.toLong("00:00:00:00:00:04"),
null,
IPv4.toIPv4Address("192.168.0.4"),
1L, 4);
device5 = deviceManager.learnEntity(HexString.toLong("00:00:00:00:00:05"),
null,
IPv4.toIPv4Address("192.168.0.5"),
2L, 4);
/*
* Setup listener context - DST_DEVICE, ORIG_DST_DEVICE setup by
* individual test case. SRC_IFACES, DST_IFACES no-op.
*/
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_SRC_DEVICE,
device3);
/*
* Setup decision mock
*/
reset(decision);
if (!reconcileTest) {
expect(decision.getSourceDevice())
.andReturn(device3)
.atLeastOnce();
expect(decision.getSourcePort())
.andReturn(new SwitchPort(1L, packetInPort))
.atLeastOnce();
expect(decision.getRoutingAction())
.andReturn(IRoutingDecision.RoutingAction.FORWARD)
.atLeastOnce();
}
dstDevices = new ArrayList<IDevice>();
expect(decision.getDestinationDevices())
.andReturn(dstDevices)
.atLeastOnce();
if (flowModProg) {
if (!reconcileTest) {
expect(decision.getWildcards())
.andReturn(null)
.atLeastOnce();
}
expect(decision.getHardTimeout())
.andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT)
.atLeastOnce();
}
IRoutingDecision.rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION,
decision);
/*
* Build test packet data, packet-in, packet-out
*/
testPacket = new Ethernet()
.setDestinationMACAddress("00:00:00:00:00:04")
.setSourceMACAddress("00:00:00:00:00:03")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setSourceAddress("192.168.0.3")
.setDestinationAddress("10.0.0.1")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketSerialized = testPacket.serialize();
packetIn = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort(packetInPort)
.setPacketData(testPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized.length);
packetOut =
(OFPacketOut) mockControllerProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut.setBufferId(this.packetIn.getBufferId())
.setInPort(this.packetIn.getInPort());
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(new OFActionOutput(packetOutPort, (short) 0xffff));
packetOut.setActions(poactions)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
/*
* Mock tunnel service
*/
reset(tunnelManager);
expect(tunnelManager.isTunnelEndpoint(device4))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(device5))
.andReturn(true).anyTimes();
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(false).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong()))
.andReturn(null).anyTimes();
expect(tunnelManager.getTunnelLoopbackPort(EasyMock.anyLong()))
.andReturn(null).anyTimes();
expect(tunnelManager.isTunnelSubnet(EasyMock.anyInt()))
.andReturn(false).anyTimes();
/*
* Mock topology
*/
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(3L).anyTimes();
expect(topology.getL2DomainId(1L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, false)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, false)).andReturn(3L).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)5, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)5))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)4, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)4))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)3))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)2, true))
.andReturn(false).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)5, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)4, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)3, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)3))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(2L, (short)2, true))
.andReturn(false).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1, 1, (short)4, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)5, 1, (short)4, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1, 1, (short)3, true))
.andReturn(new NodePortTuple(1, (short)3)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort()))
.andReturn(true).anyTimes();
expect(topology.getAllowedIncomingBroadcastPort(1L, (short)1, true))
.andReturn(new NodePortTuple(1L, (short)1)).anyTimes();
expect(topology.getAllowedIncomingBroadcastPort(1L, (short)5, true))
.andReturn(new NodePortTuple(1L, (short)1)).anyTimes();
expect(topology.isBroadcastDomainPort(1L, (short)5, true))
.andReturn(true).anyTimes();
expect(topology.isBroadcastDomainPort(1L, (short)1, true))
.andReturn(true).anyTimes();
}
/*
* Topology:
* ---------
* *--------*
* | L3 NOF | ----- H3
* *--------*
* |
* ----------------------
* | | | |
* (*) | | | |
* +-----------+ +-----------+
* | (1) (5) | | (5) (1) |
* | | | |
* | | | |
* | (2)|o ----- o|(2) |
* | | | |
* | SW1 | | SW2 |
* | | | |
* | | | |
* | | | |
* | (4) (3) | | (3) (4) |
* +-----------+ +-----------+
* o o o o
* | | | |
* | --H1 H2--| |
* TEP1 TEP2
*
* Test Context:
* -------------
* o H3 wants to talk to H2
* o H1 and H2 are in the subnet X
* o L3 NOF is configured with a nexthop of (TEP1, TEP2)[ECMP] to subnet X
* o Packet from H3 destined to H2 hashes to TEP1 as next hop on L3 NOF
* o So packet comes in on (SW1, 1)
* o VirtualRouting(VRS) finds out that packet destined to TEP1's MAC,
* but destIP is H2. Let's say VRS looks up it's config and decides to
* Forward. It is assumed that VRS would re-program CONTEXT_DST_DEVICE
* to H2 and set CONTEXT_ORIG_DST_DEVICE to TEP1.
* o Now, packet reaches Forwarding.
*
* Test Assumptions:
* ----------------
* o It is assumed that (SW1, 1) is the designated port in topology to
* receive traffic from NOF destined to TEP1
*
* Test Expectation:
* -----------------
* o Forwarding notices from CONTEXT_ORIG_DST_DEVICE that this packet was
* destined to TEP1's MAC, but is actually destined to H2's IP.
* o Forwarding should make sure that (SW1, 1) is the designated port to
* receive traffic for (TEP1 and not H2).
* o Forwarding should query the SW-SW path from topology for SW1 --> SW2
* and prepend (SW1, 1) and append (SW2, 3) to construct the final path.
* This would be { (SW1, 1) (SW1, 2) (SW2, 2) (SW2, 3) }
* o FlowMods should be programed on SW1 and SW2 appropriately.
*
*/
@Test
public void testForwardNOFViaTunnelOrigDestToOFSwRemote() throws Exception {
/*
* Common setup for all NOFViaTunnelToOFSw* tests
*/
setupNOFViaTunnelToOFSwTest((short)1, (short)2, true, false);
/*
* Test specific setup
*/
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_DST_DEVICE,
device2);
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_ORIG_DST_DEVICE,
device4);
dstDevices.add(device2);
/*
* Expected Flow-mods
*/
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)2, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod)mockControllerProvider
.getOFMessageFactory()
.getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
OFFlowMod fm2 = fm1.clone();
((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
fm2.getMatch().setInputPort((short)2);
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
/*
* Setup mock routes
*/
Route route = new Route(1L, 2L);
route.setPath(new ArrayList<NodePortTuple>());
route.getPath().add(new NodePortTuple(1L, (short)2));
route.getPath().add(new NodePortTuple(2L, (short)2));
long cookie = forwarding.getHashByMac(device2.getMACAddress());
expect(routingEngine.getRoute(1L, 2L, cookie, true))
.andReturn(route)
.anyTimes();
/*
* Replay mocks
*/
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
/*
* Finally the test
*/
forwarding.receive(sw1, this.packetIn, cntx);
/*
* Verify
*/
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
verify(topology);
verify(tunnelManager);
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 OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod)
assertEquals(m, fm1);
}
OFMessage m = wc2.getValue();
assertTrue(m.equals(fm2));
}
/*
* Topology:
* ---------
* *--------*
* | L3 NOF | ----- H3
* *--------*
* |
* ----------------------
* | | | |
* (*) | | | |
* +-----------+ +-----------+
* | (1) (5) | | (5) (1) |
* | | | |
* | | | |
* | (2) |o ----- o|(2) |
* | | | |
* | SW1 | | SW2 |
* | | | |
* | | | |
* | | | |
* | (4) (3) | | (4) (3) |
* +-----------+ +-----------+
* o o o o
* | | | |
* | --H1 H2--| |
* TEP1 TEP2
*
* Test Context:
* -------------
* o H3 wants to talk to H1
* o H1 and H2 are in the subnet X
* o L3 NOF is configured with a nexthop of (TEP1, TEP2)[ECMP] to subnet X
* o Packet from H3 destined to H1 hashes to TEP1 as next hop on L3 NOF
* o So packet comes in on (SW1, 1)
* o VirtualRouting(VRS) finds out that packet destined to TEP1's MAC,
* but destIP is H1. Let's say VRS looks up it's config and decides to
* Forward. It is assumed that VRS would re-program CONTEXT_DST_DEVICE
* to H1 and set CONTEXT_ORIG_DST_DEVICE to TEP1.
* o Now, packet reaches Forwarding.
*
* Test Assumptions:
* ----------------
* o It is assumed that (SW1, 1) is the designated port in topology to
* receive traffic from NOF destined to TEP1
*
* Test Expectation:
* -----------------
* o Forwarding notices from CONTEXT_ORIG_DST_DEVICE that this packet was
* destined to TEP1's MAC, but is actually destined to H1(CONTEXT_DST_DEVICE).
* o Forwarding should make sure that (SW1, 1) is the designated port to
* receive traffic for (TEP1 and not H1).
* o Forwarding should simply add (SW1, 1) and (SW1, 3) to construct
* the final path. It should not query SW-SW path as destination is
* local to SW1.
* o FlowMods should be programed only on SW1.
*/
@Test
public void testForwardNOFViaTunnelOrigDestToOFSwLocal() throws Exception {
/*
* Common setup for all NOFViaTunnelToOFSw tests
*/
setupNOFViaTunnelToOFSwTest((short)1, (short)3, true, false);
/*
* Test specific setup
*/
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_DST_DEVICE,
device1);
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_ORIG_DST_DEVICE,
device4);
dstDevices.add(device1);
/*
* Expected Flow-mods
*/
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod)mockControllerProvider
.getOFMessageFactory()
.getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
/*
* Capture writes of OFMessages(flowmods, packetout) to switches
*/
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
/*
* Replay mocks
*/
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
/*
* Finally the test
*/
forwarding.receive(sw1, this.packetIn, cntx);
/*
* Verify
*/
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
verify(topology);
verify(tunnelManager);
assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod.
assertFalse(wc2.hasCaptured()); // no flowmods on wc2
List<OFMessage> msglist = wc1.getValues();
for (OFMessage m: msglist) {
if (m instanceof OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod)
assertEquals(m, fm1);
}
}
/*
* Topology:
* ---------
* *--------*
* | L3 NOF | ----- H3
* *--------*
* |
* ----------------------
* | | | |
* (*) | | | | (*)
* +-----------+ +-----------+
* | (1) (5) | | (5) (1) |
* | | | |
* | | | |
* | (2) |o ----- o|(2) |
* | | | |
* | SW1 | | SW2 |
* | | | |
* | | | |
* | | | |
* | (4) (3) | | (4) (3) |
* +-----------+ +-----------+
* o o o o
* | | | |
* | --H1 H2--| |
* TEP1 TEP2
*
* Test Context:
* -------------
* o H3 wants to talk to H1
* o H1 and H2 are in the subnet X
* o L3 NOF is configured with a nexthop of (TEP1, TEP2)[ECMP] to subnet X
* o Packet from H3 destined to H1 hashes to TEP1 as next hop on L3 NOF
* o However, NOF does not where TEP1's MAC is present. So it floods it
* to (SW1, 1), (SW1, 5), (SW2, 5), (SW2, 1)
* o Consider we are currently processing packet-in from (SW1, 5)
* o VirtualRouting(VRS) finds out that packet destined to TEP1's MAC,
* but destIP is H1. Let's say VRS looks up it's config and decides to
* Forward. It is assumed that VRS would re-program CONTEXT_DST_DEVICE
* to H1 and set CONTEXT_ORIG_DST_DEVICE to TEP1.
* o Now, packet reaches Forwarding.
*
* Test Assumptions:
* ----------------
* o It is assumed that (SW1, 1) is the designated port in topology to
* receive traffic from NOF destined to TEP2.
*
* Test Expectation:
* -----------------
* o Forwarding notices from CONTEXT_ORIG_DST_DEVICE that this packet was
* destined to TEP1's MAC, but is actually destined to H1(CONTEXT_DST_DEVICE).
* o Forwarding should make sure that (SW1, 1) is the designated port to
* receive traffic for (TEP1 and not H1).
* o Forwarding should simply add (SW1, 1) and (SW1, 3) to construct
* the final path. It should not query SW-SW path as destination is
* local to SW2.
* o Forwarding figures that the packet-in switch,port (SW1, 5) is not
* in the path and hence should not program any flowmods.
* o Instead it should inject arps originating with TEP1 as target IP in
* ARP request:
* o Broadcast Port for SW1 : (1)
* o Permitted Unicast Port for TEP1 on SW1: also (1)
*/
@Test
public void testForwardNOFViaTunnelOrigDestToOFSwLocalDeny()
throws Exception {
/*
* Common setup for all NOFViaTunnelToOFSw tests
*/
setupNOFViaTunnelToOFSwTest((short)5, (short)3, false, false);
/*
* Test specific setup
*/
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_DST_DEVICE,
device1);
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_ORIG_DST_DEVICE,
device4);
dstDevices.add(device1);
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
/*
* Mock the sdnplatform provider service
*/
IControllerService bcp = createMock(IControllerService.class);
forwarding.setControllerProvider(bcp);
IControllerService.bcStore.put(cntx,
IControllerService.CONTEXT_PI_PAYLOAD,
(Ethernet)testPacket);
expect(bcp.getOFMessageFactory())
.andReturn(mockControllerProvider.getOFMessageFactory())
.anyTimes();
expect(bcp.injectOfMessage(sw1, getFakeArpPi(this.packetIn,
(Ethernet)testPacket,
device4.getIPv4Addresses()[0].intValue(),
Short.valueOf((short)1))))
.andReturn(true);
expectLastCall().times(2);
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
switches.put(2L, sw2);
switches.put(3L, sw3);
expect(bcp.getSwitches()).andReturn(switches).anyTimes();
/*
* Replay mocks
*/
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager, bcp);
/*
* Finally the test
*/
forwarding.receive(sw1, this.packetIn, cntx);
/*
* Verify
*/
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
verify(topology);
verify(tunnelManager);
verify(bcp);
assertFalse(wc1.hasCaptured()); // no packetout + flowmod on wc1.
assertFalse(wc2.hasCaptured()); // no flowmods on wc2
}
@Test
public void testForwardNOFViaTunnelOrigDestToOFSwLocalReconcile()
throws Exception {
/*
* Common setup for all NOFViaTunnelToOFSw tests
*/
setupNOFViaTunnelToOFSwTest((short)1, (short)3, true, true);
/*
* Test specific setup
*/
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_DST_DEVICE,
device1);
IDeviceService.fcStore.put(cntx,
IDeviceService.CONTEXT_ORIG_DST_DEVICE,
device4);
dstDevices.add(device1);
/*
* Expected Flow-mods
*/
OFMatch match = new OFMatch();
match.loadFromPacket(testPacketSerialized, (short) 1);
OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod)mockControllerProvider
.getOFMessageFactory()
.getMessage(OFType.FLOW_MOD);
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(expected_wildcards))
.setPriority(forwarding.getAccessPriority())
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(2L << 52)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
fm1.setFlags((short)1); // set flow-mod-removal flag on src switch only
fm1.getMatch().setWildcards(match.getWildcards());
fm1.setCommand(OFFlowMod.OFPFC_MODIFY);
/*
* Capture writes of OFMessages(flowmods, packetout) to switches
*/
sw1.write(capture(wc1), capture(fc1));
expectLastCall().anyTimes();
sw1.flush();
expectLastCall().once();
sw2.write(capture(wc2), capture(fc2));
expectLastCall().anyTimes();
/*
* Replay mocks
*/
replay(sw1, sw2, routingEngine, decision, topology, tunnelManager);
/*
* Prepare the reconcile flow mods
*/
ArrayList<OFMatchReconcile> ofmRcList = new ArrayList<OFMatchReconcile>();
OFMatchReconcile ofmRc = new OFMatchReconcile();
ofmRc.ofmWithSwDpid.setOfMatch(match.clone());
ofmRc.ofmWithSwDpid.setSwitchDataPathId(1L);
ofmRc.rcAction = ReconcileAction.UPDATE_PATH;
ofmRc.cntx = cntx;
ofmRcList.add(ofmRc);
/*
* Finally, the test
*/
forwarding.reconcileFlows(ofmRcList);
/*
* Verify
*/
verify(sw1);
verify(sw2);
verify(routingEngine);
verify(decision);
verify(topology);
verify(tunnelManager);
assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod.
assertFalse(wc2.hasCaptured()); // no flowmods on wc2
List<OFMessage> msglist = wc1.getValues();
for (OFMessage m: msglist) {
if (m instanceof OFPacketOut)
assertEquals(m, packetOut);
else if (m instanceof OFFlowMod)
assertEquals(m, fm1);
}
}
}