blob: a790c8bd4ae8f2cb4be8c3d24143ec7fdb90d357 [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.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))
Madan Jampani538be742016-02-10 14:55:38 -080092 .build()
93 .asDistributedSet();
samanwita palf28207b2015-09-04 10:41:56 -070094
samanwita palf28207b2015-09-04 10:41:56 -070095 log.info("Started");
96 }
97
98 @Deactivate
99 protected void deactivate() {
samanwita palf28207b2015-09-04 10:41:56 -0700100 log.info("Stopped");
101 }
102
103 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700104 public Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP) {
samanwita palf28207b2015-09-04 10:41:56 -0700105
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700106 IpAssignment assignmentInfo;
samanwita pal2a313402015-09-14 16:03:22 -0700107 if (allocationMap.containsKey(hostId)) {
108 assignmentInfo = allocationMap.get(hostId).value();
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700109 IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
samanwita palf28207b2015-09-04 10:41:56 -0700110 Ip4Address ipAddr = assignmentInfo.ipAddress();
111
daniel877bb2f2015-11-12 21:33:05 +0900112 if (assignmentInfo.rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900113 return assignmentInfo.ipAddress();
114 } else if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700115 status == IpAssignment.AssignmentStatus.Option_Requested) {
samanwita palf28207b2015-09-04 10:41:56 -0700116 // Client has a currently Active Binding.
samanwita pal0bff4302015-09-15 13:37:00 -0700117 if (ipWithinRange(ipAddr)) {
samanwita palf28207b2015-09-04 10:41:56 -0700118 return ipAddr;
119 }
120
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700121 } else if (status == IpAssignment.AssignmentStatus.Option_Expired) {
samanwita palf28207b2015-09-04 10:41:56 -0700122 // Client has a Released or Expired Binding.
123 if (freeIPPool.contains(ipAddr)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700124 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700125 .ipAddress(ipAddr)
126 .timestamp(new Date())
127 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700128 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700129 .build();
130 if (freeIPPool.remove(ipAddr)) {
samanwita pal2a313402015-09-14 16:03:22 -0700131 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700132 return ipAddr;
133 }
134 }
135 }
samanwita palf28207b2015-09-04 10:41:56 -0700136 } else if (requestedIP.toInt() != 0) {
137 // Client has requested an IP.
138 if (freeIPPool.contains(requestedIP)) {
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700139 assignmentInfo = IpAssignment.builder()
samanwita palf28207b2015-09-04 10:41:56 -0700140 .ipAddress(requestedIP)
141 .timestamp(new Date())
142 .leasePeriod(timeoutForPendingAssignments)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700143 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
samanwita palf28207b2015-09-04 10:41:56 -0700144 .build();
145 if (freeIPPool.remove(requestedIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700146 allocationMap.put(hostId, assignmentInfo);
samanwita palf28207b2015-09-04 10:41:56 -0700147 return requestedIP;
148 }
149 }
150 }
151
152 // Allocate a new IP from the server's pool of available IP.
153 Ip4Address nextIPAddr = fetchNextIP();
samanwita pal2a313402015-09-14 16:03:22 -0700154 if (nextIPAddr != null) {
155 assignmentInfo = IpAssignment.builder()
156 .ipAddress(nextIPAddr)
157 .timestamp(new Date())
158 .leasePeriod(timeoutForPendingAssignments)
159 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested)
160 .build();
samanwita palf28207b2015-09-04 10:41:56 -0700161
samanwita pal2a313402015-09-14 16:03:22 -0700162 allocationMap.put(hostId, assignmentInfo);
163 }
samanwita palf28207b2015-09-04 10:41:56 -0700164 return nextIPAddr;
165
166 }
167
168 @Override
daniel877bb2f2015-11-12 21:33:05 +0900169 public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900170 List<Ip4Address> addressList) {
daniel877bb2f2015-11-12 21:33:05 +0900171 log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString());
172
Madan Jampani56dcdce2016-02-03 13:58:52 -0800173 Versioned<IpAssignment> currentAssignment = allocationMap.get(hostId);
174 IpAssignment newAssignment = null;
175 if (currentAssignment == null) {
176 if (rangeNotEnforced) {
177 newAssignment = IpAssignment.builder()
178 .ipAddress(ipAddr)
179 .timestamp(new Date())
180 .leasePeriod(leaseTime)
181 .rangeNotEnforced(true)
182 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
183 .subnetMask((Ip4Address) addressList.toArray()[0])
184 .dhcpServer((Ip4Address) addressList.toArray()[1])
185 .routerAddress((Ip4Address) addressList.toArray()[2])
186 .domainServer((Ip4Address) addressList.toArray()[3])
187 .build();
Hyunsun Moon08242082015-12-08 01:29:22 -0800188
Madan Jampani56dcdce2016-02-03 13:58:52 -0800189 } else if (freeIPPool.remove(ipAddr)) {
190 newAssignment = IpAssignment.builder()
191 .ipAddress(ipAddr)
192 .timestamp(new Date())
193 .leasePeriod(leaseTime)
194 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
195 .build();
196 } else {
197 return false;
198 }
199 return allocationMap.putIfAbsent(hostId, newAssignment) == null;
200 // TODO: handle the case where map changed.
201 } else {
202 IpAssignment existingAssignment = currentAssignment.value();
203 if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
204 (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
205 switch (existingAssignment.assignmentStatus()) {
206 case Option_RangeNotEnforced:
207 newAssignment = IpAssignment.builder()
208 .ipAddress(ipAddr)
209 .timestamp(new Date())
210 .leasePeriod(existingAssignment.leasePeriod())
211 .rangeNotEnforced(true)
212 .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
213 .subnetMask(existingAssignment.subnetMask())
214 .dhcpServer(existingAssignment.dhcpServer())
215 .routerAddress(existingAssignment.routerAddress())
216 .domainServer(existingAssignment.domainServer())
217 .build();
218 break;
219 case Option_Assigned:
220 case Option_Requested:
221 newAssignment = IpAssignment.builder()
222 .ipAddress(ipAddr)
223 .timestamp(new Date())
224 .leasePeriod(leaseTime)
225 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
226 .build();
227 break;
228 case Option_Expired:
229 if (freeIPPool.remove(ipAddr)) {
230 newAssignment = IpAssignment.builder()
231 .ipAddress(ipAddr)
232 .timestamp(new Date())
233 .leasePeriod(leaseTime)
234 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
235 .build();
236 }
237 break;
238 default:
239 break;
240 }
241 return allocationMap.replace(hostId, currentAssignment.version(), newAssignment);
242 } else {
243 return false;
244 }
245 }
samanwita palf28207b2015-09-04 10:41:56 -0700246 }
247
248 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700249 public Ip4Address releaseIP(HostId hostId) {
samanwita pal2a313402015-09-14 16:03:22 -0700250 if (allocationMap.containsKey(hostId)) {
251 IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700252 .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
samanwita palf28207b2015-09-04 10:41:56 -0700253 .build();
254 Ip4Address freeIP = newAssignment.ipAddress();
samanwita pal2a313402015-09-14 16:03:22 -0700255 allocationMap.put(hostId, newAssignment);
samanwita pal0bff4302015-09-15 13:37:00 -0700256 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700257 freeIPPool.add(freeIP);
258 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700259 return freeIP;
samanwita palf28207b2015-09-04 10:41:56 -0700260 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700261 return null;
samanwita palf28207b2015-09-04 10:41:56 -0700262 }
263
264 @Override
265 public void setDefaultTimeoutForPurge(int timeInSeconds) {
266 timeoutForPendingAssignments = timeInSeconds;
267 }
268
269 @Override
samanwita pal0bff4302015-09-15 13:37:00 -0700270 public Map<HostId, IpAssignment> listAssignedMapping() {
samanwita palf28207b2015-09-04 10:41:56 -0700271
samanwita pal0bff4302015-09-15 13:37:00 -0700272 Map<HostId, IpAssignment> validMapping = new HashMap<>();
273 IpAssignment assignment;
samanwita pal2a313402015-09-14 16:03:22 -0700274 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
samanwita pal0bff4302015-09-15 13:37:00 -0700275 assignment = entry.getValue().value();
danielcd9deed2015-10-30 17:16:16 +0900276 if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned
daniel877bb2f2015-11-12 21:33:05 +0900277 || assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_RangeNotEnforced) {
samanwita pal0bff4302015-09-15 13:37:00 -0700278 validMapping.put(entry.getKey(), assignment);
samanwita palf28207b2015-09-04 10:41:56 -0700279 }
280 }
samanwita pal0bff4302015-09-15 13:37:00 -0700281 return validMapping;
282 }
Thomas Vachuska54dc3522015-09-09 00:11:45 -0700283
samanwita pal0bff4302015-09-15 13:37:00 -0700284 @Override
285 public Map<HostId, IpAssignment> listAllMapping() {
286 Map<HostId, IpAssignment> validMapping = new HashMap<>();
287 for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
288 validMapping.put(entry.getKey(), entry.getValue().value());
289 }
290 return validMapping;
samanwita palf28207b2015-09-04 10:41:56 -0700291 }
292
293 @Override
daniel877bb2f2015-11-12 21:33:05 +0900294 public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900295 List<Ip4Address> addressList) {
samanwita pal2a313402015-09-14 16:03:22 -0700296 HostId host = HostId.hostId(macID);
daniel877bb2f2015-11-12 21:33:05 +0900297 return assignIP(host, ipAddr, -1, rangeNotEnforced, addressList);
samanwita palf28207b2015-09-04 10:41:56 -0700298 }
299
300 @Override
301 public boolean removeStaticIP(MacAddress macID) {
samanwita pal2a313402015-09-14 16:03:22 -0700302 HostId host = HostId.hostId(macID);
303 if (allocationMap.containsKey(host)) {
304 IpAssignment assignment = allocationMap.get(host).value();
daniel877bb2f2015-11-12 21:33:05 +0900305
306 if (assignment.rangeNotEnforced()) {
307 allocationMap.remove(host);
308 return true;
309 }
310
samanwita palf28207b2015-09-04 10:41:56 -0700311 Ip4Address freeIP = assignment.ipAddress();
312 if (assignment.leasePeriod() < 0) {
samanwita pal2a313402015-09-14 16:03:22 -0700313 allocationMap.remove(host);
samanwita pal0bff4302015-09-15 13:37:00 -0700314 if (ipWithinRange(freeIP)) {
samanwita pal2a313402015-09-14 16:03:22 -0700315 freeIPPool.add(freeIP);
316 }
samanwita palf28207b2015-09-04 10:41:56 -0700317 return true;
318 }
319 }
320 return false;
321 }
322
323 @Override
324 public Iterable<Ip4Address> getAvailableIPs() {
Sho SHIMIZUfd0933b2015-09-11 15:17:48 -0700325 return ImmutableSet.copyOf(freeIPPool);
samanwita palf28207b2015-09-04 10:41:56 -0700326 }
327
328 @Override
329 public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
330 // Clear all entries from previous range.
samanwita pal0bff4302015-09-15 13:37:00 -0700331 allocationMap.clear();
332 freeIPPool.clear();
samanwita palf28207b2015-09-04 10:41:56 -0700333 startIPRange = startIP;
334 endIPRange = endIP;
samanwita palf28207b2015-09-04 10:41:56 -0700335
336 int lastIP = endIP.toInt();
337 Ip4Address nextIP;
338 for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
339 nextIP = Ip4Address.valueOf(loopCounter);
340 freeIPPool.add(nextIP);
341 }
342 }
343
danielcd9deed2015-10-30 17:16:16 +0900344 @Override
345 public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
346 return allocationMap.get(hostId).value();
347 }
348
samanwita palf28207b2015-09-04 10:41:56 -0700349 /**
350 * Fetches the next available IP from the free pool pf IPs.
351 *
352 * @return the next available IP address
353 */
354 private Ip4Address fetchNextIP() {
355 for (Ip4Address freeIP : freeIPPool) {
356 if (freeIPPool.remove(freeIP)) {
357 return freeIP;
358 }
359 }
360 return null;
361 }
samanwita pal0bff4302015-09-15 13:37:00 -0700362
363 /**
364 * Returns true if the given ip is within the range of available IPs.
365 *
366 * @param ip given ip address
367 * @return true if within range, false otherwise
368 */
369 private boolean ipWithinRange(Ip4Address ip) {
370 if ((ip.toInt() >= startIPRange.toInt()) && (ip.toInt() <= endIPRange.toInt())) {
371 return true;
372 }
373 return false;
374 }
samanwita palf28207b2015-09-04 10:41:56 -0700375}
danielcd9deed2015-10-30 17:16:16 +0900376