ba70c250b63627c4f4bbf2a44e95045011433000
[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.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;
24
25 @XmlRootElement
26 @XmlAccessorType(XmlAccessType.NONE)
27 public final class NeutronSubnet extends NeutronBaseAttributes<NeutronSubnet> {
28     private static final Logger LOG = LoggerFactory.getLogger(NeutronCRUDInterfaces.class);
29
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;
37
38     // See OpenStack Network API v2.0 Reference for description of
39     // annotated attributes
40
41     @XmlElement(name = "network_id")
42     String networkUUID;
43
44     @XmlElement(defaultValue = "4", name = "ip_version")
45     Integer ipVersion;
46
47     @XmlElement(name = "cidr")
48     String cidr;
49
50     @XmlElement(name = "gateway_ip")
51     String gatewayIp;
52
53     @XmlElement(name = "dns_nameservers")
54     List<String> dnsNameservers;
55
56     @XmlElement(name = "allocation_pools")
57     List<NeutronSubnetIpAllocationPool> allocationPools;
58
59     @XmlElement(name = "host_routes")
60     List<NeutronRoute> hostRoutes;
61
62     @XmlElement(defaultValue = "true", name = "enable_dhcp")
63     Boolean enableDHCP;
64
65     @XmlElement(name = "ipv6_address_mode", nillable = true)
66     String ipV6AddressMode;
67
68     @XmlElement(name = "ipv6_ra_mode", nillable = true)
69     String ipV6RaMode;
70
71     public String getNetworkUUID() {
72         return networkUUID;
73     }
74
75     public void setNetworkUUID(String networkUUID) {
76         this.networkUUID = networkUUID;
77     }
78
79     public Integer getIpVersion() {
80         return ipVersion;
81     }
82
83     public void setIpVersion(Integer ipVersion) {
84         this.ipVersion = ipVersion;
85     }
86
87     public String getCidr() {
88         return cidr;
89     }
90
91     public void setCidr(String cidr) {
92         this.cidr = cidr;
93     }
94
95     public String getGatewayIp() {
96         return gatewayIp;
97     }
98
99     public void setGatewayIp(String gatewayIp) {
100         this.gatewayIp = gatewayIp;
101     }
102
103     public List<String> getDnsNameservers() {
104         return dnsNameservers;
105     }
106
107     public void setDnsNameservers(List<String> dnsNameservers) {
108         this.dnsNameservers = dnsNameservers;
109     }
110
111     public List<NeutronSubnetIpAllocationPool> getAllocationPools() {
112         return allocationPools;
113     }
114
115     public void setAllocationPools(List<NeutronSubnetIpAllocationPool> allocationPools) {
116         this.allocationPools = allocationPools;
117     }
118
119     public List<NeutronRoute> getHostRoutes() {
120         return hostRoutes;
121     }
122
123     public void setHostRoutes(List<NeutronRoute> hostRoutes) {
124         this.hostRoutes = hostRoutes;
125     }
126
127     public boolean isEnableDHCP() {
128         if (enableDHCP == null) {
129             return true;
130         }
131         return enableDHCP;
132     }
133
134     public Boolean getEnableDHCP() {
135         return enableDHCP;
136     }
137
138     public void setEnableDHCP(Boolean newValue) {
139         enableDHCP = newValue;
140     }
141
142     public String getIpV6AddressMode() {
143         return ipV6AddressMode;
144     }
145
146     public void setIpV6AddressMode(String ipV6AddressMode) {
147         this.ipV6AddressMode = ipV6AddressMode;
148     }
149
150     public String getIpV6RaMode() {
151         return ipV6RaMode;
152     }
153
154     public void setIpV6RaMode(String ipV6RaMode) {
155         this.ipV6RaMode = ipV6RaMode;
156     }
157
158     @Override
159     protected boolean extractField(String field, NeutronSubnet ans) {
160         switch (field) {
161             case "network_id":
162                 ans.setNetworkUUID(this.getNetworkUUID());
163                 break;
164             case "ip_version":
165                 ans.setIpVersion(this.getIpVersion());
166                 break;
167             case "cidr":
168                 ans.setCidr(this.getCidr());
169                 break;
170             case "gateway_ip":
171                 ans.setGatewayIp(this.getGatewayIp());
172                 break;
173             case "dns_nameservers":
174                 List<String> nsList = new ArrayList<>();
175                 nsList.addAll(this.getDnsNameservers());
176                 ans.setDnsNameservers(nsList);
177                 break;
178             case "allocation_pools":
179                 List<NeutronSubnetIpAllocationPool> pools = new ArrayList<>();
180                 pools.addAll(this.getAllocationPools());
181                 ans.setAllocationPools(pools);
182                 break;
183             case "host_routes":
184                 List<NeutronRoute> routes = new ArrayList<>();
185                 routes.addAll(this.getHostRoutes());
186                 ans.setHostRoutes(routes);
187                 break;
188             case "enable_dhcp":
189                 ans.setEnableDHCP(this.getEnableDHCP());
190                 break;
191             case "ipv6_address_mode":
192                 ans.setIpV6AddressMode(this.getIpV6AddressMode());
193                 break;
194             case "ipv6_ra_mode":
195                 ans.setIpV6RaMode(this.getIpV6RaMode());
196                 break;
197             default:
198                 return super.extractField(field, ans);
199         }
200         return true;
201     }
202
203     /* test to see if the cidr address used to define this subnet
204      * is a valid network address (an necessary condition when creating
205      * a new subnet)
206      */
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) {
211             try {
212                 SubnetUtils util = new SubnetUtils(cidr);
213                 SubnetInfo info = util.getInfo();
214                 if (!info.getNetworkAddress().equals(info.getAddress())) {
215                     return false;
216                 }
217             } catch (IllegalArgumentException e) {
218                 LOG.warn("Failure in isValidCIDR()", e);
219                 return false;
220             }
221             return true;
222         }
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) {
229                 return false;
230             }
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) {
238                     return false;
239                 }
240             }
241             return true;
242         }
243         return false;
244     }
245
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)
249      */
250     public boolean gatewayIp_Pool_overlap() {
251         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
252             if (ipVersion == IPV4_VERSION && pool.contains(gatewayIp)) {
253                 return true;
254             }
255             if (ipVersion == IPV6_VERSION && pool.containsV6(gatewayIp)) {
256                 return true;
257             }
258         }
259         return false;
260     }
261
262     @Override
263     public void initDefaults() {
264         if (enableDHCP == null) {
265             enableDHCP = true;
266         }
267         if (ipVersion == null) {
268             ipVersion = IPV4_VERSION;
269         }
270         if (dnsNameservers == null) {
271             dnsNameservers = new ArrayList<>();
272         }
273         if (hostRoutes == null) {
274             hostRoutes = new ArrayList<>();
275         }
276         if (allocationPools == null) {
277             allocationPools = new ArrayList<>();
278             if (ipVersion == IPV4_VERSION) {
279                 try {
280                     SubnetUtils util = new SubnetUtils(cidr);
281                     SubnetInfo info = util.getInfo();
282                     if (gatewayIp == null || "".equals(gatewayIp)) {
283                         gatewayIp = info.getLowAddress();
284                     }
285                     if (allocationPools.size() < 1) {
286                         NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(info.getLowAddress(),
287                                 info.getHighAddress());
288                         allocationPools = source.splitPool(gatewayIp);
289                     }
290                 } catch (IllegalArgumentException e) {
291                     LOG.warn("Failure in initDefault()", e);
292                     return;
293                 }
294             }
295             if (ipVersion == IPV6_VERSION) {
296                 String[] parts = cidr.split("/");
297                 if (parts.length != 2) {
298                     return;
299                 }
300
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;
309                 }
310                 if (allocationPools.size() < 1) {
311                     NeutronSubnetIpAllocationPool source = new NeutronSubnetIpAllocationPool(lowAddress,
312                             highAddress);
313                     allocationPools = source.splitPoolV6(gatewayIp);
314                 }
315             }
316         }
317     }
318
319     /* this method tests to see if the supplied IPv4 address
320      * is valid for this subnet or not
321      */
322     public boolean isValidIp(String ipAddress) {
323         if (ipVersion == IPV4_VERSION) {
324             try {
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);
330                 return false;
331             }
332         }
333
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)) {
344                     return false;
345                 }
346             }
347             return true;
348         }
349         return false;
350     }
351
352     /* method to get the lowest available address of the subnet.
353      * go through all the allocation pools and keep the lowest of their
354      * low addresses.
355      */
356     public String getLowAddr() {
357         String ans = null;
358         for (NeutronSubnetIpAllocationPool pool : allocationPools) {
359             if (ans == null) {
360                 ans = pool.getPoolStart();
361             } else {
362                 if (ipVersion == IPV4_VERSION && NeutronSubnetIpAllocationPool
363                         .convert(pool.getPoolStart()) < NeutronSubnetIpAllocationPool.convert(ans)) {
364                     ans = pool.getPoolStart();
365                 }
366                 if (ipVersion == IPV6_VERSION && NeutronSubnetIpAllocationPool.convertV6(pool.getPoolStart())
367                         .compareTo(NeutronSubnetIpAllocationPool.convertV6(ans)) < 0) {
368                     ans = pool.getPoolStart();
369                 }
370             }
371         }
372         return ans;
373     }
374
375     @Override
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
381                 + "]";
382     }
383 }