blob: c364d8bb788959e43365774e51451a256ca01e3a [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;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080021import static org.easymock.EasyMock.expectLastCall;
Jonathan Hart704ca142014-10-09 09:34:39 -070022import static org.easymock.EasyMock.replay;
Jonathan Harta887ba82014-11-03 15:20:52 -080023import static org.junit.Assert.assertArrayEquals;
24import static org.junit.Assert.assertEquals;
25import static org.junit.Assert.assertFalse;
26import static org.junit.Assert.assertTrue;
Jonathan Hart704ca142014-10-09 09:34:39 -070027
28import java.util.ArrayList;
Jonathan Hart704ca142014-10-09 09:34:39 -070029import java.util.Collections;
30import java.util.Comparator;
31import java.util.List;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070032import java.util.Set;
Jonathan Hart704ca142014-10-09 09:34:39 -070033
34import org.junit.Before;
35import org.junit.Test;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.core.DefaultApplicationId;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.DefaultHost;
41import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.Host;
44import org.onosproject.net.HostId;
45import org.onosproject.net.HostLocation;
46import org.onosproject.net.Link;
47import org.onosproject.net.Port;
48import org.onosproject.net.PortNumber;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080051import org.onosproject.net.flow.FlowRule;
52import org.onosproject.net.flow.FlowRuleService;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.flow.instructions.Instruction;
54import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
55import org.onosproject.net.host.HostService;
56import org.onosproject.net.host.InterfaceIpAddress;
57import org.onosproject.net.host.PortAddresses;
58import org.onosproject.net.link.LinkListener;
59import org.onosproject.net.link.LinkService;
60import org.onosproject.net.packet.OutboundPacket;
61import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
63import org.onosproject.net.provider.ProviderId;
Jonathan Hart704ca142014-10-09 09:34:39 -070064import org.onlab.packet.ARP;
65import org.onlab.packet.Ethernet;
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080066import org.onlab.packet.Ip4Address;
67import org.onlab.packet.Ip4Prefix;
Jonathan Hart704ca142014-10-09 09:34:39 -070068import org.onlab.packet.MacAddress;
69import org.onlab.packet.VlanId;
70
71import com.google.common.collect.Sets;
72
73/**
74 * Tests for the {@link ProxyArpManager} class.
75 */
76public class ProxyArpManagerTest {
77
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070078 private static final int NUM_DEVICES = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070079 private static final int NUM_PORTS_PER_DEVICE = 3;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070080 private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
81 private static final int NUM_FLOOD_PORTS = 3;
Jonathan Hart704ca142014-10-09 09:34:39 -070082
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080083 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
84 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
Jonathan Hart704ca142014-10-09 09:34:39 -070085
86 private static final ProviderId PID = new ProviderId("of", "foo");
87
88 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
89 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
90 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
91 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
92 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
93 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
94
95 private static final DeviceId DID1 = getDeviceId(1);
96 private static final DeviceId DID2 = getDeviceId(2);
97 private static final PortNumber P1 = PortNumber.portNumber(1);
98 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
99 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800100 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700101
102 private ProxyArpManager proxyArp;
103
104 private TestPacketService packetService;
105
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800106 private CoreService coreService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700107 private DeviceService deviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800108 private FlowRuleService flowRuleService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700109 private LinkService linkService;
110 private HostService hostService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800111 private ApplicationId appId = new DefaultApplicationId((short) 100,
112 "org.onosproject.net.proxyarp");
Jonathan Hart704ca142014-10-09 09:34:39 -0700113
114 @Before
115 public void setUp() throws Exception {
116 proxyArp = new ProxyArpManager();
117 packetService = new TestPacketService();
118 proxyArp.packetService = packetService;
119
120 // Create a host service mock here. Must be replayed by tests once the
121 // expectations have been set up
122 hostService = createMock(HostService.class);
123 proxyArp.hostService = hostService;
124
125 createTopology();
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800126 proxyArp.coreService = coreService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700127 proxyArp.deviceService = deviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800128 proxyArp.flowRuleService = flowRuleService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700129 proxyArp.linkService = linkService;
130
131 proxyArp.activate();
132 }
133
134 /**
135 * Creates a fake topology to feed into the ARP module.
136 * <p/>
137 * The default topology is a unidirectional ring topology. Each switch has
138 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
139 * is free (edge port).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700140 * The first half of the switches have IP addresses configured on their
141 * free ports (port 1). The second half of the switches have no IP
142 * addresses configured.
Jonathan Hart704ca142014-10-09 09:34:39 -0700143 */
144 private void createTopology() {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800145 coreService = createMock(CoreService.class);
146 expect(coreService.registerApplication(appId.name()))
147 .andReturn(appId).anyTimes();
148 replay(coreService);
149
150 flowRuleService = createMock(FlowRuleService.class);
151 flowRuleService.applyFlowRules(anyObject(FlowRule.class));
152 expectLastCall().anyTimes();
153 replay(flowRuleService);
154
Jonathan Hart704ca142014-10-09 09:34:39 -0700155 deviceService = createMock(DeviceService.class);
156 linkService = createMock(LinkService.class);
157
158 deviceService.addListener(anyObject(DeviceListener.class));
159 linkService.addListener(anyObject(LinkListener.class));
160
161 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
162 createLinks(NUM_DEVICES);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700163 addAddressBindings();
Jonathan Hart704ca142014-10-09 09:34:39 -0700164 }
165
166 /**
167 * Creates the devices for the fake topology.
168 */
169 private void createDevices(int numDevices, int numPorts) {
170 List<Device> devices = new ArrayList<>();
171
172 for (int i = 1; i <= numDevices; i++) {
173 DeviceId devId = getDeviceId(i);
174 Device device = createMock(Device.class);
175 expect(device.id()).andReturn(devId).anyTimes();
176 replay(device);
177
178 devices.add(device);
179
180 List<Port> ports = new ArrayList<>();
181 for (int j = 1; j <= numPorts; j++) {
182 Port port = createMock(Port.class);
183 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
184 replay(port);
185 ports.add(port);
186 }
187
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700188 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
189 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700190 }
191
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700192 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700193 replay(deviceService);
194 }
195
196 /**
197 * Creates the links for the fake topology.
198 * NB: Only unidirectional links are created, as for this purpose all we
199 * need is to occupy the ports with some link.
200 */
201 private void createLinks(int numDevices) {
202 List<Link> links = new ArrayList<Link>();
203
204 for (int i = 1; i <= numDevices; i++) {
205 ConnectPoint src = new ConnectPoint(
206 getDeviceId(i),
207 PortNumber.portNumber(2));
208 ConnectPoint dst = new ConnectPoint(
209 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
210 PortNumber.portNumber(3));
211
212 Link link = createMock(Link.class);
213 expect(link.src()).andReturn(src).anyTimes();
214 expect(link.dst()).andReturn(dst).anyTimes();
215 replay(link);
216
217 links.add(link);
218 }
219
220 expect(linkService.getLinks()).andReturn(links).anyTimes();
221 replay(linkService);
222 }
223
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700224 private void addAddressBindings() {
225 Set<PortAddresses> addresses = Sets.newHashSet();
226
227 for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
228 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800229 Ip4Prefix prefix1 =
230 Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
231 Ip4Address addr1 =
232 Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
233 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
234 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700235 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
236 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Jonathan Harta887ba82014-11-03 15:20:52 -0800237 PortAddresses pa1 =
238 new PortAddresses(cp, Sets.newHashSet(ia1),
239 MacAddress.valueOf(2 * i - 1));
240 PortAddresses pa2 =
241 new PortAddresses(cp, Sets.newHashSet(ia2),
242 MacAddress.valueOf(2 * i));
243
244 addresses.add(pa1);
245 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700246
247 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800248 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700249 }
250
251 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
252
253 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
254 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
255 P1);
256 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800257 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700258 }
259 }
260
Jonathan Hart704ca142014-10-09 09:34:39 -0700261 /**
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800262 * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700263 * IP address is not known.
264 * Verifies the method returns false.
265 */
266 @Test
267 public void testNotKnown() {
268 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
269 replay(hostService);
270
271 assertFalse(proxyArp.known(IP1));
272 }
273
274 /**
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800275 * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700276 * IP address is known.
277 * Verifies the method returns true.
278 */
279 @Test
280 public void testKnown() {
281 Host host1 = createMock(Host.class);
282 Host host2 = createMock(Host.class);
283
284 expect(hostService.getHostsByIp(IP1))
285 .andReturn(Sets.newHashSet(host1, host2));
286 replay(hostService);
287
288 assertTrue(proxyArp.known(IP1));
289 }
290
291 /**
292 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
293 * destination host is known.
294 * Verifies the correct ARP reply is sent out the correct port.
295 */
296 @Test
297 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700298 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700299 Collections.singleton(IP1));
300
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700301 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700302 Collections.singleton(IP2));
303
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700304 expect(hostService.getHostsByIp(IP1))
305 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700306 expect(hostService.getHost(HID2)).andReturn(requestor);
307
308 replay(hostService);
309
310 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
311
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700312 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700313
314 assertEquals(1, packetService.packets.size());
315 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700316 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700317 }
318
319 /**
320 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
321 * destination host is not known.
322 * Verifies the ARP request is flooded out the correct edge ports.
323 */
324 @Test
325 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700326 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700327 Collections.singleton(IP2));
328
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700329 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700330 .andReturn(Collections.<Host>emptySet());
331 expect(hostService.getHost(HID2)).andReturn(requestor);
332
333 replay(hostService);
334
335 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
336
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700337 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700338
339 verifyFlood(arpRequest);
340 }
341
342 /**
343 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
344 * destination host is known for that IP address, but is not on the same
345 * VLAN as the source host.
346 * Verifies the ARP request is flooded out the correct edge ports.
347 */
348 @Test
349 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700350 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700351 Collections.singleton(IP1));
352
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700353 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700354 Collections.singleton(IP2));
355
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700356 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700357 .andReturn(Collections.singleton(replyer));
358 expect(hostService.getHost(HID2)).andReturn(requestor);
359
360 replay(hostService);
361
362 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
363
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700364 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700365
366 verifyFlood(arpRequest);
367 }
368
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700369 @Test
370 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800371 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
372 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
373 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800374 MacAddress firstMac = MacAddress.valueOf(1L);
375 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700376
377 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
378 Collections.singleton(theirIp));
379
380 expect(hostService.getHost(HID2)).andReturn(requestor);
381 replay(hostService);
382
383 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
384
385 proxyArp.reply(arpRequest, LOC1);
386
387 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800388 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700389 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
390
391 // Test a request for the second address on that port
392 packetService.packets.clear();
393 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
394
395 proxyArp.reply(arpRequest, LOC1);
396
397 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800398 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700399 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
400 }
401
402 @Test
403 public void testReplyExternalPortBadRequest() {
404 replay(hostService); // no further host service expectations
405
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800406 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700407
408 // Request for a valid external IP address but coming in the wrong port
409 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800410 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700411 proxyArp.reply(arpRequest, LOC1);
412 assertEquals(0, packetService.packets.size());
413
414 // Request for a valid internal IP address but coming in an external port
415 packetService.packets.clear();
416 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
417 proxyArp.reply(arpRequest, LOC1);
418 assertEquals(0, packetService.packets.size());
419 }
420
421 @Test
422 public void testReplyToRequestFromUs() {
423 replay(hostService); // no further host service expectations
424
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800425 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700426 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800427 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700428
429 // This is a request from something inside our network (like a BGP
430 // daemon) to an external host.
431 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
432 proxyArp.reply(arpRequest, getLocation(5));
433
434 assertEquals(1, packetService.packets.size());
435 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
436
437 // The same request from a random external port should fail
438 packetService.packets.clear();
439 proxyArp.reply(arpRequest, getLocation(2));
440 assertEquals(0, packetService.packets.size());
441 }
442
Jonathan Hart704ca142014-10-09 09:34:39 -0700443 /**
444 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
445 * destination host is known.
446 * Verifies the correct ARP request is sent out the correct port.
447 */
448 @Test
449 public void testForwardToHost() {
450 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
451 Collections.singleton(IP1));
452
453 expect(hostService.getHost(HID1)).andReturn(host1);
454 replay(hostService);
455
456 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
457
458 proxyArp.forward(arpRequest);
459
460 assertEquals(1, packetService.packets.size());
461 OutboundPacket packet = packetService.packets.get(0);
462
463 verifyPacketOut(arpRequest, LOC1, packet);
464 }
465
466 /**
467 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
468 * destination host is not known.
469 * Verifies the correct ARP request is flooded out the correct edge ports.
470 */
471 @Test
472 public void testForwardFlood() {
473 expect(hostService.getHost(HID1)).andReturn(null);
474 replay(hostService);
475
476 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
477
478 proxyArp.forward(arpRequest);
479
480 verifyFlood(arpRequest);
481 }
482
483 /**
484 * Verifies that the given packet was flooded out all available edge ports.
485 *
486 * @param packet the packet that was expected to be flooded
487 */
488 private void verifyFlood(Ethernet packet) {
489 assertEquals(NUM_FLOOD_PORTS, packetService.packets.size());
490
491 Collections.sort(packetService.packets,
492 new Comparator<OutboundPacket>() {
493 @Override
494 public int compare(OutboundPacket o1, OutboundPacket o2) {
495 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
496 }
497 });
498
499 for (int i = 0; i < NUM_FLOOD_PORTS; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700500 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
501 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700502
503 OutboundPacket outboundPacket = packetService.packets.get(i);
504 verifyPacketOut(packet, cp, outboundPacket);
505 }
506 }
507
508 /**
509 * Verifies the given packet was sent out the given port.
510 *
511 * @param expected the packet that was expected to be sent
512 * @param outPort the port the packet was expected to be sent out
513 * @param actual the actual OutboundPacket to verify
514 */
515 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
516 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800517 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700518 assertEquals(1, actual.treatment().instructions().size());
519 assertEquals(outPort.deviceId(), actual.sendThrough());
520 Instruction instruction = actual.treatment().instructions().get(0);
521 assertTrue(instruction instanceof OutputInstruction);
522 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
523 }
524
525 /**
526 * Returns the device ID of the ith device.
527 *
528 * @param i device to get the ID of
529 * @return the device ID
530 */
531 private static DeviceId getDeviceId(int i) {
532 return DeviceId.deviceId("" + i);
533 }
534
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700535 private static HostLocation getLocation(int i) {
536 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
537 }
538
Jonathan Hart704ca142014-10-09 09:34:39 -0700539 /**
540 * Builds an ARP packet with the given parameters.
541 *
542 * @param opcode opcode of the ARP packet
543 * @param srcMac source MAC address
544 * @param dstMac destination MAC address, or null if this is a request
545 * @param srcIp source IP address
546 * @param dstIp destination IP address
547 * @return the ARP packet
548 */
549 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800550 Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700551 Ethernet eth = new Ethernet();
552
553 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800554 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700555 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800556 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700557 }
558
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800559 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700560 eth.setEtherType(Ethernet.TYPE_ARP);
561 eth.setVlanID(VLAN1.toShort());
562
563 ARP arp = new ARP();
564 arp.setOpCode(opcode);
565 arp.setProtocolType(ARP.PROTO_TYPE_IP);
566 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
567
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800568 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700569 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800570 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700571
572 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800573 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700574 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800575 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700576 }
577
578 arp.setSenderProtocolAddress(srcIp.toOctets());
579 arp.setTargetProtocolAddress(dstIp.toOctets());
580
581 eth.setPayload(arp);
582 return eth;
583 }
584
585 /**
586 * Test PacketService implementation that simply stores OutboundPackets
587 * passed to {@link #emit(OutboundPacket)} for later verification.
588 */
589 class TestPacketService implements PacketService {
590
591 List<OutboundPacket> packets = new ArrayList<>();
592
593 @Override
594 public void addProcessor(PacketProcessor processor, int priority) {
595 }
596
597 @Override
598 public void removeProcessor(PacketProcessor processor) {
599 }
600
601 @Override
602 public void emit(OutboundPacket packet) {
603 packets.add(packet);
604 }
605 }
606}