diff options
author | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-03 14:08:10 -0800 |
---|---|---|
committer | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-03 14:08:10 -0800 |
commit | 643ee33289bd2cb9e6afbfb09b4ed72d467ba1c2 (patch) | |
tree | c2c376a44a359544fe3d4c45eb0cc0e2ec4a7080 /framework/src/onos/apps/dhcp/app/src | |
parent | 46eeb79b54345bdafb6055b8ee4bad4ce8b01274 (diff) |
This updates ONOS src tree to commit id
03fa5e571cabbd001ddb1598847e1150b11c7333
Change-Id: I13b554026d6f902933e35887d29bd5fdb669c0bd
Signed-off-by: Ashlee Young <ashlee@wildernessvoice.com>
Diffstat (limited to 'framework/src/onos/apps/dhcp/app/src')
24 files changed, 2666 insertions, 0 deletions
diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpLeaseDetails.java new file mode 100644 index 00000000..95f49e69 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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/app/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpListAllMappings.java new file mode 100644 index 00000000..209ba683 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.net.HostId; + +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<HostId, IpAssignment> allocationMap = dhcpService.listMapping(); + + for (Map.Entry<HostId, IpAssignment> entry : allocationMap.entrySet()) { + print(DHCP_MAPPING_FORMAT, entry.getKey().toString(), entry.getValue().ipAddress().toString()); + } + } +} diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpRemoveStaticMapping.java new file mode 100644 index 00000000..a92cd250 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java new file mode 100644 index 00000000..9f4f6580 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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/app/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/FreeIpCompleter.java new file mode 100644 index 00000000..228d70fd --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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<String> candidates) { + // Delegate string completer + StringsCompleter delegate = new StringsCompleter(); + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + Iterator<Ip4Address> it = dhcpService.getAvailableIPs().iterator(); + SortedSet<String> 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/app/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/MacIdCompleter.java new file mode 100644 index 00000000..d6cd73a7 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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<String> candidates) { + // Delegate string completer + StringsCompleter delegate = new StringsCompleter(); + HostService service = AbstractShellCommand.get(HostService.class); + Iterator<Host> it = service.getHosts().iterator(); + SortedSet<String> 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/app/src/main/java/org/onosproject/dhcp/cli/package-info.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/package-info.java new file mode 100644 index 00000000..f8780195 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * CLI implementation for sample application that assigns and manages DHCP leases. + */ +package org.onosproject.dhcp.cli;
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java new file mode 100644 index 00000000..4353d623 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpConfig.java @@ -0,0 +1,319 @@ +/* + * 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.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +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<ApplicationId> { + + 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"; + 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"; + + public static final int DEFAULT = -1; + + /** + * Returns the dhcp server ip. + * + * @return ip address or null if not set + */ + public Ip4Address ip() { + String ip = get(MY_IP, null); + return ip != null ? Ip4Address.valueOf(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 MacAddress mac() { + String mac = get(MY_MAC, null); + return mac != null ? MacAddress.valueOf(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 Ip4Address subnetMask() { + String ip = get(SUBNET_MASK, null); + return ip != null ? Ip4Address.valueOf(ip) : 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 Ip4Address broadcastAddress() { + String ip = get(BROADCAST_ADDRESS, null); + return ip != null ? Ip4Address.valueOf(ip) : 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 -1 if not set + */ + public int ttl() { + return get(TTL, DEFAULT); + } + + /** + * Sets the Time To Live for the reply packets. + * + * @param ttl new ttl; null to clear + * @return self + */ + public BasicElementConfig ttl(int ttl) { + return (BasicElementConfig) setOrClear(TTL, ttl); + } + + /** + * Returns the Lease Time offered by the DHCP Server. + * + * @return lease time or -1 if not set + */ + public int leaseTime() { + return get(LEASE_TIME, DEFAULT); + } + + /** + * Sets the Lease Time offered by the DHCP Server. + * + * @param lease new lease time; null to clear + * @return self + */ + public BasicElementConfig leaseTime(int lease) { + return (BasicElementConfig) setOrClear(LEASE_TIME, lease); + } + + /** + * Returns the Renew Time offered by the DHCP Server. + * + * @return renew time or -1 if not set + */ + public int renewTime() { + return get(RENEW_TIME, DEFAULT); + } + + /** + * Sets the Renew Time offered by the DHCP Server. + * + * @param renew new renew time; null to clear + * @return self + */ + public BasicElementConfig renewTime(int renew) { + return (BasicElementConfig) setOrClear(RENEW_TIME, renew); + } + + /** + * Returns the Rebind Time offered by the DHCP Server. + * + * @return rebind time or -1 if not set + */ + public int rebindTime() { + return get(REBIND_TIME, DEFAULT); + } + + /** + * Sets the Rebind Time offered by the DHCP Server. + * + * @param rebind new rebind time; null to clear + * @return self + */ + public BasicElementConfig rebindTime(int rebind) { + return (BasicElementConfig) setOrClear(REBIND_TIME, rebind); + } + + /** + * Returns the Router Address. + * + * @return router address or null if not set + */ + public Ip4Address routerAddress() { + String ip = get(ROUTER_ADDRESS, null); + return ip != null ? Ip4Address.valueOf(ip) : 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 Ip4Address domainServer() { + String ip = get(DOMAIN_SERVER, null); + return ip != null ? Ip4Address.valueOf(ip) : 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); + } + + /** + * Returns the delay in minutes after which the dhcp server will purge expired entries. + * + * @return time delay or -1 if not set + */ + public int timerDelay() { + return get(TIMER_DELAY, DEFAULT); + } + + /** + * 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(int delay) { + return (BasicElementConfig) setOrClear(TIMER_DELAY, delay); + } + + /** + * Returns the default timeout for pending assignments. + * + * @return default timeout or -1 if not set + */ + public int defaultTimeout() { + return get(DEFAULT_TIMEOUT, DEFAULT); + } + + /** + * Sets the default timeout for pending assignments. + * + * @param defaultTimeout new default timeout; null to clear + * @return self + */ + public BasicElementConfig defaultTimeout(int 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 Ip4Address startIp() { + String ip = get(START_IP, null); + return ip != null ? Ip4Address.valueOf(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 Ip4Address endIp() { + String ip = get(END_IP, null); + return ip != null ? Ip4Address.valueOf(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/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java new file mode 100644 index 00000000..96d94a2b --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java @@ -0,0 +1,699 @@ +/* + * 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.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.onlab.util.Timer; +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.ConnectPoint; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +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.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.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +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 InternalConfigListener cfgListener = new InternalConfigListener(); + + private final Set<ConfigFactory> factories = ImmutableSet.of( + new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY, + DhcpConfig.class, + "dhcp") { + @Override + public DhcpConfig createConfig() { + return new DhcpConfig(); + } + } + ); + @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 final HostProvider hostProvider = new InternalHostProvider(); + + private ApplicationId appId; + + // Hardcoded values are default values. + + private static Ip4Address myIP = Ip4Address.valueOf("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 Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0"); + + private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255"); + + private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2"); + + private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2"); + + private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255"); + + protected Timeout timeout; + + protected static int timerDelay = 2; + + @Activate + protected void activate() { + // start the dhcp server + appId = coreService.registerApplication("org.onosproject.dhcp"); + + cfgService.addListener(cfgListener); + factories.forEach(cfgService::registerConfigFactory); + cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class)); + + hostProviderService = hostProviderRegistry.register(hostProvider); + packetService.addProcessor(processor, PacketProcessor.director(0)); + requestPackets(); + timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + cfgService.removeListener(cfgListener); + factories.forEach(cfgService::unregisterConfigFactory); + packetService.removeProcessor(processor); + hostProviderRegistry.unregister(hostProvider); + hostProviderService = null; + cancelPackets(); + timeout.cancel(); + 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<HostId, IpAssignment> listMapping() { + return dhcpStore.listAssignedMapping(); + } + + @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<Ip4Address> 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, Ip4Address ipOffered, byte outgoingMessageType) { + + // 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(myIP.toInt()); + ipv4Reply.setDestinationAddress(ipOffered.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); + dhcpReply.setFlags(dhcpPacket.getFlags()); + dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress()); + dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress()); + dhcpReply.setTransactionId(dhcpPacket.getTransactionId()); + + if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) { + dhcpReply.setYourIPAddress(ipOffered.toInt()); + dhcpReply.setServerIPAddress(myIP.toInt()); + if (dhcpPacket.getGatewayIPAddress() == 0) { + ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt()); + } + } + dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); + dhcpReply.setHardwareAddressLength((byte) 6); + + // DHCP Options. + DHCPOption option = new DHCPOption(); + List<DHCPOption> 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(myIP.toOctets()); + optionList.add(option); + + if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) { + + // 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); + option.setData(subnetMask.toOctets()); + optionList.add(option); + + // Broadcast Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue()); + option.setLength((byte) 4); + option.setData(broadcastAddress.toOctets()); + optionList.add(option); + + // Router Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue()); + option.setLength((byte) 4); + option.setData(routerAddress.toOctets()); + optionList.add(option); + + // DNS Server Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue()); + option.setLength((byte) 4); + option.setData(domainServer.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()); + context.block(); + 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) { + + DHCPPacketType incomingPacketType = DHCPPacketType.getType(0); + for (DHCPOption option : dhcpPayload.getOptions()) { + if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) { + byte[] data = option.getData(); + incomingPacketType = DHCPPacketType.getType(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; + } + } + DHCPPacketType outgoingPacketType; + MacAddress clientMAC = new MacAddress(dhcpPayload.getClientHardwareAddress()); + VlanId vlanId = VlanId.vlanId(packet.getVlanID()); + HostId hostId = HostId.hostId(clientMAC, vlanId); + + if (incomingPacketType.getValue() == DHCPPacketType.DHCPDISCOVER.getValue()) { + + outgoingPacketType = DHCPPacketType.DHCPOFFER; + Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP); + if (ipOffered != null) { + Ethernet ethReply = buildReply(packet, ipOffered, + (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + } + + } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) { + + if (flagIfServerIP && flagIfRequestedIP) { + // SELECTING state + if (myIP.equals(serverIP)) { + + if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) { + outgoingPacketType = DHCPPacketType.DHCPACK; + discoverHost(context, requestedIP); + } else { + outgoingPacketType = DHCPPacketType.DHCPNAK; + } + Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + } + } else if (flagIfRequestedIP) { + // INIT-REBOOT state + if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) { + outgoingPacketType = DHCPPacketType.DHCPACK; + Ethernet ethReply = buildReply(packet, requestedIP, (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(hostId, clientIaddr, leaseTime)) { + outgoingPacketType = DHCPPacketType.DHCPACK; + discoverHost(context, clientIaddr); + } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 && + ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) { + outgoingPacketType = DHCPPacketType.DHCPNAK; + } else { + return; + } + Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); + } + } + } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) { + Ip4Address ip4Address = dhcpStore.releaseIP(hostId); + if (ip4Address != null) { + hostProviderService.removeIpFromHost(hostId, ip4Address); + } + } + } + } + + /** + * 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<IpAddress> 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) && + Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) { + + 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 == null) { + return; + } + if (cfg.ip() != null) { + myIP = cfg.ip(); + } + if (cfg.mac() != null) { + myMAC = 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() != -1) { + packetTTL = (byte) cfg.ttl(); + } + if (cfg.leaseTime() != -1) { + leaseTime = cfg.leaseTime(); + } + if (cfg.renewTime() != -1) { + renewalTime = cfg.renewTime(); + } + if (cfg.rebindTime() != -1) { + rebindingTime = cfg.rebindTime(); + } + if (cfg.defaultTimeout() != -1) { + dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout()); + } + if (cfg.timerDelay() != -1) { + timerDelay = cfg.timerDelay(); + } + if ((cfg.startIp() != null) && (cfg.endIp() != null)) { + dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp()); + } + } + + + @Override + public void event(NetworkConfigEvent event) { + + if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) && + event.configClass().equals(DhcpConfig.class)) { + + DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class); + reconfigureNetwork(cfg); + log.info("Reconfigured"); + } + } + } + + 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 + } + } + + private class PurgeListTask implements TimerTask { + + @Override + public void run(Timeout to) { + IpAssignment ipAssignment; + Date dateNow = new Date(); + + Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping(); + for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) { + ipAssignment = entry.getValue(); + + long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime(); + if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) && + (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) { + + Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey()); + if (ip4Address != null) { + hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress()); + } + } + } + timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES); + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpUi.java new file mode 100644 index 00000000..bb2bd2c2 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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<UiView> 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/app/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java new file mode 100644 index 00000000..9ce65d5e --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpViewMessageHandler.java @@ -0,0 +1,97 @@ +/* + * 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.onosproject.cli.AbstractShellCommand; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.net.HostId; +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 HOST = "host"; + private static final String IP = "ip"; + private static final String LEASE = "lease"; + + private static final String[] COL_IDS = { + HOST, IP, LEASE + }; + + @Override + protected Collection<RequestHandler> createRequestHandlers() { + return ImmutableSet.of( + new DataRequestHandler() + ); + } + + // handler for dhcp table requests + private final class DataRequestHandler extends TableRequestHandler { + + private DataRequestHandler() { + super(DHCP_DATA_REQ, DHCP_DATA_RESP, DHCP); + } + + @Override + protected String defaultColumnId() { + return HOST; + } + + @Override + protected String[] getColumnIds() { + return COL_IDS; + } + + @Override + protected void populateTable(TableModel tm, ObjectNode payload) { + DhcpService dhcpService = AbstractShellCommand.get(DhcpService.class); + Map<HostId, IpAssignment> allocationMap = dhcpService.listMapping(); + + for (Map.Entry<HostId, IpAssignment> entry : allocationMap.entrySet()) { + populateRow(tm.addRow(), entry); + } + } + + private void populateRow(TableModel.Row row, Map.Entry<HostId, IpAssignment> entry) { + if (entry.getValue().leasePeriod() > 0) { + Date now = new Date(entry.getValue().timestamp().getTime() + entry.getValue().leasePeriod()); + row.cell(HOST, entry.getKey()) + .cell(IP, entry.getValue().ipAddress()) + .cell(LEASE, now.toString()); + } else { + row.cell(HOST, entry.getKey()) + .cell(IP, entry.getValue().ipAddress()) + .cell(LEASE, "Infinite Static Lease"); + } + } + } +} diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java new file mode 100644 index 00000000..63f69d40 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java @@ -0,0 +1,328 @@ +/* + * 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.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onlab.util.KryoNamespace; +import org.onosproject.dhcp.DhcpStore; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.net.HostId; +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.Objects; + +/** + * 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<HostId, IpAssignment> allocationMap; + + private DistributedSet<Ip4Address> freeIPPool; + + private static Ip4Address startIPRange; + + private static Ip4Address endIPRange; + + // Hardcoded values are default values. + + private static int timeoutForPendingAssignments = 60; + + @Activate + protected void activate() { + allocationMap = storageService.<HostId, IpAssignment>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.<Ip4Address>setBuilder() + .withName("onos-dhcp-freeIP") + .withSerializer(Serializer.using(KryoNamespaces.API)) + .build(); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + log.info("Stopped"); + } + + @Override + public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) { + + IpAssignment assignmentInfo; + if (allocationMap.containsKey(hostId)) { + assignmentInfo = allocationMap.get(hostId).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 (ipWithinRange(ipAddr)) { + 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(hostId, assignmentInfo); + return ipAddr; + } + } + } + } 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(hostId, assignmentInfo); + return requestedIP; + } + } + } + + // Allocate a new IP from the server's pool of available IP. + Ip4Address nextIPAddr = fetchNextIP(); + if (nextIPAddr != null) { + assignmentInfo = IpAssignment.builder() + .ipAddress(nextIPAddr) + .timestamp(new Date()) + .leasePeriod(timeoutForPendingAssignments) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested) + .build(); + + allocationMap.put(hostId, assignmentInfo); + } + return nextIPAddr; + + } + + @Override + public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime) { + + IpAssignment assignmentInfo; + if (allocationMap.containsKey(hostId)) { + assignmentInfo = allocationMap.get(hostId).value(); + IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus(); + + if (Objects.equals(assignmentInfo.ipAddress(), ipAddr) && ipWithinRange(ipAddr)) { + + if (status == IpAssignment.AssignmentStatus.Option_Assigned || + status == IpAssignment.AssignmentStatus.Option_Requested) { + // Client has a currently active binding with the server. + assignmentInfo = IpAssignment.builder() + .ipAddress(ipAddr) + .timestamp(new Date()) + .leasePeriod(leaseTime) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned) + .build(); + allocationMap.put(hostId, assignmentInfo); + return true; + } else if (status == IpAssignment.AssignmentStatus.Option_Expired) { + // Client has an expired binding with the server. + 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(hostId, 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(hostId, assignmentInfo); + return true; + } + } + return false; + } + + @Override + public Ip4Address releaseIP(HostId hostId) { + if (allocationMap.containsKey(hostId)) { + IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value()) + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired) + .build(); + Ip4Address freeIP = newAssignment.ipAddress(); + allocationMap.put(hostId, newAssignment); + if (ipWithinRange(freeIP)) { + freeIPPool.add(freeIP); + } + return freeIP; + } + return null; + } + + @Override + public void setDefaultTimeoutForPurge(int timeInSeconds) { + timeoutForPendingAssignments = timeInSeconds; + } + + @Override + public Map<HostId, IpAssignment> listAssignedMapping() { + + Map<HostId, IpAssignment> validMapping = new HashMap<>(); + IpAssignment assignment; + for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) { + assignment = entry.getValue().value(); + if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned) { + validMapping.put(entry.getKey(), assignment); + } + } + return validMapping; + } + + @Override + public Map<HostId, IpAssignment> listAllMapping() { + Map<HostId, IpAssignment> validMapping = new HashMap<>(); + for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) { + validMapping.put(entry.getKey(), entry.getValue().value()); + } + return validMapping; + } + + @Override + public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) { + HostId host = HostId.hostId(macID); + return assignIP(host, ipAddr, -1); + } + + @Override + public boolean removeStaticIP(MacAddress macID) { + HostId host = HostId.hostId(macID); + if (allocationMap.containsKey(host)) { + IpAssignment assignment = allocationMap.get(host).value(); + Ip4Address freeIP = assignment.ipAddress(); + if (assignment.leasePeriod() < 0) { + allocationMap.remove(host); + if (ipWithinRange(freeIP)) { + freeIPPool.add(freeIP); + } + return true; + } + } + return false; + } + + @Override + public Iterable<Ip4Address> getAvailableIPs() { + return ImmutableSet.copyOf(freeIPPool); + } + + @Override + public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) { + // Clear all entries from previous range. + allocationMap.clear(); + freeIPPool.clear(); + startIPRange = startIP; + endIPRange = endIP; + + 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; + } + + /** + * Returns true if the given ip is within the range of available IPs. + * + * @param ip given ip address + * @return true if within range, false otherwise + */ + private boolean ipWithinRange(Ip4Address ip) { + if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) { + return true; + } + return false; + } +} diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/package-info.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/package-info.java new file mode 100644 index 00000000..12e14e48 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Implementation classes for sample application that assigns and manages DHCP leases. + */ +package org.onosproject.dhcp.impl;
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java new file mode 100644 index 00000000..646ab7ea --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DHCPWebResource.java @@ -0,0 +1,164 @@ +/* + * 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.net.HostId; +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<HostId, IpAssignment> intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("host", 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<Ip4Address> 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<HostId, IpAssignment> intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("host", 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<HostId, IpAssignment> intents = service.listMapping(); + ArrayNode arrayNode = root.putArray("mappings"); + intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode() + .put("host", i.getKey().toString()) + .put("ip", i.getValue().ipAddress().toString()))); + + return ok(root.toString()).build(); + } +} diff --git a/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/package-info.java b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/package-info.java new file mode 100644 index 00000000..73173c55 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * REST APIs for sample application that assigns and manages DHCP leases. + */ +package org.onosproject.dhcp.rest;
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/dhcp/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..ce716315 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,43 @@ +<!-- + ~ 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. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.dhcp.cli.DhcpListAllMappings"/> + </command> + <command> + <action class="org.onosproject.dhcp.cli.DhcpLeaseDetails"/> + </command> + <command> + <action class="org.onosproject.dhcp.cli.DhcpSetStaticMapping"/> + <completers> + <ref component-id="macIDCompleter"/> + <ref component-id="freeIPCompleter"/> + </completers> + </command> + <command> + <action class="org.onosproject.dhcp.cli.DhcpRemoveStaticMapping"/> + <completers> + <ref component-id="macIDCompleter"/> + </completers> + </command> + </command-bundle> + + <bean id="macIDCompleter" class="org.onosproject.dhcp.cli.MacIdCompleter"/> + <bean id="freeIPCompleter" class="org.onosproject.dhcp.cli.FreeIpCompleter"/> + +</blueprint>
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.css b/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.css new file mode 100644 index 00000000..e0a29314 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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/app/src/main/resources/app/view/dhcp/dhcp.html b/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.html new file mode 100644 index 00000000..5782badf --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.html @@ -0,0 +1,47 @@ +<!-- DHCP Server partial HTML --> +<div id="ov-dhcp"> + <div class="tabular-header"> + <h2>DHCP Mappings ({{tableData.length}} total)</h2> + <div class="ctrl-btns"> + <div class="refresh" ng-class="{active: autoRefresh}" + icon icon-size="36" icon-id="refresh" + tooltip tt-msg="autoRefreshTip" + ng-click="toggleRefresh()"></div> + </div> + </div> + + <div class="summary-list" onos-table-resize> + <div ng-show="loading" class="loading-wheel" + icon icon-id="loading" icon-size="75"></div> + + <div class="table-header" onos-sortable-header> + <table> + <tr> + <td colId="host" sortable>Host ID</td> + <td colId="ip" sortable>IP Address</td> + <td colId="lease" sortable>Lease Expiry</td> + </tr> + </table> + </div> + + <div class="table-body"> + <table onos-flash-changes id-prop="host"> + <tr ng-if="!tableData.length" class="no-data"> + <td colspan="2"> + No mappings found + </td> + </tr> + + <tr ng-repeat="dhcp in tableData track by $index" + ng-click="selectCallback($event, dhcp)" + ng-repeat-complete row-id="{{dhcp.host}}"> + <td>{{dhcp.host}}</td> + <td>{{dhcp.ip}}</td> + <td>{{dhcp.lease}}</td> + </tr> + </table> + </div> + + </div> + +</div> diff --git a/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.js b/framework/src/onos/apps/dhcp/app/src/main/resources/app/view/dhcp/dhcp.js new file mode 100644 index 00000000..061d0de6 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/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/app/src/main/resources/gui/css.html b/framework/src/onos/apps/dhcp/app/src/main/resources/gui/css.html new file mode 100644 index 00000000..d02ad44a --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/resources/gui/css.html @@ -0,0 +1 @@ +<link rel="stylesheet" href="app/view/dhcp/dhcp.css">
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/resources/gui/js.html b/framework/src/onos/apps/dhcp/app/src/main/resources/gui/js.html new file mode 100644 index 00000000..d37b5768 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/resources/gui/js.html @@ -0,0 +1 @@ +<script src="app/view/dhcp/dhcp.js"></script>
\ No newline at end of file diff --git a/framework/src/onos/apps/dhcp/app/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/dhcp/app/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..27504548 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" + xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + id="ONOS" version="2.5"> + <display-name>DHCP Server REST API v1.0</display-name> + + <servlet> + <servlet-name>JAX-RS Service</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name> + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.config.property.classnames</param-name> + <param-value> + org.onosproject.dhcp.rest.DHCPWebResource + </param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>JAX-RS Service</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> +</web-app> diff --git a/framework/src/onos/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/framework/src/onos/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java new file mode 100644 index 00000000..fd4701c6 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java @@ -0,0 +1,392 @@ +/* + * 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.IpAddress; +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 HostId CLIENT1_HOST = HostId.hostId(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_HOST.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_HOST.mac().toBytes()); + dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); + dhcpReply.setHardwareAddressLength((byte) 6); + + // DHCP Options. + DHCPOption option = new DHCPOption(); + List<DHCPOption> 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_HOST.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(HostId hostId, Ip4Address requestedIP) { + return Ip4Address.valueOf(EXPECTED_IP); + } + + public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime) { + return true; + } + + public void setDefaultTimeoutForPurge(int timeInSeconds) { + } + + public Ip4Address releaseIP(HostId hostId) { + return null; + } + + public Map<HostId, IpAssignment> listAssignedMapping() { + return listAllMapping(); + } + + public Map<HostId, IpAssignment> listAllMapping() { + Map<HostId, IpAssignment> 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_HOST, assignment); + return map; + } + + public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) { + return true; + } + + public boolean removeStaticIP(MacAddress macID) { + return true; + } + + public Iterable<Ip4Address> getAvailableIPs() { + List<Ip4Address> 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<HostProvider> + implements HostProviderService { + + protected TestHostProviderService(HostProvider provider) { + super(provider); + } + + @Override + public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) { + + } + + @Override + public void hostVanished(HostId hostId) { + } + + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + + } + + } + + /** + * 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<ProviderId> getProviders() { + return null; + } + + } + +} diff --git a/framework/src/onos/apps/dhcp/app/src/test/resources/dhcp-cfg.json b/framework/src/onos/apps/dhcp/app/src/test/resources/dhcp-cfg.json new file mode 100644 index 00000000..abc48a83 --- /dev/null +++ b/framework/src/onos/apps/dhcp/app/src/test/resources/dhcp-cfg.json @@ -0,0 +1,22 @@ +{ + "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", + "delay": "3", + "timeout": "150", + "startip": "10.0.0.110", + "endip": "10.0.0.130" + } + } + } +}
\ No newline at end of file |