Action redesign: use the new ActionInfo Genius classes
[netvirt.git] / vpnservice / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / utils / Ipv6ServiceUtils.java
1 /*
2  * Copyright (c) 2016 Red Hat, 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.utils;
10
11 import com.google.common.base.Optional;
12 import com.google.common.net.InetAddresses;
13 import java.math.BigInteger;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.nio.ByteBuffer;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.ExecutionException;
23 import org.apache.commons.lang3.StringUtils;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.mdsalutil.ActionInfo;
28 import org.opendaylight.genius.mdsalutil.FlowEntity;
29 import org.opendaylight.genius.mdsalutil.InstructionInfo;
30 import org.opendaylight.genius.mdsalutil.InstructionType;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.MatchFieldType;
33 import org.opendaylight.genius.mdsalutil.MatchInfo;
34 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
35 import org.opendaylight.genius.mdsalutil.NwConstants;
36 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
37 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
38 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
39 import org.opendaylight.genius.utils.ServiceIndex;
40 import org.opendaylight.netvirt.elan.utils.ElanUtils;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
59 import org.opendaylight.yangtools.yang.binding.DataObject;
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 public class Ipv6ServiceUtils {
65     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
66     private ConcurrentMap<String, InstanceIdentifier<Flow>> icmpv6FlowMap;
67     public static final Ipv6ServiceUtils INSTANCE = new Ipv6ServiceUtils();
68     public static Ipv6Address ALL_NODES_MCAST_ADDR;
69     public static Ipv6Address UNSPECIFIED_ADDR;
70
71     public Ipv6ServiceUtils() {
72         icmpv6FlowMap = new ConcurrentHashMap<>();
73         try {
74             UNSPECIFIED_ADDR = Ipv6Address.getDefaultInstance(
75                     InetAddress.getByName("0:0:0:0:0:0:0:0").getHostAddress());
76             ALL_NODES_MCAST_ADDR = Ipv6Address.getDefaultInstance(InetAddress.getByName("FF02::1").getHostAddress());
77         } catch (UnknownHostException e) {
78             LOG.error("Ipv6ServiceUtils: Failed to instantiate the ipv6 address", e);
79         }
80     }
81
82     public static Ipv6ServiceUtils getInstance() {
83         return INSTANCE;
84     }
85
86     /**
87      * Retrieves the object from the datastore.
88      * @param broker the data broker.
89      * @param datastoreType the data store type.
90      * @param path the wild card path.
91      * @return the required object.
92      */
93     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
94                                                           InstanceIdentifier<T> path) {
95         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
96         Optional<T> result = Optional.absent();
97         try {
98             result = tx.read(datastoreType, path).get();
99         } catch (InterruptedException | ExecutionException e) {
100             throw new RuntimeException(e);
101         } finally {
102             tx.close();
103         }
104         return result;
105     }
106
107     /**
108      * Retrieves the Interface from the datastore.
109      * @param broker the data broker
110      * @param interfaceName the interface name
111      * @return the interface.
112      */
113     public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
114         .Interface getInterface(DataBroker broker, String interfaceName) {
115         Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
116             .Interface> optInterface =
117                 read(broker, LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
118         if (optInterface.isPresent()) {
119             return optInterface.get();
120         }
121         return null;
122     }
123
124     /**
125      * Builds the interface identifier.
126      * @param interfaceName the interface name.
127      * @return the interface identifier.
128      */
129     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
130             .interfaces.Interface> getInterfaceIdentifier(String interfaceName) {
131         return InstanceIdentifier.builder(Interfaces.class)
132                 .child(
133                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
134                                 .Interface.class, new InterfaceKey(interfaceName)).build();
135     }
136
137     public String bytesToHexString(byte[] bytes) {
138         if (bytes == null) {
139             return "null";
140         }
141         StringBuffer buf = new StringBuffer();
142         for (int i = 0; i < bytes.length; i++) {
143             if (i > 0) {
144                 buf.append(":");
145             }
146             short u8byte = (short) (bytes[i] & 0xff);
147             String tmp = Integer.toHexString(u8byte);
148             if (tmp.length() == 1) {
149                 buf.append("0");
150             }
151             buf.append(tmp);
152         }
153         return buf.toString();
154     }
155
156     public byte[] bytesFromHexString(String values) {
157         String target = "";
158         if (values != null) {
159             target = values;
160         }
161         String[] octets = target.split(":");
162
163         byte[] ret = new byte[octets.length];
164         for (int i = 0; i < octets.length; i++) {
165             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
166         }
167         return ret;
168     }
169
170     public int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
171         long checksum = getSummation(ip6Hdr.getSourceIpv6());
172         checksum += getSummation(ip6Hdr.getDestinationIpv6());
173         checksum = normalizeChecksum(checksum);
174
175         checksum += ip6Hdr.getIpv6Length();
176         checksum += ip6Hdr.getNextHeader();
177
178         int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
179         long value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
180         checksum += value;
181         checksum = normalizeChecksum(checksum);
182         icmp6Offset += 2;
183
184         //move to icmp6 payload skipping the checksum field
185         icmp6Offset += 2;
186         int length = packet.length - icmp6Offset;
187         while (length > 1) {
188             value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
189             checksum += value;
190             checksum = normalizeChecksum(checksum);
191             icmp6Offset += 2;
192             length -= 2;
193         }
194
195         if (length > 0) {
196             checksum += packet[icmp6Offset];
197             checksum = normalizeChecksum(checksum);
198         }
199
200         int finalChecksum = (int)(~checksum & 0xffff);
201         return finalChecksum;
202     }
203
204     public boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
205         int checksum = calcIcmpv6Checksum(packet, ip6Hdr);
206
207         if (checksum == recvChecksum) {
208             return true;
209         }
210         return false;
211     }
212
213     private long getSummation(Ipv6Address addr) {
214         byte[] baddr = null;
215         try {
216             baddr = InetAddress.getByName(addr.getValue()).getAddress();
217         } catch (UnknownHostException e) {
218             LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
219         }
220
221         long sum = 0;
222         int len = 0;
223         long value = 0;
224         while (len < baddr.length) {
225             value = (((baddr[len] & 0xff) << 8) | (baddr[len + 1] & 0xff));
226             sum += value;
227             sum = normalizeChecksum(sum);
228             len += 2;
229         }
230         return sum;
231     }
232
233     private long normalizeChecksum(long value) {
234         if ((value & 0xffff0000) > 0) {
235             value = (value & 0xffff);
236             value += 1;
237         }
238         return value;
239     }
240
241     public byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
242         byte[] data = new byte[16];
243         Arrays.fill(data, (byte)0);
244
245         ByteBuffer buf = ByteBuffer.wrap(data);
246         buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue().toString()));
247         buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue().toString()));
248         buf.putShort((short)ethPdu.getEthertype().intValue());
249         return data;
250     }
251
252     public byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
253         byte[] data = new byte[128];
254         Arrays.fill(data, (byte)0);
255
256         ByteBuffer buf = ByteBuffer.wrap(data);
257         long flowLabel = (((long)(ip6Pdu.getVersion().shortValue() & 0x0f) << 28)
258                 | (ip6Pdu.getFlowLabel().longValue() & 0x0fffffff));
259         buf.putInt((int)flowLabel);
260         buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
261         buf.put((byte)ip6Pdu.getNextHeader().shortValue());
262         buf.put((byte)ip6Pdu.getHopLimit().shortValue());
263         try {
264             byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
265             buf.put(baddr);
266             baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
267             buf.put(baddr);
268         } catch (UnknownHostException e) {
269             LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
270         }
271         return data;
272     }
273
274     public Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
275         byte[] octets = bytesFromHexString(mac.getValue());
276
277         /* As per the RFC2373, steps involved to generate a LLA include
278            1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
279               between OUI and NIC Specific part.
280            2. Invert the Universal/Local flag in the OUI portion of the address.
281            3. Use the prefix "FE80::/10" along with the above 64 bit Interface
282               identifier to generate the IPv6 LLA. */
283
284         StringBuffer interfaceID = new StringBuffer();
285         short u8byte = (short) (octets[0] & 0xff);
286         u8byte ^= 1 << 1;
287         interfaceID.append(Integer.toHexString(0xFF & u8byte));
288         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
289         interfaceID.append(":");
290         interfaceID.append(Integer.toHexString(0xFF & octets[2]));
291         interfaceID.append("ff:fe");
292         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
293         interfaceID.append(":");
294         interfaceID.append(Integer.toHexString(0xFF & octets[4]));
295         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
296
297         // Return the address in its fully expanded format.
298         Ipv6Address ipv6LLA = new Ipv6Address(InetAddresses.forString(
299                 "fe80:0:0:0:" + interfaceID.toString()).getHostAddress());
300         return ipv6LLA;
301     }
302
303     private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
304         List<MatchInfo> matches = new ArrayList<>();
305         matches.add(new MatchInfo(MatchFieldType.eth_type,
306                 new long[] { NwConstants.ETHTYPE_IPV6 }));
307         matches.add(new MatchInfo(MatchFieldType.ip_proto,
308                 new long[] { IPProtocols.IPV6ICMP.intValue() }));
309         matches.add(new MatchInfo(MatchFieldType.icmp_v6,
310                 new long[] { Ipv6Constants.ICMP_V6_RS_CODE, 0}));
311         matches.add(new MatchInfo(MatchFieldType.metadata,
312                 new BigInteger[] { ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE}));
313         return matches;
314     }
315
316     private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, String ndTarget) {
317         List<MatchInfo> matches = new ArrayList<>();
318         matches.add(new MatchInfo(MatchFieldType.eth_type,
319                 new long[] { NwConstants.ETHTYPE_IPV6 }));
320         matches.add(new MatchInfo(MatchFieldType.ip_proto,
321                 new long[] { IPProtocols.IPV6ICMP.intValue() }));
322         matches.add(new MatchInfo(MatchFieldType.icmp_v6,
323                 new long[] { Ipv6Constants.ICMP_V6_NS_CODE, 0}));
324         matches.add(new MatchInfo(MatchFieldType.ipv6_nd_target,
325                 new String[] { ndTarget }));
326         matches.add(new MatchInfo(MatchFieldType.metadata,
327                 new BigInteger[] { ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE}));
328         return matches;
329     }
330
331     private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
332         return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
333                 .append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
334                 .append(elanTag).append(Ipv6Constants.FLOWID_SEPARATOR)
335                 .append(flowType).toString();
336     }
337
338     public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId,  Long elanTag, String ipv6Address,
339                                         IMdsalApiManager mdsalUtil,int addOrRemove) {
340         List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
341         List<InstructionInfo> instructions = new ArrayList<>();
342         List<ActionInfo> actionsInfos = new ArrayList<>();
343         actionsInfos.add(new ActionPuntToController());
344         instructions.add(new InstructionInfo(InstructionType.apply_actions,
345                 actionsInfos));
346         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
347                 getIPv6FlowRef(dpId, elanTag, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
348                 0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
349         if (addOrRemove == Ipv6Constants.DEL_FLOW) {
350             LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
351             mdsalUtil.removeFlow(rsFlowEntity);
352         } else {
353             LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
354             mdsalUtil.installFlow(rsFlowEntity);
355         }
356     }
357
358     public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, Long elanTag, IMdsalApiManager mdsalUtil,
359                                         int addOrRemove) {
360         if (dpId == null || dpId.equals(Ipv6Constants.INVALID_DPID)) {
361             return;
362         }
363         List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
364         List<InstructionInfo> instructions = new ArrayList<>();
365         List<ActionInfo> actionsInfos = new ArrayList<>();
366         // Punt to controller
367         actionsInfos.add(new ActionPuntToController());
368         instructions.add(new InstructionInfo(InstructionType.apply_actions,
369                 actionsInfos));
370         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
371                 getIPv6FlowRef(dpId, elanTag, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
372                 NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
373         if (addOrRemove == Ipv6Constants.DEL_FLOW) {
374             LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
375             mdsalUtil.removeFlow(rsFlowEntity);
376         } else {
377             LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
378             mdsalUtil.installFlow(rsFlowEntity);
379         }
380     }
381
382     public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
383                                           BigInteger cookie, List<Instruction> instructions) {
384         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
385                 .setFlowPriority(flowPriority).setInstruction(instructions);
386         return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
387                 .setServiceName(serviceName).setServicePriority(servicePriority)
388                 .setServiceType(ServiceTypeFlowBased.class)
389                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
390     }
391
392     private InstanceIdentifier buildServiceId(String interfaceName,
393                                               short priority) {
394         return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
395                 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
396                 .child(BoundServices.class, new BoundServicesKey(priority)).build();
397     }
398
399     public void bindIpv6Service(DataBroker broker, String interfaceName, Long elanTag, short tableId) {
400         int instructionKey = 0;
401         List<Instruction> instructions = new ArrayList<>();
402         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(ElanUtils.getElanMetadataLabel(elanTag),
403                 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
404         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
405         short serviceIndex = ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME, NwConstants.IPV6_SERVICE_INDEX);
406         BoundServices
407                 serviceInfo =
408                 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
409                         serviceIndex, Ipv6Constants.DEFAULT_FLOW_PRIORITY,
410                         NwConstants.COOKIE_IPV6_TABLE, instructions);
411         MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
412                 buildServiceId(interfaceName, serviceIndex), serviceInfo);
413     }
414
415     public void unbindIpv6Service(DataBroker broker, String interfaceName) {
416         MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
417                 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
418                         NwConstants.IPV6_SERVICE_INDEX)));
419     }
420
421     public static BigInteger getDataPathId(String dpId) {
422         long dpid = 0L;
423         if (dpId != null) {
424             dpid = new BigInteger(dpId.replaceAll(":", ""), 16).longValue();
425         }
426         return BigInteger.valueOf(dpid);
427     }
428 }