2 * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. 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
8 package org.opendaylight.netvirt.elan.arp.responder;
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.BiFunction;
20 import java.util.function.Supplier;
21 import java.util.stream.Collectors;
23 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
24 import org.opendaylight.genius.mdsalutil.ActionInfo;
25 import org.opendaylight.genius.mdsalutil.BucketInfo;
26 import org.opendaylight.genius.mdsalutil.FlowEntity;
27 import org.opendaylight.genius.mdsalutil.GroupEntity;
28 import org.opendaylight.genius.mdsalutil.InstructionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
34 import org.opendaylight.genius.mdsalutil.actions.ActionLoadIpToSpa;
35 import org.opendaylight.genius.mdsalutil.actions.ActionLoadMacToSha;
36 import org.opendaylight.genius.mdsalutil.actions.ActionMoveShaToTha;
37 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationEth;
38 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSpaToTpa;
39 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
40 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
41 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetArpOp;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
46 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
47 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
48 import org.opendaylight.genius.mdsalutil.matches.MatchArpTpa;
49 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
50 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
51 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
63 import org.opendaylight.yangtools.yang.common.RpcResult;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * Arp Responder Utility Class.
70 public class ArpResponderUtil {
72 private static final Logger LOG = LoggerFactory.getLogger(ArpResponderUtil.class);
74 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
79 private ArpResponderUtil() {
84 * Install Group flow on the DPN.
87 * Reference of MDSAL API RPC that provides API for installing
90 * DPN on which group flow to be installed
92 * Uniquely identifiable Group Id for the group flow
94 * Name of the group flow
96 * List of the bucket actions for the group flow
98 public static void installGroup(IMdsalApiManager mdSalManager, BigInteger dpnId, long groupdId, String groupName,
99 List<BucketInfo> buckets) {
100 LOG.trace("Installing group flow on dpn {}", dpnId);
101 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupdId, groupName, GroupTypes.GroupAll, buckets);
102 mdSalManager.syncInstallGroup(groupEntity, WAIT_TIME_FOR_SYNC_INSTALL);
104 Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
105 } catch (InterruptedException e1) {
106 LOG.warn("Error while waiting for ARP Responder Group Entry to be installed on DPN {} ", dpnId);
111 * Get Default ARP Responder Drop flow on the DPN.
114 * DPN on which group flow to be installed
116 public static FlowEntity getArpResponderTableMissFlow(BigInteger dpnId) {
117 return MDSALUtil.buildFlowEntity(dpnId, NwConstants.ARP_RESPONDER_TABLE,
118 String.valueOf(NwConstants.ARP_RESPONDER_TABLE), NwConstants.TABLE_MISS_PRIORITY,
119 ArpResponderConstant.DROP_FLOW_NAME.value(), 0, 0, NwConstants.COOKIE_ARP_RESPONDER,
120 new ArrayList<MatchInfo>(),
121 Collections.singletonList(new InstructionApplyActions(Collections.singletonList(new ActionDrop()))));
125 * Get Bucket Actions for ARP Responder Group Flow.
128 * Install Default Groups, Group has 3 Buckets
131 * <li>Punt to controller</li>
132 * <li>Resubmit to Table {@link NwConstants#LPORT_DISPATCHER_TABLE}, for
134 * <li>Resubmit to Table {@link NwConstants#ARP_RESPONDER_TABLE}, for ARP
135 * Auto response from DPN itself</li>
138 * @param resubmitTableId
139 * Resubmit Flow Table Id
140 * @param resubmitTableId2
141 * Resubmit Flow Table Id
142 * @return List of bucket actions
144 public static List<BucketInfo> getDefaultBucketInfos(short resubmitTableId, short resubmitTableId2) {
145 return Arrays.asList(
146 new BucketInfo(Collections.singletonList(new ActionPuntToController())),
147 new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId))),
148 new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId2))));
152 * Get Match Criteria for the ARP Responder Flow.
155 * List of Match Criteria for ARP Responder
158 * <li>Packet is ARP</li>
159 * <li>Packet is ARP Request</li>
160 * <li>The ARP packet is requesting for Gateway IP</li>
161 * <li>Metadata which is generated by using Service
162 * Index({@link NwConstants#L3VPN_SERVICE_INDEX}) Lport Tag
163 * ({@link MetaDataUtil#METADATA_MASK_LPORT_TAG}) and VRF
164 * ID({@link MetaDataUtil#METADATA_MASK_VRFID})</li>
169 * @param elanInstance
172 * Ip Address to be matched to this flow
173 * @return List of Match criteria
175 public static List<MatchInfo> getMatchCriteria(int lportTag, ElanInstance elanInstance,
178 BigInteger metadata = ElanHelper.getElanMetadataLabel(elanInstance.getElanTag(), lportTag);
179 BigInteger metadataMask = ElanHelper.getElanMetadataMask();
180 return Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST, new MatchArpTpa(ipAddress, "32"),
181 new MatchMetadata(metadata, metadataMask));
186 * Get List of actions for ARP Responder Flows.
189 * Actions consists of all the ARP actions and Resubmit Action to table
190 * {@link NwConstants#ELAN_BASE_TABLE} such that packets can flow ELAN Rule
193 * IP Address for which ARP Response packet is to be generated
195 * MacAddress for which ARP Response packet is to be generated
196 * @return List of ARP Responder Actions actions
198 public static List<Action> getActions(IInterfaceManager ifaceMgrRpcService, String ifName, String ipAddress,
201 AtomicInteger actionCounter = new AtomicInteger();
202 List<Action> actions = arpActions.apply(actionCounter, macAddress, ipAddress);
203 actions.addAll(getEgressActionsForInterface(ifaceMgrRpcService, ifName, actionCounter.get()));
204 LOG.trace("Total Number of actions is {}", actionCounter);
210 * A Interface that represent lambda TriFunction.
221 @SuppressWarnings("checkstyle:ParameterName")
222 public interface TriFunction<T, U, S, R> {
232 * @return computed result
234 R apply(T t, U u, S s);
238 * Lambda to apply arpAction. Inputs action counter, mac address and ip
241 private static TriFunction<AtomicInteger, String, String, List<Action>> arpActions = (actionCounter, mac, ip) -> {
242 List<Action> actions = new ArrayList<>();
243 Collections.addAll(actions, new ActionMoveSourceDestinationEth().buildAction(actionCounter.getAndIncrement()),
244 new ActionSetFieldEthernetSource(new MacAddress(mac)).buildAction(actionCounter.getAndIncrement()),
245 new ActionSetArpOp(NwConstants.ARP_REPLY).buildAction(actionCounter.getAndIncrement()),
246 new ActionMoveShaToTha().buildAction(actionCounter.getAndIncrement()),
247 new ActionMoveSpaToTpa().buildAction(actionCounter.getAndIncrement()),
248 new ActionLoadMacToSha(new MacAddress(mac)).buildAction(actionCounter.getAndIncrement()),
249 new ActionLoadIpToSpa(ip).buildAction(actionCounter.getAndIncrement()),
250 new ActionNxLoadInPort(BigInteger.ZERO).buildAction(actionCounter.getAndIncrement()));
256 * Get instruction list for ARP responder flows.
258 public static List<Instruction> getInterfaceInstructions(IInterfaceManager ifaceMgrRpcService, String interfaceName,
259 String ipAddress, String macAddress) {
260 List<Action> actions = ArpResponderUtil.getActions(ifaceMgrRpcService, interfaceName, ipAddress, macAddress);
261 return Collections.singletonList(MDSALUtil.buildApplyActionsInstruction(actions));
265 * Get instruction list for ARP responder flows originated from ext-net e.g.
267 * The split-horizon bit should be reset in order to allow traffic from
268 * provider network to be routed back to flat/VLAN network and override the
269 * egress table drop flow.<br>
270 * In order to allow write-metadata in the ARP responder table the resubmit
271 * action needs to be replaced with goto instruction.
273 public static List<Instruction> getExtInterfaceInstructions(IInterfaceManager ifaceMgrRpcService,
274 String extInterfaceName, String ipAddress, String macAddress) {
275 AtomicInteger tableId = new AtomicInteger(-1);
276 List<Instruction> instructions = new ArrayList<>();
277 List<Action> actions = getActions(ifaceMgrRpcService, extInterfaceName, ipAddress, macAddress);
278 actions.removeIf(v -> {
279 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = v
281 if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
282 tableId.set(((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable());
289 instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, 0));
291 if (tableId.get() != -1) {
292 // replace resubmit action with goto so it can co-exist with
294 if ((short) tableId.get() > NwConstants.ARP_RESPONDER_TABLE) {
295 instructions.add(new InstructionGotoTable((short) tableId.get()).buildInstruction(2));
297 LOG.warn("Failed to insall responder flow for interface {}. Resubmit to {} can't be replaced with goto",
298 extInterfaceName, tableId);
306 * Install ARP Responder FLOW.
308 * @param mdSalManager
309 * Reference of MDSAL API RPC that provides API for installing
312 * DPN on which flow to be installed
314 * Uniquely Identifiable Arp Responder Table flow Id
322 * List of Match Criteria for the flow
323 * @param instructions
324 * List of Instructions for the flow
326 public static void installFlow(IMdsalApiManager mdSalManager, BigInteger dpnId, String flowId, String flowName,
327 int priority, BigInteger cookie, List<MatchInfo> matches, List<Instruction> instructions) {
328 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.ARP_RESPONDER_TABLE, flowId, priority, flowName, 0, 0,
329 cookie, matches, instructions);
330 mdSalManager.installFlow(dpnId, flowEntity);
334 * Remove flow form DPN.
336 * @param mdSalManager
337 * Reference of MDSAL API RPC that provides API for installing
340 * DPN form which flow to be removed
342 * Uniquely Identifiable Arp Responder Table flow Id that is to
345 public static void removeFlow(IMdsalApiManager mdSalManager, BigInteger dpnId, String flowId) {
346 Flow flowEntity = MDSALUtil.buildFlow(NwConstants.ARP_RESPONDER_TABLE, flowId);
347 mdSalManager.removeFlow(dpnId, flowEntity);
351 * Creates Uniquely Identifiable flow Id.
354 * LportTag of the flow
356 * Gateway IP for which ARP Response flow to be installed
357 * @return Unique Flow Id
359 * @see ArpResponderConstant#FLOW_ID_FORMAT_WITH_LPORT
360 * @see ArpResponderConstant#FLOW_ID_FORMAT_WITHOUT_LPORT
362 public static String getFlowId(int lportTag, String ipAdress) {
363 return MessageFormat.format(ArpResponderConstant.FLOW_ID_FORMAT_WITH_LPORT.value(),
364 NwConstants.ARP_RESPONDER_TABLE, lportTag, ipAdress);
368 * Generate Cookie per flow.
371 * Cookie is generated by Summation of
372 * {@link NwConstants#COOKIE_ARP_RESPONDER} + 1 + lportTag + Gateway IP
375 * Lport Tag of the flow
377 * Gateway IP for which ARP Response flow to be installed
380 public static BigInteger generateCookie(int lportTag, String ipAddress) {
381 LOG.trace("IPAddress in long {}", ipAddress);
382 BigInteger cookie = NwConstants.COOKIE_ARP_RESPONDER.add(BigInteger.valueOf(255))
383 .add(BigInteger.valueOf(ipTolong(ipAddress)));
384 return cookie.add(BigInteger.valueOf(lportTag));
387 private static BiFunction<Short, Integer, BigInteger> cookie = (tableId,
388 arpOpType) -> NwConstants.COOKIE_ARP_RESPONDER.add(BigInteger.ONE).add(BigInteger.valueOf(tableId))
389 .add(BigInteger.valueOf(arpOpType));
391 private static BiFunction<Short, Integer, String> flowRef = (tableId,
392 arpOpType) -> (tableId == NwConstants.ARP_CHECK_TABLE
393 ? ArpResponderConstant.FLOWID_PREFIX_FOR_ARP_CHECK.value()
394 : ArpResponderConstant.FLOWID_PREFIX_FOR_MY_GW_MAC.value()) + tableId + NwConstants.FLOWID_SEPARATOR
395 + (arpOpType == NwConstants.ARP_REQUEST ? "arp.request" : "arp.replay");
397 public static FlowEntity createArpDefaultFlow(BigInteger dpId, short tableId, int arpOpType,
398 Supplier<List<MatchInfo>> matches, Supplier<List<ActionInfo>> actions) {
400 List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actions.get()));
401 return MDSALUtil.buildFlowEntity(dpId, tableId, flowRef.apply(tableId, arpOpType),
402 NwConstants.DEFAULT_ARP_FLOW_PRIORITY, flowRef.apply(tableId, arpOpType), 0, 0,
403 cookie.apply(tableId, arpOpType), matches.get(), instructions);
407 * Get IP Address in Long from String.
410 * IP Address that to be converted to long
411 * @return Long value of the IP Address
413 private static long ipTolong(String address) {
415 // Parse IP parts into an int array
416 long[] ip = new long[4];
417 String[] parts = address.split("\\.");
419 for (int i = 0; i < 4; i++) {
420 ip[i] = Long.parseLong(parts[i]);
422 // Add the above IP parts into an int number representing your IP
423 // in a 32-bit binary form
425 for (int i = 0; i < 4; i++) {
426 ipNumbers += ip[i] << (24 - (8 * i));
433 * Get List of Egress Action for the VPN interface.
435 * @param ifaceMgrRpcService
436 * Interface Manager RPC reference that invokes API to retrieve
439 * VPN Interface for which Egress Action to be retrieved
440 * @param actionCounter
442 * @return List of Egress Actions
444 public static List<Action> getEgressActionsForInterface(IInterfaceManager ifaceMgrRpcService, String ifName,
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());
452 * Uses the IdManager to retrieve ARP Responder GroupId from ELAN pool.
456 * @return the integer
458 public static Long retrieveStandardArpResponderGroupId(IdManagerService idManager) {
460 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
461 .setPoolName(ArpResponderConstant.ELAN_ID_POOL_NAME.value())
462 .setIdKey(ArpResponderConstant.ARP_RESPONDER_GROUP_ID.value()).build();
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();
471 LOG.warn("RPC Call to Allocate Id returned with Errors {}", rpcResult.getErrors());
473 } catch (InterruptedException | ExecutionException e) {
474 LOG.warn("Exception when Allocating Id", e);