Remove redundant names in paths
[netvirt.git] / elanmanager / api / src / main / java / org / opendaylight / netvirt / elan / arp / responder / ArpResponderUtil.java
1 /*
2  * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.elan.arp.responder;
9
10 import java.math.BigInteger;
11 import java.text.MessageFormat;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.atomic.AtomicInteger;
19 import java.util.function.Supplier;
20 import java.util.stream.Collectors;
21
22 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
23 import org.opendaylight.genius.mdsalutil.ActionInfo;
24 import org.opendaylight.genius.mdsalutil.BucketInfo;
25 import org.opendaylight.genius.mdsalutil.FlowEntity;
26 import org.opendaylight.genius.mdsalutil.GroupEntity;
27 import org.opendaylight.genius.mdsalutil.InstructionInfo;
28 import org.opendaylight.genius.mdsalutil.MDSALUtil;
29 import org.opendaylight.genius.mdsalutil.MatchInfo;
30 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
33 import org.opendaylight.genius.mdsalutil.actions.ActionLoadIpToSpa;
34 import org.opendaylight.genius.mdsalutil.actions.ActionLoadMacToSha;
35 import org.opendaylight.genius.mdsalutil.actions.ActionMoveShaToTha;
36 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationEth;
37 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSpaToTpa;
38 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
39 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
40 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetArpOp;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
43 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
45 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
46 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
47 import org.opendaylight.genius.mdsalutil.matches.MatchArpTpa;
48 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
49 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
50 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 /**
67  * Arp Responder Utility Class.
68  */
69 public final class ArpResponderUtil {
70
71     private static final Logger LOG = LoggerFactory.getLogger(ArpResponderUtil.class);
72
73     private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
74
75     /**
76      * A Utility class.
77      */
78     private ArpResponderUtil() {
79     }
80
81     /**
82      * Install Group flow on the DPN.
83      *
84      * @param mdSalManager
85      *            Reference of MDSAL API RPC that provides API for installing
86      *            group flow
87      * @param dpnId
88      *            DPN on which group flow to be installed
89      * @param groupdId
90      *            Uniquely identifiable Group Id for the group flow
91      * @param groupName
92      *            Name of the group flow
93      * @param buckets
94      *            List of the bucket actions for the group flow
95      */
96     public static void installGroup(IMdsalApiManager mdSalManager, BigInteger dpnId, long groupdId, String groupName,
97             List<BucketInfo> buckets) {
98         LOG.trace("Installing group flow on dpn {}", dpnId);
99         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupdId, groupName, GroupTypes.GroupAll, buckets);
100         mdSalManager.syncInstallGroup(groupEntity);
101         try {
102             Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
103         } catch (InterruptedException e1) {
104             LOG.warn("Error while waiting for ARP Responder Group Entry to be installed on DPN {} ", dpnId);
105         }
106     }
107
108     /**
109      * Get Default ARP Responder Drop flow on the DPN.
110      *
111      * @param dpnId
112      *            DPN on which group flow to be installed
113      */
114     public static FlowEntity getArpResponderTableMissFlow(BigInteger dpnId) {
115         return MDSALUtil.buildFlowEntity(dpnId, NwConstants.ARP_RESPONDER_TABLE,
116                 String.valueOf(NwConstants.ARP_RESPONDER_TABLE), NwConstants.TABLE_MISS_PRIORITY,
117                 ArpResponderConstant.DROP_FLOW_NAME.value(), 0, 0, NwConstants.COOKIE_ARP_RESPONDER,
118                 new ArrayList<MatchInfo>(),
119                 Collections.singletonList(new InstructionApplyActions(Collections.singletonList(new ActionDrop()))));
120     }
121
122     /**
123      * Get Bucket Actions for ARP Responder Group Flow.
124      *
125      * <p>
126      * Install Default Groups, Group has 3 Buckets
127      * </p>
128      * <ul>
129      * <li>Punt to controller</li>
130      * <li>Resubmit to Table {@link NwConstants#LPORT_DISPATCHER_TABLE}, for
131      * ELAN flooding
132      * <li>Resubmit to Table {@link NwConstants#ARP_RESPONDER_TABLE}, for ARP
133      * Auto response from DPN itself</li>
134      * </ul>
135      *
136      * @param resubmitTableId
137      *            Resubmit Flow Table Id
138      * @param resubmitTableId2
139      *            Resubmit Flow Table Id
140      * @return List of bucket actions
141      */
142     public static List<BucketInfo> getDefaultBucketInfos(short resubmitTableId, short resubmitTableId2) {
143         return Arrays.asList(
144                 new BucketInfo(Collections.singletonList(new ActionPuntToController())),
145                 new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId))),
146                 new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId2))));
147     }
148
149     /**
150      * Get Match Criteria for the ARP Responder Flow.
151      *
152      * <p>
153      * List of Match Criteria for ARP Responder
154      * </p>
155      * <ul>
156      * <li>Packet is ARP</li>
157      * <li>Packet is ARP Request</li>
158      * <li>The ARP packet is requesting for Gateway IP</li>
159      * <li>Metadata which is generated by using Service
160      * Index({@link NwConstants#L3VPN_SERVICE_INDEX}) Lport Tag
161      * ({@link MetaDataUtil#METADATA_MASK_LPORT_TAG}) and VRF
162      * ID({@link MetaDataUtil#METADATA_MASK_VRFID})</li>
163      * </ul>
164      *
165      * @param lportTag
166      *            LPort Tag
167      * @param elanInstance
168      *            Elan Instance
169      * @param ipAddress
170      *            Ip Address to be matched to this flow
171      * @return List of Match criteria
172      */
173     public static List<MatchInfo> getMatchCriteria(int lportTag, ElanInstance elanInstance,
174             String ipAddress) {
175
176         BigInteger metadata = ElanHelper.getElanMetadataLabel(elanInstance.getElanTag(), lportTag);
177         BigInteger metadataMask = ElanHelper.getElanMetadataMask();
178         return Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST, new MatchArpTpa(ipAddress, "32"),
179                 new MatchMetadata(metadata, metadataMask));
180
181     }
182
183     /**
184      * Get List of actions for ARP Responder Flows.
185      *
186      * <p>
187      * Actions consists of all the ARP actions and Resubmit Action to table
188      * {@link NwConstants#ELAN_BASE_TABLE} such that packets can flow ELAN Rule
189      *
190      * @param ipAddress
191      *            IP Address for which ARP Response packet is to be generated
192      * @param macAddress
193      *            MacAddress for which ARP Response packet is to be generated
194      * @return List of ARP Responder Actions actions
195      */
196     public static List<Action> getActions(IInterfaceManager ifaceMgrRpcService, String ifName, String ipAddress,
197             String macAddress) {
198
199         AtomicInteger actionCounter = new AtomicInteger();
200         List<Action> actions = arpActions.apply(actionCounter, macAddress, ipAddress);
201         actions.addAll(getEgressActionsForInterface(ifaceMgrRpcService, ifName, actionCounter.get()));
202         LOG.trace("Total Number of actions is {}", actionCounter);
203         return actions;
204
205     }
206
207     /**
208      * A Interface that represent lambda TriFunction.
209      *
210      * @param <T>
211      *            Input type
212      * @param <U>
213      *            Input type
214      * @param <S>
215      *            Input type
216      * @param <R>
217      *            Return Type
218      */
219     @SuppressWarnings("checkstyle:ParameterName")
220     public interface TriFunction<T, U, S, R> {
221         /**
222          * Apply the Action.
223          *
224          * @param t
225          *            Input1
226          * @param u
227          *            Input2
228          * @param s
229          *            Input3
230          * @return computed result
231          */
232         R apply(T t, U u, S s);
233     }
234
235     /**
236      * Lambda to apply arpAction. Inputs action counter, mac address and ip
237      * address
238      */
239     private static TriFunction<AtomicInteger, String, String, List<Action>> arpActions = (actionCounter, mac, ip) -> {
240         List<Action> actions = new ArrayList<>();
241         Collections.addAll(actions, new ActionMoveSourceDestinationEth().buildAction(actionCounter.getAndIncrement()),
242                 new ActionSetFieldEthernetSource(new MacAddress(mac)).buildAction(actionCounter.getAndIncrement()),
243                 new ActionSetArpOp(NwConstants.ARP_REPLY).buildAction(actionCounter.getAndIncrement()),
244                 new ActionMoveShaToTha().buildAction(actionCounter.getAndIncrement()),
245                 new ActionMoveSpaToTpa().buildAction(actionCounter.getAndIncrement()),
246                 new ActionLoadMacToSha(new MacAddress(mac)).buildAction(actionCounter.getAndIncrement()),
247                 new ActionLoadIpToSpa(ip).buildAction(actionCounter.getAndIncrement()),
248                 new ActionNxLoadInPort(BigInteger.ZERO).buildAction(actionCounter.getAndIncrement()));
249         return actions;
250
251     };
252
253     /**
254      * Get instruction list for ARP responder flows.
255      */
256     public static List<Instruction> getInterfaceInstructions(IInterfaceManager ifaceMgrRpcService, String interfaceName,
257             String ipAddress, String macAddress) {
258         List<Action> actions = ArpResponderUtil.getActions(ifaceMgrRpcService, interfaceName, ipAddress, macAddress);
259         return Collections.singletonList(MDSALUtil.buildApplyActionsInstruction(actions));
260     }
261
262     /**
263      * Get instruction list for ARP responder flows originated from ext-net e.g.
264      * router-gw/fip.<br>
265      * The split-horizon bit should be reset in order to allow traffic from
266      * provider network to be routed back to flat/VLAN network and override the
267      * egress table drop flow.<br>
268      * In order to allow write-metadata in the ARP responder table the resubmit
269      * action needs to be replaced with goto instruction.
270      */
271     public static List<Instruction> getExtInterfaceInstructions(IInterfaceManager ifaceMgrRpcService,
272             String extInterfaceName, String ipAddress, String macAddress) {
273         AtomicInteger tableId = new AtomicInteger(-1);
274         List<Instruction> instructions = new ArrayList<>();
275         List<Action> actions = getActions(ifaceMgrRpcService, extInterfaceName, ipAddress, macAddress);
276         actions.removeIf(v -> {
277             org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = v
278                     .getAction();
279             if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
280                 tableId.set(((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable());
281                 return true;
282             } else {
283                 return false;
284             }
285         });
286
287         instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, 0));
288
289         if (tableId.get() != -1) {
290             // replace resubmit action with goto so it can co-exist with
291             // write-metadata
292             if ((short) tableId.get() > NwConstants.ARP_RESPONDER_TABLE) {
293                 instructions.add(new InstructionGotoTable((short) tableId.get()).buildInstruction(2));
294             } else {
295                 LOG.warn("Failed to insall responder flow for interface {}. Resubmit to {} can't be replaced with goto",
296                         extInterfaceName, tableId);
297             }
298         }
299
300         return instructions;
301     }
302
303     /**
304      * Install ARP Responder FLOW.
305      *
306      * @param mdSalManager
307      *            Reference of MDSAL API RPC that provides API for installing
308      *            flow
309      * @param dpnId
310      *            DPN on which flow to be installed
311      * @param flowId
312      *            Uniquely Identifiable Arp Responder Table flow Id
313      * @param flowName
314      *            Readable flow name
315      * @param priority
316      *            Flow Priority
317      * @param cookie
318      *            Flow Cookie
319      * @param matches
320      *            List of Match Criteria for the flow
321      * @param instructions
322      *            List of Instructions for the flow
323      */
324     public static void installFlow(IMdsalApiManager mdSalManager, BigInteger dpnId, String flowId, String flowName,
325             int priority, BigInteger cookie, List<MatchInfo> matches, List<Instruction> instructions) {
326         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.ARP_RESPONDER_TABLE, flowId, priority, flowName, 0, 0,
327                 cookie, matches, instructions);
328         mdSalManager.installFlow(dpnId, flowEntity);
329     }
330
331     /**
332      * Remove flow form DPN.
333      *
334      * @param mdSalManager
335      *            Reference of MDSAL API RPC that provides API for installing
336      *            flow
337      * @param dpnId
338      *            DPN form which flow to be removed
339      * @param flowId
340      *            Uniquely Identifiable Arp Responder Table flow Id that is to
341      *            be removed
342      */
343     public static void removeFlow(IMdsalApiManager mdSalManager, BigInteger dpnId, String flowId) {
344         Flow flowEntity = MDSALUtil.buildFlow(NwConstants.ARP_RESPONDER_TABLE, flowId);
345         mdSalManager.removeFlow(dpnId, flowEntity);
346     }
347
348     /**
349      * Creates Uniquely Identifiable flow Id.
350      *
351      * @param lportTag
352      *            LportTag of the flow
353      * @param ipAdress
354      *            Gateway IP for which ARP Response flow to be installed
355      * @return Unique Flow Id
356      *
357      * @see ArpResponderConstant#FLOW_ID_FORMAT_WITH_LPORT
358      * @see ArpResponderConstant#FLOW_ID_FORMAT_WITHOUT_LPORT
359      */
360     public static String getFlowId(int lportTag, String ipAdress) {
361         return MessageFormat.format(ArpResponderConstant.FLOW_ID_FORMAT_WITH_LPORT.value(),
362                         NwConstants.ARP_RESPONDER_TABLE, lportTag, ipAdress);
363     }
364
365     /**
366      * Generate Cookie per flow.
367      *
368      * <p>
369      * Cookie is generated by Summation of
370      * {@link NwConstants#COOKIE_ARP_RESPONDER} + 1 + lportTag + Gateway IP
371      *
372      * @param lportTag
373      *            Lport Tag of the flow
374      * @param ipAddress
375      *            Gateway IP for which ARP Response flow to be installed
376      * @return Cookie
377      */
378     public static BigInteger generateCookie(int lportTag, String ipAddress) {
379         LOG.trace("IPAddress in long {}", ipAddress);
380         BigInteger cookie = NwConstants.COOKIE_ARP_RESPONDER.add(BigInteger.valueOf(255))
381                 .add(BigInteger.valueOf(ipTolong(ipAddress)));
382         return cookie.add(BigInteger.valueOf(lportTag));
383     }
384
385     private static BigInteger buildCookie(short tableId, int arpOpType) {
386         return NwConstants.COOKIE_ARP_RESPONDER.add(BigInteger.ONE).add(
387                 BigInteger.valueOf(tableId).add(BigInteger.valueOf(arpOpType)));
388     }
389
390     private static String buildFlowRef(short tableId, int arpOpType) {
391         return (tableId == NwConstants.ARP_CHECK_TABLE
392                 ? ArpResponderConstant.FLOWID_PREFIX_FOR_ARP_CHECK.value()
393                 : ArpResponderConstant.FLOWID_PREFIX_FOR_MY_GW_MAC.value()) + tableId + NwConstants.FLOWID_SEPARATOR
394                 + (arpOpType == NwConstants.ARP_REQUEST ? "arp.request" : "arp.replay");
395     }
396
397     public static FlowEntity createArpDefaultFlow(BigInteger dpId, short tableId, int arpOpType,
398             Supplier<List<MatchInfo>> matches, Supplier<List<ActionInfo>> actions) {
399
400         List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actions.get()));
401         return MDSALUtil.buildFlowEntity(dpId, tableId, buildFlowRef(tableId, arpOpType),
402                 NwConstants.DEFAULT_ARP_FLOW_PRIORITY, buildFlowRef(tableId, arpOpType), 0, 0,
403                 buildCookie(tableId, arpOpType), matches.get(), instructions);
404     }
405
406     /**
407      * Get IP Address in Long from String.
408      *
409      * @param address
410      *            IP Address that to be converted to long
411      * @return Long value of the IP Address
412      */
413     private static long ipTolong(String address) {
414
415         // Parse IP parts into an int array
416         long[] ip = new long[4];
417         String[] parts = address.split("\\.");
418
419         for (int i = 0; i < 4; i++) {
420             ip[i] = Long.parseLong(parts[i]);
421         }
422         // Add the above IP parts into an int number representing your IP
423         // in a 32-bit binary form
424         long ipNumbers = 0;
425         for (int i = 0; i < 4; i++) {
426             ipNumbers += ip[i] << (24 - (8 * i));
427         }
428         return ipNumbers;
429
430     }
431
432     /**
433      * Get List of Egress Action for the VPN interface.
434      *
435      * @param ifaceMgrRpcService
436      *            Interface Manager RPC reference that invokes API to retrieve
437      *            Egress Action
438      * @param ifName
439      *            VPN Interface for which Egress Action to be retrieved
440      * @param actionCounter
441      *            Action Key
442      * @return List of Egress Actions
443      */
444     public static List<Action> getEgressActionsForInterface(IInterfaceManager ifaceMgrRpcService, String ifName,
445             int actionCounter) {
446         List<ActionInfo> actionInfos = ifaceMgrRpcService.getInterfaceEgressActions(ifName);
447         AtomicInteger counter = new AtomicInteger(actionCounter);
448         return actionInfos.stream().map(v -> v.buildAction(counter.getAndIncrement())).collect(Collectors.toList());
449     }
450
451     /**
452      * Uses the IdManager to retrieve ARP Responder GroupId from ELAN pool.
453      *
454      * @param idManager
455      *            the id manager
456      * @return the integer
457      */
458     public static Long retrieveStandardArpResponderGroupId(IdManagerService idManager) {
459
460         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
461                 .setPoolName(ArpResponderConstant.ELAN_ID_POOL_NAME.value())
462                 .setIdKey(ArpResponderConstant.ARP_RESPONDER_GROUP_ID.value()).build();
463
464         try {
465             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
466             RpcResult<AllocateIdOutput> rpcResult = result.get();
467             if (rpcResult.isSuccessful()) {
468                 LOG.trace("Retrieved Group Id is {}", rpcResult.getResult().getIdValue());
469                 return rpcResult.getResult().getIdValue();
470             } else {
471                 LOG.warn("RPC Call to Allocate Id returned with Errors {}", rpcResult.getErrors());
472             }
473         } catch (InterruptedException | ExecutionException e) {
474             LOG.warn("Exception when Allocating Id", e);
475         }
476         return 0L;
477     }
478
479 }