blob: 94b0ddca0d6110d658516a1b8162f63a5818aff2 [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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,
Yuta HIGUCHIbc365602016-08-15 14:14:05 -070079 Date.class)
80 .build("dhcp")))
samanwita palf28207b2015-09-04 10:41:56 -070081 .build();
82
83 freeIPPool = storageService.<Ip4Address>setBuilder()
84 .withName("onos-dhcp-freeIP")
85 .withSerializer(Serializer.using(KryoNamespaces.API))
Madan Jampani538be742016-02-10 14:55:38 -080086 .build()
87 .asDistributedSet();
samanwita palf28207b2015-09-04 10:41:56 -070088
samanwita palf28207b2015-09-04 10:41:56 -070089 log.info("Started");
90 }
91
92 @Deactivate
93 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -070094 log.info("Stopped");
95 }
96
97 @Override
samanwita pal2a313402015-09-14 16:03:22 -070098 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -070099
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700100 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -0700101 if (allocationMap.containsKey(hostId)) {
102 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700103 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700104 Ip4Address ipAddr = assignmentInfo.ipAddress();
105
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700106 if (assignmentInfo.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900107 return assignmentInfo.ipAddress();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700108 } else if (status == Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700109 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700110 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700111 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700112 return ipAddr;
113 }
114
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700115 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700116 // Client has a Released or Expired Binding.
117 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700118 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700119 .ipAddress(ipAddr)
120 .timestamp(new Date())
121 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700122 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700123 .build();
124 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700125 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700126 return ipAddr;
127 }
128 }
129 }
samanwita palf28207b2015-09-04 10:41:56 -0700130 } else if (requestedIP.toInt() != 0) {
131 // Client has requested an IP.
132 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700133 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700134 .ipAddress(requestedIP)
135 .timestamp(new Date())
136 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700137 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700138 .build();
139 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700140 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700141 return requestedIP;
142 }
143 }
144 }
145
146 // Allocate a new IP from the server's pool of available IP.
147 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700148 if (nextIPAddr != null) {
149 assignmentInfo = IpAssignment.builder()
150 .ipAddress(nextIPAddr)
151 .timestamp(new Date())
152 .leasePeriod(timeoutForPendingAssignments)
153 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
154 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700155
samanwita pal2a313402015-09-14 16:03:22 -0700156 allocationMap.put(hostId, assignmentInfo);
157 }
samanwita palf28207b2015-09-04 10:41:56 -0700158 return nextIPAddr;
159
160 }
161
162 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700163 public boolean assignIP(HostId hostId, IpAssignment ipAssignment) {
164 log.trace("Assign IP Called HostId: {}, ipAssignment: {}",
165 hostId, ipAssignment);
daniel877bb2f2015-11-12 21:33:05 +0900166
Madan Jampani56dcdce2016-02-03 13:58:52 -0800167 IpAssignment newAssignment = null;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700168 Versioned<IpAssignment> versionedAssignment = allocationMap.get(hostId);
169 Ip4Address requestedIp = ipAssignment.ipAddress();
Hyunsun Moon08242082015-12-08 01:29:22 -0800170
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700171 if (versionedAssignment == null) {
172 // this is new IP assignment of static mapping
173 // dynamic assignment is done in suggestIP
174 if (ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
175 newAssignment = ipAssignment;
176 } else if (freeIPPool.remove(requestedIp)) {
177 newAssignment = IpAssignment.builder(ipAssignment)
178 .assignmentStatus(Option_Assigned)
Madan Jampani56dcdce2016-02-03 13:58:52 -0800179 .timestamp(new Date())
Madan Jampani56dcdce2016-02-03 13:58:52 -0800180 .build();
181 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700182 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800183 return false;
184 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700185 log.trace("Assigned {}", newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800186 return allocationMap.putIfAbsent(hostId, newAssignment) == null;
187 // TODO: handle the case where map changed.
188 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700189 // this is lease renew or rebinding
190 // update assignment status and time stamp, and keep the others
191 IpAssignment existingAssignment = versionedAssignment.value();
192 if (!existingAssignment.ipAddress().equals(requestedIp)) {
193 // return false if existing assignment is not for the
194 // requested host
195 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800196 return false;
197 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700198
199 switch (existingAssignment.assignmentStatus()) {
200 case Option_RangeNotEnforced:
201 newAssignment = IpAssignment.builder(existingAssignment)
202 .timestamp(new Date())
203 .build();
204 break;
205 case Option_Expired:
206 if (!freeIPPool.remove(requestedIp)) {
207 // requested IP is expired for this host and reserved to the other host
208 return false;
209 }
210 case Option_Assigned:
211 case Option_Requested:
212 newAssignment = IpAssignment.builder(existingAssignment)
213 .timestamp(new Date())
214 .assignmentStatus(Option_Assigned)
215 .build();
216 break;
217 default:
218 break;
219 }
220 log.trace("Assigned {}", newAssignment);
221 return allocationMap.replace(hostId, versionedAssignment.version(), newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800222 }
samanwita palf28207b2015-09-04 10:41:56 -0700223 }
224
225 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700226 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700227 if (allocationMap.containsKey(hostId)) {
sangho5808d522016-08-12 16:03:34 +0900228 // If the IP has been assigned with Option_RangeNotEnforced,
229 // we do not release the IP address nor remove the host from HostService.
230 // Therefore, if the IP is assigned statically, the IP needs to be released statically.
231 Versioned<IpAssignment> assignmentVersioned = allocationMap.get(hostId);
232 if (Versioned.valueOrNull(assignmentVersioned) != null &&
233 assignmentVersioned.value().assignmentStatus().equals(Option_RangeNotEnforced)) {
234 return null;
235 }
236
samanwita pal2a313402015-09-14 16:03:22 -0700237 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700238 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
239 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700240 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700241 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700242 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700243 freeIPPool.add(freeIP);
244 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700245 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700246 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700247 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700248 }
249
250 @Override
251 public void setDefaultTimeoutForPurge(int timeInSeconds) {
252 timeoutForPendingAssignments = timeInSeconds;
253 }
254
255 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700256 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700257
samanwita pal0bff4302015-09-15 13:37:00 -0700258 Map<HostId, IpAssignment> validMapping = new HashMap<>();
259 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700260 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700261 assignment = entry.getValue().value();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700262 if (assignment.assignmentStatus() == Option_Assigned
263 || assignment.assignmentStatus() == Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700264 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700265 }
266 }
samanwita pal0bff4302015-09-15 13:37:00 -0700267 return validMapping;
268 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700269
samanwita pal0bff4302015-09-15 13:37:00 -0700270 @Override
271 public Map<HostId, IpAssignment> listAllMapping() {
272 Map<HostId, IpAssignment> validMapping = new HashMap<>();
273 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
274 validMapping.put(entry.getKey(), entry.getValue().value());
275 }
276 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700277 }
278
279 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700280 public boolean assignStaticIP(MacAddress macAddress, IpAssignment ipAssignment) {
281 HostId host = HostId.hostId(macAddress);
282 return assignIP(host, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700283 }
284
285 @Override
286 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700287 HostId host = HostId.hostId(macID);
288 if (allocationMap.containsKey(host)) {
289 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900290
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700291 if (assignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
daniel877bb2f2015-11-12 21:33:05 +0900292 allocationMap.remove(host);
293 return true;
294 }
295
samanwita palf28207b2015-09-04 10:41:56 -0700296 Ip4Address freeIP = assignment.ipAddress();
297 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700298 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700299 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700300 freeIPPool.add(freeIP);
301 }
samanwita palf28207b2015-09-04 10:41:56 -0700302 return true;
303 }
304 }
305 return false;
306 }
307
308 @Override
309 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700310 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700311 }
312
313 @Override
314 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
315 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700316 allocationMap.clear();
317 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700318 startIPRange = startIP;
319 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700320
321 int lastIP = endIP.toInt();
322 Ip4Address nextIP;
323 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
324 nextIP = Ip4Address.valueOf(loopCounter);
325 freeIPPool.add(nextIP);
326 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700327 log.debug("Updated free IP pool {}:{} size:{}", startIP, endIP, freeIPPool.size());
samanwita palf28207b2015-09-04 10:41:56 -0700328 }
329
danielcd9deed2015-10-30 17:16:16 +0900330 @Override
331 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700332 if (allocationMap.get(hostId) != null) {
333 return allocationMap.get(hostId).value();
334 } else {
335 return null;
336 }
danielcd9deed2015-10-30 17:16:16 +0900337 }
338
samanwita palf28207b2015-09-04 10:41:56 -0700339 /**
340 * Fetches the next available IP from the free pool pf IPs.
341 *
342 * @return the next available IP address
343 */
344 private Ip4Address fetchNextIP() {
345 for (Ip4Address freeIP : freeIPPool) {
346 if (freeIPPool.remove(freeIP)) {
347 return freeIP;
348 }
349 }
350 return null;
351 }
samanwita pal0bff4302015-09-15 13:37:00 -0700352
353 /**
354 * Returns true if the given ip is within the range of available IPs.
355 *
356 * @param ip given ip address
357 * @return true if within range, false otherwise
358 */
359 private boolean ipWithinRange(Ip4Address ip) {
360 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
361 return true;
362 }
363 return false;
364 }
samanwita palf28207b2015-09-04 10:41:56 -0700365}
danielcd9deed2015-10-30 17:16:16 +0900366