inet.ipaddr.AddressNetwork Maven / Gradle / Ivy
Show all versions of ipaddress Show documentation
/*
* Copyright 2016-2018 Sean C Foley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* or at
* https://github.com/seancfoley/IPAddress/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inet.ipaddr;
import java.io.Serializable;
import java.util.Map;
import inet.ipaddr.Address.AddressValueProvider;
import inet.ipaddr.format.standard.AddressCreator;
/**
* An object representing a collection of addresses.
*
* It also encapsulates settings for handling all addresses in the network like the prefix configuration that determines certain properties of the addresses in the network.
*
* If your use of the IPAddress library has non-default configuration settings in this AddressNetwork class, and within the same JVM the IPAddress library
* is being used elsewhere with different configuration settings, then you have two options available to you:
*
* 1. Use classloaders to load the two uses of IPAddress in different classloaders, a common Java architecture that is part of the language itself to address just this issue
*
* 2. Use your own network classes, and within them overide the configuration methods to return the values you desire.
*
* All access to the network classes is through public virtual accessor methods getNetwork or getXNetwork in the classes XAddress, XAddressSection, XAddressSegment
* where X is one of MAC, IPv6, or IPv4. So you need to subclass those classes, and then override those getNetwork and getXNetwork methods to return your own network instances.
* There are a couple of other places to consider to ensure only your own network instances are used.
* XAddressString objects obtain their network object from the validation parameters supplied to the constructor, so you would customize those validation parameters as well.
* The same is true for the HostName class, which uses an embedded address validation instance inside the host name parameters instance.
* Finally, the address generator/cache classes (that are nested classes that in the network) use validation parameters as well that would be customized to your own network instances.
*
* Generally you would use the same network object for any given address type (ie one for IPv6, one for IPv4, one for MAC), although this is not necessary.
* However, it is necessary that the configuration is the same for any given address type.
*
* Now suppose you wish to ensure any and all methods in this library create instances of your own subclasses of the XAddress, XAddressSection, XAddressSegment classes.
*
* All internally created address components are created by the address creator instance owned by the network object.
* So you override the getAddressCreator() in your new network classes to provide your own address creator object.
*
*
* @author sfoley
*
*/
public abstract class AddressNetwork implements Serializable {
private static final long serialVersionUID = 4L;
public interface AddressSegmentCreator {
S[] createSegmentArray(int length);
S createSegment(int value);
S createSegment(int value, Integer segmentPrefixLength);
S createSegment(int lower, int upper, Integer segmentPrefixLength);
}
public abstract AddressCreator, ?, ?, S> getAddressCreator();
public void clearCaches() {
getAddressCreator().clearCaches();
}
public void setSegmentCaching(boolean enable) {
getAddressCreator().setSegmentCaching(enable);
}
//// Configuration
/*
* a few sources about the network address -
* https://superuser.com/questions/379451/why-can-a-network-address-not-be-a-valid-host-address
* https://serverfault.com/questions/451238/why-cant-all-zeros-in-the-host-portion-of-ip-address-be-used-for-a-host
* //// Configuration
* Maybe use a bit from https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing, see the phrase "In common usage"
* https://github.com/ipaddress-gem/ipaddress
*/
/**
* Prefix Handling Configuration
*
* The library is designed to treat prefixes three different ways:
*
1. All prefixes are subnets. This was the legacy behaviour for version earlier than version 4.
* All prefixed addresses are converted to the block of addresses that share the same prefix.
* For addresses in which prefixes are derived from the address ranges, such as MAC, prefix lengths are implicitly calculated from the range,
* so 1:2:3:*:*:* implicitly has the prefix length of 24. This is also the case for any address derived from the original.
*
* 2. Addresses with zero-values hosts are treated as subnets. More precisely, addresses whose hosts are entirely zero,
* or addresses whose hosts start with zeros and end with the full range of values are treated as subnets.
* So, for example, 1.2.0.0/16 is converted to 1.2.*.* which is the block of addresses with with prefix 1.2.
* Also, 1.2.0.* /16 or 1.2.*.* /16 are also equivalent to the block of 65535 addresses 1.2.*.* associated with prefix length 16.
* Addresses with non-zero hosts, such as 1.2.0.1/16 are treated differently. 1.2.0.1/16 is equivalent to the single address 1.2.0.1 and is not a treated as a subnet block of multiple addresses.
* The new behaviour is akin to the typical convention used by network administrators in which the address with a host of zero is known as the network address.
* The all-zero address 0.0.0.0 is conventionally known as INADDR_ANY (any address on the local machine), and when paired with prefix zero it is known as the default route (the route for all addresses).
*
* The same is true on the IPv6 side, where 1:2:3:4::/64 is treated as the subnet of all addresses with prefix 1:2:3:4.
* With IPv6 it is a common convention to depict a prefixed network as a:b:c:d::/64, with the host shown as all zeros.
* This is also known as the subnet router anycast address in IPv6. The all-zero address '::' is the value of IN6ADDR_ANY_INIT, the analog to the IPv4 INADDR_ANY.
*
* In summary:
*
- A prefixed address whose host bits are all 0 is not a single host address, instead it represents a subnet, the block of all addresses with that prefix.
*
- A prefixed address whose host is non-zero is treated as a single address with the given prefix length.
*
*
* So for example, 1.2.0.0/16 will give you the subnet block 1.2.*.* /16, and once you have it, if you want just the single address 1.2.0.0/16, you can get it using {@link IPAddress#getLower()}.
*
* This option has less meaning for other address types in which ranges are explicit, such as MAC addresses. However, this option does allow you, using the appropriate constructor, to assign a prefix length to any address.
* So there is no automatic fixed mapping between the range of the address values and the associated prefix length.
*
* Additionally, when starting with an address whose prefix was calculated from its range, you can derive additionally addresses from the original, and those addresses will have the same prefix.
* For instance, 1:2:3:*:*:* implicitly has the prefix length of 24 regardless of the prefix configuration. But with this prefix configuration,
* you can then construct a derived address with the same prefix, for example with new MACAddressString("1:2:3:*:*:*").getAddress().replace(MACAddressString("1:2:3:4:5:6").getSection(2));
*
* 3. The third option is the setting for which prefixes are never automatically converted to subnets. Any subnet must be explicitly defined,
* such as 1.2.*.* /16
*
* For addresses in which ranges are explicit, such as MAC addresses, this option is no different than the second option.
*
*
* In summary:
* - When PrefixConfiguration == ALL_PREFIXES_ARE_SUBNETS all prefixed addresses have hosts that span all possible host values.
* - When PrefixConfiguration == PREFIXED_ZERO_HOSTS_ARE_SUBNETS addresses constructed with zero host will have hosts that span all possible values, such as 1.2.0.0/16 which is equivalent to 1.2.*.* /16
* - When PrefixConfiguration == EXPLICIT_SUBNETS hosts that span all values are explicit, such as 1.2.*.* /16, while 1.2.0.0/16 is just a single address with a single host value of zero.
*
*
* Note that when setting a non-default prefix configuration, indeterminate behaviour can result from the same addresses using different prefix configuration settings at different times, so this method must be used carefully.
*
* Should you wish to use two different prefix configurations in the same app, it can be done safely using classloaders,
* and it can also be done using different network instances. To used different networks, you can override the virtual methods
* for getting network instances in your address component classes.
*/
public enum PrefixConfiguration {
ALL_PREFIXED_ADDRESSES_ARE_SUBNETS,//legacy behaviour
PREFIXED_ZERO_HOSTS_ARE_SUBNETS,//default
EXPLICIT_SUBNETS;
/**
* @return whether this is ALL_PREFIXED_ADDRESSES_ARE_SUBNETS
*/
public boolean allPrefixedAddressesAreSubnets() {
return this == ALL_PREFIXED_ADDRESSES_ARE_SUBNETS;
}
/**
* @return whether this is PREFIXED_ZERO_HOSTS_ARE_SUBNETS
*/
public boolean zeroHostsAreSubnets() {
return this == PREFIXED_ZERO_HOSTS_ARE_SUBNETS;
}
/**
* @return whether this is EXPLICIT_SUBNETS
*/
public boolean prefixedSubnetsAreExplicit() {
return this == EXPLICIT_SUBNETS;
}
}
private static PrefixConfiguration defaultPrefixConfiguration = PrefixConfiguration.PREFIXED_ZERO_HOSTS_ARE_SUBNETS;
//public static PrefixConfiguration prefixConfiguration = PrefixConfiguration.ALL_PREFIXES_ARE_SUBNETS; //old behaviour (version 3 and under)
/**
* This method determines the prefix configuration in use by this network.
*
* The prefix configuration determines whether a prefixed address like 1.2.0.0/16 results in a subnet block (ie 1.2.*.*) or just a single address (1.2.0.0) with a prefix length.
*
* If you wish to change the default behaviour, you can either call {@link inet.ipaddr.ipv4.IPv4AddressNetwork#setDefaultPrefixConfiguration(PrefixConfiguration)},
* or {@link inet.ipaddr.ipv6.IPv6AddressNetwork#setDefaultPrefixConfiguration(PrefixConfiguration)} or you can override this method in your own network and use your own network for your addresses.
*
* @see PrefixConfiguration
*/
public abstract PrefixConfiguration getPrefixConfiguration();
public static PrefixConfiguration getDefaultPrefixConfiguration() {
return defaultPrefixConfiguration;
}
protected boolean isCompatible(AddressNetwork> other) {
return IPAddressSection.isCompatibleNetworks(this, other);
}
/**
* Generates and caches HostIdentifierString instances. Choose a map of your choice to implement a cache of address string identifiers.
*
* You choose the map of your choice to be the backing map for the cache.
* For example, for thread-safe access to the cache, ConcurrentHashMap is a good choice.
* For maps of bounded size, LinkedHashMap provides the removeEldestEntry method to override to implement LRU or other eviction mechanisms.
*
* @author sfoley
*
* @param the type to be cached, typically either IPAddressString or HostName
*/
public static abstract class HostIdentifierStringGenerator implements Serializable {
private static final long serialVersionUID = 4L;
protected final Map backingMap;
public HostIdentifierStringGenerator() {
this(null);
}
public HostIdentifierStringGenerator(Map backingMap) {
this.backingMap = backingMap;
}
public Map getBackingMap() {
return backingMap;
}
/*
* If you wish to maintain a count of added addresses, or a log, then override this method
*/
protected void added(T added) {}
/**
* Returns whether the given instance is in the cache.
* @param value
* @return whether the given instance of T is in the cache
*/
public boolean contains(T value) {
return backingMap.containsValue(value);
}
/**
* Gets the object for the given key. If the object does not exist yet then it is created and added to the cache.
* @param key
* @return the object for the given key
*/
public T get(String key) {
if(backingMap == null) {
return create(key);
}
T result = backingMap.get(key);
if(result == null) {
result = create(key);
String normalizedKey = result.toNormalizedString();
//we want to use only the IPAddressString or HostName that was created from the normalized string.
//This helps things like getHostAddress to have predictable behaviour
result = create(normalizedKey);
T existing = backingMap.putIfAbsent(normalizedKey, result);
if(existing == null) {
added(result);
} else {
result = existing;
}
if(!normalizedKey.equals(key)) {
backingMap.put(key, result);
}
}
return result;
}
public abstract T get(byte bytes[]);
public abstract T get(AddressValueProvider addressProvider);
protected abstract T create(String key);
}
}