blob: d922b8fd014234c55e6e61ef4e7e66b5b22c5d6f [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;
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.List;
43import java.util.HashMap;
samanwita pal0bff4302015-09-15 13:37:00 -070044import java.util.Objects;
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
51@Component(immediate = true)
52@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070053public class DistributedDhcpStore implements DhcpStore {
samanwita palf28207b2015-09-04 10:41:56 -070054
55 private final Logger log = LoggerFactory.getLogger(getClass());
56
57 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
58 protected StorageService storageService;
59
samanwita pal2a313402015-09-14 16:03:22 -070060 private ConsistentMap<HostId, IpAssignment> allocationMap;
samanwita palf28207b2015-09-04 10:41:56 -070061
62 private DistributedSet<Ip4Address> freeIPPool;
63
samanwita palf28207b2015-09-04 10:41:56 -070064 private static Ip4Address startIPRange;
65
66 private static Ip4Address endIPRange;
67
68 // Hardcoded values are default values.
69
samanwita palf28207b2015-09-04 10:41:56 -070070 private static int timeoutForPendingAssignments = 60;
Hyunsun Moon08242082015-12-08 01:29:22 -080071 private static final int MAX_RETRIES = 3;
72 private static final int MAX_BACKOFF = 10;
samanwita palf28207b2015-09-04 10:41:56 -070073
74 @Activate
75 protected void activate() {
samanwita pal2a313402015-09-14 16:03:22 -070076 allocationMap = storageService.<HostId, IpAssignment>consistentMapBuilder()
samanwita palf28207b2015-09-04 10:41:56 -070077 .withName("onos-dhcp-assignedIP")
78 .withSerializer(Serializer.using(
79 new KryoNamespace.Builder()
80 .register(KryoNamespaces.API)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070081 .register(IpAssignment.class,
82 IpAssignment.AssignmentStatus.class,
samanwita palf28207b2015-09-04 10:41:56 -070083 Date.class,
84 long.class,
85 Ip4Address.class)
86 .build()))
87 .build();
88
89 freeIPPool = storageService.<Ip4Address>setBuilder()
90 .withName("onos-dhcp-freeIP")
91 .withSerializer(Serializer.using(KryoNamespaces.API))
92 .build();
93
samanwita palf28207b2015-09-04 10:41:56 -070094 log.info("Started");
95 }
96
97 @Deactivate
98 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -070099 log.info("Stopped");
100 }
101
102 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700103 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -0700104
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700105 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -0700106 if (allocationMap.containsKey(hostId)) {
107 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700108 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700109 Ip4Address ipAddr = assignmentInfo.ipAddress();
110
daniel877bb2f2015-11-12 21:33:05 +0900111 if (assignmentInfo.rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900112 return assignmentInfo.ipAddress();
113 } else if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700114 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700115 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700116 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700117 return ipAddr;
118 }
119
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700120 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700121 // Client has a Released or Expired Binding.
122 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700123 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700124 .ipAddress(ipAddr)
125 .timestamp(new Date())
126 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700127 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700128 .build();
129 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700130 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700131 return ipAddr;
132 }
133 }
134 }
samanwita palf28207b2015-09-04 10:41:56 -0700135 } else if (requestedIP.toInt() != 0) {
136 // Client has requested an IP.
137 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700138 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700139 .ipAddress(requestedIP)
140 .timestamp(new Date())
141 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700142 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700143 .build();
144 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700145 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700146 return requestedIP;
147 }
148 }
149 }
150
151 // Allocate a new IP from the server's pool of available IP.
152 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700153 if (nextIPAddr != null) {
154 assignmentInfo = IpAssignment.builder()
155 .ipAddress(nextIPAddr)
156 .timestamp(new Date())
157 .leasePeriod(timeoutForPendingAssignments)
158 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
159 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700160
samanwita pal2a313402015-09-14 16:03:22 -0700161 allocationMap.put(hostId, assignmentInfo);
162 }
samanwita palf28207b2015-09-04 10:41:56 -0700163 return nextIPAddr;
164
165 }
166
167 @Override
daniel877bb2f2015-11-12 21:33:05 +0900168 public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900169 List<Ip4Address> addressList) {
daniel877bb2f2015-11-12 21:33:05 +0900170 log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString());
171
Madan Jampani56dcdce2016-02-03 13:58:52 -0800172 Versioned<IpAssignment> currentAssignment = allocationMap.get(hostId);
173 IpAssignment newAssignment = null;
174 if (currentAssignment == null) {
175 if (rangeNotEnforced) {
176 newAssignment = IpAssignment.builder()
177 .ipAddress(ipAddr)
178 .timestamp(new Date())
179 .leasePeriod(leaseTime)
180 .rangeNotEnforced(true)
181 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
182 .subnetMask((Ip4Address) addressList.toArray()[0])
183 .dhcpServer((Ip4Address) addressList.toArray()[1])
184 .routerAddress((Ip4Address) addressList.toArray()[2])
185 .domainServer((Ip4Address) addressList.toArray()[3])
186 .build();
Hyunsun Moon08242082015-12-08 01:29:22 -0800187
Madan Jampani56dcdce2016-02-03 13:58:52 -0800188 } else if (freeIPPool.remove(ipAddr)) {
189 newAssignment = IpAssignment.builder()
190 .ipAddress(ipAddr)
191 .timestamp(new Date())
192 .leasePeriod(leaseTime)
193 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
194 .build();
195 } else {
196 return false;
197 }
198 return allocationMap.putIfAbsent(hostId, newAssignment) == null;
199 // TODO: handle the case where map changed.
200 } else {
201 IpAssignment existingAssignment = currentAssignment.value();
202 if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
203 (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
204 switch (existingAssignment.assignmentStatus()) {
205 case Option_RangeNotEnforced:
206 newAssignment = IpAssignment.builder()
207 .ipAddress(ipAddr)
208 .timestamp(new Date())
209 .leasePeriod(existingAssignment.leasePeriod())
210 .rangeNotEnforced(true)
211 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
212 .subnetMask(existingAssignment.subnetMask())
213 .dhcpServer(existingAssignment.dhcpServer())
214 .routerAddress(existingAssignment.routerAddress())
215 .domainServer(existingAssignment.domainServer())
216 .build();
217 break;
218 case Option_Assigned:
219 case Option_Requested:
220 newAssignment = IpAssignment.builder()
221 .ipAddress(ipAddr)
222 .timestamp(new Date())
223 .leasePeriod(leaseTime)
224 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
225 .build();
226 break;
227 case Option_Expired:
228 if (freeIPPool.remove(ipAddr)) {
229 newAssignment = IpAssignment.builder()
230 .ipAddress(ipAddr)
231 .timestamp(new Date())
232 .leasePeriod(leaseTime)
233 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
234 .build();
235 }
236 break;
237 default:
238 break;
239 }
240 return allocationMap.replace(hostId, currentAssignment.version(), newAssignment);
241 } else {
242 return false;
243 }
244 }
samanwita palf28207b2015-09-04 10:41:56 -0700245 }
246
247 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700248 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700249 if (allocationMap.containsKey(hostId)) {
250 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700251 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
samanwita palf28207b2015-09-04 10:41:56 -0700252 .build();
253 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700254 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700255 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700256 freeIPPool.add(freeIP);
257 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700258 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700259 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700260 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700261 }
262
263 @Override
264 public void setDefaultTimeoutForPurge(int timeInSeconds) {
265 timeoutForPendingAssignments = timeInSeconds;
266 }
267
268 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700269 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700270
samanwita pal0bff4302015-09-15 13:37:00 -0700271 Map<HostId, IpAssignment> validMapping = new HashMap<>();
272 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700273 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700274 assignment = entry.getValue().value();
danielcd9deed2015-10-30 17:16:16 +0900275 if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned
daniel877bb2f2015-11-12 21:33:05 +0900276 || assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700277 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700278 }
279 }
samanwita pal0bff4302015-09-15 13:37:00 -0700280 return validMapping;
281 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700282
samanwita pal0bff4302015-09-15 13:37:00 -0700283 @Override
284 public Map<HostId, IpAssignment> listAllMapping() {
285 Map<HostId, IpAssignment> validMapping = new HashMap<>();
286 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
287 validMapping.put(entry.getKey(), entry.getValue().value());
288 }
289 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700290 }
291
292 @Override
daniel877bb2f2015-11-12 21:33:05 +0900293 public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900294 List<Ip4Address> addressList) {
samanwita pal2a313402015-09-14 16:03:22 -0700295 HostId host = HostId.hostId(macID);
daniel877bb2f2015-11-12 21:33:05 +0900296 return assignIP(host, ipAddr, -1, rangeNotEnforced, addressList);
samanwita palf28207b2015-09-04 10:41:56 -0700297 }
298
299 @Override
300 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700301 HostId host = HostId.hostId(macID);
302 if (allocationMap.containsKey(host)) {
303 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900304
305 if (assignment.rangeNotEnforced()) {
306 allocationMap.remove(host);
307 return true;
308 }
309
samanwita palf28207b2015-09-04 10:41:56 -0700310 Ip4Address freeIP = assignment.ipAddress();
311 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700312 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700313 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700314 freeIPPool.add(freeIP);
315 }
samanwita palf28207b2015-09-04 10:41:56 -0700316 return true;
317 }
318 }
319 return false;
320 }
321
322 @Override
323 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700324 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700325 }
326
327 @Override
328 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
329 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700330 allocationMap.clear();
331 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700332 startIPRange = startIP;
333 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700334
335 int lastIP = endIP.toInt();
336 Ip4Address nextIP;
337 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
338 nextIP = Ip4Address.valueOf(loopCounter);
339 freeIPPool.add(nextIP);
340 }
341 }
342
danielcd9deed2015-10-30 17:16:16 +0900343 @Override
344 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
345 return allocationMap.get(hostId).value();
346 }
347
samanwita palf28207b2015-09-04 10:41:56 -0700348 /**
349 * Fetches the next available IP from the free pool pf IPs.
350 *
351 * @return the next available IP address
352 */
353 private Ip4Address fetchNextIP() {
354 for (Ip4Address freeIP : freeIPPool) {
355 if (freeIPPool.remove(freeIP)) {
356 return freeIP;
357 }
358 }
359 return null;
360 }
samanwita pal0bff4302015-09-15 13:37:00 -0700361
362 /**
363 * Returns true if the given ip is within the range of available IPs.
364 *
365 * @param ip given ip address
366 * @return true if within range, false otherwise
367 */
368 private boolean ipWithinRange(Ip4Address ip) {
369 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
370 return true;
371 }
372 return false;
373 }
samanwita palf28207b2015-09-04 10:41:56 -0700374}
danielcd9deed2015-10-30 17:16:16 +0900375