blob: 7344e6c3d4cd727b114b6acfc4a2afa16771c675 [file] [log] [blame]
samanwita pal18696f62015-07-17 13:15:43 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
samanwita palf66ed8a2015-07-21 15:45:33 -070016package org.onosproject.dhcpserver.impl;
samanwita pal18696f62015-07-17 13:15:43 -070017
samanwita palf66ed8a2015-07-21 15:45:33 -070018import com.google.common.collect.ImmutableSet;
samanwita pal18696f62015-07-17 13:15:43 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.jboss.netty.util.Timeout;
26import org.jboss.netty.util.TimerTask;
27import org.onlab.packet.Ip4Address;
28import org.onlab.packet.MacAddress;
29import org.onlab.util.KryoNamespace;
30import org.onlab.util.Timer;
samanwita palf66ed8a2015-07-21 15:45:33 -070031import org.onosproject.dhcpserver.DHCPStore;
32import org.onosproject.dhcpserver.IPAssignment;
samanwita pal18696f62015-07-17 13:15:43 -070033import org.onosproject.store.serializers.KryoNamespaces;
34import org.onosproject.store.service.ConsistentMap;
35import org.onosproject.store.service.DistributedSet;
36import org.onosproject.store.service.Serializer;
37import org.onosproject.store.service.StorageService;
38import org.onosproject.store.service.Versioned;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
42import java.util.Date;
samanwita palf66ed8a2015-07-21 15:45:33 -070043import java.util.HashMap;
samanwita pal18696f62015-07-17 13:15:43 -070044import java.util.Map;
45import java.util.concurrent.TimeUnit;
46
47/**
48 * Manages the pool of available IP Addresses in the network and
49 * Remembers the mapping between MAC ID and IP Addresses assigned.
50 */
51
52@Component(immediate = true)
53@Service
54public class DistributedDHCPStore implements DHCPStore {
55
56 private final Logger log = LoggerFactory.getLogger(getClass());
57
58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 protected StorageService storageService;
60
61 private ConsistentMap<MacAddress, IPAssignment> allocationMap;
62
63 private DistributedSet<Ip4Address> freeIPPool;
64
65 private Timeout timeout;
66
67 // TODO Make the hardcoded values configurable.
68
69 private static final int INITIAL_DELAY = 2;
70
samanwita palf66ed8a2015-07-21 15:45:33 -070071 private static int timeoutForPendingAssignments = 60;
samanwita pal18696f62015-07-17 13:15:43 -070072
samanwita palf66ed8a2015-07-21 15:45:33 -070073 private static final Ip4Address START_IP = Ip4Address.valueOf("10.1.0.140");
samanwita pal18696f62015-07-17 13:15:43 -070074
samanwita palf66ed8a2015-07-21 15:45:33 -070075 private static final Ip4Address END_IP = Ip4Address.valueOf("10.1.0.160");
samanwita pal18696f62015-07-17 13:15:43 -070076
77 @Activate
78 protected void activate() {
79 allocationMap = storageService.<MacAddress, IPAssignment>consistentMapBuilder()
80 .withName("onos-dhcp-assignedIP")
81 .withSerializer(Serializer.using(
82 new KryoNamespace.Builder()
83 .register(KryoNamespaces.API)
84 .register(IPAssignment.class,
85 IPAssignment.AssignmentStatus.class,
86 Date.class,
87 long.class,
88 Ip4Address.class)
89 .build()))
90 .build();
91
92 freeIPPool = storageService.<Ip4Address>setBuilder()
93 .withName("onos-dhcp-freeIP")
94 .withSerializer(Serializer.using(KryoNamespaces.API))
95 .build();
96
97 populateIPPoolfromRange(START_IP, END_IP);
98 timeout = Timer.getTimer().newTimeout(new PurgeListTask(), INITIAL_DELAY, TimeUnit.MINUTES);
99
100 log.info("Started");
101 }
102
103 @Deactivate
104 protected void deactivate() {
105 timeout.cancel();
106 log.info("Stopped");
107 }
108
109 @Override
samanwita palf09c09e2015-07-22 16:06:42 -0700110 public Ip4Address suggestIP(MacAddress macID, Ip4Address requestedIP) {
samanwita pal18696f62015-07-17 13:15:43 -0700111
112 IPAssignment assignmentInfo;
113 if (allocationMap.containsKey(macID)) {
114 assignmentInfo = allocationMap.get(macID).value();
samanwita palf09c09e2015-07-22 16:06:42 -0700115 IPAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
116 Ip4Address ipAddr = assignmentInfo.ipAddress();
117
118 if (status == IPAssignment.AssignmentStatus.Option_Assigned ||
119 status == IPAssignment.AssignmentStatus.Option_Requested) {
120 // Client has a currently Active Binding.
121 return ipAddr;
122
123 } else if (status == IPAssignment.AssignmentStatus.Option_Expired) {
124 // Client has a Released or Expired Binding.
125 if (freeIPPool.contains(ipAddr)) {
126 assignmentInfo = IPAssignment.builder()
127 .ipAddress(ipAddr)
128 .timestamp(new Date())
129 .leasePeriod(timeoutForPendingAssignments)
130 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Requested)
131 .build();
132 if (freeIPPool.remove(ipAddr)) {
133 allocationMap.put(macID, assignmentInfo);
134 return ipAddr;
135 }
136 }
137 }
samanwita palf66ed8a2015-07-21 15:45:33 -0700138 return assignmentInfo.ipAddress();
samanwita pal18696f62015-07-17 13:15:43 -0700139
samanwita palf09c09e2015-07-22 16:06:42 -0700140 } else if (requestedIP.toInt() != 0) {
141 // Client has requested an IP.
142 if (freeIPPool.contains(requestedIP)) {
143 assignmentInfo = IPAssignment.builder()
144 .ipAddress(requestedIP)
145 .timestamp(new Date())
146 .leasePeriod(timeoutForPendingAssignments)
147 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Requested)
148 .build();
149 if (freeIPPool.remove(requestedIP)) {
150 allocationMap.put(macID, assignmentInfo);
151 return requestedIP;
152 }
153 }
samanwita pal18696f62015-07-17 13:15:43 -0700154 }
samanwita palf09c09e2015-07-22 16:06:42 -0700155
156 // Allocate a new IP from the server's pool of available IP.
157 Ip4Address nextIPAddr = fetchNextIP();
158 assignmentInfo = IPAssignment.builder()
159 .ipAddress(nextIPAddr)
160 .timestamp(new Date())
161 .leasePeriod(timeoutForPendingAssignments)
162 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Requested)
163 .build();
164
165 allocationMap.put(macID, assignmentInfo);
166 return nextIPAddr;
167
samanwita pal18696f62015-07-17 13:15:43 -0700168 }
169
170 @Override
samanwita palf66ed8a2015-07-21 15:45:33 -0700171 public boolean assignIP(MacAddress macID, Ip4Address ipAddr, int leaseTime) {
samanwita pal18696f62015-07-17 13:15:43 -0700172
173 IPAssignment assignmentInfo;
174 if (allocationMap.containsKey(macID)) {
175 assignmentInfo = allocationMap.get(macID).value();
samanwita palf66ed8a2015-07-21 15:45:33 -0700176 if (assignmentInfo.ipAddress().toInt() == ipAddr.toInt()) {
177
178 assignmentInfo = IPAssignment.builder()
179 .ipAddress(ipAddr)
180 .timestamp(new Date())
181 .leasePeriod(leaseTime)
182 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Assigned)
183 .build();
samanwita pal18696f62015-07-17 13:15:43 -0700184 allocationMap.put(macID, assignmentInfo);
185 return true;
186 }
187 } else if (freeIPPool.contains(ipAddr)) {
samanwita palf66ed8a2015-07-21 15:45:33 -0700188 assignmentInfo = IPAssignment.builder()
189 .ipAddress(ipAddr)
190 .timestamp(new Date())
191 .leasePeriod(leaseTime)
192 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Assigned)
193 .build();
samanwita pal18696f62015-07-17 13:15:43 -0700194 if (freeIPPool.remove(ipAddr)) {
195 allocationMap.put(macID, assignmentInfo);
196 return true;
197 }
198 }
199 return false;
200 }
201
202 @Override
203 public void releaseIP(MacAddress macID) {
204 if (allocationMap.containsKey(macID)) {
samanwita palf09c09e2015-07-22 16:06:42 -0700205 IPAssignment newAssignment = IPAssignment.builder(allocationMap.get(macID).value())
206 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Expired)
207 .build();
208 Ip4Address freeIP = newAssignment.ipAddress();
209 allocationMap.put(macID, newAssignment);
samanwita pal18696f62015-07-17 13:15:43 -0700210 freeIPPool.add(freeIP);
211 }
212 }
213
214 @Override
samanwita palf66ed8a2015-07-21 15:45:33 -0700215 public void setDefaultTimeoutForPurge(int timeInSeconds) {
samanwita pal18696f62015-07-17 13:15:43 -0700216 timeoutForPendingAssignments = timeInSeconds;
217 }
218
samanwita palf66ed8a2015-07-21 15:45:33 -0700219 @Override
220 public Map<MacAddress, Ip4Address> listMapping() {
221
222 Map<MacAddress, Ip4Address> allMapping = new HashMap<>();
223 for (Map.Entry<MacAddress, Versioned<IPAssignment>> entry: allocationMap.entrySet()) {
224 IPAssignment assignment = entry.getValue().value();
225 if (assignment.assignmentStatus() == IPAssignment.AssignmentStatus.Option_Assigned) {
226 allMapping.put(entry.getKey(), assignment.ipAddress());
227 }
228 }
229
230 return allMapping;
231 }
232
233 @Override
234 public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) {
235 return assignIP(macID, ipAddr, -1);
236 }
237
238 @Override
239 public boolean removeStaticIP(MacAddress macID) {
240 if (allocationMap.containsKey(macID)) {
241 IPAssignment assignment = allocationMap.get(macID).value();
242 Ip4Address freeIP = assignment.ipAddress();
243 if (assignment.leasePeriod() < 0) {
244 allocationMap.remove(macID);
245 freeIPPool.add(freeIP);
246 return true;
247 }
248 }
249 return false;
250 }
251
252 @Override
253 public Iterable<Ip4Address> getAvailableIPs() {
254 return ImmutableSet.<Ip4Address>copyOf(freeIPPool);
255 }
256
samanwita pal18696f62015-07-17 13:15:43 -0700257 /**
258 * Appends all the IPs in a given range to the free pool of IPs.
259 *
260 * @param startIP Start IP for the range
261 * @param endIP End IP for the range
262 */
263 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
264 int lastIP = endIP.toInt();
265 Ip4Address nextIP;
266 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
267 nextIP = Ip4Address.valueOf(loopCounter);
268 freeIPPool.add(nextIP);
269 }
270 }
271
272 /**
273 * Fetches the next available IP from the free pool pf IPs.
274 *
275 * @return the next available IP address
276 */
277 private Ip4Address fetchNextIP() {
278 for (Ip4Address freeIP : freeIPPool) {
279 if (freeIPPool.remove(freeIP)) {
280 return freeIP;
281 }
282 }
283 return null;
284 }
285
286 /**
287 * Purges the IP allocation map to remove expired entries and returns the freed IPs to the free pool.
288 */
289 private class PurgeListTask implements TimerTask {
290
291 @Override
292 public void run(Timeout to) {
samanwita palf09c09e2015-07-22 16:06:42 -0700293 IPAssignment ipAssignment, newAssignment;
samanwita pal18696f62015-07-17 13:15:43 -0700294 Date dateNow = new Date();
295 for (Map.Entry<MacAddress, Versioned<IPAssignment>> entry: allocationMap.entrySet()) {
296 ipAssignment = entry.getValue().value();
samanwita palf66ed8a2015-07-21 15:45:33 -0700297 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
samanwita palf09c09e2015-07-22 16:06:42 -0700298 if ((ipAssignment.assignmentStatus() != IPAssignment.AssignmentStatus.Option_Expired) &&
299 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriod()))) {
samanwita palf66ed8a2015-07-21 15:45:33 -0700300 Ip4Address freeIP = ipAssignment.ipAddress();
samanwita palf09c09e2015-07-22 16:06:42 -0700301
302 newAssignment = IPAssignment.builder(ipAssignment)
303 .assignmentStatus(IPAssignment.AssignmentStatus.Option_Expired)
304 .build();
305 allocationMap.put(entry.getKey(), newAssignment);
samanwita pal18696f62015-07-17 13:15:43 -0700306 freeIPPool.add(freeIP);
307 }
308 }
309 timeout = Timer.getTimer().newTimeout(new PurgeListTask(), INITIAL_DELAY, TimeUnit.MINUTES);
310 }
311
312 }
313
314}