blob: 38a19053ba29c49a1939a0cd26b5d6cb737a9d57 [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
samanwita palf28207b2015-09-04 10:41:56 -07003 *
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 */
16package org.onosproject.dhcp.impl;
17
18import com.google.common.collect.ImmutableSet;
19import 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;
samanwita palf28207b2015-09-04 10:41:56 -070025import org.onlab.packet.Ip4Address;
26import org.onlab.packet.MacAddress;
27import org.onlab.util.KryoNamespace;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070028import org.onosproject.dhcp.DhcpStore;
29import org.onosproject.dhcp.IpAssignment;
samanwita pal2a313402015-09-14 16:03:22 -070030import org.onosproject.net.HostId;
samanwita palf28207b2015-09-04 10:41:56 -070031import org.onosproject.store.serializers.KryoNamespaces;
32import org.onosproject.store.service.ConsistentMap;
33import org.onosproject.store.service.DistributedSet;
34import org.onosproject.store.service.Serializer;
35import org.onosproject.store.service.StorageService;
36import org.onosproject.store.service.Versioned;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
40import java.util.Date;
samanwita palf28207b2015-09-04 10:41:56 -070041import java.util.Map;
danielcd9deed2015-10-30 17:16:16 +090042import java.util.HashMap;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070043
44import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Assigned;
45import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
samanwita palf28207b2015-09-04 10:41:56 -070046
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
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070054public class DistributedDhcpStore implements DhcpStore {
samanwita palf28207b2015-09-04 10:41:56 -070055
56 private final Logger log = LoggerFactory.getLogger(getClass());
57
58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 protected StorageService storageService;
60
samanwita pal2a313402015-09-14 16:03:22 -070061 private ConsistentMap<HostId, IpAssignment> allocationMap;
samanwita palf28207b2015-09-04 10:41:56 -070062 private DistributedSet<Ip4Address> freeIPPool;
63
samanwita palf28207b2015-09-04 10:41:56 -070064 private static Ip4Address startIPRange;
samanwita palf28207b2015-09-04 10:41:56 -070065 private static Ip4Address endIPRange;
66
67 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -070068 private static int timeoutForPendingAssignments = 60;
69
70 @Activate
71 protected void activate() {
samanwita pal2a313402015-09-14 16:03:22 -070072 allocationMap = storageService.<HostId, IpAssignment>consistentMapBuilder()
samanwita palf28207b2015-09-04 10:41:56 -070073 .withName("onos-dhcp-assignedIP")
74 .withSerializer(Serializer.using(
75 new KryoNamespace.Builder()
76 .register(KryoNamespaces.API)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070077 .register(IpAssignment.class,
78 IpAssignment.AssignmentStatus.class,
samanwita palf28207b2015-09-04 10:41:56 -070079 Date.class,
80 long.class,
81 Ip4Address.class)
82 .build()))
83 .build();
84
85 freeIPPool = storageService.<Ip4Address>setBuilder()
86 .withName("onos-dhcp-freeIP")
87 .withSerializer(Serializer.using(KryoNamespaces.API))
Madan Jampani538be742016-02-10 14:55:38 -080088 .build()
89 .asDistributedSet();
samanwita palf28207b2015-09-04 10:41:56 -070090
samanwita palf28207b2015-09-04 10:41:56 -070091 log.info("Started");
92 }
93
94 @Deactivate
95 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -070096 log.info("Stopped");
97 }
98
99 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700100 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -0700101
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700102 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -0700103 if (allocationMap.containsKey(hostId)) {
104 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700105 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700106 Ip4Address ipAddr = assignmentInfo.ipAddress();
107
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700108 if (assignmentInfo.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900109 return assignmentInfo.ipAddress();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700110 } else if (status == Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700111 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700112 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700113 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700114 return ipAddr;
115 }
116
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700117 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700118 // Client has a Released or Expired Binding.
119 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700120 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700121 .ipAddress(ipAddr)
122 .timestamp(new Date())
123 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700124 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700125 .build();
126 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700127 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700128 return ipAddr;
129 }
130 }
131 }
samanwita palf28207b2015-09-04 10:41:56 -0700132 } else if (requestedIP.toInt() != 0) {
133 // Client has requested an IP.
134 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700135 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700136 .ipAddress(requestedIP)
137 .timestamp(new Date())
138 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700139 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700140 .build();
141 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700142 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700143 return requestedIP;
144 }
145 }
146 }
147
148 // Allocate a new IP from the server's pool of available IP.
149 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700150 if (nextIPAddr != null) {
151 assignmentInfo = IpAssignment.builder()
152 .ipAddress(nextIPAddr)
153 .timestamp(new Date())
154 .leasePeriod(timeoutForPendingAssignments)
155 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
156 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700157
samanwita pal2a313402015-09-14 16:03:22 -0700158 allocationMap.put(hostId, assignmentInfo);
159 }
samanwita palf28207b2015-09-04 10:41:56 -0700160 return nextIPAddr;
161
162 }
163
164 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700165 public boolean assignIP(HostId hostId, IpAssignment ipAssignment) {
166 log.trace("Assign IP Called HostId: {}, ipAssignment: {}",
167 hostId, ipAssignment);
daniel877bb2f2015-11-12 21:33:05 +0900168
Madan Jampani56dcdce2016-02-03 13:58:52 -0800169 IpAssignment newAssignment = null;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700170 Versioned<IpAssignment> versionedAssignment = allocationMap.get(hostId);
171 Ip4Address requestedIp = ipAssignment.ipAddress();
Hyunsun Moon08242082015-12-08 01:29:22 -0800172
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700173 if (versionedAssignment == null) {
174 // this is new IP assignment of static mapping
175 // dynamic assignment is done in suggestIP
176 if (ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
177 newAssignment = ipAssignment;
178 } else if (freeIPPool.remove(requestedIp)) {
179 newAssignment = IpAssignment.builder(ipAssignment)
180 .assignmentStatus(Option_Assigned)
Madan Jampani56dcdce2016-02-03 13:58:52 -0800181 .timestamp(new Date())
Madan Jampani56dcdce2016-02-03 13:58:52 -0800182 .build();
183 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700184 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800185 return false;
186 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700187 log.trace("Assigned {}", newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800188 return allocationMap.putIfAbsent(hostId, newAssignment) == null;
189 // TODO: handle the case where map changed.
190 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700191 // this is lease renew or rebinding
192 // update assignment status and time stamp, and keep the others
193 IpAssignment existingAssignment = versionedAssignment.value();
194 if (!existingAssignment.ipAddress().equals(requestedIp)) {
195 // return false if existing assignment is not for the
196 // requested host
197 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800198 return false;
199 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700200
201 switch (existingAssignment.assignmentStatus()) {
202 case Option_RangeNotEnforced:
203 newAssignment = IpAssignment.builder(existingAssignment)
204 .timestamp(new Date())
205 .build();
206 break;
207 case Option_Expired:
208 if (!freeIPPool.remove(requestedIp)) {
209 // requested IP is expired for this host and reserved to the other host
210 return false;
211 }
212 case Option_Assigned:
213 case Option_Requested:
214 newAssignment = IpAssignment.builder(existingAssignment)
215 .timestamp(new Date())
216 .assignmentStatus(Option_Assigned)
217 .build();
218 break;
219 default:
220 break;
221 }
222 log.trace("Assigned {}", newAssignment);
223 return allocationMap.replace(hostId, versionedAssignment.version(), newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800224 }
samanwita palf28207b2015-09-04 10:41:56 -0700225 }
226
227 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700228 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700229 if (allocationMap.containsKey(hostId)) {
230 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700231 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
232 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700233 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700234 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700235 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700236 freeIPPool.add(freeIP);
237 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700238 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700239 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700240 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700241 }
242
243 @Override
244 public void setDefaultTimeoutForPurge(int timeInSeconds) {
245 timeoutForPendingAssignments = timeInSeconds;
246 }
247
248 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700249 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700250
samanwita pal0bff4302015-09-15 13:37:00 -0700251 Map<HostId, IpAssignment> validMapping = new HashMap<>();
252 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700253 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700254 assignment = entry.getValue().value();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700255 if (assignment.assignmentStatus() == Option_Assigned
256 || assignment.assignmentStatus() == Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700257 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700258 }
259 }
samanwita pal0bff4302015-09-15 13:37:00 -0700260 return validMapping;
261 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700262
samanwita pal0bff4302015-09-15 13:37:00 -0700263 @Override
264 public Map<HostId, IpAssignment> listAllMapping() {
265 Map<HostId, IpAssignment> validMapping = new HashMap<>();
266 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
267 validMapping.put(entry.getKey(), entry.getValue().value());
268 }
269 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700270 }
271
272 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700273 public boolean assignStaticIP(MacAddress macAddress, IpAssignment ipAssignment) {
274 HostId host = HostId.hostId(macAddress);
275 return assignIP(host, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700276 }
277
278 @Override
279 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700280 HostId host = HostId.hostId(macID);
281 if (allocationMap.containsKey(host)) {
282 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900283
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700284 if (assignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
daniel877bb2f2015-11-12 21:33:05 +0900285 allocationMap.remove(host);
286 return true;
287 }
288
samanwita palf28207b2015-09-04 10:41:56 -0700289 Ip4Address freeIP = assignment.ipAddress();
290 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700291 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700292 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700293 freeIPPool.add(freeIP);
294 }
samanwita palf28207b2015-09-04 10:41:56 -0700295 return true;
296 }
297 }
298 return false;
299 }
300
301 @Override
302 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700303 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700304 }
305
306 @Override
307 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
308 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700309 allocationMap.clear();
310 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700311 startIPRange = startIP;
312 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700313
314 int lastIP = endIP.toInt();
315 Ip4Address nextIP;
316 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
317 nextIP = Ip4Address.valueOf(loopCounter);
318 freeIPPool.add(nextIP);
319 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700320 log.debug("Updated free IP pool {}:{} size:{}", startIP, endIP, freeIPPool.size());
samanwita palf28207b2015-09-04 10:41:56 -0700321 }
322
danielcd9deed2015-10-30 17:16:16 +0900323 @Override
324 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700325 if (allocationMap.get(hostId) != null) {
326 return allocationMap.get(hostId).value();
327 } else {
328 return null;
329 }
danielcd9deed2015-10-30 17:16:16 +0900330 }
331
samanwita palf28207b2015-09-04 10:41:56 -0700332 /**
333 * Fetches the next available IP from the free pool pf IPs.
334 *
335 * @return the next available IP address
336 */
337 private Ip4Address fetchNextIP() {
338 for (Ip4Address freeIP : freeIPPool) {
339 if (freeIPPool.remove(freeIP)) {
340 return freeIP;
341 }
342 }
343 return null;
344 }
samanwita pal0bff4302015-09-15 13:37:00 -0700345
346 /**
347 * Returns true if the given ip is within the range of available IPs.
348 *
349 * @param ip given ip address
350 * @return true if within range, false otherwise
351 */
352 private boolean ipWithinRange(Ip4Address ip) {
353 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
354 return true;
355 }
356 return false;
357 }
samanwita palf28207b2015-09-04 10:41:56 -0700358}
danielcd9deed2015-10-30 17:16:16 +0900359