2 * Copyright (c) 2015 Brocade Communications Systems, Inc. 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.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
10 import static com.google.common.base.Preconditions.checkNotNull;
12 import java.math.BigInteger;
13 import java.util.Map.Entry;
14 import java.util.concurrent.Callable;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.atomic.AtomicBoolean;
22 import java.util.concurrent.atomic.AtomicLong;
24 import javax.annotation.Nullable;
26 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
27 import org.opendaylight.openflowplugin.api.OFConstants;
28 import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
29 import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
30 import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
31 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
32 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.common.RpcResult;
72 import org.osgi.framework.BundleContext;
73 import org.osgi.framework.ServiceReference;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
77 import com.google.common.base.Preconditions;
78 import com.google.common.collect.ImmutableList;
79 import com.google.common.util.concurrent.FutureCallback;
80 import com.google.common.util.concurrent.Futures;
81 import com.google.common.util.concurrent.JdkFutureAdapters;
82 import com.google.common.util.concurrent.ListenableFuture;
83 import com.google.common.util.concurrent.ListeningExecutorService;
84 import com.google.common.util.concurrent.MoreExecutors;
88 * @author Anil Vishnoi (avishnoi@Brocade.com)
91 public class GatewayMacResolverService extends AbstractServiceInstance
92 implements ConfigInterface, GatewayMacResolver,PacketProcessingListener {
94 private static final Logger LOG = LoggerFactory.getLogger(GatewayMacResolverService.class);
95 private static final short TABEL_FOR_ARP_FLOW = 0;
96 private static final String ARP_REPLY_TO_CONTROLLER_FLOW_NAME = "GatewayArpReplyRouter";
97 private static final int ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY = 10000;
98 private static final Instruction SEND_TO_CONTROLLER_INSTRUCTION;
99 private ArpSender arpSender;
100 private SalFlowService flowService;
101 private final AtomicLong flowCookie = new AtomicLong();
102 private final ConcurrentMap<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataMap =
103 new ConcurrentHashMap<Ipv4Address, ArpResolverMetadata>();
104 private final int ARP_WATCH_BROTHERS = 10;
105 private final int WAIT_CYCLES = 3;
106 private final int PER_CYCLE_WAIT_DURATION = 1000;
107 private final int REFRESH_INTERVAL = 10;
108 private final ListeningExecutorService arpWatcherWall = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ARP_WATCH_BROTHERS));
109 private final ScheduledExecutorService gatewayMacRefresherPool = Executors.newScheduledThreadPool(1);
110 private final ScheduledExecutorService refreshRequester = Executors.newSingleThreadScheduledExecutor();
111 private AtomicBoolean initializationDone = new AtomicBoolean(false);
114 ApplyActions applyActions = new ApplyActionsBuilder().setAction(
115 ImmutableList.of(ArpFlowFactory.createSendToControllerAction(0))).build();
116 SEND_TO_CONTROLLER_INSTRUCTION = new InstructionBuilder().setOrder(0)
117 .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build())
121 public GatewayMacResolverService(){
122 super(Service.GATEWAY_RESOLVER);
125 public GatewayMacResolverService(Service service){
130 if(!initializationDone.get()){
131 initializationDone.set(true);
132 ProviderContext providerContext = NetvirtProvidersProvider.getProviderContext();
133 checkNotNull(providerContext);
134 PacketProcessingService packetProcessingService = providerContext.getRpcService(PacketProcessingService.class);
135 if (packetProcessingService != null) {
136 LOG.debug("{} was found.", PacketProcessingService.class.getSimpleName());
137 this.arpSender = new ArpSender(packetProcessingService);
139 LOG.error("Missing service {}", PacketProcessingService.class.getSimpleName());
140 this.arpSender = null;
142 flowService = providerContext.getRpcService(SalFlowService.class);
143 refreshRequester.scheduleWithFixedDelay(new Runnable(){
147 if (!gatewayToArpMetadataMap.isEmpty()){
148 for(final Entry<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataEntry : gatewayToArpMetadataMap.entrySet()){
149 final Ipv4Address gatewayIp = gatewayToArpMetadataEntry.getKey();
150 final ArpResolverMetadata gatewayMetaData = gatewayToArpMetadataEntry.getValue();
151 gatewayMacRefresherPool.schedule(new Runnable(){
156 final Node externalNetworkBridge = getExternalBridge(gatewayMetaData.getExternalNetworkBridgeDpid());
157 if(externalNetworkBridge == null){
158 LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
159 + "is not connected to controller.",gatewayIp.getValue(),gatewayMetaData.getExternalNetworkBridgeDpid() );
162 LOG.debug("Refresh Gateway Mac for gateway {} using source ip {} and mac {} for ARP request",
163 gatewayIp.getValue(),gatewayMetaData.getArpRequestSourceIp().getValue(),gatewayMetaData.getArpRequestMacAddress().getValue());
165 sendGatewayArpRequest(externalNetworkBridge,gatewayIp,gatewayMetaData.getArpRequestSourceIp(), gatewayMetaData.getArpRequestMacAddress());
167 }, 1, TimeUnit.SECONDS);
171 }, REFRESH_INTERVAL, REFRESH_INTERVAL, TimeUnit.SECONDS);
175 * Method do following actions:
176 * 1. Install flow to direct ARP response packet to controller
177 * 2. Send ARP request packet out on all port of the given External network bridge.
178 * 3. Cache the flow that need to be removed once ARP resolution is done.
179 * 4. Return listenable future so that user can add callback to get the MacAddress
180 * @param externalNetworkBridgeDpid Broadcast ARP request packet on this bridge
181 * @param gatewayIp IP address for which MAC need to be resolved
182 * @param sourceIpAddress Source Ip address for the ARP request packet
183 * @param sourceMacAddress Source Mac address for the ARP request packet
184 * @param periodicRefresh Enable/Disable periodic refresh of the Gateway Mac address
185 * NOTE:Periodic refresh is not supported yet.
186 * @param gatewayIp Resolve MAC address of this Gateway Ip
187 * @return Future object
190 public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
191 final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh){
192 Preconditions.checkNotNull(sourceIpAddress);
193 Preconditions.checkNotNull(sourceMacAddress);
194 Preconditions.checkNotNull(gatewayIp);
196 LOG.info("Trigger Mac resolution for gateway {}, using source ip {} and mac {}",
197 gatewayIp.getValue(),sourceIpAddress.getValue(),sourceMacAddress.getValue());
200 if(gatewayToArpMetadataMap.containsKey(gatewayIp)){
201 if(gatewayToArpMetadataMap.get(gatewayIp).getGatewayMacAddress() != null){
202 return arpWatcherWall.submit(new Callable<MacAddress>(){
205 public MacAddress call() throws Exception {
206 return gatewayToArpMetadataMap.get(gatewayIp).getGatewayMacAddress();
211 gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(
212 externalNetworkBridgeDpid, gatewayIp,sourceIpAddress,sourceMacAddress,periodicRefresh));
216 final Node externalNetworkBridge = getExternalBridge(externalNetworkBridgeDpid);
217 if(externalNetworkBridge == null){
218 LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
219 + "is not connected to controller.",gatewayIp.getValue(),externalNetworkBridgeDpid );
223 sendGatewayArpRequest(externalNetworkBridge,gatewayIp,sourceIpAddress, sourceMacAddress);
225 //Wait for MacAddress population in cache
226 return waitForMacAddress(gatewayIp);
229 private Node getExternalBridge(final Long externalNetworkBridgeDpid){
230 final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
232 return getOpenFlowNode(nodeName);
235 private void sendGatewayArpRequest(final Node externalNetworkBridge,final Ipv4Address gatewayIp,
236 final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress){
237 final ArpMessageAddress senderAddress = new ArpMessageAddress(sourceMacAddress,sourceIpAddress);
239 //Build arp reply router flow
240 final Flow arpReplyToControllerFlow = createArpReplyToControllerFlow(senderAddress, gatewayIp);
242 final InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(Nodes.class)
243 .child(Node.class, externalNetworkBridge.getKey())
245 final InstanceIdentifier<Flow> flowIid = createFlowIid(arpReplyToControllerFlow, nodeIid);
246 final NodeRef nodeRef = new NodeRef(nodeIid);
249 Future<RpcResult<AddFlowOutput>> addFlowResult = flowService.addFlow(new AddFlowInputBuilder(
250 arpReplyToControllerFlow).setFlowRef(new FlowRef(flowIid)).setNode(nodeRef).build());
251 //wait for flow installation
252 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
253 new FutureCallback<RpcResult<AddFlowOutput>>() {
256 public void onSuccess(RpcResult<AddFlowOutput> result) {
257 if (!result.isSuccessful()) {
258 LOG.warn("Flow to route ARP Reply to Controller is not installed successfully : {} \nErrors: {}", flowIid,result.getErrors());
261 LOG.debug("Flow to route ARP Reply to Controller installed successfully : {}", flowIid);
264 gatewayToArpMetadataMap.get(gatewayIp).setFlowToRemove(
265 new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
267 //Broadcast ARP request packets
268 for (NodeConnector egressNc : externalNetworkBridge.getNodeConnector()) {
269 KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid = nodeIid.child(
270 NodeConnector.class, new NodeConnectorKey(egressNc.getId()));
271 ListenableFuture<RpcResult<Void>> futureSendArpResult = arpSender.sendArp(
272 senderAddress, gatewayIp, egressNcIid);
273 Futures.addCallback(futureSendArpResult, logResult(gatewayIp, egressNcIid));
278 public void onFailure(Throwable t) {
279 LOG.warn("ARP Reply to Controller flow was not created: {}", flowIid, t);
285 private ListenableFuture<MacAddress> waitForMacAddress(final Ipv4Address gatewayIp){
287 return arpWatcherWall.submit(new Callable<MacAddress>(){
290 public MacAddress call() throws Exception {
291 for(int cycle = 0;cycle < WAIT_CYCLES;cycle++){
292 //Sleep before checking mac address, so meanwhile ARP request packets
293 // will be broadcasted on the bridge.
294 Thread.sleep(PER_CYCLE_WAIT_DURATION);
295 ArpResolverMetadata arpResolverMetadata = gatewayToArpMetadataMap.get(gatewayIp);
296 if(arpResolverMetadata != null && arpResolverMetadata.getGatewayMacAddress() != null){
297 if(!arpResolverMetadata.isPeriodicRefresh()){
298 return gatewayToArpMetadataMap.remove(gatewayIp).getGatewayMacAddress();
300 return arpResolverMetadata.getGatewayMacAddress();
308 private static @Nullable Ipv4Address getIPv4Addresses(IpAddress ipAddress) {
309 if (ipAddress.getIpv4Address() == null) {
312 return ipAddress.getIpv4Address();
315 private Flow createArpReplyToControllerFlow(final ArpMessageAddress senderAddress, final Ipv4Address ipForRequestedMac) {
316 checkNotNull(senderAddress);
317 checkNotNull(ipForRequestedMac);
318 FlowBuilder arpFlow = new FlowBuilder().setTableId(TABEL_FOR_ARP_FLOW)
319 .setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME)
320 .setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY)
321 .setBufferId(OFConstants.OFP_NO_BUFFER)
324 .setCookie(new FlowCookie(BigInteger.valueOf(flowCookie.incrementAndGet())))
325 .setFlags(new FlowModFlags(false, false, false, false, false));
327 EthernetMatch ethernetMatch = ArpFlowFactory.createEthernetMatch(senderAddress.getHardwareAddress());
328 ArpMatch arpMatch = ArpFlowFactory.createArpMatch(senderAddress, ipForRequestedMac);
329 Match match = new MatchBuilder().setEthernetMatch(ethernetMatch).setLayer3Match(arpMatch).build();
330 arpFlow.setMatch(match);
331 arpFlow.setInstructions(new InstructionsBuilder().setInstruction(
332 ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build());
333 arpFlow.setId(createFlowId(senderAddress, ipForRequestedMac));
334 return arpFlow.build();
337 private FlowId createFlowId(ArpMessageAddress senderAddress, Ipv4Address ipForRequestedMac) {
338 String flowId = ARP_REPLY_TO_CONTROLLER_FLOW_NAME + "|" + ipForRequestedMac.getValue();
339 return new FlowId(flowId);
342 private static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) {
343 return nodeIid.builder()
344 .augmentation(FlowCapableNode.class)
345 .child(Table.class, new TableKey(flow.getTableId()))
346 .child(Flow.class, new FlowKey(flow.getId()))
350 private FutureCallback<RpcResult<Void>> logResult(final Ipv4Address tpa,
351 final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid) {
352 return new FutureCallback<RpcResult<Void>>() {
355 public void onSuccess(RpcResult<Void> result) {
356 LOG.debug("ARP Request for IP {} was sent from {}.", tpa.getValue(), egressNcIid);
360 public void onFailure(Throwable t) {
361 LOG.warn("ARP Request for IP {} was NOT sent from {}.", tpa.getValue(), egressNcIid);
367 public void onPacketReceived(PacketReceived potentialArp) {
368 Arp arp = ArpResolverUtils.getArpFrom(potentialArp);
370 if (arp.getOperation() != ArpOperation.REPLY.intValue()) {
371 LOG.trace("Packet is not ARP REPLY packet.");
374 if (LOG.isTraceEnabled()) {
375 LOG.trace("ARP REPLY received - {}", ArpUtils.getArpToStringFormat(arp));
377 NodeKey nodeKey = potentialArp.getIngress().getValue().firstKeyOf(Node.class, NodeKey.class);
378 if (nodeKey == null) {
379 LOG.info("Unknown source node of ARP packet: {}", potentialArp);
382 Ipv4Address gatewayIpAddress = ArpUtils.bytesToIp(arp.getSenderProtocolAddress());
383 MacAddress gatewayMacAddress = ArpUtils.bytesToMac(arp.getSenderHardwareAddress());
384 ArpResolverMetadata candidateGatewayIp = gatewayToArpMetadataMap.get(gatewayIpAddress);
385 if(candidateGatewayIp != null){
386 LOG.debug("Resolved MAC for Gateway Ip {} is {}",gatewayIpAddress.getValue(),gatewayMacAddress.getValue());
387 candidateGatewayIp.setGatewayMacAddress(gatewayMacAddress);
388 flowService.removeFlow(candidateGatewayIp.getFlowToRemove());
394 public void setDependencies(BundleContext bundleContext,
395 ServiceReference serviceReference) {
396 super.setDependencies(bundleContext.getServiceReference(GatewayMacResolver.class.getName()), this);
401 public void setDependencies(Object impl) {}
404 public void stopPeriodicRefresh(Ipv4Address gatewayIp) {
406 gatewayToArpMetadataMap.remove(gatewayIp);