blob: 866fe619b70a63e150abb4e079b38b2dd70e81de [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
Thomas Vachuska54dc3522015-09-09 00:11:45 -07002 * Copyright 2015 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;
Hyunsun Moon08242082015-12-08 01:29:22 -080028import org.onlab.util.Tools;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070029import org.onosproject.dhcp.DhcpStore;
30import org.onosproject.dhcp.IpAssignment;
samanwita pal2a313402015-09-14 16:03:22 -070031import org.onosproject.net.HostId;
samanwita palf28207b2015-09-04 10:41:56 -070032import org.onosproject.store.serializers.KryoNamespaces;
33import org.onosproject.store.service.ConsistentMap;
Hyunsun Moon08242082015-12-08 01:29:22 -080034import org.onosproject.store.service.ConsistentMapException;
samanwita palf28207b2015-09-04 10:41:56 -070035import 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 palf28207b2015-09-04 10:41:56 -070043import java.util.Map;
danielcd9deed2015-10-30 17:16:16 +090044import java.util.List;
45import java.util.HashMap;
samanwita pal0bff4302015-09-15 13:37:00 -070046import java.util.Objects;
Hyunsun Moon08242082015-12-08 01:29:22 -080047import java.util.concurrent.atomic.AtomicBoolean;
samanwita palf28207b2015-09-04 10:41:56 -070048
49/**
50 * Manages the pool of available IP Addresses in the network and
51 * Remembers the mapping between MAC ID and IP Addresses assigned.
52 */
53
54@Component(immediate = true)
55@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070056public class DistributedDhcpStore implements DhcpStore {
samanwita palf28207b2015-09-04 10:41:56 -070057
58 private final Logger log = LoggerFactory.getLogger(getClass());
59
60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 protected StorageService storageService;
62
samanwita pal2a313402015-09-14 16:03:22 -070063 private ConsistentMap<HostId, IpAssignment> allocationMap;
samanwita palf28207b2015-09-04 10:41:56 -070064
65 private DistributedSet<Ip4Address> freeIPPool;
66
samanwita palf28207b2015-09-04 10:41:56 -070067 private static Ip4Address startIPRange;
68
69 private static Ip4Address endIPRange;
70
71 // Hardcoded values are default values.
72
samanwita palf28207b2015-09-04 10:41:56 -070073 private static int timeoutForPendingAssignments = 60;
Hyunsun Moon08242082015-12-08 01:29:22 -080074 private static final int MAX_RETRIES = 3;
75 private static final int MAX_BACKOFF = 10;
samanwita palf28207b2015-09-04 10:41:56 -070076
77 @Activate
78 protected void activate() {
samanwita pal2a313402015-09-14 16:03:22 -070079 allocationMap = storageService.<HostId, IpAssignment>consistentMapBuilder()
samanwita palf28207b2015-09-04 10:41:56 -070080 .withName("onos-dhcp-assignedIP")
81 .withSerializer(Serializer.using(
82 new KryoNamespace.Builder()
83 .register(KryoNamespaces.API)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070084 .register(IpAssignment.class,
85 IpAssignment.AssignmentStatus.class,
samanwita palf28207b2015-09-04 10:41:56 -070086 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
samanwita palf28207b2015-09-04 10:41:56 -070097 log.info("Started");
98 }
99
100 @Deactivate
101 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -0700102 log.info("Stopped");
103 }
104
105 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700106 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -0700107
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700108 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -0700109 if (allocationMap.containsKey(hostId)) {
110 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700111 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700112 Ip4Address ipAddr = assignmentInfo.ipAddress();
113
daniel877bb2f2015-11-12 21:33:05 +0900114 if (assignmentInfo.rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900115 return assignmentInfo.ipAddress();
116 } else if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700117 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700118 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700119 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700120 return ipAddr;
121 }
122
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700123 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700124 // Client has a Released or Expired Binding.
125 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700126 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700127 .ipAddress(ipAddr)
128 .timestamp(new Date())
129 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700130 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700131 .build();
132 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700133 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700134 return ipAddr;
135 }
136 }
137 }
samanwita palf28207b2015-09-04 10:41:56 -0700138 } else if (requestedIP.toInt() != 0) {
139 // Client has requested an IP.
140 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700141 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700142 .ipAddress(requestedIP)
143 .timestamp(new Date())
144 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700145 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700146 .build();
147 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700148 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700149 return requestedIP;
150 }
151 }
152 }
153
154 // Allocate a new IP from the server's pool of available IP.
155 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700156 if (nextIPAddr != null) {
157 assignmentInfo = IpAssignment.builder()
158 .ipAddress(nextIPAddr)
159 .timestamp(new Date())
160 .leasePeriod(timeoutForPendingAssignments)
161 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
162 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700163
samanwita pal2a313402015-09-14 16:03:22 -0700164 allocationMap.put(hostId, assignmentInfo);
165 }
samanwita palf28207b2015-09-04 10:41:56 -0700166 return nextIPAddr;
167
168 }
169
170 @Override
daniel877bb2f2015-11-12 21:33:05 +0900171 public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900172 List<Ip4Address> addressList) {
daniel877bb2f2015-11-12 21:33:05 +0900173 log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString());
174
Hyunsun Moon08242082015-12-08 01:29:22 -0800175 AtomicBoolean assigned = Tools.retryable(() -> {
176 AtomicBoolean result = new AtomicBoolean(false);
177 allocationMap.compute(
178 hostId,
179 (h, existingAssignment) -> {
180 IpAssignment assignment = existingAssignment;
181 if (existingAssignment == null) {
182 if (rangeNotEnforced) {
183 assignment = IpAssignment.builder()
184 .ipAddress(ipAddr)
185 .timestamp(new Date())
186 .leasePeriod(leaseTime)
187 .rangeNotEnforced(true)
188 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
189 .subnetMask((Ip4Address) addressList.toArray()[0])
190 .dhcpServer((Ip4Address) addressList.toArray()[1])
191 .routerAddress((Ip4Address) addressList.toArray()[2])
192 .domainServer((Ip4Address) addressList.toArray()[3])
193 .build();
194 result.set(true);
195 } else if (freeIPPool.remove(ipAddr)) {
196 assignment = IpAssignment.builder()
197 .ipAddress(ipAddr)
198 .timestamp(new Date())
199 .leasePeriod(leaseTime)
200 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
201 .build();
202 result.set(true);
203 }
204 } else if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
205 (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
206 switch (existingAssignment.assignmentStatus()) {
207 case Option_RangeNotEnforced:
208 assignment = IpAssignment.builder()
209 .ipAddress(ipAddr)
210 .timestamp(new Date())
211 .leasePeriod(leaseTime)
212 .rangeNotEnforced(true)
213 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
214 .subnetMask(existingAssignment.subnetMask())
215 .dhcpServer(existingAssignment.dhcpServer())
216 .routerAddress(existingAssignment.routerAddress())
217 .domainServer(existingAssignment.domainServer())
218 .build();
219 result.set(true);
220 break;
221 case Option_Assigned:
222 case Option_Requested:
223 assignment = IpAssignment.builder()
224 .ipAddress(ipAddr)
225 .timestamp(new Date())
226 .leasePeriod(leaseTime)
227 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
228 .build();
229 result.set(true);
230 break;
231 case Option_Expired:
232 if (freeIPPool.remove(ipAddr)) {
233 assignment = IpAssignment.builder()
234 .ipAddress(ipAddr)
235 .timestamp(new Date())
236 .leasePeriod(leaseTime)
237 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
238 .build();
239 result.set(true);
240 }
241 break;
242 default:
243 break;
244 }
samanwita pal0bff4302015-09-15 13:37:00 -0700245 }
Hyunsun Moon08242082015-12-08 01:29:22 -0800246 return assignment;
247 });
248 return result;
249 }, ConsistentMapException.class, MAX_RETRIES, MAX_BACKOFF).get();
250
251 return assigned.get();
samanwita palf28207b2015-09-04 10:41:56 -0700252 }
253
254 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700255 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700256 if (allocationMap.containsKey(hostId)) {
257 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700258 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
samanwita palf28207b2015-09-04 10:41:56 -0700259 .build();
260 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700261 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700262 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700263 freeIPPool.add(freeIP);
264 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700265 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700266 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700267 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700268 }
269
270 @Override
271 public void setDefaultTimeoutForPurge(int timeInSeconds) {
272 timeoutForPendingAssignments = timeInSeconds;
273 }
274
275 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700276 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700277
samanwita pal0bff4302015-09-15 13:37:00 -0700278 Map<HostId, IpAssignment> validMapping = new HashMap<>();
279 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700280 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700281 assignment = entry.getValue().value();
danielcd9deed2015-10-30 17:16:16 +0900282 if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned
daniel877bb2f2015-11-12 21:33:05 +0900283 || assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700284 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700285 }
286 }
samanwita pal0bff4302015-09-15 13:37:00 -0700287 return validMapping;
288 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700289
samanwita pal0bff4302015-09-15 13:37:00 -0700290 @Override
291 public Map<HostId, IpAssignment> listAllMapping() {
292 Map<HostId, IpAssignment> validMapping = new HashMap<>();
293 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
294 validMapping.put(entry.getKey(), entry.getValue().value());
295 }
296 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700297 }
298
299 @Override
daniel877bb2f2015-11-12 21:33:05 +0900300 public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900301 List<Ip4Address> addressList) {
samanwita pal2a313402015-09-14 16:03:22 -0700302 HostId host = HostId.hostId(macID);
daniel877bb2f2015-11-12 21:33:05 +0900303 return assignIP(host, ipAddr, -1, rangeNotEnforced, addressList);
samanwita palf28207b2015-09-04 10:41:56 -0700304 }
305
306 @Override
307 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700308 HostId host = HostId.hostId(macID);
309 if (allocationMap.containsKey(host)) {
310 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900311
312 if (assignment.rangeNotEnforced()) {
313 allocationMap.remove(host);
314 return true;
315 }
316
samanwita palf28207b2015-09-04 10:41:56 -0700317 Ip4Address freeIP = assignment.ipAddress();
318 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700319 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700320 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700321 freeIPPool.add(freeIP);
322 }
samanwita palf28207b2015-09-04 10:41:56 -0700323 return true;
324 }
325 }
326 return false;
327 }
328
329 @Override
330 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700331 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700332 }
333
334 @Override
335 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
336 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700337 allocationMap.clear();
338 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700339 startIPRange = startIP;
340 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700341
342 int lastIP = endIP.toInt();
343 Ip4Address nextIP;
344 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
345 nextIP = Ip4Address.valueOf(loopCounter);
346 freeIPPool.add(nextIP);
347 }
348 }
349
danielcd9deed2015-10-30 17:16:16 +0900350 @Override
351 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
352 return allocationMap.get(hostId).value();
353 }
354
samanwita palf28207b2015-09-04 10:41:56 -0700355 /**
356 * Fetches the next available IP from the free pool pf IPs.
357 *
358 * @return the next available IP address
359 */
360 private Ip4Address fetchNextIP() {
361 for (Ip4Address freeIP : freeIPPool) {
362 if (freeIPPool.remove(freeIP)) {
363 return freeIP;
364 }
365 }
366 return null;
367 }
samanwita pal0bff4302015-09-15 13:37:00 -0700368
369 /**
370 * Returns true if the given ip is within the range of available IPs.
371 *
372 * @param ip given ip address
373 * @return true if within range, false otherwise
374 */
375 private boolean ipWithinRange(Ip4Address ip) {
376 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
377 return true;
378 }
379 return false;
380 }
samanwita palf28207b2015-09-04 10:41:56 -0700381}
danielcd9deed2015-10-30 17:16:16 +0900382