blob: 6d909717294b16406b367f3d7d6e9db3592d432e [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;
Saurav Dasc313c402015-02-27 10:09:47 -080054import org.onosproject.net.flow.FlowRule;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080055import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import org.onosproject.net.flow.instructions.Instruction;
57import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
58import org.onosproject.net.host.HostService;
59import org.onosproject.net.host.InterfaceIpAddress;
60import org.onosproject.net.host.PortAddresses;
61import org.onosproject.net.link.LinkListener;
62import org.onosproject.net.link.LinkService;
63import org.onosproject.net.packet.OutboundPacket;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080064import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080065import org.onosproject.net.packet.PacketProcessor;
66import org.onosproject.net.packet.PacketService;
67import org.onosproject.net.provider.ProviderId;
Jonathan Hart704ca142014-10-09 09:34:39 -070068
69import com.google.common.collect.Sets;
70
71/**
72 * Tests for the {@link ProxyArpManager} class.
73 */
74public class ProxyArpManagerTest {
75
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070076 private static final int NUM_DEVICES = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070077 private static final int NUM_PORTS_PER_DEVICE = 3;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070078 private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
79 private static final int NUM_FLOOD_PORTS = 3;
Jonathan Hart704ca142014-10-09 09:34:39 -070080
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080081 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
82 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
Jonathan Hart704ca142014-10-09 09:34:39 -070083
84 private static final ProviderId PID = new ProviderId("of", "foo");
85
86 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
87 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
88 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
89 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
90 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
91 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
92
93 private static final DeviceId DID1 = getDeviceId(1);
94 private static final DeviceId DID2 = getDeviceId(2);
95 private static final PortNumber P1 = PortNumber.portNumber(1);
96 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
97 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080098 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart704ca142014-10-09 09:34:39 -070099
100 private ProxyArpManager proxyArp;
101
102 private TestPacketService packetService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700103 private DeviceService deviceService;
104 private LinkService linkService;
105 private HostService hostService;
106
107 @Before
108 public void setUp() throws Exception {
109 proxyArp = new ProxyArpManager();
110 packetService = new TestPacketService();
111 proxyArp.packetService = packetService;
112
113 // Create a host service mock here. Must be replayed by tests once the
114 // expectations have been set up
115 hostService = createMock(HostService.class);
116 proxyArp.hostService = hostService;
117
118 createTopology();
119 proxyArp.deviceService = deviceService;
120 proxyArp.linkService = linkService;
121
122 proxyArp.activate();
123 }
124
125 /**
126 * Creates a fake topology to feed into the ARP module.
127 * <p/>
128 * The default topology is a unidirectional ring topology. Each switch has
129 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
130 * is free (edge port).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700131 * The first half of the switches have IP addresses configured on their
132 * free ports (port 1). The second half of the switches have no IP
133 * addresses configured.
Jonathan Hart704ca142014-10-09 09:34:39 -0700134 */
135 private void createTopology() {
136 deviceService = createMock(DeviceService.class);
137 linkService = createMock(LinkService.class);
138
139 deviceService.addListener(anyObject(DeviceListener.class));
140 linkService.addListener(anyObject(LinkListener.class));
141
142 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
143 createLinks(NUM_DEVICES);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700144 addAddressBindings();
Jonathan Hart704ca142014-10-09 09:34:39 -0700145 }
146
147 /**
148 * Creates the devices for the fake topology.
149 */
150 private void createDevices(int numDevices, int numPorts) {
151 List<Device> devices = new ArrayList<>();
152
153 for (int i = 1; i <= numDevices; i++) {
154 DeviceId devId = getDeviceId(i);
155 Device device = createMock(Device.class);
156 expect(device.id()).andReturn(devId).anyTimes();
157 replay(device);
158
159 devices.add(device);
160
161 List<Port> ports = new ArrayList<>();
162 for (int j = 1; j <= numPorts; j++) {
163 Port port = createMock(Port.class);
164 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
165 replay(port);
166 ports.add(port);
167 }
168
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700169 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
170 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700171 }
172
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700173 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700174 replay(deviceService);
175 }
176
177 /**
178 * Creates the links for the fake topology.
179 * NB: Only unidirectional links are created, as for this purpose all we
180 * need is to occupy the ports with some link.
181 */
182 private void createLinks(int numDevices) {
183 List<Link> links = new ArrayList<Link>();
184
185 for (int i = 1; i <= numDevices; i++) {
186 ConnectPoint src = new ConnectPoint(
187 getDeviceId(i),
188 PortNumber.portNumber(2));
189 ConnectPoint dst = new ConnectPoint(
190 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
191 PortNumber.portNumber(3));
192
193 Link link = createMock(Link.class);
194 expect(link.src()).andReturn(src).anyTimes();
195 expect(link.dst()).andReturn(dst).anyTimes();
196 replay(link);
197
198 links.add(link);
199 }
200
201 expect(linkService.getLinks()).andReturn(links).anyTimes();
202 replay(linkService);
203 }
204
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700205 private void addAddressBindings() {
206 Set<PortAddresses> addresses = Sets.newHashSet();
207
208 for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
209 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800210 Ip4Prefix prefix1 =
211 Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
212 Ip4Address addr1 =
213 Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
214 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
215 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700216 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
217 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Jonathan Harta887ba82014-11-03 15:20:52 -0800218 PortAddresses pa1 =
219 new PortAddresses(cp, Sets.newHashSet(ia1),
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800220 MacAddress.valueOf(2 * i - 1),
221 VlanId.vlanId((short) 1));
Jonathan Harta887ba82014-11-03 15:20:52 -0800222 PortAddresses pa2 =
223 new PortAddresses(cp, Sets.newHashSet(ia2),
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800224 MacAddress.valueOf(2 * i),
225 VlanId.NONE);
Jonathan Harta887ba82014-11-03 15:20:52 -0800226
227 addresses.add(pa1);
228 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700229
230 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800231 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700232 }
233
234 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
235
236 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
237 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
238 P1);
239 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800240 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700241 }
242 }
243
Jonathan Hart704ca142014-10-09 09:34:39 -0700244 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800245 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700246 * IP address is not known.
247 * Verifies the method returns false.
248 */
249 @Test
250 public void testNotKnown() {
251 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
252 replay(hostService);
253
Jonathan Hartf84591d2015-01-16 14:33:43 -0800254 assertFalse(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700255 }
256
257 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800258 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700259 * IP address is known.
260 * Verifies the method returns true.
261 */
262 @Test
263 public void testKnown() {
264 Host host1 = createMock(Host.class);
265 Host host2 = createMock(Host.class);
266
267 expect(hostService.getHostsByIp(IP1))
268 .andReturn(Sets.newHashSet(host1, host2));
269 replay(hostService);
270
Jonathan Hartf84591d2015-01-16 14:33:43 -0800271 assertTrue(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700272 }
273
274 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800275 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700276 * destination host is known.
277 * Verifies the correct ARP reply is sent out the correct port.
278 */
279 @Test
280 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700281 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700282 Collections.singleton(IP1));
283
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700284 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700285 Collections.singleton(IP2));
286
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700287 expect(hostService.getHostsByIp(IP1))
288 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700289 expect(hostService.getHost(HID2)).andReturn(requestor);
290
291 replay(hostService);
292
293 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
294
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700295 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700296
297 assertEquals(1, packetService.packets.size());
298 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700299 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700300 }
301
302 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800303 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700304 * destination host is not known.
305 * Verifies the ARP request is flooded out the correct edge ports.
306 */
307 @Test
308 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700309 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700310 Collections.singleton(IP2));
311
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700312 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700313 .andReturn(Collections.<Host>emptySet());
314 expect(hostService.getHost(HID2)).andReturn(requestor);
315
316 replay(hostService);
317
318 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
319
Jonathan Hartf84591d2015-01-16 14:33:43 -0800320 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700321
322 verifyFlood(arpRequest);
323 }
324
325 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800326 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700327 * destination host is known for that IP address, but is not on the same
328 * VLAN as the source host.
329 * Verifies the ARP request is flooded out the correct edge ports.
330 */
331 @Test
332 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700333 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700334 Collections.singleton(IP1));
335
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700336 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700337 Collections.singleton(IP2));
338
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700339 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700340 .andReturn(Collections.singleton(replyer));
341 expect(hostService.getHost(HID2)).andReturn(requestor);
342
343 replay(hostService);
344
345 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
346
Jonathan Hartf84591d2015-01-16 14:33:43 -0800347 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700348
349 verifyFlood(arpRequest);
350 }
351
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700352 @Test
353 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800354 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
355 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
356 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800357 MacAddress firstMac = MacAddress.valueOf(1L);
358 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700359
360 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
361 Collections.singleton(theirIp));
362
363 expect(hostService.getHost(HID2)).andReturn(requestor);
364 replay(hostService);
365
366 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
367
368 proxyArp.reply(arpRequest, LOC1);
369
370 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800371 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700372 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
373
374 // Test a request for the second address on that port
375 packetService.packets.clear();
376 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
377
378 proxyArp.reply(arpRequest, LOC1);
379
380 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800381 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700382 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
383 }
384
385 @Test
386 public void testReplyExternalPortBadRequest() {
387 replay(hostService); // no further host service expectations
388
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800389 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700390
391 // Request for a valid external IP address but coming in the wrong port
392 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800393 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700394 proxyArp.reply(arpRequest, LOC1);
395 assertEquals(0, packetService.packets.size());
396
397 // Request for a valid internal IP address but coming in an external port
398 packetService.packets.clear();
399 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
400 proxyArp.reply(arpRequest, LOC1);
401 assertEquals(0, packetService.packets.size());
402 }
403
404 @Test
405 public void testReplyToRequestFromUs() {
406 replay(hostService); // no further host service expectations
407
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800408 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700409 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800410 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700411
412 // This is a request from something inside our network (like a BGP
413 // daemon) to an external host.
414 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
415 proxyArp.reply(arpRequest, getLocation(5));
416
417 assertEquals(1, packetService.packets.size());
418 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
419
420 // The same request from a random external port should fail
421 packetService.packets.clear();
422 proxyArp.reply(arpRequest, getLocation(2));
423 assertEquals(0, packetService.packets.size());
424 }
425
Jonathan Hart704ca142014-10-09 09:34:39 -0700426 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800427 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700428 * destination host is known.
429 * Verifies the correct ARP request is sent out the correct port.
430 */
431 @Test
432 public void testForwardToHost() {
433 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
434 Collections.singleton(IP1));
435
436 expect(hostService.getHost(HID1)).andReturn(host1);
437 replay(hostService);
438
439 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
440
Jonathan Hartf84591d2015-01-16 14:33:43 -0800441 proxyArp.forward(arpRequest, LOC2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700442
443 assertEquals(1, packetService.packets.size());
444 OutboundPacket packet = packetService.packets.get(0);
445
446 verifyPacketOut(arpRequest, LOC1, packet);
447 }
448
449 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800450 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700451 * destination host is not known.
452 * Verifies the correct ARP request is flooded out the correct edge ports.
453 */
454 @Test
455 public void testForwardFlood() {
456 expect(hostService.getHost(HID1)).andReturn(null);
457 replay(hostService);
458
459 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
460
Jonathan Hartf84591d2015-01-16 14:33:43 -0800461 proxyArp.forward(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700462
463 verifyFlood(arpRequest);
464 }
465
466 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800467 * Verifies that the given packet was flooded out all available edge ports,
468 * except for the input port.
Jonathan Hart704ca142014-10-09 09:34:39 -0700469 *
470 * @param packet the packet that was expected to be flooded
471 */
472 private void verifyFlood(Ethernet packet) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800473 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
474 assertEquals(NUM_FLOOD_PORTS - 1, packetService.packets.size());
Jonathan Hart704ca142014-10-09 09:34:39 -0700475
476 Collections.sort(packetService.packets,
477 new Comparator<OutboundPacket>() {
478 @Override
479 public int compare(OutboundPacket o1, OutboundPacket o2) {
480 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
481 }
482 });
483
Jonathan Hartf84591d2015-01-16 14:33:43 -0800484
485 for (int i = 0; i < NUM_FLOOD_PORTS - 1; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700486 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
487 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700488
489 OutboundPacket outboundPacket = packetService.packets.get(i);
490 verifyPacketOut(packet, cp, outboundPacket);
491 }
492 }
493
494 /**
495 * Verifies the given packet was sent out the given port.
496 *
497 * @param expected the packet that was expected to be sent
498 * @param outPort the port the packet was expected to be sent out
499 * @param actual the actual OutboundPacket to verify
500 */
501 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
502 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800503 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700504 assertEquals(1, actual.treatment().instructions().size());
505 assertEquals(outPort.deviceId(), actual.sendThrough());
506 Instruction instruction = actual.treatment().instructions().get(0);
507 assertTrue(instruction instanceof OutputInstruction);
508 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
509 }
510
511 /**
512 * Returns the device ID of the ith device.
513 *
514 * @param i device to get the ID of
515 * @return the device ID
516 */
517 private static DeviceId getDeviceId(int i) {
518 return DeviceId.deviceId("" + i);
519 }
520
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700521 private static HostLocation getLocation(int i) {
522 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
523 }
524
Jonathan Hart704ca142014-10-09 09:34:39 -0700525 /**
526 * Builds an ARP packet with the given parameters.
527 *
528 * @param opcode opcode of the ARP packet
529 * @param srcMac source MAC address
530 * @param dstMac destination MAC address, or null if this is a request
531 * @param srcIp source IP address
532 * @param dstIp destination IP address
533 * @return the ARP packet
534 */
535 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800536 Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700537 Ethernet eth = new Ethernet();
538
539 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800540 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700541 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800542 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700543 }
544
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800545 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700546 eth.setEtherType(Ethernet.TYPE_ARP);
547 eth.setVlanID(VLAN1.toShort());
548
549 ARP arp = new ARP();
550 arp.setOpCode(opcode);
551 arp.setProtocolType(ARP.PROTO_TYPE_IP);
552 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
553
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800554 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700555 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800556 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700557
558 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800559 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700560 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800561 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700562 }
563
564 arp.setSenderProtocolAddress(srcIp.toOctets());
565 arp.setTargetProtocolAddress(dstIp.toOctets());
566
567 eth.setPayload(arp);
568 return eth;
569 }
570
571 /**
572 * Test PacketService implementation that simply stores OutboundPackets
573 * passed to {@link #emit(OutboundPacket)} for later verification.
574 */
575 class TestPacketService implements PacketService {
576
577 List<OutboundPacket> packets = new ArrayList<>();
578
579 @Override
580 public void addProcessor(PacketProcessor processor, int priority) {
581 }
582
583 @Override
584 public void removeProcessor(PacketProcessor processor) {
585 }
586
587 @Override
588 public void emit(OutboundPacket packet) {
589 packets.add(packet);
590 }
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800591
592 @Override
593 public void requestPackets(TrafficSelector selector,
594 PacketPriority priority, ApplicationId appId) {
595 }
Saurav Dasc313c402015-02-27 10:09:47 -0800596
597 @Override
598 public void requestPackets(TrafficSelector selector,
599 PacketPriority priority, ApplicationId appId,
600 FlowRule.Type tableType) {
601 }
Jonathan Hart704ca142014-10-09 09:34:39 -0700602 }
603}