blob: 8beac4a2a8117fc96c679be2a69db34bac8c2cae [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 Radoslavov76b0ae22014-10-27 15:33:19 -070060import org.onlab.packet.IpAddress;
Jonathan Hart704ca142014-10-09 09:34:39 -070061import org.onlab.packet.IpPrefix;
62import 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 Radoslavov33f228a2014-10-27 19:33:16 -070077 private static final IpAddress IP1 = IpAddress.valueOf("192.168.1.1");
78 private static final IpAddress IP2 = IpAddress.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 Radoslavov76b0ae22014-10-27 15:33:19 -0700207 IpPrefix prefix1 = IpPrefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
208 IpAddress addr1 = IpAddress.valueOf("10.0." + (2 * i - 1) + ".1");
209 IpPrefix prefix2 = IpPrefix.valueOf("10.0." + (2 * i) + ".0/24");
210 IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1");
211 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
212 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Jonathan Harta887ba82014-11-03 15:20:52 -0800213 PortAddresses pa1 =
214 new PortAddresses(cp, Sets.newHashSet(ia1),
215 MacAddress.valueOf(2 * i - 1));
216 PortAddresses pa2 =
217 new PortAddresses(cp, Sets.newHashSet(ia2),
218 MacAddress.valueOf(2 * i));
219
220 addresses.add(pa1);
221 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700222
223 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800224 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700225 }
226
227 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
228
229 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
230 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
231 P1);
232 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800233 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700234 }
235 }
236
Jonathan Hart704ca142014-10-09 09:34:39 -0700237 /**
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700238 * Tests {@link ProxyArpManager#known(IpAddress)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700239 * IP address is not known.
240 * Verifies the method returns false.
241 */
242 @Test
243 public void testNotKnown() {
244 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
245 replay(hostService);
246
247 assertFalse(proxyArp.known(IP1));
248 }
249
250 /**
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700251 * Tests {@link ProxyArpManager#known(IpAddress)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700252 * IP address is known.
253 * Verifies the method returns true.
254 */
255 @Test
256 public void testKnown() {
257 Host host1 = createMock(Host.class);
258 Host host2 = createMock(Host.class);
259
260 expect(hostService.getHostsByIp(IP1))
261 .andReturn(Sets.newHashSet(host1, host2));
262 replay(hostService);
263
264 assertTrue(proxyArp.known(IP1));
265 }
266
267 /**
268 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
269 * destination host is known.
270 * Verifies the correct ARP reply is sent out the correct port.
271 */
272 @Test
273 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700274 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700275 Collections.singleton(IP1));
276
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700277 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700278 Collections.singleton(IP2));
279
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700280 expect(hostService.getHostsByIp(IP1))
281 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700282 expect(hostService.getHost(HID2)).andReturn(requestor);
283
284 replay(hostService);
285
286 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
287
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700288 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700289
290 assertEquals(1, packetService.packets.size());
291 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700292 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700293 }
294
295 /**
296 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
297 * destination host is not known.
298 * Verifies the ARP request is flooded out the correct edge ports.
299 */
300 @Test
301 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700302 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700303 Collections.singleton(IP2));
304
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700305 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700306 .andReturn(Collections.<Host>emptySet());
307 expect(hostService.getHost(HID2)).andReturn(requestor);
308
309 replay(hostService);
310
311 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
312
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700313 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700314
315 verifyFlood(arpRequest);
316 }
317
318 /**
319 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
320 * destination host is known for that IP address, but is not on the same
321 * VLAN as the source host.
322 * Verifies the ARP request is flooded out the correct edge ports.
323 */
324 @Test
325 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700326 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700327 Collections.singleton(IP1));
328
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700329 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700330 Collections.singleton(IP2));
331
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700332 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700333 .andReturn(Collections.singleton(replyer));
334 expect(hostService.getHost(HID2)).andReturn(requestor);
335
336 replay(hostService);
337
338 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
339
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700340 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700341
342 verifyFlood(arpRequest);
343 }
344
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700345 @Test
346 public void testReplyToRequestForUs() {
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700347 IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
348 IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1");
349 IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800350 MacAddress firstMac = MacAddress.valueOf(1L);
351 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700352
353 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
354 Collections.singleton(theirIp));
355
356 expect(hostService.getHost(HID2)).andReturn(requestor);
357 replay(hostService);
358
359 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
360
361 proxyArp.reply(arpRequest, LOC1);
362
363 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800364 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700365 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
366
367 // Test a request for the second address on that port
368 packetService.packets.clear();
369 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
370
371 proxyArp.reply(arpRequest, LOC1);
372
373 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800374 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700375 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
376 }
377
378 @Test
379 public void testReplyExternalPortBadRequest() {
380 replay(hostService); // no further host service expectations
381
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700382 IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700383
384 // Request for a valid external IP address but coming in the wrong port
385 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700386 IpAddress.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700387 proxyArp.reply(arpRequest, LOC1);
388 assertEquals(0, packetService.packets.size());
389
390 // Request for a valid internal IP address but coming in an external port
391 packetService.packets.clear();
392 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
393 proxyArp.reply(arpRequest, LOC1);
394 assertEquals(0, packetService.packets.size());
395 }
396
397 @Test
398 public void testReplyToRequestFromUs() {
399 replay(hostService); // no further host service expectations
400
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700401 IpAddress ourIp = IpAddress.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700402 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700403 IpAddress theirIp = IpAddress.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700404
405 // This is a request from something inside our network (like a BGP
406 // daemon) to an external host.
407 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
408 proxyArp.reply(arpRequest, getLocation(5));
409
410 assertEquals(1, packetService.packets.size());
411 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
412
413 // The same request from a random external port should fail
414 packetService.packets.clear();
415 proxyArp.reply(arpRequest, getLocation(2));
416 assertEquals(0, packetService.packets.size());
417 }
418
Jonathan Hart704ca142014-10-09 09:34:39 -0700419 /**
420 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
421 * destination host is known.
422 * Verifies the correct ARP request is sent out the correct port.
423 */
424 @Test
425 public void testForwardToHost() {
426 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
427 Collections.singleton(IP1));
428
429 expect(hostService.getHost(HID1)).andReturn(host1);
430 replay(hostService);
431
432 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
433
434 proxyArp.forward(arpRequest);
435
436 assertEquals(1, packetService.packets.size());
437 OutboundPacket packet = packetService.packets.get(0);
438
439 verifyPacketOut(arpRequest, LOC1, packet);
440 }
441
442 /**
443 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
444 * destination host is not known.
445 * Verifies the correct ARP request is flooded out the correct edge ports.
446 */
447 @Test
448 public void testForwardFlood() {
449 expect(hostService.getHost(HID1)).andReturn(null);
450 replay(hostService);
451
452 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
453
454 proxyArp.forward(arpRequest);
455
456 verifyFlood(arpRequest);
457 }
458
459 /**
460 * Verifies that the given packet was flooded out all available edge ports.
461 *
462 * @param packet the packet that was expected to be flooded
463 */
464 private void verifyFlood(Ethernet packet) {
465 assertEquals(NUM_FLOOD_PORTS, packetService.packets.size());
466
467 Collections.sort(packetService.packets,
468 new Comparator<OutboundPacket>() {
469 @Override
470 public int compare(OutboundPacket o1, OutboundPacket o2) {
471 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
472 }
473 });
474
475 for (int i = 0; i < NUM_FLOOD_PORTS; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700476 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
477 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700478
479 OutboundPacket outboundPacket = packetService.packets.get(i);
480 verifyPacketOut(packet, cp, outboundPacket);
481 }
482 }
483
484 /**
485 * Verifies the given packet was sent out the given port.
486 *
487 * @param expected the packet that was expected to be sent
488 * @param outPort the port the packet was expected to be sent out
489 * @param actual the actual OutboundPacket to verify
490 */
491 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
492 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800493 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700494 assertEquals(1, actual.treatment().instructions().size());
495 assertEquals(outPort.deviceId(), actual.sendThrough());
496 Instruction instruction = actual.treatment().instructions().get(0);
497 assertTrue(instruction instanceof OutputInstruction);
498 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
499 }
500
501 /**
502 * Returns the device ID of the ith device.
503 *
504 * @param i device to get the ID of
505 * @return the device ID
506 */
507 private static DeviceId getDeviceId(int i) {
508 return DeviceId.deviceId("" + i);
509 }
510
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700511 private static HostLocation getLocation(int i) {
512 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
513 }
514
Jonathan Hart704ca142014-10-09 09:34:39 -0700515 /**
516 * Builds an ARP packet with the given parameters.
517 *
518 * @param opcode opcode of the ARP packet
519 * @param srcMac source MAC address
520 * @param dstMac destination MAC address, or null if this is a request
521 * @param srcIp source IP address
522 * @param dstIp destination IP address
523 * @return the ARP packet
524 */
525 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700526 IpAddress srcIp, IpAddress dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700527 Ethernet eth = new Ethernet();
528
529 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800530 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700531 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800532 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700533 }
534
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800535 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700536 eth.setEtherType(Ethernet.TYPE_ARP);
537 eth.setVlanID(VLAN1.toShort());
538
539 ARP arp = new ARP();
540 arp.setOpCode(opcode);
541 arp.setProtocolType(ARP.PROTO_TYPE_IP);
542 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
543
Pavlin Radoslavov52307e62014-10-29 15:07:37 -0700544 arp.setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700545 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800546 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700547
548 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800549 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700550 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800551 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700552 }
553
554 arp.setSenderProtocolAddress(srcIp.toOctets());
555 arp.setTargetProtocolAddress(dstIp.toOctets());
556
557 eth.setPayload(arp);
558 return eth;
559 }
560
561 /**
562 * Test PacketService implementation that simply stores OutboundPackets
563 * passed to {@link #emit(OutboundPacket)} for later verification.
564 */
565 class TestPacketService implements PacketService {
566
567 List<OutboundPacket> packets = new ArrayList<>();
568
569 @Override
570 public void addProcessor(PacketProcessor processor, int priority) {
571 }
572
573 @Override
574 public void removeProcessor(PacketProcessor processor) {
575 }
576
577 @Override
578 public void emit(OutboundPacket packet) {
579 packets.add(packet);
580 }
581 }
582}