blob: 2c50246b33cdb9d4311572677a5e395def5cbf28 [file] [log] [blame]
Andreas Papazois6e1fca32016-02-09 11:27:10 +02001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.sdxl3;
17
18import com.google.common.collect.Sets;
19import org.junit.Before;
20import org.junit.Test;
21import org.onlab.packet.ARP;
22import org.onlab.packet.Ethernet;
23import org.onlab.packet.ICMP6;
24import org.onlab.packet.IPacket;
25import org.onlab.packet.IPv6;
26import org.onlab.packet.Ip4Address;
27import org.onlab.packet.Ip4Prefix;
28import org.onlab.packet.Ip6Address;
29import org.onlab.packet.Ip6Prefix;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
32import org.onlab.packet.MacAddress;
33import org.onlab.packet.VlanId;
34import org.onlab.packet.ndp.NeighborAdvertisement;
35import org.onlab.packet.ndp.NeighborDiscoveryOptions;
36import org.onlab.packet.ndp.NeighborSolicitation;
37import org.onosproject.incubator.net.intf.Interface;
38import org.onosproject.incubator.net.intf.InterfaceService;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.DefaultHost;
41import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.Host;
44import org.onosproject.net.HostId;
45import org.onosproject.net.HostLocation;
46import org.onosproject.net.Link;
47import org.onosproject.net.Port;
48import org.onosproject.net.PortNumber;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.edge.EdgePortService;
52import org.onosproject.net.flow.instructions.Instruction;
53import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
54import org.onosproject.net.host.HostService;
55import org.onosproject.net.host.InterfaceIpAddress;
56import org.onosproject.net.link.LinkListener;
57import org.onosproject.net.link.LinkService;
58import org.onosproject.net.packet.DefaultInboundPacket;
59import org.onosproject.net.packet.InboundPacket;
60import org.onosproject.net.packet.OutboundPacket;
61import org.onosproject.net.packet.PacketServiceAdapter;
62import org.onosproject.net.provider.ProviderId;
63import org.onosproject.routing.config.BgpConfig;
64
65import java.nio.ByteBuffer;
66import java.util.ArrayList;
67import java.util.Arrays;
68import java.util.Collections;
69import java.util.HashSet;
70import java.util.List;
71import java.util.Optional;
72import java.util.Set;
73
74import static org.easymock.EasyMock.anyObject;
75import static org.easymock.EasyMock.createMock;
76import static org.easymock.EasyMock.expect;
77import static org.easymock.EasyMock.replay;
78import static org.easymock.EasyMock.reset;
79import static org.hamcrest.Matchers.anyOf;
80import static org.hamcrest.Matchers.is;
81import static org.junit.Assert.assertArrayEquals;
82import static org.junit.Assert.assertEquals;
83import static org.junit.Assert.assertNotNull;
84import static org.junit.Assert.assertThat;
85import static org.junit.Assert.assertTrue;
86
87/**
88 * Tests for the {@link SdxL3ArpHandler} class. It is based on tests for basic
89 * proxy ARP handling. Additional test cases for traffic between BGP peers are
90 * also included.
91 */
92public class SdxL3ArpHandlerTest {
93
94 private static final int NUM_DEVICES = 10;
95 private static final int NUM_PORTS_PER_DEVICE = 3;
96 private static final int LAST_CONF_DEVICE_INTF_VLAN_IP = 3;
97 private static final int LAST_CONF_DEVICE_INTF_VLAN = 6;
98
99 private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
100 private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
101 private static final Ip6Address IP3 = Ip6Address.valueOf("1000:ffff::1");
102 private static final Ip6Address IP4 = Ip6Address.valueOf("1000:ffff::2");
103
104 private static final ProviderId PID = new ProviderId("of", "foo");
105
106 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
107 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
108 private static final VlanId VLAN10 = VlanId.vlanId((short) 10);
109
110 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01");
111 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02");
112 private static final MacAddress MAC3 = MacAddress.valueOf("00:00:00:00:00:03");
113 private static final MacAddress MAC4 = MacAddress.valueOf("00:00:00:00:00:04");
114 private static final MacAddress MAC10 = MacAddress.valueOf("00:00:00:00:00:0A");
115
116 private static final MacAddress SOLICITED_MAC3 = MacAddress.valueOf("33:33:FF:00:00:01");
117
118 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
119 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
120 private static final HostId HID3 = HostId.hostId(MAC3, VLAN1);
121 private static final HostId HID4 = HostId.hostId(MAC4, VLAN1);
122 private static final HostId HID10 = HostId.hostId(MAC10, VLAN10);
123
124 private static final DeviceId DID1 = getDeviceId(1);
125 private static final DeviceId DID2 = getDeviceId(2);
126
127 private static final PortNumber P1 = PortNumber.portNumber(1);
128
129 private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
130 private static final ConnectPoint CP2 = new ConnectPoint(DID2, P1);
131
132 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
133
134 private static final String PEER1_IP = "10.0.1.201";
135 private static final String PEER2_IP = "10.0.1.101";
136 private static final String PEER1_IP6 = "1000::1";
137 private static final String PEER2_IP6 = "1000::100";
138
139 private final byte[] zeroMacAddress = MacAddress.ZERO.toBytes();
140
141 // The first three devices in the topology have interfaces configured
142 // with VLANs and IPs
143 private final List<ConnectPoint> configIpCPoints = new ArrayList<>();
144
145 // Other three devices in the topology (from 4 to 6) have interfaces
146 // configured only with VLANs
147 private final List<ConnectPoint> configVlanCPoints = new ArrayList<>();
148
149 // Remaining devices in the network (id > 6) don't have any interface
150 // configured.
151 private final List<ConnectPoint> noConfigCPoints = new ArrayList<>();
152
153 private SdxL3ArpHandler proxyArp;
154
155 private BgpConfig bgpConfig;
156 private TestPacketService packetService;
157 private DeviceService deviceService;
158 private EdgePortService edgePortService;
159 private LinkService linkService;
160 private HostService hostService;
161 private InterfaceService interfaceService;
162
163 @Before
164 public void setUp() throws Exception {
165
166 bgpConfig = createMock(BgpConfig.class);
167 packetService = new TestPacketService();
168 hostService = createMock(HostService.class);
169 edgePortService = createMock(EdgePortService.class);
170 interfaceService = createMock(InterfaceService.class);
171
172 // Create the topology
173 createTopology();
174
175 setupNoConfigCPoints();
176 setupconfigIpCPoints();
177 setupconfigVlanCPoints();
178
179 proxyArp = new SdxL3ArpHandler(bgpConfig,
180 edgePortService,
181 hostService,
182 packetService,
183 interfaceService);
184 }
185
186 /**
187 * Creates a fake topology to feed into the ARP module.
188 * <p>
189 * The default topology is a unidirectional ring topology. Each switch has
190 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
191 * is free (edge port).
192 * The first half of the switches have IP addresses configured on their
193 * free ports (port 1). The second half of the switches have no IP
194 * addresses configured.
195 */
196 private void createTopology() {
197 deviceService = createMock(DeviceService.class);
198 linkService = createMock(LinkService.class);
199
200 deviceService.addListener(anyObject(DeviceListener.class));
201 linkService.addListener(anyObject(LinkListener.class));
202
203 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
204 createLinks(NUM_DEVICES);
205 addIntfConfig();
206 addEmptyBgpConfig();
207 popluateEdgePortService();
208 }
209
210 /**
211 * Creates the devices for the fake topology.
212 */
213 private void createDevices(int numDevices, int numPorts) {
214 List<Device> devices = new ArrayList<>();
215
216 for (int i = 1; i <= numDevices; i++) {
217 DeviceId devId = getDeviceId(i);
218 Device device = createMock(Device.class);
219 expect(device.id()).andReturn(devId).anyTimes();
220 replay(device);
221
222 devices.add(device);
223
224 List<Port> ports = new ArrayList<>();
225 for (int j = 1; j <= numPorts; j++) {
226 Port port = createMock(Port.class);
227 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
228 replay(port);
229 ports.add(port);
230 }
231
232 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
233 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
234 }
235
236 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
237 replay(deviceService);
238 }
239
240 /**
241 * Creates the links for the fake topology.
242 * NB: Only unidirectional links are created, as for this purpose all we
243 * need is to occupy the ports with some link.
244 */
245 private void createLinks(int numDevices) {
246 List<Link> links = new ArrayList<>();
247
248 for (int i = 1; i <= numDevices; i++) {
249 ConnectPoint src = new ConnectPoint(
250 getDeviceId(i),
251 PortNumber.portNumber(2));
252 ConnectPoint dst = new ConnectPoint(
253 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
254 PortNumber.portNumber(3));
255
256 Link link = createMock(Link.class);
257 expect(link.src()).andReturn(src).anyTimes();
258 expect(link.dst()).andReturn(dst).anyTimes();
259 replay(link);
260
261 links.add(link);
262 }
263
264 expect(linkService.getLinks()).andReturn(links).anyTimes();
265 replay(linkService);
266 }
267
268 /**
269 * On the first three devices two config interfaces are binded on port 1.
270 * The first one with VLAN1, the second one with VLAN equals to none.
271 * Both interfaces have an IP.
272 * On devices 4, 5 and 6 it's binded a config interface on port 1.
273 * The interface is configured with VLAN 1 and no IP.
274 */
275 private void addIntfConfig() {
276 Set<Interface> interfaces = Sets.newHashSet();
277
278 Set<Interface> vlanOneSet = new HashSet<>();
279
280 for (int i = 1; i <= LAST_CONF_DEVICE_INTF_VLAN_IP; i++) {
281 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
282
283 // Interface addresses for IPv4
284 Ip4Prefix prefix1 = Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
285 Ip4Address addr1 = Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
286 Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
287 Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
288 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
289 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
290
291 // Interface addresses for IPv6
292 Ip6Prefix prefix3 = Ip6Prefix.valueOf((2 * i - 1) + "000::0/64");
293 Ip6Address addr3 = Ip6Address.valueOf((2 * i - 1) + "000::1");
294 Ip6Prefix prefix4 = Ip6Prefix.valueOf((2 * i) + "000::0/64");
295 Ip6Address addr4 = Ip6Address.valueOf((2 * i) + "000::2");
296 InterfaceIpAddress ia3 = new InterfaceIpAddress(addr3, prefix3);
297 InterfaceIpAddress ia4 = new InterfaceIpAddress(addr4, prefix4);
298
299 // Setting up interfaces
300 Interface intf1 = new Interface(cp, Sets.newHashSet(ia1, ia3),
301 MacAddress.valueOf(2 * i - 1),
302 VlanId.vlanId((short) 1));
303 Interface intf2 = new Interface(cp, Sets.newHashSet(ia2, ia4),
304 MacAddress.valueOf(2 * i),
305 VlanId.NONE);
306
307 interfaces.add(intf1);
308 interfaces.add(intf2);
309
310 vlanOneSet.add(intf1);
311
312 expect(interfaceService.getInterfacesByPort(cp))
313 .andReturn(Sets.newHashSet(intf1, intf2)).anyTimes();
314 }
315 for (int i = LAST_CONF_DEVICE_INTF_VLAN_IP + 1; i <= LAST_CONF_DEVICE_INTF_VLAN; i++) {
316 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
317 Interface intf1 = new Interface(cp, null,
318 MacAddress.NONE,
319 VlanId.vlanId((short) 1));
320
321 interfaces.add(intf1);
322 vlanOneSet.add(intf1);
323
324 expect(interfaceService.getInterfacesByPort(cp))
325 .andReturn(Sets.newHashSet(intf1)).anyTimes();
326 }
327 expect(interfaceService.getInterfacesByVlan(VLAN1))
328 .andReturn(vlanOneSet).anyTimes();
329 expect(interfaceService.getInterfacesByVlan(VLAN10))
330 .andReturn(Collections.emptySet()).anyTimes();
331 expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
332
333 for (int i = LAST_CONF_DEVICE_INTF_VLAN + 1; i <= NUM_DEVICES; i++) {
334 ConnectPoint cp = new ConnectPoint(getDeviceId(i),
335 P1);
336 expect(interfaceService.getInterfacesByPort(cp))
337 .andReturn(Collections.emptySet()).anyTimes();
338 }
339 }
340
341 /**
342 * Adds an empty BGP configuration for the purposes of basic ARP handling.
343 */
344 private void addEmptyBgpConfig() {
345 Set<BgpConfig.BgpSpeakerConfig> speakers = Sets.newHashSet();
346
347 expect(bgpConfig.bgpSpeakers()).andReturn(speakers).anyTimes();
348 replay(bgpConfig);
349 }
350
351 /**
352 * Populates edge ports in the EdgePortService to return all port 1
353 * as edge ports.
354 */
355 private void popluateEdgePortService() {
356 Set<ConnectPoint> edgeConnectPoints = new HashSet<>();
357
358 for (int i = 1; i <= NUM_DEVICES; i++) {
359 for (int j = 1; j <= NUM_PORTS_PER_DEVICE; j++) {
360 ConnectPoint edgeConnectPoint = new ConnectPoint(
361 getDeviceId(i),
362 PortNumber.portNumber(1));
363 ConnectPoint noEdgeConnectPointOne = new ConnectPoint(
364 getDeviceId(i),
365 PortNumber.portNumber(2));
366 ConnectPoint noEdgeConnectPointTwo = new ConnectPoint(
367 getDeviceId(i),
368 PortNumber.portNumber(3));
369
370 edgeConnectPoints.add(edgeConnectPoint);
371
372 expect(edgePortService.isEdgePoint(edgeConnectPoint))
373 .andReturn(true).anyTimes();
374 expect(edgePortService.isEdgePoint(noEdgeConnectPointOne))
375 .andReturn(false).anyTimes();
376 expect(edgePortService.isEdgePoint(noEdgeConnectPointTwo))
377 .andReturn(false).anyTimes();
378 }
379 }
380 expect(edgePortService.getEdgePoints())
381 .andReturn(edgeConnectPoints).anyTimes();
382
383 replay(edgePortService);
384 }
385
386 /**
387 * Creates a list of connect points used to verify floodling on ports
388 * with no interfaces configured (all ports without interface config).
389 */
390 private void setupNoConfigCPoints() {
391 for (int i = NUM_DEVICES / 2 + 2; i <= NUM_DEVICES; i++) {
392 ConnectPoint connectPoint = new ConnectPoint(
393 getDeviceId(i),
394 PortNumber.portNumber(1));
395 noConfigCPoints.add(connectPoint);
396 }
397 }
398
399 /**
400 * Creates a list of connect points used to verify floodling on ports
401 * with interfaces configured (both VLAN and IP).
402 */
403 private void setupconfigIpCPoints() {
404 for (int i = 1; i <= 3; i++) {
405 ConnectPoint connectPoint = new ConnectPoint(
406 getDeviceId(i),
407 PortNumber.portNumber(1));
408 configIpCPoints.add(connectPoint);
409 }
410 }
411
412 /**
413 * Creates a list of connect points used to verify floodling on ports
414 * with interfaces configured (both VLAN and IP).
415 */
416 private void setupconfigVlanCPoints() {
417 for (int i = LAST_CONF_DEVICE_INTF_VLAN_IP + 1; i <= LAST_CONF_DEVICE_INTF_VLAN; i++) {
418 ConnectPoint connectPoint = new ConnectPoint(
419 getDeviceId(i),
420 PortNumber.portNumber(1));
421 configVlanCPoints.add(connectPoint);
422 }
423 }
424
425 /**
426 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
427 * destination host is known.
428 * Two host using the same VLAN are registered on the host service on devices 5 and 6.
429 * Host on port 6 asks for the MAC of the device on port 5.
430 * Since the destination mac address is known, the request is not flooded to anywhere
431 * and ONOS directly builds an ARP reply, sended back to the requester on device 6.
432 * It's verified that a proper ARP reply is received on port 1 of device 6.
433 */
434 @Test
435 public void testReplyKnown() {
436 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(NUM_DEVICES),
437 Collections.singleton(IP1));
438
439 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(NUM_DEVICES - 1),
440 Collections.singleton(IP2));
441
442 expect(hostService.getHostsByIp(IP2))
443 .andReturn(Collections.singleton(replyer));
444 expect(hostService.getHost(HID1)).andReturn(requestor);
445
446 replay(hostService);
447 replay(interfaceService);
448
449 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
450
451 InboundPacket pkt =
452 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
453 arpRequest,
454 ByteBuffer.wrap(arpRequest.serialize()));
455 proxyArp.processPacketIn(pkt);
456
457 assertEquals(1, packetService.packets.size());
458 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, MAC2, MAC1, IP2, IP1);
459 verifyPacketOut(arpReply, getLocation(NUM_DEVICES), packetService.packets.get(0));
460 }
461
462 /**
463 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
464 * destination host is known.
465 * Verifies the correct NDP reply is sent out the correct port.
466 */
467 @Test
468 public void testReplyKnownIpv6() {
469 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(4),
470 Collections.singleton(IP3));
471
472 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5),
473 Collections.singleton(IP4));
474
475 expect(hostService.getHostsByIp(IP3))
476 .andReturn(Collections.singleton(replyer));
477 expect(hostService.getHost(HID4)).andReturn(requestor);
478
479 replay(hostService);
480 replay(interfaceService);
481
482 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
483 MAC4, SOLICITED_MAC3,
484 IP4, IP3);
485
486 InboundPacket pkt =
487 new DefaultInboundPacket(new ConnectPoint(getDeviceId(5), P1),
488 ndpRequest,
489 ByteBuffer.wrap(ndpRequest.serialize()));
490 proxyArp.processPacketIn(pkt);
491
492 assertEquals(1, packetService.packets.size());
493 Ethernet ndpReply = buildNdp(ICMP6.NEIGHBOR_ADVERTISEMENT,
494 MAC3, MAC4, IP3, IP4);
495 verifyPacketOut(ndpReply, getLocation(5), packetService.packets.get(0));
496 }
497
498 /**
499 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
500 * destination host is not known.
501 * Only a requestor is present (on device 6, port 1). The device has a VLAN configured
502 * which is not configured anywhere in the system.
503 * Since the destination is not known, and since the ARP request can't be sent out of
504 * interfaces configured, the ARP request is flooded out of ports 4 and 5.
505 * Verifies the ARP request is flooded out the correct edge ports.
506 */
507 @Test
508 public void testReplyUnknown() {
509 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
510 Collections.singleton(IP1));
511
512 expect(hostService.getHostsByIp(IP2))
513 .andReturn(Collections.emptySet());
514 expect(interfaceService.getInterfacesByIp(IP1))
515 .andReturn(Collections.emptySet());
516 expect(hostService.getHost(HID10)).andReturn(requestor);
517
518 replay(hostService);
519 replay(interfaceService);
520
521 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
522
523 InboundPacket pkt =
524 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
525 arpRequest,
526 ByteBuffer.wrap(arpRequest.serialize()));
527 proxyArp.processPacketIn(pkt);
528
529 verifyFlood(arpRequest, noConfigCPoints);
530 }
531
532 /**
533 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
534 * destination host is not known.
535 * Verifies the NDP request is flooded out the correct edge ports.
536 */
537 @Test
538 public void testReplyUnknownIpv6() {
539 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
540 Collections.singleton(IP4));
541
542 expect(hostService.getHostsByIp(IP3))
543 .andReturn(Collections.emptySet());
544 expect(interfaceService.getInterfacesByIp(IP4))
545 .andReturn(Collections.emptySet());
546 expect(hostService.getHost(HID4)).andReturn(requestor);
547
548 replay(hostService);
549 replay(interfaceService);
550
551 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
552 MAC4, SOLICITED_MAC3,
553 IP4, IP3);
554
555 InboundPacket pkt =
556 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
557 ndpRequest,
558 ByteBuffer.wrap(ndpRequest.serialize()));
559 proxyArp.processPacketIn(pkt);
560
561 verifyFlood(ndpRequest, noConfigCPoints);
562 }
563
564 /**
565 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
566 * destination host is known for that IP address, but is not on the same
567 * VLAN as the source host.
568 * An host is connected on device 6, port 1 where no interfaces are defined. It sends
569 * ARP requests from VLAN10, not configured anywhere in the network. Another host with
570 * the IP address requested lives on device 5, port 1 in the network. Anyway, since the
571 * host uses another VLAN it's not found and the ARP packet is flooded out of port
572 * 4 and 5.
573 *
574 * Verifies the ARP request is flooded out the correct edge ports.
575 */
576 @Test
577 public void testReplyDifferentVlan() {
578 Host requestor = new DefaultHost(PID, HID10, MAC10, VLAN10, getLocation(NUM_DEVICES),
579 Collections.singleton(IP1));
580
581 Host replyer = new DefaultHost(PID, HID2, MAC2, VLAN2, getLocation(NUM_DEVICES - 1),
582 Collections.singleton(IP2));
583
584 expect(hostService.getHostsByIp(IP2))
585 .andReturn(Collections.singleton(replyer));
586 expect(interfaceService.getInterfacesByIp(IP1))
587 .andReturn(Collections.emptySet());
588 expect(hostService.getHost(HID10)).andReturn(requestor);
589
590 replay(hostService);
591 replay(interfaceService);
592
593 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN10, MAC10, null, IP1, IP2);
594
595 InboundPacket pkt =
596 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
597 arpRequest,
598 ByteBuffer.wrap(arpRequest.serialize()));
599 proxyArp.processPacketIn(pkt);
600
601 verifyFlood(arpRequest, noConfigCPoints);
602 }
603
604 /**
605 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} 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 InboundPacket pkt =
631 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
632 arpRequest,
633 ByteBuffer.wrap(arpRequest.serialize()));
634 proxyArp.processPacketIn(pkt);
635
636 verifyFlood(arpRequest, noConfigCPoints);
637 }
638
639 /**
640 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
641 * a vlan packet comes in from a port without interfaces configured. The destination
642 * host is not known for that IP address and there are some interfaces configured on
643 * the same vlan.
644 * It's expected to see the ARP request going out through ports with no interfaces
645 * configured, devices 4 and 5, port 1.
646 *
647 * Verifies the ARP request is flooded out the correct edge ports.
648 */
649 @Test
650 public void testConfiguredVlanOnInterfaces() {
651 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(6),
652 Collections.singleton(IP1));
653
654 expect(hostService.getHostsByIp(IP2))
655 .andReturn(Collections.emptySet());
656 expect(interfaceService.getInterfacesByIp(IP1))
657 .andReturn(Collections.emptySet());
658 expect(hostService.getHost(HID1)).andReturn(requestor);
659
660 replay(hostService);
661 replay(interfaceService);
662
663 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, IP1, IP2);
664
665 InboundPacket pkt =
666 new DefaultInboundPacket(new ConnectPoint(getDeviceId(6), P1),
667 arpRequest,
668 ByteBuffer.wrap(arpRequest.serialize()));
669 proxyArp.processPacketIn(pkt);
670
671 verifyFlood(arpRequest, configVlanCPoints);
672 }
673
674 /**
675 * Tests {@link SdxL3ArpHandler#processPacketIn(InboundPacket)} in the case where the
676 * destination host is known for that IP address, but is not on the same
677 * VLAN as the source host.
678 * Verifies the NDP request is flooded out the correct edge ports.
679 */
680 @Test
681 public void testReplyDifferentVlanIpv6() {
682 Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(NUM_DEVICES),
683 Collections.singleton(IP4));
684
685 Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN2, getLocation(NUM_DEVICES - 1),
686 Collections.singleton(IP3));
687
688 expect(hostService.getHostsByIp(IP3))
689 .andReturn(Collections.singleton(replyer));
690 expect(interfaceService.getInterfacesByIp(IP4))
691 .andReturn(Collections.emptySet());
692 expect(hostService.getHost(HID4)).andReturn(requestor);
693
694 replay(hostService);
695 replay(interfaceService);
696
697 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
698 MAC4, SOLICITED_MAC3,
699 IP4, IP3);
700
701 InboundPacket pkt =
702 new DefaultInboundPacket(new ConnectPoint(getDeviceId(NUM_DEVICES), P1),
703 ndpRequest,
704 ByteBuffer.wrap(ndpRequest.serialize()));
705
706 proxyArp.processPacketIn(pkt);
707
708 verifyFlood(ndpRequest, noConfigCPoints);
709 }
710
711 /**
712 * Test ARP request from external network to an internal host.
713 */
714 @Test
715 public void testReplyToRequestForUs() {
716 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
717 Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
718 Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
719 MacAddress firstMac = MacAddress.valueOf(1L);
720 MacAddress secondMac = MacAddress.valueOf(2L);
721
722 Host requestor = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
723 Collections.singleton(theirIp));
724
725 expect(hostService.getHost(HID1)).andReturn(requestor);
726 replay(hostService);
727 replay(interfaceService);
728
729 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourFirstIp);
730
731 InboundPacket pkt =
732 new DefaultInboundPacket(CP1, arpRequest,
733 ByteBuffer.wrap(arpRequest.serialize()));
734
735 proxyArp.processPacketIn(pkt);
736
737 assertEquals(1, packetService.packets.size());
738 Ethernet arpReply = buildArp(ARP.OP_REPLY, VLAN1, firstMac, MAC1, ourFirstIp, theirIp);
739 verifyPacketOut(arpReply, CP1, packetService.packets.get(0));
740
741 // Test a request for the second address on that port
742 packetService.packets.clear();
743 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, ourSecondIp);
744
745 pkt = new DefaultInboundPacket(CP1, arpRequest,
746 ByteBuffer.wrap(arpRequest.serialize()));
747 proxyArp.processPacketIn(pkt);
748
749 assertEquals(1, packetService.packets.size());
750 arpReply = buildArp(ARP.OP_REPLY, VLAN1, secondMac, MAC1, ourSecondIp, theirIp);
751 verifyPacketOut(arpReply, CP1, packetService.packets.get(0));
752 }
753
754 /**
755 * Test NDP request from external network to an internal host.
756 */
757 @Test
758 public void testReplyToRequestForUsIpv6() {
759 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
760 Ip6Address ourFirstIp = Ip6Address.valueOf("1000::1");
761 Ip6Address ourSecondIp = Ip6Address.valueOf("2000::2");
762 MacAddress firstMac = MacAddress.valueOf(1L);
763 MacAddress secondMac = MacAddress.valueOf(2L);
764
765 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
766 Collections.singleton(theirIp));
767
768 expect(hostService.getHost(HID2)).andReturn(requestor);
769 expect(hostService.getHostsByIp(ourFirstIp))
770 .andReturn(Collections.singleton(requestor));
771 replay(hostService);
772 replay(interfaceService);
773
774 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
775 MAC2,
776 MacAddress.valueOf("33:33:ff:00:00:01"),
777 theirIp,
778 ourFirstIp);
779
780 InboundPacket pkt =
781 new DefaultInboundPacket(CP1, ndpRequest,
782 ByteBuffer.wrap(ndpRequest.serialize()));
783 proxyArp.processPacketIn(pkt);
784
785 assertEquals(1, packetService.packets.size());
786
787 Ethernet ndpReply = buildNdp(ICMP6.NEIGHBOR_ADVERTISEMENT,
788 firstMac,
789 MAC2,
790 ourFirstIp,
791 theirIp);
792 verifyPacketOut(ndpReply, CP1, packetService.packets.get(0));
793
794 // Test a request for the second address on that port
795 packetService.packets.clear();
796 ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
797 MAC2,
798 MacAddress.valueOf("33:33:ff:00:00:01"),
799 theirIp,
800 ourSecondIp);
801
802 pkt = new DefaultInboundPacket(CP1, ndpRequest,
803 ByteBuffer.wrap(ndpReply.serialize()));
804
805 proxyArp.processPacketIn(pkt);
806
807 assertEquals(1, packetService.packets.size());
808
809 ndpReply = buildNdp(ICMP6.NEIGHBOR_ADVERTISEMENT,
810 secondMac,
811 MAC2,
812 ourSecondIp,
813 theirIp);
814 verifyPacketOut(ndpReply, CP1, packetService.packets.get(0));
815 }
816
817 /**
818 * Request for a valid external IPv4 address but coming in the wrong port.
819 */
820 @Test
821 public void testReplyExternalPortBadRequest() {
822 replay(hostService); // no further host service expectations
823 replay(interfaceService);
824
825 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
826
827 // Request for a valid external IP address but coming in the wrong port
828 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp,
829 Ip4Address.valueOf("10.0.3.1"));
830 InboundPacket pkt =
831 new DefaultInboundPacket(CP1, arpRequest,
832 ByteBuffer.wrap(arpRequest.serialize()));
833 proxyArp.processPacketIn(pkt);
834
835 assertEquals(0, packetService.packets.size());
836
837 // Request for a valid internal IP address but coming in an external port
838 packetService.packets.clear();
839 arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, theirIp, IP1);
840 pkt = new DefaultInboundPacket(CP1, arpRequest,
841 ByteBuffer.wrap(arpRequest.serialize()));
842 proxyArp.processPacketIn(pkt);
843 assertEquals(0, packetService.packets.size());
844 }
845
846 /**
847 * Request for a valid external IPv6 address but coming in the wrong port.
848 */
849 @Test
850 public void testReplyExternalPortBadRequestIpv6() {
851 replay(hostService); // no further host service expectations
852 replay(interfaceService);
853
854 Ip6Address theirIp = Ip6Address.valueOf("1000::ffff");
855
856 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
857 MAC1,
858 MacAddress.valueOf("33:33:ff:00:00:01"),
859 theirIp,
860 Ip6Address.valueOf("3000::1"));
861
862 InboundPacket pkt =
863 new DefaultInboundPacket(CP1, ndpRequest,
864 ByteBuffer.wrap(ndpRequest.serialize()));
865
866 proxyArp.processPacketIn(pkt);
867
868 assertEquals(0, packetService.packets.size());
869
870 // Request for a valid internal IP address but coming in an external port
871 packetService.packets.clear();
872 ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
873 MAC1,
874 MacAddress.valueOf("33:33:ff:00:00:01"),
875 theirIp,
876 IP3);
877
878 pkt = new DefaultInboundPacket(CP1, ndpRequest,
879 ByteBuffer.wrap(ndpRequest.serialize()));
880
881 proxyArp.processPacketIn(pkt);
882
883 assertEquals(0, packetService.packets.size());
884 }
885
886 /**
887 * Test ARP request from internal network to an external host.
888 */
889 @Test
890 public void testReplyToRequestFromUs() {
891 Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
892 MacAddress ourMac = MacAddress.valueOf(1L);
893 Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
894
895 expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
896 expect(interfaceService.getInterfacesByIp(ourIp)).andReturn(
897 Collections.singleton(new Interface(getLocation(1),
898 Collections.singleton(new InterfaceIpAddress(ourIp, IpPrefix.valueOf("10.0.1.1/24"))),
899 ourMac, VLAN1)));
900 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
901 replay(hostService);
902 replay(interfaceService);
903
904 // This is a request from something inside our network (like a BGP
905 // daemon) to an external host.
906 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, ourMac, null, ourIp, theirIp);
907 //Ensure the packet is allowed through (it is not to an internal port)
908
909 InboundPacket pkt =
910 new DefaultInboundPacket(new ConnectPoint(getDeviceId(5), P1),
911 arpRequest,
912 ByteBuffer.wrap(arpRequest.serialize()));
913 proxyArp.processPacketIn(pkt);
914
915 assertEquals(1, packetService.packets.size());
916
917 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
918
919 // The same request from a random external port should fail
920 packetService.packets.clear();
921 pkt = new DefaultInboundPacket(new ConnectPoint(getDeviceId(2), P1),
922 arpRequest,
923 ByteBuffer.wrap(arpRequest.serialize()));
924
925 proxyArp.processPacketIn(pkt);
926
927 assertEquals(0, packetService.packets.size());
928 }
929
930 /**
931 * Test NDP request from internal network to an external host.
932 */
933 @Test
934 public void testReplyToRequestFromUsIpv6() {
935 Ip6Address ourIp = Ip6Address.valueOf("1000::1");
936 MacAddress ourMac = MacAddress.valueOf(1L);
937 Ip6Address theirIp = Ip6Address.valueOf("1000::100");
938
939 expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet());
940 expect(interfaceService.getInterfacesByIp(ourIp))
941 .andReturn(Collections.singleton(new Interface(getLocation(1),
942 Collections.singleton(new InterfaceIpAddress(
943 ourIp,
944 IpPrefix.valueOf("1000::1/64"))),
945 ourMac,
946 VLAN1)));
947 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
948 replay(hostService);
949 replay(interfaceService);
950
951 // This is a request from something inside our network (like a BGP
952 // daemon) to an external host.
953 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
954 ourMac,
955 MacAddress.valueOf("33:33:ff:00:00:01"),
956 ourIp,
957 theirIp);
958
959 InboundPacket pkt =
960 new DefaultInboundPacket(new ConnectPoint(getDeviceId(5), P1),
961 ndpRequest,
962 ByteBuffer.wrap(ndpRequest.serialize()));
963 proxyArp.processPacketIn(pkt);
964
965 assertEquals(1, packetService.packets.size());
966
967 verifyPacketOut(ndpRequest, getLocation(1), packetService.packets.get(0));
968
969 // The same request from a random external port should fail
970 packetService.packets.clear();
971
972 pkt = new DefaultInboundPacket(new ConnectPoint(getDeviceId(2), P1),
973 ndpRequest,
974 ByteBuffer.wrap(ndpRequest.serialize()));
975
976 proxyArp.processPacketIn(pkt);
977
978 assertEquals(0, packetService.packets.size());
979 }
980
981
982 /**
983 * Request for a valid IPv4 address for BGP peer and coming from a
984 * BGP peer.
985 */
986 @Test
987 public void testReplyExternalPortForPeer() {
988 Ip4Address peer1 = Ip4Address.valueOf(PEER1_IP);
989 MacAddress ourMac = MacAddress.valueOf(1L);
990 Ip4Address peer2 = Ip4Address.valueOf(PEER2_IP);
991
992 expect(hostService.getHostsByIp(peer2)).andReturn(Collections.emptySet());
993 expect(interfaceService.getInterfacesByIp(peer1)).andReturn(
994 Collections.singleton(
995 new Interface(getLocation(1),
996 Collections.singleton(
997 new InterfaceIpAddress(
998 peer1,
999 IpPrefix.valueOf("10.0.1.1/24"))),
1000 ourMac, VLAN1)));
1001 expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null);
1002 replay(hostService);
1003 replay(interfaceService);
1004
1005 addPeersToBgpConfig();
1006
1007 // Request for a valid external IP address belonging to BGP peer
1008 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, VLAN1, MAC1, null, peer1,
1009 peer2);
1010 InboundPacket pkt =
1011 new DefaultInboundPacket(CP1, arpRequest,
1012 ByteBuffer.wrap(arpRequest.serialize()));
1013 proxyArp.processPacketIn(pkt);
1014
1015 assertEquals(1, packetService.packets.size());
1016 }
1017
1018 /**
1019 * Request for a valid IPv6 address for BGP peer and coming from a
1020 * BGP peer.
1021 */
1022 @Test
1023 public void testReplyExternalPortForPeerIpv6() {
1024 Ip6Address peer1 = Ip6Address.valueOf(PEER1_IP6);
1025 MacAddress peer1Mac = MacAddress.valueOf(1L);
1026 Ip6Address peer2 = Ip6Address.valueOf(PEER2_IP6);
1027
1028 expect(hostService.getHostsByIp(peer2)).andReturn(Collections.emptySet());
1029 expect(interfaceService.getInterfacesByIp(peer1))
1030 .andReturn(Collections.singleton(new Interface(getLocation(1),
1031 Collections.singleton(new InterfaceIpAddress(
1032 peer1,
1033 IpPrefix.valueOf("1000::1/64"))),
1034 peer1Mac,
1035 VLAN1)));
1036 expect(hostService.getHost(HostId.hostId(peer1Mac, VLAN1))).andReturn(null);
1037 replay(hostService);
1038 replay(interfaceService);
1039
1040 addPeersToBgpConfig();
1041
1042 Ethernet ndpRequest = buildNdp(ICMP6.NEIGHBOR_SOLICITATION,
1043 MAC1,
1044 MacAddress.valueOf("33:33:ff:00:00:01"),
1045 peer1,
1046 peer2);
1047
1048 InboundPacket pkt =
1049 new DefaultInboundPacket(CP1, ndpRequest,
1050 ByteBuffer.wrap(ndpRequest.serialize()));
1051
1052 proxyArp.processPacketIn(pkt);
1053
1054 assertEquals(1, packetService.packets.size());
1055 }
1056
1057 private void addPeersToBgpConfig() {
1058 reset(bgpConfig);
1059
1060 Set<BgpConfig.BgpSpeakerConfig> speakers = new HashSet<>();
1061
1062 Optional<String> speakerName = Optional.empty();
1063 ConnectPoint connectPoint = CP2;
1064 Set<IpAddress> connectedPeers =
1065 new HashSet<>(Arrays.asList(IpAddress.valueOf(PEER1_IP),
1066 IpAddress.valueOf(PEER2_IP),
1067 IpAddress.valueOf(PEER1_IP6),
1068 IpAddress.valueOf(PEER2_IP6)));
1069
1070 speakers.add(new BgpConfig.BgpSpeakerConfig(speakerName,
1071 connectPoint,
1072 connectedPeers));
1073
1074 expect(bgpConfig.bgpSpeakers()).andReturn(speakers).anyTimes();
1075 replay(bgpConfig);
1076 }
1077
1078 /**
1079 * Verifies that the given packet was flooded out all available edge ports,
1080 * except for the input port.
1081 *
1082 * @param packet the packet that was expected to be flooded
1083 * @param connectPoints the connectPoints where the outpacket should be
1084 * observed
1085 */
1086 private void verifyFlood(Ethernet packet, List<ConnectPoint> connectPoints) {
1087
1088 // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded.
1089 assertEquals(connectPoints.size() - 1, packetService.packets.size());
1090
1091 Collections.sort(packetService.packets,
1092 (o1, o2) -> o1.sendThrough().uri().compareTo(o2.sendThrough().uri()));
1093
1094 for (int i = 0; i < connectPoints.size() - 1; i++) {
1095 OutboundPacket outboundPacket = packetService.packets.get(i);
1096 verifyPacketOut(packet, connectPoints.get(i), outboundPacket);
1097 }
1098 }
1099
1100 /**
1101 * Verifies the given packet was sent out the given port.
1102 *
1103 * @param expected the packet that was expected to be sent
1104 * @param outPort the port the packet was expected to be sent out
1105 * @param actual the actual OutboundPacket to verify
1106 */
1107 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
1108 OutboundPacket actual) {
1109 assertArrayEquals(expected.serialize(), actual.data().array());
1110 assertEquals(1, actual.treatment().immediate().size());
1111 assertEquals(outPort.deviceId(), actual.sendThrough());
1112 Instruction instruction = actual.treatment().immediate().get(0);
1113 assertTrue(instruction instanceof OutputInstruction);
1114 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
1115 }
1116
1117 /**
1118 * Returns the device ID of the ith device.
1119 *
1120 * @param i device to get the ID of
1121 * @return the device ID
1122 */
1123 private static DeviceId getDeviceId(int i) {
1124 return DeviceId.deviceId("" + i);
1125 }
1126
1127 private static HostLocation getLocation(int i) {
1128 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
1129 }
1130
1131 /**
1132 * Builds an ARP packet with the given parameters.
1133 *
1134 * @param opcode opcode of the ARP packet
1135 * @param srcMac source MAC address
1136 * @param dstMac destination MAC address, or null if this is a request
1137 * @param srcIp source IP address
1138 * @param dstIp destination IP address
1139 * @return the ARP packet
1140 */
1141 private Ethernet buildArp(short opcode, VlanId vlanId, MacAddress srcMac,
1142 MacAddress dstMac, Ip4Address srcIp, Ip4Address dstIp) {
1143 Ethernet eth = new Ethernet();
1144
1145 if (dstMac == null) {
1146 eth.setDestinationMACAddress(MacAddress.BROADCAST);
1147 } else {
1148 eth.setDestinationMACAddress(dstMac);
1149 }
1150
1151 eth.setSourceMACAddress(srcMac);
1152 eth.setEtherType(Ethernet.TYPE_ARP);
1153 eth.setVlanID(vlanId.toShort());
1154
1155 ARP arp = new ARP();
1156 arp.setOpCode(opcode);
1157 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1158 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1159
1160 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1161 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1162 arp.setSenderHardwareAddress(srcMac.toBytes());
1163
1164 if (dstMac == null) {
1165 arp.setTargetHardwareAddress(zeroMacAddress);
1166 } else {
1167 arp.setTargetHardwareAddress(dstMac.toBytes());
1168 }
1169
1170 arp.setSenderProtocolAddress(srcIp.toOctets());
1171 arp.setTargetProtocolAddress(dstIp.toOctets());
1172
1173 eth.setPayload(arp);
1174 return eth;
1175 }
1176
1177 /**
1178 * Builds an NDP packet with the given parameters.
1179 *
1180 * @param type NeighborSolicitation or NeighborAdvertisement
1181 * @param srcMac source MAC address
1182 * @param dstMac destination MAC address, or null if this is a request
1183 * @param srcIp source IP address
1184 * @param dstIp destination IP address
1185 * @return the NDP packet
1186 */
1187 private Ethernet buildNdp(byte type, MacAddress srcMac, MacAddress dstMac,
1188 Ip6Address srcIp, Ip6Address dstIp) {
1189 assertThat(type, anyOf(
1190 is(ICMP6.NEIGHBOR_SOLICITATION),
1191 is(ICMP6.NEIGHBOR_ADVERTISEMENT)
1192 ));
1193 assertNotNull(srcMac);
1194 assertNotNull(dstMac);
1195 assertNotNull(srcIp);
1196 assertNotNull(dstIp);
1197
1198 IPacket ndp;
1199 if (type == ICMP6.NEIGHBOR_SOLICITATION) {
1200 ndp = new NeighborSolicitation().setTargetAddress(dstIp.toOctets());
1201 } else {
1202 ndp = new NeighborAdvertisement()
1203 .setSolicitedFlag((byte) 1)
1204 .setOverrideFlag((byte) 1)
1205 .setTargetAddress(srcIp.toOctets())
1206 .addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
1207 srcMac.toBytes());
1208 }
1209
1210 ICMP6 icmp6 = new ICMP6();
1211 icmp6.setIcmpType(type);
1212 icmp6.setIcmpCode((byte) 0);
1213 icmp6.setPayload(ndp);
1214
1215 IPv6 ipv6 = new IPv6();
1216 ipv6.setDestinationAddress(dstIp.toOctets());
1217 ipv6.setSourceAddress(srcIp.toOctets());
1218 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
1219 ipv6.setHopLimit((byte) 255);
1220 ipv6.setPayload(icmp6);
1221
1222 Ethernet eth = new Ethernet();
1223 eth.setDestinationMACAddress(dstMac);
1224 eth.setSourceMACAddress(srcMac);
1225 eth.setEtherType(Ethernet.TYPE_IPV6);
1226 eth.setVlanID(VLAN1.toShort());
1227 eth.setPayload(ipv6);
1228
1229 return eth;
1230 }
1231
1232 /**
1233 * Test PacketService implementation that simply stores OutboundPackets
1234 * passed to {@link #emit(OutboundPacket)} for later verification.
1235 */
1236 class TestPacketService extends PacketServiceAdapter {
1237
1238 List<OutboundPacket> packets = new ArrayList<>();
1239
1240 @Override
1241 public void emit(OutboundPacket packet) {
1242 packets.add(packet);
1243 }
1244 }
1245}
1246