blob: ca218c61502eac02d435eaa849f7e29a35fcd5f4 [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),
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800219 MacAddress.valueOf(2 * i - 1),
220 VlanId.vlanId((short) 1));
Jonathan Harta887ba82014-11-03 15:20:52 -0800221 PortAddresses pa2 =
222 new PortAddresses(cp, Sets.newHashSet(ia2),
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800223 MacAddress.valueOf(2 * i),
224 VlanId.NONE);
Jonathan Harta887ba82014-11-03 15:20:52 -0800225
226 addresses.add(pa1);
227 addresses.add(pa2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700228
229 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800230 .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700231 }
232
233 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
234
235 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
236 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
237 P1);
238 expect(hostService.getAddressBindingsForPort(cp))
Jonathan Harta887ba82014-11-03 15:20:52 -0800239 .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700240 }
241 }
242
Jonathan Hart704ca142014-10-09 09:34:39 -0700243 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800244 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700245 * IP address is not known.
246 * Verifies the method returns false.
247 */
248 @Test
249 public void testNotKnown() {
250 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
251 replay(hostService);
252
Jonathan Hartf84591d2015-01-16 14:33:43 -0800253 assertFalse(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700254 }
255
256 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800257 * Tests {@link ProxyArpManager#isKnown(Ip4Address)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700258 * IP address is known.
259 * Verifies the method returns true.
260 */
261 @Test
262 public void testKnown() {
263 Host host1 = createMock(Host.class);
264 Host host2 = createMock(Host.class);
265
266 expect(hostService.getHostsByIp(IP1))
267 .andReturn(Sets.newHashSet(host1, host2));
268 replay(hostService);
269
Jonathan Hartf84591d2015-01-16 14:33:43 -0800270 assertTrue(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700271 }
272
273 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800274 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700275 * destination host is known.
276 * Verifies the correct ARP reply is sent out the correct port.
277 */
278 @Test
279 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700280 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700281 Collections.singleton(IP1));
282
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700283 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700284 Collections.singleton(IP2));
285
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700286 expect(hostService.getHostsByIp(IP1))
287 .andReturn(Collections.singleton(replyer));
Jonathan Hart704ca142014-10-09 09:34:39 -0700288 expect(hostService.getHost(HID2)).andReturn(requestor);
289
290 replay(hostService);
291
292 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
293
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700294 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700295
296 assertEquals(1, packetService.packets.size());
297 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700298 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700299 }
300
301 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800302 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700303 * destination host is not known.
304 * Verifies the ARP request is flooded out the correct edge ports.
305 */
306 @Test
307 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700308 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700309 Collections.singleton(IP2));
310
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700311 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700312 .andReturn(Collections.<Host>emptySet());
313 expect(hostService.getHost(HID2)).andReturn(requestor);
314
315 replay(hostService);
316
317 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
318
Jonathan Hartf84591d2015-01-16 14:33:43 -0800319 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700320
321 verifyFlood(arpRequest);
322 }
323
324 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800325 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700326 * destination host is known for that IP address, but is not on the same
327 * VLAN as the source host.
328 * Verifies the ARP request is flooded out the correct edge ports.
329 */
330 @Test
331 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700332 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700333 Collections.singleton(IP1));
334
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700335 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700336 Collections.singleton(IP2));
337
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700338 expect(hostService.getHostsByIp(IP1))
Jonathan Hart704ca142014-10-09 09:34:39 -0700339 .andReturn(Collections.singleton(replyer));
340 expect(hostService.getHost(HID2)).andReturn(requestor);
341
342 replay(hostService);
343
344 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
345
Jonathan Hartf84591d2015-01-16 14:33:43 -0800346 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700347
348 verifyFlood(arpRequest);
349 }
350
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700351 @Test
352 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800353 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
354 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
355 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
Jonathan Harta887ba82014-11-03 15:20:52 -0800356 MacAddress firstMac = MacAddress.valueOf(1L);
357 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700358
359 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
360 Collections.singleton(theirIp));
361
362 expect(hostService.getHost(HID2)).andReturn(requestor);
363 replay(hostService);
364
365 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
366
367 proxyArp.reply(arpRequest, LOC1);
368
369 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800370 Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700371 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
372
373 // Test a request for the second address on that port
374 packetService.packets.clear();
375 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
376
377 proxyArp.reply(arpRequest, LOC1);
378
379 assertEquals(1, packetService.packets.size());
Jonathan Harta887ba82014-11-03 15:20:52 -0800380 arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700381 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
382 }
383
384 @Test
385 public void testReplyExternalPortBadRequest() {
386 replay(hostService); // no further host service expectations
387
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800388 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700389
390 // Request for a valid external IP address but coming in the wrong port
391 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800392 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700393 proxyArp.reply(arpRequest, LOC1);
394 assertEquals(0, packetService.packets.size());
395
396 // Request for a valid internal IP address but coming in an external port
397 packetService.packets.clear();
398 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
399 proxyArp.reply(arpRequest, LOC1);
400 assertEquals(0, packetService.packets.size());
401 }
402
403 @Test
404 public void testReplyToRequestFromUs() {
405 replay(hostService); // no further host service expectations
406
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800407 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700408 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800409 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700410
411 // This is a request from something inside our network (like a BGP
412 // daemon) to an external host.
413 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
414 proxyArp.reply(arpRequest, getLocation(5));
415
416 assertEquals(1, packetService.packets.size());
417 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
418
419 // The same request from a random external port should fail
420 packetService.packets.clear();
421 proxyArp.reply(arpRequest, getLocation(2));
422 assertEquals(0, packetService.packets.size());
423 }
424
Jonathan Hart704ca142014-10-09 09:34:39 -0700425 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800426 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700427 * destination host is known.
428 * Verifies the correct ARP request is sent out the correct port.
429 */
430 @Test
431 public void testForwardToHost() {
432 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
433 Collections.singleton(IP1));
434
435 expect(hostService.getHost(HID1)).andReturn(host1);
436 replay(hostService);
437
438 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
439
Jonathan Hartf84591d2015-01-16 14:33:43 -0800440 proxyArp.forward(arpRequest, LOC2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700441
442 assertEquals(1, packetService.packets.size());
443 OutboundPacket packet = packetService.packets.get(0);
444
445 verifyPacketOut(arpRequest, LOC1, packet);
446 }
447
448 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800449 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700450 * destination host is not known.
451 * Verifies the correct ARP request is flooded out the correct edge ports.
452 */
453 @Test
454 public void testForwardFlood() {
455 expect(hostService.getHost(HID1)).andReturn(null);
456 replay(hostService);
457
458 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
459
Jonathan Hartf84591d2015-01-16 14:33:43 -0800460 proxyArp.forward(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700461
462 verifyFlood(arpRequest);
463 }
464
465 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -0800466 * Verifies that the given packet was flooded out all available edge ports,
467 * except for the input port.
Jonathan Hart704ca142014-10-09 09:34:39 -0700468 *
469 * @param packet the packet that was expected to be flooded
470 */
471 private void verifyFlood(Ethernet packet) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800472 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
473 assertEquals(NUM_FLOOD_PORTS - 1, packetService.packets.size());
Jonathan Hart704ca142014-10-09 09:34:39 -0700474
475 Collections.sort(packetService.packets,
476 new Comparator<OutboundPacket>() {
477 @Override
478 public int compare(OutboundPacket o1, OutboundPacket o2) {
479 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
480 }
481 });
482
Jonathan Hartf84591d2015-01-16 14:33:43 -0800483
484 for (int i = 0; i < NUM_FLOOD_PORTS - 1; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700485 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
486 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700487
488 OutboundPacket outboundPacket = packetService.packets.get(i);
489 verifyPacketOut(packet, cp, outboundPacket);
490 }
491 }
492
493 /**
494 * Verifies the given packet was sent out the given port.
495 *
496 * @param expected the packet that was expected to be sent
497 * @param outPort the port the packet was expected to be sent out
498 * @param actual the actual OutboundPacket to verify
499 */
500 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
501 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800502 assertArrayEquals(expected.serialize(), actual.data().array());
Jonathan Hart704ca142014-10-09 09:34:39 -0700503 assertEquals(1, actual.treatment().instructions().size());
504 assertEquals(outPort.deviceId(), actual.sendThrough());
505 Instruction instruction = actual.treatment().instructions().get(0);
506 assertTrue(instruction instanceof OutputInstruction);
507 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
508 }
509
510 /**
511 * Returns the device ID of the ith device.
512 *
513 * @param i device to get the ID of
514 * @return the device ID
515 */
516 private static DeviceId getDeviceId(int i) {
517 return DeviceId.deviceId("" + i);
518 }
519
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700520 private static HostLocation getLocation(int i) {
521 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
522 }
523
Jonathan Hart704ca142014-10-09 09:34:39 -0700524 /**
525 * Builds an ARP packet with the given parameters.
526 *
527 * @param opcode opcode of the ARP packet
528 * @param srcMac source MAC address
529 * @param dstMac destination MAC address, or null if this is a request
530 * @param srcIp source IP address
531 * @param dstIp destination IP address
532 * @return the ARP packet
533 */
534 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800535 Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -0700536 Ethernet eth = new Ethernet();
537
538 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800539 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -0700540 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800541 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700542 }
543
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800544 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -0700545 eth.setEtherType(Ethernet.TYPE_ARP);
546 eth.setVlanID(VLAN1.toShort());
547
548 ARP arp = new ARP();
549 arp.setOpCode(opcode);
550 arp.setProtocolType(ARP.PROTO_TYPE_IP);
551 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
552
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800553 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -0700554 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800555 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700556
557 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800558 arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
Jonathan Hart704ca142014-10-09 09:34:39 -0700559 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800560 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -0700561 }
562
563 arp.setSenderProtocolAddress(srcIp.toOctets());
564 arp.setTargetProtocolAddress(dstIp.toOctets());
565
566 eth.setPayload(arp);
567 return eth;
568 }
569
570 /**
571 * Test PacketService implementation that simply stores OutboundPackets
572 * passed to {@link #emit(OutboundPacket)} for later verification.
573 */
574 class TestPacketService implements PacketService {
575
576 List<OutboundPacket> packets = new ArrayList<>();
577
578 @Override
579 public void addProcessor(PacketProcessor processor, int priority) {
580 }
581
582 @Override
583 public void removeProcessor(PacketProcessor processor) {
584 }
585
586 @Override
587 public void emit(OutboundPacket packet) {
588 packets.add(packet);
589 }
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800590
591 @Override
592 public void requestPackets(TrafficSelector selector,
593 PacketPriority priority, ApplicationId appId) {
594 }
Jonathan Hart704ca142014-10-09 09:34:39 -0700595 }
596}