Jonathan Hart
Committed by Gerrit Code Review

Remove deprecated openflow host and link providers

Change-Id: I9dbaa28a70998b6265a178bb459a56221f458b75
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-of-providers</artifactId>
<version>1.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-of-provider-host</artifactId>
<packaging>bundle</packaging>
<description>ONOS OpenFlow protocol host provider</description>
</project>
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.of.host.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IpAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowController;
import org.onosproject.openflow.controller.OpenFlowPacketContext;
import org.onosproject.openflow.controller.PacketListener;
import org.slf4j.Logger;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
* end-station hosts.
*/
@Component(immediate = true)
@Deprecated
public class OpenFlowHostProvider extends AbstractProvider implements HostProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenFlowController controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
private HostProviderService providerService;
private final InternalHostProvider listener = new InternalHostProvider();
private boolean ipLearn = true;
/**
* Creates an OpenFlow host provider.
*/
public OpenFlowHostProvider() {
super(new ProviderId("of", "org.onosproject.provider.openflow"));
}
@Activate
public void activate() {
providerService = providerRegistry.register(this);
controller.addPacketListener(10, listener);
log.info("Started");
}
@Deactivate
public void deactivate() {
providerRegistry.unregister(this);
controller.removePacketListener(listener);
providerService = null;
log.info("Stopped");
}
@Override
public void triggerProbe(Host host) {
log.info("Triggering probe on device {}", host);
}
private class InternalHostProvider implements PacketListener {
@Override
public void handlePacket(OpenFlowPacketContext pktCtx) {
Ethernet eth = pktCtx.parsed();
if (eth == null) {
return;
}
VlanId vlan = VlanId.vlanId(eth.getVlanID());
ConnectPoint heardOn = new ConnectPoint(deviceId(Dpid.uri(pktCtx.dpid())),
portNumber(pktCtx.inPort()));
// If this is not an edge port, bail out.
Topology topology = topologyService.currentTopology();
if (topologyService.isInfrastructure(topology, heardOn)) {
return;
}
HostLocation hloc = new HostLocation(deviceId(Dpid.uri(pktCtx.dpid())),
portNumber(pktCtx.inPort()),
System.currentTimeMillis());
HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
// Potentially a new or moved host
if (eth.getEtherType() == Ethernet.TYPE_ARP) {
ARP arp = (ARP) eth.getPayload();
IpAddress ip =
IpAddress.valueOf(IpAddress.Version.INET,
arp.getSenderProtocolAddress());
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
providerService.hostDetected(hid, hdescr);
} else if (ipLearn && eth.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 pip = (IPv4) eth.getPayload();
IpAddress ip =
IpAddress.valueOf(pip.getSourceAddress());
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
providerService.hostDetected(hid, hdescr);
}
}
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provider that uses OpenFlow controller as a means of host discovery and tracking.
*/
package org.onosproject.provider.of.host.impl;
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.of.host.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.provider.AbstractProviderService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyServiceAdapter;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowPacketContext;
import org.onosproject.openflow.controller.OpenflowControllerAdapter;
import org.onosproject.openflow.controller.PacketListener;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.types.OFPort;
public class OpenFlowHostProviderTest {
private static final Integer INPORT = 10;
private static final Dpid DPID1 = new Dpid(100);
private static final Dpid DPID2 = new Dpid(200);
private static final Dpid DPID3 = new Dpid(300);
private static final VlanId VLAN = VlanId.vlanId();
private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
private static final byte[] IP = new byte[]{10, 0, 0, 1};
private final OpenFlowHostProvider provider = new OpenFlowHostProvider();
private final TestHostRegistry hostService = new TestHostRegistry();
private final TestController controller = new TestController();
private final TestTopologyService topoService = new TestTopologyService();
private TestHostProviderService providerService;
@Before
public void setUp() {
provider.providerRegistry = hostService;
provider.controller = controller;
provider.topologyService = topoService;
provider.activate();
}
@Test
public void basics() {
assertNotNull("registration expected", providerService);
assertEquals("incorrect provider", provider, providerService.provider());
}
@Test
public void events() {
// new host
controller.processPacket(DPID1, null);
assertNotNull("new host expected", providerService.added);
assertNull("host motion unexpected", providerService.moved);
// the host moved to new switch
controller.processPacket(DPID2, null);
assertNotNull("host motion expected", providerService.moved);
// the host was misheard on a spine
controller.processPacket(DPID3, null);
assertNull("host misheard on spine switch", providerService.spine);
}
@After
public void tearDown() {
provider.deactivate();
provider.providerRegistry = null;
provider.controller = null;
}
private class TestHostRegistry implements HostProviderRegistry {
@Override
public HostProviderService register(HostProvider provider) {
providerService = new TestHostProviderService(provider);
return providerService;
}
@Override
public void unregister(HostProvider provider) {
}
@Override
public Set<ProviderId> getProviders() {
return null;
}
}
private class TestHostProviderService
extends AbstractProviderService<HostProvider>
implements HostProviderService {
Dpid added = null;
Dpid moved = null;
Dpid spine = null;
protected TestHostProviderService(HostProvider provider) {
super(provider);
}
@Override
public void hostDetected(HostId hostId, HostDescription hostDescription) {
Dpid descr = Dpid.dpid(hostDescription.location().deviceId().uri());
if (added == null) {
added = descr;
} else if ((moved == null) && !descr.equals(added)) {
moved = descr;
} else {
spine = descr;
}
}
@Override
public void hostVanished(HostId hostId) {
}
}
private class TestController extends OpenflowControllerAdapter {
PacketListener pktListener;
@Override
public void addPacketListener(int priority, PacketListener listener) {
pktListener = listener;
}
@Override
public void processPacket(Dpid dpid, OFMessage msg) {
OpenFlowPacketContext ctx = new TestPacketContext(dpid);
pktListener.handlePacket(ctx);
}
}
private class TestTopologyService extends TopologyServiceAdapter {
@Override
public boolean isInfrastructure(Topology topology,
ConnectPoint connectPoint) {
//simulate DPID3 as an infrastructure switch
if (Dpid.dpid(connectPoint.deviceId().uri()).equals(DPID3)) {
return true;
}
return false;
}
}
private class TestPacketContext implements OpenFlowPacketContext {
protected Dpid swid;
public TestPacketContext(Dpid dpid) {
swid = dpid;
}
@Override
public boolean block() {
return false;
}
@Override
public void send() {
}
@Override
public void build(OFPort outPort) {
}
@Override
public void build(Ethernet ethFrame, OFPort outPort) {
}
@Override
public Ethernet parsed() {
// just things we (and serializers) need
ARP arp = new ARP();
arp.setSenderProtocolAddress(IP)
.setSenderHardwareAddress(MAC.toBytes())
.setTargetHardwareAddress(BCMAC.toBytes())
.setTargetProtocolAddress(IP);
Ethernet eth = new Ethernet();
eth.setEtherType(Ethernet.TYPE_ARP)
.setVlanID(VLAN.toShort())
.setSourceMACAddress(MAC)
.setDestinationMACAddress(BCMAC)
.setPayload(arp);
return eth;
}
@Override
public byte[] unparsed() {
return null;
}
@Override
public Dpid dpid() {
return swid;
}
@Override
public Integer inPort() {
return INPORT;
}
@Override
public boolean isHandled() {
return false;
}
@Override
public boolean isBuffered() {
return false;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-of-providers</artifactId>
<version>1.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-of-provider-link</artifactId>
<packaging>bundle</packaging>
<description>ONOS OpenFlow protocol link provider</description>
</project>
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.of.link.impl;
import static org.onosproject.openflow.controller.Dpid.uri;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link.Type;
import org.onosproject.net.PortNumber;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowController;
import org.onosproject.openflow.controller.OpenFlowSwitch;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONLabLddp;
import org.onlab.packet.ONLabLddp.DPIDandPort;
import org.onlab.util.Timer;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.slf4j.Logger;
/**
* Run discovery process from a physical switch. Ports are initially labeled as
* slow ports. When an LLDP is successfully received, label the remote port as
* fast. Every probeRate milliseconds, loop over all fast ports and send an
* LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
* discovery implementation.
*/
@Deprecated
public class LinkDiscovery implements TimerTask {
private final OpenFlowSwitch sw;
// send 1 probe every probeRate milliseconds
private final long probeRate;
private final Set<Integer> slowPorts;
private final Set<Integer> fastPorts;
// number of unacknowledged probes per port
private final Map<Integer, AtomicInteger> portProbeCount;
// number of probes to send before link is removed
private static final short MAX_PROBE_COUNT = 3;
private Iterator<Integer> slowIterator;
private final OFFactory ofFactory;
private final Logger log = getLogger(getClass());
private final ONLabLddp lldpPacket;
private final Ethernet ethPacket;
private Ethernet bddpEth;
private final boolean useBDDP;
private final OpenFlowController ctrl;
private final LinkProviderService linkProvider;
protected final Map<Integer, OFPortDesc> ports;
private Timeout timeout;
/*
* Instantiates discovery manager for the given physical switch. Creates a
* generic LLDP packet that will be customized for the port it is sent out on.
* Starts the the timer for the discovery process.
*
* @param sw the physical switch
* @param useBDDP flag to also use BDDP for discovery
*/
public LinkDiscovery(final OpenFlowSwitch sw,
OpenFlowController ctrl, LinkProviderService providerService, Boolean... useBDDP) {
this.sw = sw;
this.ofFactory = sw.factory();
this.ctrl = ctrl;
this.probeRate = 3000;
this.linkProvider = providerService;
this.slowPorts = Collections.synchronizedSet(new HashSet<Integer>());
this.fastPorts = Collections.synchronizedSet(new HashSet<Integer>());
this.ports = new ConcurrentHashMap<>();
this.portProbeCount = new HashMap<Integer, AtomicInteger>();
this.lldpPacket = new ONLabLddp();
this.lldpPacket.setSwitch(this.sw.getId());
this.ethPacket = new Ethernet();
this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
this.ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
this.ethPacket.setPayload(this.lldpPacket);
this.ethPacket.setPad(true);
this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
if (this.useBDDP) {
this.bddpEth = new Ethernet();
this.bddpEth.setPayload(this.lldpPacket);
this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
this.bddpEth.setDestinationMACAddress(ONLabLddp.BDDP_MULTICAST);
this.bddpEth.setPad(true);
log.info("Using BDDP to discover network");
}
for (OFPortDesc port : sw.getPorts()) {
if (port.getPortNo() != OFPort.LOCAL) {
addPort(port);
}
}
timeout = Timer.getTimer().newTimeout(this, 0,
TimeUnit.MILLISECONDS);
this.log.info("Started discovery manager for switch {}",
sw.getId());
}
/**
* Add physical port port to discovery process.
* Send out initial LLDP and label it as slow port.
*
* @param port the port
*/
public void addPort(final OFPortDesc port) {
// Ignore ports that are not on this switch, or already booted. */
this.ports.put(port.getPortNo().getPortNumber(), port);
synchronized (this) {
this.log.debug("sending init probe to port {}",
port.getPortNo().getPortNumber());
OFPacketOut pkt;
pkt = this.createLLDPPacketOut(port);
this.sw.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sw.sendMsg(bpkt);
}
this.slowPorts.add(port.getPortNo().getPortNumber());
}
}
/**
* Removes physical port from discovery process.
*
* @param port the port
*/
public void removePort(final OFPortDesc port) {
// Ignore ports that are not on this switch
int portnum = port.getPortNo().getPortNumber();
this.ports.remove(portnum);
synchronized (this) {
if (this.slowPorts.contains(portnum)) {
this.slowPorts.remove(portnum);
} else if (this.fastPorts.contains(portnum)) {
this.fastPorts.remove(portnum);
this.portProbeCount.remove(portnum);
// no iterator to update
} else {
this.log.warn(
"tried to dynamically remove non-existing port {}",
portnum);
}
}
ConnectPoint cp = new ConnectPoint(
DeviceId.deviceId(uri(sw.getId())),
PortNumber.portNumber(port.getPortNo().getPortNumber()));
linkProvider.linksVanished(cp);
}
/**
* Method called by remote port to acknowledge receipt of LLDP sent by
* this port. If slow port, updates label to fast. If fast port, decrements
* number of unacknowledged probes.
*
* @param port the port
*/
public void ackProbe(final Integer port) {
final int portNumber = port;
synchronized (this) {
if (this.slowPorts.contains(portNumber)) {
this.log.debug("Setting slow port to fast: {}:{}",
this.sw.getId(), portNumber);
this.slowPorts.remove(portNumber);
this.fastPorts.add(portNumber);
this.portProbeCount.put(portNumber, new AtomicInteger(0));
} else {
if (this.fastPorts.contains(portNumber)) {
this.portProbeCount.get(portNumber).set(0);
} else {
this.log.debug(
"Got ackProbe for non-existing port: {}",
portNumber);
}
}
}
}
/**
* Creates packet_out LLDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
*/
private OFPacketOut createLLDPPacketOut(final OFPortDesc port) {
if (port == null) {
return null;
}
OFPacketOut.Builder packetOut = this.ofFactory.buildPacketOut();
packetOut.setBufferId(OFBufferId.NO_BUFFER);
OFAction act = this.ofFactory.actions().buildOutput()
.setPort(port.getPortNo()).build();
packetOut.setActions(Collections.singletonList(act));
this.lldpPacket.setPort(port.getPortNo().getPortNumber());
this.ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
final byte[] lldp = this.ethPacket.serialize();
packetOut.setData(lldp);
return packetOut.build();
}
/**
* Creates packet_out BDDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
*/
private OFPacketOut createBDDPPacketOut(final OFPortDesc port) {
if (port == null) {
return null;
}
OFPacketOut.Builder packetOut = sw.factory().buildPacketOut();
packetOut.setBufferId(OFBufferId.NO_BUFFER);
OFActionOutput.Builder act = sw.factory().actions().buildOutput()
.setPort(port.getPortNo());
OFAction out = act.build();
packetOut.setActions(Collections.singletonList(out));
this.lldpPacket.setPort(port.getPortNo().getPortNumber());
this.bddpEth.setSourceMACAddress(port.getHwAddr().getBytes());
final byte[] bddp = this.bddpEth.serialize();
packetOut.setData(bddp);
return packetOut.build();
}
private void sendMsg(final OFMessage msg) {
if (msg == null) {
return;
}
this.sw.sendMsg(msg);
}
public String getName() {
return "LinkDiscovery " + this.sw.getStringId();
}
/*
* Handles an incoming LLDP packet. Creates link in topology and sends ACK
* to port where LLDP originated.
*/
public boolean handleLLDP(final byte[] pkt, Integer inPort) {
short ethType = ONLabLddp.isOVXLLDP(pkt);
if (ethType == Ethernet.TYPE_LLDP || ethType == Ethernet.TYPE_BSN) {
final Integer dstPort = inPort;
final DPIDandPort dp = ONLabLddp.parseLLDP(pkt);
final OpenFlowSwitch srcSwitch = ctrl.getSwitch(new Dpid(dp.getDpid()));
final Integer srcPort = dp.getPort();
if (srcSwitch == null) {
return true;
}
this.ackProbe(srcPort);
ConnectPoint src = new ConnectPoint(
DeviceId.deviceId(uri(srcSwitch.getId())),
PortNumber.portNumber(srcPort));
ConnectPoint dst = new ConnectPoint(
DeviceId.deviceId(uri(sw.getId())),
PortNumber.portNumber(dstPort));
LinkDescription ld;
if (ethType == Ethernet.TYPE_BSN) {
ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
} else {
ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
}
linkProvider.linkDetected(ld);
return true;
} else {
this.log.debug("Ignoring unknown LLDP");
return false;
}
}
private OFPortDesc findPort(Integer inPort) {
return ports.get(inPort);
}
/**
* Execute this method every t milliseconds. Loops over all ports
* labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
* port.
*
* @param t timeout
*/
@Override
public void run(final Timeout t) {
this.log.debug("sending probes");
synchronized (this) {
final Iterator<Integer> fastIterator = this.fastPorts.iterator();
while (fastIterator.hasNext()) {
final Integer portNumber = fastIterator.next();
OFPortDesc port = findPort(portNumber);
if (port == null) {
// port can be null
// #removePort modifies `ports` outside synchronized block
continue;
}
final int probeCount = this.portProbeCount.get(portNumber)
.getAndIncrement();
if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
this.log.debug("sending fast probe to port");
OFPacketOut pkt = this.createLLDPPacketOut(port);
this.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sendMsg(bpkt);
}
} else {
// Update fast and slow ports
fastIterator.remove();
this.slowPorts.add(portNumber);
this.portProbeCount.remove(portNumber);
// Remove link from topology
final OFPortDesc srcPort = port;
ConnectPoint cp = new ConnectPoint(
DeviceId.deviceId(uri(sw.getId())),
PortNumber.portNumber(srcPort.getPortNo().getPortNumber()));
linkProvider.linksVanished(cp);
}
}
// send a probe for the next slow port
if (!this.slowPorts.isEmpty()) {
this.slowIterator = this.slowPorts.iterator();
while (this.slowIterator.hasNext()) {
final int portNumber = this.slowIterator.next();
this.log.debug("sending slow probe to port {}", portNumber);
OFPortDesc port = findPort(portNumber);
OFPacketOut pkt = this.createLLDPPacketOut(port);
this.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sendMsg(bpkt);
}
}
}
}
// reschedule timer
timeout = Timer.getTimer().newTimeout(this, this.probeRate,
TimeUnit.MILLISECONDS);
}
public void removeAllPorts() {
for (OFPortDesc port : ports.values()) {
removePort(port);
}
}
public void stop() {
timeout.cancel();
removeAllPorts();
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.of.link.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.net.DeviceId;
import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowController;
import org.onosproject.openflow.controller.OpenFlowPacketContext;
import org.onosproject.openflow.controller.OpenFlowSwitch;
import org.onosproject.openflow.controller.OpenFlowSwitchListener;
import org.onosproject.openflow.controller.PacketListener;
import org.onosproject.openflow.controller.RoleState;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortState;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.slf4j.Logger;
/**
* Provider which uses an OpenFlow controller to detect network
* infrastructure links.
*/
@Component(immediate = true)
@Deprecated
public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenFlowController controller;
private LinkProviderService providerService;
private final boolean useBDDP = true;
private final InternalLinkProvider listener = new InternalLinkProvider();
protected final Map<Dpid, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
/**
* Creates an OpenFlow link provider.
*/
public OpenFlowLinkProvider() {
super(new ProviderId("of", "org.onosproject.provider.openflow"));
}
@Activate
public void activate() {
providerService = providerRegistry.register(this);
controller.addListener(listener);
controller.addPacketListener(0, listener);
for (OpenFlowSwitch sw : controller.getSwitches()) {
listener.switchAdded(new Dpid(sw.getId()));
}
log.info("Started");
}
@Deactivate
public void deactivate() {
for (LinkDiscovery ld : discoverers.values()) {
ld.stop();
}
providerRegistry.unregister(this);
controller.removeListener(listener);
controller.removePacketListener(listener);
providerService = null;
log.info("Stopped");
}
private class InternalLinkProvider implements PacketListener, OpenFlowSwitchListener {
@Override
public void handlePacket(OpenFlowPacketContext pktCtx) {
LinkDiscovery ld = discoverers.get(pktCtx.dpid());
if (ld == null) {
return;
}
if (ld.handleLLDP(pktCtx.unparsed(), pktCtx.inPort())) {
pktCtx.block();
}
}
@Override
public void switchAdded(Dpid dpid) {
discoverers.put(dpid, new LinkDiscovery(controller.getSwitch(dpid),
controller, providerService, useBDDP));
}
@Override
public void switchRemoved(Dpid dpid) {
LinkDiscovery ld = discoverers.remove(dpid);
if (ld != null) {
ld.removeAllPorts();
}
providerService.linksVanished(
DeviceId.deviceId("of:" + Long.toHexString(dpid.value())));
}
@Override
public void switchChanged(Dpid dpid) {
//might not need to do anything since DeviceManager is notified
}
@Override
public void portChanged(Dpid dpid, OFPortStatus status) {
LinkDiscovery ld = discoverers.get(dpid);
if (ld == null) {
return;
}
final OFPortDesc port = status.getDesc();
final boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) &&
!port.getConfig().contains(OFPortConfig.PORT_DOWN);
if (enabled) {
ld.addPort(port);
} else {
/*
* remove port calls linkVanished
*/
ld.removePort(port);
}
}
@Override
public void receivedRoleReply(Dpid dpid, RoleState requested,
RoleState response) {
// do nothing for this.
}
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provider that uses OpenFlow controller as a means of infrastructure link inference.
*/
package org.onosproject.provider.of.link.impl;
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.of.link.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.provider.AbstractProviderService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowPacketContext;
import org.onosproject.openflow.controller.OpenFlowSwitch;
import org.onosproject.openflow.controller.OpenFlowSwitchListener;
import org.onosproject.openflow.controller.OpenflowControllerAdapter;
import org.onosproject.openflow.controller.PacketListener;
import org.onosproject.openflow.controller.RoleState;
import org.onosproject.openflow.controller.OpenFlowSwitch.TableType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONLabLddp;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortReason;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.TableId;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class OpenFlowLinkProviderTest {
private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001");
private static final DeviceId DID2 = DeviceId.deviceId("of:0000000000000002");
private static final Dpid DPID2 = Dpid.dpid(DID2.uri());
private static final Dpid DPID1 = Dpid.dpid(DID1.uri());
private static final OFPortDesc PD1 = portDesc(1, true);
private static final OFPortDesc PD2 = portDesc(2, true);
private static final OFPortDesc PD3 = portDesc(1, true);
private static final OFPortDesc PD4 = portDesc(2, true);
private static final List<OFPortDesc> PLIST1 = Lists.newArrayList(PD1, PD2);
private static final List<OFPortDesc> PLIST2 = Lists.newArrayList(PD3, PD4);
private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch(DPID1, PLIST1);
private static final TestOpenFlowSwitch SW2 = new TestOpenFlowSwitch(DPID2, PLIST2);
private final OpenFlowLinkProvider provider = new OpenFlowLinkProvider();
private final TestLinkRegistry linkService = new TestLinkRegistry();
private final TestController controller = new TestController();
private TestLinkProviderService providerService;
private TestPacketContext pktCtx;
@Before
public void setUp() {
pktCtx = new TestPacketContext(DPID2);
provider.providerRegistry = linkService;
controller.switchMap.put(DPID1, SW1);
controller.switchMap.put(DPID2, SW2);
provider.controller = controller;
provider.activate();
}
@Test
public void basics() {
assertNotNull("registration expected", providerService);
assertEquals("incorrect provider", provider, providerService.provider());
}
@Test
public void switchAdd() {
controller.listener.switchAdded(DPID1);
assertFalse("Device not added", provider.discoverers.isEmpty());
}
@Test
public void switchRemove() {
controller.listener.switchAdded(DPID1);
controller.listener.switchRemoved(DPID1);
assertTrue("Discoverer is not gone", provider.discoverers.isEmpty());
assertTrue("Device is not gone.", vanishedDpid(DPID1));
}
@Test
public void portUp() {
controller.listener.switchAdded(DPID1);
controller.listener.portChanged(DPID1, portStatus(true, 3));
assertTrue("Port not added to discoverer",
provider.discoverers.get(DPID1).ports.containsKey(3));
}
@Test
public void portDown() {
controller.listener.switchAdded(DPID1);
controller.listener.portChanged(DPID1, portStatus(false, 1));
assertFalse("Port added to discoverer",
provider.discoverers.get(DPID1).ports.containsKey(1));
assertTrue("Port is not gone.", vanishedPort((long) 1));
}
@Test
public void portUnknown() {
controller.listener.switchAdded(DPID1);
controller.listener.portChanged(DPID2, portStatus(false, 1));
assertNull("DPID exists",
provider.discoverers.get(DPID2));
}
@Test
public void unknownPktCtx() {
controller.pktListener.handlePacket(pktCtx);
assertFalse("Context should still be free", pktCtx.isHandled());
}
@Test
public void knownPktCtx() {
controller.listener.switchAdded(DPID1);
controller.listener.switchAdded(DPID2);
controller.pktListener.handlePacket(pktCtx);
assertTrue("Link not detected", detectedLink(DPID1, DPID2));
}
@After
public void tearDown() {
provider.deactivate();
provider.providerRegistry = null;
provider.controller = null;
}
private OFPortStatus portStatus(boolean up, int port) {
OFPortDesc desc = portDesc(port, up);
OFPortStatus status = OFFactoryVer10.INSTANCE.buildPortStatus()
.setDesc(desc)
.setReason(up ? OFPortReason.ADD : OFPortReason.DELETE).build();
return status;
}
private static OFPortDesc portDesc(int port, boolean up) {
OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc();
builder.setPortNo(OFPort.of(port));
if (!up) {
builder.setConfig(Collections.singleton(OFPortConfig.PORT_DOWN));
}
return builder.build();
}
private boolean vanishedDpid(Dpid... dpids) {
for (int i = 0; i < dpids.length; i++) {
if (!providerService.vanishedDpid.contains(dpids[i])) {
return false;
}
}
return true;
}
private boolean vanishedPort(Long... ports) {
for (int i = 0; i < ports.length; i++) {
if (!providerService.vanishedPort.contains(ports[i])) {
return false;
}
}
return true;
}
private boolean detectedLink(Dpid src, Dpid dst) {
for (Dpid key : providerService.discoveredLinks.keySet()) {
if (key.equals(src)) {
return providerService.discoveredLinks.get(src).equals(dst);
}
}
return false;
}
private class TestLinkRegistry implements LinkProviderRegistry {
@Override
public LinkProviderService register(LinkProvider provider) {
providerService = new TestLinkProviderService(provider);
return providerService;
}
@Override
public void unregister(LinkProvider provider) {
}
@Override
public Set<ProviderId> getProviders() {
return null;
}
}
private class TestLinkProviderService
extends AbstractProviderService<LinkProvider>
implements LinkProviderService {
List<Dpid> vanishedDpid = Lists.newLinkedList();
List<Long> vanishedPort = Lists.newLinkedList();
Map<Dpid, Dpid> discoveredLinks = Maps.newHashMap();
protected TestLinkProviderService(LinkProvider provider) {
super(provider);
}
@Override
public void linkDetected(LinkDescription linkDescription) {
Dpid sDpid = Dpid.dpid(linkDescription.src().deviceId().uri());
Dpid dDpid = Dpid.dpid(linkDescription.dst().deviceId().uri());
discoveredLinks.put(sDpid, dDpid);
}
@Override
public void linkVanished(LinkDescription linkDescription) {
}
@Override
public void linksVanished(ConnectPoint connectPoint) {
vanishedPort.add(connectPoint.port().toLong());
}
@Override
public void linksVanished(DeviceId deviceId) {
vanishedDpid.add(Dpid.dpid(deviceId.uri()));
}
}
private class TestController extends OpenflowControllerAdapter {
PacketListener pktListener;
OpenFlowSwitchListener listener;
Map<Dpid, OpenFlowSwitch> switchMap = new HashMap<Dpid, OpenFlowSwitch>();
@Override
public void addPacketListener(int priority, PacketListener listener) {
pktListener = listener;
}
@Override
public void removePacketListener(PacketListener listener) {
pktListener = null;
}
@Override
public void addListener(OpenFlowSwitchListener listener) {
this.listener = listener;
}
@Override
public void removeListener(OpenFlowSwitchListener listener) {
this.listener = null;
}
@Override
public void processPacket(Dpid dpid, OFMessage msg) {
OpenFlowPacketContext ctx = new TestPacketContext(dpid);
pktListener.handlePacket(ctx);
}
@Override
public Iterable<OpenFlowSwitch> getSwitches() {
return Collections.emptyList();
}
@Override
public OpenFlowSwitch getSwitch(Dpid dpid) {
return switchMap.get(dpid);
}
}
private class TestPacketContext implements OpenFlowPacketContext {
protected Dpid swid;
protected boolean blocked = false;
public TestPacketContext(Dpid dpid) {
swid = dpid;
}
@Override
public boolean block() {
blocked = true;
return blocked;
}
@Override
public void send() {
}
@Override
public void build(OFPort outPort) {
}
@Override
public void build(Ethernet ethFrame, OFPort outPort) {
}
@Override
public Ethernet parsed() {
return null;
}
@Override
public byte[] unparsed() {
ONLabLddp lldp = new ONLabLddp();
lldp.setSwitch(DPID1.value());
Ethernet ethPacket = new Ethernet();
ethPacket.setEtherType(Ethernet.TYPE_LLDP);
ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
ethPacket.setPayload(lldp);
ethPacket.setPad(true);
lldp.setPort(PD1.getPortNo().getPortNumber());
ethPacket.setSourceMACAddress(PD1.getHwAddr().getBytes());
return ethPacket.serialize();
}
@Override
public Dpid dpid() {
return swid;
}
@Override
public Integer inPort() {
return PD3.getPortNo().getPortNumber();
}
@Override
public boolean isHandled() {
return blocked;
}
@Override
public boolean isBuffered() {
return false;
}
}
private static class TestOpenFlowSwitch implements OpenFlowSwitch {
private final List<OFPortDesc> ports;
private final Dpid dpid;
public TestOpenFlowSwitch(Dpid dpid, List<OFPortDesc> ports) {
this.ports = ports;
this.dpid = dpid;
}
RoleState state;
List<OFMessage> sent = new ArrayList<OFMessage>();
OFFactory factory = OFFactoryVer10.INSTANCE;
@Override
public void sendMsg(OFMessage msg) {
sent.add(msg);
}
@Override
public void sendMsg(List<OFMessage> msgs) {
}
@Override
public void handleMessage(OFMessage fromSwitch) {
}
@Override
public void setRole(RoleState role) {
state = role;
}
@Override
public RoleState getRole() {
return state;
}
@Override
public List<OFPortDesc> getPorts() {
return ports;
}
@Override
public OFFactory factory() {
return factory;
}
@Override
public String getStringId() {
return null;
}
@Override
public long getId() {
return dpid.value();
}
@Override
public String manufacturerDescription() {
return null;
}
@Override
public String datapathDescription() {
return null;
}
@Override
public String hardwareDescription() {
return null;
}
@Override
public String softwareDescription() {
return null;
}
@Override
public String serialNumber() {
return null;
}
@Override
public boolean isConnected() {
return true;
}
@Override
public void disconnectSwitch() {
}
@Override
public boolean isOptical() {
return false;
}
@Override
public void returnRoleReply(RoleState requested, RoleState reponse) {
}
@Override
public String channelId() {
return "1.2.3.4:1";
}
@Override
public TableType getTableType(TableId tid) {
return TableType.NONE;
}
@Override
public void transformAndSendMsg(OFMessage msg, TableType tableType) {
// TODO Auto-generated method stub
}
}
}
......@@ -33,8 +33,6 @@
<modules>
<module>device</module>
<module>link</module>
<module>host</module>
<module>packet</module>
<module>flow</module>
<module>group</module>
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* LLDP packets OpenVirteX uses for discovery of physical network topology.
* Refer to IEEE Std 802.1ABTM-2009 for more information.
*
*/
@Deprecated
public class ONLabLddp extends LLDP {
private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
// ON.Lab OUI and OVX name for organizationally specific TLVs
public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
public static final String OVX_NAME = "OpenVirteX";
public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
0x01};
public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
(byte) 0xc2, 0x00, 0x00, 0x0e};
public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
public static final short ETHERTYPE_VLAN = (short) 0x8100;
// TLV constants: type, size and subtype
// Organizationally specific TLV also have packet offset and contents of TLV
// header
private static final byte CHASSIS_TLV_TYPE = 1;
private static final byte CHASSIS_TLV_SIZE = 7;
private static final byte CHASSIS_TLV_SUBTYPE = 4;
private static final byte PORT_TLV_TYPE = 2;
private static final byte PORT_TLV_SIZE = 5;
private static final byte PORT_TLV_SUBTYPE = 2;
private static final byte TTL_TLV_TYPE = 3;
private static final byte TTL_TLV_SIZE = 2;
private static final byte NAME_TLV_TYPE = 127;
// 4 = OUI (3) + subtype (1)
private static final byte NAME_TLV_SIZE = (byte) (4 + ONLabLddp.OVX_NAME.length());
private static final byte NAME_TLV_SUBTYPE = 1;
private static final short NAME_TLV_OFFSET = 34;
private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
// Contents of full name TLV
private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
.putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
.put(OVX_NAME.getBytes(StandardCharsets.UTF_8)).array();
private static final byte DPID_TLV_TYPE = 127;
private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
// (1) + dpid (8)
private static final byte DPID_TLV_SUBTYPE = 2;
private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
// Contents of dpid TLV
// Note that this does *not* contain the actual dpid since we cannot match
// on it
private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
.putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
.array();
// Pre-built contents of both organizationally specific TLVs
private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
// Default switch, port number and TTL
private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
private static final int DEFAULT_PORT = 0;
private static final short DEFAULT_TTL = 120; // in seconds
// Minimum and OVX-generated LLDP packet sizes
private static final short MINIMUM_LLDP_SIZE = 61;
// Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
private static final short OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
+ PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
// Field offsets in OVX-generated LLDP
private static final short ETHERTYPE_OFFSET = 12;
private static final short PORT_OFFSET = 26;
private static final short DPID_OFFSET = 56;
// Private member fields
// Byte arrays for TLV information string
private ByteBuffer bb;
private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
private final byte[] portId = new byte[PORT_TLV_SIZE];
private final byte[] ttl = new byte[TTL_TLV_SIZE];
private final byte[] ouiName = new byte[NAME_TLV_SIZE];
private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
// TLVs
private final LLDPTLV chassisTLV;
private final LLDPTLV portTLV;
private final LLDPTLV ttlTLV;
private final LLDPTLV ouiNameTLV;
private final LLDPTLV ouiDpidTLV;
private final List<LLDPTLV> optionalTLVList;
/**
* Instantiates a new OVX LDDP message.
*/
public ONLabLddp() {
// Create TLVs
this.chassisTLV = new LLDPTLV();
this.portTLV = new LLDPTLV();
this.ttlTLV = new LLDPTLV();
this.ouiNameTLV = new LLDPTLV();
this.ouiDpidTLV = new LLDPTLV();
this.optionalTLVList = new LinkedList<LLDPTLV>();
this.optionalTLVList.add(this.ouiNameTLV);
this.optionalTLVList.add(this.ouiDpidTLV);
// Add TLVs to LLDP packet
this.setChassisId(this.chassisTLV);
this.setPortId(this.portTLV);
this.setTtl(this.ttlTLV);
this.setOptionalTLVList(this.optionalTLVList);
// Set TLVs to default values
this.setChassisTLV(DEFAULT_DPID);
this.setPortTLV(DEFAULT_PORT);
this.setTTLTLV(DEFAULT_TTL);
this.setOUIName(ONLabLddp.OVX_NAME);
this.setOUIDpid(DEFAULT_DPID);
}
/**
* Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
* we use another organizationally specific TLV to put the full dpid (see
* setOUIDpid()).
*
* @param dpid the switch DPID
*/
private void setChassisTLV(final byte[] dpid) {
this.bb = ByteBuffer.wrap(this.chassisId);
this.bb.put(CHASSIS_TLV_SUBTYPE);
for (int i = 2; i < 8; i++) {
bb.put(dpid[i]);
}
this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
this.chassisTLV.setType(CHASSIS_TLV_TYPE);
this.chassisTLV.setValue(this.chassisId);
}
/**
* Sets port TLV.
*
* @param portNumber the port number
*/
private void setPortTLV(final int portNumber) {
this.bb = ByteBuffer.wrap(this.portId);
this.bb.put(PORT_TLV_SUBTYPE);
this.bb.putInt(portNumber);
this.portTLV.setLength(PORT_TLV_SIZE);
this.portTLV.setType(PORT_TLV_TYPE);
this.portTLV.setValue(this.portId);
}
/**
* Sets Time To Live TLV.
*
* @param time the time to live
*/
private void setTTLTLV(final short time) {
this.bb = ByteBuffer.wrap(this.ttl);
this.bb.putShort(time);
this.ttlTLV.setLength(TTL_TLV_SIZE);
this.ttlTLV.setType(TTL_TLV_TYPE);
this.ttlTLV.setValue(this.ttl);
}
/**
* Set. organizationally specific TLV for OVX name (subtype 1).
*
* @param name the name
*/
private void setOUIName(final String name) {
this.bb = ByteBuffer.wrap(ouiName);
this.bb.put(ONLabLddp.ONLAB_OUI);
this.bb.put(NAME_TLV_SUBTYPE);
this.bb.put(name.getBytes(StandardCharsets.UTF_8));
this.ouiNameTLV.setLength(NAME_TLV_SIZE);
this.ouiNameTLV.setType(NAME_TLV_TYPE);
this.ouiNameTLV.setValue(ouiName);
}
/**
* Sets organizationally specific TLV for OVX full dpid (subtype 2).
*
* @param dpid the switch DPID
*/
private void setOUIDpid(final byte[] dpid) {
this.bb = ByteBuffer.wrap(ouiDpid);
this.bb.put(ONLabLddp.ONLAB_OUI);
this.bb.put(DPID_TLV_SUBTYPE);
this.bb.put(dpid);
this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
this.ouiDpidTLV.setType(DPID_TLV_TYPE);
this.ouiDpidTLV.setValue(ouiDpid);
}
/**
* Sets switch DPID in LLDP packet.
*
* @param dp the switch instance
*/
public void setSwitch(long dp) {
final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
.array();
this.setChassisTLV(dpid);
this.setOUIDpid(dpid);
}
/**
* Sets port in LLDP packet.
*
* @param port the port instance
*/
public void setPort(int port) {
this.setPortTLV(port);
}
/**
* Serializes full LLDP packet to byte array. Need to set both switch and
* port before you can serialize.
*/
@Override
public byte[] serialize() {
return super.serialize();
}
/**
* Checks if LLDP packet has correct size, LLDP multicast address, and
* ethertype. Packet assumed to have Ethernet header.
*
* @param packet packet data
* @return true if packet is LLDP, false otherwise
*/
public static boolean isLLDP(final byte[] packet) {
// Does packet exist and does it have the mininum size?
if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
return false;
}
// Packet has LLDP multicast destination address?
final ByteBuffer bb = ByteBuffer.wrap(packet);
final byte[] dst = new byte[6];
bb.get(dst);
if (!(Arrays.equals(dst, ONLabLddp.LLDP_NICIRA)
|| Arrays.equals(dst, ONLabLddp.LLDP_MULTICAST) || Arrays.equals(
dst, ONLabLddp.BDDP_MULTICAST))) {
return false;
}
// Fetch ethertype, skip VLAN tag if it's there
short etherType = bb.getShort(ETHERTYPE_OFFSET);
if (etherType == ETHERTYPE_VLAN) {
etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
}
// Check ethertype
if (etherType == Ethernet.TYPE_LLDP) {
return true;
}
if (etherType == Ethernet.TYPE_BSN) {
return true;
}
return false;
}
/**
* Checks if packet has size of OVX-generated LLDP, and correctness of two
* organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
* valid LLDP packet
*
* @param packet packet data
* @return eth type or -1
*/
public static short isOVXLLDP(byte[] packet) {
if (packet.length < OVX_LLDP_SIZE) {
return -1;
}
// Extra offset due to VLAN tag
final ByteBuffer bb = ByteBuffer.wrap(packet);
int offset = 0;
short ethType = bb.getShort(ETHERTYPE_OFFSET);
if (ethType != Ethernet.TYPE_LLDP
&& ethType != Ethernet.TYPE_BSN) {
offset = 4;
ethType = bb.getShort(ETHERTYPE_OFFSET + offset);
if (ethType != Ethernet.TYPE_LLDP
&& ethType != Ethernet.TYPE_BSN) {
return -1;
}
}
// Compare packet's organizationally specific TLVs to the expected
// values
for (int i = 0; i < OUI_TLV.length; i++) {
if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
return -1;
}
}
return ethType;
}
/**
* Extracts dpid and port from OVX-generated LLDP packet.
*
* @param packet packet data
* @return Dpid and port
*/
public static DPIDandPort parseLLDP(final byte[] packet) {
final ByteBuffer bb = ByteBuffer.wrap(packet);
// Extra offset due to VLAN tag
int offset = 0;
if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
&& bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
offset = 4;
}
final int port = bb.getInt(PORT_OFFSET + offset);
final long dpid = bb.getLong(DPID_OFFSET + offset);
return new DPIDandPort(dpid, port);
}
public static class DPIDandPort {
private final long dpid;
private final int port;
public DPIDandPort(long dpid, int port) {
this.dpid = dpid;
this.port = port;
}
public long getDpid() {
return this.dpid;
}
public int getPort() {
return this.port;
}
}
}