blob: a349738f9d57afecb93c95162618d2e8e25210b8 [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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.proxyarp.impl;
Jonathan Hart704ca142014-10-09 09:34:39 -070017
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;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080035import org.onlab.packet.ARP;
36import org.onlab.packet.Ethernet;
37import org.onlab.packet.Ip4Address;
38import org.onlab.packet.Ip4Prefix;
39import org.onlab.packet.MacAddress;
40import org.onlab.packet.VlanId;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080041import org.onosproject.core.ApplicationId;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.ConnectPoint;
43import org.onosproject.net.DefaultHost;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Host;
47import org.onosproject.net.HostId;
48import org.onosproject.net.HostLocation;
49import org.onosproject.net.Link;
50import org.onosproject.net.Port;
51import org.onosproject.net.PortNumber;
52import org.onosproject.net.device.DeviceListener;
53import org.onosproject.net.device.DeviceService;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080054import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.flow.instructions.Instruction;
56import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
57import org.onosproject.net.host.HostService;
58import org.onosproject.net.host.InterfaceIpAddress;
59import org.onosproject.net.host.PortAddresses;
60import org.onosproject.net.link.LinkListener;
61import org.onosproject.net.link.LinkService;
62import org.onosproject.net.packet.OutboundPacket;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080063import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080064import org.onosproject.net.packet.PacketProcessor;
65import org.onosproject.net.packet.PacketService;
66import org.onosproject.net.provider.ProviderId;
Jonathan Hart704ca142014-10-09 09:34:39 -070067
68import com.google.common.collect.Sets;
69
70/**
71 * Tests for the {@link ProxyArpManager} class.
72 */
73public class ProxyArpManagerTest {
74
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070075 private static final int NUM_DEVICES = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070076 private static final int NUM_PORTS_PER_DEVICE = 3;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070077 private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
78 private static final int NUM_FLOOD_PORTS = 3;
Jonathan Hart704ca142014-10-09 09:34:39 -070079
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080080 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
81 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
Jonathan Hart704ca142014-10-09 09:34:39 -070082
83 private static final ProviderId PID = new ProviderId("of", "foo");
84
85 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
86 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
87 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
88 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
89 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
90 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
91
92 private static final DeviceId DID1 = getDeviceId(1);
93 private static final DeviceId DID2 = getDeviceId(2);
94 private static final PortNumber P1 = PortNumber.portNumber(1);
95 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
96 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080097 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart704ca142014-10-09 09:34:39 -070098
99 private ProxyArpManager proxyArp;
100
101 private TestPacketService packetService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700102 private DeviceService deviceService;
103 private LinkService linkService;
104 private HostService hostService;
105
106 @Before
107 public void setUp() throws Exception {
108 proxyArp = new ProxyArpManager();
109 packetService = new TestPacketService();
110 proxyArp.packetService = packetService;
111
112 // Create a host service mock here. Must be replayed by tests once the
113 // expectations have been set up
114 hostService = createMock(HostService.class);
115 proxyArp.hostService = hostService;
116
117 createTopology();
118 proxyArp.deviceService = deviceService;
119 proxyArp.linkService = linkService;
120
121 proxyArp.activate();
122 }
123
124 /**
125 * Creates a fake topology to feed into the ARP module.
126 * <p/>
127 * The default topology is a unidirectional ring topology. Each switch has
128 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
129 * is free (edge port).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700130 * The first half of the switches have IP addresses configured on their
131 * free ports (port 1). The second half of the switches have no IP
132 * addresses configured.
Jonathan Hart704ca142014-10-09 09:34:39 -0700133 */
134 private void createTopology() {
135 deviceService = createMock(DeviceService.class);
136 linkService = createMock(LinkService.class);
137
138 deviceService.addListener(anyObject(DeviceListener.class));
139 linkService.addListener(anyObject(LinkListener.class));
140
141 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
142 createLinks(NUM_DEVICES);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700143 addAddressBindings();
Jonathan Hart704ca142014-10-09 09:34:39 -0700144 }
145
146 /**
147 * Creates the devices for the fake topology.
148 */
149 private void createDevices(int numDevices, int numPorts) {
150 List<Device> devices = new ArrayList<>();
151
152 for (int i = 1; i <= numDevices; i++) {
153 DeviceId devId = getDeviceId(i);
154 Device device = createMock(Device.class);
155 expect(device.id()).andReturn(devId).anyTimes();
156 replay(device);
157
158 devices.add(device);
159
160 List<Port> ports = new ArrayList<>();
161 for (int j = 1; j <= numPorts; j++) {
162 Port port = createMock(Port.class);
163 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
164 replay(port);
165 ports.add(port);
166 }
167
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700168 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
169 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700170 }
171
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700172 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700173 replay(deviceService);
174 }
175
176 /**
177 * Creates the links for the fake topology.
178 * NB: Only unidirectional links are created, as for this purpose all we
179 * need is to occupy the ports with some link.
180 */
181 private void createLinks(int numDevices) {
182 List<Link> links = new ArrayList<Link>();
183
184 for (int i = 1; i <= numDevices; i++) {
185 ConnectPoint src = new ConnectPoint(
186 getDeviceId(i),
187 PortNumber.portNumber(2));
188 ConnectPoint dst = new ConnectPoint(
189 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
190 PortNumber.portNumber(3));
191
192 Link link = createMock(Link.class);
193 expect(link.src()).andReturn(src).anyTimes();
194 expect(link.dst()).andReturn(dst).anyTimes();
195 replay(link);
196
197 links.add(link);
198 }
199
200 expect(linkService.getLinks()).andReturn(links).anyTimes();
201 replay(linkService);
202 }
203
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700204 private void addAddressBindings() {
205 Set<PortAddresses> addresses = Sets.newHashSet();
206
207 for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
208 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800209 Ip4Prefix prefix1 =
210 Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
211 Ip4Address addr1 =
212 Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
213 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
214 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700215 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
216 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Jonathan Harta887ba82014-11-03 15:20:52 -0800217 PortAddresses pa1 =
218 new PortAddresses(cp, Sets.newHashSet(ia1),
219 MacAddress.valueOf(2 * i - 1));
220 PortAddresses pa2 =
221 new PortAddresses(cp, Sets.newHashSet(ia2),
222 MacAddress.valueOf(2 * i));
223
224 addresses.add(pa1);
225 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700226
227 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800228 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700229 }
230
231 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
232
233 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
234 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
235 P1);
236 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800237 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700238 }
239 }
240
Jonathan Hart704ca142014-10-09 09:34:39 -0700241 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800242 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700243 * IP address is not known.
244 * Verifies the method returns false.
245 */
246 @Test
247 public void testNotKnown() {
248 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
249 replay(hostService);
250
Jonathan Hartf84591d2015-01-16 14:33:43 -0800251 assertFalse(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700252 }
253
254 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800255 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700256 * IP address is known.
257 * Verifies the method returns true.
258 */
259 @Test
260 public void testKnown() {
261 Host host1 = createMock(Host.class);
262 Host host2 = createMock(Host.class);
263
264 expect(hostService.getHostsByIp(IP1))
265 .andReturn(Sets.newHashSet(host1, host2));
266 replay(hostService);
267
Jonathan Hartf84591d2015-01-16 14:33:43 -0800268 assertTrue(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700269 }
270
271 /**
272 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
273 * destination host is known.
274 * Verifies the correct ARP reply is sent out the correct port.
275 */
276 @Test
277 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700278 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700279 Collections.singleton(IP1));
280
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700281 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700282 Collections.singleton(IP2));
283
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700284 expect(hostService.getHostsByIp(IP1))
285 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700286 expect(hostService.getHost(HID2)).andReturn(requestor);
287
288 replay(hostService);
289
290 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
291
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700292 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700293
294 assertEquals(1, packetService.packets.size());
295 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700296 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700297 }
298
299 /**
300 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
301 * destination host is not known.
302 * Verifies the ARP request is flooded out the correct edge ports.
303 */
304 @Test
305 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700306 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700307 Collections.singleton(IP2));
308
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700309 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700310 .andReturn(Collections.<Host>emptySet());
311 expect(hostService.getHost(HID2)).andReturn(requestor);
312
313 replay(hostService);
314
315 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
316
Jonathan Hartf84591d2015-01-16 14:33:43 -0800317 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700318
319 verifyFlood(arpRequest);
320 }
321
322 /**
323 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
324 * destination host is known for that IP address, but is not on the same
325 * VLAN as the source host.
326 * Verifies the ARP request is flooded out the correct edge ports.
327 */
328 @Test
329 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700330 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700331 Collections.singleton(IP1));
332
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700333 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700334 Collections.singleton(IP2));
335
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700336 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700337 .andReturn(Collections.singleton(replyer));
338 expect(hostService.getHost(HID2)).andReturn(requestor);
339
340 replay(hostService);
341
342 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
343
Jonathan Hartf84591d2015-01-16 14:33:43 -0800344 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700345
346 verifyFlood(arpRequest);
347 }
348
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700349 @Test
350 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800351 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
352 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
353 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800354 MacAddress firstMac = MacAddress.valueOf(1L);
355 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700356
357 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
358 Collections.singleton(theirIp));
359
360 expect(hostService.getHost(HID2)).andReturn(requestor);
361 replay(hostService);
362
363 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
364
365 proxyArp.reply(arpRequest, LOC1);
366
367 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800368 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700369 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
370
371 // Test a request for the second address on that port
372 packetService.packets.clear();
373 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
374
375 proxyArp.reply(arpRequest, LOC1);
376
377 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800378 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700379 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
380 }
381
382 @Test
383 public void testReplyExternalPortBadRequest() {
384 replay(hostService); // no further host service expectations
385
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800386 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700387
388 // Request for a valid external IP address but coming in the wrong port
389 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800390 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700391 proxyArp.reply(arpRequest, LOC1);
392 assertEquals(0, packetService.packets.size());
393
394 // Request for a valid internal IP address but coming in an external port
395 packetService.packets.clear();
396 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
397 proxyArp.reply(arpRequest, LOC1);
398 assertEquals(0, packetService.packets.size());
399 }
400
401 @Test
402 public void testReplyToRequestFromUs() {
403 replay(hostService); // no further host service expectations
404
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800405 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700406 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800407 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700408
409 // This is a request from something inside our network (like a BGP
410 // daemon) to an external host.
411 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
412 proxyArp.reply(arpRequest, getLocation(5));
413
414 assertEquals(1, packetService.packets.size());
415 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
416
417 // The same request from a random external port should fail
418 packetService.packets.clear();
419 proxyArp.reply(arpRequest, getLocation(2));
420 assertEquals(0, packetService.packets.size());
421 }
422
Jonathan Hart704ca142014-10-09 09:34:39 -0700423 /**
424 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
425 * destination host is known.
426 * Verifies the correct ARP request is sent out the correct port.
427 */
428 @Test
429 public void testForwardToHost() {
430 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
431 Collections.singleton(IP1));
432
433 expect(hostService.getHost(HID1)).andReturn(host1);
434 replay(hostService);
435
436 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
437
Jonathan Hartf84591d2015-01-16 14:33:43 -0800438 proxyArp.forward(arpRequest, LOC2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700439
440 assertEquals(1, packetService.packets.size());
441 OutboundPacket packet = packetService.packets.get(0);
442
443 verifyPacketOut(arpRequest, LOC1, packet);
444 }
445
446 /**
447 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
448 * destination host is not known.
449 * Verifies the correct ARP request is flooded out the correct edge ports.
450 */
451 @Test
452 public void testForwardFlood() {
453 expect(hostService.getHost(HID1)).andReturn(null);
454 replay(hostService);
455
456 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
457
Jonathan Hartf84591d2015-01-16 14:33:43 -0800458 proxyArp.forward(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700459
460 verifyFlood(arpRequest);
461 }
462
463 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800464 * Verifies that the given packet was flooded out all available edge ports,
465 * except for the input port.
Jonathan Hart704ca142014-10-09 09:34:39 -0700466 *
467 * @param packet the packet that was expected to be flooded
468 */
469 private void verifyFlood(Ethernet packet) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800470 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
471 assertEquals(NUM_FLOOD_PORTS - 1, packetService.packets.size());
Jonathan Hart704ca142014-10-09 09:34:39 -0700472
473 Collections.sort(packetService.packets,
474 new Comparator<OutboundPacket>() {
475 @Override
476 public int compare(OutboundPacket o1, OutboundPacket o2) {
477 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
478 }
479 });
480
Jonathan Hartf84591d2015-01-16 14:33:43 -0800481
482 for (int i = 0; i < NUM_FLOOD_PORTS - 1; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700483 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
484 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700485
486 OutboundPacket outboundPacket = packetService.packets.get(i);
487 verifyPacketOut(packet, cp, outboundPacket);
488 }
489 }
490
491 /**
492 * Verifies the given packet was sent out the given port.
493 *
494 * @param expected the packet that was expected to be sent
495 * @param outPort the port the packet was expected to be sent out
496 * @param actual the actual OutboundPacket to verify
497 */
498 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
499 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800500 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700501 assertEquals(1, actual.treatment().instructions().size());
502 assertEquals(outPort.deviceId(), actual.sendThrough());
503 Instruction instruction = actual.treatment().instructions().get(0);
504 assertTrue(instruction instanceof OutputInstruction);
505 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
506 }
507
508 /**
509 * Returns the device ID of the ith device.
510 *
511 * @param i device to get the ID of
512 * @return the device ID
513 */
514 private static DeviceId getDeviceId(int i) {
515 return DeviceId.deviceId("" + i);
516 }
517
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700518 private static HostLocation getLocation(int i) {
519 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
520 }
521
Jonathan Hart704ca142014-10-09 09:34:39 -0700522 /**
523 * Builds an ARP packet with the given parameters.
524 *
525 * @param opcode opcode of the ARP packet
526 * @param srcMac source MAC address
527 * @param dstMac destination MAC address, or null if this is a request
528 * @param srcIp source IP address
529 * @param dstIp destination IP address
530 * @return the ARP packet
531 */
532 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800533 Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700534 Ethernet eth = new Ethernet();
535
536 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800537 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700538 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800539 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700540 }
541
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800542 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700543 eth.setEtherType(Ethernet.TYPE_ARP);
544 eth.setVlanID(VLAN1.toShort());
545
546 ARP arp = new ARP();
547 arp.setOpCode(opcode);
548 arp.setProtocolType(ARP.PROTO_TYPE_IP);
549 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
550
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800551 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700552 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800553 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700554
555 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800556 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700557 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800558 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700559 }
560
561 arp.setSenderProtocolAddress(srcIp.toOctets());
562 arp.setTargetProtocolAddress(dstIp.toOctets());
563
564 eth.setPayload(arp);
565 return eth;
566 }
567
568 /**
569 * Test PacketService implementation that simply stores OutboundPackets
570 * passed to {@link #emit(OutboundPacket)} for later verification.
571 */
572 class TestPacketService implements PacketService {
573
574 List<OutboundPacket> packets = new ArrayList<>();
575
576 @Override
577 public void addProcessor(PacketProcessor processor, int priority) {
578 }
579
580 @Override
581 public void removeProcessor(PacketProcessor processor) {
582 }
583
584 @Override
585 public void emit(OutboundPacket packet) {
586 packets.add(packet);
587 }
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800588
589 @Override
590 public void requestPackets(TrafficSelector selector,
591 PacketPriority priority, ApplicationId appId) {
592 }
Jonathan Hart704ca142014-10-09 09:34:39 -0700593 }
594}