/*
* 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.concurrent.Future;
import org.opendaylight.controller.liblldp.EtherTypes;
import org.opendaylight.controller.liblldp.Ethernet;
import org.opendaylight.controller.liblldp.NetUtils;
import org.opendaylight.controller.liblldp.PacketException;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
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.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.packet.service.rev130709.PacketProcessingService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
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.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Uses packet-out for sending ARP Requests.
*/
public class ArpSender {
private static final Logger LOG = LoggerFactory.getLogger(ArpSender.class);
private static final String OFPP_ALL = "0xfffffffc";
private final PacketProcessingService packetProcessingService;
public ArpSender(PacketProcessingService packetProcessingService) {
this.packetProcessingService = checkNotNull(packetProcessingService);
}
/**
* Sends ARP Request as packet-out from all openflow physical ports on the given node.
*
* @param senderAddress the addresses used in sender part of ARP packet
* @param tpa the target protocol address, in this case IPv4 address for which MAC should be
* discovered
* @param nodeIid the path to node from where the ARP packet will be flooded
* @return future result about success of packet-out
*/
public ListenableFuture<RpcResult<Void>> floodArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
InstanceIdentifier<Node> nodeIid) {
checkNotNull(senderAddress);
checkNotNull(tpa);
checkNotNull(nodeIid);
// node connector representing all physical ports on node
NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(createNodeConnectorId(OFPP_ALL,
nodeIid.firstKeyOf(Node.class, NodeKey.class).getId()));
InstanceIdentifier<NodeConnector> egressNc = nodeIid.child(NodeConnector.class, nodeConnectorKey);
return sendArp(senderAddress, tpa, egressNc);
}
private NodeConnectorId createNodeConnectorId(String connectorId, NodeId nodeId) {
StringBuilder stringId = new StringBuilder(nodeId.getValue()).append(":").append(connectorId);
return new NodeConnectorId(stringId.toString());
}
/**
* Sends ARP Request as packet-out from the given port (node connector).
*
* @param senderAddress the addresses used in sender part of ARP packet
* @param tpa the target protocol address, in this case IPv4 address for which MAC should be
* discovered
* @param egressNc the path to node connector from where the ARP packet will be sent
* @return future result about success of packet-out
*/
public ListenableFuture<RpcResult<Void>> sendArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
InstanceIdentifier<NodeConnector> egressNc) {
checkNotNull(senderAddress);
checkNotNull(tpa);
checkNotNull(egressNc);
final Ethernet arpFrame = createArpFrame(senderAddress, tpa);
byte[] arpFrameAsBytes;
try {
arpFrameAsBytes = arpFrame.serialize();
} catch (PacketException e) {
LOG.warn("Serializition of ARP packet is not successful.", e);
if (LOG.isDebugEnabled()) {
LOG.debug("ARP packet: {}", ArpUtils.getArpFrameToStringFormat(arpFrame));
}
return Futures.immediateFailedFuture(e);
}
// Generate packet with destination switch and port
TransmitPacketInput packet = new TransmitPacketInputBuilder().setEgress(new NodeConnectorRef(egressNc))
.setNode(new NodeRef(egressNc.firstIdentifierOf(Node.class)))
.setPayload(arpFrameAsBytes)
.build();
if (LOG.isTraceEnabled()) {
LOG.trace("Sending ARP REQUEST \n{}", ArpUtils.getArpFrameToStringFormat(arpFrame));
}
Future<RpcResult<Void>> futureTransmitPacketResult = packetProcessingService.transmitPacket(packet);
return JdkFutureAdapters.listenInPoolThread(futureTransmitPacketResult);
}
private Ethernet createArpFrame(ArpMessageAddress senderAddress, Ipv4Address tpa) {
byte[] senderMac = ArpUtils.macToBytes(senderAddress.getHardwareAddress());
byte[] senderIp = ArpUtils.ipToBytes(senderAddress.getProtocolAddress());
byte[] targetMac = NetUtils.getBroadcastMACAddr();
byte[] targetIp = ArpUtils.ipToBytes(tpa);
Ethernet arpFrame = new Ethernet().setSourceMACAddress(senderMac)
.setDestinationMACAddress(targetMac)
.setEtherType(EtherTypes.ARP.shortValue());
Arp arp = new Arp().setOperation(ArpOperation.REQUEST.intValue())
.setSenderHardwareAddress(senderMac)
.setSenderProtocolAddress(senderIp)
.setTargetHardwareAddress(targetMac)
.setTargetProtocolAddress(targetIp);
arpFrame.setPayload(arp);
return arpFrame;
}
}