Praseed Balakrishnan

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 31 changed files with 1718 additions and 93 deletions
......@@ -45,6 +45,12 @@
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
</dependency>
......
package org.onlab.onos.sdnip;
import java.util.List;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
......@@ -8,6 +10,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.sdnip.bgp.BgpConstants;
import org.onlab.onos.sdnip.config.BgpPeer;
import org.onlab.onos.sdnip.config.BgpSpeaker;
import org.onlab.onos.sdnip.config.Interface;
......@@ -20,8 +23,6 @@ import org.onlab.packet.IpPrefix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Manages the connectivity requirements between peers.
*/
......@@ -30,37 +31,44 @@ public class PeerConnectivityManager {
private static final Logger log = LoggerFactory.getLogger(
PeerConnectivityManager.class);
// TODO these shouldn't be defined here
private static final short BGP_PORT = 179;
private static final int IPV4_BIT_LENGTH = 32;
private final SdnIpConfigService configInfoService;
private final SdnIpConfigService configService;
private final InterfaceService interfaceService;
private final IntentService intentService;
private final ApplicationId appId;
/**
* Creates a new PeerConnectivityManager.
*
* @param appId the application ID
* @param configService the SDN-IP config service
* @param interfaceService the interface service
* @param intentService the intent service
*/
public PeerConnectivityManager(ApplicationId appId,
SdnIpConfigService configInfoService,
SdnIpConfigService configService,
InterfaceService interfaceService,
IntentService intentService) {
this.appId = appId;
this.configInfoService = configInfoService;
this.configService = configService;
this.interfaceService = interfaceService;
this.intentService = intentService;
}
/**
* Starts the peer connectivity manager.
*/
public void start() {
// TODO are any of these errors?
if (interfaceService.getInterfaces().isEmpty()) {
log.warn("The interface in configuration file is empty. "
+ "Thus, the SDN-IP application can not be started.");
} else if (configInfoService.getBgpPeers().isEmpty()) {
} else if (configService.getBgpPeers().isEmpty()) {
log.warn("The BGP peer in configuration file is empty."
+ "Thus, the SDN-IP application can not be started.");
} else if (configInfoService.getBgpSpeakers() == null) {
} else if (configService.getBgpSpeakers() == null) {
log.error("The BGP speaker in configuration file is empty. "
+ "Thus, the SDN-IP application can not be started.");
......@@ -79,7 +87,7 @@ public class PeerConnectivityManager {
* for paths from all peers to each BGP speaker.
*/
private void setupBgpPaths() {
for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers()
for (BgpSpeaker bgpSpeaker : configService.getBgpSpeakers()
.values()) {
log.debug("Start to set up BGP paths for BGP speaker: {}",
bgpSpeaker);
......@@ -88,7 +96,7 @@ public class PeerConnectivityManager {
List<InterfaceAddress> interfaceAddresses =
bgpSpeaker.interfaceAddresses();
for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) {
for (BgpPeer bgpPeer : configService.getBgpPeers().values()) {
log.debug("Start to set up BGP paths between BGP speaker: {} "
+ "to BGP peer: {}", bgpSpeaker, bgpPeer);
......@@ -121,16 +129,14 @@ public class PeerConnectivityManager {
// install intent for BGP path from BGPd to BGP peer matching
// destination TCP port 179
// TODO: The usage of PacketMatchBuilder will be improved, then we
// only need to new the PacketMatchBuilder once.
// By then, the code here will be improved accordingly.
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpDst(BGP_PORT)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchTcpDst((short) BgpConstants.BGP_PORT)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
......@@ -149,9 +155,11 @@ public class PeerConnectivityManager {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpSrc(BGP_PORT)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchTcpSrc((short) BgpConstants.BGP_PORT)
.build();
PointToPointIntent intentMatchSrcTcpPort =
......@@ -167,9 +175,11 @@ public class PeerConnectivityManager {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpDst(BGP_PORT)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchTcpDst((short) BgpConstants.BGP_PORT)
.build();
PointToPointIntent reversedIntentMatchDstTcpPort =
......@@ -185,9 +195,11 @@ public class PeerConnectivityManager {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpSrc(BGP_PORT)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchTcpSrc((short) BgpConstants.BGP_PORT)
.build();
PointToPointIntent reversedIntentMatchSrcTcpPort =
......@@ -211,7 +223,7 @@ public class PeerConnectivityManager {
* for paths from all peers to each BGP speaker.
*/
private void setupIcmpPaths() {
for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers()
for (BgpSpeaker bgpSpeaker : configService.getBgpSpeakers()
.values()) {
log.debug("Start to set up ICMP paths for BGP speaker: {}",
bgpSpeaker);
......@@ -219,7 +231,7 @@ public class PeerConnectivityManager {
List<InterfaceAddress> interfaceAddresses = bgpSpeaker
.interfaceAddresses();
for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) {
for (BgpPeer bgpPeer : configService.getBgpPeers().values()) {
Interface peerInterface = interfaceService.getInterface(
bgpPeer.connectPoint());
......@@ -253,8 +265,10 @@ public class PeerConnectivityManager {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
......@@ -271,8 +285,10 @@ public class PeerConnectivityManager {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(),
IpAddress.MAX_INET_MASK))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(),
IpAddress.MAX_INET_MASK))
.build();
PointToPointIntent reversedIntent =
......
......@@ -55,8 +55,6 @@ import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
/**
* This class processes BGP route update, translates each update into a intent
* and submits the intent.
* <p/>
* TODO: Make it thread-safe.
*/
public class Router implements RouteListener {
......@@ -69,14 +67,13 @@ public class Router implements RouteListener {
// Stores all incoming route updates in a queue.
private BlockingQueue<RouteUpdate> routeUpdates;
// The Ip4Address is the next hop address of each route update.
// The IpAddress is the next hop address of each route update.
private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
private ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent> pushedRouteIntents;
private IntentService intentService;
//private IProxyArpService proxyArp;
private HostService hostService;
private SdnIpConfigService configInfoService;
private SdnIpConfigService configService;
private InterfaceService interfaceService;
private ExecutorService bgpUpdatesExecutor;
......@@ -98,18 +95,19 @@ public class Router implements RouteListener {
/**
* Class constructor.
*
* @param appId the application ID
* @param intentService the intent service
* @param hostService the host service
* @param configInfoService the configuration service
* @param configService the configuration service
* @param interfaceService the interface service
*/
public Router(ApplicationId appId, IntentService intentService,
HostService hostService, SdnIpConfigService configInfoService,
HostService hostService, SdnIpConfigService configService,
InterfaceService interfaceService) {
this.appId = appId;
this.intentService = intentService;
this.hostService = hostService;
this.configInfoService = configInfoService;
this.configService = configService;
this.interfaceService = interfaceService;
bgpRoutes = new ConcurrentInvertedRadixTree<>(
......@@ -172,7 +170,7 @@ public class Router implements RouteListener {
@Override
public void update(RouteUpdate routeUpdate) {
log.debug("Received new route Update: {}", routeUpdate);
log.debug("Received new route update: {}", routeUpdate);
try {
routeUpdates.put(routeUpdate);
......@@ -498,9 +496,11 @@ public class Router implements RouteListener {
private void executeRouteAdd(RouteEntry routeEntry) {
log.debug("Executing route add: {}", routeEntry);
// Monitor the IP address so we'll get notified of updates to the MAC
// address.
hostService.startMonitoringIp(routeEntry.nextHop());
// See if we know the MAC address of the next hop
//MacAddress nextHopMacAddress =
//proxyArp.getMacAddress(routeEntry.getNextHop());
MacAddress nextHopMacAddress = null;
Set<Host> hosts = hostService.getHostsByIp(
routeEntry.nextHop().toPrefix());
......@@ -511,9 +511,6 @@ public class Router implements RouteListener {
if (nextHopMacAddress == null) {
routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
//proxyArp.sendArpRequest(routeEntry.getNextHop(), this, true);
// TODO maybe just do this for every prefix anyway
hostService.startMonitoringIp(routeEntry.nextHop());
return;
}
......@@ -536,11 +533,11 @@ public class Router implements RouteListener {
// Find the attachment point (egress interface) of the next hop
Interface egressInterface;
if (configInfoService.getBgpPeers().containsKey(nextHopIpAddress)) {
if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
// Route to a peer
log.debug("Route to peer {}", nextHopIpAddress);
BgpPeer peer =
configInfoService.getBgpPeers().get(nextHopIpAddress);
configService.getBgpPeers().get(nextHopIpAddress);
egressInterface =
interfaceService.getInterface(peer.connectPoint());
} else {
......@@ -593,17 +590,12 @@ public class Router implements RouteListener {
}
// Match the destination IP prefix at the first hop
//PacketMatchBuilder builder = new PacketMatchBuilder();
//builder.setEtherType(Ethernet.TYPE_IPV4).setDstIpNet(prefix);
//PacketMatch packetMatch = builder.build();
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(prefix)
.build();
// Rewrite the destination MAC address
//ModifyDstMacAction modifyDstMacAction =
//new ModifyDstMacAction(nextHopMacAddress);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setEthDst(nextHopMacAddress)
.build();
......@@ -635,10 +627,6 @@ public class Router implements RouteListener {
log.debug("Processing route delete: {}", routeEntry);
IpPrefix prefix = routeEntry.prefix();
// TODO check the change of logic here - remove doesn't check that
// the route entry was what we expected (and we can't do this
// concurrently)
if (bgpRoutes.remove(RouteEntry.createBinaryString(prefix))) {
//
// Only delete flows if an entry was actually removed from the
......@@ -680,17 +668,19 @@ public class Router implements RouteListener {
}
/**
* This method handles the prefixes which are waiting for ARP replies for
* MAC addresses of next hops.
* Signals the Router that the MAC to IP mapping has potentially been
* updated. This has the effect of updating the MAC address for any
* installed prefixes if it has changed, as well as installing any pending
* prefixes that were waiting for MAC resolution.
*
* @param ipAddress next hop router IP address, for which we sent ARP
* request out
* @param macAddress MAC address which is relative to the ipAddress
* @param ipAddress the IP address that an event was received for
* @param macAddress the most recently known MAC address for the IP address
*/
//@Override
// TODO change name
public void arpResponse(IpAddress ipAddress, MacAddress macAddress) {
log.debug("Received ARP response: {} => {}", ipAddress, macAddress);
private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
log.debug("Received updated MAC info: {} => {}", ipAddress, macAddress);
// TODO here we should check whether the next hop for any of our
// installed prefixes has changed, not just prefixes pending installation.
// We synchronize on this to prevent changes to the radix tree
// while we're pushing intents. If the tree changes, the
......@@ -708,8 +698,6 @@ public class Router implements RouteListener {
bgpRoutes.getValueForExactKey(binaryString);
if (foundRouteEntry != null &&
foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
log.debug("Pushing prefix {} next hop {}",
routeEntry.prefix(), routeEntry.nextHop());
// We only push prefix flows if the prefix is still in the
// radix tree and the next hop is the same as our
// update.
......@@ -717,9 +705,8 @@ public class Router implements RouteListener {
// for the ARP, or the next hop could have changed.
addRouteIntentToNextHop(prefix, ipAddress, macAddress);
} else {
log.debug("Received ARP response, but {}/{} is no longer in"
+ " the radix tree", routeEntry.prefix(),
routeEntry.nextHop());
log.debug("{} has been revoked before the MAC was resolved",
routeEntry);
}
}
}
......@@ -769,7 +756,7 @@ public class Router implements RouteListener {
event.type() == HostEvent.Type.HOST_UPDATED) {
Host host = event.subject();
for (IpPrefix ip : host.ipAddresses()) {
arpResponse(ip.toIpAddress(), host.mac());
updateMac(ip.toIpAddress(), host.mac());
}
}
}
......
......@@ -26,7 +26,7 @@ import org.slf4j.Logger;
@Service
public class SdnIp implements SdnIpService {
private static final String SDN_ID_APP = "org.onlab.onos.sdnip";
private static final String SDN_IP_APP = "org.onlab.onos.sdnip";
private final Logger log = getLogger(getClass());
......@@ -53,8 +53,10 @@ public class SdnIp implements SdnIpService {
InterfaceService interfaceService = new HostToInterfaceAdaptor(hostService);
ApplicationId appId = coreService.registerApplication(SDN_ID_APP);
peerConnectivity = new PeerConnectivityManager(appId, config, interfaceService, intentService);
ApplicationId appId = coreService.registerApplication(SDN_IP_APP);
peerConnectivity = new PeerConnectivityManager(appId, config,
interfaceService, intentService);
peerConnectivity.start();
router = new Router(appId, intentService, hostService, config, interfaceService);
......
......@@ -26,12 +26,12 @@ import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.junit.TestUtils;
import org.onlab.junit.TestUtils.TestUtilsException;
import org.onlab.onos.sdnip.RouteListener;
import org.onlab.onos.sdnip.RouteUpdate;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.util.TestUtils;
import org.onlab.util.TestUtils.TestUtilsException;
import com.google.common.net.InetAddresses;
......
......@@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
* This class tests the immutability, equality, and non-equality of
......@@ -17,7 +18,7 @@ public class IntentIdTest {
*/
@Test
public void intentIdFollowsGuidelineForImmutableObject() {
ImmutableClassChecker.assertThatClassIsImmutable(IntentId.class);
assertThatClassIsImmutable(IntentId.class);
}
/**
......
......@@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
import static org.onlab.onos.net.NetTestTools.hid;
/**
......@@ -104,6 +105,6 @@ public class TestHostToHostIntent {
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.assertThatClassIsImmutable(HostToHostIntent.class);
assertThatClassIsImmutable(HostToHostIntent.class);
}
}
......
......@@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
import static org.onlab.onos.net.NetTestTools.link;
import java.util.HashSet;
......@@ -154,6 +155,6 @@ public class TestLinkCollectionIntent {
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.assertThatClassIsImmutable(LinkCollectionIntent.class);
assertThatClassIsImmutable(LinkCollectionIntent.class);
}
}
......
......@@ -15,6 +15,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
import static org.onlab.onos.net.NetTestTools.connectPoint;
/**
......@@ -135,7 +136,6 @@ public class TestMultiPointToSinglePointIntent {
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.
assertThatClassIsImmutable(MultiPointToSinglePointIntent.class);
assertThatClassIsImmutable(MultiPointToSinglePointIntent.class);
}
}
......
......@@ -35,6 +35,7 @@
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
......
......@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
......@@ -159,6 +160,21 @@ public class DistributedFlowRuleStore
}
});
clusterCommunicator.addSubscriber(GET_DEVICE_FLOW_ENTRIES, new ClusterMessageHandler() {
@Override
public void handle(ClusterMessage message) {
DeviceId deviceId = SERIALIZER.decode(message.payload());
log.info("Received get flow entries request for {} from {}", deviceId, message.sender());
Set<FlowEntry> flowEntries = getFlowEntriesInternal(deviceId);
try {
message.respond(SERIALIZER.encode(flowEntries));
} catch (IOException e) {
log.error("Failed to respond to peer's getFlowEntries request", e);
}
}
});
log.info("Started");
}
......@@ -217,9 +233,33 @@ public class DistributedFlowRuleStore
@Override
public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
return getFlowEntriesInternal(deviceId);
}
log.info("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
replicaInfo.master().orNull(), deviceId);
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
GET_DEVICE_FLOW_ENTRIES,
SERIALIZER.encode(deviceId));
try {
ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
} catch (IOException | TimeoutException e) {
// FIXME: throw a FlowStoreException
throw new RuntimeException(e);
}
}
private Set<FlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
Collection<? extends FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptyList();
return Collections.emptySet();
}
return ImmutableSet.copyOf(rules);
}
......
......@@ -13,4 +13,7 @@ public final class FlowStoreMessageSubjects {
public static final MessageSubject GET_FLOW_ENTRY
= new MessageSubject("peer-forward-get-flow-entry");
public static final MessageSubject GET_DEVICE_FLOW_ENTRIES
= new MessageSubject("peer-forward-get-device-flow-entries");
}
......
......@@ -34,7 +34,7 @@
<version>2.10.1</version>
<configuration>
<show>package</show>
<excludePackageNames>org.onlab.thirdparty:*.impl:*.impl.*:org.onlab.onos.provider.*:org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli*:org.onlab.onos.tvue:org.onlab.onos.foo:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.optical:org.onlab.onos.config:org.onlab.onos.calendar:org.onlab.onos.sdnip*:org.onlab.onos.metrics</excludePackageNames>
<excludePackageNames>org.onlab.thirdparty:*.impl:*.impl.*:org.onlab.onos.provider.*:org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli*:org.onlab.onos.tvue:org.onlab.onos.foo:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.optical:org.onlab.onos.config:org.onlab.onos.calendar:org.onlab.onos.sdnip*:org.onlab.onos.metrics:org.onlab.onos.store.*:org.onlab.onos.openflow.*</excludePackageNames>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API</doctitle>
<groups>
......
......@@ -248,7 +248,7 @@
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
......
......@@ -27,6 +27,16 @@
<artifactId>guava-testlib</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
package org.onlab.junit;
import org.hamcrest.Description;
import org.hamcrest.StringDescription;
......
package org.onlab.util;
package org.onlab.junit;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
......
package org.onlab.junit;
import org.hamcrest.Description;
import org.hamcrest.StringDescription;
import org.onlab.junit.TestUtils.TestUtilsException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Hamcrest style class for verifying that a class follows the
* accepted rules for utility classes.
*
* The rules that are enforced for utility classes:
* - the class must be declared final
* - the class must have only one constructor
* - the constructor must be private and inaccessible to callers
* - the class must have only static methods
*/
public class UtilityClassChecker {
private String failureReason = "";
/**
* Method to determine if a given class is a properly specified
* utility class. In addition to checking that the class meets the criteria
* for utility classes, an object of the class type is allocated to force
* test code coverage onto the class constructor.
*
* @param clazz the class to check
* @return true if the given class is a properly specified utility class.
*/
private boolean isProperlyDefinedUtilityClass(Class<?> clazz) {
// class must be declared final
if (!Modifier.isFinal(clazz.getModifiers())) {
failureReason = "a class that is not final";
return false;
}
// class must have only one constructor
final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if (constructors.length != 1) {
failureReason = "a class with more than one constructor";
return false;
}
// constructor must not be accessible outside of the class
final Constructor<?> constructor = constructors[0];
if (constructor.isAccessible()) {
failureReason = "a class with an accessible default constructor";
return false;
}
// constructor must be private
if (!Modifier.isPrivate(constructor.getModifiers())) {
failureReason = "a class with a default constructor that is not private";
return false;
}
// class must have only static methods
for (final Method method : clazz.getMethods()) {
if (method.getDeclaringClass().equals(clazz)) {
if (!Modifier.isStatic(method.getModifiers())) {
failureReason = "a class with one or more non-static methods";
return false;
}
}
}
try {
final Object newObject = TestUtils.callConstructor(constructor);
if (newObject == null) {
failureReason = "could not instantiate a new object";
return false;
}
} catch (TestUtilsException e) {
failureReason = "could not instantiate a new object";
return false;
}
return true;
}
/**
* Describe why an error was reported. Uses Hamcrest style Description
* interfaces.
*
* @param description the Description object to use for reporting the
* mismatch
*/
public void describeMismatch(Description description) {
description.appendText(failureReason);
}
/**
* Describe the source object that caused an error, using a Hamcrest
* Matcher style interface. In this case, it always returns
* that we are looking for a properly defined utility class.
*
* @param description the Description object to use to report the "to"
* object
*/
public void describeTo(Description description) {
description.appendText("a properly defined utility class");
}
/**
* Assert that the given class adheres to the utility class rules.
*
* @param clazz the class to check
*
* @throws java.lang.AssertionError if the class is not a valid
* utility class
*/
public static void assertThatClassIsUtility(Class<?> clazz) {
final UtilityClassChecker checker = new UtilityClassChecker();
if (!checker.isProperlyDefinedUtilityClass(clazz)) {
final Description toDescription = new StringDescription();
final Description mismatchDescription = new StringDescription();
checker.describeTo(toDescription);
checker.describeMismatch(mismatchDescription);
final String reason =
"\n" +
"Expected: is \"" + toDescription.toString() + "\"\n" +
" but : was \"" + mismatchDescription.toString() + "\"";
throw new AssertionError(reason);
}
}
}
package org.onlab.junit;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
* Set of unit tests to check the implementation of the immutable class
* checker.
*/
public class ImmutableClassCheckerTest {
/**
* Test class for non final class check.
*/
// CHECKSTYLE IGNORE FinalClass FOR NEXT 1 LINES
static class NonFinal {
private NonFinal() { }
}
/**
* Check that a non final class correctly produces an error.
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testNonFinalClass() throws Exception {
boolean gotException = false;
try {
assertThatClassIsImmutable(NonFinal.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("is not final"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class for non private member class check.
*/
static final class FinalProtectedMember {
protected final int x = 0;
}
/**
* Check that a final class with a non-private member is properly detected.
*
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testFinalProtectedMember() throws Exception {
boolean gotException = false;
try {
assertThatClassIsImmutable(FinalProtectedMember.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("a field named 'x' that is not private"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class for non private member class check.
*/
static final class NotFinalPrivateMember {
private int x = 0;
}
/**
* Check that a final class with a non-final private
* member is properly detected.
*
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testNotFinalPrivateMember() throws Exception {
boolean gotException = false;
try {
assertThatClassIsImmutable(NotFinalPrivateMember.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("a field named 'x' that is not final"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class for non private member class check.
*/
static final class ClassWithSetter {
private final int x = 0;
public void setX(int newX) {
}
}
/**
* Check that a final class with a final private
* member that is modifyable by a setter is properly detected.
*
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testClassWithSetter() throws Exception {
boolean gotException = false;
try {
assertThatClassIsImmutable(ClassWithSetter.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("a class with a setter named 'setX'"));
gotException = true;
}
assertThat(gotException, is(true));
}
}
package org.onlab.util;
package org.onlab.junit;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
......@@ -6,7 +6,7 @@ import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.TestUtils.TestUtilsException;
import org.onlab.junit.TestUtils.TestUtilsException;
/**
* Test and usage examples for TestUtils.
......
package org.onlab.junit;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.onlab.junit.UtilityClassChecker.assertThatClassIsUtility;
/**
* Set of unit tests to check the implementation of the utility class
* checker.
*/
public class UtilityClassCheckerTest {
// CHECKSTYLE:OFF test data intentionally not final
/**
* Test class for non final class check.
*/
static class NonFinal {
private NonFinal() { }
}
// CHECKSTYLE:ON
/**
* Check that a non final class correctly produces an error.
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testNonFinalClass() throws Exception {
boolean gotException = false;
try {
assertThatClassIsUtility(NonFinal.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("is not final"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class for final no constructor class check.
*/
static final class FinalNoConstructor {
}
/**
* Check that a final class with no declared constructor correctly produces
* an error. In this case, the compiler generates a default constructor
* for you, but the constructor is 'protected' and will fail the check.
*
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testFinalNoConstructorClass() throws Exception {
boolean gotException = false;
try {
assertThatClassIsUtility(FinalNoConstructor.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("class with a default constructor that " +
"is not private"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class for class with more than one constructor check.
*/
static final class TwoConstructors {
private TwoConstructors() { }
private TwoConstructors(int x) { }
}
/**
* Check that a non static class correctly produces an error.
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testOnlyOneConstructor() throws Exception {
boolean gotException = false;
try {
assertThatClassIsUtility(TwoConstructors.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("more than one constructor"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class with a non private constructor.
*/
static final class NonPrivateConstructor {
protected NonPrivateConstructor() { }
}
/**
* Check that a class with a non private constructor correctly
* produces an error.
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testNonPrivateConstructor() throws Exception {
boolean gotException = false;
try {
assertThatClassIsUtility(NonPrivateConstructor.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("constructor that is not private"));
gotException = true;
}
assertThat(gotException, is(true));
}
/**
* Test class with a non static method.
*/
static final class NonStaticMethod {
private NonStaticMethod() { }
public void aPublicMethod() { }
}
/**
* Check that a class with a non static method correctly produces an error.
* @throws Exception if any of the reflection lookups fail.
*/
@Test
public void testNonStaticMethod() throws Exception {
boolean gotException = false;
try {
assertThatClassIsUtility(NonStaticMethod.class);
} catch (AssertionError assertion) {
assertThat(assertion.getMessage(),
containsString("one or more non-static methods"));
gotException = true;
}
assertThat(gotException, is(true));
}
}
......@@ -24,6 +24,7 @@
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
......
package org.onlab.packet;
import java.nio.ByteBuffer;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The class representing an IPv4 address.
* This class is immutable.
*/
public final class Ip4Address implements Comparable<Ip4Address> {
private final int value;
/** The length of the address in bytes (octets). */
public static final int BYTE_LENGTH = 4;
/** The length of the address in bits. */
public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
/**
* Default constructor.
*/
public Ip4Address() {
this.value = 0;
}
/**
* Copy constructor.
*
* @param other the object to copy from
*/
public Ip4Address(Ip4Address other) {
this.value = other.value;
}
/**
* Constructor from an integer value.
*
* @param value the value to use
*/
public Ip4Address(int value) {
this.value = value;
}
/**
* Constructor from a byte array with the IPv4 address stored in network
* byte order (i.e., the most significant byte first).
*
* @param value the value to use
*/
public Ip4Address(byte[] value) {
this(value, 0);
}
/**
* Constructor from a byte array with the IPv4 address stored in network
* byte order (i.e., the most significant byte first), and a given offset
* from the beginning of the byte array.
*
* @param value the value to use
* @param offset the offset in bytes from the beginning of the byte array
*/
public Ip4Address(byte[] value, int offset) {
checkNotNull(value);
// Verify the arguments
if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
String msg;
if (value.length < BYTE_LENGTH) {
msg = "Invalid IPv4 address array: array length: " +
value.length + ". Must be at least " + BYTE_LENGTH;
} else {
msg = "Invalid IPv4 address array: array offset: " +
offset + ". Must be in the interval [0, " +
(value.length - BYTE_LENGTH) + "]";
}
throw new IllegalArgumentException(msg);
}
// Read the address
ByteBuffer bb = ByteBuffer.wrap(value);
this.value = bb.getInt(offset);
}
/**
* Constructs an IPv4 address from a string representation of the address.
*<p>
* Example: "1.2.3.4"
*
* @param value the value to use
*/
public Ip4Address(String value) {
checkNotNull(value);
String[] splits = value.split("\\.");
if (splits.length != 4) {
final String msg = "Invalid IPv4 address string: " + value;
throw new IllegalArgumentException(msg);
}
int result = 0;
for (int i = 0; i < BYTE_LENGTH; i++) {
result |= Integer.parseInt(splits[i]) <<
((BYTE_LENGTH - (i + 1)) * Byte.SIZE);
}
this.value = result;
}
/**
* Gets the IPv4 address as a byte array.
*
* @return a byte array with the IPv4 address stored in network byte order
* (i.e., the most significant byte first).
*/
public byte[] toOctets() {
return ByteBuffer.allocate(BYTE_LENGTH).putInt(value).array();
}
/**
* Creates an IPv4 network mask prefix.
*
* @param prefixLen the length of the mask prefix. Must be in the interval
* [0, 32].
* @return a new IPv4 address that contains a mask prefix of the
* specified length
*/
public static Ip4Address makeMaskPrefix(int prefixLen) {
// Verify the prefix length
if ((prefixLen < 0) || (prefixLen > Ip4Address.BIT_LENGTH)) {
final String msg = "Invalid IPv4 prefix length: " + prefixLen +
". Must be in the interval [0, 32].";
throw new IllegalArgumentException(msg);
}
long v =
(0xffffffffL << (Ip4Address.BIT_LENGTH - prefixLen)) & 0xffffffffL;
return new Ip4Address((int) v);
}
/**
* Creates an IPv4 address by masking it with a network mask of given
* mask length.
*
* @param addr the address to mask
* @param prefixLen the length of the mask prefix. Must be in the interval
* [0, 32].
* @return a new IPv4 address that is masked with a mask prefix of the
* specified length
*/
public static Ip4Address makeMaskedAddress(final Ip4Address addr,
int prefixLen) {
Ip4Address mask = Ip4Address.makeMaskPrefix(prefixLen);
long v = addr.value & mask.value;
return new Ip4Address((int) v);
}
/**
* Gets the value of the IPv4 address.
*
* @return the value of the IPv4 address
*/
public int getValue() {
return value;
}
/**
* Converts the IPv4 value to a '.' separated string.
*
* @return the IPv4 value as a '.' separated string
*/
@Override
public String toString() {
return ((this.value >> 24) & 0xff) + "." +
((this.value >> 16) & 0xff) + "." +
((this.value >> 8) & 0xff) + "." +
(this.value & 0xff);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Ip4Address)) {
return false;
}
Ip4Address other = (Ip4Address) o;
if (this.value != other.value) {
return false;
}
return true;
}
@Override
public int hashCode() {
return this.value;
}
@Override
public int compareTo(Ip4Address o) {
Long lv = ((long) this.value) & 0xffffffffL;
Long rv = ((long) o.value) & 0xffffffffL;
return lv.compareTo(rv);
}
}
package org.onlab.packet;
import java.util.Objects;
/**
* The class representing an IPv4 network address.
* This class is immutable.
*/
public final class Ip4Prefix {
private final Ip4Address address; // The IPv4 address
private final short prefixLen; // The prefix length
/**
* Default constructor.
*/
public Ip4Prefix() {
this.address = new Ip4Address();
this.prefixLen = 0;
}
/**
* Copy constructor.
*
* @param other the object to copy from
*/
public Ip4Prefix(Ip4Prefix other) {
this.address = new Ip4Address(other.address);
this.prefixLen = other.prefixLen;
}
/**
* Constructor for a given address and prefix length.
*
* @param address the address to use
* @param prefixLen the prefix length to use
*/
public Ip4Prefix(Ip4Address address, short prefixLen) {
this.address = Ip4Address.makeMaskedAddress(address, prefixLen);
this.prefixLen = prefixLen;
}
/**
* Constructs an IPv4 prefix from a string representation of the
* prefix.
*<p>
* Example: "1.2.0.0/16"
*
* @param value the value to use
*/
public Ip4Prefix(String value) {
String[] splits = value.split("/");
if (splits.length != 2) {
throw new IllegalArgumentException("Specified IPv4 prefix must contain an IPv4 " +
"address and a prefix length separated by '/'");
}
this.prefixLen = Short.decode(splits[1]);
this.address = Ip4Address.makeMaskedAddress(new Ip4Address(splits[0]),
this.prefixLen);
}
/**
* Gets the address value of the IPv4 prefix.
*
* @return the address value of the IPv4 prefix
*/
public Ip4Address getAddress() {
return address;
}
/**
* Gets the prefix length value of the IPv4 prefix.
*
* @return the prefix length value of the IPv4 prefix
*/
public short getPrefixLen() {
return prefixLen;
}
/**
* Converts the IPv4 prefix value to an "address/prefixLen" string.
*
* @return the IPv4 prefix value as an "address/prefixLen" string
*/
@Override
public String toString() {
return this.address.toString() + "/" + this.prefixLen;
}
/**
* Compares the value of two Ip4Prefix objects.
* <p/>
* Note the value of the IPv4 address is compared directly between the
* objects, and must match exactly for the objects to be considered equal.
* This may result in objects which represent the same IP prefix being
* classified as unequal, because the unsignificant bits of the address
* field don't match (the bits to the right of the prefix length).
* <p/>
* TODO Change this behavior so that objects that represent the same prefix
* are classified as equal according to this equals method.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Ip4Prefix)) {
return false;
}
Ip4Prefix otherIp4Prefix = (Ip4Prefix) other;
return Objects.equals(this.address, otherIp4Prefix.address)
&& this.prefixLen == otherIp4Prefix.prefixLen;
}
@Override
public int hashCode() {
return Objects.hash(address, prefixLen);
}
}
package org.onlab.packet;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Objects;
import com.google.common.net.InetAddresses;
import com.google.common.primitives.UnsignedLongs;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* The class representing an IPv6 address.
* This class is immutable.
*/
public final class Ip6Address implements Comparable<Ip6Address> {
private final long valueHigh; // The higher (more significant) 64 bits
private final long valueLow; // The lower (less significant) 64 bits
/** The length of the address in bytes (octets). */
public static final int BYTE_LENGTH = 16;
/** The length of the address in bits. */
public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
/**
* Default constructor.
*/
public Ip6Address() {
this.valueHigh = 0;
this.valueLow = 0;
}
/**
* Copy constructor.
*
* @param other the object to copy from
*/
public Ip6Address(Ip6Address other) {
this.valueHigh = other.valueHigh;
this.valueLow = other.valueLow;
}
/**
* Constructor from integer values.
*
* @param valueHigh the higher (more significant) 64 bits of the address
* @param valueLow the lower (less significant) 64 bits of the address
*/
public Ip6Address(long valueHigh, long valueLow) {
this.valueHigh = valueHigh;
this.valueLow = valueLow;
}
/**
* Constructor from a byte array with the IPv6 address stored in network
* byte order (i.e., the most significant byte first).
*
* @param value the value to use
*/
public Ip6Address(byte[] value) {
this(value, 0);
}
/**
* Constructor from a byte array with the IPv6 address stored in network
* byte order (i.e., the most significant byte first), and a given offset
* from the beginning of the byte array.
*
* @param value the value to use
* @param offset the offset in bytes from the beginning of the byte array
*/
public Ip6Address(byte[] value, int offset) {
checkNotNull(value);
// Verify the arguments
if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
String msg;
if (value.length < BYTE_LENGTH) {
msg = "Invalid IPv6 address array: array length: " +
value.length + ". Must be at least " + BYTE_LENGTH;
} else {
msg = "Invalid IPv6 address array: array offset: " +
offset + ". Must be in the interval [0, " +
(value.length - BYTE_LENGTH) + "]";
}
throw new IllegalArgumentException(msg);
}
// Read the address
ByteBuffer bb = ByteBuffer.wrap(value);
bb.position(offset);
this.valueHigh = bb.getLong();
this.valueLow = bb.getLong();
}
/**
* Constructs an IPv6 address from a string representation of the address.
*<p>
* Example: "1111:2222::8888"
*
* @param value the value to use
*/
public Ip6Address(String value) {
checkNotNull(value);
if (value.isEmpty()) {
final String msg = "Specified IPv6 cannot be an empty string";
throw new IllegalArgumentException(msg);
}
InetAddress addr = null;
try {
addr = InetAddresses.forString(value);
} catch (IllegalArgumentException e) {
final String msg = "Invalid IPv6 address string: " + value;
throw new IllegalArgumentException(msg);
}
byte[] bytes = addr.getAddress();
ByteBuffer bb = ByteBuffer.wrap(bytes);
this.valueHigh = bb.getLong();
this.valueLow = bb.getLong();
}
/**
* Gets the IPv6 address as a byte array.
*
* @return a byte array with the IPv6 address stored in network byte order
* (i.e., the most significant byte first).
*/
public byte[] toOctets() {
return ByteBuffer.allocate(BYTE_LENGTH)
.putLong(valueHigh).putLong(valueLow).array();
}
/**
* Creates an IPv6 network mask prefix.
*
* @param prefixLen the length of the mask prefix. Must be in the interval
* [0, 128].
* @return a new IPv6 address that contains a mask prefix of the
* specified length
*/
public static Ip6Address makeMaskPrefix(int prefixLen) {
long vh, vl;
// Verify the prefix length
if ((prefixLen < 0) || (prefixLen > Ip6Address.BIT_LENGTH)) {
final String msg = "Invalid IPv6 prefix length: " + prefixLen +
". Must be in the interval [0, 128].";
throw new IllegalArgumentException(msg);
}
if (prefixLen == 0) {
//
// NOTE: Apparently, the result of "<< 64" shifting to the left
// results in all 1s instead of all 0s, hence we handle it as
// a special case.
//
vh = 0;
vl = 0;
} else if (prefixLen <= 64) {
vh = (0xffffffffffffffffL << (64 - prefixLen)) & 0xffffffffffffffffL;
vl = 0;
} else {
vh = -1L; // All 1s
vl = (0xffffffffffffffffL << (128 - prefixLen)) & 0xffffffffffffffffL;
}
return new Ip6Address(vh, vl);
}
/**
* Creates an IPv6 address by masking it with a network mask of given
* mask length.
*
* @param addr the address to mask
* @param prefixLen the length of the mask prefix. Must be in the interval
* [0, 128].
* @return a new IPv6 address that is masked with a mask prefix of the
* specified length
*/
public static Ip6Address makeMaskedAddress(final Ip6Address addr,
int prefixLen) {
Ip6Address mask = Ip6Address.makeMaskPrefix(prefixLen);
long vh = addr.valueHigh & mask.valueHigh;
long vl = addr.valueLow & mask.valueLow;
return new Ip6Address(vh, vl);
}
/**
* Gets the value of the higher (more significant) 64 bits of the address.
*
* @return the value of the higher (more significant) 64 bits of the
* address
*/
public long getValueHigh() {
return valueHigh;
}
/**
* Gets the value of the lower (less significant) 64 bits of the address.
*
* @return the value of the lower (less significant) 64 bits of the
* address
*/
public long getValueLow() {
return valueLow;
}
/**
* Converts the IPv6 value to a ':' separated string.
*
* @return the IPv6 value as a ':' separated string
*/
@Override
public String toString() {
ByteBuffer bb = ByteBuffer.allocate(Ip6Address.BYTE_LENGTH);
bb.putLong(valueHigh);
bb.putLong(valueLow);
InetAddress inetAddr = null;
try {
inetAddr = InetAddress.getByAddress(bb.array());
} catch (UnknownHostException e) {
// Should never happen
checkState(false, "Internal error: Ip6Address.toString()");
return "::";
}
return InetAddresses.toAddrString(inetAddr);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Ip6Address)) {
return false;
}
Ip6Address other = (Ip6Address) o;
return this.valueHigh == other.valueHigh
&& this.valueLow == other.valueLow;
}
@Override
public int hashCode() {
return Objects.hash(valueHigh, valueLow);
}
@Override
public int compareTo(Ip6Address o) {
// Compare the high-order 64-bit value
if (this.valueHigh != o.valueHigh) {
return UnsignedLongs.compare(this.valueHigh, o.valueHigh);
}
// Compare the low-order 64-bit value
if (this.valueLow != o.valueLow) {
return UnsignedLongs.compare(this.valueLow, o.valueLow);
}
return 0;
}
}
package org.onlab.packet;
import java.util.Objects;
/**
* The class representing an IPv6 network address.
* This class is immutable.
*/
public final class Ip6Prefix {
private final Ip6Address address; // The IPv6 address
private final short prefixLen; // The prefix length
/**
* Default constructor.
*/
public Ip6Prefix() {
this.address = new Ip6Address();
this.prefixLen = 0;
}
/**
* Copy constructor.
*
* @param other the object to copy from
*/
public Ip6Prefix(Ip6Prefix other) {
this.address = new Ip6Address(other.address);
this.prefixLen = other.prefixLen;
}
/**
* Constructor for a given address and prefix length.
*
* @param address the address to use
* @param prefixLen the prefix length to use
*/
public Ip6Prefix(Ip6Address address, short prefixLen) {
this.address = Ip6Address.makeMaskedAddress(address, prefixLen);
this.prefixLen = prefixLen;
}
/**
* Constructs an IPv6 prefix from a string representation of the
* prefix.
*<p>
* Example: "1111:2222::/32"
*
* @param value the value to use
*/
public Ip6Prefix(String value) {
String[] splits = value.split("/");
if (splits.length != 2) {
throw new IllegalArgumentException("Specified IPv6 prefix must contain an IPv6 " +
"address and a prefix length separated by '/'");
}
this.prefixLen = Short.decode(splits[1]);
this.address = Ip6Address.makeMaskedAddress(new Ip6Address(splits[0]),
this.prefixLen);
}
/**
* Gets the address value of the IPv6 prefix.
*
* @return the address value of the IPv6 prefix
*/
public Ip6Address getAddress() {
return address;
}
/**
* Gets the prefix length value of the IPv6 prefix.
*
* @return the prefix length value of the IPv6 prefix
*/
public short getPrefixLen() {
return prefixLen;
}
/**
* Converts the IPv6 prefix value to an "address/prefixLen" string.
*
* @return the IPv6 prefix value as an "address/prefixLen" string
*/
@Override
public String toString() {
return this.address.toString() + "/" + this.prefixLen;
}
/**
* Compares the value of two Ip6Prefix objects.
* <p/>
* Note the value of the IPv6 address is compared directly between the
* objects, and must match exactly for the objects to be considered equal.
* This may result in objects which represent the same IP prefix being
* classified as unequal, because the unsignificant bits of the address
* field don't match (the bits to the right of the prefix length).
* <p/>
* TODO Change this behavior so that objects that represent the same prefix
* are classified as equal according to this equals method.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Ip6Prefix)) {
return false;
}
Ip6Prefix otherIp6Prefix = (Ip6Prefix) other;
return Objects.equals(this.address, otherIp6Prefix.address)
&& this.prefixLen == otherIp6Prefix.prefixLen;
}
@Override
public int hashCode() {
return Objects.hash(address, prefixLen);
}
}
This diff is collapsed. Click to expand it.
package org.onlab.packet;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
* Tests for class {@link Ip4Prefix}.
*/
public class Ip4PrefixTest {
/**
* Tests the immutability of {@link Ip4Prefix}.
*/
@Test
public void testImmutable() {
assertThatClassIsImmutable(Ip4Prefix.class);
}
/**
* Tests default class constructor.
*/
@Test
public void testDefaultConstructor() {
Ip4Prefix ip4prefix = new Ip4Prefix();
assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
}
/**
* Tests valid class copy constructor.
*/
@Test
public void testCopyConstructor() {
Ip4Prefix fromAddr = new Ip4Prefix("1.2.3.0/24");
Ip4Prefix ip4prefix = new Ip4Prefix(fromAddr);
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
fromAddr = new Ip4Prefix("0.0.0.0/0");
ip4prefix = new Ip4Prefix(fromAddr);
assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
fromAddr = new Ip4Prefix("255.255.255.255/32");
ip4prefix = new Ip4Prefix(fromAddr);
assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
}
/**
* Tests invalid class copy constructor for a null object to copy from.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullObject() {
Ip4Prefix fromAddr = null;
Ip4Prefix ip4prefix = new Ip4Prefix(fromAddr);
}
/**
* Tests valid class constructor for an address and prefix length.
*/
@Test
public void testConstructorForAddressAndPrefixLength() {
Ip4Prefix ip4prefix =
new Ip4Prefix(new Ip4Address("1.2.3.0"), (short) 24);
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix(new Ip4Address("1.2.3.4"), (short) 24);
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix(new Ip4Address("1.2.3.5"), (short) 32);
assertThat(ip4prefix.toString(), is("1.2.3.5/32"));
ip4prefix = new Ip4Prefix(new Ip4Address("0.0.0.0"), (short) 0);
assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
ip4prefix =
new Ip4Prefix(new Ip4Address("255.255.255.255"), (short) 32);
assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
}
/**
* Tests valid class constructor for a string.
*/
@Test
public void testConstructorForString() {
Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix("1.2.3.4/24");
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix("1.2.3.5/32");
assertThat(ip4prefix.toString(), is("1.2.3.5/32"));
ip4prefix = new Ip4Prefix("0.0.0.0/0");
assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
ip4prefix = new Ip4Prefix("255.255.255.255/32");
assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
}
/**
* Tests invalid class constructor for a null string.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullString() {
String fromString = null;
Ip4Prefix ip4prefix = new Ip4Prefix(fromString);
}
/**
* Tests invalid class constructor for an empty string.
*/
@Test(expected = IllegalArgumentException.class)
public void testInvalidConstructors() {
// Check constructor for invalid ID: empty string
Ip4Prefix ip4prefix = new Ip4Prefix("");
}
/**
* Tests getting the value of an address.
*/
@Test
public void testGetValue() {
Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
assertThat(ip4prefix.getAddress(), equalTo(new Ip4Address("1.2.3.0")));
assertThat(ip4prefix.getPrefixLen(), is((short) 24));
ip4prefix = new Ip4Prefix("0.0.0.0/0");
assertThat(ip4prefix.getAddress(), equalTo(new Ip4Address("0.0.0.0")));
assertThat(ip4prefix.getPrefixLen(), is((short) 0));
ip4prefix = new Ip4Prefix("255.255.255.255/32");
assertThat(ip4prefix.getAddress(),
equalTo(new Ip4Address("255.255.255.255")));
assertThat(ip4prefix.getPrefixLen(), is((short) 32));
}
/**
* Tests equality of {@link Ip4Address}.
*/
@Test
public void testEquality() {
Ip4Prefix addr1net = new Ip4Prefix("1.2.3.0/24");
Ip4Prefix addr2net = new Ip4Prefix("1.2.3.0/24");
assertThat(addr1net, is(addr2net));
addr1net = new Ip4Prefix("1.2.3.0/24");
addr2net = new Ip4Prefix("1.2.3.4/24");
assertThat(addr1net, is(addr2net));
addr1net = new Ip4Prefix("0.0.0.0/0");
addr2net = new Ip4Prefix("0.0.0.0/0");
assertThat(addr1net, is(addr2net));
addr1net = new Ip4Prefix("255.255.255.255/32");
addr2net = new Ip4Prefix("255.255.255.255/32");
assertThat(addr1net, is(addr2net));
}
/**
* Tests non-equality of {@link Ip4Address}.
*/
@Test
public void testNonEquality() {
Ip4Prefix addr1net = new Ip4Prefix("1.2.0.0/16");
Ip4Prefix addr2net = new Ip4Prefix("1.3.0.0/16");
Ip4Prefix addr3net = new Ip4Prefix("1.3.0.0/24");
Ip4Prefix addr4net = new Ip4Prefix("0.0.0.0/0");
Ip4Prefix addr5net = new Ip4Prefix("255.255.255.255/32");
assertThat(addr1net, is(not(addr2net)));
assertThat(addr3net, is(not(addr2net)));
assertThat(addr4net, is(not(addr2net)));
assertThat(addr5net, is(not(addr2net)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix("1.2.3.4/24");
assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
ip4prefix = new Ip4Prefix("0.0.0.0/0");
assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
ip4prefix = new Ip4Prefix("255.255.255.255/32");
assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
}
}
This diff is collapsed. Click to expand it.
package org.onlab.packet;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
* Tests for class {@link Ip6Prefix}.
*/
public class Ip6PrefixTest {
/**
* Tests the immutability of {@link Ip6Prefix}.
*/
@Test
public void testImmutable() {
assertThatClassIsImmutable(Ip6Prefix.class);
}
/**
* Tests default class constructor.
*/
@Test
public void testDefaultConstructor() {
Ip6Prefix ip6prefix = new Ip6Prefix();
assertThat(ip6prefix.toString(), is("::/0"));
}
/**
* Tests valid class copy constructor.
*/
@Test
public void testCopyConstructor() {
Ip6Prefix fromAddr = new Ip6Prefix("1100::/8");
Ip6Prefix ip6prefix = new Ip6Prefix(fromAddr);
assertThat(ip6prefix.toString(), is("1100::/8"));
fromAddr = new Ip6Prefix("::/0");
ip6prefix = new Ip6Prefix(fromAddr);
assertThat(ip6prefix.toString(), is("::/0"));
fromAddr =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
ip6prefix = new Ip6Prefix(fromAddr);
assertThat(ip6prefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
}
/**
* Tests invalid class copy constructor for a null object to copy from.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullObject() {
Ip6Prefix fromAddr = null;
Ip6Prefix ip6prefix = new Ip6Prefix(fromAddr);
}
/**
* Tests valid class constructor for an address and prefix length.
*/
@Test
public void testConstructorForAddressAndPrefixLength() {
Ip6Prefix ip6prefix =
new Ip6Prefix(new Ip6Address("1100::"), (short) 8);
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix =
new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
(short) 8);
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix =
new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8800"),
(short) 120);
assertThat(ip6prefix.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
ip6prefix = new Ip6Prefix(new Ip6Address("::"), (short) 0);
assertThat(ip6prefix.toString(), is("::/0"));
ip6prefix =
new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
(short) 128);
assertThat(ip6prefix.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8885/128"));
ip6prefix =
new Ip6Prefix(new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
(short) 128);
assertThat(ip6prefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
ip6prefix =
new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
(short) 64);
assertThat(ip6prefix.toString(), is("1111:2222:3333:4444::/64"));
}
/**
* Tests valid class constructor for a string.
*/
@Test
public void testConstructorForString() {
Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8800/120");
assertThat(ip6prefix.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
ip6prefix = new Ip6Prefix("::/0");
assertThat(ip6prefix.toString(), is("::/0"));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/128");
assertThat(ip6prefix.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8885/128"));
ip6prefix = new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
assertThat(ip6prefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/64");
assertThat(ip6prefix.toString(), is("1111:2222:3333:4444::/64"));
}
/**
* Tests invalid class constructor for a null string.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullString() {
String fromString = null;
Ip6Prefix ip6prefix = new Ip6Prefix(fromString);
}
/**
* Tests invalid class constructor for an empty string.
*/
@Test(expected = IllegalArgumentException.class)
public void testInvalidConstructors() {
// Check constructor for invalid ID: empty string
Ip6Prefix ip6prefix = new Ip6Prefix("");
}
/**
* Tests getting the value of an address.
*/
@Test
public void testGetValue() {
Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("1100::")));
assertThat(ip6prefix.getPrefixLen(), is((short) 8));
ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("1100::")));
assertThat(ip6prefix.getPrefixLen(), is((short) 8));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8800/120");
assertThat(ip6prefix.getAddress(),
equalTo(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8800")));
assertThat(ip6prefix.getPrefixLen(), is((short) 120));
ip6prefix = new Ip6Prefix("::/0");
assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("::")));
assertThat(ip6prefix.getPrefixLen(), is((short) 0));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/128");
assertThat(ip6prefix.getAddress(),
equalTo(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885")));
assertThat(ip6prefix.getPrefixLen(), is((short) 128));
ip6prefix =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
assertThat(ip6prefix.getAddress(),
equalTo(new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
assertThat(ip6prefix.getPrefixLen(), is((short) 128));
ip6prefix =
new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/64");
assertThat(ip6prefix.getAddress(),
equalTo(new Ip6Address("1111:2222:3333:4444::")));
assertThat(ip6prefix.getPrefixLen(), is((short) 64));
}
/**
* Tests equality of {@link Ip6Address}.
*/
@Test
public void testEquality() {
Ip6Prefix addr1net = new Ip6Prefix("1100::/8");
Ip6Prefix addr2net = new Ip6Prefix("1100::/8");
assertThat(addr1net, is(addr2net));
addr1net = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
addr2net = new Ip6Prefix("1100::/8");
assertThat(addr1net, is(addr2net));
addr1net = new Ip6Prefix("::/0");
addr2net = new Ip6Prefix("::/0");
assertThat(addr1net, is(addr2net));
addr1net =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
addr2net =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
assertThat(addr1net, is(addr2net));
}
/**
* Tests non-equality of {@link Ip6Address}.
*/
@Test
public void testNonEquality() {
Ip6Prefix addr1net = new Ip6Prefix("1100::/8");
Ip6Prefix addr2net = new Ip6Prefix("1200::/8");
Ip6Prefix addr3net = new Ip6Prefix("1200::/12");
Ip6Prefix addr4net = new Ip6Prefix("::/0");
Ip6Prefix addr5net =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
assertThat(addr1net, is(not(addr2net)));
assertThat(addr3net, is(not(addr2net)));
assertThat(addr4net, is(not(addr2net)));
assertThat(addr5net, is(not(addr2net)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
assertThat(ip6prefix.toString(), is("1100::/8"));
ip6prefix = new Ip6Prefix("::/0");
assertThat(ip6prefix.toString(), is("::/0"));
ip6prefix =
new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
assertThat(ip6prefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
}
}