blob: bfb659e54fd22c6a26a6ab3d8c2b2061f70f0f86 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jonathan Hart704ca142014-10-09 09:34:39 -070016package org.onlab.onos.net.proxyarp.impl;
17
18import static org.easymock.EasyMock.anyObject;
19import static org.easymock.EasyMock.createMock;
20import static org.easymock.EasyMock.expect;
21import static org.easymock.EasyMock.replay;
Jonathan Harta887ba82014-11-03 15:20:52 -080022import static org.junit.Assert.assertArrayEquals;
23import static org.junit.Assert.assertEquals;
24import static org.junit.Assert.assertFalse;
25import static org.junit.Assert.assertTrue;
Jonathan Hart704ca142014-10-09 09:34:39 -070026
27import java.util.ArrayList;
Jonathan Hart704ca142014-10-09 09:34:39 -070028import java.util.Collections;
29import java.util.Comparator;
30import java.util.List;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070031import java.util.Set;
Jonathan Hart704ca142014-10-09 09:34:39 -070032
33import org.junit.Before;
34import org.junit.Test;
35import org.onlab.onos.net.ConnectPoint;
36import org.onlab.onos.net.DefaultHost;
37import org.onlab.onos.net.Device;
38import org.onlab.onos.net.DeviceId;
39import org.onlab.onos.net.Host;
40import org.onlab.onos.net.HostId;
41import org.onlab.onos.net.HostLocation;
42import org.onlab.onos.net.Link;
43import org.onlab.onos.net.Port;
44import org.onlab.onos.net.PortNumber;
45import org.onlab.onos.net.device.DeviceListener;
46import org.onlab.onos.net.device.DeviceService;
47import org.onlab.onos.net.flow.instructions.Instruction;
48import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
49import org.onlab.onos.net.host.HostService;
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -070050import org.onlab.onos.net.host.InterfaceIpAddress;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070051import org.onlab.onos.net.host.PortAddresses;
Jonathan Hart704ca142014-10-09 09:34:39 -070052import org.onlab.onos.net.link.LinkListener;
53import org.onlab.onos.net.link.LinkService;
54import org.onlab.onos.net.packet.OutboundPacket;
55import org.onlab.onos.net.packet.PacketProcessor;
56import org.onlab.onos.net.packet.PacketService;
57import org.onlab.onos.net.provider.ProviderId;
58import org.onlab.packet.ARP;
59import org.onlab.packet.Ethernet;
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080060import org.onlab.packet.Ip4Address;
61import org.onlab.packet.Ip4Prefix;
Jonathan Hart704ca142014-10-09 09:34:39 -070062import org.onlab.packet.MacAddress;
63import org.onlab.packet.VlanId;
64
65import com.google.common.collect.Sets;
66
67/**
68 * Tests for the {@link ProxyArpManager} class.
69 */
70public class ProxyArpManagerTest {
71
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070072 private static final int NUM_DEVICES = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070073 private static final int NUM_PORTS_PER_DEVICE = 3;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070074 private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
75 private static final int NUM_FLOOD_PORTS = 3;
Jonathan Hart704ca142014-10-09 09:34:39 -070076
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080077 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
78 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
Jonathan Hart704ca142014-10-09 09:34:39 -070079
80 private static final ProviderId PID = new ProviderId("of", "foo");
81
82 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
83 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
84 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
85 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
86 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
87 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
88
89 private static final DeviceId DID1 = getDeviceId(1);
90 private static final DeviceId DID2 = getDeviceId(2);
91 private static final PortNumber P1 = PortNumber.portNumber(1);
92 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
93 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080094 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart704ca142014-10-09 09:34:39 -070095
96 private ProxyArpManager proxyArp;
97
98 private TestPacketService packetService;
99
100 private DeviceService deviceService;
101 private LinkService linkService;
102 private HostService hostService;
103
104 @Before
105 public void setUp() throws Exception {
106 proxyArp = new ProxyArpManager();
107 packetService = new TestPacketService();
108 proxyArp.packetService = packetService;
109
110 // Create a host service mock here. Must be replayed by tests once the
111 // expectations have been set up
112 hostService = createMock(HostService.class);
113 proxyArp.hostService = hostService;
114
115 createTopology();
116 proxyArp.deviceService = deviceService;
117 proxyArp.linkService = linkService;
118
119 proxyArp.activate();
120 }
121
122 /**
123 * Creates a fake topology to feed into the ARP module.
124 * <p/>
125 * The default topology is a unidirectional ring topology. Each switch has
126 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
127 * is free (edge port).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700128 * The first half of the switches have IP addresses configured on their
129 * free ports (port 1). The second half of the switches have no IP
130 * addresses configured.
Jonathan Hart704ca142014-10-09 09:34:39 -0700131 */
132 private void createTopology() {
133 deviceService = createMock(DeviceService.class);
134 linkService = createMock(LinkService.class);
135
136 deviceService.addListener(anyObject(DeviceListener.class));
137 linkService.addListener(anyObject(LinkListener.class));
138
139 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
140 createLinks(NUM_DEVICES);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700141 addAddressBindings();
Jonathan Hart704ca142014-10-09 09:34:39 -0700142 }
143
144 /**
145 * Creates the devices for the fake topology.
146 */
147 private void createDevices(int numDevices, int numPorts) {
148 List<Device> devices = new ArrayList<>();
149
150 for (int i = 1; i <= numDevices; i++) {
151 DeviceId devId = getDeviceId(i);
152 Device device = createMock(Device.class);
153 expect(device.id()).andReturn(devId).anyTimes();
154 replay(device);
155
156 devices.add(device);
157
158 List<Port> ports = new ArrayList<>();
159 for (int j = 1; j <= numPorts; j++) {
160 Port port = createMock(Port.class);
161 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
162 replay(port);
163 ports.add(port);
164 }
165
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700166 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
167 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700168 }
169
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700170 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700171 replay(deviceService);
172 }
173
174 /**
175 * Creates the links for the fake topology.
176 * NB: Only unidirectional links are created, as for this purpose all we
177 * need is to occupy the ports with some link.
178 */
179 private void createLinks(int numDevices) {
180 List<Link> links = new ArrayList<Link>();
181
182 for (int i = 1; i <= numDevices; i++) {
183 ConnectPoint src = new ConnectPoint(
184 getDeviceId(i),
185 PortNumber.portNumber(2));
186 ConnectPoint dst = new ConnectPoint(
187 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
188 PortNumber.portNumber(3));
189
190 Link link = createMock(Link.class);
191 expect(link.src()).andReturn(src).anyTimes();
192 expect(link.dst()).andReturn(dst).anyTimes();
193 replay(link);
194
195 links.add(link);
196 }
197
198 expect(linkService.getLinks()).andReturn(links).anyTimes();
199 replay(linkService);
200 }
201
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700202 private void addAddressBindings() {
203 Set<PortAddresses> addresses = Sets.newHashSet();
204
205 for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
206 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800207 Ip4Prefix prefix1 =
208 Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
209 Ip4Address addr1 =
210 Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
211 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
212 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700213 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
214 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Jonathan Harta887ba82014-11-03 15:20:52 -0800215 PortAddresses pa1 =
216 new PortAddresses(cp, Sets.newHashSet(ia1),
217 MacAddress.valueOf(2 * i - 1));
218 PortAddresses pa2 =
219 new PortAddresses(cp, Sets.newHashSet(ia2),
220 MacAddress.valueOf(2 * i));
221
222 addresses.add(pa1);
223 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700224
225 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800226 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700227 }
228
229 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
230
231 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
232 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
233 P1);
234 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800235 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700236 }
237 }
238
Jonathan Hart704ca142014-10-09 09:34:39 -0700239 /**
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800240 * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700241 * IP address is not known.
242 * Verifies the method returns false.
243 */
244 @Test
245 public void testNotKnown() {
246 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
247 replay(hostService);
248
249 assertFalse(proxyArp.known(IP1));
250 }
251
252 /**
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800253 * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700254 * IP address is known.
255 * Verifies the method returns true.
256 */
257 @Test
258 public void testKnown() {
259 Host host1 = createMock(Host.class);
260 Host host2 = createMock(Host.class);
261
262 expect(hostService.getHostsByIp(IP1))
263 .andReturn(Sets.newHashSet(host1, host2));
264 replay(hostService);
265
266 assertTrue(proxyArp.known(IP1));
267 }
268
269 /**
270 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
271 * destination host is known.
272 * Verifies the correct ARP reply is sent out the correct port.
273 */
274 @Test
275 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700276 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700277 Collections.singleton(IP1));
278
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700279 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700280 Collections.singleton(IP2));
281
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700282 expect(hostService.getHostsByIp(IP1))
283 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700284 expect(hostService.getHost(HID2)).andReturn(requestor);
285
286 replay(hostService);
287
288 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
289
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700290 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700291
292 assertEquals(1, packetService.packets.size());
293 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700294 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700295 }
296
297 /**
298 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
299 * destination host is not known.
300 * Verifies the ARP request is flooded out the correct edge ports.
301 */
302 @Test
303 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700304 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700305 Collections.singleton(IP2));
306
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700307 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700308 .andReturn(Collections.<Host>emptySet());
309 expect(hostService.getHost(HID2)).andReturn(requestor);
310
311 replay(hostService);
312
313 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
314
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700315 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700316
317 verifyFlood(arpRequest);
318 }
319
320 /**
321 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
322 * destination host is known for that IP address, but is not on the same
323 * VLAN as the source host.
324 * Verifies the ARP request is flooded out the correct edge ports.
325 */
326 @Test
327 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700328 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700329 Collections.singleton(IP1));
330
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700331 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700332 Collections.singleton(IP2));
333
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700334 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700335 .andReturn(Collections.singleton(replyer));
336 expect(hostService.getHost(HID2)).andReturn(requestor);
337
338 replay(hostService);
339
340 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
341
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700342 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700343
344 verifyFlood(arpRequest);
345 }
346
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700347 @Test
348 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800349 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
350 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
351 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800352 MacAddress firstMac = MacAddress.valueOf(1L);
353 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700354
355 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
356 Collections.singleton(theirIp));
357
358 expect(hostService.getHost(HID2)).andReturn(requestor);
359 replay(hostService);
360
361 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
362
363 proxyArp.reply(arpRequest, LOC1);
364
365 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800366 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700367 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
368
369 // Test a request for the second address on that port
370 packetService.packets.clear();
371 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
372
373 proxyArp.reply(arpRequest, LOC1);
374
375 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800376 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700377 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
378 }
379
380 @Test
381 public void testReplyExternalPortBadRequest() {
382 replay(hostService); // no further host service expectations
383
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800384 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700385
386 // Request for a valid external IP address but coming in the wrong port
387 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800388 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700389 proxyArp.reply(arpRequest, LOC1);
390 assertEquals(0, packetService.packets.size());
391
392 // Request for a valid internal IP address but coming in an external port
393 packetService.packets.clear();
394 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
395 proxyArp.reply(arpRequest, LOC1);
396 assertEquals(0, packetService.packets.size());
397 }
398
399 @Test
400 public void testReplyToRequestFromUs() {
401 replay(hostService); // no further host service expectations
402
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800403 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700404 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800405 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700406
407 // This is a request from something inside our network (like a BGP
408 // daemon) to an external host.
409 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
410 proxyArp.reply(arpRequest, getLocation(5));
411
412 assertEquals(1, packetService.packets.size());
413 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
414
415 // The same request from a random external port should fail
416 packetService.packets.clear();
417 proxyArp.reply(arpRequest, getLocation(2));
418 assertEquals(0, packetService.packets.size());
419 }
420
Jonathan Hart704ca142014-10-09 09:34:39 -0700421 /**
422 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
423 * destination host is known.
424 * Verifies the correct ARP request is sent out the correct port.
425 */
426 @Test
427 public void testForwardToHost() {
428 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
429 Collections.singleton(IP1));
430
431 expect(hostService.getHost(HID1)).andReturn(host1);
432 replay(hostService);
433
434 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
435
436 proxyArp.forward(arpRequest);
437
438 assertEquals(1, packetService.packets.size());
439 OutboundPacket packet = packetService.packets.get(0);
440
441 verifyPacketOut(arpRequest, LOC1, packet);
442 }
443
444 /**
445 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
446 * destination host is not known.
447 * Verifies the correct ARP request is flooded out the correct edge ports.
448 */
449 @Test
450 public void testForwardFlood() {
451 expect(hostService.getHost(HID1)).andReturn(null);
452 replay(hostService);
453
454 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
455
456 proxyArp.forward(arpRequest);
457
458 verifyFlood(arpRequest);
459 }
460
461 /**
462 * Verifies that the given packet was flooded out all available edge ports.
463 *
464 * @param packet the packet that was expected to be flooded
465 */
466 private void verifyFlood(Ethernet packet) {
467 assertEquals(NUM_FLOOD_PORTS, packetService.packets.size());
468
469 Collections.sort(packetService.packets,
470 new Comparator<OutboundPacket>() {
471 @Override
472 public int compare(OutboundPacket o1, OutboundPacket o2) {
473 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
474 }
475 });
476
477 for (int i = 0; i < NUM_FLOOD_PORTS; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700478 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
479 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700480
481 OutboundPacket outboundPacket = packetService.packets.get(i);
482 verifyPacketOut(packet, cp, outboundPacket);
483 }
484 }
485
486 /**
487 * Verifies the given packet was sent out the given port.
488 *
489 * @param expected the packet that was expected to be sent
490 * @param outPort the port the packet was expected to be sent out
491 * @param actual the actual OutboundPacket to verify
492 */
493 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
494 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800495 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700496 assertEquals(1, actual.treatment().instructions().size());
497 assertEquals(outPort.deviceId(), actual.sendThrough());
498 Instruction instruction = actual.treatment().instructions().get(0);
499 assertTrue(instruction instanceof OutputInstruction);
500 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
501 }
502
503 /**
504 * Returns the device ID of the ith device.
505 *
506 * @param i device to get the ID of
507 * @return the device ID
508 */
509 private static DeviceId getDeviceId(int i) {
510 return DeviceId.deviceId("" + i);
511 }
512
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700513 private static HostLocation getLocation(int i) {
514 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
515 }
516
Jonathan Hart704ca142014-10-09 09:34:39 -0700517 /**
518 * Builds an ARP packet with the given parameters.
519 *
520 * @param opcode opcode of the ARP packet
521 * @param srcMac source MAC address
522 * @param dstMac destination MAC address, or null if this is a request
523 * @param srcIp source IP address
524 * @param dstIp destination IP address
525 * @return the ARP packet
526 */
527 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800528 Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700529 Ethernet eth = new Ethernet();
530
531 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800532 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700533 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800534 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700535 }
536
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800537 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700538 eth.setEtherType(Ethernet.TYPE_ARP);
539 eth.setVlanID(VLAN1.toShort());
540
541 ARP arp = new ARP();
542 arp.setOpCode(opcode);
543 arp.setProtocolType(ARP.PROTO_TYPE_IP);
544 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
545
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800546 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700547 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800548 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700549
550 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800551 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700552 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800553 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700554 }
555
556 arp.setSenderProtocolAddress(srcIp.toOctets());
557 arp.setTargetProtocolAddress(dstIp.toOctets());
558
559 eth.setPayload(arp);
560 return eth;
561 }
562
563 /**
564 * Test PacketService implementation that simply stores OutboundPackets
565 * passed to {@link #emit(OutboundPacket)} for later verification.
566 */
567 class TestPacketService implements PacketService {
568
569 List<OutboundPacket> packets = new ArrayList<>();
570
571 @Override
572 public void addProcessor(PacketProcessor processor, int priority) {
573 }
574
575 @Override
576 public void removeProcessor(PacketProcessor processor) {
577 }
578
579 @Override
580 public void emit(OutboundPacket packet) {
581 packets.add(packet);
582 }
583 }
584}