2 * Copyright 2014-2015 Open Networking Laboratory
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.onosproject.net.proxyarp.impl;
18 import com.google.common.collect.Sets;
19 import org.junit.Before;
20 import org.junit.Test;
21 import org.onlab.packet.ARP;
22 import org.onlab.packet.Ethernet;
23 import org.onlab.packet.ICMP6;
24 import org.onlab.packet.IPacket;
25 import org.onlab.packet.IPv6;
26 import org.onlab.packet.Ip4Address;
27 import org.onlab.packet.Ip4Prefix;
28 import org.onlab.packet.Ip6Address;
29 import org.onlab.packet.Ip6Prefix;
30 import org.onlab.packet.IpPrefix;
31 import org.onlab.packet.MacAddress;
32 import org.onlab.packet.VlanId;
33 import org.onlab.packet.ndp.NeighborAdvertisement;
34 import org.onlab.packet.ndp.NeighborDiscoveryOptions;
35 import org.onlab.packet.ndp.NeighborSolicitation;
36 import org.onosproject.incubator.net.intf.Interface;
37 import org.onosproject.incubator.net.intf.InterfaceService;
38 import org.onosproject.net.ConnectPoint;
39 import org.onosproject.net.DefaultHost;
40 import org.onosproject.net.Device;
41 import org.onosproject.net.DeviceId;
42 import org.onosproject.net.Host;
43 import org.onosproject.net.HostId;
44 import org.onosproject.net.HostLocation;
45 import org.onosproject.net.Link;
46 import org.onosproject.net.Port;
47 import org.onosproject.net.PortNumber;
48 import org.onosproject.net.device.DeviceListener;
49 import org.onosproject.net.device.DeviceService;
50 import org.onosproject.net.edge.EdgePortService;
51 import org.onosproject.net.flow.DefaultTrafficTreatment;
52 import org.onosproject.net.flow.TrafficTreatment;
53 import org.onosproject.net.flow.instructions.Instruction;
54 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
55 import org.onosproject.net.host.HostService;
56 import org.onosproject.net.host.InterfaceIpAddress;
57 import org.onosproject.net.link.LinkListener;
58 import org.onosproject.net.link.LinkService;
59 import org.onosproject.net.packet.DefaultOutboundPacket;
60 import org.onosproject.net.packet.OutboundPacket;
61 import org.onosproject.net.packet.PacketServiceAdapter;
62 import org.onosproject.net.provider.ProviderId;
63 import org.onosproject.net.proxyarp.ProxyArpStore;
64 import org.onosproject.net.proxyarp.ProxyArpStoreDelegate;
66 import java.nio.ByteBuffer;
67 import java.util.ArrayList;
68 import java.util.Collections;
69 import java.util.HashSet;
70 import java.util.List;
73 import static org.easymock.EasyMock.anyObject;
74 import static org.easymock.EasyMock.createMock;
75 import static org.easymock.EasyMock.expect;
76 import static org.easymock.EasyMock.replay;
77 import static org.hamcrest.Matchers.anyOf;
78 import static org.hamcrest.Matchers.is;
79 import static org.junit.Assert.assertArrayEquals;
80 import static org.junit.Assert.assertEquals;
81 import static org.junit.Assert.assertFalse;
82 import static org.junit.Assert.assertNotNull;
83 import static org.junit.Assert.assertThat;
84 import static org.junit.Assert.assertTrue;
87 * Tests for the {@link ProxyArpManager} class.
89 public class ProxyArpManagerTest {
91 private static final int NUM_DEVICES = 10;
92 private static final int NUM_PORTS_PER_DEVICE = 3;
93 private static final int LAST_CONF_DEVICE_INTF_VLAN_IP = 3;
94 private static final int LAST_CONF_DEVICE_INTF_VLAN = 6;
96 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
97 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
98 private static final Ip6Address IP3 = Ip6Address.valueOf("1000:ffff::1");
99 private static final Ip6Address IP4 = Ip6Address.valueOf("1000:ffff::2");
101 private static final ProviderId PID = new ProviderId("of", "foo");
103 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
104 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
105 private static final VlanId VLAN10 = VlanId.vlanId((short) 10);
107 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01");
108 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02");
109 private static final MacAddress MAC3 = MacAddress.valueOf("00:00:00:00:00:03");
110 private static final MacAddress MAC4 = MacAddress.valueOf("00:00:00:00:00:04");
111 private static final MacAddress MAC10 = MacAddress.valueOf("00:00:00:00:00:0A");
113 private static final MacAddress SOLICITED_MAC3 = MacAddress.valueOf("33:33:FF:00:00:01");
115 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
116 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
117 private static final HostId HID3 = HostId.hostId(MAC3, VLAN1);
118 private static final HostId HID4 = HostId.hostId(MAC4, VLAN1);
119 private static final HostId HID10 = HostId.hostId(MAC10, VLAN10);
121 private static final HostId SOLICITED_HID3 = HostId.hostId(SOLICITED_MAC3, VLAN1);
123 private static final DeviceId DID1 = getDeviceId(1);
124 private static final DeviceId DID2 = getDeviceId(2);
126 private static final PortNumber P1 = PortNumber.portNumber(1);
128 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
129 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
131 private final byte[] zeroMacAddress = MacAddress.ZERO.toBytes();
133 // The first three devices in the topology have interfaces configured
134 // with VLANs and IPs
135 private final List<ConnectPoint> configIpCPoints = new ArrayList<>();
137 // Other three devices in the topology (from 4 to 6) have interfaces
138 // configured only with VLANs
139 private final List<ConnectPoint> configVlanCPoints = new ArrayList<>();
141 // Remaining devices in the network (id > 6) don't have any interface
143 private final List<ConnectPoint> noConfigCPoints = new ArrayList<>();
145 private ProxyArpManager proxyArp;
147 private TestPacketService packetService;
148 private DeviceService deviceService;
149 private EdgePortService edgePortService;
150 private LinkService linkService;
151 private HostService hostService;
152 private InterfaceService interfaceService;
155 public void setUp() throws Exception {
156 proxyArp = new ProxyArpManager();
157 packetService = new TestPacketService();
158 proxyArp.packetService = packetService;
159 proxyArp.store = new TestProxyArpStoreAdapter();
161 // Create a host service mock here.
162 hostService = createMock(HostService.class);
163 proxyArp.hostService = hostService;
165 // Create an edge port service.
166 edgePortService = createMock(EdgePortService.class);
167 proxyArp.edgeService = edgePortService;
169 // Create interface service
170 interfaceService = createMock(InterfaceService.class);
171 proxyArp.interfaceService = interfaceService;
173 // Create the topology
175 proxyArp.deviceService = deviceService;
176 proxyArp.linkService = linkService;
178 setupNoConfigCPoints();
179 setupconfigIpCPoints();
180 setupconfigVlanCPoints();
186 * Creates a fake topology to feed into the ARP module.
188 * The default topology is a unidirectional ring topology. Each switch has
189 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
190 * is free (edge port).
191 * The first half of the switches have IP addresses configured on their
192 * free ports (port 1). The second half of the switches have no IP
193 * addresses configured.
195 private void createTopology() {
196 deviceService = createMock(DeviceService.class);
197 linkService = createMock(LinkService.class);
199 deviceService.addListener(anyObject(DeviceListener.class));
200 linkService.addListener(anyObject(LinkListener.class));
202 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
203 createLinks(NUM_DEVICES);
205 popluateEdgePortService();
209 * Creates the devices for the fake topology.
211 private void createDevices(int numDevices, int numPorts) {
212 List<Device> devices = new ArrayList<>();
214 for (int i = 1; i <= numDevices; i++) {
215 DeviceId devId = getDeviceId(i);
216 Device device = createMock(Device.class);
217 expect(device.id()).andReturn(devId).anyTimes();
222 List<Port> ports = new ArrayList<>();
223 for (int j = 1; j <= numPorts; j++) {
224 Port port = createMock(Port.class);
225 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
230 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
231 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
234 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
235 replay(deviceService);
239 * Creates the links for the fake topology.
240 * NB: Only unidirectional links are created, as for this purpose all we
241 * need is to occupy the ports with some link.
243 private void createLinks(int numDevices) {
244 List<Link> links = new ArrayList<>();
246 for (int i = 1; i <= numDevices; i++) {
247 ConnectPoint src = new ConnectPoint(
249 PortNumber.portNumber(2));
250 ConnectPoint dst = new ConnectPoint(
251 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
252 PortNumber.portNumber(3));
254 Link link = createMock(Link.class);
255 expect(link.src()).andReturn(src).anyTimes();
256 expect(link.dst()).andReturn(dst).anyTimes();
262 expect(linkService.getLinks()).andReturn(links).anyTimes();
267 * On the first three devices two config interfaces are binded on port 1.
268 * The first one with VLAN1, the second one with VLAN equals to none.
269 * Both interfaces have an IP.
270 * On devices 4, 5 and 6 it's binded a config interface on port 1.
271 * The interface is configured with VLAN 1 and no IP.
273 private void addIntfConfig() {
274 Set<Interface> interfaces = Sets.newHashSet();
276 Set<Interface> vlanOneSet = new HashSet<>();
278 for (int i = 1; i <= LAST_CONF_DEVICE_INTF_VLAN_IP; i++) {
279 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
281 // Interface addresses for IPv4
282 Ip4Prefix prefix1 = Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
283 Ip4Address addr1 = Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
284 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
285 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
286 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
287 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
289 // Interface addresses for IPv6
290 Ip6Prefix prefix3 = Ip6Prefix.valueOf((2 * i - 1) + "000::0/64");
291 Ip6Address addr3 = Ip6Address.valueOf((2 * i - 1) + "000::1");
292 Ip6Prefix prefix4 = Ip6Prefix.valueOf((2 * i) + "000::0/64");
293 Ip6Address addr4 = Ip6Address.valueOf((2 * i) + "000::2");
294 InterfaceIpAddress ia3 = new InterfaceIpAddress(addr3, prefix3);
295 InterfaceIpAddress ia4 = new InterfaceIpAddress(addr4, prefix4);
297 // Setting up interfaces
298 Interface intf1 = new Interface(cp, Sets.newHashSet(ia1, ia3),
299 MacAddress.valueOf(2 * i - 1),
300 VlanId.vlanId((short) 1));
301 Interface intf2 = new Interface(cp, Sets.newHashSet(ia2, ia4),
302 MacAddress.valueOf(2 * i),
305 interfaces.add(intf1);
306 interfaces.add(intf2);
308 vlanOneSet.add(intf1);
310 expect(interfaceService.getInterfacesByPort(cp))
311 .andReturn(Sets.newHashSet(intf1, intf2)).anyTimes();
313 for (int i = LAST_CONF_DEVICE_INTF_VLAN_IP + 1; i <= LAST_CONF_DEVICE_INTF_VLAN; i++) {
314 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
315 Interface intf1 = new Interface(cp, null,
317 VlanId.vlanId((short) 1));
319 interfaces.add(intf1);
320 vlanOneSet.add(intf1);
322 expect(interfaceService.getInterfacesByPort(cp))
323 .andReturn(Sets.newHashSet(intf1)).anyTimes();
325 expect(interfaceService.getInterfacesByVlan(VLAN1))
326 .andReturn(vlanOneSet).anyTimes();
327 expect(interfaceService.getInterfacesByVlan(VLAN10))
328 .andReturn(Collections.emptySet()).anyTimes();
329 expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
331 for (int i = LAST_CONF_DEVICE_INTF_VLAN + 1; i <= NUM_DEVICES; i++) {
332 ConnectPoint cp = new ConnectPoint(getDeviceId(i),
334 expect(interfaceService.getInterfacesByPort(cp))
335 .andReturn(Collections.emptySet()).anyTimes();
340 * Populates edge ports in the EdgePortService to return all port 1
343 private void popluateEdgePortService() {
344 Set<ConnectPoint> edgeConnectPoints = new HashSet<>();
346 for (int i = 1; i <= NUM_DEVICES; i++) {
347 for (int j = 1; j <= NUM_PORTS_PER_DEVICE; j++) {
348 ConnectPoint edgeConnectPoint = new ConnectPoint(
350 PortNumber.portNumber(1));
351 ConnectPoint noEdgeConnectPointOne = new ConnectPoint(
353 PortNumber.portNumber(2));
354 ConnectPoint noEdgeConnectPointTwo = new ConnectPoint(
356 PortNumber.portNumber(3));
358 edgeConnectPoints.add(edgeConnectPoint);
360 expect(edgePortService.isEdgePoint(edgeConnectPoint))
361 .andReturn(true).anyTimes();
362 expect(edgePortService.isEdgePoint(noEdgeConnectPointOne))
363 .andReturn(false).anyTimes();
364 expect(edgePortService.isEdgePoint(noEdgeConnectPointTwo))
365 .andReturn(false).anyTimes();
368 expect(edgePortService.getEdgePoints())
369 .andReturn(edgeConnectPoints).anyTimes();
371 replay(edgePortService);
375 * Creates a list of connect points used to verify floodling on ports
376 * with no interfaces configured (all ports without interface config).
378 private void setupNoConfigCPoints() {
379 for (int i = NUM_DEVICES / 2 + 2; i <= NUM_DEVICES; i++) {
380 ConnectPoint connectPoint = new ConnectPoint(
382 PortNumber.portNumber(1));
383 noConfigCPoints.add(connectPoint);
388 * Creates a list of connect points used to verify floodling on ports
389 * with interfaces configured (both VLAN and IP).
391 private void setupconfigIpCPoints() {
392 for (int i = 1; i <= 3; i++) {
393 ConnectPoint connectPoint = new ConnectPoint(
395 PortNumber.portNumber(1));
396 configIpCPoints.add(connectPoint);
401 * Creates a list of connect points used to verify floodling on ports
402 * with interfaces configured (both VLAN and IP).
404 private void setupconfigVlanCPoints() {
405 for (int i = LAST_CONF_DEVICE_INTF_VLAN_IP + 1; i <= LAST_CONF_DEVICE_INTF_VLAN; i++) {
406 ConnectPoint connectPoint = new ConnectPoint(
408 PortNumber.portNumber(1));
409 configVlanCPoints.add(connectPoint);
414 * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
415 * case where the IP address is not known.
416 * Verifies the method returns false.
419 public void testNotKnown() {
420 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
422 replay(interfaceService);
424 assertFalse(proxyArp.isKnown(IP1));
428 * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
429 * case where the IP address is known.
430 * Verifies the method returns true.
433 public void testKnown() {
434 Host host1 = createMock(Host.class);
435 Host host2 = createMock(Host.class);
437 expect(hostService.getHostsByIp(IP1))
438 .andReturn(Sets.newHashSet(host1, host2));
440 replay(interfaceService);
442 assertTrue(proxyArp.isKnown(IP1));
446 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
447 * destination host is known.
448 * Two host using the same VLAN are registered on the host service on devices 5 and 6.
449 * Host on port 6 asks for the MAC of the device on port 5.
450 * Since the destination mac address is known, the request is not flooded to anywhere
451 * and ONOS directly builds an ARP reply, sended back to the requester on device 6.
452 * It's verified that a proper ARP reply is received on port 1 of device 6.
455 public void testReplyKnown() {
456 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(NUM_DEVICES),
457 Collections.singleton(IP1));
459 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(NUM_DEVICES - 1),
460 Collections.singleton(IP2));
462 expect(hostService.getHostsByIp(IP2))
463 .andReturn(Collections.singleton(replyer));
464 expect(hostService.getHost(HID1)).andReturn(requestor);
467 replay(interfaceService);
469 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
471 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
473 assertEquals(1, packetService.packets.size());
474 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, MAC2, MAC1, IP2, IP1);
475 verifyPacketOut(arpReply, getLocation(NUM_DEVICES), packetService.packets.get(0));
479 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
480 * destination host is known.
481 * Verifies the correct NDP reply is sent out the correct port.
484 public void testReplyKnownIpv6() {
485 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(4),
486 Collections.singleton(IP3));
488 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5),
489 Collections.singleton(IP4));
491 expect(hostService.getHostsByIp(IP3))
492 .andReturn(Collections.singleton(replyer));
493 expect(hostService.getHost(HID4)).andReturn(requestor);
496 replay(interfaceService);
498 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
499 MAC4, SOLICITED_MAC3,
502 proxyArp.reply(ndpRequest, getLocation(5));
504 assertEquals(1, packetService.packets.size());
505 Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT,
506 MAC3, MAC4, IP3, IP4);
507 verifyPacketOut(ndpReply, getLocation(5), packetService.packets.get(0));
511 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
512 * destination host is not known.
513 * Only a requestor is present (on device 6, port 1). The device has a VLAN configured
514 * which is not configured anywhere in the system.
515 * Since the destination is not known, and since the ARP request can't be sent out of
516 * interfaces configured, the ARP request is flooded out of ports 4 and 5.
517 * Verifies the ARP request is flooded out the correct edge ports.
520 public void testReplyUnknown() {
521 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
522 Collections.singleton(IP1));
524 expect(hostService.getHostsByIp(IP2))
525 .andReturn(Collections.emptySet());
526 expect(interfaceService.getInterfacesByIp(IP1))
527 .andReturn(Collections.emptySet());
528 expect(hostService.getHost(HID10)).andReturn(requestor);
531 replay(interfaceService);
533 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
535 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
537 verifyFlood(arpRequest, noConfigCPoints);
541 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
542 * destination host is not known.
543 * Verifies the NDP request is flooded out the correct edge ports.
546 public void testReplyUnknownIpv6() {
547 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
548 Collections.singleton(IP4));
550 expect(hostService.getHostsByIp(IP3))
551 .andReturn(Collections.emptySet());
552 expect(interfaceService.getInterfacesByIp(IP4))
553 .andReturn(Collections.emptySet());
554 expect(hostService.getHost(HID4)).andReturn(requestor);
557 replay(interfaceService);
559 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
560 MAC4, SOLICITED_MAC3,
563 proxyArp.reply(ndpRequest, getLocation(NUM_DEVICES));
565 verifyFlood(ndpRequest, noConfigCPoints);
569 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
570 * destination host is known for that IP address, but is not on the same
571 * VLAN as the source host.
572 * An host is connected on device 6, port 1 where no interfaces are defined. It sends
573 * ARP requests from VLAN10, not configured anywhere in the network. Another host with
574 * the IP address requested lives on device 5, port 1 in the network. Anyway, since the
575 * host uses another VLAN it's not found and the ARP packet is flooded out of port
578 * Verifies the ARP request is flooded out the correct edge ports.
581 public void testReplyDifferentVlan() {
582 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
583 Collections.singleton(IP1));
585 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN2, getLocation(NUM_DEVICES - 1),
586 Collections.singleton(IP2));
588 expect(hostService.getHostsByIp(IP2))
589 .andReturn(Collections.singleton(replyer));
590 expect(interfaceService.getInterfacesByIp(IP1))
591 .andReturn(Collections.emptySet());
592 expect(hostService.getHost(HID10)).andReturn(requestor);
595 replay(interfaceService);
597 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
599 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
601 verifyFlood(arpRequest, noConfigCPoints);
605 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
606 * a vlan packet comes in from a port without interfaces configured. The destination
607 * host is unknown for that IP address and there are some interfaces configured on
609 * It's expected to see the ARP request going out through ports with no interfaces
610 * configured, devices 4 and 5, port 1.
612 * Verifies the ARP request is flooded out the correct edge ports.
615 public void testConfiguredVlan() {
616 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(NUM_DEVICES),
617 Collections.singleton(IP1));
619 expect(hostService.getHostsByIp(IP2))
620 .andReturn(Collections.emptySet());
621 expect(interfaceService.getInterfacesByIp(IP1))
622 .andReturn(Collections.emptySet());
623 expect(hostService.getHost(HID1)).andReturn(requestor);
626 replay(interfaceService);
628 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
630 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
632 verifyFlood(arpRequest, noConfigCPoints);
636 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
637 * a vlan packet comes in from a port without interfaces configured. The destination
638 * host is not known for that IP address and there are some interfaces configured on
640 * It's expected to see the ARP request going out through ports with no interfaces
641 * configured, devices 4 and 5, port 1.
643 * Verifies the ARP request is flooded out the correct edge ports.
646 public void testConfiguredVlanOnInterfaces() {
647 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(6),
648 Collections.singleton(IP1));
650 expect(hostService.getHostsByIp(IP2))
651 .andReturn(Collections.emptySet());
652 expect(interfaceService.getInterfacesByIp(IP1))
653 .andReturn(Collections.emptySet());
654 expect(hostService.getHost(HID1)).andReturn(requestor);
657 replay(interfaceService);
659 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
661 proxyArp.reply(arpRequest, getLocation(6));
663 verifyFlood(arpRequest, configVlanCPoints);
667 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
668 * destination host is known for that IP address, but is not on the same
669 * VLAN as the source host.
670 * Verifies the NDP request is flooded out the correct edge ports.
673 public void testReplyDifferentVlanIpv6() {
674 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
675 Collections.singleton(IP4));
677 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN2, getLocation(NUM_DEVICES - 1),
678 Collections.singleton(IP3));
680 expect(hostService.getHostsByIp(IP3))
681 .andReturn(Collections.singleton(replyer));
682 expect(interfaceService.getInterfacesByIp(IP4))
683 .andReturn(Collections.emptySet());
684 expect(hostService.getHost(HID4)).andReturn(requestor);
687 replay(interfaceService);
689 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
690 MAC4, SOLICITED_MAC3,
693 proxyArp.reply(ndpRequest, getLocation(NUM_DEVICES));
695 verifyFlood(ndpRequest, noConfigCPoints);
699 * Test ARP request from external network to an internal host.
702 public void testReplyToRequestForUs() {
703 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
704 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
705 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
706 MacAddress firstMac = MacAddress.valueOf(1L);
707 MacAddress secondMac = MacAddress.valueOf(2L);
709 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
710 Collections.singleton(theirIp));
712 expect(hostService.getHost(HID1)).andReturn(requestor);
714 replay(interfaceService);
716 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourFirstIp);
718 proxyArp.reply(arpRequest, LOC1);
720 assertEquals(1, packetService.packets.size());
721 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, firstMac, MAC1, ourFirstIp, theirIp);
722 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
724 // Test a request for the second address on that port
725 packetService.packets.clear();
726 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourSecondIp);
728 proxyArp.reply(arpRequest, LOC1);
730 assertEquals(1, packetService.packets.size());
731 arpReply = buildArp(ARP.OP_REPLY, VLAN1, secondMac, MAC1, ourSecondIp, theirIp);
732 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
736 * Test NDP request from external network to an internal host.
739 public void testReplyToRequestForUsIpv6() {
740 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
741 Ip6Address ourFirstIp = Ip6Address.valueOf("1000::1");
742 Ip6Address ourSecondIp = Ip6Address.valueOf("2000::2");
743 MacAddress firstMac = MacAddress.valueOf(1L);
744 MacAddress secondMac = MacAddress.valueOf(2L);
746 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
747 Collections.singleton(theirIp));
749 expect(hostService.getHost(HID2)).andReturn(requestor);
750 expect(hostService.getHostsByIp(ourFirstIp))
751 .andReturn(Collections.singleton(requestor));
753 replay(interfaceService);
755 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
757 MacAddress.valueOf("33:33:ff:00:00:01"),
761 proxyArp.reply(ndpRequest, LOC1);
762 assertEquals(1, packetService.packets.size());
764 Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT,
769 verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0));
771 // Test a request for the second address on that port
772 packetService.packets.clear();
773 ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
775 MacAddress.valueOf("33:33:ff:00:00:01"),
778 proxyArp.reply(ndpRequest, LOC1);
779 assertEquals(1, packetService.packets.size());
781 ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT,
786 verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0));
790 * Request for a valid external IPv4 address but coming in the wrong port.
793 public void testReplyExternalPortBadRequest() {
794 replay(hostService); // no further host service expectations
795 replay(interfaceService);
797 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
799 // Request for a valid external IP address but coming in the wrong port
800 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp,
801 Ip4Address.valueOf("10.0.3.1"));
802 proxyArp.reply(arpRequest, LOC1);
803 assertEquals(0, packetService.packets.size());
805 // Request for a valid internal IP address but coming in an external port
806 packetService.packets.clear();
807 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, IP1);
808 proxyArp.reply(arpRequest, LOC1);
809 assertEquals(0, packetService.packets.size());
813 * Request for a valid external IPv6 address but coming in the wrong port.
816 public void testReplyExternalPortBadRequestIpv6() {
817 replay(hostService); // no further host service expectations
818 replay(interfaceService);
820 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
822 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
824 MacAddress.valueOf("33:33:ff:00:00:01"),
826 Ip6Address.valueOf("3000::1"));
827 proxyArp.reply(ndpRequest, LOC1);
828 assertEquals(0, packetService.packets.size());
830 // Request for a valid internal IP address but coming in an external port
831 packetService.packets.clear();
832 ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
834 MacAddress.valueOf("33:33:ff:00:00:01"),
837 proxyArp.reply(ndpRequest, LOC1);
838 assertEquals(0, packetService.packets.size());
842 * Test ARP request from internal network to an external host.
845 public void testReplyToRequestFromUs() {
846 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
847 MacAddress ourMac = MacAddress.valueOf(1L);
848 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
850 expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
851 expect(interfaceService.getInterfacesByIp(ourIp))
852 .andReturn(Collections.singleton(new Interface(getLocation(1),
853 Collections.singleton(new InterfaceIpAddress(ourIp, IpPrefix.valueOf("10.0.1.1/24"))),
855 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
857 replay(interfaceService);
859 // This is a request from something inside our network (like a BGP
860 // daemon) to an external host.
861 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, ourMac, null, ourIp, theirIp);
862 //Ensure the packet is allowed through (it is not to an internal port)
864 proxyArp.reply(arpRequest, getLocation(5));
865 assertEquals(1, packetService.packets.size());
866 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
868 // The same request from a random external port should fail
869 packetService.packets.clear();
870 proxyArp.reply(arpRequest, getLocation(2));
871 assertEquals(0, packetService.packets.size());
875 * Test NDP request from internal network to an external host.
878 public void testReplyToRequestFromUsIpv6() {
879 Ip6Address ourIp = Ip6Address.valueOf("1000::1");
880 MacAddress ourMac = MacAddress.valueOf(1L);
881 Ip6Address theirIp = Ip6Address.valueOf("1000::100");
883 expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
884 expect(interfaceService.getInterfacesByIp(ourIp))
885 .andReturn(Collections.singleton(new Interface(getLocation(1),
886 Collections.singleton(new InterfaceIpAddress(
888 IpPrefix.valueOf("1000::1/64"))),
891 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
893 replay(interfaceService);
895 // This is a request from something inside our network (like a BGP
896 // daemon) to an external host.
897 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
899 MacAddress.valueOf("33:33:ff:00:00:01"),
903 proxyArp.reply(ndpRequest, getLocation(5));
904 assertEquals(1, packetService.packets.size());
905 verifyPacketOut(ndpRequest, getLocation(1), packetService.packets.get(0));
907 // The same request from a random external port should fail
908 packetService.packets.clear();
909 proxyArp.reply(ndpRequest, getLocation(2));
910 assertEquals(0, packetService.packets.size());
914 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
915 * destination host is known.
916 * Verifies the correct ARP request is sent out the correct port.
919 public void testForwardToHost() {
920 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
921 Collections.singleton(IP1));
922 Host host2 = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC2,
923 Collections.singleton(IP2));
925 expect(hostService.getHost(HID1)).andReturn(host1);
926 expect(hostService.getHost(HID2)).andReturn(host2);
928 replay(interfaceService);
930 Ethernet arpRequest = buildArp(ARP.OP_REPLY, VLAN1, MAC2, MAC1, IP2, IP1);
932 proxyArp.forward(arpRequest, LOC2);
934 assertEquals(1, packetService.packets.size());
935 OutboundPacket packet = packetService.packets.get(0);
937 verifyPacketOut(arpRequest, LOC1, packet);
941 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
942 * destination host is known.
943 * Verifies the correct ARP request is sent out the correct port.
946 public void testForwardToHostIpv6() {
947 Host host1 = new DefaultHost(PID, HID3, MAC3, VLAN1, LOC1,
948 Collections.singleton(IP3));
949 Host host2 = new DefaultHost(PID, HID4, MAC4, VLAN1, LOC2,
950 Collections.singleton(IP4));
952 expect(hostService.getHost(SOLICITED_HID3)).andReturn(host1);
953 expect(hostService.getHost(HID4)).andReturn(host2);
955 replay(interfaceService);
957 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
958 MAC4, SOLICITED_MAC3,
961 proxyArp.forward(ndpRequest, LOC2);
963 assertEquals(1, packetService.packets.size());
964 OutboundPacket packet = packetService.packets.get(0);
966 verifyPacketOut(ndpRequest, LOC1, packet);
970 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
971 * destination host is not known.
972 * Verifies the correct ARP request is flooded out the correct edge ports.
975 public void testForwardFlood() {
976 expect(hostService.getHost(HID2)).andReturn(null);
978 replay(interfaceService);
980 Ethernet arpRequest = buildArp(ARP.OP_REPLY, VLAN1, MAC1, MAC2, IP1, IP2);
982 proxyArp.forward(arpRequest, getLocation(NUM_DEVICES));
984 verifyFlood(arpRequest, noConfigCPoints);
988 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
989 * destination host is not known.
990 * Verifies the correct NDP request is flooded out the correct edge ports.
993 public void testForwardFloodIpv6() {
994 expect(hostService.getHost(SOLICITED_HID3)).andReturn(null);
996 replay(interfaceService);
998 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
999 MAC4, SOLICITED_MAC3,
1002 proxyArp.forward(ndpRequest, getLocation(NUM_DEVICES));
1004 verifyFlood(ndpRequest, noConfigCPoints);
1008 * Verifies that the given packet was flooded out all available edge ports,
1009 * except for the input port.
1011 * @param packet the packet that was expected to be flooded
1012 * @param connectPoints the connectPoints where the outpacket should be
1015 private void verifyFlood(Ethernet packet, List<ConnectPoint> connectPoints) {
1017 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
1018 assertEquals(connectPoints.size() - 1, packetService.packets.size());
1020 Collections.sort(packetService.packets,
1021 (o1, o2) -> o1.sendThrough().uri().compareTo(o2.sendThrough().uri()));
1023 for (int i = 0; i < connectPoints.size() - 1; i++) {
1024 OutboundPacket outboundPacket = packetService.packets.get(i);
1025 verifyPacketOut(packet, connectPoints.get(i), outboundPacket);
1030 * Verifies the given packet was sent out the given port.
1032 * @param expected the packet that was expected to be sent
1033 * @param outPort the port the packet was expected to be sent out
1034 * @param actual the actual OutboundPacket to verify
1036 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
1037 OutboundPacket actual) {
1038 assertArrayEquals(expected.serialize(), actual.data().array());
1039 assertEquals(1, actual.treatment().immediate().size());
1040 assertEquals(outPort.deviceId(), actual.sendThrough());
1041 Instruction instruction = actual.treatment().immediate().get(0);
1042 assertTrue(instruction instanceof OutputInstruction);
1043 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
1047 * Returns the device ID of the ith device.
1049 * @param i device to get the ID of
1050 * @return the device ID
1052 private static DeviceId getDeviceId(int i) {
1053 return DeviceId.deviceId("" + i);
1056 private static HostLocation getLocation(int i) {
1057 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
1061 * Builds an ARP packet with the given parameters.
1063 * @param opcode opcode of the ARP packet
1064 * @param srcMac source MAC address
1065 * @param dstMac destination MAC address, or null if this is a request
1066 * @param srcIp source IP address
1067 * @param dstIp destination IP address
1068 * @return the ARP packet
1070 private Ethernet buildArp(short opcode, VlanId vlanId, MacAddress srcMac,
1071 MacAddress dstMac, Ip4Address srcIp, Ip4Address dstIp) {
1072 Ethernet eth = new Ethernet();
1074 if (dstMac == null) {
1075 eth.setDestinationMACAddress(MacAddress.BROADCAST);
1077 eth.setDestinationMACAddress(dstMac);
1080 eth.setSourceMACAddress(srcMac);
1081 eth.setEtherType(Ethernet.TYPE_ARP);
1082 eth.setVlanID(vlanId.toShort());
1084 ARP arp = new ARP();
1085 arp.setOpCode(opcode);
1086 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1087 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1089 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1090 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1091 arp.setSenderHardwareAddress(srcMac.toBytes());
1093 if (dstMac == null) {
1094 arp.setTargetHardwareAddress(zeroMacAddress);
1096 arp.setTargetHardwareAddress(dstMac.toBytes());
1099 arp.setSenderProtocolAddress(srcIp.toOctets());
1100 arp.setTargetProtocolAddress(dstIp.toOctets());
1102 eth.setPayload(arp);
1107 * Builds an NDP packet with the given parameters.
1109 * @param type NeighborSolicitation or NeighborAdvertisement
1110 * @param srcMac source MAC address
1111 * @param dstMac destination MAC address, or null if this is a request
1112 * @param srcIp source IP address
1113 * @param dstIp destination IP address
1114 * @return the NDP packet
1116 private Ethernet buildNDP(byte type, MacAddress srcMac, MacAddress dstMac,
1117 Ip6Address srcIp, Ip6Address dstIp) {
1118 assertThat(type, anyOf(
1119 is(ICMP6.NEIGHBOR_SOLICITATION),
1120 is(ICMP6.NEIGHBOR_ADVERTISEMENT)
1122 assertNotNull(srcMac);
1123 assertNotNull(dstMac);
1124 assertNotNull(srcIp);
1125 assertNotNull(dstIp);
1128 if (type == ICMP6.NEIGHBOR_SOLICITATION) {
1129 ndp = new NeighborSolicitation().setTargetAddress(dstIp.toOctets());
1131 ndp = new NeighborAdvertisement()
1132 .setSolicitedFlag((byte) 1)
1133 .setOverrideFlag((byte) 1)
1134 .setTargetAddress(srcIp.toOctets())
1135 .addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
1139 ICMP6 icmp6 = new ICMP6();
1140 icmp6.setIcmpType(type);
1141 icmp6.setIcmpCode((byte) 0);
1142 icmp6.setPayload(ndp);
1144 IPv6 ipv6 = new IPv6();
1145 ipv6.setDestinationAddress(dstIp.toOctets());
1146 ipv6.setSourceAddress(srcIp.toOctets());
1147 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
1148 ipv6.setHopLimit((byte) 255);
1149 ipv6.setPayload(icmp6);
1151 Ethernet eth = new Ethernet();
1152 eth.setDestinationMACAddress(dstMac);
1153 eth.setSourceMACAddress(srcMac);
1154 eth.setEtherType(Ethernet.TYPE_IPV6);
1155 eth.setVlanID(VLAN1.toShort());
1156 eth.setPayload(ipv6);
1162 * Test PacketService implementation that simply stores OutboundPackets
1163 * passed to {@link #emit(OutboundPacket)} for later verification.
1165 class TestPacketService extends PacketServiceAdapter {
1167 List<OutboundPacket> packets = new ArrayList<>();
1170 public void emit(OutboundPacket packet) {
1171 packets.add(packet);
1176 private class TestProxyArpStoreAdapter implements ProxyArpStore {
1178 public void forward(ConnectPoint outPort, Host subject, ByteBuffer packet) {
1179 TrafficTreatment tt = DefaultTrafficTreatment.builder().setOutput(outPort.port()).build();
1180 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), tt, packet));
1184 public void setDelegate(ProxyArpStoreDelegate delegate) {