blob: e8ce2ccea4880628f252e8ca306af671f25623fc [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;
samanwita palf28207b2015-09-04 10:41:56 -070019import org.onlab.packet.Ip4Address;
20import org.onlab.packet.MacAddress;
21import org.onlab.util.KryoNamespace;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070022import org.onosproject.dhcp.DhcpStore;
23import org.onosproject.dhcp.IpAssignment;
samanwita pal2a313402015-09-14 16:03:22 -070024import org.onosproject.net.HostId;
samanwita palf28207b2015-09-04 10:41:56 -070025import org.onosproject.store.serializers.KryoNamespaces;
26import org.onosproject.store.service.ConsistentMap;
27import org.onosproject.store.service.DistributedSet;
28import org.onosproject.store.service.Serializer;
29import org.onosproject.store.service.StorageService;
30import org.onosproject.store.service.Versioned;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070031import org.osgi.service.component.annotations.Activate;
32import org.osgi.service.component.annotations.Component;
33import org.osgi.service.component.annotations.Deactivate;
34import org.osgi.service.component.annotations.Reference;
35import org.osgi.service.component.annotations.ReferenceCardinality;
samanwita palf28207b2015-09-04 10:41:56 -070036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.util.Date;
danielcd9deed2015-10-30 17:16:16 +090040import java.util.HashMap;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070041import java.util.Map;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070042
43import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Assigned;
44import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
samanwita palf28207b2015-09-04 10:41:56 -070045
46/**
47 * Manages the pool of available IP Addresses in the network and
48 * Remembers the mapping between MAC ID and IP Addresses assigned.
49 */
50
Ray Milkeyd84f89b2018-08-17 14:54:17 -070051@Component(immediate = true, service = DhcpStore.class)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070052public class DistributedDhcpStore implements DhcpStore {
samanwita palf28207b2015-09-04 10:41:56 -070053
54 private final Logger log = LoggerFactory.getLogger(getClass());
55
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056 @Reference(cardinality = ReferenceCardinality.MANDATORY)
samanwita palf28207b2015-09-04 10:41:56 -070057 protected StorageService storageService;
58
samanwita pal2a313402015-09-14 16:03:22 -070059 private ConsistentMap<HostId, IpAssignment> allocationMap;
samanwita palf28207b2015-09-04 10:41:56 -070060 private DistributedSet<Ip4Address> freeIPPool;
61
samanwita palf28207b2015-09-04 10:41:56 -070062 private static Ip4Address startIPRange;
samanwita palf28207b2015-09-04 10:41:56 -070063 private static Ip4Address endIPRange;
64
65 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -070066 private static int timeoutForPendingAssignments = 60;
67
68 @Activate
69 protected void activate() {
samanwita pal2a313402015-09-14 16:03:22 -070070 allocationMap = storageService.<HostId, IpAssignment>consistentMapBuilder()
samanwita palf28207b2015-09-04 10:41:56 -070071 .withName("onos-dhcp-assignedIP")
72 .withSerializer(Serializer.using(
73 new KryoNamespace.Builder()
74 .register(KryoNamespaces.API)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070075 .register(IpAssignment.class,
76 IpAssignment.AssignmentStatus.class,
Yuta HIGUCHIbc365602016-08-15 14:14:05 -070077 Date.class)
78 .build("dhcp")))
samanwita palf28207b2015-09-04 10:41:56 -070079 .build();
80
81 freeIPPool = storageService.<Ip4Address>setBuilder()
82 .withName("onos-dhcp-freeIP")
83 .withSerializer(Serializer.using(KryoNamespaces.API))
Madan Jampani538be742016-02-10 14:55:38 -080084 .build()
85 .asDistributedSet();
samanwita palf28207b2015-09-04 10:41:56 -070086
samanwita palf28207b2015-09-04 10:41:56 -070087 log.info("Started");
88 }
89
90 @Deactivate
91 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -070092 log.info("Stopped");
93 }
94
95 @Override
samanwita pal2a313402015-09-14 16:03:22 -070096 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -070097
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070098 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -070099 if (allocationMap.containsKey(hostId)) {
100 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700101 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700102 Ip4Address ipAddr = assignmentInfo.ipAddress();
103
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700104 if (assignmentInfo.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900105 return assignmentInfo.ipAddress();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700106 } else if (status == Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700107 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700108 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700109 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700110 return ipAddr;
111 }
112
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700113 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700114 // Client has a Released or Expired Binding.
115 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700116 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700117 .ipAddress(ipAddr)
118 .timestamp(new Date())
119 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700120 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700121 .build();
122 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700123 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700124 return ipAddr;
125 }
126 }
127 }
samanwita palf28207b2015-09-04 10:41:56 -0700128 } else if (requestedIP.toInt() != 0) {
129 // Client has requested an IP.
130 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700131 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700132 .ipAddress(requestedIP)
133 .timestamp(new Date())
134 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700135 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700136 .build();
137 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700138 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700139 return requestedIP;
140 }
141 }
142 }
143
144 // Allocate a new IP from the server's pool of available IP.
145 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700146 if (nextIPAddr != null) {
147 assignmentInfo = IpAssignment.builder()
148 .ipAddress(nextIPAddr)
149 .timestamp(new Date())
150 .leasePeriod(timeoutForPendingAssignments)
151 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
152 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700153
samanwita pal2a313402015-09-14 16:03:22 -0700154 allocationMap.put(hostId, assignmentInfo);
155 }
samanwita palf28207b2015-09-04 10:41:56 -0700156 return nextIPAddr;
157
158 }
159
160 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700161 public boolean assignIP(HostId hostId, IpAssignment ipAssignment) {
162 log.trace("Assign IP Called HostId: {}, ipAssignment: {}",
163 hostId, ipAssignment);
daniel877bb2f2015-11-12 21:33:05 +0900164
Madan Jampani56dcdce2016-02-03 13:58:52 -0800165 IpAssignment newAssignment = null;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700166 Versioned<IpAssignment> versionedAssignment = allocationMap.get(hostId);
167 Ip4Address requestedIp = ipAssignment.ipAddress();
Hyunsun Moon08242082015-12-08 01:29:22 -0800168
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700169 if (versionedAssignment == null) {
170 // this is new IP assignment of static mapping
171 // dynamic assignment is done in suggestIP
172 if (ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
173 newAssignment = ipAssignment;
174 } else if (freeIPPool.remove(requestedIp)) {
175 newAssignment = IpAssignment.builder(ipAssignment)
176 .assignmentStatus(Option_Assigned)
Madan Jampani56dcdce2016-02-03 13:58:52 -0800177 .timestamp(new Date())
Madan Jampani56dcdce2016-02-03 13:58:52 -0800178 .build();
179 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700180 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800181 return false;
182 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700183 log.trace("Assigned {}", newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800184 return allocationMap.putIfAbsent(hostId, newAssignment) == null;
185 // TODO: handle the case where map changed.
186 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700187 // this is lease renew or rebinding
188 // update assignment status and time stamp, and keep the others
189 IpAssignment existingAssignment = versionedAssignment.value();
190 if (!existingAssignment.ipAddress().equals(requestedIp)) {
191 // return false if existing assignment is not for the
192 // requested host
193 log.trace("Failed to assign IP for {}", ipAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800194 return false;
195 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700196
197 switch (existingAssignment.assignmentStatus()) {
198 case Option_RangeNotEnforced:
199 newAssignment = IpAssignment.builder(existingAssignment)
200 .timestamp(new Date())
201 .build();
202 break;
203 case Option_Expired:
204 if (!freeIPPool.remove(requestedIp)) {
205 // requested IP is expired for this host and reserved to the other host
206 return false;
207 }
208 case Option_Assigned:
209 case Option_Requested:
210 newAssignment = IpAssignment.builder(existingAssignment)
211 .timestamp(new Date())
212 .assignmentStatus(Option_Assigned)
213 .build();
214 break;
215 default:
216 break;
217 }
218 log.trace("Assigned {}", newAssignment);
219 return allocationMap.replace(hostId, versionedAssignment.version(), newAssignment);
Madan Jampani56dcdce2016-02-03 13:58:52 -0800220 }
samanwita palf28207b2015-09-04 10:41:56 -0700221 }
222
223 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700224 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700225 if (allocationMap.containsKey(hostId)) {
sangho5808d522016-08-12 16:03:34 +0900226 // If the IP has been assigned with Option_RangeNotEnforced,
227 // we do not release the IP address nor remove the host from HostService.
228 // Therefore, if the IP is assigned statically, the IP needs to be released statically.
229 Versioned<IpAssignment> assignmentVersioned = allocationMap.get(hostId);
230 if (Versioned.valueOrNull(assignmentVersioned) != null &&
231 assignmentVersioned.value().assignmentStatus().equals(Option_RangeNotEnforced)) {
232 return null;
233 }
234
samanwita pal2a313402015-09-14 16:03:22 -0700235 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700236 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
237 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700238 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700239 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700240 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700241 freeIPPool.add(freeIP);
242 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700243 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700244 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700245 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700246 }
247
248 @Override
249 public void setDefaultTimeoutForPurge(int timeInSeconds) {
250 timeoutForPendingAssignments = timeInSeconds;
251 }
252
253 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700254 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700255
samanwita pal0bff4302015-09-15 13:37:00 -0700256 Map<HostId, IpAssignment> validMapping = new HashMap<>();
257 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700258 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700259 assignment = entry.getValue().value();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700260 if (assignment.assignmentStatus() == Option_Assigned
261 || assignment.assignmentStatus() == Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700262 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700263 }
264 }
samanwita pal0bff4302015-09-15 13:37:00 -0700265 return validMapping;
266 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700267
samanwita pal0bff4302015-09-15 13:37:00 -0700268 @Override
269 public Map<HostId, IpAssignment> listAllMapping() {
270 Map<HostId, IpAssignment> validMapping = new HashMap<>();
271 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
272 validMapping.put(entry.getKey(), entry.getValue().value());
273 }
274 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700275 }
276
277 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700278 public boolean assignStaticIP(MacAddress macAddress, IpAssignment ipAssignment) {
279 HostId host = HostId.hostId(macAddress);
280 return assignIP(host, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700281 }
282
283 @Override
284 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700285 HostId host = HostId.hostId(macID);
286 if (allocationMap.containsKey(host)) {
287 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900288
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700289 if (assignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
daniel877bb2f2015-11-12 21:33:05 +0900290 allocationMap.remove(host);
291 return true;
292 }
293
samanwita palf28207b2015-09-04 10:41:56 -0700294 Ip4Address freeIP = assignment.ipAddress();
295 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700296 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700297 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700298 freeIPPool.add(freeIP);
299 }
samanwita palf28207b2015-09-04 10:41:56 -0700300 return true;
301 }
302 }
303 return false;
304 }
305
306 @Override
307 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700308 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700309 }
310
311 @Override
312 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
313 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700314 allocationMap.clear();
315 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700316 startIPRange = startIP;
317 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700318
319 int lastIP = endIP.toInt();
320 Ip4Address nextIP;
321 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
322 nextIP = Ip4Address.valueOf(loopCounter);
323 freeIPPool.add(nextIP);
324 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700325 log.debug("Updated free IP pool {}:{} size:{}", startIP, endIP, freeIPPool.size());
samanwita palf28207b2015-09-04 10:41:56 -0700326 }
327
danielcd9deed2015-10-30 17:16:16 +0900328 @Override
329 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700330 if (allocationMap.get(hostId) != null) {
331 return allocationMap.get(hostId).value();
332 } else {
333 return null;
334 }
danielcd9deed2015-10-30 17:16:16 +0900335 }
336
samanwita palf28207b2015-09-04 10:41:56 -0700337 /**
338 * Fetches the next available IP from the free pool pf IPs.
339 *
340 * @return the next available IP address
341 */
342 private Ip4Address fetchNextIP() {
343 for (Ip4Address freeIP : freeIPPool) {
344 if (freeIPPool.remove(freeIP)) {
345 return freeIP;
346 }
347 }
348 return null;
349 }
samanwita pal0bff4302015-09-15 13:37:00 -0700350
351 /**
352 * Returns true if the given ip is within the range of available IPs.
353 *
354 * @param ip given ip address
355 * @return true if within range, false otherwise
356 */
357 private boolean ipWithinRange(Ip4Address ip) {
358 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
359 return true;
360 }
361 return false;
362 }
samanwita palf28207b2015-09-04 10:41:56 -0700363}
danielcd9deed2015-10-30 17:16:16 +0900364