2ff8957c9d531d6fae4b0105652cd000ad3be9a1
[neutron.git] / neutron-spi / src / main / java / org / opendaylight / neutron / spi / NeutronSubnet.java
1 /*
2  * Copyright (c) 2013, 2015 IBM Corporation and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.neutron.spi;
10
11 import com.google.common.net.InetAddresses;
12 import java.io.Serializable;
13 import java.math.BigInteger;
14 import java.net.Inet6Address;
15 import java.util.ArrayList;
16 import java.util.List;
17 import javax.xml.bind.annotation.XmlAccessType;
18 import javax.xml.bind.annotation.XmlAccessorType;
19 import javax.xml.bind.annotation.XmlElement;
20 import javax.xml.bind.annotation.XmlRootElement;
21 import org.apache.commons.net.util.SubnetUtils;
22 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 @XmlRootElement
27 @XmlAccessorType(XmlAccessType.NONE)
28 public final class NeutronSubnet extends NeutronBaseAttributes<NeutronSubnet> implements Serializable {
29     private static final Logger LOGGER = LoggerFactory.getLogger(NeutronCRUDInterfaces.class);
30
31     private static final long serialVersionUID = 1L;
32     private static final int IPV4_VERSION = 4;
33     private static final int IPV6_VERSION = 6;
34     private static final int IPV6_LENGTH = 128;
35     private static final int IPV6_LENGTH_BYTES = 8;
36     private static final long IPV6_LSB_MASK = 0x000000FF;
37     private static final int IPV6_BYTE_OFFSET = 7;
38
39     // See OpenStack Network API v2.0 Reference for description of
40     // annotated attributes
41
42     @XmlElement(name = "network_id")
43     String networkUUID;
44
45     @XmlElement(defaultValue = "4", name = "ip_version")
46     Integer ipVersion;
47
48     @XmlElement(name = "cidr")
49     String cidr;
50
51     @XmlElement(name = "gateway_ip")
52     String gatewayIp;
53
54     @XmlElement(name = "dns_nameservers")
55     List<String> dnsNameservers;
56
57     @XmlElement(name = "allocation_pools")
58     List<NeutronSubnetIpAllocationPool> allocationPools;
59
60     @XmlElement(name = "host_routes")
61     List<NeutronRoute> hostRoutes;
62
63     @XmlElement(defaultValue = "true", name = "enable_dhcp")
64     Boolean enableDHCP;
65
66     @XmlElement(name = "ipv6_address_mode", nillable = true)
67     String ipV6AddressMode;
68
69     @XmlElement(name = "ipv6_ra_mode", nillable = true)
70     String ipV6RaMode;
71
72     public String getNetworkUUID() {
73         return networkUUID;
74     }
75
76     public void setNetworkUUID(String networkUUID) {
77         this.networkUUID = networkUUID;
78     }
79
80     public Integer getIpVersion() {
81         return ipVersion;
82     }
83
84     public void setIpVersion(Integer ipVersion) {
85         this.ipVersion = ipVersion;
86     }
87
88     public String getCidr() {
89         return cidr;
90     }
91
92     public void setCidr(String cidr) {
93         this.cidr = cidr;
94     }
95
96     public String getGatewayIp() {
97         return gatewayIp;
98     }
99
100     public void setGatewayIp(String gatewayIp) {
101         this.gatewayIp = gatewayIp;
102     }
103
104     public List<String> getDnsNameservers() {
105         return dnsNameservers;
106     }
107
108     public void setDnsNameservers(List<String> dnsNameservers) {
109         this.dnsNameservers = dnsNameservers;
110     }
111
112     public List<NeutronSubnetIpAllocationPool> getAllocationPools() {
113         return allocationPools;
114     }
115
116     public void setAllocationPools(List<NeutronSubnetIpAllocationPool> allocationPools) {
117         this.allocationPools = allocationPools;
118     }
119
120     public List<NeutronRoute> getHostRoutes() {
121         return hostRoutes;
122     }
123
124     public void setHostRoutes(List<NeutronRoute> hostRoutes) {
125         this.hostRoutes = hostRoutes;
126     }
127
128     public boolean isEnableDHCP() {
129         if (enableDHCP == null) {
130             return true;
131         }
132         return enableDHCP;
133     }
134
135     public Boolean getEnableDHCP() {
136         return enableDHCP;
137     }
138
139     public void setEnableDHCP(Boolean newValue) {
140         enableDHCP = newValue;
141     }
142
143     public String getIpV6AddressMode() {
144         return ipV6AddressMode;
145     }
146
147     public void setIpV6AddressMode(String ipV6AddressMode) {
148         this.ipV6AddressMode = ipV6AddressMode;
149     }
150
151     public String getIpV6RaMode() {
152         return ipV6RaMode;
153     }
154
155     public void setIpV6RaMode(String ipV6RaMode) {
156         this.ipV6RaMode = ipV6RaMode;
157     }
158
159     /**
160      * This method copies selected fields from the object and returns them
161      * as a new object, suitable for marshaling.
162      *
163      * @param fields
164      *            List of attributes to be extracted
165      * @return an OpenStackSubnets object with only the selected fields
166      *             populated
167      */
168
169     public NeutronSubnet extractFields(List<String> fields) {
170         NeutronSubnet ans = new NeutronSubnet();
171         for (String s : fields) {
172             if (extractField(s, ans)) {
173                 continue;
174             }
175             switch (s) {
176                 case "network_id":
177                     ans.setNetworkUUID(this.getNetworkUUID());
178                     break;
179                 case "ip_version":
180                     ans.setIpVersion(this.getIpVersion());
181                     break;
182                 case "cidr":
183                     ans.setCidr(this.getCidr());
184                     break;
185                 case "gateway_ip":
186                     ans.setGatewayIp(this.getGatewayIp());
187                     break;
188                 case "dns_nameservers":
189                     List<String> nsList = new ArrayList<String>();
190                     nsList.addAll(this.getDnsNameservers());
191                     ans.setDnsNameservers(nsList);
192                     break;
193                 case "allocation_pools":
194                     List<NeutronSubnetIpAllocationPool> pools = new ArrayList<NeutronSubnetIpAllocationPool>();
195                     pools.addAll(this.getAllocationPools());
196                     ans.setAllocationPools(pools);
197                     break;
198                 case "host_routes":
199                     List<NeutronRoute> hostRoutes = new ArrayList<NeutronRoute>();
200                     hostRoutes.addAll(this.getHostRoutes());
201                     ans.setHostRoutes(hostRoutes);
202                     break;
203                 case "enable_dhcp":
204                     ans.setEnableDHCP(this.getEnableDHCP());
205                     break;
206                 case "ipv6_address_mode":
207                     ans.setIpV6AddressMode(this.getIpV6AddressMode());
208                     break;
209                 case "ipv6_ra_mode":
210                     ans.setIpV6RaMode(this.getIpV6RaMode());
211                     break;
212                 default:
213                     LOGGER.warn("{} is not an OpenStackSubnet suitable field.", s);
214                     break;
215             }
216         }
217         return ans;
218     }
219
220     /* test to see if the cidr address used to define this subnet
221      * is a valid network address (an necessary condition when creating
222      * a new subnet)
223      */
224     public boolean isValidCIDR() {
225         // fix for Bug 2290 - need to wrap the existing test as
226         // IPv4 because SubnetUtils doesn't support IPv6
227         if (ipVersion == IPV4_VERSION) {
228             try {
229                 SubnetUtils util = new SubnetUtils(cidr);
230                 SubnetInfo info = util.getInfo();
231                 if (!info.getNetworkAddress().equals(info.getAddress())) {
232                     return false;
233                 }
234             } catch (IllegalArgumentException e) {
235                 LOGGER.warn("Failure in isValidCIDR()", e);
236                 return false;
237             }
238             return true;
239         }
240         if (ipVersion == IPV6_VERSION) {
241             // fix for Bug2290 - this is custom code because no classes
242             // with ODL-friendly licenses have been found
243             // extract address (in front of /) and length (after /)
244             String[] parts = cidr.split("/");
245             if (parts.length != 2) {
246                 return false;
247             }
248             int length = Integer.parseInt(parts[1]);
249             //TODO?: limit check on length
250             // convert to byte array
251             byte[] addrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
252             for (int index = length; index < IPV6_LENGTH; index++) {
253                 if (((((int) addrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
254                         & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES)))) != 0) {
255                     return (false);
256                 }
257             }
258             return (true);
259         }
260         return false;
261     }
262
263     /* test to see if the gateway IP specified overlaps with specified
264      * allocation pools (an error condition when creating a new subnet
265      * or assigning a gateway IP)
266      */
267     public boolean gatewayIp_Pool_overlap() {
268         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
269             if (ipVersion == IPV4_VERSION && pool.contains(gatewayIp)) {
270                 return true;
271             }
272             if (ipVersion == IPV6_VERSION && pool.containsV6(gatewayIp)) {
273                 return true;
274             }
275         }
276         return false;
277     }
278
279     @Override
280     public void initDefaults() {
281         if (enableDHCP == null) {
282             enableDHCP = true;
283         }
284         if (ipVersion == null) {
285             ipVersion = IPV4_VERSION;
286         }
287         if (dnsNameservers == null) {
288             dnsNameservers = new ArrayList<String>();
289         }
290         if (hostRoutes == null) {
291             hostRoutes = new ArrayList<NeutronRoute>();
292         }
293         if (allocationPools == null) {
294             allocationPools = new ArrayList<NeutronSubnetIpAllocationPool>();
295             if (ipVersion == IPV4_VERSION) {
296                 try {
297                     SubnetUtils util = new SubnetUtils(cidr);
298                     SubnetInfo info = util.getInfo();
299                     if (gatewayIp == null || ("").equals(gatewayIp)) {
300                         gatewayIp = info.getLowAddress();
301                     }
302                     if (allocationPools.size() < 1) {
303                         NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(info.getLowAddress(),
304                                 info.getHighAddress());
305                         allocationPools = source.splitPool(gatewayIp);
306                     }
307                 } catch (IllegalArgumentException e) {
308                     LOGGER.warn("Failure in initDefault()", e);
309                     return;
310                 }
311             }
312             if (ipVersion == IPV6_VERSION) {
313                 String[] parts = cidr.split("/");
314                 if (parts.length != 2) {
315                     return;
316                 }
317
318                 int length = Integer.parseInt(parts[1]);
319                 BigInteger lowAddressBi = NeutronSubnetIpAllocationPool.convertV6(parts[0]);
320                 String lowAddress = NeutronSubnetIpAllocationPool.bigIntegerToIp(lowAddressBi.add(BigInteger.ONE));
321                 BigInteger mask = BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE);
322                 String highAddress = NeutronSubnetIpAllocationPool
323                         .bigIntegerToIp(lowAddressBi.add(mask).subtract(BigInteger.ONE));
324                 if (gatewayIp == null || ("").equals(gatewayIp)) {
325                     gatewayIp = lowAddress;
326                 }
327                 if (allocationPools.size() < 1) {
328                     NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(lowAddress,
329                             highAddress);
330                     allocationPools = source.splitPoolV6(gatewayIp);
331                 }
332             }
333         }
334     }
335
336     /* this method tests to see if the supplied IPv4 address
337      * is valid for this subnet or not
338      */
339     public boolean isValidIp(String ipAddress) {
340         if (ipVersion == IPV4_VERSION) {
341             try {
342                 SubnetUtils util = new SubnetUtils(cidr);
343                 SubnetInfo info = util.getInfo();
344                 return info.isInRange(ipAddress);
345             } catch (IllegalArgumentException e) {
346                 LOGGER.warn("Failure in isValidIp()", e);
347                 return false;
348             }
349         }
350
351         if (ipVersion == IPV6_VERSION) {
352             String[] parts = cidr.split("/");
353             int length = Integer.parseInt(parts[1]);
354             byte[] cidrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
355             byte[] ipBytes = ((Inet6Address) InetAddresses.forString(ipAddress)).getAddress();
356             for (int index = 0; index < length; index++) {
357                 if (((((int) cidrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK) & (1 << (IPV6_BYTE_OFFSET
358                         - (index % IPV6_LENGTH_BYTES)))) != (
359                             (((int) ipBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
360                             & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES))))) {
361                     return (false);
362                 }
363             }
364             return (true);
365         }
366         return false;
367     }
368
369     /* method to get the lowest available address of the subnet.
370      * go through all the allocation pools and keep the lowest of their
371      * low addresses.
372      */
373     public String getLowAddr() {
374         String ans = null;
375         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
376             if (ans == null) {
377                 ans = pool.getPoolStart();
378             } else {
379                 if (ipVersion == IPV4_VERSION && NeutronSubnetIpAllocationPool
380                         .convert(pool.getPoolStart()) < NeutronSubnetIpAllocationPool.convert(ans)) {
381                     ans = pool.getPoolStart();
382                 }
383                 if (ipVersion == IPV6_VERSION && NeutronSubnetIpAllocationPool.convertV6(pool.getPoolStart())
384                         .compareTo(NeutronSubnetIpAllocationPool.convertV6(ans)) < 0) {
385                     ans = pool.getPoolStart();
386                 }
387             }
388         }
389         return ans;
390     }
391
392     @Override
393     public String toString() {
394         return "NeutronSubnet [subnetUUID=" + uuid + ", networkUUID=" + networkUUID + ", name=" + name + ", ipVersion="
395                 + ipVersion + ", cidr=" + cidr + ", gatewayIp=" + gatewayIp + ", dnsNameservers=" + dnsNameservers
396                 + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes + ", enableDHCP=" + enableDHCP
397                 + ", tenantID=" + tenantID + ", ipv6AddressMode=" + ipV6AddressMode + ", ipv6RaMode=" + ipV6RaMode
398                 + "]";
399     }
400 }