f3e848b6c9a3d719f4124200cad55fab96a36e04
[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 LOG = 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     @Override
160     protected boolean extractField(String field, NeutronSubnet ans) {
161         switch (field) {
162             case "network_id":
163                 ans.setNetworkUUID(this.getNetworkUUID());
164                 break;
165             case "ip_version":
166                 ans.setIpVersion(this.getIpVersion());
167                 break;
168             case "cidr":
169                 ans.setCidr(this.getCidr());
170                 break;
171             case "gateway_ip":
172                 ans.setGatewayIp(this.getGatewayIp());
173                 break;
174             case "dns_nameservers":
175                 List<String> nsList = new ArrayList<>();
176                 nsList.addAll(this.getDnsNameservers());
177                 ans.setDnsNameservers(nsList);
178                 break;
179             case "allocation_pools":
180                 List<NeutronSubnetIpAllocationPool> pools = new ArrayList<>();
181                 pools.addAll(this.getAllocationPools());
182                 ans.setAllocationPools(pools);
183                 break;
184             case "host_routes":
185                 List<NeutronRoute> routes = new ArrayList<>();
186                 routes.addAll(this.getHostRoutes());
187                 ans.setHostRoutes(routes);
188                 break;
189             case "enable_dhcp":
190                 ans.setEnableDHCP(this.getEnableDHCP());
191                 break;
192             case "ipv6_address_mode":
193                 ans.setIpV6AddressMode(this.getIpV6AddressMode());
194                 break;
195             case "ipv6_ra_mode":
196                 ans.setIpV6RaMode(this.getIpV6RaMode());
197                 break;
198             default:
199                 return super.extractField(field, ans);
200         }
201         return true;
202     }
203
204     /* test to see if the cidr address used to define this subnet
205      * is a valid network address (an necessary condition when creating
206      * a new subnet)
207      */
208     public boolean isValidCIDR() {
209         // fix for Bug 2290 - need to wrap the existing test as
210         // IPv4 because SubnetUtils doesn't support IPv6
211         if (ipVersion == IPV4_VERSION) {
212             try {
213                 SubnetUtils util = new SubnetUtils(cidr);
214                 SubnetInfo info = util.getInfo();
215                 if (!info.getNetworkAddress().equals(info.getAddress())) {
216                     return false;
217                 }
218             } catch (IllegalArgumentException e) {
219                 LOG.warn("Failure in isValidCIDR()", e);
220                 return false;
221             }
222             return true;
223         }
224         if (ipVersion == IPV6_VERSION) {
225             // fix for Bug2290 - this is custom code because no classes
226             // with ODL-friendly licenses have been found
227             // extract address (in front of /) and length (after /)
228             String[] parts = cidr.split("/");
229             if (parts.length != 2) {
230                 return false;
231             }
232             int length = Integer.parseInt(parts[1]);
233             //TODO?: limit check on length
234             // convert to byte array
235             byte[] addrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
236             for (int index = length; index < IPV6_LENGTH; index++) {
237                 if (((((int) addrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
238                         & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES)))) != 0) {
239                     return (false);
240                 }
241             }
242             return (true);
243         }
244         return false;
245     }
246
247     /* test to see if the gateway IP specified overlaps with specified
248      * allocation pools (an error condition when creating a new subnet
249      * or assigning a gateway IP)
250      */
251     public boolean gatewayIp_Pool_overlap() {
252         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
253             if (ipVersion == IPV4_VERSION && pool.contains(gatewayIp)) {
254                 return true;
255             }
256             if (ipVersion == IPV6_VERSION && pool.containsV6(gatewayIp)) {
257                 return true;
258             }
259         }
260         return false;
261     }
262
263     @Override
264     public void initDefaults() {
265         if (enableDHCP == null) {
266             enableDHCP = true;
267         }
268         if (ipVersion == null) {
269             ipVersion = IPV4_VERSION;
270         }
271         if (dnsNameservers == null) {
272             dnsNameservers = new ArrayList<>();
273         }
274         if (hostRoutes == null) {
275             hostRoutes = new ArrayList<>();
276         }
277         if (allocationPools == null) {
278             allocationPools = new ArrayList<>();
279             if (ipVersion == IPV4_VERSION) {
280                 try {
281                     SubnetUtils util = new SubnetUtils(cidr);
282                     SubnetInfo info = util.getInfo();
283                     if (gatewayIp == null || ("").equals(gatewayIp)) {
284                         gatewayIp = info.getLowAddress();
285                     }
286                     if (allocationPools.size() < 1) {
287                         NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(info.getLowAddress(),
288                                 info.getHighAddress());
289                         allocationPools = source.splitPool(gatewayIp);
290                     }
291                 } catch (IllegalArgumentException e) {
292                     LOG.warn("Failure in initDefault()", e);
293                     return;
294                 }
295             }
296             if (ipVersion == IPV6_VERSION) {
297                 String[] parts = cidr.split("/");
298                 if (parts.length != 2) {
299                     return;
300                 }
301
302                 int length = Integer.parseInt(parts[1]);
303                 BigInteger lowAddressBi = NeutronSubnetIpAllocationPool.convertV6(parts[0]);
304                 String lowAddress = NeutronSubnetIpAllocationPool.bigIntegerToIp(lowAddressBi.add(BigInteger.ONE));
305                 BigInteger mask = BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE);
306                 String highAddress = NeutronSubnetIpAllocationPool
307                         .bigIntegerToIp(lowAddressBi.add(mask).subtract(BigInteger.ONE));
308                 if (gatewayIp == null || ("").equals(gatewayIp)) {
309                     gatewayIp = lowAddress;
310                 }
311                 if (allocationPools.size() < 1) {
312                     NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(lowAddress,
313                             highAddress);
314                     allocationPools = source.splitPoolV6(gatewayIp);
315                 }
316             }
317         }
318     }
319
320     /* this method tests to see if the supplied IPv4 address
321      * is valid for this subnet or not
322      */
323     public boolean isValidIp(String ipAddress) {
324         if (ipVersion == IPV4_VERSION) {
325             try {
326                 SubnetUtils util = new SubnetUtils(cidr);
327                 SubnetInfo info = util.getInfo();
328                 return info.isInRange(ipAddress);
329             } catch (IllegalArgumentException e) {
330                 LOG.warn("Failure in isValidIp()", e);
331                 return false;
332             }
333         }
334
335         if (ipVersion == IPV6_VERSION) {
336             String[] parts = cidr.split("/");
337             int length = Integer.parseInt(parts[1]);
338             byte[] cidrBytes = ((Inet6Address) InetAddresses.forString(parts[0])).getAddress();
339             byte[] ipBytes = ((Inet6Address) InetAddresses.forString(ipAddress)).getAddress();
340             for (int index = 0; index < length; index++) {
341                 if (((((int) cidrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK) & (1 << (IPV6_BYTE_OFFSET
342                         - (index % IPV6_LENGTH_BYTES)))) != (
343                             (((int) ipBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
344                             & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES))))) {
345                     return (false);
346                 }
347             }
348             return (true);
349         }
350         return false;
351     }
352
353     /* method to get the lowest available address of the subnet.
354      * go through all the allocation pools and keep the lowest of their
355      * low addresses.
356      */
357     public String getLowAddr() {
358         String ans = null;
359         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
360             if (ans == null) {
361                 ans = pool.getPoolStart();
362             } else {
363                 if (ipVersion == IPV4_VERSION && NeutronSubnetIpAllocationPool
364                         .convert(pool.getPoolStart()) < NeutronSubnetIpAllocationPool.convert(ans)) {
365                     ans = pool.getPoolStart();
366                 }
367                 if (ipVersion == IPV6_VERSION && NeutronSubnetIpAllocationPool.convertV6(pool.getPoolStart())
368                         .compareTo(NeutronSubnetIpAllocationPool.convertV6(ans)) < 0) {
369                     ans = pool.getPoolStart();
370                 }
371             }
372         }
373         return ans;
374     }
375
376     @Override
377     public String toString() {
378         return "NeutronSubnet [subnetUUID=" + uuid + ", networkUUID=" + networkUUID + ", name=" + name + ", ipVersion="
379                 + ipVersion + ", cidr=" + cidr + ", gatewayIp=" + gatewayIp + ", dnsNameservers=" + dnsNameservers
380                 + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes + ", enableDHCP=" + enableDHCP
381                 + ", tenantID=" + tenantID + ", ipv6AddressMode=" + ipV6AddressMode + ", ipv6RaMode=" + ipV6RaMode
382                 + "]";
383     }
384 }