9b2e2ff3f762dd60708ffc974e829e97fa69bc4b
[netvirt.git] / vpnservice / ipv6service / impl / src / test / java / org / opendaylight / netvirt / ipv6service / Ipv6PktHandlerTest.java
1 /*
2  * Copyright (c) 2016 Dell Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.ipv6service;
10
11 import static org.mockito.Matchers.any;
12 import static org.mockito.Mockito.*;
13
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;
39
40 public class Ipv6PktHandlerTest {
41     private PacketProcessingService pktProcessService;
42     private Ipv6PktHandler pktHandler;
43     private IfMgr ifMgrInstance;
44     private long counter;
45     private static final int THREAD_WAIT_TIME = 100;
46
47     @Before
48     public void initTest() {
49         pktProcessService = Mockito.mock(PacketProcessingService.class);
50         ifMgrInstance = Mockito.mock(IfMgr.class);
51
52         pktHandler = new Ipv6PktHandler();
53         pktHandler.setPacketProcessingService(pktProcessService);
54         pktHandler.setIfMgrInstance(ifMgrInstance);
55         counter = pktHandler.getPacketProcessedCounter();
56     }
57
58     @Test
59     public void testOnPacketReceivedWithInvalidPacket() throws Exception {
60         pktHandler.onPacketReceived(null);
61         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
62         byte pktArray[] = {};
63         PacketReceived packet = new PacketReceivedBuilder().setPayload(pktArray).build();
64         pktHandler.onPacketReceived(packet);
65         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
66     }
67
68     @Test
69     public void testOnPacketReceivedWithInvalidParams() throws Exception {
70         //invalid ethtype
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
78                 "FF",                                              // Hop limit
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
81         )).build());
82         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
83
84         //invalid ipv6 header
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
88                 "86 DD",                                           // IPv6
89                 "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
90                 "00 18",                                           // Payload length
91                 "33",                                              // Next header is authentication
92                 "FF",                                              // Hop limit
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
95         )).build());
96         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
97
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
102                 "86 DD",                                           // IPv6
103                 "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
104                 "00 18",                                           // Payload length
105                 "3A",                                              // Next header is ICMPv6
106                 "FF",                                              // Hop limit
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
110                 "00",                                              // Code
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
114         )).build());
115         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
116     }
117
118     @Test
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
124                 "86 DD",                                           // IPv6
125                 "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
126                 "00 18",                                           // Payload length
127                 "3A",                                              // Next header is ICMPv6
128                 "FF",                                              // Hop limit
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
132                 "00",                                              // Code
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
136         )).build());
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));
140
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
147                 "86 DD",                                           // IPv6
148                 "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
149                 "00 18",                                           // Payload length
150                 "3A",                                              // Next header is ICMPv6
151                 "FF",                                              // Hop limit
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
155                 "00",                                              // Code
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
159         )).build());
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));
163     }
164
165     @Test
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);
172
173         IpAddress gwIpAddress = Mockito.mock(IpAddress.class);
174         when(gwIpAddress.getIpv4Address()).thenReturn(null);
175         when(gwIpAddress.getIpv6Address()).thenReturn(new Ipv6Address("2001:db8::1"));
176
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()));
184
185         List<VirtualSubnet> subnetList = new ArrayList<>();
186         subnetList.add(v6Subnet);
187         when(intf.getSubnets()).thenReturn(subnetList);
188
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
195                 "86 DD",                                           // IPv6
196                 "60 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
197                 "00 38",                                           // Payload length
198                 "3A",                                              // Next header is ICMPv6
199                 "FF",                                              // Hop limit
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.
203                 "00",                                              // Code
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
221         );
222
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
229                 "86 DD",                                           // IPv6
230                 "60 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
231                 "00 10",                                           // Payload length
232                 "3A",                                              // Next header is ICMPv6
233                 "FF",                                              // Hop limit
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
237                 "00",                                              // Code
238                 "B4 47",                                           // Checksum (valid)
239                 "00 00 00 00",                                     // ICMPv6 message body
240                 "01",                                              // ICMPv6 Option: Source Link Layer Address
241                 "01",                                              // Length
242                 "FA 16 3E 69 2C F3"                                // Link Layer Address
243         )).setIngress(ncRef).setMatch(matchbuilder.build()).build());
244
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());
251     }
252
253     @Test
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);
260
261         IpAddress gwIpAddress = Mockito.mock(IpAddress.class);
262         when(gwIpAddress.getIpv4Address()).thenReturn(null);
263         when(gwIpAddress.getIpv6Address()).thenReturn(new Ipv6Address("2001:db8:1111::1"));
264
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()));
272
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()));
279
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()));
286
287         List<VirtualSubnet> subnetList = new ArrayList<>();
288         subnetList.add(v6Subnet1);
289         subnetList.add(v6Subnet2);
290         subnetList.add(v6Subnet3);
291         when(intf.getSubnets()).thenReturn(subnetList);
292
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
299                 "86 DD",                                           // IPv6
300                 "60 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
301                 "00 78",                                           // Payload length
302                 "3A",                                              // Next header is ICMPv6
303                 "FF",                                              // Hop limit
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.
307                 "00",                                              // Code
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
341         );
342
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
349                 "86 DD",                                           // IPv6
350                 "60 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
351                 "00 10",                                           // Payload length
352                 "3A",                                              // Next header is ICMPv6
353                 "FF",                                              // Hop limit
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
357                 "00",                                              // Code
358                 "B4 47",                                           // Checksum (valid)
359                 "00 00 00 00",                                     // ICMPv6 message body
360                 "01",                                              // ICMPv6 Option: Source Link Layer Address
361                 "01",                                              // Length
362                 "FA 16 3E 69 2C F3"                                // Link Layer Address
363         )).setIngress(ncRef).setMatch(matchbuilder.build()).build());
364
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());
371     }
372
373     private void waitForPacketProcessing() throws InterruptedException {
374         int timeOut = 1;
375         while (timeOut < 5) {
376             if (pktHandler.getPacketProcessedCounter() > counter) {
377                 break;
378             }
379             Thread.sleep(THREAD_WAIT_TIME);
380             timeOut++;
381         }
382     }
383
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);
391         }
392         byte[] packet = new byte[packetLength];
393         int index = 0;
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);
398                 index++;
399             }
400         }
401         return packet;
402     }
403 }