2 * Copyright (c) 2015 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
9 package org.opendaylight.vpnservice.arputil.internal;
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static com.google.common.base.Preconditions.checkNotNull;
14 import java.math.BigInteger;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19 import java.util.concurrent.ExecutorService;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.Future;
23 import org.opendaylight.controller.liblldp.NetUtils;
24 import org.opendaylight.controller.liblldp.Packet;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
27 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
28 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
31 import org.opendaylight.vpnservice.mdsalutil.NWUtil;
32 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
33 import org.opendaylight.vpnservice.mdsalutil.packet.ARP;
34 import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.ArpRequestReceivedBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.ArpResponseReceivedBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.GetMacInput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.GetMacOutput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.GetMacOutputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.MacChangedBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.OdlArputilService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.SendArpRequestInput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.SendArpRequestInputBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.SendArpResponseInput;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.arputil.rev151126.interfaces.InterfaceAddress;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.BaseIds;
68 import org.opendaylight.yangtools.concepts.ListenerRegistration;
69 import org.opendaylight.yangtools.yang.binding.DataObject;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
72 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
73 import org.opendaylight.yangtools.yang.common.RpcResult;
74 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
78 import com.google.common.base.Optional;
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.SettableFuture;
84 public class ArpUtilImpl implements OdlArputilService,
85 PacketProcessingListener, AutoCloseable {
87 private static final String FAILED_TO_GET_SRC_IP_FOR_INTERFACE = "Failed to get src ip for %s";
89 private static final String FAILED_TO_GET_SRC_MAC_FOR_INTERFACE = "Failed to get src mac for interface %s iid %s ";
91 private static final String FAILED_TO_SEND_ARP_REQ_FOR_INTERFACE = "failed to send arp req for interface ";
93 private static final String UNKNOWN_IP_ADDRESS_SUPPLIED = "unknown ip address supplied";
95 private static final String NODE_CONNECTOR_NOT_FOUND_ERROR = "Node connector id not found for interface %s";
97 private static final String DPN_NOT_FOUND_ERROR = "dpn not found for interface %s ";
99 private static final short ARP_REQUEST_OP = (short) 1;
101 private static final short ARP_RESPONSE_OP = (short) 2;
103 private static final Logger LOGGER = LoggerFactory
104 .getLogger(ArpUtilImpl.class);
106 ExecutorService threadPool = Executors.newFixedThreadPool(1);
108 DataBroker dataBroker;
109 PacketProcessingService packetProcessingService;
110 NotificationPublishService notificationPublishService;
111 NotificationService notificationService;
112 IMdsalApiManager mdsalMgr;
114 ListenerRegistration<ArpUtilImpl> listenerRegistration;
116 ConcurrentMap<String, String> macsDB = new ConcurrentHashMap<>();
117 ConcurrentMap<String, SettableFuture<RpcResult<GetMacOutput>>> getMacFutures = new ConcurrentHashMap<>();
119 public ArpUtilImpl(DataBroker db,
120 PacketProcessingService packetProcessingService,
121 NotificationPublishService notificationPublishService,
122 NotificationService notificationService,
123 IMdsalApiManager mdsalApiManager) {
125 this.dataBroker = db;
126 this.packetProcessingService = packetProcessingService;
127 this.notificationPublishService = notificationPublishService;
128 this.mdsalMgr = mdsalApiManager;
129 this.notificationService = notificationService;
131 listenerRegistration = notificationService
132 .registerNotificationListener(this);
133 LOGGER.info("ArpUtil Manager Initialized ");
137 public void close() throws Exception {
138 listenerRegistration.close();
139 LOGGER.trace("ArpUtil manager Closed");
142 String getIpAddressInString(IpAddress ipAddress)
143 throws UnknownHostException {
144 return InetAddress.getByName(ipAddress.getIpv4Address().getValue())
148 public Future<RpcResult<GetMacOutput>> getMac(GetMacInput input) {
151 final String dstIpAddress = getIpAddressInString(input
153 if (LOGGER.isTraceEnabled()) {
154 LOGGER.trace("getMac rpc invoked for ip " + dstIpAddress);
156 if (getMacFutures.get(dstIpAddress) != null) {
157 if (LOGGER.isInfoEnabled()) {
158 LOGGER.info("get mac already in progress for the ip "
161 return getMacFutures.get(dstIpAddress);
163 SendArpRequestInputBuilder builder = new SendArpRequestInputBuilder()
164 .setInterfaceAddress(input.getInterfaceAddress())
165 .setIpaddress(input.getIpaddress());
166 Future<RpcResult<Void>> arpReqFt = sendArpRequest(builder.build());
167 final SettableFuture<RpcResult<GetMacOutput>> ft = SettableFuture
171 JdkFutureAdapters.listenInPoolThread(arpReqFt, threadPool),
172 new FutureCallback<RpcResult<Void>>() {
174 public void onFailure(Throwable e) {
175 RpcResultBuilder<GetMacOutput> resultBuilder = RpcResultBuilder
176 .<GetMacOutput> failed().withError(
177 ErrorType.APPLICATION,
179 ft.set(resultBuilder.build());
183 public void onSuccess(RpcResult<Void> result) {
184 LOGGER.trace("Successfully sent the arp pkt out for ip "
189 getMacFutures.put(dstIpAddress, ft);
191 } catch (Exception e) {
192 LOGGER.trace("failed to handle getMac request for {} {}",
193 input.getIpaddress(), e);
194 RpcResultBuilder<GetMacOutput> resultBuilder = RpcResultBuilder
195 .<GetMacOutput> failed().withError(ErrorType.APPLICATION,
197 return Futures.immediateFuture(resultBuilder.build());
201 byte[] getIpAddressBytes(IpAddress ip) throws UnknownHostException {
202 return InetAddress.getByName(ip.getIpv4Address().getValue())
207 public Future<RpcResult<Void>> sendArpRequest(
208 SendArpRequestInput arpReqInput) {
209 if (LOGGER.isTraceEnabled()) {
210 LOGGER.trace("rpc sendArpRequest invoked for ip "
211 + arpReqInput.getIpaddress());
216 String interfaceName = null;
218 byte[] dstIpBytes = null;
220 RpcResultBuilder<Void> failureBuilder = RpcResultBuilder
222 RpcResultBuilder<Void> successBuilder = RpcResultBuilder
226 dstIpBytes = getIpAddressBytes(arpReqInput.getIpaddress());
227 } catch (Exception e) {
228 failureBuilder.withError(ErrorType.APPLICATION,
229 UNKNOWN_IP_ADDRESS_SUPPLIED);
230 return Futures.immediateFuture(failureBuilder.build());
233 int localErrorCount = 0;
234 for (InterfaceAddress interfaceAddress : arpReqInput
235 .getInterfaceAddress()) {
237 interfaceName = interfaceAddress.getInterface();
238 srcIpBytes = getIpAddressBytes(interfaceAddress.getIpAddress());
240 NodeConnectorId id = getNodeConnectorFromDataStore(interfaceName);
242 dpnId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(id));
243 Long portid = MDSALUtil.getOfPortNumberFromPortName(id);
244 checkArgument(null != dpnId && BigInteger.ZERO != dpnId,
245 DPN_NOT_FOUND_ERROR, interfaceName);
247 NodeConnectorRef ref = MDSALUtil.getNodeConnRef(dpnId,
249 checkNotNull(ref, NODE_CONNECTOR_NOT_FOUND_ERROR, interfaceName);
251 if (LOGGER.isTraceEnabled()) {
253 "sendArpRequest received dpnId {} out interface {}",
254 dpnId, interfaceName);
256 byte srcMac[] = MDSALUtil.getMacAddressForNodeConnector(
258 (InstanceIdentifier<NodeConnector>) ref.getValue());
259 checkNotNull(srcMac, FAILED_TO_GET_SRC_MAC_FOR_INTERFACE,
260 interfaceName, ref.getValue());
261 checkNotNull(srcIpBytes, FAILED_TO_GET_SRC_IP_FOR_INTERFACE,
264 payload = ArpPacketUtil.getPayload(ARP_REQUEST_OP, srcMac,
265 srcIpBytes, ArpPacketUtil.EthernetDestination_Broadcast,
268 sendPacketOut(dpnId, payload, ref);
270 if (LOGGER.isTraceEnabled()) {
271 LOGGER.trace("sent arp request for "
272 + arpReqInput.getIpaddress());
274 } catch (Exception e) {
275 LOGGER.trace("failed to send arp req for {} on interface {}",
276 arpReqInput.getIpaddress(), interfaceName);
279 .withError(ErrorType.APPLICATION,
280 FAILED_TO_SEND_ARP_REQ_FOR_INTERFACE
283 .withError(ErrorType.APPLICATION,
284 FAILED_TO_SEND_ARP_REQ_FOR_INTERFACE
289 if (localErrorCount == arpReqInput.getInterfaceAddress().size()) {
290 // All the requests failed
291 return Futures.immediateFuture(failureBuilder.build());
293 return Futures.immediateFuture(successBuilder.build());
296 public Future<RpcResult<Void>> sendPacketOut(BigInteger dpnId,
297 byte[] payload, NodeConnectorRef ref) {
299 NodeConnectorRef nodeConnectorRef = MDSALUtil.getNodeConnRef(dpnId,
301 return packetProcessingService
302 .transmitPacket(new TransmitPacketInputBuilder()
305 new NodeRef(InstanceIdentifier
306 .builder(Nodes.class)
308 new NodeKey(new NodeId(
309 "openflow:" + dpnId)))
311 .setIngress(nodeConnectorRef).setEgress(ref).build());
315 public Future<RpcResult<Void>> sendArpResponse(SendArpResponseInput input) {
316 if (LOGGER.isTraceEnabled()) {
317 LOGGER.trace("sendArpResponse rpc invoked");
324 String interfaceName = input.getInterface();
325 NodeConnectorId id = getNodeConnectorFromDataStore(interfaceName);
327 dpnId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(id));
328 Long portid = MDSALUtil.getOfPortNumberFromPortName(id);
330 NodeConnectorRef ref = MDSALUtil.getNodeConnRef(dpnId,
332 checkArgument(null != dpnId && BigInteger.ZERO != dpnId,
333 DPN_NOT_FOUND_ERROR, interfaceName);
334 checkNotNull(ref, NODE_CONNECTOR_NOT_FOUND_ERROR, interfaceName);
336 if (LOGGER.isTraceEnabled()) {
338 "sendArpRequest received dpnId {} out interface {}",
339 dpnId, interfaceName);
342 byte[] srcIpBytes = getIpAddressBytes(input.getSrcIpAddress());
343 byte[] dstIpBytes = getIpAddressBytes(input.getIpaddress());
344 byte srcMac[] = MDSALUtil.getMacAddressForNodeConnector(dataBroker,
345 (InstanceIdentifier<NodeConnector>) ref.getValue());
346 byte[] dstMac = NWUtil.parseMacAddress(input.getMacaddress()
348 checkNotNull(srcIpBytes, FAILED_TO_GET_SRC_IP_FOR_INTERFACE,
350 payload = ArpPacketUtil.getPayload(ARP_RESPONSE_OP, srcMac, srcIpBytes,
353 sendPacketOut(dpnId, payload, ref);
354 if (LOGGER.isTraceEnabled()) {
355 LOGGER.trace("sent the arp response for "
356 + input.getSrcIpAddress());
358 } catch (Exception e) {
359 LOGGER.trace("failed to send arp response for {} {}",
360 input.getSrcIpAddress(), e);
361 return RpcResultBuilder.<Void> failed()
362 .withError(ErrorType.APPLICATION, e.getMessage(), e)
365 RpcResultBuilder<Void> rpcResultBuilder = RpcResultBuilder.success();
366 return Futures.immediateFuture(rpcResultBuilder.build());
370 public void onPacketReceived(PacketReceived packetReceived) {
371 Class<? extends PacketInReason> pktInReason = packetReceived
372 .getPacketInReason();
373 if (LOGGER.isTraceEnabled()) {
374 LOGGER.trace("Packet Received {}", packetReceived);
377 if (pktInReason == SendToController.class) {
380 int tableId = packetReceived.getTableId().getValue();
382 byte[] data = packetReceived.getPayload();
383 Ethernet ethernet = new Ethernet();
385 ethernet.deserialize(data, 0, data.length
386 * NetUtils.NumBitsInAByte);
387 if (ethernet.getEtherType() != ARP_REQUEST_OP
388 && ethernet.getEtherType() != ARP_REQUEST_OP) {
392 Packet pkt = ethernet.getPayload();
394 InetAddress srcInetAddr = InetAddress.getByAddress(arp
395 .getSenderProtocolAddress());
396 InetAddress dstInetAddr = InetAddress.getByAddress(arp
397 .getTargetProtocolAddress());
398 byte[] srcMac = ethernet.getSourceMACAddress();
400 NodeConnectorRef ref = packetReceived.getIngress();
402 String interfaceName = MDSALUtil.getInterfaceName(ref, dataBroker);
404 checkAndFireMacChangedNotification(interfaceName, srcInetAddr,
407 macsDB.put(interfaceName + "-" + srcInetAddr.getHostAddress(),
408 NWUtil.toStringMacAddress(srcMac));
410 if (arp.getOpCode() == ARP_REQUEST_OP) {
411 fireArpReqRecvdNotification(interfaceName, srcInetAddr,
412 srcMac, dstInetAddr, tableId);
414 fireArpRespRecvdNotification(interfaceName, srcInetAddr,
417 if (getMacFutures.get(srcInetAddr.getHostAddress()) != null) {
418 threadPool.submit(new MacResponderTask(arp));
421 } catch (Exception e) {
422 LOGGER.trace("Failed to decode packet: {}", e);
427 class MacResponderTask implements Runnable {
430 MacResponderTask(ARP arp) {
437 GetMacOutputBuilder outputBuilder;
439 SettableFuture<RpcResult<GetMacOutput>> future = null;
440 RpcResultBuilder<GetMacOutput> resultBuilder;
442 srcAddr = InetAddress.getByAddress(arp
443 .getSenderProtocolAddress());
444 srcMac = NWUtil.toStringMacAddress(arp
445 .getSenderHardwareAddress());
446 future = getMacFutures.remove(srcAddr.getHostAddress());
447 if (future == null) {
448 LOGGER.trace("There are no pending mac requests.");
451 outputBuilder = new GetMacOutputBuilder()
452 .setMacaddress(new PhysAddress(srcMac));
453 resultBuilder = RpcResultBuilder.success(outputBuilder.build());
454 if (LOGGER.isTraceEnabled()) {
455 LOGGER.trace("sent the mac response for ip {}",
456 srcAddr.getHostAddress());
458 } catch (Exception e) {
459 LOGGER.trace("failed to send mac response {} ", e);
460 resultBuilder = RpcResultBuilder.<GetMacOutput> failed()
461 .withError(ErrorType.APPLICATION, e.getMessage(), e);
463 future.set(resultBuilder.build());
467 private void fireArpRespRecvdNotification(String interfaceName,
468 InetAddress inetAddr, byte[] macAddressBytes, int tableId)
469 throws InterruptedException {
471 IpAddress ip = new IpAddress(inetAddr.getHostAddress().toCharArray());
472 String macAddress = NWUtil.toStringMacAddress(macAddressBytes);
473 PhysAddress mac = new PhysAddress(macAddress);
474 ArpResponseReceivedBuilder builder = new ArpResponseReceivedBuilder();
475 builder.setInterface(interfaceName);
476 builder.setIpaddress(ip);
477 builder.setOfTableId((long) tableId);
478 builder.setMacaddress(mac);
479 notificationPublishService.putNotification(builder.build());
482 private void fireArpReqRecvdNotification(String interfaceName,
483 InetAddress srcInetAddr, byte[] srcMac, InetAddress dstInetAddr,
484 int tableId) throws InterruptedException {
485 String macAddress = NWUtil.toStringMacAddress(srcMac);
486 ArpRequestReceivedBuilder builder = new ArpRequestReceivedBuilder();
487 builder.setInterface(interfaceName);
488 builder.setOfTableId((long) tableId);
489 builder.setSrcIpaddress(new IpAddress(srcInetAddr.getHostAddress()
491 builder.setDstIpaddress(new IpAddress(dstInetAddr.getHostAddress()
493 builder.setSrcMac(new PhysAddress(macAddress));
494 notificationPublishService.putNotification(builder.build());
497 private void checkAndFireMacChangedNotification(String interfaceName,
498 InetAddress inetAddr, byte[] macAddressBytes)
499 throws InterruptedException {
501 IpAddress ip = new IpAddress(inetAddr.getHostAddress().toCharArray());
502 String macAddress = NWUtil.toStringMacAddress(macAddressBytes);
503 PhysAddress mac = new PhysAddress(macAddress);
505 if (!macAddress.equals(macsDB.get(interfaceName + "-"
506 + inetAddr.getHostAddress()))) {
507 if (LOGGER.isTraceEnabled()) {
508 LOGGER.trace("mac address changed for " + inetAddr);
510 MacChangedBuilder builder = new MacChangedBuilder();
511 builder.setInterface(interfaceName);
512 builder.setIpaddress(ip);
513 builder.setMacaddress(mac);
514 notificationPublishService.putNotification(builder.build());
518 private InstanceIdentifier<Interface> buildInterfaceId(String interfaceName) {
519 InstanceIdentifierBuilder<Interface> idBuilder = InstanceIdentifier
520 .builder(Interfaces.class).child(Interface.class,
521 new InterfaceKey(interfaceName));
522 InstanceIdentifier<Interface> id = idBuilder.build();
527 private NodeConnectorId getNodeConnectorFromDataStore(String interfaceName) {
528 InstanceIdentifier<Interface> id = buildInterfaceId(interfaceName);
529 Optional<Interface> interf = MDSALUtil.read(dataBroker,
530 LogicalDatastoreType.CONFIGURATION,
532 if (interf.isPresent()) {
533 return interf.get().getAugmentation(BaseIds.class).getOfPortId();