2 * Copyright (c) 2016 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.vpnmanager.arp.responder;
10 import java.math.BigInteger;
11 import java.text.MessageFormat;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.genius.mdsalutil.BucketInfo;
21 import org.opendaylight.genius.mdsalutil.FlowEntity;
22 import org.opendaylight.genius.mdsalutil.GroupEntity;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.genius.mdsalutil.MatchFieldType;
25 import org.opendaylight.genius.mdsalutil.MatchInfo;
26 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
29 import org.opendaylight.genius.mdsalutil.actions.ActionLoadIpToSpa;
30 import org.opendaylight.genius.mdsalutil.actions.ActionLoadMacToSha;
31 import org.opendaylight.genius.mdsalutil.actions.ActionMoveShaToTha;
32 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationEth;
33 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSpaToTpa;
34 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
35 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
36 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
37 import org.opendaylight.genius.mdsalutil.actions.ActionSetArpOp;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
39 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
41 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
42 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
43 import org.opendaylight.netvirt.vpnmanager.ArpReplyOrRequest;
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.action.types.rev131112.action.list.Action;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
58 import org.opendaylight.yangtools.yang.common.RpcResult;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
63 * Arp Responder Utility Class
67 public class ArpResponderUtil {
69 private final static Logger LOG = LoggerFactory
70 .getLogger(ArpResponderUtil.class);
72 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
77 private ArpResponderUtil() {
82 * Install Group flow on the DPN
85 * Reference of MDSAL API RPC that provides API for installing
88 * DPN on which group flow to be installed
90 * Uniquely identifiable Group Id for the group flow
92 * Name of the group flow
94 * List of the bucket actions for the group flow
96 public static void installGroup(final IMdsalApiManager mdSalManager,
97 final BigInteger dpnId, final long groupdId, final String groupName,
98 final List<BucketInfo> buckets) {
99 LOG.trace("Installing group flow on dpn {}", dpnId);
100 final GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId,
101 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
117 public static FlowEntity getArpResponderTableMissFlow(final BigInteger dpnId) {
118 return MDSALUtil.buildFlowEntity(dpnId, NwConstants.ARP_RESPONDER_TABLE,
119 String.valueOf(NwConstants.ARP_RESPONDER_TABLE),
120 NwConstants.TABLE_MISS_PRIORITY,
121 ArpResponderConstant.DROP_FLOW_NAME.value(), 0, 0,
122 NwConstants.COOKIE_ARP_RESPONDER,
123 new ArrayList<MatchInfo>(),
124 Collections.singletonList(new InstructionApplyActions(Collections.singletonList(new ActionDrop()))));
128 * Get Bucket Actions for ARP Responder Group Flow
131 * Install Default Groups, Group has 3 Buckets
134 * <li>Punt to controller</li>
135 * <li>Resubmit to Table {@link NwConstants#LPORT_DISPATCHER_TABLE}, for
137 * <li>Resubmit to Table {@link NwConstants#ARP_RESPONDER_TABLE}, for ARP
138 * Auto response from DPN itself</li>
141 * @param resubmitTableId
142 * Resubmit Flow Table Id
143 * @param resubmitTableId2
144 * Resubmit Flow Table Id
145 * @return List of bucket actions
147 public static List<BucketInfo> getDefaultBucketInfos(
148 final short resubmitTableId, final short resubmitTableId2) {
149 final List<BucketInfo> buckets = new ArrayList<>();
150 buckets.add(new BucketInfo(Collections.singletonList(new ActionPuntToController())));
151 buckets.add(new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId))));
152 buckets.add(new BucketInfo(Collections.singletonList(new ActionNxResubmit(resubmitTableId2))));
157 * Get Match Criteria for the ARP Responder Flow
159 * List of Match Criteria for ARP Responder
162 * <li>Packet is ARP</li>
163 * <li>Packet is ARP Request</li>
164 * <li>The ARP packet is requesting for Gateway IP</li>
165 * <li>Metadata which is generated by using Service
166 * Index({@link NwConstants#L3VPN_SERVICE_INDEX}) Lport Tag
167 * ({@link MetaDataUtil#METADATA_MASK_LPORT_TAG}) and VRF
168 * ID({@link MetaDataUtil#METADATA_MASK_VRFID})</li>
177 * @return List of Match criteria
179 public static List<MatchInfo> getMatchCriteria(final int lPortTag,
180 final long vpnId, final String ipAddress) {
182 final List<MatchInfo> matches = new ArrayList<MatchInfo>();
183 short mIndex = NwConstants.L3VPN_SERVICE_INDEX;
184 final BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(
185 lPortTag, ++mIndex, MetaDataUtil.getVpnIdMetadata(vpnId));
186 final BigInteger metadataMask = MetaDataUtil
187 .getMetaDataMaskForLPortDispatcher(
188 MetaDataUtil.METADATA_MASK_SERVICE_INDEX,
189 MetaDataUtil.METADATA_MASK_LPORT_TAG,
190 MetaDataUtil.METADATA_MASK_VRFID);
192 // Matching Arp request flows
193 matches.add(new MatchInfo(MatchFieldType.eth_type,
194 new long[] { NwConstants.ETHTYPE_ARP }));
195 matches.add(new MatchInfo(MatchFieldType.metadata,
196 new BigInteger[] { metadata, metadataMask }));
197 matches.add(new MatchInfo(MatchFieldType.arp_op,
198 new long[] { ArpReplyOrRequest.REQUEST.getArpOperation() }));
199 matches.add(new MatchInfo(MatchFieldType.arp_tpa,
200 new String[] { ipAddress, "32" }));
206 * Get List of actions for ARP Responder Flows
208 * Actions consists of all the ARP actions from
209 * and Egress Actions Retrieved
211 * @param ifaceMgrRpcService
212 * Interface manager RPC reference to invoke RPC to get Egress
213 * actions for the interface
214 * @param vpnInterface
215 * VPN Interface for which flow to be installed
220 * @return List of ARP Responder Actions actions
222 public static List<Action> getActions(
223 final OdlInterfaceRpcService ifaceMgrRpcService,
224 final String vpnInterface, final String ipAddress,
225 final String macAddress) {
227 final List<Action> actions = new ArrayList<>();
228 int actionCounter = 0;
229 actions.add(new ActionMoveSourceDestinationEth().buildAction(actionCounter++));
230 actions.add(new ActionSetFieldEthernetSource(new MacAddress(macAddress)).buildAction(actionCounter++));
231 actions.add(new ActionSetArpOp(NwConstants.ARP_REPLY).buildAction(actionCounter++));
232 actions.add(new ActionMoveShaToTha().buildAction(actionCounter++));
233 actions.add(new ActionMoveSpaToTpa().buildAction(actionCounter++));
234 actions.add(new ActionLoadMacToSha(new MacAddress(macAddress)).buildAction(actionCounter++));
235 actions.add(new ActionLoadIpToSpa(ipAddress).buildAction(actionCounter++));
236 //A temporary fix until to send packet to incoming port by loading IN_PORT with zero, until in_port is overridden in table=0
237 actions.add(new ActionNxLoadInPort(BigInteger.ZERO).buildAction(actionCounter++));
239 actions.addAll(getEgressActionsForInterface(ifaceMgrRpcService,
240 vpnInterface, actionCounter));
241 LOG.trace("Total Number of actions is {}", actionCounter);
247 * Get instruction list for ARP responder flows originated from ext-net e.g.
249 * The split-horizon bit should be reset in order to allow traffic from
250 * provider network to be routed back to flat/VLAN network and override the
251 * egress table drop flow.<br>
252 * In order to allow write-metadata in the ARP responder table the resubmit
253 * action needs to be replaced with goto instruction.
255 * @param ifaceMgrRpcService
256 * @param extInterfaceName
261 public static List<Instruction> getExtInterfaceInstructions(final OdlInterfaceRpcService ifaceMgrRpcService,
262 final String extInterfaceName, final String ipAddress, final String macAddress) {
263 Short tableId = null;
264 List<Instruction> instructions = new ArrayList<>();
265 List<Action> actions = getActions(ifaceMgrRpcService, extInterfaceName, ipAddress, macAddress);
266 for (Iterator<Action> iterator = actions.iterator(); iterator.hasNext();) {
267 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = iterator
269 if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
270 tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
276 instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, 0));
277 // reset the split-horizon bit to allow traffic to be sent back to the
280 new InstructionWriteMetadata(BigInteger.ZERO, MetaDataUtil.METADATA_MASK_SH_FLAG).buildInstruction(1));
282 if (tableId != null) {
283 // replace resubmit action with goto so it can co-exist with
285 if (tableId > NwConstants.ARP_RESPONDER_TABLE) {
286 instructions.add(new InstructionGotoTable(tableId).buildInstruction(2));
288 LOG.warn("Failed to insall responder flow for interface {}. Resubmit to {} can't be replaced with goto",
289 extInterfaceName, tableId);
297 * Install ARP Responder FLOW
299 * @param mdSalManager
300 * Reference of MDSAL API RPC that provides API for installing
303 * Write Transaction to write the flow
305 * DPN on which flow to be installed
307 * Uniquely Identifiable Arp Responder Table flow Id
315 * List of Match Criteria for the flow
316 * @param instructions
317 * List of Instructions for the flow
319 public static void installFlow(final IMdsalApiManager mdSalManager,
320 final WriteTransaction writeInvTxn, final BigInteger dpnId,
321 final String flowId, final String flowName,
322 final int priority, final BigInteger cookie,
323 List<MatchInfo> matches, List<Instruction> instructions) {
325 final Flow flowEntity = MDSALUtil.buildFlowNew(
326 NwConstants.ARP_RESPONDER_TABLE, flowId, priority, flowName, 0,
327 0, cookie, matches, instructions);
328 mdSalManager.addFlowToTx(dpnId, flowEntity, writeInvTxn);
332 * Remove flow form DPN
334 * @param mdSalManager
335 * Reference of MDSAL API RPC that provides API for installing
338 * Write Transaction to write the flow
340 * DPN form which flow to be removed
342 * Uniquely Identifiable Arp Responder Table flow Id that is to
345 public static void removeFlow(final IMdsalApiManager mdSalManager,
346 final WriteTransaction writeInvTxn,
347 final BigInteger dpnId, final String flowId) {
348 final Flow flowEntity = MDSALUtil
349 .buildFlow(NwConstants.ARP_RESPONDER_TABLE, flowId);
350 mdSalManager.removeFlowToTx(dpnId, flowEntity, writeInvTxn);
354 * Creates Uniquely Identifiable flow Id
356 * <b>Refer:</b> {@link ArpResponderConstant#FLOW_ID_FORMAT}
359 * LportTag of the flow
361 * Gateway IP for which ARP Response flow to be installed
362 * @return Unique Flow Id
364 public static String getFlowID(final int lportTag, final String gwIp) {
365 return MessageFormat.format(ArpResponderConstant.FLOW_ID_FORMAT.value(),
366 NwConstants.ARP_RESPONDER_TABLE, lportTag, gwIp);
370 * Generate Cookie per flow
372 * Cookie is generated by Summation of
373 * {@link NwConstants#COOKIE_ARP_RESPONDER} + 1 + lportTag + Gateway IP
376 * Lport Tag of the flow
378 * Gateway IP for which ARP Response flow to be installed
381 public static BigInteger generateCookie(final long lportTag,
383 LOG.trace("IPAddress in long {}", gwIp);
384 return NwConstants.COOKIE_ARP_RESPONDER.add(BigInteger.ONE)
385 .add(BigInteger.valueOf(lportTag))
386 .add(BigInteger.valueOf(ipTolong(gwIp)));
390 * Get IP Address in Long from String
393 * IP Address that to be converted to long
394 * @return Long value of the IP Address
396 private static long ipTolong(String address) {
398 // Parse IP parts into an int array
399 long[] ip = new long[4];
400 String[] parts = address.split("\\.");
402 for (int i = 0; i < 4; i++) {
403 ip[i] = Long.parseLong(parts[i]);
405 // Add the above IP parts into an int number representing your IP
406 // in a 32-bit binary form
408 for (int i = 0; i < 4; i++) {
409 ipNumbers += ip[i] << (24 - (8 * i));
416 * Get List of Egress Action for the VPN interface
418 * @param ifaceMgrRpcService
419 * Interface Manager RPC reference that invokes API to retrieve
422 * VPN Interface for which Egress Action to be retrieved
423 * @param actionCounter
425 * @return List of Egress Actions
427 public static List<Action> getEgressActionsForInterface(
428 final OdlInterfaceRpcService ifaceMgrRpcService, String ifName,
430 final List<Action> listActions = new ArrayList<>();
432 final RpcResult<GetEgressActionsForInterfaceOutput> result = ifaceMgrRpcService
433 .getEgressActionsForInterface(
434 new GetEgressActionsForInterfaceInputBuilder()
435 .setIntfName(ifName).build())
437 if (result.isSuccessful()) {
438 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions = result
439 .getResult().getAction();
440 for (final Action action : actions) {
443 .add(new org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder(
444 action).setKey(new ActionKey(actionCounter))
445 .setOrder(actionCounter++).build());
450 "RPC Call to Get egress actions for interface {} returned with Errors {}",
451 ifName, result.getErrors());
453 } catch (InterruptedException | ExecutionException e) {
454 LOG.warn("Exception when egress actions for interface {}", ifName,
461 * Uses the IdManager to retrieve ARP Responder GroupId from ELAN pool.
465 * @return the integer
467 public static Long retrieveStandardArpResponderGroupId(IdManagerService idManager) {
469 AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(ArpResponderConstant.ELAN_ID_POOL_NAME.value())
470 .setIdKey(ArpResponderConstant.ARP_RESPONDER_GROUP_ID.value()).build();
473 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
474 RpcResult<AllocateIdOutput> rpcResult = result.get();
475 if (rpcResult.isSuccessful()) {
476 LOG.trace("Retrieved Group Id is {}", rpcResult.getResult().getIdValue().longValue());
477 return rpcResult.getResult().getIdValue().longValue();
479 LOG.warn("RPC Call to Allocate Id returned with Errors {}", rpcResult.getErrors());
481 } catch (InterruptedException | ExecutionException e) {
482 LOG.warn("Exception when Allocating Id", e);