blob: ee9dc8fb6eee43a5345c2893b01acf68cfb2b560 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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
Thomas Vachuska27bee092015-06-23 19:03:10 -070018import com.google.common.collect.Sets;
Jonathan Hart704ca142014-10-09 09:34:39 -070019import org.junit.Before;
20import org.junit.Test;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080021import org.onlab.packet.ARP;
22import org.onlab.packet.Ethernet;
Charles Chanad3b5502015-10-23 16:24:20 -070023import org.onlab.packet.ICMP6;
24import org.onlab.packet.IPacket;
25import org.onlab.packet.IPv6;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080026import org.onlab.packet.Ip4Address;
27import org.onlab.packet.Ip4Prefix;
Charles Chanad3b5502015-10-23 16:24:20 -070028import org.onlab.packet.Ip6Address;
29import org.onlab.packet.Ip6Prefix;
Jonathan Hart4cb39882015-08-12 23:50:55 -040030import org.onlab.packet.IpPrefix;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080031import org.onlab.packet.MacAddress;
32import org.onlab.packet.VlanId;
Charles Chanad3b5502015-10-23 16:24:20 -070033import org.onlab.packet.ndp.NeighborAdvertisement;
34import org.onlab.packet.ndp.NeighborDiscoveryOptions;
35import org.onlab.packet.ndp.NeighborSolicitation;
Jonathan Hart4cb39882015-08-12 23:50:55 -040036import org.onosproject.incubator.net.intf.Interface;
37import org.onosproject.incubator.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.DefaultHost;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.Host;
43import org.onosproject.net.HostId;
44import org.onosproject.net.HostLocation;
45import org.onosproject.net.Link;
46import org.onosproject.net.Port;
47import org.onosproject.net.PortNumber;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
Luca Pretef70d3992015-10-30 16:24:14 -070050import org.onosproject.net.edge.EdgePortService;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070051import org.onosproject.net.flow.DefaultTrafficTreatment;
52import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.flow.instructions.Instruction;
54import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
55import org.onosproject.net.host.HostService;
56import org.onosproject.net.host.InterfaceIpAddress;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.link.LinkListener;
58import org.onosproject.net.link.LinkService;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070059import org.onosproject.net.packet.DefaultOutboundPacket;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.packet.OutboundPacket;
Thomas Vachuska27bee092015-06-23 19:03:10 -070061import org.onosproject.net.packet.PacketServiceAdapter;
Brian O'Connorabafb502014-12-02 22:26:20 -080062import org.onosproject.net.provider.ProviderId;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070063import org.onosproject.net.proxyarp.ProxyArpStore;
64import org.onosproject.net.proxyarp.ProxyArpStoreDelegate;
Jonathan Hart704ca142014-10-09 09:34:39 -070065
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070066import java.nio.ByteBuffer;
Thomas Vachuska27bee092015-06-23 19:03:10 -070067import java.util.ArrayList;
68import java.util.Collections;
Luca Pretef70d3992015-10-30 16:24:14 -070069import java.util.HashSet;
Thomas Vachuska27bee092015-06-23 19:03:10 -070070import java.util.List;
71import java.util.Set;
72
Jonathan Hart4cb39882015-08-12 23:50:55 -040073import static org.easymock.EasyMock.anyObject;
74import static org.easymock.EasyMock.createMock;
75import static org.easymock.EasyMock.expect;
76import static org.easymock.EasyMock.replay;
Charles Chanad3b5502015-10-23 16:24:20 -070077import static org.hamcrest.Matchers.anyOf;
78import static org.hamcrest.Matchers.is;
Jonathan Hart4cb39882015-08-12 23:50:55 -040079import static org.junit.Assert.assertArrayEquals;
80import static org.junit.Assert.assertEquals;
81import static org.junit.Assert.assertFalse;
Charles Chanad3b5502015-10-23 16:24:20 -070082import static org.junit.Assert.assertNotNull;
83import static org.junit.Assert.assertThat;
Jonathan Hart4cb39882015-08-12 23:50:55 -040084import static org.junit.Assert.assertTrue;
Jonathan Hart704ca142014-10-09 09:34:39 -070085
86/**
87 * Tests for the {@link ProxyArpManager} class.
88 */
89public class ProxyArpManagerTest {
90
Luca Pretef70d3992015-10-30 16:24:14 -070091 private static final int NUM_DEVICES = 10;
Jonathan Hart704ca142014-10-09 09:34:39 -070092 private static final int NUM_PORTS_PER_DEVICE = 3;
Luca Pretef70d3992015-10-30 16:24:14 -070093 private static final int LAST_CONF_DEVICE_INTF_VLAN_IP = 3;
94 private static final int LAST_CONF_DEVICE_INTF_VLAN = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070095
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -080096 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
97 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
Luca Pretef70d3992015-10-30 16:24:14 -070098 private static final Ip6Address IP3 = Ip6Address.valueOf("1000:ffff::1");
99 private static final Ip6Address IP4 = Ip6Address.valueOf("1000:ffff::2");
Jonathan Hart704ca142014-10-09 09:34:39 -0700100
101 private static final ProviderId PID = new ProviderId("of", "foo");
102
103 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
104 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
Luca Pretef70d3992015-10-30 16:24:14 -0700105 private static final VlanId VLAN10 = VlanId.vlanId((short) 10);
106
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");
112
Charles Chanad3b5502015-10-23 16:24:20 -0700113 private static final MacAddress SOLICITED_MAC3 = MacAddress.valueOf("33:33:FF:00:00:01");
Luca Pretef70d3992015-10-30 16:24:14 -0700114
Jonathan Hart704ca142014-10-09 09:34:39 -0700115 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
116 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
Charles Chanad3b5502015-10-23 16:24:20 -0700117 private static final HostId HID3 = HostId.hostId(MAC3, VLAN1);
118 private static final HostId HID4 = HostId.hostId(MAC4, VLAN1);
Luca Pretef70d3992015-10-30 16:24:14 -0700119 private static final HostId HID10 = HostId.hostId(MAC10, VLAN10);
120
Charles Chanad3b5502015-10-23 16:24:20 -0700121 private static final HostId SOLICITED_HID3 = HostId.hostId(SOLICITED_MAC3, VLAN1);
Jonathan Hart704ca142014-10-09 09:34:39 -0700122
123 private static final DeviceId DID1 = getDeviceId(1);
124 private static final DeviceId DID2 = getDeviceId(2);
Luca Pretef70d3992015-10-30 16:24:14 -0700125
Jonathan Hart704ca142014-10-09 09:34:39 -0700126 private static final PortNumber P1 = PortNumber.portNumber(1);
Luca Pretef70d3992015-10-30 16:24:14 -0700127
Jonathan Hart704ca142014-10-09 09:34:39 -0700128 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
129 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
130
Luca Pretef70d3992015-10-30 16:24:14 -0700131 private final byte[] zeroMacAddress = MacAddress.ZERO.toBytes();
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700132
Luca Pretef70d3992015-10-30 16:24:14 -0700133 // The first three devices in the topology have interfaces configured
134 // with VLANs and IPs
135 private final List<ConnectPoint> configIpCPoints = new ArrayList<>();
136
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<>();
140
141 // Remaining devices in the network (id > 6) don't have any interface
142 // configured.
143 private final List<ConnectPoint> noConfigCPoints = new ArrayList<>();
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700144
Jonathan Hart704ca142014-10-09 09:34:39 -0700145 private ProxyArpManager proxyArp;
146
147 private TestPacketService packetService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700148 private DeviceService deviceService;
Luca Pretef70d3992015-10-30 16:24:14 -0700149 private EdgePortService edgePortService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700150 private LinkService linkService;
151 private HostService hostService;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400152 private InterfaceService interfaceService;
Jonathan Hart704ca142014-10-09 09:34:39 -0700153
154 @Before
155 public void setUp() throws Exception {
156 proxyArp = new ProxyArpManager();
157 packetService = new TestPacketService();
158 proxyArp.packetService = packetService;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700159 proxyArp.store = new TestProxyArpStoreAdapter();
Jonathan Hart704ca142014-10-09 09:34:39 -0700160
Luca Pretef70d3992015-10-30 16:24:14 -0700161 // Create a host service mock here.
Jonathan Hart704ca142014-10-09 09:34:39 -0700162 hostService = createMock(HostService.class);
163 proxyArp.hostService = hostService;
164
Luca Pretef70d3992015-10-30 16:24:14 -0700165 // Create an edge port service.
166 edgePortService = createMock(EdgePortService.class);
167 proxyArp.edgeService = edgePortService;
168
169 // Create interface service
Jonathan Hart4cb39882015-08-12 23:50:55 -0400170 interfaceService = createMock(InterfaceService.class);
171 proxyArp.interfaceService = interfaceService;
172
Luca Pretef70d3992015-10-30 16:24:14 -0700173 // Create the topology
Jonathan Hart704ca142014-10-09 09:34:39 -0700174 createTopology();
175 proxyArp.deviceService = deviceService;
176 proxyArp.linkService = linkService;
177
Luca Pretef70d3992015-10-30 16:24:14 -0700178 setupNoConfigCPoints();
179 setupconfigIpCPoints();
180 setupconfigVlanCPoints();
181
Jonathan Hart704ca142014-10-09 09:34:39 -0700182 proxyArp.activate();
183 }
184
185 /**
186 * Creates a fake topology to feed into the ARP module.
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700187 * <p>
Jonathan Hart704ca142014-10-09 09:34:39 -0700188 * 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).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700191 * 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.
Jonathan Hart704ca142014-10-09 09:34:39 -0700194 */
195 private void createTopology() {
196 deviceService = createMock(DeviceService.class);
197 linkService = createMock(LinkService.class);
198
199 deviceService.addListener(anyObject(DeviceListener.class));
200 linkService.addListener(anyObject(LinkListener.class));
201
202 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
203 createLinks(NUM_DEVICES);
Luca Pretef70d3992015-10-30 16:24:14 -0700204 addIntfConfig();
205 popluateEdgePortService();
Jonathan Hart704ca142014-10-09 09:34:39 -0700206 }
207
208 /**
209 * Creates the devices for the fake topology.
210 */
211 private void createDevices(int numDevices, int numPorts) {
212 List<Device> devices = new ArrayList<>();
213
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();
218 replay(device);
219
220 devices.add(device);
221
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();
226 replay(port);
227 ports.add(port);
228 }
229
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700230 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
231 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700232 }
233
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700234 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700235 replay(deviceService);
236 }
237
238 /**
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.
242 */
243 private void createLinks(int numDevices) {
Sho SHIMIZUd88db6f2015-09-09 14:22:06 -0700244 List<Link> links = new ArrayList<>();
Jonathan Hart704ca142014-10-09 09:34:39 -0700245
246 for (int i = 1; i <= numDevices; i++) {
247 ConnectPoint src = new ConnectPoint(
248 getDeviceId(i),
249 PortNumber.portNumber(2));
250 ConnectPoint dst = new ConnectPoint(
251 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
252 PortNumber.portNumber(3));
253
254 Link link = createMock(Link.class);
255 expect(link.src()).andReturn(src).anyTimes();
256 expect(link.dst()).andReturn(dst).anyTimes();
257 replay(link);
258
259 links.add(link);
260 }
261
262 expect(linkService.getLinks()).andReturn(links).anyTimes();
263 replay(linkService);
264 }
265
Luca Pretef70d3992015-10-30 16:24:14 -0700266 /**
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.
272 */
273 private void addIntfConfig() {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400274 Set<Interface> interfaces = Sets.newHashSet();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700275
Luca Pretef70d3992015-10-30 16:24:14 -0700276 Set<Interface> vlanOneSet = new HashSet<>();
277
278 for (int i = 1; i <= LAST_CONF_DEVICE_INTF_VLAN_IP; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700279 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
Charles Chanad3b5502015-10-23 16:24:20 -0700280
Luca Pretef70d3992015-10-30 16:24:14 -0700281 // Interface addresses for IPv4
Charles Chanad3b5502015-10-23 16:24:20 -0700282 Ip4Prefix prefix1 = Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
283 Ip4Address addr1 = Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800284 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
285 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700286 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
287 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
Charles Chanad3b5502015-10-23 16:24:20 -0700288
Luca Pretef70d3992015-10-30 16:24:14 -0700289 // Interface addresses for IPv6
Charles Chanad3b5502015-10-23 16:24:20 -0700290 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");
Luca Pretef70d3992015-10-30 16:24:14 -0700293 Ip6Address addr4 = Ip6Address.valueOf((2 * i) + "000::2");
Charles Chanad3b5502015-10-23 16:24:20 -0700294 InterfaceIpAddress ia3 = new InterfaceIpAddress(addr3, prefix3);
295 InterfaceIpAddress ia4 = new InterfaceIpAddress(addr4, prefix4);
296
Luca Pretef70d3992015-10-30 16:24:14 -0700297 // Setting up interfaces
Charles Chanad3b5502015-10-23 16:24:20 -0700298 Interface intf1 = new Interface(cp, Sets.newHashSet(ia1, ia3),
Jonathan Hart4cb39882015-08-12 23:50:55 -0400299 MacAddress.valueOf(2 * i - 1),
300 VlanId.vlanId((short) 1));
Charles Chanad3b5502015-10-23 16:24:20 -0700301 Interface intf2 = new Interface(cp, Sets.newHashSet(ia2, ia4),
Jonathan Hart4cb39882015-08-12 23:50:55 -0400302 MacAddress.valueOf(2 * i),
303 VlanId.NONE);
Luca Pretef70d3992015-10-30 16:24:14 -0700304
Jonathan Hart4cb39882015-08-12 23:50:55 -0400305 interfaces.add(intf1);
306 interfaces.add(intf2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700307
Luca Pretef70d3992015-10-30 16:24:14 -0700308 vlanOneSet.add(intf1);
309
Jonathan Hart4cb39882015-08-12 23:50:55 -0400310 expect(interfaceService.getInterfacesByPort(cp))
311 .andReturn(Sets.newHashSet(intf1, intf2)).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700312 }
Luca Pretef70d3992015-10-30 16:24:14 -0700313 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,
316 MacAddress.NONE,
317 VlanId.vlanId((short) 1));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700318
Luca Pretef70d3992015-10-30 16:24:14 -0700319 interfaces.add(intf1);
320 vlanOneSet.add(intf1);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400321
322 expect(interfaceService.getInterfacesByPort(cp))
Luca Pretef70d3992015-10-30 16:24:14 -0700323 .andReturn(Sets.newHashSet(intf1)).anyTimes();
324 }
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();
330
331 for (int i = LAST_CONF_DEVICE_INTF_VLAN + 1; i <= NUM_DEVICES; i++) {
332 ConnectPoint cp = new ConnectPoint(getDeviceId(i),
333 P1);
334 expect(interfaceService.getInterfacesByPort(cp))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400335 .andReturn(Collections.emptySet()).anyTimes();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700336 }
337 }
338
Jonathan Hart704ca142014-10-09 09:34:39 -0700339 /**
Luca Pretef70d3992015-10-30 16:24:14 -0700340 * Populates edge ports in the EdgePortService to return all port 1
341 * as edge ports.
342 */
343 private void popluateEdgePortService() {
344 Set<ConnectPoint> edgeConnectPoints = new HashSet<>();
345
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(
349 getDeviceId(i),
350 PortNumber.portNumber(1));
351 ConnectPoint noEdgeConnectPointOne = new ConnectPoint(
352 getDeviceId(i),
353 PortNumber.portNumber(2));
354 ConnectPoint noEdgeConnectPointTwo = new ConnectPoint(
355 getDeviceId(i),
356 PortNumber.portNumber(3));
357
358 edgeConnectPoints.add(edgeConnectPoint);
359
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();
366 }
367 }
368 expect(edgePortService.getEdgePoints())
369 .andReturn(edgeConnectPoints).anyTimes();
370
371 replay(edgePortService);
372 }
373
374 /**
375 * Creates a list of connect points used to verify floodling on ports
376 * with no interfaces configured (all ports without interface config).
377 */
378 private void setupNoConfigCPoints() {
379 for (int i = NUM_DEVICES / 2 + 2; i <= NUM_DEVICES; i++) {
380 ConnectPoint connectPoint = new ConnectPoint(
381 getDeviceId(i),
382 PortNumber.portNumber(1));
383 noConfigCPoints.add(connectPoint);
384 }
385 }
386
387 /**
388 * Creates a list of connect points used to verify floodling on ports
389 * with interfaces configured (both VLAN and IP).
390 */
391 private void setupconfigIpCPoints() {
392 for (int i = 1; i <= 3; i++) {
393 ConnectPoint connectPoint = new ConnectPoint(
394 getDeviceId(i),
395 PortNumber.portNumber(1));
396 configIpCPoints.add(connectPoint);
397 }
398 }
399
400 /**
401 * Creates a list of connect points used to verify floodling on ports
402 * with interfaces configured (both VLAN and IP).
403 */
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(
407 getDeviceId(i),
408 PortNumber.portNumber(1));
409 configVlanCPoints.add(connectPoint);
410 }
411 }
412
413 /**
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700414 * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
415 * case where the IP address is not known.
Jonathan Hart704ca142014-10-09 09:34:39 -0700416 * Verifies the method returns false.
417 */
418 @Test
419 public void testNotKnown() {
420 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
421 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400422 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700423
Jonathan Hartf84591d2015-01-16 14:33:43 -0800424 assertFalse(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700425 }
426
427 /**
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700428 * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the
429 * case where the IP address is known.
Jonathan Hart704ca142014-10-09 09:34:39 -0700430 * Verifies the method returns true.
431 */
432 @Test
433 public void testKnown() {
434 Host host1 = createMock(Host.class);
435 Host host2 = createMock(Host.class);
436
437 expect(hostService.getHostsByIp(IP1))
438 .andReturn(Sets.newHashSet(host1, host2));
439 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400440 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700441
Jonathan Hartf84591d2015-01-16 14:33:43 -0800442 assertTrue(proxyArp.isKnown(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700443 }
444
445 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800446 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700447 * destination host is known.
Luca Pretef70d3992015-10-30 16:24:14 -0700448 * 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.
Jonathan Hart704ca142014-10-09 09:34:39 -0700453 */
454 @Test
455 public void testReplyKnown() {
Luca Pretef70d3992015-10-30 16:24:14 -0700456 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(NUM_DEVICES),
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700457 Collections.singleton(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700458
Luca Pretef70d3992015-10-30 16:24:14 -0700459 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(NUM_DEVICES - 1),
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700460 Collections.singleton(IP2));
Jonathan Hart704ca142014-10-09 09:34:39 -0700461
Luca Pretef70d3992015-10-30 16:24:14 -0700462 expect(hostService.getHostsByIp(IP2))
Thomas Vachuska27bee092015-06-23 19:03:10 -0700463 .andReturn(Collections.singleton(replyer));
Luca Pretef70d3992015-10-30 16:24:14 -0700464 expect(hostService.getHost(HID1)).andReturn(requestor);
Jonathan Hart704ca142014-10-09 09:34:39 -0700465
466 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400467 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700468
Luca Pretef70d3992015-10-30 16:24:14 -0700469 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700470
Luca Pretef70d3992015-10-30 16:24:14 -0700471 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
Jonathan Hart704ca142014-10-09 09:34:39 -0700472
473 assertEquals(1, packetService.packets.size());
Luca Pretef70d3992015-10-30 16:24:14 -0700474 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, MAC2, MAC1, IP2, IP1);
475 verifyPacketOut(arpReply, getLocation(NUM_DEVICES), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700476 }
477
478 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800479 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Charles Chanad3b5502015-10-23 16:24:20 -0700480 * destination host is known.
481 * Verifies the correct NDP reply is sent out the correct port.
482 */
483 @Test
484 public void testReplyKnownIpv6() {
Charles Chanad3b5502015-10-23 16:24:20 -0700485 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(4),
486 Collections.singleton(IP3));
487
488 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5),
489 Collections.singleton(IP4));
490
491 expect(hostService.getHostsByIp(IP3))
492 .andReturn(Collections.singleton(replyer));
493 expect(hostService.getHost(HID4)).andReturn(requestor);
494
495 replay(hostService);
496 replay(interfaceService);
497
498 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
499 MAC4, SOLICITED_MAC3,
500 IP4, IP3);
501
502 proxyArp.reply(ndpRequest, getLocation(5));
503
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));
508 }
509
510 /**
511 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700512 * destination host is not known.
Luca Pretef70d3992015-10-30 16:24:14 -0700513 * 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.
Jonathan Hart704ca142014-10-09 09:34:39 -0700517 * Verifies the ARP request is flooded out the correct edge ports.
518 */
519 @Test
520 public void testReplyUnknown() {
Luca Pretef70d3992015-10-30 16:24:14 -0700521 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
522 Collections.singleton(IP1));
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700523
Luca Pretef70d3992015-10-30 16:24:14 -0700524 expect(hostService.getHostsByIp(IP2))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400525 .andReturn(Collections.emptySet());
Luca Pretef70d3992015-10-30 16:24:14 -0700526 expect(interfaceService.getInterfacesByIp(IP1))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400527 .andReturn(Collections.emptySet());
Luca Pretef70d3992015-10-30 16:24:14 -0700528 expect(hostService.getHost(HID10)).andReturn(requestor);
Jonathan Hart704ca142014-10-09 09:34:39 -0700529
530 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400531 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700532
Luca Pretef70d3992015-10-30 16:24:14 -0700533 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700534
Luca Pretef70d3992015-10-30 16:24:14 -0700535 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700536
Luca Pretef70d3992015-10-30 16:24:14 -0700537 verifyFlood(arpRequest, noConfigCPoints);
Jonathan Hart704ca142014-10-09 09:34:39 -0700538 }
539
540 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800541 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Charles Chanad3b5502015-10-23 16:24:20 -0700542 * destination host is not known.
543 * Verifies the NDP request is flooded out the correct edge ports.
544 */
545 @Test
546 public void testReplyUnknownIpv6() {
Luca Pretef70d3992015-10-30 16:24:14 -0700547 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
Charles Chanad3b5502015-10-23 16:24:20 -0700548 Collections.singleton(IP4));
549
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);
555
556 replay(hostService);
557 replay(interfaceService);
558
559 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
560 MAC4, SOLICITED_MAC3,
561 IP4, IP3);
562
Luca Pretef70d3992015-10-30 16:24:14 -0700563 proxyArp.reply(ndpRequest, getLocation(NUM_DEVICES));
Charles Chanad3b5502015-10-23 16:24:20 -0700564
Luca Pretef70d3992015-10-30 16:24:14 -0700565 verifyFlood(ndpRequest, noConfigCPoints);
Charles Chanad3b5502015-10-23 16:24:20 -0700566 }
567
568 /**
569 * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700570 * destination host is known for that IP address, but is not on the same
571 * VLAN as the source host.
Luca Pretef70d3992015-10-30 16:24:14 -0700572 * 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
576 * 4 and 5.
577 *
Jonathan Hart704ca142014-10-09 09:34:39 -0700578 * Verifies the ARP request is flooded out the correct edge ports.
579 */
580 @Test
581 public void testReplyDifferentVlan() {
Luca Pretef70d3992015-10-30 16:24:14 -0700582 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700583 Collections.singleton(IP1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700584
Luca Pretef70d3992015-10-30 16:24:14 -0700585 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN2, getLocation(NUM_DEVICES - 1),
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700586 Collections.singleton(IP2));
Jonathan Hart704ca142014-10-09 09:34:39 -0700587
Luca Pretef70d3992015-10-30 16:24:14 -0700588 expect(hostService.getHostsByIp(IP2))
Jonathan Hart704ca142014-10-09 09:34:39 -0700589 .andReturn(Collections.singleton(replyer));
Luca Pretef70d3992015-10-30 16:24:14 -0700590 expect(interfaceService.getInterfacesByIp(IP1))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400591 .andReturn(Collections.emptySet());
Luca Pretef70d3992015-10-30 16:24:14 -0700592 expect(hostService.getHost(HID10)).andReturn(requestor);
Jonathan Hart704ca142014-10-09 09:34:39 -0700593
594 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400595 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700596
Luca Pretef70d3992015-10-30 16:24:14 -0700597 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700598
Luca Pretef70d3992015-10-30 16:24:14 -0700599 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
600
601 verifyFlood(arpRequest, noConfigCPoints);
602 }
603
604 /**
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
608 * the same vlan.
609 * It's expected to see the ARP request going out through ports with no interfaces
610 * configured, devices 4 and 5, port 1.
611 *
612 * Verifies the ARP request is flooded out the correct edge ports.
613 */
614 @Test
615 public void testConfiguredVlan() {
616 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(NUM_DEVICES),
617 Collections.singleton(IP1));
618
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);
624
625 replay(hostService);
626 replay(interfaceService);
627
628 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
629
630 proxyArp.reply(arpRequest, getLocation(NUM_DEVICES));
631
632 verifyFlood(arpRequest, noConfigCPoints);
633 }
634
635 /**
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
639 * the same vlan.
640 * It's expected to see the ARP request going out through ports with no interfaces
641 * configured, devices 4 and 5, port 1.
642 *
643 * Verifies the ARP request is flooded out the correct edge ports.
644 */
645 @Test
646 public void testConfiguredVlanOnInterfaces() {
647 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(6),
648 Collections.singleton(IP1));
649
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);
655
656 replay(hostService);
657 replay(interfaceService);
658
659 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
660
Jonathan Hartf84591d2015-01-16 14:33:43 -0800661 proxyArp.reply(arpRequest, getLocation(6));
Jonathan Hart704ca142014-10-09 09:34:39 -0700662
Luca Pretef70d3992015-10-30 16:24:14 -0700663 verifyFlood(arpRequest, configVlanCPoints);
Jonathan Hart704ca142014-10-09 09:34:39 -0700664 }
665
Charles Chanad3b5502015-10-23 16:24:20 -0700666 /**
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.
671 */
672 @Test
673 public void testReplyDifferentVlanIpv6() {
Luca Pretef70d3992015-10-30 16:24:14 -0700674 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
Charles Chanad3b5502015-10-23 16:24:20 -0700675 Collections.singleton(IP4));
676
Luca Pretef70d3992015-10-30 16:24:14 -0700677 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN2, getLocation(NUM_DEVICES - 1),
678 Collections.singleton(IP3));
679
Charles Chanad3b5502015-10-23 16:24:20 -0700680 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);
685
686 replay(hostService);
687 replay(interfaceService);
688
689 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
690 MAC4, SOLICITED_MAC3,
691 IP4, IP3);
692
Luca Pretef70d3992015-10-30 16:24:14 -0700693 proxyArp.reply(ndpRequest, getLocation(NUM_DEVICES));
Charles Chanad3b5502015-10-23 16:24:20 -0700694
Luca Pretef70d3992015-10-30 16:24:14 -0700695 verifyFlood(ndpRequest, noConfigCPoints);
Charles Chanad3b5502015-10-23 16:24:20 -0700696 }
697
698 /**
699 * Test ARP request from external network to an internal host.
700 */
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700701 @Test
702 public void testReplyToRequestForUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800703 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");
Jonathan Harta887ba82014-11-03 15:20:52 -0800706 MacAddress firstMac = MacAddress.valueOf(1L);
707 MacAddress secondMac = MacAddress.valueOf(2L);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700708
Luca Pretef70d3992015-10-30 16:24:14 -0700709 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700710 Collections.singleton(theirIp));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700711
Luca Pretef70d3992015-10-30 16:24:14 -0700712 expect(hostService.getHost(HID1)).andReturn(requestor);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700713 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400714 replay(interfaceService);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700715
Luca Pretef70d3992015-10-30 16:24:14 -0700716 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourFirstIp);
717
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700718 proxyArp.reply(arpRequest, LOC1);
719
720 assertEquals(1, packetService.packets.size());
Luca Pretef70d3992015-10-30 16:24:14 -0700721 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, firstMac, MAC1, ourFirstIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700722 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
723
724 // Test a request for the second address on that port
725 packetService.packets.clear();
Luca Pretef70d3992015-10-30 16:24:14 -0700726 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourSecondIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700727
728 proxyArp.reply(arpRequest, LOC1);
729
730 assertEquals(1, packetService.packets.size());
Luca Pretef70d3992015-10-30 16:24:14 -0700731 arpReply = buildArp(ARP.OP_REPLY, VLAN1, secondMac, MAC1, ourSecondIp, theirIp);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700732 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
733 }
734
Charles Chanad3b5502015-10-23 16:24:20 -0700735 /**
736 * Test NDP request from external network to an internal host.
737 */
738 @Test
739 public void testReplyToRequestForUsIpv6() {
740 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
741 Ip6Address ourFirstIp = Ip6Address.valueOf("1000::1");
Luca Pretef70d3992015-10-30 16:24:14 -0700742 Ip6Address ourSecondIp = Ip6Address.valueOf("2000::2");
Charles Chanad3b5502015-10-23 16:24:20 -0700743 MacAddress firstMac = MacAddress.valueOf(1L);
744 MacAddress secondMac = MacAddress.valueOf(2L);
745
746 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
747 Collections.singleton(theirIp));
748
749 expect(hostService.getHost(HID2)).andReturn(requestor);
750 expect(hostService.getHostsByIp(ourFirstIp))
751 .andReturn(Collections.singleton(requestor));
752 replay(hostService);
753 replay(interfaceService);
754
755 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
756 MAC2,
757 MacAddress.valueOf("33:33:ff:00:00:01"),
758 theirIp,
759 ourFirstIp);
Luca Pretef70d3992015-10-30 16:24:14 -0700760
Charles Chanad3b5502015-10-23 16:24:20 -0700761 proxyArp.reply(ndpRequest, LOC1);
762 assertEquals(1, packetService.packets.size());
763
764 Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT,
765 firstMac,
766 MAC2,
767 ourFirstIp,
768 theirIp);
769 verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0));
770
771 // Test a request for the second address on that port
772 packetService.packets.clear();
773 ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
774 MAC2,
Luca Pretef70d3992015-10-30 16:24:14 -0700775 MacAddress.valueOf("33:33:ff:00:00:01"),
776 theirIp,
777 ourSecondIp);
Charles Chanad3b5502015-10-23 16:24:20 -0700778 proxyArp.reply(ndpRequest, LOC1);
779 assertEquals(1, packetService.packets.size());
780
781 ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT,
782 secondMac,
783 MAC2,
784 ourSecondIp,
785 theirIp);
786 verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0));
787 }
788
789 /**
790 * Request for a valid external IPv4 address but coming in the wrong port.
791 */
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700792 @Test
793 public void testReplyExternalPortBadRequest() {
794 replay(hostService); // no further host service expectations
Jonathan Hart4cb39882015-08-12 23:50:55 -0400795 replay(interfaceService);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700796
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800797 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700798
799 // Request for a valid external IP address but coming in the wrong port
Luca Pretef70d3992015-10-30 16:24:14 -0700800 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp,
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700801 Ip4Address.valueOf("10.0.3.1"));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700802 proxyArp.reply(arpRequest, LOC1);
803 assertEquals(0, packetService.packets.size());
804
805 // Request for a valid internal IP address but coming in an external port
806 packetService.packets.clear();
Luca Pretef70d3992015-10-30 16:24:14 -0700807 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, IP1);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700808 proxyArp.reply(arpRequest, LOC1);
809 assertEquals(0, packetService.packets.size());
810 }
811
Charles Chanad3b5502015-10-23 16:24:20 -0700812 /**
813 * Request for a valid external IPv6 address but coming in the wrong port.
814 */
815 @Test
816 public void testReplyExternalPortBadRequestIpv6() {
817 replay(hostService); // no further host service expectations
818 replay(interfaceService);
819
820 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
821
822 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
Luca Pretef70d3992015-10-30 16:24:14 -0700823 MAC1,
824 MacAddress.valueOf("33:33:ff:00:00:01"),
825 theirIp,
826 Ip6Address.valueOf("3000::1"));
Charles Chanad3b5502015-10-23 16:24:20 -0700827 proxyArp.reply(ndpRequest, LOC1);
828 assertEquals(0, packetService.packets.size());
829
830 // Request for a valid internal IP address but coming in an external port
831 packetService.packets.clear();
832 ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
Luca Pretef70d3992015-10-30 16:24:14 -0700833 MAC1,
834 MacAddress.valueOf("33:33:ff:00:00:01"),
835 theirIp,
836 IP3);
Charles Chanad3b5502015-10-23 16:24:20 -0700837 proxyArp.reply(ndpRequest, LOC1);
838 assertEquals(0, packetService.packets.size());
839 }
840
841 /**
842 * Test ARP request from internal network to an external host.
843 */
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700844 @Test
845 public void testReplyToRequestFromUs() {
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800846 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700847 MacAddress ourMac = MacAddress.valueOf(1L);
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800848 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700849
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700850 expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400851 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"))),
854 ourMac, VLAN1)));
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700855 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
856 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400857 replay(interfaceService);
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700858
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700859 // This is a request from something inside our network (like a BGP
860 // daemon) to an external host.
Luca Pretef70d3992015-10-30 16:24:14 -0700861 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, ourMac, null, ourIp, theirIp);
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700862 //Ensure the packet is allowed through (it is not to an internal port)
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700863
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700864 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700865 assertEquals(1, packetService.packets.size());
866 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
867
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());
872 }
873
Jonathan Hart704ca142014-10-09 09:34:39 -0700874 /**
Charles Chanad3b5502015-10-23 16:24:20 -0700875 * Test NDP request from internal network to an external host.
876 */
877 @Test
878 public void testReplyToRequestFromUsIpv6() {
879 Ip6Address ourIp = Ip6Address.valueOf("1000::1");
880 MacAddress ourMac = MacAddress.valueOf(1L);
881 Ip6Address theirIp = Ip6Address.valueOf("1000::100");
882
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(
887 ourIp,
888 IpPrefix.valueOf("1000::1/64"))),
889 ourMac,
890 VLAN1)));
891 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
892 replay(hostService);
893 replay(interfaceService);
894
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,
898 ourMac,
899 MacAddress.valueOf("33:33:ff:00:00:01"),
900 ourIp,
901 theirIp);
902
Charles Chanad3b5502015-10-23 16:24:20 -0700903 proxyArp.reply(ndpRequest, getLocation(5));
904 assertEquals(1, packetService.packets.size());
905 verifyPacketOut(ndpRequest, getLocation(1), packetService.packets.get(0));
906
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());
911 }
912
913 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800914 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700915 * destination host is known.
916 * Verifies the correct ARP request is sent out the correct port.
917 */
918 @Test
919 public void testForwardToHost() {
920 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700921 Collections.singleton(IP1));
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700922 Host host2 = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC2,
923 Collections.singleton(IP2));
Jonathan Hart704ca142014-10-09 09:34:39 -0700924
925 expect(hostService.getHost(HID1)).andReturn(host1);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700926 expect(hostService.getHost(HID2)).andReturn(host2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700927 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400928 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700929
Luca Pretef70d3992015-10-30 16:24:14 -0700930 Ethernet arpRequest = buildArp(ARP.OP_REPLY, VLAN1, MAC2, MAC1, IP2, IP1);
Jonathan Hart704ca142014-10-09 09:34:39 -0700931
Jonathan Hartf84591d2015-01-16 14:33:43 -0800932 proxyArp.forward(arpRequest, LOC2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700933
934 assertEquals(1, packetService.packets.size());
935 OutboundPacket packet = packetService.packets.get(0);
936
937 verifyPacketOut(arpRequest, LOC1, packet);
938 }
939
940 /**
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800941 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Charles Chanad3b5502015-10-23 16:24:20 -0700942 * destination host is known.
943 * Verifies the correct ARP request is sent out the correct port.
944 */
945 @Test
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));
951
952 expect(hostService.getHost(SOLICITED_HID3)).andReturn(host1);
953 expect(hostService.getHost(HID4)).andReturn(host2);
954 replay(hostService);
955 replay(interfaceService);
956
957 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
958 MAC4, SOLICITED_MAC3,
959 IP4, IP3);
960
961 proxyArp.forward(ndpRequest, LOC2);
962
963 assertEquals(1, packetService.packets.size());
964 OutboundPacket packet = packetService.packets.get(0);
965
966 verifyPacketOut(ndpRequest, LOC1, packet);
967 }
968
969 /**
970 * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
Jonathan Hart704ca142014-10-09 09:34:39 -0700971 * destination host is not known.
972 * Verifies the correct ARP request is flooded out the correct edge ports.
973 */
974 @Test
975 public void testForwardFlood() {
Luca Pretef70d3992015-10-30 16:24:14 -0700976 expect(hostService.getHost(HID2)).andReturn(null);
Jonathan Hart704ca142014-10-09 09:34:39 -0700977 replay(hostService);
Jonathan Hart4cb39882015-08-12 23:50:55 -0400978 replay(interfaceService);
Jonathan Hart704ca142014-10-09 09:34:39 -0700979
Luca Pretef70d3992015-10-30 16:24:14 -0700980 Ethernet arpRequest = buildArp(ARP.OP_REPLY, VLAN1, MAC1, MAC2, IP1, IP2);
Jonathan Hart704ca142014-10-09 09:34:39 -0700981
Luca Pretef70d3992015-10-30 16:24:14 -0700982 proxyArp.forward(arpRequest, getLocation(NUM_DEVICES));
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700983
Luca Pretef70d3992015-10-30 16:24:14 -0700984 verifyFlood(arpRequest, noConfigCPoints);
Jonathan Hart704ca142014-10-09 09:34:39 -0700985 }
986
987 /**
Charles Chanad3b5502015-10-23 16:24:20 -0700988 * 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.
991 */
992 @Test
993 public void testForwardFloodIpv6() {
994 expect(hostService.getHost(SOLICITED_HID3)).andReturn(null);
995 replay(hostService);
996 replay(interfaceService);
997
998 Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION,
999 MAC4, SOLICITED_MAC3,
1000 IP4, IP3);
1001
Luca Pretef70d3992015-10-30 16:24:14 -07001002 proxyArp.forward(ndpRequest, getLocation(NUM_DEVICES));
Charles Chanad3b5502015-10-23 16:24:20 -07001003
Luca Pretef70d3992015-10-30 16:24:14 -07001004 verifyFlood(ndpRequest, noConfigCPoints);
Charles Chanad3b5502015-10-23 16:24:20 -07001005 }
1006
1007 /**
Jonathan Hartf84591d2015-01-16 14:33:43 -08001008 * Verifies that the given packet was flooded out all available edge ports,
1009 * except for the input port.
Jonathan Hart704ca142014-10-09 09:34:39 -07001010 *
1011 * @param packet the packet that was expected to be flooded
Luca Pretef70d3992015-10-30 16:24:14 -07001012 * @param connectPoints the connectPoints where the outpacket should be
1013 * observed
Jonathan Hart704ca142014-10-09 09:34:39 -07001014 */
Luca Pretef70d3992015-10-30 16:24:14 -07001015 private void verifyFlood(Ethernet packet, List<ConnectPoint> connectPoints) {
1016
Jonathan Hartf84591d2015-01-16 14:33:43 -08001017 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
Luca Pretef70d3992015-10-30 16:24:14 -07001018 assertEquals(connectPoints.size() - 1, packetService.packets.size());
Jonathan Hart704ca142014-10-09 09:34:39 -07001019
1020 Collections.sort(packetService.packets,
Jonathan Hart4cb39882015-08-12 23:50:55 -04001021 (o1, o2) -> o1.sendThrough().uri().compareTo(o2.sendThrough().uri()));
Jonathan Hart704ca142014-10-09 09:34:39 -07001022
Luca Pretef70d3992015-10-30 16:24:14 -07001023 for (int i = 0; i < connectPoints.size() - 1; i++) {
Jonathan Hart704ca142014-10-09 09:34:39 -07001024 OutboundPacket outboundPacket = packetService.packets.get(i);
Luca Pretef70d3992015-10-30 16:24:14 -07001025 verifyPacketOut(packet, connectPoints.get(i), outboundPacket);
Jonathan Hart704ca142014-10-09 09:34:39 -07001026 }
1027 }
1028
1029 /**
1030 * Verifies the given packet was sent out the given port.
1031 *
1032 * @param expected the packet that was expected to be sent
Thomas Vachuska27bee092015-06-23 19:03:10 -07001033 * @param outPort the port the packet was expected to be sent out
1034 * @param actual the actual OutboundPacket to verify
Jonathan Hart704ca142014-10-09 09:34:39 -07001035 */
1036 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
Thomas Vachuska27bee092015-06-23 19:03:10 -07001037 OutboundPacket actual) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001038 assertArrayEquals(expected.serialize(), actual.data().array());
Ray Milkey42507352015-03-20 15:16:10 -07001039 assertEquals(1, actual.treatment().immediate().size());
Jonathan Hart704ca142014-10-09 09:34:39 -07001040 assertEquals(outPort.deviceId(), actual.sendThrough());
Ray Milkey42507352015-03-20 15:16:10 -07001041 Instruction instruction = actual.treatment().immediate().get(0);
Jonathan Hart704ca142014-10-09 09:34:39 -07001042 assertTrue(instruction instanceof OutputInstruction);
1043 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
1044 }
1045
1046 /**
1047 * Returns the device ID of the ith device.
1048 *
1049 * @param i device to get the ID of
1050 * @return the device ID
1051 */
1052 private static DeviceId getDeviceId(int i) {
1053 return DeviceId.deviceId("" + i);
1054 }
1055
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -07001056 private static HostLocation getLocation(int i) {
1057 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
1058 }
1059
Jonathan Hart704ca142014-10-09 09:34:39 -07001060 /**
1061 * Builds an ARP packet with the given parameters.
1062 *
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
Thomas Vachuska27bee092015-06-23 19:03:10 -07001066 * @param srcIp source IP address
1067 * @param dstIp destination IP address
Jonathan Hart704ca142014-10-09 09:34:39 -07001068 * @return the ARP packet
1069 */
Luca Pretef70d3992015-10-30 16:24:14 -07001070 private Ethernet buildArp(short opcode, VlanId vlanId, MacAddress srcMac,
1071 MacAddress dstMac, Ip4Address srcIp, Ip4Address dstIp) {
Jonathan Hart704ca142014-10-09 09:34:39 -07001072 Ethernet eth = new Ethernet();
1073
1074 if (dstMac == null) {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001075 eth.setDestinationMACAddress(MacAddress.BROADCAST);
Jonathan Hart704ca142014-10-09 09:34:39 -07001076 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001077 eth.setDestinationMACAddress(dstMac);
Jonathan Hart704ca142014-10-09 09:34:39 -07001078 }
1079
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001080 eth.setSourceMACAddress(srcMac);
Jonathan Hart704ca142014-10-09 09:34:39 -07001081 eth.setEtherType(Ethernet.TYPE_ARP);
Luca Pretef70d3992015-10-30 16:24:14 -07001082 eth.setVlanID(vlanId.toShort());
Jonathan Hart704ca142014-10-09 09:34:39 -07001083
1084 ARP arp = new ARP();
1085 arp.setOpCode(opcode);
1086 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1087 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1088
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -08001089 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
Jonathan Hart704ca142014-10-09 09:34:39 -07001090 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001091 arp.setSenderHardwareAddress(srcMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -07001092
1093 if (dstMac == null) {
Luca Pretef70d3992015-10-30 16:24:14 -07001094 arp.setTargetHardwareAddress(zeroMacAddress);
Jonathan Hart704ca142014-10-09 09:34:39 -07001095 } else {
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -08001096 arp.setTargetHardwareAddress(dstMac.toBytes());
Jonathan Hart704ca142014-10-09 09:34:39 -07001097 }
1098
1099 arp.setSenderProtocolAddress(srcIp.toOctets());
1100 arp.setTargetProtocolAddress(dstIp.toOctets());
1101
1102 eth.setPayload(arp);
1103 return eth;
1104 }
1105
1106 /**
Charles Chanad3b5502015-10-23 16:24:20 -07001107 * Builds an NDP packet with the given parameters.
1108 *
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
1115 */
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)
1121 ));
1122 assertNotNull(srcMac);
1123 assertNotNull(dstMac);
1124 assertNotNull(srcIp);
1125 assertNotNull(dstIp);
1126
1127 IPacket ndp;
1128 if (type == ICMP6.NEIGHBOR_SOLICITATION) {
1129 ndp = new NeighborSolicitation().setTargetAddress(dstIp.toOctets());
1130 } else {
1131 ndp = new NeighborAdvertisement()
1132 .setSolicitedFlag((byte) 1)
1133 .setOverrideFlag((byte) 1)
1134 .setTargetAddress(srcIp.toOctets())
1135 .addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
1136 srcMac.toBytes());
1137 }
1138
1139 ICMP6 icmp6 = new ICMP6();
1140 icmp6.setIcmpType(type);
1141 icmp6.setIcmpCode((byte) 0);
1142 icmp6.setPayload(ndp);
1143
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);
1150
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);
1157
1158 return eth;
1159 }
1160
1161 /**
Jonathan Hart704ca142014-10-09 09:34:39 -07001162 * Test PacketService implementation that simply stores OutboundPackets
1163 * passed to {@link #emit(OutboundPacket)} for later verification.
1164 */
Thomas Vachuska27bee092015-06-23 19:03:10 -07001165 class TestPacketService extends PacketServiceAdapter {
Jonathan Hart704ca142014-10-09 09:34:39 -07001166
1167 List<OutboundPacket> packets = new ArrayList<>();
1168
1169 @Override
Jonathan Hart704ca142014-10-09 09:34:39 -07001170 public void emit(OutboundPacket packet) {
1171 packets.add(packet);
1172 }
Jonathan Hart3cfce8e2015-01-14 16:43:27 -08001173
Jonathan Hart704ca142014-10-09 09:34:39 -07001174 }
Aaron Kruglikovd8123832015-07-06 14:20:25 -07001175
Thomas Vachuskab2c47a72015-08-05 14:22:54 -07001176 private class TestProxyArpStoreAdapter implements ProxyArpStore {
1177 @Override
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));
1181 }
1182
1183 @Override
1184 public void setDelegate(ProxyArpStoreDelegate delegate) {
1185 }
1186 }
Jonathan Hart704ca142014-10-09 09:34:39 -07001187}