/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.arp; import static com.google.common.base.Preconditions.checkNotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import javax.annotation.Nullable; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.net.util.SubnetUtils; import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2ContextId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Builder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayL3Context; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.JdkFutureAdapters; import com.google.common.util.concurrent.ListenableFuture; public class ArpTasker implements PacketProcessingListener { private static final Logger LOG = LoggerFactory.getLogger(ArpTasker.class); private static final short TABEL_FOR_ARP_FLOW = 0; private static final String ARP_REPLY_TO_CONTROLLER_FLOW_NAME = "arpReplyToController"; private static final int ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY = 10000; private static final Instruction SEND_TO_CONTROLLER_INSTRUCTION; private final ArpSender arpSender; private final SalFlowService flowService; private final DataBroker dataProvider; private final ListMultimap<String, Pair<RemoveFlowInput, EndpointL3Key>> requestInfoByKey = Multimaps.synchronizedListMultimap(ArrayListMultimap.<String, Pair<RemoveFlowInput, EndpointL3Key>>create()); static { ApplyActions applyActions = new ApplyActionsBuilder().setAction( ImmutableList.of(ArpFlowFactory.createSendToControllerAction(0))).build(); SEND_TO_CONTROLLER_INSTRUCTION = new InstructionBuilder().setOrder(0) .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build()) .build(); } public ArpTasker(DataBroker dataProvider, PacketProcessingService packetProcessingService, SalFlowService flowService) { this.dataProvider = checkNotNull(dataProvider); this.arpSender = new ArpSender(packetProcessingService); this.flowService = flowService; } @Override public void onPacketReceived(PacketReceived potentialArp) { Arp arp = null; try { arp = ArpResolverUtils.getArpFrom(potentialArp); } catch (Exception e) { LOG.trace( "Failed to decode potential ARP packet. This could occur when other than ARP packet was received.", e); return; } if (arp.getOperation() != ArpOperation.REPLY.intValue()) { LOG.trace("ARP packet is not REPLY."); return; } if (LOG.isTraceEnabled()) { LOG.trace("ARP REPLY received - {}", ArpUtils.getArpToStringFormat(arp)); } NodeKey nodeKey = potentialArp.getIngress().getValue().firstKeyOf(Node.class, NodeKey.class); if (nodeKey == null) { LOG.info("Unknown source node of ARP packet: {}", potentialArp); return; } Ipv4Address spa = ArpUtils.bytesToIp(arp.getSenderProtocolAddress()); MacAddress sha = ArpUtils.bytesToMac(arp.getSenderHardwareAddress()); List<Pair<RemoveFlowInput, EndpointL3Key>> removeFlowInputsAndL3EpKeys = requestInfoByKey.get(createKey(nodeKey.getId(), spa)); for (Pair<RemoveFlowInput, EndpointL3Key> removeFlowInputAndL3EpKey : removeFlowInputsAndL3EpKeys) { flowService.removeFlow(removeFlowInputAndL3EpKey.getLeft()); } // each L3EpKey on right-side part of Pair is same final EndpointL3Key l3EpKey = removeFlowInputsAndL3EpKeys.get(0).getRight(); ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); InstanceIdentifier<EndpointL3> l3EpIid = IidFactory.l3EndpointIid(l3EpKey.getL3Context(), l3EpKey.getIpAddress()); Optional<EndpointL3> potentialL3Ep = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, l3EpIid, rwTx); if (!potentialL3Ep.isPresent()) { LOG.info("L3 endpoint {} where MAC should be added does not exist anymore.", l3EpKey); rwTx.cancel(); return; } final EndpointL3Builder updatedL3EpBuilder = new EndpointL3Builder(potentialL3Ep.get()).setMacAddress(sha) .setTimestamp(System.currentTimeMillis()); EndpointL3 updatedL3Ep = updatedL3EpBuilder.build(); L2BridgeDomainId l2BdId = resolveL2BridgeDomainId(updatedL3Ep, rwTx); if (l2BdId != null) { updatedL3Ep = updatedL3EpBuilder.setL2Context(l2BdId).build(); EndpointBuilder newEpBuilder = new EndpointBuilder(updatedL3Ep).setKey(new EndpointKey(l2BdId, sha)); OfOverlayL3Context augmentation = updatedL3Ep.getAugmentation(OfOverlayL3Context.class); if (augmentation != null) { newEpBuilder.addAugmentation(OfOverlayContext.class, new OfOverlayContextBuilder(augmentation).build()); } Endpoint newEp = newEpBuilder.build(); rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointIid(l2BdId, sha), newEp); LOG.trace("Endpoint was created {}", newEp); } rwTx.put(LogicalDatastoreType.OPERATIONAL, l3EpIid, updatedL3Ep); LOG.trace("MAC was added to L3 endpoint {}", updatedL3Ep); rwTx.submit(); } private @Nullable L2BridgeDomainId resolveL2BridgeDomainId(EndpointL3 l3Ep, ReadTransaction rTx) { TenantId tenantId = l3Ep.getTenant(); Subnet subnetOfL3Ep = readSubnet(l3Ep, rTx); if (subnetOfL3Ep == null) { return null; } ContextId parentOfSubnet = subnetOfL3Ep.getParent(); if (parentOfSubnet == null) { return null; } L2ContextId l2ContextId = new L2ContextId(parentOfSubnet); Optional<L2BridgeDomain> potentialL2Bd = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, IidFactory.l2BridgeDomainIid(tenantId, new L2BridgeDomainId(l2ContextId)), rTx); if (potentialL2Bd.isPresent()) { return potentialL2Bd.get().getId(); } Optional<L2FloodDomain> potentialL2Fd = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, IidFactory.l2FloodDomainIid(tenantId, new L2FloodDomainId(l2ContextId)), rTx); if (!potentialL2Fd.isPresent()) { return null; } return potentialL2Fd.get().getParent(); } /** * Uses ARP to get MAC for the given L3 endpoint. Tries to find MAC for IP from * {@link EndpointL3#getKey()}.<br> * {@link EndpointL3#getNetworkContainment()} has to point to a {@link Subnet}.<br> * ARP Request is sent from all node connectors obtaining from * {@link OfOverlayNodeConfig#getExternalInterfaces()}<br> * MAC address obtained from ARP reply is added to the given L3 endpoint (if still exits).<br> * Also an {@link Endpoint} is created based on MAC If the subnet from network containment point * to {@link L2BridgeDomain} directly or throught {@link L2FloodDomain}. * * @param l3Ep the L3 endpoint which needs to have an MAC address */ public void addMacForL3EpAndCreateEp(final EndpointL3 l3Ep) { final Ipv4Address tpa = getIPv4Addresses(l3Ep); if (tpa == null) { LOG.debug("L3 endpoint {} does not contain IPv4 address.", l3Ep.getKey()); return; } ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction(); final SetMultimap<Node, Pair<InstanceIdentifier<NodeConnector>, MacAddress>> extNcWithMacByNode = readNodesWithExternalIfaces(rTx); if (extNcWithMacByNode.isEmpty()) { LOG.debug("No node with external interface was found."); rTx.close(); return; } final Ipv4Address senderIpAddress = createSenderIpAddress(l3Ep, rTx); if (senderIpAddress == null) { LOG.warn("Cannot create sender IPv4 address for L3 endpoint {}", l3Ep); rTx.close(); return; } rTx.close(); for (final Node node : extNcWithMacByNode.keySet()) { final InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(Nodes.class) .child(Node.class, node.getKey()) .build(); final NodeRef nodeRef = new NodeRef(nodeIid); List<ListenableFuture<RpcResult<AddFlowOutput>>> arpFlowResultFutures = new ArrayList<>(); List<Pair<RemoveFlowInput, EndpointL3Key>> flowsForRemove = new ArrayList<>(); for (final Pair<InstanceIdentifier<NodeConnector>, MacAddress> extNcIidAndMac : extNcWithMacByNode.get(node)) { final ArpMessageAddress senderAddress = new ArpMessageAddress(extNcIidAndMac.getRight(), senderIpAddress); NodeConnectorId ncId = extNcIidAndMac.getLeft().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId(); final Flow arpReplyToControllerFlow = createArpReplyToControllerFlow(senderAddress, tpa, ncId); flowsForRemove.add(new ImmutablePair<>(new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode( nodeRef).build(), l3Ep.getKey())); final InstanceIdentifier<Flow> flowIid = createFlowIid(arpReplyToControllerFlow, nodeIid); Future<RpcResult<AddFlowOutput>> futureAddFlowResult = flowService.addFlow(new AddFlowInputBuilder( arpReplyToControllerFlow).setFlowRef(new FlowRef(flowIid)).setNode(nodeRef).build()); arpFlowResultFutures.add(JdkFutureAdapters.listenInPoolThread(futureAddFlowResult)); } requestInfoByKey.putAll(createKey(node.getId(), tpa), flowsForRemove); ListenableFuture<List<RpcResult<AddFlowOutput>>> futureArpFlowResults = Futures.allAsList(arpFlowResultFutures); Futures.addCallback(futureArpFlowResults, new FutureCallback<List<RpcResult<AddFlowOutput>>>() { @Override public void onSuccess(List<RpcResult<AddFlowOutput>> result) { for (RpcResult<AddFlowOutput> addFlowResult : result) { if (!addFlowResult.isSuccessful()) { LOG.warn("An ARP Reply to Controller flow was not created on node {} \nErrors: {}", node.getId().getValue(), addFlowResult.getErrors()); continue; } } LOG.debug("ARP Reply to Controller flows were created on node {}", node.getId().getValue()); for (final Pair<InstanceIdentifier<NodeConnector>, MacAddress> extNcIidAndMac : extNcWithMacByNode.get(node)) { final ArpMessageAddress senderAddress = new ArpMessageAddress(extNcIidAndMac.getRight(), senderIpAddress); ListenableFuture<RpcResult<Void>> futureSendArpResult = arpSender.sendArp(senderAddress, tpa, extNcIidAndMac.getLeft()); Futures.addCallback(futureSendArpResult, logResult(tpa, extNcIidAndMac.getLeft())); } } @Override public void onFailure(Throwable t) { LOG.error( "Illegal state - Installation of ARP flows on node {} failed. Node can contain just some ARP flows.", node.getId(), t); } }); } } private static @Nullable Ipv4Address getIPv4Addresses(EndpointL3 l3ep) { IpAddress ipAddress = l3ep.getKey().getIpAddress(); if (ipAddress.getIpv4Address() == null) { return null; } return ipAddress.getIpv4Address(); } private SetMultimap<Node, Pair<InstanceIdentifier<NodeConnector>, MacAddress>> readNodesWithExternalIfaces( ReadTransaction rTx) { Optional<Nodes> potentialNodes = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(Nodes.class).build(), rTx); if (!potentialNodes.isPresent() || potentialNodes.get().getNode() == null) { return ImmutableSetMultimap.of(); } List<Node> nodes = potentialNodes.get().getNode(); SetMultimap<Node, Pair<InstanceIdentifier<NodeConnector>, MacAddress>> extIfacesByNode = HashMultimap.create(); for (Node node : nodes) { OfOverlayNodeConfig ofOverlayNode = node.getAugmentation(OfOverlayNodeConfig.class); if (ofOverlayNode != null) { List<ExternalInterfaces> externalIfaces = ofOverlayNode.getExternalInterfaces(); if (externalIfaces != null) { for (ExternalInterfaces extIface : externalIfaces) { NodeConnectorId externalNc = extIface.getNodeConnectorId(); InstanceIdentifier<NodeConnector> extNcIid = InstanceIdentifier.builder(Nodes.class) .child(Node.class, node.getKey()) .child(NodeConnector.class, new NodeConnectorKey(externalNc)) .build(); Optional<NodeConnector> potentialExtNcFromOper = DataStoreHelper.readFromDs( LogicalDatastoreType.OPERATIONAL, extNcIid, rTx); if (!potentialExtNcFromOper.isPresent()) { LOG.debug("Node connector {} does not exit in OPER DS. Node from CONF: {}", externalNc.getValue(), node); continue; } FlowCapableNodeConnector externalFcNc = potentialExtNcFromOper.get().getAugmentation( FlowCapableNodeConnector.class); if (externalFcNc == null || externalFcNc.getHardwareAddress() == null) { LOG.debug("Hardware address does not exist on node connector {}", externalNc.getValue()); LOG.trace("Node connector from OPER DS {}", potentialExtNcFromOper.get()); continue; } extIfacesByNode.put(node, new ImmutablePair<>(extNcIid, externalFcNc.getHardwareAddress())); } } } } return extIfacesByNode; } private @Nullable Ipv4Address createSenderIpAddress(EndpointL3 l3Ep, ReadTransaction rTx) { Subnet subnetOfL3Ep = readSubnet(l3Ep, rTx); if (subnetOfL3Ep == null) { return null; } SubnetInfo subnetInfo = new SubnetUtils(subnetOfL3Ep.getIpPrefix().getIpv4Prefix().getValue()).getInfo(); String senderIp = subnetInfo.getHighAddress(); if (senderIp.equals(l3Ep.getKey().getIpAddress().getIpv4Address().getValue())) { senderIp = subnetInfo.getLowAddress(); } return new Ipv4Address(senderIp); } private @Nullable Subnet readSubnet(EndpointL3 l3Ep, ReadTransaction rTx) { NetworkDomainId l3EpNetworkContainment = l3Ep.getNetworkContainment(); if (l3EpNetworkContainment == null) { LOG.debug("L3 endpoint {} does not contain network containment.", l3Ep.getKey()); return null; } if (l3Ep.getTenant() == null) { LOG.debug("L3 endpoint {} does not contain tenat.", l3Ep.getKey()); return null; } Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(l3Ep.getTenant(), new SubnetId(l3EpNetworkContainment)), rTx); if (!potentialSubnet.isPresent()) { LOG.debug( "Network containment {} of L3 endpoint {} does not point to a subnet or the subnet does not exist.", l3EpNetworkContainment.getValue(), l3Ep.getKey()); return null; } return potentialSubnet.get(); } private Flow createArpReplyToControllerFlow(ArpMessageAddress senderAddress, Ipv4Address ipForRequestedMac, NodeConnectorId inPort) { checkNotNull(senderAddress); checkNotNull(ipForRequestedMac); FlowBuilder arpFlow = new FlowBuilder().setTableId(TABEL_FOR_ARP_FLOW) .setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME) .setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY) .setBufferId(OFConstants.OFP_NO_BUFFER) .setIdleTimeout(0) .setHardTimeout(0) .setFlags(new FlowModFlags(false, false, false, false, false)); EthernetMatch ethernetMatch = ArpFlowFactory.createEthernetMatch(); ArpMatch arpMatch = ArpFlowFactory.createArpMatch(senderAddress, ipForRequestedMac); Match match = new MatchBuilder().setEthernetMatch(ethernetMatch).setLayer3Match(arpMatch).setInPort(inPort).build(); arpFlow.setMatch(match); arpFlow.setInstructions(new InstructionsBuilder().setInstruction( ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build()); arpFlow.setId(createFlowId(ethernetMatch, arpMatch)); return arpFlow.build(); } private FlowId createFlowId(EthernetMatch ethernetMatch, ArpMatch arpMatch) { StringBuilder sb = new StringBuilder(); sb.append(ARP_REPLY_TO_CONTROLLER_FLOW_NAME); sb.append("|").append(ethernetMatch); sb.append("|").append(arpMatch); return new FlowId(sb.toString()); } private static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) { return nodeIid.builder() .augmentation(FlowCapableNode.class) .child(Table.class, new TableKey(flow.getTableId())) .child(Flow.class, new FlowKey(flow.getId())) .build(); } private FutureCallback<RpcResult<Void>> logResult(final Ipv4Address tpa, final InstanceIdentifier<NodeConnector> ncIid) { return new FutureCallback<RpcResult<Void>>() { @Override public void onSuccess(RpcResult<Void> result) { LOG.debug("ARP Request for IP {} was sent from {}.", tpa.getValue(), ncIid); } @Override public void onFailure(Throwable t) { LOG.warn("ARP Request for IP {} was NOT sent from {}.", tpa.getValue(), ncIid); } }; } private static String createKey(NodeId node, Ipv4Address ip) { return node.getValue() + "_" + "_" + ip.getValue(); } }