2 * Copyright (c) 2016 Dell Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netvirt.ipv6service;
11 import static org.mockito.Matchers.any;
12 import static org.mockito.Mockito.*;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.List;
17 import org.junit.Before;
18 import org.junit.Test;
19 import org.mockito.Mockito;
20 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Metadata;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.MetadataBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceivedBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.packet.received.MatchBuilder;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 public class Ipv6PktHandlerTest {
41 private PacketProcessingService pktProcessService;
42 private Ipv6PktHandler pktHandler;
43 private IfMgr ifMgrInstance;
45 private static final int THREAD_WAIT_TIME = 100;
48 public void initTest() {
49 pktProcessService = Mockito.mock(PacketProcessingService.class);
50 ifMgrInstance = Mockito.mock(IfMgr.class);
52 pktHandler = new Ipv6PktHandler();
53 pktHandler.setPacketProcessingService(pktProcessService);
54 pktHandler.setIfMgrInstance(ifMgrInstance);
55 counter = pktHandler.getPacketProcessedCounter();
59 public void testOnPacketReceivedWithInvalidPacket() throws Exception {
60 pktHandler.onPacketReceived(null);
61 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
63 PacketReceived packet = new PacketReceivedBuilder().setPayload(pktArray).build();
64 pktHandler.onPacketReceived(packet);
65 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
69 public void testOnPacketReceivedWithInvalidParams() throws Exception {
71 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
72 "33 33 FF F5 00 00", // Destination MAC
73 "00 01 02 03 04 05", // Source MAC
74 "80 00", // Invalid (fake IPv6)
75 "6E 00 00 00", // Version 6, traffic class E0, no flowlabel
76 "00 18", // Payload length
77 "3A", // Next header is authentication
79 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
80 "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00" // Destination IP
82 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
85 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
86 "33 33 FF F5 00 00", // Destination MAC
87 "00 01 02 03 04 05", // Source MAC
89 "6E 00 00 00", // Version 6, traffic class E0, no flowlabel
90 "00 18", // Payload length
91 "33", // Next header is authentication
93 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
94 "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00" // Destination IP
96 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
98 //invalid icmpv6 header
99 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
100 "33 33 FF F5 00 00", // Destination MAC
101 "00 01 02 03 04 05", // Source MAC
103 "6E 00 00 00", // Version 6, traffic class E0, no flowlabel
104 "00 18", // Payload length
105 "3A", // Next header is ICMPv6
107 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
108 "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00", // Destination IP
109 "85", // ICMPv6 router solicitation
111 "67 3C", // Checksum (valid)
112 "00 00 00 00", // ICMPv6 message body
113 "FE 80 00 00 00 00 00 00 C0 00 54 FF FE F5 00 00" // Target
115 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
119 public void testonPacketReceivedRouterSolicitationWithInvalidPayload() throws Exception {
120 // incorrect checksum in Router Solicitation
121 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
122 "33 33 FF F5 00 00", // Destination MAC
123 "00 01 02 03 04 05", // Source MAC
125 "6E 00 00 00", // Version 6, traffic class E0, no flowlabel
126 "00 18", // Payload length
127 "3A", // Next header is ICMPv6
129 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
130 "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00", // Destination IP
131 "85", // ICMPv6 router solicitation
133 "69 3E", // Checksum (invalid, should be 67 3C)
134 "00 00 00 00", // ICMPv6 message body
135 "FE 80 00 00 00 00 00 00 C0 00 54 FF FE F5 00 00" // Target
137 //wait on this thread until the async job is completed in the packet handler.
138 waitForPacketProcessing();
139 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
141 // Request from an unknown port (i.e., unknown MAC Address)
142 when(ifMgrInstance.obtainV6Interface(any())).thenReturn(null);
143 counter = pktHandler.getPacketProcessedCounter();
144 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
145 "33 33 FF F5 00 00", // Destination MAC
146 "00 01 02 03 04 05", // Source MAC
148 "6E 00 00 00", // Version 6, traffic class E0, no flowlabel
149 "00 18", // Payload length
150 "3A", // Next header is ICMPv6
152 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
153 "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00", // Destination IP
154 "85", // ICMPv6 router solicitation
156 "69 3C", // Checksum (valid)
157 "00 00 00 00", // ICMPv6 message body
158 "FE 80 00 00 00 00 00 00 C0 00 54 FF FE F5 00 00" // Target
160 //wait on this thread until the async job is completed in the packet handler.
161 waitForPacketProcessing();
162 verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
166 public void testonPacketReceivedRouterSolicitationWithSingleSubnet() throws Exception {
167 VirtualPort intf = Mockito.mock(VirtualPort.class);
168 when(intf.getMacAddress()).thenReturn("fa:16:3e:4e:18:0c");
169 when(ifMgrInstance.getInterfaceNameFromTag(anyLong())).thenReturn("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
170 when(ifMgrInstance.obtainV6Interface(any())).thenReturn(intf);
171 when(ifMgrInstance.getRouterV6InterfaceForNetwork(any())).thenReturn(intf);
173 IpAddress gwIpAddress = Mockito.mock(IpAddress.class);
174 when(gwIpAddress.getIpv4Address()).thenReturn(null);
175 when(gwIpAddress.getIpv6Address()).thenReturn(new Ipv6Address("2001:db8::1"));
177 VirtualSubnet v6Subnet = new VirtualSubnet();
178 VirtualRouter vRouter = new VirtualRouter();
179 v6Subnet.setRouter(vRouter);
180 v6Subnet.setGatewayIp(gwIpAddress);
181 v6Subnet.setIpv6AddressMode(Ipv6Constants.IPV6_SLAAC);
182 v6Subnet.setIpv6RAMode(Ipv6Constants.IPV6_SLAAC);
183 v6Subnet.setSubnetCidr(new IpPrefix("2001:db8::/64".toCharArray()));
185 List<VirtualSubnet> subnetList = new ArrayList<>();
186 subnetList.add(v6Subnet);
187 when(intf.getSubnets()).thenReturn(subnetList);
189 InstanceIdentifier<Node> ncId = InstanceIdentifier.builder(Nodes.class)
190 .child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
191 NodeConnectorRef ncRef = new NodeConnectorRef(ncId);
192 byte[] expected_payload = buildPacket(
193 "FA 16 3E 69 2C F3", // Destination MAC
194 "FA 16 3E 4E 18 0C", // Source MAC
196 "60 00 00 00", // Version 6, traffic class E0, no flowlabel
197 "00 38", // Payload length
198 "3A", // Next header is ICMPv6
200 "FE 80 00 00 00 00 00 00 F8 16 3E FF FE 4E 18 0C", // Source IP
201 "FE 80 00 00 00 00 00 00 F8 16 3E FF FE 69 2C F3", // Destination IP
202 "86", // ICMPv6 router advertisement.
204 "F0 71", // Checksum (valid)
205 "40", // Current Hop Limit
206 "00", // ICMPv6 RA Flags
207 "11 94", // Router Lifetime
208 "00 00 00 00", // Reachable time
209 "00 00 00 00", // Retransmission time.
210 "01", // Type: Source Link-Layer Option
211 "01", // Option length
212 "FA 16 3E 4E 18 0C", // Source Link layer address
213 "03", // Type: Prefix Information
214 "04", // Option length
215 "40", // Prefix length
216 "C0", // Prefix flags
217 "00 27 8D 00", // Valid lifetime
218 "00 09 3A 80", // Preferred lifetime
219 "00 00 00 00", // Reserved
220 "20 01 0D B8 00 00 00 00 00 00 00 00 00 00 00 00" // Prefix
223 BigInteger mdata = new BigInteger(String.valueOf(0x1000000));
224 Metadata metadata = new MetadataBuilder().setMetadata(mdata).build();
225 MatchBuilder matchbuilder = new MatchBuilder().setMetadata(metadata);
226 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
227 "33 33 00 00 00 02", // Destination MAC
228 "FA 16 3E 69 2C F3", // Source MAC
230 "60 00 00 00", // Version 6, traffic class E0, no flowlabel
231 "00 10", // Payload length
232 "3A", // Next header is ICMPv6
234 "FE 80 00 00 00 00 00 00 F8 16 3E FF FE 69 2C F3", // Source IP
235 "FF 02 00 00 00 00 00 00 00 00 00 00 00 00 00 02", // Destination IP
236 "85", // ICMPv6 router solicitation
238 "B4 47", // Checksum (valid)
239 "00 00 00 00", // ICMPv6 message body
240 "01", // ICMPv6 Option: Source Link Layer Address
242 "FA 16 3E 69 2C F3" // Link Layer Address
243 )).setIngress(ncRef).setMatch(matchbuilder.build()).build());
245 //wait on this thread until the async job is completed in the packet handler.
246 waitForPacketProcessing();
247 verify(pktProcessService, times(1)).transmitPacket(any(TransmitPacketInput.class));
248 verify(pktProcessService).transmitPacket(new TransmitPacketInputBuilder().setPayload(expected_payload).
249 setNode(new NodeRef(ncId)).
250 setEgress(ncRef).build());
254 public void testonPacketReceivedRouterSolicitationWithMultipleSubnets() throws Exception {
255 VirtualPort intf = Mockito.mock(VirtualPort.class);
256 when(intf.getMacAddress()).thenReturn("50:7B:9D:78:54:F3");
257 when(ifMgrInstance.obtainV6Interface(any())).thenReturn(intf);
258 when(ifMgrInstance.getInterfaceNameFromTag(anyLong())).thenReturn("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
259 when(ifMgrInstance.getRouterV6InterfaceForNetwork(any())).thenReturn(intf);
261 IpAddress gwIpAddress = Mockito.mock(IpAddress.class);
262 when(gwIpAddress.getIpv4Address()).thenReturn(null);
263 when(gwIpAddress.getIpv6Address()).thenReturn(new Ipv6Address("2001:db8:1111::1"));
265 VirtualSubnet v6Subnet1 = new VirtualSubnet();
266 VirtualRouter vRouter = new VirtualRouter();
267 v6Subnet1.setRouter(vRouter);
268 v6Subnet1.setGatewayIp(gwIpAddress);
269 v6Subnet1.setIpv6AddressMode(Ipv6Constants.IPV6_SLAAC);
270 v6Subnet1.setIpv6RAMode(Ipv6Constants.IPV6_SLAAC);
271 v6Subnet1.setSubnetCidr(new IpPrefix("2001:db8:1111::/64".toCharArray()));
273 VirtualSubnet v6Subnet2 = new VirtualSubnet();
274 v6Subnet2.setRouter(vRouter);
275 v6Subnet2.setGatewayIp(gwIpAddress);
276 v6Subnet2.setIpv6AddressMode(Ipv6Constants.IPV6_DHCPV6_STATELESS);
277 v6Subnet2.setIpv6RAMode(Ipv6Constants.IPV6_DHCPV6_STATELESS);
278 v6Subnet2.setSubnetCidr(new IpPrefix("2001:db8:2222::/64".toCharArray()));
280 VirtualSubnet v6Subnet3 = new VirtualSubnet();
281 v6Subnet3.setRouter(vRouter);
282 v6Subnet3.setGatewayIp(gwIpAddress);
283 v6Subnet3.setIpv6AddressMode(Ipv6Constants.IPV6_DHCPV6_STATEFUL);
284 v6Subnet3.setIpv6RAMode(Ipv6Constants.IPV6_DHCPV6_STATEFUL);
285 v6Subnet3.setSubnetCidr(new IpPrefix("2001:db8:3333::/64".toCharArray()));
287 List<VirtualSubnet> subnetList = new ArrayList<>();
288 subnetList.add(v6Subnet1);
289 subnetList.add(v6Subnet2);
290 subnetList.add(v6Subnet3);
291 when(intf.getSubnets()).thenReturn(subnetList);
293 InstanceIdentifier<Node> ncId = InstanceIdentifier.builder(Nodes.class)
294 .child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
295 NodeConnectorRef ncRef = new NodeConnectorRef(ncId);
296 byte[] expected_payload = buildPacket(
297 "FA 16 3E 69 2C F3", // Destination MAC
298 "50 7B 9D 78 54 F3", // Source MAC
300 "60 00 00 00", // Version 6, traffic class E0, no flowlabel
301 "00 78", // Payload length
302 "3A", // Next header is ICMPv6
304 "FE 80 00 00 00 00 00 00 52 7B 9D FF FE 78 54 F3", // Source IP
305 "FE 80 00 00 00 00 00 00 F8 16 3E FF FE 69 2C F3", // Destination IP
306 "86", // ICMPv6 router advertisement.
308 "2E 03", // Checksum (valid)
309 "40", // Current Hop Limit
310 "C0", // ICMPv6 RA Flags
311 "11 94", // Router Lifetime
312 "00 00 00 00", // Reachable time
313 "00 00 00 00", // Retransmission time.
314 "01", // Type: Source Link-Layer Option
315 "01", // Option length
316 "50 7B 9D 78 54 F3", // Source Link layer address
317 "03", // Type: Prefix Information
318 "04", // Option length
319 "40", // Prefix length
320 "C0", // Prefix flags
321 "00 27 8D 00", // Valid lifetime
322 "00 09 3A 80", // Preferred lifetime
323 "00 00 00 00", // Reserved
324 "20 01 0D B8 11 11 00 00 00 00 00 00 00 00 00 00", // Prefix
325 "03", // Type: Prefix Information
326 "04", // Option length
327 "40", // Prefix length
328 "C0", // Prefix flags
329 "00 27 8D 00", // Valid lifetime
330 "00 09 3A 80", // Preferred lifetime
331 "00 00 00 00", // Reserved
332 "20 01 0D B8 22 22 00 00 00 00 00 00 00 00 00 00", // Prefix
333 "03", // Type: Prefix Information
334 "04", // Option length
335 "40", // Prefix length
336 "80", // Prefix flags
337 "00 27 8D 00", // Valid lifetime
338 "00 09 3A 80", // Preferred lifetime
339 "00 00 00 00", // Reserved
340 "20 01 0D B8 33 33 00 00 00 00 00 00 00 00 00 00" // Prefix
343 BigInteger mdata = new BigInteger(String.valueOf(0x1000000));
344 Metadata metadata = new MetadataBuilder().setMetadata(mdata).build();
345 MatchBuilder matchbuilder = new MatchBuilder().setMetadata(metadata);
346 pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
347 "33 33 00 00 00 02", // Destination MAC
348 "FA 16 3E 69 2C F3", // Source MAC
350 "60 00 00 00", // Version 6, traffic class E0, no flowlabel
351 "00 10", // Payload length
352 "3A", // Next header is ICMPv6
354 "FE 80 00 00 00 00 00 00 F8 16 3E FF FE 69 2C F3", // Source IP
355 "FF 02 00 00 00 00 00 00 00 00 00 00 00 00 00 02", // Destination IP
356 "85", // ICMPv6 router solicitation
358 "B4 47", // Checksum (valid)
359 "00 00 00 00", // ICMPv6 message body
360 "01", // ICMPv6 Option: Source Link Layer Address
362 "FA 16 3E 69 2C F3" // Link Layer Address
363 )).setIngress(ncRef).setMatch(matchbuilder.build()).build());
365 //wait on this thread until the async job is completed in the packet handler.
366 waitForPacketProcessing();
367 verify(pktProcessService, times(1)).transmitPacket(any(TransmitPacketInput.class));
368 verify(pktProcessService).transmitPacket(new TransmitPacketInputBuilder().setPayload(expected_payload).
369 setNode(new NodeRef(ncId)).
370 setEgress(ncRef).build());
373 private void waitForPacketProcessing() throws InterruptedException {
375 while (timeOut < 5) {
376 if (pktHandler.getPacketProcessedCounter() > counter) {
379 Thread.sleep(THREAD_WAIT_TIME);
384 private byte[] buildPacket(String... contents) {
385 List<String[]> splitContents = new ArrayList<>();
386 int packetLength = 0;
387 for (String content : contents) {
388 String[] split = content.split(" ");
389 packetLength += split.length;
390 splitContents.add(split);
392 byte[] packet = new byte[packetLength];
394 for (String[] split : splitContents) {
395 for (String component : split) {
396 // We can't use Byte.parseByte() here, it refuses anything > 7F
397 packet[index] = (byte) Integer.parseInt(component, 16);