1a160d98afcd918b3245e1c59d9e576e67453907
[onosfw.git] /
1 /*
2  * Copyright 2014-2015 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  */
16 package org.onosproject.net.proxyarp.impl;
17
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Sets;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.onlab.packet.ARP;
23 import org.onlab.packet.Ethernet;
24 import org.onlab.packet.Ip4Address;
25 import org.onlab.packet.Ip4Prefix;
26 import org.onlab.packet.IpPrefix;
27 import org.onlab.packet.MacAddress;
28 import org.onlab.packet.VlanId;
29 import org.onosproject.incubator.net.intf.Interface;
30 import org.onosproject.incubator.net.intf.InterfaceService;
31 import org.onosproject.net.ConnectPoint;
32 import org.onosproject.net.DefaultHost;
33 import org.onosproject.net.Device;
34 import org.onosproject.net.DeviceId;
35 import org.onosproject.net.Host;
36 import org.onosproject.net.HostId;
37 import org.onosproject.net.HostLocation;
38 import org.onosproject.net.Link;
39 import org.onosproject.net.Port;
40 import org.onosproject.net.PortNumber;
41 import org.onosproject.net.device.DeviceListener;
42 import org.onosproject.net.device.DeviceService;
43 import org.onosproject.net.edgeservice.impl.EdgeManager;
44 import org.onosproject.net.flow.DefaultTrafficTreatment;
45 import org.onosproject.net.flow.TrafficTreatment;
46 import org.onosproject.net.flow.instructions.Instruction;
47 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
48 import org.onosproject.net.host.HostService;
49 import org.onosproject.net.host.InterfaceIpAddress;
50 import org.onosproject.net.link.LinkListener;
51 import org.onosproject.net.link.LinkService;
52 import org.onosproject.net.packet.DefaultOutboundPacket;
53 import org.onosproject.net.packet.OutboundPacket;
54 import org.onosproject.net.packet.PacketServiceAdapter;
55 import org.onosproject.net.provider.ProviderId;
56 import org.onosproject.net.proxyarp.ProxyArpStore;
57 import org.onosproject.net.proxyarp.ProxyArpStoreDelegate;
58
59 import java.nio.ByteBuffer;
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.Set;
64
65 import static org.easymock.EasyMock.anyObject;
66 import static org.easymock.EasyMock.createMock;
67 import static org.easymock.EasyMock.expect;
68 import static org.easymock.EasyMock.replay;
69 import static org.junit.Assert.assertArrayEquals;
70 import static org.junit.Assert.assertEquals;
71 import static org.junit.Assert.assertFalse;
72 import static org.junit.Assert.assertTrue;
73
74 /**
75  * Tests for the {@link ProxyArpManager} class.
76  */
77 public class ProxyArpManagerTest {
78
79     private static final int NUM_DEVICES = 6;
80     private static final int NUM_PORTS_PER_DEVICE = 3;
81     private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
82     private static final int NUM_FLOOD_PORTS = 3;
83
84     private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
85     private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
86
87     private static final ProviderId PID = new ProviderId("of", "foo");
88
89     private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
90     private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
91     private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
92     private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
93     private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
94     private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
95
96     private static final DeviceId DID1 = getDeviceId(1);
97     private static final DeviceId DID2 = getDeviceId(2);
98     private static final PortNumber P1 = PortNumber.portNumber(1);
99     private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
100     private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
101     private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
102
103     //Return values used for various functions of the TestPacketService inner class.
104     private boolean isEdgePointReturn;
105     private List<ConnectPoint> getEdgePointsNoArg;
106
107
108     private ProxyArpManager proxyArp;
109
110     private TestPacketService packetService;
111     private DeviceService deviceService;
112     private LinkService linkService;
113     private HostService hostService;
114     private InterfaceService interfaceService;
115
116     @Before
117     public void setUp() throws Exception {
118         proxyArp = new ProxyArpManager();
119         packetService = new TestPacketService();
120         proxyArp.packetService = packetService;
121         proxyArp.store = new TestProxyArpStoreAdapter();
122
123         proxyArp.edgeService = new TestEdgePortService();
124
125         // Create a host service mock here. Must be replayed by tests once the
126         // expectations have been set up
127         hostService = createMock(HostService.class);
128         proxyArp.hostService = hostService;
129
130         interfaceService = createMock(InterfaceService.class);
131         proxyArp.interfaceService = interfaceService;
132
133         createTopology();
134         proxyArp.deviceService = deviceService;
135         proxyArp.linkService = linkService;
136
137         proxyArp.activate();
138     }
139
140     /**
141      * Creates a fake topology to feed into the ARP module.
142      * <p>
143      * The default topology is a unidirectional ring topology. Each switch has
144      * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
145      * is free (edge port).
146      * The first half of the switches have IP addresses configured on their
147      * free ports (port 1). The second half of the switches have no IP
148      * addresses configured.
149      */
150     private void createTopology() {
151         deviceService = createMock(DeviceService.class);
152         linkService = createMock(LinkService.class);
153
154         deviceService.addListener(anyObject(DeviceListener.class));
155         linkService.addListener(anyObject(LinkListener.class));
156
157         createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
158         createLinks(NUM_DEVICES);
159         addAddressBindings();
160     }
161
162     /**
163      * Creates the devices for the fake topology.
164      */
165     private void createDevices(int numDevices, int numPorts) {
166         List<Device> devices = new ArrayList<>();
167
168         for (int i = 1; i <= numDevices; i++) {
169             DeviceId devId = getDeviceId(i);
170             Device device = createMock(Device.class);
171             expect(device.id()).andReturn(devId).anyTimes();
172             replay(device);
173
174             devices.add(device);
175
176             List<Port> ports = new ArrayList<>();
177             for (int j = 1; j <= numPorts; j++) {
178                 Port port = createMock(Port.class);
179                 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
180                 replay(port);
181                 ports.add(port);
182             }
183
184             expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
185             expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
186         }
187
188         expect(deviceService.getDevices()).andReturn(devices).anyTimes();
189         replay(deviceService);
190     }
191
192     /**
193      * Creates the links for the fake topology.
194      * NB: Only unidirectional links are created, as for this purpose all we
195      * need is to occupy the ports with some link.
196      */
197     private void createLinks(int numDevices) {
198         List<Link> links = new ArrayList<>();
199
200         for (int i = 1; i <= numDevices; i++) {
201             ConnectPoint src = new ConnectPoint(
202                     getDeviceId(i),
203                     PortNumber.portNumber(2));
204             ConnectPoint dst = new ConnectPoint(
205                     getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
206                     PortNumber.portNumber(3));
207
208             Link link = createMock(Link.class);
209             expect(link.src()).andReturn(src).anyTimes();
210             expect(link.dst()).andReturn(dst).anyTimes();
211             replay(link);
212
213             links.add(link);
214         }
215
216         expect(linkService.getLinks()).andReturn(links).anyTimes();
217         replay(linkService);
218     }
219
220     private void addAddressBindings() {
221         Set<Interface> interfaces = Sets.newHashSet();
222
223         for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
224             ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
225             Ip4Prefix prefix1 =
226                     Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
227             Ip4Address addr1 =
228                     Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
229             Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
230             Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
231             InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
232             InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
233             Interface intf1 = new Interface(cp, Sets.newHashSet(ia1),
234                     MacAddress.valueOf(2 * i - 1),
235                     VlanId.vlanId((short) 1));
236             Interface intf2 = new Interface(cp, Sets.newHashSet(ia2),
237                     MacAddress.valueOf(2 * i),
238                     VlanId.NONE);
239
240             interfaces.add(intf1);
241             interfaces.add(intf2);
242
243             expect(interfaceService.getInterfacesByPort(cp))
244                     .andReturn(Sets.newHashSet(intf1, intf2)).anyTimes();
245         }
246
247         expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
248
249         for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
250             ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
251                     P1);
252
253             expect(interfaceService.getInterfacesByPort(cp))
254                     .andReturn(Collections.emptySet()).anyTimes();
255         }
256     }
257
258     /**
259      * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
260      * case where the IP address is not known.
261      * Verifies the method returns false.
262      */
263     @Test
264     public void testNotKnown() {
265         expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
266         replay(hostService);
267         replay(interfaceService);
268
269         assertFalse(proxyArp.isKnown(IP1));
270     }
271
272     /**
273      * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
274      * case where the IP address is known.
275      * Verifies the method returns true.
276      */
277     @Test
278     public void testKnown() {
279         Host host1 = createMock(Host.class);
280         Host host2 = createMock(Host.class);
281
282         expect(hostService.getHostsByIp(IP1))
283                 .andReturn(Sets.newHashSet(host1, host2));
284         replay(hostService);
285         replay(interfaceService);
286
287         assertTrue(proxyArp.isKnown(IP1));
288     }
289
290     /**
291      * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
292      * destination host is known.
293      * Verifies the correct ARP reply is sent out the correct port.
294      */
295     @Test
296     public void testReplyKnown() {
297         //Set the return value of isEdgePoint from the edgemanager.
298         isEdgePointReturn = true;
299
300         Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
301                 Collections.singleton(IP1));
302
303         Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
304                 Collections.singleton(IP2));
305
306         expect(hostService.getHostsByIp(IP1))
307                 .andReturn(Collections.singleton(replyer));
308         expect(hostService.getHost(HID2)).andReturn(requestor);
309
310         replay(hostService);
311         replay(interfaceService);
312
313         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
314
315         proxyArp.reply(arpRequest, getLocation(5));
316
317         assertEquals(1, packetService.packets.size());
318         Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
319         verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
320     }
321
322     /**
323      * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
324      * destination host is not known.
325      * Verifies the ARP request is flooded out the correct edge ports.
326      */
327     @Test
328     public void testReplyUnknown() {
329         isEdgePointReturn = true;
330
331         Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
332                 Collections.singleton(IP2));
333
334         expect(hostService.getHostsByIp(IP1))
335                 .andReturn(Collections.emptySet());
336         expect(interfaceService.getInterfacesByIp(IP2))
337                 .andReturn(Collections.emptySet());
338         expect(hostService.getHost(HID2)).andReturn(requestor);
339
340
341         replay(hostService);
342         replay(interfaceService);
343
344         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
345
346         //Setup the set of edge ports to be used in the reply method
347         getEdgePointsNoArg = Lists.newLinkedList();
348         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1)));
349         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1)));
350
351         proxyArp.reply(arpRequest, getLocation(6));
352
353         verifyFlood(arpRequest);
354     }
355
356     /**
357      * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
358      * destination host is known for that IP address, but is not on the same
359      * VLAN as the source host.
360      * Verifies the ARP request is flooded out the correct edge ports.
361      */
362     @Test
363     public void testReplyDifferentVlan() {
364
365         Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
366                 Collections.singleton(IP1));
367
368         Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
369                 Collections.singleton(IP2));
370
371         expect(hostService.getHostsByIp(IP1))
372                 .andReturn(Collections.singleton(replyer));
373         expect(interfaceService.getInterfacesByIp(IP2))
374                 .andReturn(Collections.emptySet());
375         expect(hostService.getHost(HID2)).andReturn(requestor);
376
377         replay(hostService);
378         replay(interfaceService);
379
380         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
381
382         //Setup for flood test
383         getEdgePointsNoArg = Lists.newLinkedList();
384         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1)));
385         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1)));
386         proxyArp.reply(arpRequest, getLocation(6));
387
388         verifyFlood(arpRequest);
389     }
390
391     @Test
392     public void testReplyToRequestForUs() {
393         Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
394         Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
395         Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
396         MacAddress firstMac = MacAddress.valueOf(1L);
397         MacAddress secondMac = MacAddress.valueOf(2L);
398
399         Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
400                 Collections.singleton(theirIp));
401
402         expect(hostService.getHost(HID2)).andReturn(requestor);
403         replay(hostService);
404         replay(interfaceService);
405
406         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
407         isEdgePointReturn = true;
408         proxyArp.reply(arpRequest, LOC1);
409
410         assertEquals(1, packetService.packets.size());
411         Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
412         verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
413
414         // Test a request for the second address on that port
415         packetService.packets.clear();
416         arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
417
418         proxyArp.reply(arpRequest, LOC1);
419
420         assertEquals(1, packetService.packets.size());
421         arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
422         verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
423     }
424
425     @Test
426     public void testReplyExternalPortBadRequest() {
427         replay(hostService); // no further host service expectations
428         replay(interfaceService);
429
430         Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
431
432         // Request for a valid external IP address but coming in the wrong port
433         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
434                 Ip4Address.valueOf("10.0.3.1"));
435         proxyArp.reply(arpRequest, LOC1);
436         assertEquals(0, packetService.packets.size());
437
438         // Request for a valid internal IP address but coming in an external port
439         packetService.packets.clear();
440         arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
441         proxyArp.reply(arpRequest, LOC1);
442         assertEquals(0, packetService.packets.size());
443     }
444
445     @Test
446     public void testReplyToRequestFromUs() {
447         Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
448         MacAddress ourMac = MacAddress.valueOf(1L);
449         Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
450
451         expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
452         expect(interfaceService.getInterfacesByIp(ourIp))
453                 .andReturn(Collections.singleton(new Interface(getLocation(1),
454                         Collections.singleton(new InterfaceIpAddress(ourIp, IpPrefix.valueOf("10.0.1.1/24"))),
455                         ourMac, VLAN1)));
456         expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
457         replay(hostService);
458         replay(interfaceService);
459
460         // This is a request from something inside our network (like a BGP
461         // daemon) to an external host.
462         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
463         //Ensure the packet is allowed through (it is not to an internal port)
464         isEdgePointReturn = true;
465
466         proxyArp.reply(arpRequest, getLocation(5));
467         assertEquals(1, packetService.packets.size());
468         verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
469
470         // The same request from a random external port should fail
471         packetService.packets.clear();
472         proxyArp.reply(arpRequest, getLocation(2));
473         assertEquals(0, packetService.packets.size());
474     }
475
476     /**
477      * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
478      * destination host is known.
479      * Verifies the correct ARP request is sent out the correct port.
480      */
481     @Test
482     public void testForwardToHost() {
483         Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
484                 Collections.singleton(IP1));
485         Host host2 = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC2,
486                                      Collections.singleton(IP2));
487
488         expect(hostService.getHost(HID1)).andReturn(host1);
489         expect(hostService.getHost(HID2)).andReturn(host2);
490         replay(hostService);
491         replay(interfaceService);
492
493         Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
494
495         proxyArp.forward(arpRequest, LOC2);
496
497         assertEquals(1, packetService.packets.size());
498         OutboundPacket packet = packetService.packets.get(0);
499
500         verifyPacketOut(arpRequest, LOC1, packet);
501     }
502
503     /**
504      * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
505      * destination host is not known.
506      * Verifies the correct ARP request is flooded out the correct edge ports.
507      */
508     @Test
509     public void testForwardFlood() {
510         expect(hostService.getHost(HID1)).andReturn(null);
511         replay(hostService);
512         replay(interfaceService);
513
514         Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
515
516         //populate the list of edges when so that when forward hits flood in the manager it contains the values
517         //that should continue on
518         getEdgePointsNoArg = Lists.newLinkedList();
519         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("3"), PortNumber.portNumber(1)));
520         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1)));
521         getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1)));
522
523         proxyArp.forward(arpRequest, getLocation(6));
524
525         verifyFlood(arpRequest);
526     }
527
528     /**
529      * Verifies that the given packet was flooded out all available edge ports,
530      * except for the input port.
531      *
532      * @param packet the packet that was expected to be flooded
533      */
534     private void verifyFlood(Ethernet packet) {
535         // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
536         assertEquals(NUM_FLOOD_PORTS - 1, packetService.packets.size());
537
538         Collections.sort(packetService.packets,
539                 (o1, o2) -> o1.sendThrough().uri().compareTo(o2.sendThrough().uri()));
540
541
542         for (int i = 0; i < NUM_FLOOD_PORTS - 1; i++) {
543             ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
544                     PortNumber.portNumber(1));
545
546             OutboundPacket outboundPacket = packetService.packets.get(i);
547             verifyPacketOut(packet, cp, outboundPacket);
548         }
549     }
550
551     /**
552      * Verifies the given packet was sent out the given port.
553      *
554      * @param expected the packet that was expected to be sent
555      * @param outPort  the port the packet was expected to be sent out
556      * @param actual   the actual OutboundPacket to verify
557      */
558     private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
559                                  OutboundPacket actual) {
560         assertArrayEquals(expected.serialize(), actual.data().array());
561         assertEquals(1, actual.treatment().immediate().size());
562         assertEquals(outPort.deviceId(), actual.sendThrough());
563         Instruction instruction = actual.treatment().immediate().get(0);
564         assertTrue(instruction instanceof OutputInstruction);
565         assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
566     }
567
568     /**
569      * Returns the device ID of the ith device.
570      *
571      * @param i device to get the ID of
572      * @return the device ID
573      */
574     private static DeviceId getDeviceId(int i) {
575         return DeviceId.deviceId("" + i);
576     }
577
578     private static HostLocation getLocation(int i) {
579         return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
580     }
581
582     /**
583      * Builds an ARP packet with the given parameters.
584      *
585      * @param opcode opcode of the ARP packet
586      * @param srcMac source MAC address
587      * @param dstMac destination MAC address, or null if this is a request
588      * @param srcIp  source IP address
589      * @param dstIp  destination IP address
590      * @return the ARP packet
591      */
592     private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
593                               Ip4Address srcIp, Ip4Address dstIp) {
594         Ethernet eth = new Ethernet();
595
596         if (dstMac == null) {
597             eth.setDestinationMACAddress(MacAddress.BROADCAST);
598         } else {
599             eth.setDestinationMACAddress(dstMac);
600         }
601
602         eth.setSourceMACAddress(srcMac);
603         eth.setEtherType(Ethernet.TYPE_ARP);
604         eth.setVlanID(VLAN1.toShort());
605
606         ARP arp = new ARP();
607         arp.setOpCode(opcode);
608         arp.setProtocolType(ARP.PROTO_TYPE_IP);
609         arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
610
611         arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
612         arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
613         arp.setSenderHardwareAddress(srcMac.toBytes());
614
615         if (dstMac == null) {
616             arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS);
617         } else {
618             arp.setTargetHardwareAddress(dstMac.toBytes());
619         }
620
621         arp.setSenderProtocolAddress(srcIp.toOctets());
622         arp.setTargetProtocolAddress(dstIp.toOctets());
623
624         eth.setPayload(arp);
625         return eth;
626     }
627
628     /**
629      * Test PacketService implementation that simply stores OutboundPackets
630      * passed to {@link #emit(OutboundPacket)} for later verification.
631      */
632     class TestPacketService extends PacketServiceAdapter {
633
634         List<OutboundPacket> packets = new ArrayList<>();
635
636         @Override
637         public void emit(OutboundPacket packet) {
638             packets.add(packet);
639         }
640
641     }
642
643     class TestEdgePortService extends EdgeManager {
644
645         @Override
646         public boolean isEdgePoint(ConnectPoint connectPoint) {
647             return isEdgePointReturn;
648         }
649
650         @Override
651         public Iterable<ConnectPoint> getEdgePoints() {
652             return getEdgePointsNoArg;
653         }
654     }
655
656     private class TestProxyArpStoreAdapter implements ProxyArpStore {
657         @Override
658         public void forward(ConnectPoint outPort, Host subject, ByteBuffer packet) {
659             TrafficTreatment tt = DefaultTrafficTreatment.builder().setOutput(outPort.port()).build();
660             packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), tt, packet));
661         }
662
663         @Override
664         public void setDelegate(ProxyArpStoreDelegate delegate) {
665         }
666     }
667 }