2 * Copyright (c) 2013, 2015 IBM Corporation and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.neutron.spi;
11 import com.google.common.net.InetAddresses;
12 import java.math.BigInteger;
13 import java.net.Inet6Address;
14 import java.util.ArrayList;
15 import java.util.List;
16 import javax.xml.bind.annotation.XmlAccessType;
17 import javax.xml.bind.annotation.XmlAccessorType;
18 import javax.xml.bind.annotation.XmlElement;
19 import javax.xml.bind.annotation.XmlRootElement;
20 import org.apache.commons.net.util.SubnetUtils;
21 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 @XmlAccessorType(XmlAccessType.NONE)
27 public final class NeutronSubnet extends NeutronBaseAttributes<NeutronSubnet> {
28 private static final Logger LOG = LoggerFactory.getLogger(NeutronSubnet.class);
30 private static final long serialVersionUID = 1L;
31 private static final int IPV4_VERSION = 4;
32 private static final int IPV6_VERSION = 6;
33 private static final int IPV6_LENGTH = 128;
34 private static final int IPV6_LENGTH_BYTES = 8;
35 private static final long IPV6_LSB_MASK = 0x000000FF;
36 private static final int IPV6_BYTE_OFFSET = 7;
38 // See OpenStack Network API v2.0 Reference for description of
39 // annotated attributes
41 @XmlElement(name = "network_id")
44 @XmlElement(defaultValue = "4", name = "ip_version")
47 @XmlElement(name = "cidr")
50 @XmlElement(name = "gateway_ip")
53 @XmlElement(name = "dns_nameservers")
54 List<String> dnsNameservers;
56 @XmlElement(name = "allocation_pools")
57 List<NeutronSubnetIpAllocationPool> allocationPools;
59 @XmlElement(name = "host_routes")
60 List<NeutronRoute> hostRoutes;
62 @XmlElement(defaultValue = "true", name = "enable_dhcp")
65 @XmlElement(name = "ipv6_address_mode", nillable = true)
66 String ipV6AddressMode;
68 @XmlElement(name = "ipv6_ra_mode", nillable = true)
71 public String getNetworkUUID() {
75 public void setNetworkUUID(String networkUUID) {
76 this.networkUUID = networkUUID;
79 public Integer getIpVersion() {
83 public void setIpVersion(Integer ipVersion) {
84 this.ipVersion = ipVersion;
87 public String getCidr() {
91 public void setCidr(String cidr) {
95 public String getGatewayIp() {
99 public void setGatewayIp(String gatewayIp) {
100 this.gatewayIp = gatewayIp;
103 public List<String> getDnsNameservers() {
104 return dnsNameservers;
107 public void setDnsNameservers(List<String> dnsNameservers) {
108 this.dnsNameservers = dnsNameservers;
111 public List<NeutronSubnetIpAllocationPool> getAllocationPools() {
112 return allocationPools;
115 public void setAllocationPools(List<NeutronSubnetIpAllocationPool> allocationPools) {
116 this.allocationPools = allocationPools;
119 public List<NeutronRoute> getHostRoutes() {
123 public void setHostRoutes(List<NeutronRoute> hostRoutes) {
124 this.hostRoutes = hostRoutes;
127 public boolean isEnableDHCP() {
128 if (enableDHCP == null) {
134 public Boolean getEnableDHCP() {
138 public void setEnableDHCP(Boolean newValue) {
139 enableDHCP = newValue;
142 public String getIpV6AddressMode() {
143 return ipV6AddressMode;
146 public void setIpV6AddressMode(String ipV6AddressMode) {
147 this.ipV6AddressMode = ipV6AddressMode;
150 public String getIpV6RaMode() {
154 public void setIpV6RaMode(String ipV6RaMode) {
155 this.ipV6RaMode = ipV6RaMode;
159 protected boolean extractField(String field, NeutronSubnet ans) {
162 ans.setNetworkUUID(this.getNetworkUUID());
165 ans.setIpVersion(this.getIpVersion());
168 ans.setCidr(this.getCidr());
171 ans.setGatewayIp(this.getGatewayIp());
173 case "dns_nameservers":
174 List<String> nsList = new ArrayList<>();
175 nsList.addAll(this.getDnsNameservers());
176 ans.setDnsNameservers(nsList);
178 case "allocation_pools":
179 List<NeutronSubnetIpAllocationPool> pools = new ArrayList<>();
180 pools.addAll(this.getAllocationPools());
181 ans.setAllocationPools(pools);
184 List<NeutronRoute> routes = new ArrayList<>();
185 routes.addAll(this.getHostRoutes());
186 ans.setHostRoutes(routes);
189 ans.setEnableDHCP(this.getEnableDHCP());
191 case "ipv6_address_mode":
192 ans.setIpV6AddressMode(this.getIpV6AddressMode());
195 ans.setIpV6RaMode(this.getIpV6RaMode());
198 return super.extractField(field, ans);
203 /* test to see if the cidr address used to define this subnet
204 * is a valid network address (an necessary condition when creating
207 public boolean isValidCIDR() {
208 // fix for Bug 2290 - need to wrap the existing test as
209 // IPv4 because SubnetUtils doesn't support IPv6
210 if (ipVersion == IPV4_VERSION) {
212 SubnetUtils util = new SubnetUtils(cidr);
213 SubnetInfo info = util.getInfo();
214 if (!info.getNetworkAddress().equals(info.getAddress())) {
217 } catch (IllegalArgumentException e) {
218 LOG.warn("Failure in isValidCIDR()", e);
223 if (ipVersion == IPV6_VERSION) {
224 // fix for Bug2290 - this is custom code because no classes
225 // with ODL-friendly licenses have been found
226 // extract address (in front of /) and length (after /)
227 String[] parts = cidr.split("/");
228 if (parts.length != 2) {
231 int length = Integer.parseInt(parts[1]);
232 //TODO?: limit check on length
233 // convert to byte array
234 byte[] addrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
235 for (int index = length; index < IPV6_LENGTH; index++) {
236 if ((addrBytes[index / IPV6_LENGTH_BYTES] & IPV6_LSB_MASK
237 & 1 << IPV6_BYTE_OFFSET - index % IPV6_LENGTH_BYTES) != 0) {
246 /* test to see if the gateway IP specified overlaps with specified
247 * allocation pools (an error condition when creating a new subnet
248 * or assigning a gateway IP)
250 public boolean gatewayIp_Pool_overlap() {
251 for (NeutronSubnetIpAllocationPool pool : allocationPools) {
252 if (ipVersion == IPV4_VERSION && pool.contains(gatewayIp)) {
255 if (ipVersion == IPV6_VERSION && pool.containsV6(gatewayIp)) {
263 public void initDefaults() {
264 if (enableDHCP == null) {
267 if (ipVersion == null) {
268 ipVersion = IPV4_VERSION;
270 if (dnsNameservers == null) {
271 dnsNameservers = new ArrayList<>();
273 if (hostRoutes == null) {
274 hostRoutes = new ArrayList<>();
276 if (allocationPools == null) {
277 allocationPools = new ArrayList<>();
278 if (ipVersion == IPV4_VERSION) {
280 SubnetUtils util = new SubnetUtils(cidr);
281 SubnetInfo info = util.getInfo();
282 if (gatewayIp == null || "".equals(gatewayIp)) {
283 gatewayIp = info.getLowAddress();
285 if (allocationPools.size() < 1) {
286 NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(info.getLowAddress(),
287 info.getHighAddress());
288 allocationPools = source.splitPool(gatewayIp);
290 } catch (IllegalArgumentException e) {
291 LOG.warn("Failure in initDefault()", e);
295 if (ipVersion == IPV6_VERSION) {
296 String[] parts = cidr.split("/");
297 if (parts.length != 2) {
301 int length = Integer.parseInt(parts[1]);
302 BigInteger lowAddressBi = NeutronSubnetIpAllocationPool.convertV6(parts[0]);
303 String lowAddress = NeutronSubnetIpAllocationPool.bigIntegerToIp(lowAddressBi.add(BigInteger.ONE));
304 BigInteger mask = BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE);
305 String highAddress = NeutronSubnetIpAllocationPool
306 .bigIntegerToIp(lowAddressBi.add(mask).subtract(BigInteger.ONE));
307 if (gatewayIp == null || "".equals(gatewayIp)) {
308 gatewayIp = lowAddress;
310 if (allocationPools.size() < 1) {
311 NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(lowAddress,
313 allocationPools = source.splitPoolV6(gatewayIp);
319 /* this method tests to see if the supplied IPv4 address
320 * is valid for this subnet or not
322 public boolean isValidIp(String ipAddress) {
323 if (ipVersion == IPV4_VERSION) {
325 SubnetUtils util = new SubnetUtils(cidr);
326 SubnetInfo info = util.getInfo();
327 return info.isInRange(ipAddress);
328 } catch (IllegalArgumentException e) {
329 LOG.warn("Failure in isValidIp()", e);
334 if (ipVersion == IPV6_VERSION) {
335 String[] parts = cidr.split("/");
336 int length = Integer.parseInt(parts[1]);
337 byte[] cidrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
338 byte[] ipBytes = ((Inet6Address) InetAddresses.forString(ipAddress)).getAddress();
339 for (int index = 0; index < length; index++) {
340 if ((cidrBytes[index / IPV6_LENGTH_BYTES] & IPV6_LSB_MASK & 1 << IPV6_BYTE_OFFSET
341 - index % IPV6_LENGTH_BYTES) != (
342 ipBytes[index / IPV6_LENGTH_BYTES] & IPV6_LSB_MASK
343 & 1 << IPV6_BYTE_OFFSET - index % IPV6_LENGTH_BYTES)) {
352 /* method to get the lowest available address of the subnet.
353 * go through all the allocation pools and keep the lowest of their
356 public String getLowAddr() {
358 for (NeutronSubnetIpAllocationPool pool : allocationPools) {
360 ans = pool.getPoolStart();
362 if (ipVersion == IPV4_VERSION && NeutronSubnetIpAllocationPool
363 .convert(pool.getPoolStart()) < NeutronSubnetIpAllocationPool.convert(ans)) {
364 ans = pool.getPoolStart();
366 if (ipVersion == IPV6_VERSION && NeutronSubnetIpAllocationPool.convertV6(pool.getPoolStart())
367 .compareTo(NeutronSubnetIpAllocationPool.convertV6(ans)) < 0) {
368 ans = pool.getPoolStart();
376 public String toString() {
377 return "NeutronSubnet [subnetUUID=" + uuid + ", networkUUID=" + networkUUID + ", name=" + name + ", ipVersion="
378 + ipVersion + ", cidr=" + cidr + ", gatewayIp=" + gatewayIp + ", dnsNameservers=" + dnsNameservers
379 + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes + ", enableDHCP=" + enableDHCP
380 + ", tenantID=" + tenantID + ", ipv6AddressMode=" + ipV6AddressMode + ", ipv6RaMode=" + ipV6RaMode