From 13d05bc8458758ee39cb829098241e89616717ee Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Wed, 9 Sep 2015 22:15:21 -0700 Subject: ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60 Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd --- framework/src/onos/apps/dhcp/pom.xml | 161 +++++ .../java/org/onosproject/dhcp/DhcpService.java | 80 +++ .../main/java/org/onosproject/dhcp/DhcpStore.java | 107 ++++ .../java/org/onosproject/dhcp/IpAssignment.java | 206 +++++++ .../org/onosproject/dhcp/cli/DhcpLeaseDetails.java | 41 ++ .../onosproject/dhcp/cli/DhcpListAllMappings.java | 44 ++ .../dhcp/cli/DhcpRemoveStaticMapping.java | 56 ++ .../onosproject/dhcp/cli/DhcpSetStaticMapping.java | 61 ++ .../org/onosproject/dhcp/cli/FreeIpCompleter.java | 48 ++ .../org/onosproject/dhcp/cli/MacIdCompleter.java | 48 ++ .../java/org/onosproject/dhcp/impl/DhcpConfig.java | 227 +++++++ .../org/onosproject/dhcp/impl/DhcpManager.java | 669 +++++++++++++++++++++ .../org/onosproject/dhcp/impl/DhcpStoreConfig.java | 107 ++++ .../java/org/onosproject/dhcp/impl/DhcpUi.java | 74 +++ .../dhcp/impl/DhcpViewMessageHandler.java | 96 +++ .../dhcp/impl/DistributedDhcpStore.java | 324 ++++++++++ .../org/onosproject/dhcp/rest/DHCPWebResource.java | 163 +++++ .../resources/OSGI-INF/blueprint/shell-config.xml | 43 ++ .../dhcp/src/main/resources/app/view/dhcp/dhcp.css | 27 + .../src/main/resources/app/view/dhcp/dhcp.html | 47 ++ .../dhcp/src/main/resources/app/view/dhcp/dhcp.js | 51 ++ .../onos/apps/dhcp/src/main/resources/gui/css.html | 1 + .../onos/apps/dhcp/src/main/resources/gui/js.html | 1 + .../onos/apps/dhcp/src/main/webapp/WEB-INF/web.xml | 43 ++ .../org/onosproject/dhcp/IpAssignmentTest.java | 101 ++++ .../org/onosproject/dhcp/impl/DhcpManagerTest.java | 383 ++++++++++++ .../apps/dhcp/src/test/resources/dhcp-cfg.json | 24 + 27 files changed, 3233 insertions(+) create mode 100644 framework/src/onos/apps/dhcp/pom.xml create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpService.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/IpAssignment.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpStoreConfig.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java create mode 100644 framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/OSGI-INF/blueprint/shell-config.xml create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.css create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.html create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.js create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/gui/css.html create mode 100644 framework/src/onos/apps/dhcp/src/main/resources/gui/js.html create mode 100644 framework/src/onos/apps/dhcp/src/main/webapp/WEB-INF/web.xml create mode 100644 framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/IpAssignmentTest.java create mode 100644 framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java create mode 100644 framework/src/onos/apps/dhcp/src/test/resources/dhcp-cfg.json (limited to 'framework/src/onos/apps/dhcp') diff --git a/framework/src/onos/apps/dhcp/pom.xml b/framework/src/onos/apps/dhcp/pom.xml new file mode 100644 index 00000000..13298163 --- /dev/null +++ b/framework/src/onos/apps/dhcp/pom.xml @@ -0,0 +1,161 @@ + + + 4.0.0 + + + onos-apps + org.onosproject + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-app-dhcp + bundle + + http://onosproject.org + + DHCP Server application + + + org.onosproject.dhcp + /onos/dhcp + 1.0.0 + DHCP Server REST API + + APIs for interacting with the DHCP Server application. + + org.onosproject.dhcp.rest + + + + + org.osgi + org.osgi.compendium + + + + org.onosproject + onos-cli + ${project.version} + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + compile + + + + org.onosproject + onlab-junit + test + + + org.onosproject + onos-core-serializers + ${project.version} + + + + org.onosproject + onos-incubator-api + ${project.version} + + + org.onosproject + onos-api + ${project.version} + tests + test + + + + org.onosproject + onos-rest + ${project.version} + + + org.onosproject + onlab-rest + ${project.version} + + + javax.ws.rs + jsr311-api + 1.1.1 + + + com.sun.jersey + jersey-servlet + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + <_wab>src/main/webapp/ + + WEB-INF/classes/apidoc/swagger.json=target/swagger.json, + {maven-resources} + + + ${project.groupId}.${project.artifactId} + + + org.slf4j, + org.osgi.framework, + javax.ws.rs, + javax.ws.rs.core, + com.sun.jersey.api.core, + com.sun.jersey.spi.container.servlet, + com.sun.jersey.server.impl.container.servlet, + com.fasterxml.jackson.databind, + com.fasterxml.jackson.databind.node, + com.fasterxml.jackson.core, + org.apache.karaf.shell.commands, + org.apache.karaf.shell.console, + com.google.common.*, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onlab.util.*, + org.jboss.netty.util.* + + ${web.context} + + + + + + + diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpService.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpService.java new file mode 100644 index 00000000..20e1c614 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpService.java @@ -0,0 +1,80 @@ +/* + * Copyright 2015 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.dhcp; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; + +import java.util.Map; + +/** + * DHCP Service Interface. + */ +public interface DhcpService { + + /** + * Returns a collection of all the MacAddress to IPAddress mapping. + * + * @return collection of mappings. + */ + Map listMapping(); + + /** + * Returns the default lease time granted by the DHCP Server. + * + * @return lease time + */ + int getLeaseTime(); + + /** + * Returns the default renewal time granted by the DHCP Server. + * + * @return renewal time + */ + int getRenewalTime(); + + /** + * Returns the default rebinding time granted by the DHCP Server. + * + * @return rebinding time + */ + int getRebindingTime(); + + /** + * Registers a static IP mapping with the DHCP Server. + * + * @param macID macID of the client + * @param ipAddress IP Address requested for the client + * @return true if the mapping was successfully registered, false otherwise + */ + boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress); + + /** + * Removes a static IP mapping with the DHCP Server. + * + * @param macID macID of the client + * @return true if the mapping was successfully removed, false otherwise + */ + boolean removeStaticMapping(MacAddress macID); + + /** + * Returns the list of all the available IPs with the server. + * + * @return list of available IPs + */ + Iterable getAvailableIPs(); + +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java new file mode 100644 index 00000000..4e2d67d6 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015 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.dhcp; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; + +import java.util.Map; + +/** + * DHCPStore Interface. + */ +public interface DhcpStore { + + /** + * Appends all the IPs in a given range to the free pool of IPs. + * + * @param startIP Start IP for the range + * @param endIP End IP for the range + */ + void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP); + + /** + * Returns an IP Address for a Mac ID, in response to a DHCP DISCOVER message. + * + * @param macID Mac ID of the client requesting an IP + * @param requestedIP requested IP address + * @return IP address assigned to the Mac ID + */ + Ip4Address suggestIP(MacAddress macID, Ip4Address requestedIP); + + /** + * Assigns the requested IP to the Mac ID, in response to a DHCP REQUEST message. + * + * @param macID Mac Id of the client requesting an IP + * @param ipAddr IP Address being requested + * @param leaseTime Lease time offered by the server for this mapping + * @return returns true if the assignment was successful, false otherwise + */ + boolean assignIP(MacAddress macID, Ip4Address ipAddr, int leaseTime); + + /** + * Sets the default time for which suggested IP mappings are valid. + * + * @param timeInSeconds default time for IP mappings to be valid + */ + void setDefaultTimeoutForPurge(int timeInSeconds); + + /** + * Sets the delay after which the dhcp server will purge expired entries. + * + * @param timeInSeconds default time + */ + void setTimerDelay(int timeInSeconds); + + /** + * Releases the IP assigned to a Mac ID into the free pool. + * + * @param macID the macID for which the mapping needs to be changed + */ + void releaseIP(MacAddress macID); + + /** + * Returns a collection of all the MacAddress to IPAddress mapping. + * + * @return the collection of the mappings + */ + Map listMapping(); + + /** + * Assigns the requested IP to the MAC ID (if available) for an indefinite period of time. + * + * @param macID macID of the client + * @param ipAddr IP Address requested for the client + * @return true if the mapping was successfully registered, false otherwise + */ + boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr); + + /** + * Removes a static IP mapping associated with the given MAC ID from the DHCP Server. + * + * @param macID macID of the client + * @return true if the mapping was successfully registered, false otherwise + */ + boolean removeStaticIP(MacAddress macID); + + /** + * Returns the list of all the available IPs with the server. + * + * @return list of available IPs + */ + Iterable getAvailableIPs(); + +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/IpAssignment.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/IpAssignment.java new file mode 100644 index 00000000..c8bd1906 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/IpAssignment.java @@ -0,0 +1,206 @@ +/* + * 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.dhcp; + +import com.google.common.base.MoreObjects; +import org.onlab.packet.Ip4Address; + +import java.util.Date; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores the MAC ID to IP Address mapping details. + */ +public final class IpAssignment { + + private final Ip4Address ipAddress; + + private final Date timestamp; + + private final long leasePeriod; + + private final AssignmentStatus assignmentStatus; + + public enum AssignmentStatus { + /** + * IP has been requested by a host, but not assigned to it yet. + */ + Option_Requested, + + /** + * IP has been assigned to a host. + */ + Option_Assigned, + + /** + * IP mapping is no longer active. + */ + Option_Expired; + } + + /** + * Constructor for IPAssignment, where the ipAddress, the lease period, the timestamp + * and assignment status is supplied. + * + * @param ipAddress + * @param leasePeriod + * @param assignmentStatus + */ + private IpAssignment(Ip4Address ipAddress, + long leasePeriod, + Date timestamp, + AssignmentStatus assignmentStatus) { + this.ipAddress = ipAddress; + this.leasePeriod = leasePeriod; + this.timestamp = timestamp; + this.assignmentStatus = assignmentStatus; + } + + /** + * Returns the IP Address of the IP assignment. + * + * @return the IP address + */ + public Ip4Address ipAddress() { + return this.ipAddress; + } + + /** + * Returns the timestamp of the IP assignment. + * + * @return the timestamp + */ + public Date timestamp() { + return this.timestamp; + } + + /** + * Returns the assignment status of the IP assignment. + * + * @return the assignment status + */ + public AssignmentStatus assignmentStatus() { + return this.assignmentStatus; + } + + /** + * Returns the lease period of the IP assignment. + * + * @return the lease period + */ + public int leasePeriod() { + return (int) this.leasePeriod / 1000; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("ip", ipAddress) + .add("timestamp", timestamp) + .add("lease", leasePeriod) + .add("assignmentStatus", assignmentStatus) + .toString(); + } + + /** + * Creates and returns a new builder instance. + * + * @return new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Creates and returns a new builder instance that clones an existing IPAssignment. + * + * @param assignment ip address assignment + * @return new builder + */ + public static Builder builder(IpAssignment assignment) { + return new Builder(assignment); + } + + /** + * IPAssignment Builder. + */ + public static final class Builder { + + private Ip4Address ipAddress; + + private Date timeStamp; + + private long leasePeriod; + + private AssignmentStatus assignmentStatus; + + private Builder() { + + } + + private Builder(IpAssignment ipAssignment) { + ipAddress = ipAssignment.ipAddress(); + timeStamp = ipAssignment.timestamp(); + leasePeriod = ipAssignment.leasePeriod() * 1000; + assignmentStatus = ipAssignment.assignmentStatus(); + } + + public IpAssignment build() { + validateInputs(); + return new IpAssignment(ipAddress, + leasePeriod, + timeStamp, + assignmentStatus); + } + + public Builder ipAddress(Ip4Address addr) { + ipAddress = addr; + return this; + } + + public Builder timestamp(Date timestamp) { + timeStamp = timestamp; + return this; + } + + public Builder leasePeriod(int leasePeriodinSeconds) { + leasePeriod = leasePeriodinSeconds * 1000; + return this; + } + + public Builder assignmentStatus(AssignmentStatus status) { + assignmentStatus = status; + return this; + } + + private void validateInputs() { + checkNotNull(ipAddress, "IP Address must be specified"); + checkNotNull(assignmentStatus, "Assignment Status must be specified"); + checkNotNull(leasePeriod, "Lease Period must be specified"); + checkNotNull(timeStamp, "Timestamp must be specified"); + + switch (assignmentStatus) { + case Option_Requested: + case Option_Assigned: + case Option_Expired: + break; + default: + throw new IllegalStateException("Unknown assignment status"); + } + } + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java new file mode 100644 index 00000000..95f49e69 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java @@ -0,0 +1,41 @@ +/* + * 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.dhcp.cli; + +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; + +/** + * Lists all the default lease parameters offered by the DHCP Server. + */ +@Command(scope = "onos", name = "dhcp-lease", + description = "Lists all the default lease parameters offered by the DHCP Server") +public class DhcpLeaseDetails extends AbstractShellCommand { + + private static final String DHCP_LEASE_FORMAT = "Lease Time: %ds\nRenewal Time: %ds\nRebinding Time: %ds"; + + @Override + protected void execute() { + + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + int leaseTime = dhcpService.getLeaseTime(); + int renewTime = dhcpService.getRenewalTime(); + int rebindTime = dhcpService.getRebindingTime(); + + print(DHCP_LEASE_FORMAT, leaseTime, renewTime, rebindTime); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java new file mode 100644 index 00000000..fc470ce3 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 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.dhcp.cli; + +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; + +import java.util.Map; + +/** + * Lists all the MacAddress to IP Address mappings held by the DHCP Server. + */ +@Command(scope = "onos", name = "dhcp-list", + description = "Lists all the MAC to IP mappings held by the DHCP Server") +public class DhcpListAllMappings extends AbstractShellCommand { + + private static final String DHCP_MAPPING_FORMAT = "MAC ID: %s -> IP ASSIGNED %s"; + @Override + protected void execute() { + + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + Map allocationMap = dhcpService.listMapping(); + + for (Map.Entry entry : allocationMap.entrySet()) { + print(DHCP_MAPPING_FORMAT, entry.getKey().toString(), entry.getValue().ipAddress().toString()); + } + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java new file mode 100644 index 00000000..a92cd250 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java @@ -0,0 +1,56 @@ +/* + * 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.dhcp.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; + +/** + * Removes a static MAC Address to IP Mapping from the DHCP Server. + */ +@Command(scope = "onos", name = "dhcp-remove-static-mapping", + description = "Removes a static MAC Address to IP Mapping from the DHCP Server") +public class DhcpRemoveStaticMapping extends AbstractShellCommand { + + @Argument(index = 0, name = "macAddr", + description = "MAC Address of the client", + required = true, multiValued = false) + String macAddr = null; + + private static final String DHCP_SUCCESS = "Static Mapping Successfully Removed."; + private static final String DHCP_FAILURE = "Static Mapping Removal Failed. " + + "Either the mapping does not exist or it is not static."; + + @Override + protected void execute() { + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + + try { + MacAddress macID = MacAddress.valueOf(macAddr); + if (dhcpService.removeStaticMapping(macID)) { + print(DHCP_SUCCESS); + } else { + print(DHCP_FAILURE); + } + + } catch (IllegalArgumentException e) { + print(e.getMessage()); + } + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java new file mode 100644 index 00000000..9f4f6580 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java @@ -0,0 +1,61 @@ +/* + * 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.dhcp.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; + +/** + * Registers a static MAC Address to IP Mapping with the DHCP Server. + */ +@Command(scope = "onos", name = "dhcp-set-static-mapping", + description = "Registers a static MAC Address to IP Mapping with the DHCP Server") +public class DhcpSetStaticMapping extends AbstractShellCommand { + + @Argument(index = 0, name = "macAddr", + description = "MAC Address of the client", + required = true, multiValued = false) + String macAddr = null; + + @Argument(index = 1, name = "ipAddr", + description = "IP Address requested for static mapping", + required = true, multiValued = false) + String ipAddr = null; + + private static final String DHCP_SUCCESS = "Static Mapping Successfully Added."; + private static final String DHCP_FAILURE = "Static Mapping Failed. The IP maybe unavailable."; + @Override + protected void execute() { + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + + try { + MacAddress macID = MacAddress.valueOf(macAddr); + Ip4Address ipAddress = Ip4Address.valueOf(ipAddr); + if (dhcpService.setStaticMapping(macID, ipAddress)) { + print(DHCP_SUCCESS); + } else { + print(DHCP_FAILURE); + } + + } catch (IllegalArgumentException e) { + print(e.getMessage()); + } + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java new file mode 100644 index 00000000..228d70fd --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java @@ -0,0 +1,48 @@ +/* + * 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.dhcp.cli; + +import org.apache.karaf.shell.console.Completer; +import org.apache.karaf.shell.console.completer.StringsCompleter; +import org.onlab.packet.Ip4Address; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; + +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; + +/** + * Free IP Completer. + */ +public class FreeIpCompleter implements Completer { + + @Override + public int complete(String buffer, int cursor, List candidates) { + // Delegate string completer + StringsCompleter delegate = new StringsCompleter(); + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + Iterator it = dhcpService.getAvailableIPs().iterator(); + SortedSet strings = delegate.getStrings(); + + while (it.hasNext()) { + strings.add(it.next().toString()); + } + + // Now let the completer do the work for figuring out what to offer. + return delegate.complete(buffer, cursor, candidates); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java new file mode 100644 index 00000000..d6cd73a7 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java @@ -0,0 +1,48 @@ +/* + * 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.dhcp.cli; + +import org.apache.karaf.shell.console.Completer; +import org.apache.karaf.shell.console.completer.StringsCompleter; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.Host; +import org.onosproject.net.host.HostService; + +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; + +/** + * MAC ID Completer. + */ +public class MacIdCompleter implements Completer { + + @Override + public int complete(String buffer, int cursor, List candidates) { + // Delegate string completer + StringsCompleter delegate = new StringsCompleter(); + HostService service = AbstractShellCommand.get(HostService.class); + Iterator it = service.getHosts().iterator(); + SortedSet strings = delegate.getStrings(); + + while (it.hasNext()) { + strings.add(it.next().mac().toString()); + } + + // Now let the completer do the work for figuring out what to offer. + return delegate.complete(buffer, cursor, candidates); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java new file mode 100644 index 00000000..f8d5e63b --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java @@ -0,0 +1,227 @@ +/* + * 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.dhcp.impl; + +import org.onosproject.core.ApplicationId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.basics.BasicElementConfig; + +/** + * DHCP Config class. + */ +public class DhcpConfig extends Config { + + public static final String MY_IP = "ip"; + public static final String MY_MAC = "mac"; + public static final String SUBNET_MASK = "subnet"; + public static final String BROADCAST_ADDRESS = "broadcast"; + public static final String ROUTER_ADDRESS = "router"; + public static final String DOMAIN_SERVER = "domain"; + public static final String TTL = "ttl"; + public static final String LEASE_TIME = "lease"; + public static final String RENEW_TIME = "renew"; + public static final String REBIND_TIME = "rebind"; + + /** + * Returns the dhcp server ip. + * + * @return ip address or null if not set + */ + public String ip() { + return get(MY_IP, null); + } + + /** + * Sets the dhcp server ip. + * + * @param ip new ip address; null to clear + * @return self + */ + public BasicElementConfig ip(String ip) { + return (BasicElementConfig) setOrClear(MY_IP, ip); + } + + /** + * Returns the dhcp server mac. + * + * @return server mac or null if not set + */ + public String mac() { + return get(MY_MAC, null); + } + + /** + * Sets the dhcp server mac. + * + * @param mac new mac address; null to clear + * @return self + */ + public BasicElementConfig mac(String mac) { + return (BasicElementConfig) setOrClear(MY_MAC, mac); + } + + /** + * Returns the subnet mask. + * + * @return subnet mask or null if not set + */ + public String subnetMask() { + return get(SUBNET_MASK, null); + } + + /** + * Sets the subnet mask. + * + * @param subnet new subnet mask; null to clear + * @return self + */ + public BasicElementConfig subnetMask(String subnet) { + return (BasicElementConfig) setOrClear(SUBNET_MASK, subnet); + } + + /** + * Returns the broadcast address. + * + * @return broadcast address or null if not set + */ + public String broadcastAddress() { + return get(BROADCAST_ADDRESS, null); + } + + /** + * Sets the broadcast address. + * + * @param broadcast new broadcast address; null to clear + * @return self + */ + public BasicElementConfig broadcastAddress(String broadcast) { + return (BasicElementConfig) setOrClear(BROADCAST_ADDRESS, broadcast); + } + + /** + * Returns the Time To Live for the reply packets. + * + * @return ttl or null if not set + */ + public String ttl() { + return get(TTL, null); + } + + /** + * Sets the Time To Live for the reply packets. + * + * @param ttl new ttl; null to clear + * @return self + */ + public BasicElementConfig ttl(String ttl) { + return (BasicElementConfig) setOrClear(TTL, ttl); + } + + /** + * Returns the Lease Time offered by the DHCP Server. + * + * @return lease time or null if not set + */ + public String leaseTime() { + return get(LEASE_TIME, null); + } + + /** + * Sets the Lease Time offered by the DHCP Server. + * + * @param lease new lease time; null to clear + * @return self + */ + public BasicElementConfig leaseTime(String lease) { + return (BasicElementConfig) setOrClear(LEASE_TIME, lease); + } + + /** + * Returns the Renew Time offered by the DHCP Server. + * + * @return renew time or null if not set + */ + public String renewTime() { + return get(RENEW_TIME, null); + } + + /** + * Sets the Renew Time offered by the DHCP Server. + * + * @param renew new renew time; null to clear + * @return self + */ + public BasicElementConfig renewTime(String renew) { + return (BasicElementConfig) setOrClear(RENEW_TIME, renew); + } + + /** + * Returns the Rebind Time offered by the DHCP Server. + * + * @return rebind time or null if not set + */ + public String rebindTime() { + return get(REBIND_TIME, null); + } + + /** + * Sets the Rebind Time offered by the DHCP Server. + * + * @param rebind new rebind time; null to clear + * @return self + */ + public BasicElementConfig rebindTime(String rebind) { + return (BasicElementConfig) setOrClear(REBIND_TIME, rebind); + } + + /** + * Returns the Router Address. + * + * @return router address or null if not set + */ + public String routerAddress() { + return get(ROUTER_ADDRESS, null); + } + + /** + * Sets the Router Address. + * + * @param router new router address; null to clear + * @return self + */ + public BasicElementConfig routerAddress(String router) { + return (BasicElementConfig) setOrClear(ROUTER_ADDRESS, router); + } + + /** + * Returns the Domain Server Address. + * + * @return domain server address or null if not set + */ + public String domainServer() { + return get(DOMAIN_SERVER, null); + } + + /** + * Sets the Domain Server Address. + * + * @param domain new domain server address; null to clear + * @return self + */ + public BasicElementConfig domainServer(String domain) { + return (BasicElementConfig) setOrClear(DOMAIN_SERVER, domain); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java new file mode 100644 index 00000000..24cb0878 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java @@ -0,0 +1,669 @@ +/* + * 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.dhcp.impl; + +import com.google.common.collect.ImmutableSet; +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.apache.felix.scr.annotations.Service; +import org.onlab.packet.ARP; +import org.onlab.packet.DHCP; +import org.onlab.packet.DHCPOption; +import org.onlab.packet.DHCPPacketType; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.TpPort; +import org.onlab.packet.UDP; +import org.onlab.packet.VlanId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.DhcpStore; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigRegistry; + +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.HostProviderRegistry; +import org.onosproject.net.host.HostProviderService; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.onlab.packet.MacAddress.valueOf; +import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; + +/** + * Skeletal ONOS DHCP Server application. + */ +@Component(immediate = true) +@Service +public class DhcpManager implements DhcpService { + + private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true); + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final NetworkConfigListener cfgListener = new InternalConfigListener(); + + private final Set factories = ImmutableSet.of( + new ConfigFactory(APP_SUBJECT_FACTORY, + DhcpConfig.class, + "dhcp") { + @Override + public DhcpConfig createConfig() { + return new DhcpConfig(); + } + }, + new ConfigFactory(APP_SUBJECT_FACTORY, + DhcpStoreConfig.class, + "dhcpstore") { + @Override + public DhcpStoreConfig createConfig() { + return new DhcpStoreConfig(); + } + } + ); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry cfgService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + private DHCPPacketProcessor processor = new DHCPPacketProcessor(); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DhcpStore dhcpStore; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostProviderRegistry hostProviderRegistry; + + protected HostProviderService hostProviderService; + + private ApplicationId appId; + + // Hardcoded values are default values. + + private static String myIP = "10.0.0.2"; + + private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f"); + + /** + * leaseTime - 10 mins or 600s. + * renewalTime - 5 mins or 300s. + * rebindingTime - 6 mins or 360s. + */ + + private static int leaseTime = 600; + + private static int renewalTime = 300; + + private static int rebindingTime = 360; + + private static byte packetTTL = (byte) 127; + + private static String subnetMask = "255.0.0.0"; + + private static String broadcastAddress = "10.255.255.255"; + + private static String routerAddress = "10.0.0.2"; + + private static String domainServer = "10.0.0.2"; + private final HostProvider hostProvider = new InternalHostProvider(); + + @Activate + protected void activate() { + // start the dhcp server + appId = coreService.registerApplication("org.onosproject.dhcp"); + + cfgService.addListener(cfgListener); + factories.forEach(cfgService::registerConfigFactory); + hostProviderService = hostProviderRegistry.register(hostProvider); + packetService.addProcessor(processor, PacketProcessor.observer(1)); + requestPackets(); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + cfgService.removeListener(cfgListener); + factories.forEach(cfgService::unregisterConfigFactory); + packetService.removeProcessor(processor); + hostProviderRegistry.unregister(hostProvider); + hostProviderService = null; + cancelPackets(); + log.info("Stopped"); + } + + /** + * Request packet in via PacketService. + */ + private void requestPackets() { + + TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_IPV4) + .matchIPProtocol(IPv4.PROTOCOL_UDP) + .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT)) + .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT)); + packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId); + + selectorServer = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_ARP); + packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId); + } + + /** + * Cancel requested packets in via packet service. + */ + private void cancelPackets() { + TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_IPV4) + .matchIPProtocol(IPv4.PROTOCOL_UDP) + .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT)) + .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT)); + packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId); + + selectorServer = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_ARP); + packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId); + } + + @Override + public Map listMapping() { + return dhcpStore.listMapping(); + } + + @Override + public int getLeaseTime() { + return leaseTime; + } + + @Override + public int getRenewalTime() { + return renewalTime; + } + + @Override + public int getRebindingTime() { + return rebindingTime; + } + + @Override + public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress) { + return dhcpStore.assignStaticIP(macID, ipAddress); + } + + @Override + public boolean removeStaticMapping(MacAddress macID) { + return dhcpStore.removeStaticIP(macID); + } + + @Override + public Iterable getAvailableIPs() { + return dhcpStore.getAvailableIPs(); + } + + private class DHCPPacketProcessor implements PacketProcessor { + + /** + * Builds the DHCP Reply packet. + * + * @param packet the incoming Ethernet frame + * @param ipOffered the IP offered by the DHCP Server + * @param outgoingMessageType the message type of the outgoing packet + * @return the Ethernet reply frame + */ + private Ethernet buildReply(Ethernet packet, String ipOffered, byte outgoingMessageType) { + Ip4Address myIPAddress = Ip4Address.valueOf(myIP); + Ip4Address ipAddress; + + // Ethernet Frame. + Ethernet ethReply = new Ethernet(); + ethReply.setSourceMACAddress(myMAC); + ethReply.setDestinationMACAddress(packet.getSourceMAC()); + ethReply.setEtherType(Ethernet.TYPE_IPV4); + ethReply.setVlanID(packet.getVlanID()); + + // IP Packet + IPv4 ipv4Packet = (IPv4) packet.getPayload(); + IPv4 ipv4Reply = new IPv4(); + ipv4Reply.setSourceAddress(myIPAddress.toInt()); + ipAddress = Ip4Address.valueOf(ipOffered); + ipv4Reply.setDestinationAddress(ipAddress.toInt()); + ipv4Reply.setTtl(packetTTL); + + // UDP Datagram. + UDP udpPacket = (UDP) ipv4Packet.getPayload(); + UDP udpReply = new UDP(); + udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT); + udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT); + + // DHCP Payload. + DHCP dhcpPacket = (DHCP) udpPacket.getPayload(); + DHCP dhcpReply = new DHCP(); + dhcpReply.setOpCode(DHCP.OPCODE_REPLY); + + ipAddress = Ip4Address.valueOf(ipOffered); + dhcpReply.setYourIPAddress(ipAddress.toInt()); + dhcpReply.setServerIPAddress(myIPAddress.toInt()); + + dhcpReply.setTransactionId(dhcpPacket.getTransactionId()); + dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress()); + dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); + dhcpReply.setHardwareAddressLength((byte) 6); + + // DHCP Options. + DHCPOption option = new DHCPOption(); + List optionList = new ArrayList<>(); + + // DHCP Message Type. + option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()); + option.setLength((byte) 1); + byte[] optionData = {outgoingMessageType}; + option.setData(optionData); + optionList.add(option); + + // DHCP Server Identifier. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()); + option.setLength((byte) 4); + option.setData(myIPAddress.toOctets()); + optionList.add(option); + + // IP Address Lease Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array()); + optionList.add(option); + + // IP Address Renewal Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array()); + optionList.add(option); + + // IP Address Rebinding Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array()); + optionList.add(option); + + // Subnet Mask. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue()); + option.setLength((byte) 4); + ipAddress = Ip4Address.valueOf(subnetMask); + option.setData(ipAddress.toOctets()); + optionList.add(option); + + // Broadcast Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue()); + option.setLength((byte) 4); + ipAddress = Ip4Address.valueOf(broadcastAddress); + option.setData(ipAddress.toOctets()); + optionList.add(option); + + // Router Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue()); + option.setLength((byte) 4); + ipAddress = Ip4Address.valueOf(routerAddress); + option.setData(ipAddress.toOctets()); + optionList.add(option); + + // DNS Server Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue()); + option.setLength((byte) 4); + ipAddress = Ip4Address.valueOf(domainServer); + option.setData(ipAddress.toOctets()); + optionList.add(option); + + // End Option. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue()); + option.setLength((byte) 1); + optionList.add(option); + + dhcpReply.setOptions(optionList); + + udpReply.setPayload(dhcpReply); + ipv4Reply.setPayload(udpReply); + ethReply.setPayload(ipv4Reply); + + return ethReply; + } + + /** + * Sends the Ethernet reply frame via the Packet Service. + * + * @param context the context of the incoming frame + * @param reply the Ethernet reply frame + */ + private void sendReply(PacketContext context, Ethernet reply) { + if (reply != null) { + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + ConnectPoint sourcePoint = context.inPacket().receivedFrom(); + builder.setOutput(sourcePoint.port()); + + packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(), + builder.build(), ByteBuffer.wrap(reply.serialize()))); + } + } + + /** + * Processes the DHCP Payload and initiates a reply to the client. + * + * @param context context of the incoming message + * @param dhcpPayload the extracted DHCP payload + */ + private void processDHCPPacket(PacketContext context, DHCP dhcpPayload) { + + Ethernet packet = context.inPacket().parsed(); + boolean flagIfRequestedIP = false; + boolean flagIfServerIP = false; + Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0"); + Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0"); + + if (dhcpPayload != null) { + + // TODO Convert this to enum value. + byte incomingPacketType = 0; + for (DHCPOption option : dhcpPayload.getOptions()) { + if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) { + byte[] data = option.getData(); + incomingPacketType = data[0]; + } + if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) { + byte[] data = option.getData(); + requestedIP = Ip4Address.valueOf(data); + flagIfRequestedIP = true; + } + if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) { + byte[] data = option.getData(); + serverIP = Ip4Address.valueOf(data); + flagIfServerIP = true; + } + } + + String ipOffered = ""; + DHCPPacketType outgoingPacketType; + MacAddress clientMAC = new MacAddress(dhcpPayload.getClientHardwareAddress()); + + if (incomingPacketType == DHCPPacketType.DHCPDISCOVER.getValue()) { + + outgoingPacketType = DHCPPacketType.DHCPOFFER; + ipOffered = dhcpStore.suggestIP(clientMAC, requestedIP).toString(); + + Ethernet ethReply = buildReply(packet, ipOffered, (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + + } else if (incomingPacketType == DHCPPacketType.DHCPREQUEST.getValue()) { + + outgoingPacketType = DHCPPacketType.DHCPACK; + + if (flagIfServerIP && flagIfRequestedIP) { + // SELECTING state + if (myIP.equals(serverIP.toString()) && + dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) { + + Ethernet ethReply = buildReply(packet, requestedIP.toString(), + (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + discoverHost(context, requestedIP); + } + } else if (flagIfRequestedIP) { + // INIT-REBOOT state + if (dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) { + Ethernet ethReply = buildReply(packet, requestedIP.toString(), + (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + discoverHost(context, requestedIP); + } + } else { + // RENEWING and REBINDING state + int ciaadr = dhcpPayload.getClientIPAddress(); + if (ciaadr != 0) { + Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr); + if (dhcpStore.assignIP(clientMAC, clientIaddr, leaseTime)) { + Ethernet ethReply = buildReply(packet, clientIaddr.toString(), + (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + discoverHost(context, clientIaddr); + } + } + } + } else if (incomingPacketType == DHCPPacketType.DHCPRELEASE.getValue()) { + + dhcpStore.releaseIP(clientMAC); + } + } + } + + /** + * Processes the ARP Payload and initiates a reply to the client. + * + * @param context context of the incoming message + * @param packet the ethernet payload + */ + private void processARPPacket(PacketContext context, Ethernet packet) { + + ARP arpPacket = (ARP) packet.getPayload(); + + ARP arpReply = (ARP) arpPacket.clone(); + arpReply.setOpCode(ARP.OP_REPLY); + + arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress()); + arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress()); + arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress()); + arpReply.setSenderHardwareAddress(myMAC.toBytes()); + + // Ethernet Frame. + Ethernet ethReply = new Ethernet(); + ethReply.setSourceMACAddress(myMAC); + ethReply.setDestinationMACAddress(packet.getSourceMAC()); + ethReply.setEtherType(Ethernet.TYPE_ARP); + ethReply.setVlanID(packet.getVlanID()); + + ethReply.setPayload(arpReply); + sendReply(context, ethReply); + } + + /** + * Integrates hosts learned through DHCP into topology. + * @param context context of the incoming message + * @param ipAssigned IP Address assigned to the host by DHCP Manager + */ + private void discoverHost(PacketContext context, Ip4Address ipAssigned) { + Ethernet packet = context.inPacket().parsed(); + MacAddress mac = packet.getSourceMAC(); + VlanId vlanId = VlanId.vlanId(packet.getVlanID()); + HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0); + + Set ips = new HashSet<>(); + ips.add(ipAssigned); + + HostId hostId = HostId.hostId(mac, vlanId); + DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips); + hostProviderService.hostDetected(hostId, desc); + } + + + @Override + public void process(PacketContext context) { + + Ethernet packet = context.inPacket().parsed(); + if (packet == null) { + return; + } + + if (packet.getEtherType() == Ethernet.TYPE_IPV4) { + IPv4 ipv4Packet = (IPv4) packet.getPayload(); + + if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) { + UDP udpPacket = (UDP) ipv4Packet.getPayload(); + + if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT && + udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) { + // This is meant for the dhcp server so process the packet here. + + DHCP dhcpPayload = (DHCP) udpPacket.getPayload(); + processDHCPPacket(context, dhcpPayload); + } + } + } else if (packet.getEtherType() == Ethernet.TYPE_ARP) { + ARP arpPacket = (ARP) packet.getPayload(); + + if ((arpPacket.getOpCode() == ARP.OP_REQUEST) && + (Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()).toString().equals(myIP))) { + + processARPPacket(context, packet); + + } + } + } + } + + private class InternalConfigListener implements NetworkConfigListener { + + /** + * Reconfigures the DHCP Server according to the configuration parameters passed. + * + * @param cfg configuration object + */ + private void reconfigureNetwork(DhcpConfig cfg) { + + if (cfg.ip() != null) { + myIP = cfg.ip(); + } + if (cfg.mac() != null) { + myMAC = MacAddress.valueOf(cfg.mac()); + } + if (cfg.subnetMask() != null) { + subnetMask = cfg.subnetMask(); + } + if (cfg.broadcastAddress() != null) { + broadcastAddress = cfg.broadcastAddress(); + } + if (cfg.routerAddress() != null) { + routerAddress = cfg.routerAddress(); + } + if (cfg.domainServer() != null) { + domainServer = cfg.domainServer(); + } + if (cfg.ttl() != null) { + packetTTL = Byte.valueOf(cfg.ttl()); + } + if (cfg.leaseTime() != null) { + leaseTime = Integer.valueOf(cfg.leaseTime()); + } + if (cfg.renewTime() != null) { + renewalTime = Integer.valueOf(cfg.renewTime()); + } + if (cfg.rebindTime() != null) { + rebindingTime = Integer.valueOf(cfg.rebindTime()); + } + } + + /** + * Reconfigures the DHCP Store according to the configuration parameters passed. + * + * @param cfg configuration object + */ + private void reconfigureStore(DhcpStoreConfig cfg) { + + if (cfg.defaultTimeout() != null) { + dhcpStore.setDefaultTimeoutForPurge(Integer.valueOf(cfg.defaultTimeout())); + } + if (cfg.timerDelay() != null) { + dhcpStore.setTimerDelay(Integer.valueOf(cfg.defaultTimeout())); + } + if ((cfg.startIP() != null) && (cfg.endIP() != null)) { + dhcpStore.populateIPPoolfromRange(Ip4Address.valueOf(cfg.startIP()), + Ip4Address.valueOf(cfg.endIP())); + } + } + + @Override + public void event(NetworkConfigEvent event) { + + if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) { + if (event.configClass().equals(DhcpConfig.class)) { + DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class); + reconfigureNetwork(cfg); + log.info("Reconfigured Manager"); + } + if (event.configClass().equals(DhcpStoreConfig.class)) { + DhcpStoreConfig cfg = cfgService.getConfig(appId, DhcpStoreConfig.class); + reconfigureStore(cfg); + log.info("Reconfigured Store"); + } + } + } + } + + private class InternalHostProvider extends AbstractProvider implements HostProvider { + + /** + * Creates a provider with the supplier identifier. + */ + protected InternalHostProvider() { + super(PID); + } + + @Override + public void triggerProbe(Host host) { + // nothing to do + } + } +} \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpStoreConfig.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpStoreConfig.java new file mode 100644 index 00000000..0059e7e5 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpStoreConfig.java @@ -0,0 +1,107 @@ +/* + * 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.dhcp.impl; + +import org.onosproject.core.ApplicationId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.basics.BasicElementConfig; + +/** + * DHCP Store Config class. + */ +public class DhcpStoreConfig extends Config { + + public static final String TIMER_DELAY = "delay"; + public static final String DEFAULT_TIMEOUT = "timeout"; + public static final String START_IP = "startip"; + public static final String END_IP = "endip"; + + /** + * Returns the delay after which the dhcp server will purge expired entries. + * + * @return time delay or null if not set + */ + public String timerDelay() { + return get(TIMER_DELAY, null); + } + + /** + * Sets the delay after which the dhcp server will purge expired entries. + * + * @param delay new time delay; null to clear + * @return self + */ + public BasicElementConfig timerDelay(String delay) { + return (BasicElementConfig) setOrClear(TIMER_DELAY, delay); + } + + /** + * Returns the default timeout for pending assignments. + * + * @return default timeout or null if not set + */ + public String defaultTimeout() { + return get(DEFAULT_TIMEOUT, null); + } + + /** + * Sets the default timeout for pending assignments. + * + * @param defaultTimeout new default timeout; null to clear + * @return self + */ + public BasicElementConfig defaultTimeout(String defaultTimeout) { + return (BasicElementConfig) setOrClear(DEFAULT_TIMEOUT, defaultTimeout); + } + + /** + * Returns the start IP for the available IP Range. + * + * @return start IP or null if not set + */ + public String startIP() { + return get(START_IP, null); + } + + /** + * Sets the start IP for the available IP Range. + * + * @param startIP new start IP; null to clear + * @return self + */ + public BasicElementConfig startIP(String startIP) { + return (BasicElementConfig) setOrClear(START_IP, startIP); + } + + /** + * Returns the end IP for the available IP Range. + * + * @return end IP or null if not set + */ + public String endIP() { + return get(END_IP, null); + } + + /** + * Sets the end IP for the available IP Range. + * + * @param endIP new end IP; null to clear + * @return self + */ + public BasicElementConfig endIP(String endIP) { + return (BasicElementConfig) setOrClear(END_IP, endIP); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java new file mode 100644 index 00000000..bb2bd2c2 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java @@ -0,0 +1,74 @@ +/* + * Copyright 2015 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.dhcp.impl; + +import com.google.common.collect.ImmutableList; +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.apache.felix.scr.annotations.Service; +import org.onosproject.ui.UiExtension; +import org.onosproject.ui.UiExtensionService; +import org.onosproject.ui.UiMessageHandlerFactory; +import org.onosproject.ui.UiView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.onosproject.ui.UiView.Category.NETWORK; + +/** + * Mechanism to stream data to the GUI. + */ +@Component(immediate = true, enabled = true) +@Service(value = DhcpUi.class) +public class DhcpUi { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private static final ClassLoader CL = DhcpUi.class.getClassLoader(); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected UiExtensionService uiExtensionService; + + private final UiMessageHandlerFactory messageHandlerFactory = + () -> ImmutableList.of(new DhcpViewMessageHandler()); + + private final List views = ImmutableList.of( + new UiView(NETWORK, "dhcp", "DHCP Server") + ); + + private final UiExtension uiExtension = + new UiExtension.Builder(CL, views) + .messageHandlerFactory(messageHandlerFactory) + .resourcePath("gui") + .build(); + + @Activate + protected void activate() { + uiExtensionService.register(uiExtension); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + uiExtensionService.unregister(uiExtension); + log.info("Stopped"); + } + +} \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java new file mode 100644 index 00000000..89ed5720 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java @@ -0,0 +1,96 @@ +/* + * Copyright 2015 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.dhcp.impl; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableSet; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiMessageHandler; +import org.onosproject.ui.table.TableModel; +import org.onosproject.ui.table.TableRequestHandler; + +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +/** + * DHCPViewMessageHandler class implementation. + */ +public class DhcpViewMessageHandler extends UiMessageHandler { + + private static final String DHCP_DATA_REQ = "dhcpDataRequest"; + private static final String DHCP_DATA_RESP = "dhcpDataResponse"; + private static final String DHCP = "dhcps"; + + private static final String MAC = "mac"; + private static final String IP = "ip"; + private static final String LEASE = "lease"; + + private static final String[] COL_IDS = { + MAC, IP, LEASE + }; + + @Override + protected Collection createRequestHandlers() { + return ImmutableSet.of( + new DataRequestHandler() + ); + } + + private final class DataRequestHandler extends TableRequestHandler { + + private DataRequestHandler() { + super(DHCP_DATA_REQ, DHCP_DATA_RESP, DHCP); + } + + @Override + protected String defaultColumnId() { + return MAC; + } + + @Override + protected String[] getColumnIds() { + return COL_IDS; + } + + @Override + protected void populateTable(TableModel tm, ObjectNode payload) { + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + Map allocationMap = dhcpService.listMapping(); + + for (Map.Entry entry : allocationMap.entrySet()) { + populateRow(tm.addRow(), entry); + } + } + + private void populateRow(TableModel.Row row, Map.Entry entry) { + if (entry.getValue().leasePeriod() > 0) { + Date now = new Date(entry.getValue().timestamp().getTime() + entry.getValue().leasePeriod()); + row.cell(MAC, entry.getKey()) + .cell(IP, entry.getValue().ipAddress()) + .cell(LEASE, now.toString()); + } else { + row.cell(MAC, entry.getKey()) + .cell(IP, entry.getValue().ipAddress()) + .cell(LEASE, "Infinite Static Lease"); + } + } + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java new file mode 100644 index 00000000..6e29216a --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java @@ -0,0 +1,324 @@ +/* + * Copyright 2015 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.dhcp.impl; + +import com.google.common.collect.ImmutableSet; +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.apache.felix.scr.annotations.Service; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Timer; +import org.onosproject.dhcp.DhcpStore; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +import org.onosproject.store.service.DistributedSet; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Manages the pool of available IP Addresses in the network and + * Remembers the mapping between MAC ID and IP Addresses assigned. + */ + +@Component(immediate = true) +@Service +public class DistributedDhcpStore implements DhcpStore { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + private ConsistentMap allocationMap; + + private DistributedSet freeIPPool; + + private Timeout timeout; + + private static Ip4Address startIPRange; + + private static Ip4Address endIPRange; + + // Hardcoded values are default values. + + private static int timerDelay = 2; + + private static int timeoutForPendingAssignments = 60; + + @Activate + protected void activate() { + allocationMap = storageService.consistentMapBuilder() + .withName("onos-dhcp-assignedIP") + .withSerializer(Serializer.using( + new KryoNamespace.Builder() + .register(KryoNamespaces.API) + .register(IpAssignment.class, + IpAssignment.AssignmentStatus.class, + Date.class, + long.class, + Ip4Address.class) + .build())) + .build(); + + freeIPPool = storageService.setBuilder() + .withName("onos-dhcp-freeIP") + .withSerializer(Serializer.using(KryoNamespaces.API)) + .build(); + + timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + timeout.cancel(); + log.info("Stopped"); + } + + @Override + public Ip4Address suggestIP(MacAddress macID, Ip4Address requestedIP) { + + IpAssignment assignmentInfo; + if (allocationMap.containsKey(macID)) { + assignmentInfo = allocationMap.get(macID).value(); + IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus(); + Ip4Address ipAddr = assignmentInfo.ipAddress(); + + if (status == IpAssignment.AssignmentStatus.Option_Assigned || + status == IpAssignment.AssignmentStatus.Option_Requested) { + // Client has a currently Active Binding. + if ((ipAddr.toInt() > startIPRange.toInt()) && (ipAddr.toInt() < endIPRange.toInt())) { + return ipAddr; + } + + } else if (status == IpAssignment.AssignmentStatus.Option_Expired) { + // Client has a Released or Expired Binding. + if (freeIPPool.contains(ipAddr)) { + assignmentInfo = IpAssignment.builder() + .ipAddress(ipAddr) + .timestamp(new Date()) + .leasePeriod(timeoutForPendingAssignments) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested) + .build(); + if (freeIPPool.remove(ipAddr)) { + allocationMap.put(macID, assignmentInfo); + return ipAddr; + } + } + } + return assignmentInfo.ipAddress(); + + } else if (requestedIP.toInt() != 0) { + // Client has requested an IP. + if (freeIPPool.contains(requestedIP)) { + assignmentInfo = IpAssignment.builder() + .ipAddress(requestedIP) + .timestamp(new Date()) + .leasePeriod(timeoutForPendingAssignments) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested) + .build(); + if (freeIPPool.remove(requestedIP)) { + allocationMap.put(macID, assignmentInfo); + return requestedIP; + } + } + } + + // Allocate a new IP from the server's pool of available IP. + Ip4Address nextIPAddr = fetchNextIP(); + assignmentInfo = IpAssignment.builder() + .ipAddress(nextIPAddr) + .timestamp(new Date()) + .leasePeriod(timeoutForPendingAssignments) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested) + .build(); + + allocationMap.put(macID, assignmentInfo); + return nextIPAddr; + + } + + @Override + public boolean assignIP(MacAddress macID, Ip4Address ipAddr, int leaseTime) { + + IpAssignment assignmentInfo; + if (allocationMap.containsKey(macID)) { + assignmentInfo = allocationMap.get(macID).value(); + if ((assignmentInfo.ipAddress().toInt() == ipAddr.toInt()) && + (ipAddr.toInt() > startIPRange.toInt()) && (ipAddr.toInt() < endIPRange.toInt())) { + + assignmentInfo = IpAssignment.builder() + .ipAddress(ipAddr) + .timestamp(new Date()) + .leasePeriod(leaseTime) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned) + .build(); + allocationMap.put(macID, assignmentInfo); + return true; + } + } else if (freeIPPool.contains(ipAddr)) { + assignmentInfo = IpAssignment.builder() + .ipAddress(ipAddr) + .timestamp(new Date()) + .leasePeriod(leaseTime) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned) + .build(); + if (freeIPPool.remove(ipAddr)) { + allocationMap.put(macID, assignmentInfo); + return true; + } + } + return false; + } + + @Override + public void releaseIP(MacAddress macID) { + if (allocationMap.containsKey(macID)) { + IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(macID).value()) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired) + .build(); + Ip4Address freeIP = newAssignment.ipAddress(); + allocationMap.put(macID, newAssignment); + freeIPPool.add(freeIP); + } + } + + @Override + public void setDefaultTimeoutForPurge(int timeInSeconds) { + timeoutForPendingAssignments = timeInSeconds; + } + + @Override + public void setTimerDelay(int timeInSeconds) { + timerDelay = timeInSeconds; + } + + @Override + public Map listMapping() { + + Map allMapping = new HashMap<>(); + for (Map.Entry> entry: allocationMap.entrySet()) { + IpAssignment assignment = entry.getValue().value(); + if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned) { + allMapping.put(entry.getKey(), assignment); + } + } + return allMapping; + + } + + @Override + public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) { + return assignIP(macID, ipAddr, -1); + } + + @Override + public boolean removeStaticIP(MacAddress macID) { + if (allocationMap.containsKey(macID)) { + IpAssignment assignment = allocationMap.get(macID).value(); + Ip4Address freeIP = assignment.ipAddress(); + if (assignment.leasePeriod() < 0) { + allocationMap.remove(macID); + freeIPPool.add(freeIP); + return true; + } + } + return false; + } + + @Override + public Iterable getAvailableIPs() { + return ImmutableSet.copyOf(freeIPPool); + } + + @Override + public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) { + // Clear all entries from previous range. + startIPRange = startIP; + endIPRange = endIP; + freeIPPool.clear(); + + int lastIP = endIP.toInt(); + Ip4Address nextIP; + for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) { + nextIP = Ip4Address.valueOf(loopCounter); + freeIPPool.add(nextIP); + } + } + + /** + * Fetches the next available IP from the free pool pf IPs. + * + * @return the next available IP address + */ + private Ip4Address fetchNextIP() { + for (Ip4Address freeIP : freeIPPool) { + if (freeIPPool.remove(freeIP)) { + return freeIP; + } + } + return null; + } + + /** + * Purges the IP allocation map to remove expired entries and returns the freed IPs to the free pool. + */ + private class PurgeListTask implements TimerTask { + + @Override + public void run(Timeout to) { + IpAssignment ipAssignment, newAssignment; + Date dateNow = new Date(); + for (Map.Entry> entry: allocationMap.entrySet()) { + ipAssignment = entry.getValue().value(); + long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime(); + if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) && + (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriod()))) { + Ip4Address freeIP = ipAssignment.ipAddress(); + + newAssignment = IpAssignment.builder(ipAssignment) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired) + .build(); + allocationMap.put(entry.getKey(), newAssignment); + + if ((freeIP.toInt() > startIPRange.toInt()) && (freeIP.toInt() < endIPRange.toInt())) { + freeIPPool.add(freeIP); + } + } + } + timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES); + } + + } + +} diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java new file mode 100644 index 00000000..bfa2767d --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java @@ -0,0 +1,163 @@ +/* + * Copyright 2015 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.dhcp.rest; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.rest.AbstractWebResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * Manage DHCP address assignments. + */ +@Path("dhcp") +public class DHCPWebResource extends AbstractWebResource { + + final DhcpService service = get(DhcpService.class); + + /** + * Get DHCP server configuration data. + * Shows lease, renewal and rebinding times in seconds. + * + * @return 200 OK + */ + @GET + @Path("config") + public Response getConfigs() { + DhcpService service = get(DhcpService.class); + ObjectNode node = mapper().createObjectNode() + .put("leaseTime", service.getLeaseTime()) + .put("renewalTime", service.getRenewalTime()) + .put("rebindingTime", service.getRebindingTime()); + return ok(node.toString()).build(); + } + + /** + * Get all MAC/IP mappings. + * Shows all MAC/IP mappings held by the DHCP server. + * + * @return 200 OK + */ + @GET + @Path("mappings") + public Response listMappings() { + ObjectNode root = mapper().createObjectNode(); + + final Map intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("mac", i.getKey().toString()) + .put("ip", i.getValue().ipAddress().toString()))); + + return ok(root.toString()).build(); + } + + + + /** + * Get all available IPs. + * Shows all the IPs in the free pool of the DHCP Server. + * + * @return 200 OK + */ + @GET + @Path("available") + public Response listAvailableIPs() { + final Iterable availableIPList = service.getAvailableIPs(); + + final ObjectNode root = mapper().createObjectNode(); + ArrayNode arrayNode = root.putArray("availableIP"); + availableIPList.forEach(i -> arrayNode.add(i.toString())); + return ok(root.toString()).build(); + } + + /** + * Post a new static MAC/IP binding. + * Registers a static binding to the DHCP server, and displays the current set of bindings. + * + * @param stream JSON stream + * @return 200 OK + */ + @POST + @Path("mappings") + @Consumes(MediaType.APPLICATION_JSON) + public Response setMapping(InputStream stream) { + ObjectNode root = mapper().createObjectNode(); + + try { + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); + JsonNode macID = jsonTree.get("mac"); + JsonNode ip = jsonTree.get("ip"); + if (macID != null && ip != null) { + + if (!service.setStaticMapping(MacAddress.valueOf(macID.asText()), + Ip4Address.valueOf(ip.asText()))) { + throw new IllegalArgumentException("Static Mapping Failed. The IP maybe unavailable."); + } + } + + final Map intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("mac", i.getKey().toString()) + .put("ip", i.getValue().ipAddress().toString()))); + } catch (IOException e) { + throw new IllegalArgumentException(e.getMessage()); + } + return ok(root.toString()).build(); + } + + /** + * Delete a static MAC/IP binding. + * Removes a static binding from the DHCP Server, and displays the current set of bindings. + * + * @param macID mac address identifier + * @return 200 OK + */ + @DELETE + @Path("mappings/{macID}") + public Response deleteMapping(@PathParam("macID") String macID) { + + ObjectNode root = mapper().createObjectNode(); + + if (!service.removeStaticMapping(MacAddress.valueOf(macID))) { + throw new IllegalArgumentException("Static Mapping Removal Failed."); + } + final Map intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("mac", i.getKey().toString()) + .put("ip", i.getValue().ipAddress().toString()))); + + return ok(root.toString()).build(); + } +} diff --git a/framework/src/onos/apps/dhcp/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/dhcp/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..ce716315 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.css b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.css new file mode 100644 index 00000000..e0a29314 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.css @@ -0,0 +1,27 @@ +/* + * Copyright 2015 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. + */ + +/* + ONOS GUI -- DHCP Server -- CSS file + */ + +#ov-dhcp h2 { + display: inline-block; +} + +#ov-dhcp div.ctrl-btns { + width: 45px; +} diff --git a/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.html b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.html new file mode 100644 index 00000000..3e14570a --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.html @@ -0,0 +1,47 @@ + +
+
+

DHCP Mappings ({{tableData.length}} total)

+
+
+
+
+ +
+
+ +
+ + + + + + +
MAC AddressIP AddressLease Expiry
+
+ +
+ + + + + + + + + + +
+ No mappings found +
{{dhcp.mac}}{{dhcp.ip}}{{dhcp.lease}}
+
+ +
+ +
diff --git a/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.js b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.js new file mode 100644 index 00000000..061d0de6 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/app/view/dhcp/dhcp.js @@ -0,0 +1,51 @@ +/* + * Copyright 2015 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. + */ + +/* + ONOS GUI -- DHCP Server View Module + */ + +(function () { + 'use strict'; + + // injected refs + var $log, $scope; + + angular.module('ovDhcp', []) + .controller('OvDhcpCtrl', + ['$log', '$scope', 'TableBuilderService', + + function (_$log_, _$scope_, tbs) { + $log = _$log_; + $scope = _$scope_; + + function selCb($event, row) { + $log.debug('Got a click on:', row); + } + + tbs.buildTable({ + scope: $scope, + tag: 'dhcp', + selCb: selCb + }); + + $scope.$on('$destroy', function () { + $log.debug('OvDhcpCtrl has been destroyed'); + }); + + $log.log('OvDhcpCtrl has been created'); + }]); +}()); \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/resources/gui/css.html b/framework/src/onos/apps/dhcp/src/main/resources/gui/css.html new file mode 100644 index 00000000..d02ad44a --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/gui/css.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/resources/gui/js.html b/framework/src/onos/apps/dhcp/src/main/resources/gui/js.html new file mode 100644 index 00000000..d37b5768 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/resources/gui/js.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/dhcp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..27504548 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,43 @@ + + + + DHCP Server REST API v1.0 + + + JAX-RS Service + com.sun.jersey.spi.container.servlet.ServletContainer + + com.sun.jersey.config.property.resourceConfigClass + com.sun.jersey.api.core.ClassNamesResourceConfig + + + com.sun.jersey.config.property.classnames + + org.onosproject.dhcp.rest.DHCPWebResource + + + 1 + + + + JAX-RS Service + /* + + diff --git a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/IpAssignmentTest.java b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/IpAssignmentTest.java new file mode 100644 index 00000000..dad5ef55 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/IpAssignmentTest.java @@ -0,0 +1,101 @@ +/* + * 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.dhcp; + +import com.google.common.testing.EqualsTester; +import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.Test; +import org.onlab.packet.Ip4Address; + +import java.util.Date; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; + +/** + * Unit Tests for IPAssignment class. + */ +public class IpAssignmentTest extends TestCase { + + private final Date dateNow = new Date(); + + private final IpAssignment stats1 = IpAssignment.builder() + .ipAddress(Ip4Address.valueOf("10.10.10.10")) + .leasePeriod(300) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired) + .timestamp(dateNow) + .build(); + + private final IpAssignment stats2 = IpAssignment.builder() + .ipAddress(Ip4Address.valueOf("10.10.10.10")) + .leasePeriod(300) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned) + .timestamp(dateNow) + .build(); + + private final IpAssignment stats3 = IpAssignment.builder(stats1) + .build(); + + /** + * Tests the constructor for the class. + */ + @Test + public void testConstruction() { + assertThat(stats3.ipAddress(), is(Ip4Address.valueOf("10.10.10.10"))); + assertThat(stats3.timestamp(), is(dateNow)); + assertThat(stats3.leasePeriod(), is(300)); + assertThat(stats3.assignmentStatus(), is(IpAssignment.AssignmentStatus.Option_Expired)); + } + + /** + * Tests the equality and inequality of objects using Guava EqualsTester. + */ + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup(stats1, stats1) + .addEqualityGroup(stats2) + .testEquals(); + } + + /** + * Tests if the toString method returns a consistent value for hashing. + */ + @Test + public void testToString() { + assertThat(stats1.toString(), is(stats1.toString())); + } + + /** + * Tests if the validateInputs method returns an exception for malformed object. + */ + @Test + public void testValidateInputs() { + try { + IpAssignment stats4 = IpAssignment.builder() + .ipAddress(Ip4Address.valueOf("10.10.10.10")) + .leasePeriod(300) + .build(); + + fail("Construction of a malformed IPAssignment did not throw an exception"); + } catch (NullPointerException e) { + Assert.assertThat(e.getMessage(), containsString("must be specified")); + } + } +} \ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java new file mode 100644 index 00000000..42ac73d3 --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java @@ -0,0 +1,383 @@ +/* + * Copyright 2015 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.dhcp.impl; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.DHCP; +import org.onlab.packet.DHCPOption; +import org.onlab.packet.DHCPPacketType; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onlab.packet.UDP; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.dhcp.DhcpStore; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.config.NetworkConfigRegistryAdapter; +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.packet.DefaultInboundPacket; +import org.onosproject.net.packet.DefaultPacketContext; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.onosproject.net.NetTestTools.connectPoint; + +/** + * Set of tests of the ONOS application component. + */ + +public class DhcpManagerTest { + + private DhcpManager dhcpXManager; + + protected PacketProcessor packetProcessor; + + protected HostProviderService hostProviderService; + + private static final MacAddress CLIENT1_MAC = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); + + private static final String EXPECTED_IP = "10.2.0.2"; + + private static final Ip4Address BROADCAST = Ip4Address.valueOf("255.255.255.255"); + + private static final int TRANSACTION_ID = 1000; + + private static final ProviderId PID = new ProviderId("of", "foo"); + + @Before + public void setUp() { + dhcpXManager = new DhcpManager(); + dhcpXManager.cfgService = new TestNetworkConfigRegistry(); + dhcpXManager.packetService = new TestPacketService(); + dhcpXManager.coreService = new TestCoreService(); + dhcpXManager.dhcpStore = new TestDhcpStore(); + hostProviderService = new TestHostProviderService(new TestHostProvider()); + dhcpXManager.hostProviderService = hostProviderService; + dhcpXManager.hostProviderRegistry = new TestHostRegistry(); + dhcpXManager.activate(); + } + + @After + public void tearDown() { + dhcpXManager.deactivate(); + } + + /** + * Tests the response to a DHCP Discover Packet. + */ + @Test + public void testDiscover() { + Ethernet reply = constructDHCPPacket(DHCPPacketType.DHCPDISCOVER); + sendPacket(reply); + } + + /** + * Tests the response to a DHCP Request Packet. + */ + @Test + public void testRequest() { + Ethernet reply = constructDHCPPacket(DHCPPacketType.DHCPREQUEST); + sendPacket(reply); + } + + /** + * Sends an Ethernet packet to the process method of the Packet Processor. + * @param reply Ethernet packet + */ + private void sendPacket(Ethernet reply) { + final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize()); + InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1), + reply, + byteBuffer); + + PacketContext context = new TestPacketContext(127L, inPacket, null, false); + packetProcessor.process(context); + } + + /** + * Constructs an Ethernet packet containing a DHCP Payload. + * @param packetType DHCP Message Type + * @return Ethernet packet + */ + private Ethernet constructDHCPPacket(DHCPPacketType packetType) { + + // Ethernet Frame. + Ethernet ethReply = new Ethernet(); + ethReply.setSourceMACAddress(CLIENT1_MAC); + ethReply.setDestinationMACAddress(MacAddress.BROADCAST); + ethReply.setEtherType(Ethernet.TYPE_IPV4); + ethReply.setVlanID((short) 2); + + // IP Packet + IPv4 ipv4Reply = new IPv4(); + ipv4Reply.setSourceAddress(0); + ipv4Reply.setDestinationAddress(BROADCAST.toInt()); + ipv4Reply.setTtl((byte) 127); + + // UDP Datagram. + UDP udpReply = new UDP(); + udpReply.setSourcePort((byte) UDP.DHCP_CLIENT_PORT); + udpReply.setDestinationPort((byte) UDP.DHCP_SERVER_PORT); + + // DHCP Payload. + DHCP dhcpReply = new DHCP(); + dhcpReply.setOpCode(DHCP.OPCODE_REQUEST); + + dhcpReply.setYourIPAddress(0); + dhcpReply.setServerIPAddress(0); + + dhcpReply.setTransactionId(TRANSACTION_ID); + dhcpReply.setClientHardwareAddress(CLIENT1_MAC.toBytes()); + dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); + dhcpReply.setHardwareAddressLength((byte) 6); + + // DHCP Options. + DHCPOption option = new DHCPOption(); + List optionList = new ArrayList<>(); + + // DHCP Message Type. + option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()); + option.setLength((byte) 1); + byte[] optionData = {(byte) packetType.getValue()}; + option.setData(optionData); + optionList.add(option); + + // DHCP Requested IP. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()); + option.setLength((byte) 4); + optionData = Ip4Address.valueOf(EXPECTED_IP).toOctets(); + option.setData(optionData); + optionList.add(option); + + // End Option. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue()); + option.setLength((byte) 1); + optionList.add(option); + + dhcpReply.setOptions(optionList); + + udpReply.setPayload(dhcpReply); + ipv4Reply.setPayload(udpReply); + ethReply.setPayload(ipv4Reply); + + return ethReply; + } + + /** + * Validates the contents of the packet sent by the DHCP Manager. + * @param packet Ethernet packet received + */ + private void validatePacket(Ethernet packet) { + DHCP dhcpPacket = (DHCP) packet.getPayload().getPayload().getPayload(); + assertEquals(MacAddress.valueOf(dhcpPacket.getClientHardwareAddress()), CLIENT1_MAC); + assertEquals(Ip4Address.valueOf(dhcpPacket.getYourIPAddress()), Ip4Address.valueOf(EXPECTED_IP)); + assertEquals(dhcpPacket.getTransactionId(), TRANSACTION_ID); + } + + /** + * Mocks the DHCPStore. + */ + private final class TestDhcpStore implements DhcpStore { + + + public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) { + } + + public Ip4Address suggestIP(MacAddress macID, Ip4Address requestedIP) { + return Ip4Address.valueOf(EXPECTED_IP); + } + + public boolean assignIP(MacAddress macID, Ip4Address ipAddr, int leaseTime) { + return true; + } + + public void setDefaultTimeoutForPurge(int timeInSeconds) { + } + + public void setTimerDelay(int timeInSeconds) { + } + + public void releaseIP(MacAddress macID) { + } + + public Map listMapping() { + Map map = new HashMap<>(); + IpAssignment assignment = IpAssignment.builder() + .ipAddress(Ip4Address.valueOf(EXPECTED_IP)) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned) + .leasePeriod(300) + .timestamp(new Date()) + .build(); + map.put(CLIENT1_MAC, assignment); + return map; + } + + public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) { + return true; + } + + public boolean removeStaticIP(MacAddress macID) { + return true; + } + + public Iterable getAvailableIPs() { + List ipList = new ArrayList<>(); + ipList.add(Ip4Address.valueOf(EXPECTED_IP)); + return ImmutableSet.copyOf(ipList); + } + } + + /** + * Mocks the DefaultPacket context. + */ + private final class TestPacketContext extends DefaultPacketContext { + private TestPacketContext(long time, InboundPacket inPkt, + OutboundPacket outPkt, boolean block) { + super(time, inPkt, outPkt, block); + } + + @Override + public void send() { + // We don't send anything out. + } + } + + /** + * Keeps a reference to the PacketProcessor and verifies the OutboundPackets. + */ + private class TestPacketService extends PacketServiceAdapter { + + @Override + public void addProcessor(PacketProcessor processor, int priority) { + packetProcessor = processor; + } + + @Override + public void emit(OutboundPacket packet) { + try { + Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(), + 0, packet.data().array().length); + validatePacket(eth); + } catch (Exception e) { + fail(e.getMessage()); + } + } + } + + /** + * Mocks the CoreService. + */ + private class TestCoreService extends CoreServiceAdapter { + + } + + /** + * Mocks the NetworkConfigRegistry. + */ + private class TestNetworkConfigRegistry extends NetworkConfigRegistryAdapter { + + } + + /** + * Mocks the HostProviderService. + */ + private class TestHostProviderService extends AbstractProviderService + implements HostProviderService { + + protected TestHostProviderService(HostProvider provider) { + super(provider); + } + + @Override + public void hostDetected(HostId hostId, HostDescription hostDescription) { + } + + @Override + public void hostVanished(HostId hostId) { + } + + } + + /** + * Mocks the HostProvider. + */ + private static class TestHostProvider extends AbstractProvider + implements HostProvider { + + protected TestHostProvider() { + super(PID); + } + + @Override + public ProviderId id() { + return PID; + } + + @Override + public void triggerProbe(Host host) { + } + + } + + /** + * Mocks the HostProviderRegistry. + */ + private class TestHostRegistry implements HostProviderRegistry { + + @Override + public HostProviderService register(HostProvider provider) { + return hostProviderService; + } + + @Override + public void unregister(HostProvider provider) { + } + + @Override + public Set getProviders() { + return null; + } + + } + +} diff --git a/framework/src/onos/apps/dhcp/src/test/resources/dhcp-cfg.json b/framework/src/onos/apps/dhcp/src/test/resources/dhcp-cfg.json new file mode 100644 index 00000000..8ce5486f --- /dev/null +++ b/framework/src/onos/apps/dhcp/src/test/resources/dhcp-cfg.json @@ -0,0 +1,24 @@ +{ + "apps": { + "org.onosproject.dhcp" : { + "dhcp" : { + "ip": "10.0.0.1", + "mac": "1a:2b:3c:4e:5e:6f", + "subnet": "255.0.0.0", + "broadcast": "10.255.255.255", + "router": "10.0.0.1", + "domain": "10.0.0.1", + "ttl": "63", + "lease": "300", + "renew": "150", + "rebind": "200" + }, + "dhcpstore" : { + "delay": "3", + "timeout": "150", + "startip": "10.0.0.110", + "endip": "10.0.0.130" + } + } + } +} \ No newline at end of file -- cgit 1.2.3-korg