blob: 4084221494e85d4ed9199d1d4db4500be9c7aded [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070016package org.onlab.packet;
17
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070018import java.util.Objects;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070019
20/**
Thomas Vachuska4b420772014-10-30 16:46:17 -070021 * A class representing an IP prefix. A prefix consists of an IP address and
22 * a subnet mask.
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080023 * This class is immutable.
Thomas Vachuska4b420772014-10-30 16:46:17 -070024 * <p>
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070025 * NOTE: The stored IP address in the result IP prefix is masked to
26 * contain zeroes in all bits after the prefix length.
Thomas Vachuska4b420772014-10-30 16:46:17 -070027 * </p>
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070028 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080029public class IpPrefix {
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070030 // Maximum network mask length
31 public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
32 public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070033
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070034 private final IpAddress address;
35 private final short prefixLength;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070036
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070037 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070038 * Constructor for given IP address, and a prefix length.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070039 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070040 * @param address the IP address
41 * @param prefixLength the prefix length
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070042 * @throws IllegalArgumentException if the prefix length value is invalid
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070043 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080044 protected IpPrefix(IpAddress address, int prefixLength) {
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -080045 checkPrefixLength(address.version(), prefixLength);
46 this.address = IpAddress.makeMaskedAddress(address, prefixLength);
47 this.prefixLength = (short) prefixLength;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070048 }
49
50 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070051 * Returns the IP version of the prefix.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070052 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070053 * @return the IP version of the prefix
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070054 */
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070055 public IpAddress.Version version() {
56 return address.version();
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070057 }
58
59 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070060 * Returns the IP address value of the prefix.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070061 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070062 * @return the IP address value of the prefix
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070063 */
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070064 public IpAddress address() {
65 return address;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070066 }
67
Ayaka Koshibe16698a32014-09-13 22:19:02 -070068 /**
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070069 * Returns the IP address prefix length.
70 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070071 * @return the IP address prefix length
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070072 */
73 public int prefixLength() {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070074 return prefixLength;
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070075 }
76
77 /**
Pavlin Radoslavov34c81642014-11-04 16:21:38 -080078 * Gets the {@link Ip4Prefix} view of the IP prefix.
79 *
80 * @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
81 * otherwise null
82 */
83 public Ip4Prefix getIp4Prefix() {
84 if (version() != Ip4Prefix.VERSION) {
85 return null;
86 }
87
88 // Return this object itself if it is already instance of Ip4Prefix
89 if (this instanceof Ip4Prefix) {
90 return (Ip4Prefix) this;
91 }
92 return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
93 }
94
95 /**
96 * Gets the {@link Ip6Prefix} view of the IP prefix.
97 *
98 * @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
99 * otherwise null
100 */
101 public Ip6Prefix getIp6Prefix() {
102 if (version() != Ip6Prefix.VERSION) {
103 return null;
104 }
105
106 // Return this object itself if it is already instance of Ip6Prefix
107 if (this instanceof Ip6Prefix) {
108 return (Ip6Prefix) this;
109 }
110 return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
111 }
112
113 /**
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800114 * Converts an integer and a prefix length into an IPv4 prefix.
115 *
116 * @param address an integer representing the IPv4 address
117 * @param prefixLength the prefix length
118 * @return an IP prefix
119 * @throws IllegalArgumentException if the prefix length value is invalid
120 */
121 public static IpPrefix valueOf(int address, int prefixLength) {
122 return new IpPrefix(IpAddress.valueOf(address), prefixLength);
123 }
124
125 /**
126 * Converts a byte array and a prefix length into an IP prefix.
127 *
128 * @param version the IP address version
129 * @param address the IP address value stored in network byte order
130 * @param prefixLength the prefix length
131 * @return an IP prefix
132 * @throws IllegalArgumentException if the prefix length value is invalid
133 */
134 public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
135 int prefixLength) {
136 return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
137 }
138
139 /**
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800140 * Converts an IP address and a prefix length into an IP prefix.
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800141 *
142 * @param address the IP address
143 * @param prefixLength the prefix length
144 * @return an IP prefix
145 * @throws IllegalArgumentException if the prefix length value is invalid
146 */
147 public static IpPrefix valueOf(IpAddress address, int prefixLength) {
148 return new IpPrefix(address, prefixLength);
149 }
150
151 /**
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800152 * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
153 * "1111:2222::/64") into an IP prefix.
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800154 *
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800155 * @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
156 * "1111:2222::/64")
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800157 * @return an IP prefix
158 * @throws IllegalArgumentException if the arguments are invalid
159 */
160 public static IpPrefix valueOf(String address) {
161 final String[] parts = address.split("/");
162 if (parts.length != 2) {
163 String msg = "Malformed IP prefix string: " + address + "." +
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800164 "Address must take form \"x.x.x.x/y\" or " +
165 "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800166 throw new IllegalArgumentException(msg);
167 }
168 IpAddress ipAddress = IpAddress.valueOf(parts[0]);
169 int prefixLength = Integer.parseInt(parts[1]);
170
171 return new IpPrefix(ipAddress, prefixLength);
172 }
173
174 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700175 * Determines whether a given IP prefix is contained within this prefix.
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700176 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700177 * @param other the IP prefix to test
178 * @return true if the other IP prefix is contained in this prefix,
179 * otherwise false
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700180 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700181 public boolean contains(IpPrefix other) {
Pavlin Radoslavovdbeab4c2015-02-23 09:37:49 -0800182 if (version() != other.version()) {
183 return false;
184 }
185
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700186 if (this.prefixLength > other.prefixLength) {
187 return false; // This prefix has smaller prefix size
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700188 }
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700189
190 //
191 // Mask the other address with my prefix length.
192 // If the other prefix is within this prefix, the masked address must
193 // be same as the address of this prefix.
194 //
195 IpAddress maskedAddr =
196 IpAddress.makeMaskedAddress(other.address, this.prefixLength);
197 return this.address.equals(maskedAddr);
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700198 }
199
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700200 /**
201 * Determines whether a given IP address is contained within this prefix.
202 *
203 * @param other the IP address to test
204 * @return true if the IP address is contained in this prefix, otherwise
205 * false
206 */
207 public boolean contains(IpAddress other) {
Pavlin Radoslavovdbeab4c2015-02-23 09:37:49 -0800208 if (version() != other.version()) {
209 return false;
210 }
211
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700212 //
213 // Mask the other address with my prefix length.
214 // If the other prefix is within this prefix, the masked address must
215 // be same as the address of this prefix.
216 //
217 IpAddress maskedAddr =
218 IpAddress.makeMaskedAddress(other, this.prefixLength);
219 return this.address.equals(maskedAddr);
Jonathan Hart70da5122014-10-01 16:37:42 -0700220 }
221
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700222 @Override
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700223 public int hashCode() {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700224 return Objects.hash(address, prefixLength);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700225 }
226
227 @Override
228 public boolean equals(Object obj) {
229 if (this == obj) {
230 return true;
231 }
Pavlin Radoslavov50b70672014-11-05 11:22:25 -0800232 if ((obj == null) || (!(obj instanceof IpPrefix))) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700233 return false;
234 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700235 IpPrefix other = (IpPrefix) obj;
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700236 return ((prefixLength == other.prefixLength) &&
237 address.equals(other.address));
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700238 }
239
240 @Override
241 /*
242 * (non-Javadoc)
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700243 * The format is "x.x.x.x/y" for IPv4 prefixes.
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700244 *
245 * @see java.lang.Object#toString()
246 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700247 public String toString() {
248 final StringBuilder builder = new StringBuilder();
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700249 builder.append(address.toString());
250 builder.append("/");
251 builder.append(String.format("%d", prefixLength));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700252 return builder.toString();
253 }
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800254
255 /**
256 * Checks whether the prefix length is valid.
257 *
258 * @param version the IP address version
259 * @param prefixLength the prefix length value to check
260 * @throws IllegalArgumentException if the prefix length value is invalid
261 */
262 private static void checkPrefixLength(IpAddress.Version version,
263 int prefixLength) {
264 int maxPrefixLen = 0;
265
266 switch (version) {
267 case INET:
268 maxPrefixLen = MAX_INET_MASK_LENGTH;
269 break;
270 case INET6:
271 maxPrefixLen = MAX_INET6_MASK_LENGTH;
272 break;
273 default:
274 String msg = "Invalid IP version " + version;
275 throw new IllegalArgumentException(msg);
276 }
277
278 if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
279 String msg = "Invalid prefix length " + prefixLength + ". " +
280 "The value must be in the interval [0, " +
281 maxPrefixLen + "]";
282 throw new IllegalArgumentException(msg);
283 }
284 }
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700285}