blob: 81a49711522f53831c4abe60f35dce6667349397 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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
Jonathan Hart7f4bc522016-02-20 11:32:43 -080034 public static final IpPrefix MULTICAST_RANGE = IpPrefix.valueOf("224.0.0.0/4");
35
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070036 private final IpAddress address;
37 private final short prefixLength;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070038
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070039 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070040 * Constructor for given IP address, and a prefix length.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070041 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070042 * @param address the IP address
43 * @param prefixLength the prefix length
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070044 * @throws IllegalArgumentException if the prefix length value is invalid
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -070045 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080046 protected IpPrefix(IpAddress address, int prefixLength) {
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -080047 checkPrefixLength(address.version(), prefixLength);
48 this.address = IpAddress.makeMaskedAddress(address, prefixLength);
49 this.prefixLength = (short) prefixLength;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070050 }
51
52 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070053 * Returns the IP version of the prefix.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070054 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070055 * @return the IP version of the prefix
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070056 */
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070057 public IpAddress.Version version() {
58 return address.version();
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070059 }
60
61 /**
Pavlin Radoslavov34ffe722015-03-10 12:48:55 -070062 * Tests whether the IP version of this prefix is IPv4.
63 *
64 * @return true if the IP version of this prefix is IPv4, otherwise false.
65 */
66 public boolean isIp4() {
67 return address.isIp4();
68 }
69
70 /**
71 * Tests whether the IP version of this prefix is IPv6.
72 *
73 * @return true if the IP version of this prefix is IPv6, otherwise false.
74 */
75 public boolean isIp6() {
76 return address.isIp6();
77 }
78
79 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070080 * Returns the IP address value of the prefix.
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070081 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070082 * @return the IP address value of the prefix
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070083 */
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070084 public IpAddress address() {
85 return address;
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -070086 }
87
Ayaka Koshibe16698a32014-09-13 22:19:02 -070088 /**
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070089 * Returns the IP address prefix length.
90 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070091 * @return the IP address prefix length
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070092 */
93 public int prefixLength() {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070094 return prefixLength;
Yuta HIGUCHI10681f62014-09-21 17:49:46 -070095 }
96
97 /**
Pavlin Radoslavov34c81642014-11-04 16:21:38 -080098 * Gets the {@link Ip4Prefix} view of the IP prefix.
99 *
100 * @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
101 * otherwise null
102 */
103 public Ip4Prefix getIp4Prefix() {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700104 if (!isIp4()) {
Pavlin Radoslavov34c81642014-11-04 16:21:38 -0800105 return null;
106 }
107
108 // Return this object itself if it is already instance of Ip4Prefix
109 if (this instanceof Ip4Prefix) {
110 return (Ip4Prefix) this;
111 }
112 return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
113 }
114
115 /**
116 * Gets the {@link Ip6Prefix} view of the IP prefix.
117 *
118 * @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
119 * otherwise null
120 */
121 public Ip6Prefix getIp6Prefix() {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700122 if (!isIp6()) {
Pavlin Radoslavov34c81642014-11-04 16:21:38 -0800123 return null;
124 }
125
126 // Return this object itself if it is already instance of Ip6Prefix
127 if (this instanceof Ip6Prefix) {
128 return (Ip6Prefix) this;
129 }
130 return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
131 }
132
133 /**
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800134 * Converts an integer and a prefix length into an IPv4 prefix.
135 *
136 * @param address an integer representing the IPv4 address
137 * @param prefixLength the prefix length
138 * @return an IP prefix
139 * @throws IllegalArgumentException if the prefix length value is invalid
140 */
141 public static IpPrefix valueOf(int address, int prefixLength) {
142 return new IpPrefix(IpAddress.valueOf(address), prefixLength);
143 }
144
145 /**
146 * Converts a byte array and a prefix length into an IP prefix.
147 *
148 * @param version the IP address version
149 * @param address the IP address value stored in network byte order
150 * @param prefixLength the prefix length
151 * @return an IP prefix
152 * @throws IllegalArgumentException if the prefix length value is invalid
153 */
154 public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
155 int prefixLength) {
156 return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
157 }
158
159 /**
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800160 * Converts an IP address and a prefix length into an IP prefix.
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800161 *
162 * @param address the IP address
163 * @param prefixLength the prefix length
164 * @return an IP prefix
165 * @throws IllegalArgumentException if the prefix length value is invalid
166 */
167 public static IpPrefix valueOf(IpAddress address, int prefixLength) {
168 return new IpPrefix(address, prefixLength);
169 }
170
171 /**
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800172 * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
173 * "1111:2222::/64") into an IP prefix.
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800174 *
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800175 * @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
176 * "1111:2222::/64")
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800177 * @return an IP prefix
178 * @throws IllegalArgumentException if the arguments are invalid
179 */
180 public static IpPrefix valueOf(String address) {
181 final String[] parts = address.split("/");
182 if (parts.length != 2) {
Pavlin Radoslavovd7b45842015-03-20 15:40:59 -0700183 String msg = "Malformed IP prefix string: " + address + ". " +
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800184 "Address must take form \"x.x.x.x/y\" or " +
185 "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800186 throw new IllegalArgumentException(msg);
187 }
188 IpAddress ipAddress = IpAddress.valueOf(parts[0]);
189 int prefixLength = Integer.parseInt(parts[1]);
190
191 return new IpPrefix(ipAddress, prefixLength);
192 }
193
194 /**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700195 * Determines whether a given IP prefix is contained within this prefix.
Ayaka Koshibe16698a32014-09-13 22:19:02 -0700196 *
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700197 * @param other the IP prefix to test
198 * @return true if the other IP prefix is contained in this prefix,
199 * otherwise false
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700200 */
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700201 public boolean contains(IpPrefix other) {
Pavlin Radoslavovdbeab4c2015-02-23 09:37:49 -0800202 if (version() != other.version()) {
203 return false;
204 }
205
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700206 if (this.prefixLength > other.prefixLength) {
207 return false; // This prefix has smaller prefix size
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700208 }
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700209
210 //
211 // Mask the other address with my prefix length.
212 // If the other prefix is within this prefix, the masked address must
213 // be same as the address of this prefix.
214 //
215 IpAddress maskedAddr =
216 IpAddress.makeMaskedAddress(other.address, this.prefixLength);
217 return this.address.equals(maskedAddr);
Jonathan Hartb7a2ac32014-09-19 10:42:27 -0700218 }
219
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700220 /**
221 * Determines whether a given IP address is contained within this prefix.
222 *
223 * @param other the IP address to test
224 * @return true if the IP address is contained in this prefix, otherwise
225 * false
226 */
227 public boolean contains(IpAddress other) {
Pavlin Radoslavovdbeab4c2015-02-23 09:37:49 -0800228 if (version() != other.version()) {
229 return false;
230 }
231
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700232 //
233 // Mask the other address with my prefix length.
234 // If the other prefix is within this prefix, the masked address must
235 // be same as the address of this prefix.
236 //
237 IpAddress maskedAddr =
238 IpAddress.makeMaskedAddress(other, this.prefixLength);
239 return this.address.equals(maskedAddr);
Jonathan Hart70da5122014-10-01 16:37:42 -0700240 }
241
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700242 @Override
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700243 public int hashCode() {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700244 return Objects.hash(address, prefixLength);
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700245 }
246
247 @Override
248 public boolean equals(Object obj) {
249 if (this == obj) {
250 return true;
251 }
Pavlin Radoslavov50b70672014-11-05 11:22:25 -0800252 if ((obj == null) || (!(obj instanceof IpPrefix))) {
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700253 return false;
254 }
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -0700255 IpPrefix other = (IpPrefix) obj;
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700256 return ((prefixLength == other.prefixLength) &&
257 address.equals(other.address));
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700258 }
259
260 @Override
261 /*
262 * (non-Javadoc)
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700263 * The format is "x.x.x.x/y" for IPv4 prefixes.
Ayaka Koshibe40e7fec2014-09-16 22:32:19 -0700264 *
265 * @see java.lang.Object#toString()
266 */
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700267 public String toString() {
268 final StringBuilder builder = new StringBuilder();
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700269 builder.append(address.toString());
270 builder.append("/");
271 builder.append(String.format("%d", prefixLength));
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700272 return builder.toString();
273 }
Pavlin Radoslavov34c921a2014-11-03 15:41:22 -0800274
275 /**
276 * Checks whether the prefix length is valid.
277 *
278 * @param version the IP address version
279 * @param prefixLength the prefix length value to check
280 * @throws IllegalArgumentException if the prefix length value is invalid
281 */
282 private static void checkPrefixLength(IpAddress.Version version,
283 int prefixLength) {
284 int maxPrefixLen = 0;
285
286 switch (version) {
287 case INET:
288 maxPrefixLen = MAX_INET_MASK_LENGTH;
289 break;
290 case INET6:
291 maxPrefixLen = MAX_INET6_MASK_LENGTH;
292 break;
293 default:
294 String msg = "Invalid IP version " + version;
295 throw new IllegalArgumentException(msg);
296 }
297
298 if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
299 String msg = "Invalid prefix length " + prefixLength + ". " +
300 "The value must be in the interval [0, " +
301 maxPrefixLen + "]";
302 throw new IllegalArgumentException(msg);
303 }
304 }
Ayaka Koshibe1c7b38e2014-09-11 13:09:51 -0700305}