548 lines
17 KiB
V
548 lines
17 KiB
V
// This file is part of netaddr.
|
|
//
|
|
// netaddr is free software: you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License as published by the
|
|
// Free Software Foundation, either version 3 of the License, or (at your
|
|
// option) any later version.
|
|
//
|
|
// netaddr is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
// License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with netaddr. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
module netaddr
|
|
|
|
import encoding.binary
|
|
import math.bits
|
|
import net
|
|
|
|
pub struct Ipv4Addr {
|
|
addr [4]u8
|
|
}
|
|
|
|
// Ipv4Addr.new creates new Ipv4Addr instance from four octets.
|
|
pub fn Ipv4Addr.new(a u8, b u8, c u8, d u8) Ipv4Addr {
|
|
return Ipv4Addr{
|
|
addr: [a, b, c, d]!
|
|
}
|
|
}
|
|
|
|
// Ipv4Addr.from_octets creates new Ipv4Addr instance from four-element byte array.
|
|
pub fn Ipv4Addr.from_octets(addr [4]u8) Ipv4Addr {
|
|
return Ipv4Addr{addr}
|
|
}
|
|
|
|
// Ipv4Addr.from_string parses addr string and creates new Ipv4Addr instance.
|
|
// Only dotted-decimal form is allowed e.g. 203.0.113.5.
|
|
pub fn Ipv4Addr.from_string(addr string) !Ipv4Addr {
|
|
octets := addr.split('.')
|
|
if octets.len != 4 {
|
|
return error('expected 4 octets in ${addr}')
|
|
}
|
|
mut bytes := [4]u8{}
|
|
for i := 0; i < 4; i++ {
|
|
bytes[i] = u8(octets[i].parse_uint(10, 8) or {
|
|
return error('${octets[i]} is not valid unsigned 8 bit integer in ${addr}')
|
|
})
|
|
}
|
|
return Ipv4Addr{bytes}
|
|
}
|
|
|
|
// Ipv4Addr.from_u32 creates new Ipv4Addr instance from unsigned 32 bit integer.
|
|
pub fn Ipv4Addr.from_u32(addr u32) Ipv4Addr {
|
|
mut bytes := [4]u8{}
|
|
binary.big_endian_put_u32_fixed(mut bytes, addr)
|
|
return Ipv4Addr{bytes}
|
|
}
|
|
|
|
// str returns string representation of IP address.
|
|
pub fn (a Ipv4Addr) str() string {
|
|
// string concatenation is much faster than interpolation
|
|
return a.addr[0].str() + '.' + a.addr[1].str() + '.' + a.addr[2].str() + '.' + a.addr[3].str()
|
|
}
|
|
|
|
// u32 returns IP address represented as unsigned 32 bit integer.
|
|
pub fn (a Ipv4Addr) u32() u32 {
|
|
return binary.big_endian_u32_fixed(a.addr)
|
|
}
|
|
|
|
// u8_array returns IP address represented as byte array.
|
|
pub fn (a Ipv4Addr) u8_array() []u8 {
|
|
return a.addr[..]
|
|
}
|
|
|
|
// u8_array_fixed returns IP address represented as fixed size byte array.
|
|
pub fn (a Ipv4Addr) u8_array_fixed() [4]u8 {
|
|
return a.addr
|
|
}
|
|
|
|
// ipv6 returns IPv4-mapped or IPv4-compatible IPv6 address per RFC 4291.
|
|
// By default returns the IPv4-mapped IPv6 address e.g. ::ffff:203.0.113.90.
|
|
pub fn (a Ipv4Addr) ipv6(params Ipv4ToIpv6Params) Ipv6Addr {
|
|
mut bytes := [16]u8{}
|
|
if params.kind == .mapped {
|
|
bytes[10] = u8(255)
|
|
bytes[11] = u8(255)
|
|
}
|
|
bytes[12] = a.addr[0]
|
|
bytes[13] = a.addr[1]
|
|
bytes[14] = a.addr[2]
|
|
bytes[15] = a.addr[3]
|
|
return Ipv6Addr{
|
|
addr: bytes
|
|
}
|
|
}
|
|
|
|
// bit_len returns number of bits required to represent IP address.
|
|
// Example:
|
|
// ```
|
|
// assert netaddr.Ipv4Addr.new(0, 0, 255, 255).bit_len() == 16
|
|
// ```
|
|
pub fn (a Ipv4Addr) bit_len() int {
|
|
return bits.len_32(a.u32())
|
|
}
|
|
|
|
// family returns the `net.AddrFamily` member corresponding to IP version.
|
|
pub fn (a Ipv4Addr) family() net.AddrFamily {
|
|
return .ip // net.AddrFamily.ip means IP version 4
|
|
}
|
|
|
|
// reverse_pointer returns reverse DNS record for the IP address in .in-addr.arpa zone.
|
|
pub fn (a Ipv4Addr) reverse_pointer() string {
|
|
return a.str().split('.').reverse().join('.') + '.in-addr.arpa'
|
|
}
|
|
|
|
// is_link_local returns true if the address is reserved for link-local usage.
|
|
pub fn (a Ipv4Addr) is_link_local() bool {
|
|
return ipv4_link_local_network.contains(a)
|
|
}
|
|
|
|
// is_loopback returns true if this is a loopback address.
|
|
pub fn (a Ipv4Addr) is_loopback() bool {
|
|
return ipv4_loopback_network.contains(a)
|
|
}
|
|
|
|
// is_multicast returns true if the address is reserved for multicast use.
|
|
pub fn (a Ipv4Addr) is_multicast() bool {
|
|
return ipv4_multicast_network.contains(a)
|
|
}
|
|
|
|
// is_unicast returns true if the address is unicast.
|
|
pub fn (a Ipv4Addr) is_unicast() bool {
|
|
return !a.is_multicast()
|
|
}
|
|
|
|
// is_shared returns true if the address is allocated in shared address space.
|
|
// See RFC 6598. Addresses from network 100.64.0.0/10 is both not "private" and
|
|
// not "global", so is_private and is_global methods returns false for it.
|
|
pub fn (a Ipv4Addr) is_shared() bool {
|
|
return ipv4_public_network.contains(a)
|
|
}
|
|
|
|
// is_private returns true if the address is not globally reachable.
|
|
pub fn (a Ipv4Addr) is_private() bool {
|
|
return ipv4_private_networks.any(it.contains(a) == true)
|
|
&& ipv4_private_networks_exceptions.all(it.contains(a) == false)
|
|
}
|
|
|
|
// is_global return true if the address is globally reachable.
|
|
pub fn (a Ipv4Addr) is_global() bool {
|
|
return !a.is_private() && !ipv4_public_network.contains(a)
|
|
}
|
|
|
|
// is_reserved returns true if the address is IETF reserved.
|
|
pub fn (a Ipv4Addr) is_reserved() bool {
|
|
return ipv4_reserved_network.contains(a)
|
|
}
|
|
|
|
// is_unspecified returns true if the address is unspecified i.e. equals 0.0.0.0.
|
|
pub fn (a Ipv4Addr) is_unspecified() bool {
|
|
return a.addr == [4]u8{}
|
|
}
|
|
|
|
// is_netmask returns true if IP address is network mask.
|
|
pub fn (a Ipv4Addr) is_netmask() bool {
|
|
intval := (a.u32() ^ max_u32) + 1
|
|
return intval & (intval - 1) == 0
|
|
}
|
|
|
|
// is_hostmask returns true if IP address is host mask.
|
|
pub fn (a Ipv4Addr) is_hostmask() bool {
|
|
return (a.u32() + 1) & a.u32() == 0
|
|
}
|
|
|
|
// < returns true if a is lesser than b.
|
|
pub fn (a Ipv4Addr) < (b Ipv4Addr) bool {
|
|
return a.u32() < b.u32()
|
|
}
|
|
|
|
// == returns true if a equals b.
|
|
pub fn (a Ipv4Addr) == (b Ipv4Addr) bool {
|
|
return a.addr == b.addr
|
|
}
|
|
|
|
@[params]
|
|
pub struct Ipv4ToIpv6Params {
|
|
pub:
|
|
kind Ipv6WithEmbeddedIpv4 = .mapped
|
|
}
|
|
|
|
// See RFC 4291 Section 2.5.5.
|
|
pub enum Ipv6WithEmbeddedIpv4 {
|
|
mapped // e.g. ::ffff:203.0.113.90
|
|
compat // e.g. ::203.0.113.90, deprecated per RFC 4291 Section 4
|
|
}
|
|
|
|
pub struct Ipv4Net {
|
|
pub:
|
|
network_address Ipv4Addr
|
|
network_mask Ipv4Addr
|
|
host_mask Ipv4Addr
|
|
broadcast_address Ipv4Addr
|
|
host_address ?Ipv4Addr
|
|
prefix_len int
|
|
mut:
|
|
current u32
|
|
}
|
|
|
|
// Ipv4Net.new creates new Ipv4Net from network *addr* with given *prefix* length.
|
|
pub fn Ipv4Net.new(addr Ipv4Addr, prefix int) !Ipv4Net {
|
|
if prefix < 0 || prefix > 32 {
|
|
return error('prefix length must be in range 0-32, not ${prefix}')
|
|
}
|
|
net_mask := max_u32 ^ (max_u32 >> prefix)
|
|
mut net_addr := addr
|
|
mut host_addr := ?Ipv4Addr(none)
|
|
if (net_addr.u32() & net_mask) != net_addr.u32() {
|
|
host_addr = Ipv4Addr{net_addr.u8_array_fixed()}
|
|
net_addr = Ipv4Addr.from_u32(net_addr.u32() & net_mask)
|
|
}
|
|
host_mask := net_mask ^ max_u32
|
|
broadcast := net_addr.u32() | host_mask
|
|
return Ipv4Net{
|
|
network_address: net_addr
|
|
network_mask: Ipv4Addr.from_u32(net_mask)
|
|
host_mask: Ipv4Addr.from_u32(host_mask)
|
|
broadcast_address: Ipv4Addr.from_u32(broadcast)
|
|
host_address: host_addr
|
|
prefix_len: prefix
|
|
current: net_addr.u32()
|
|
}
|
|
}
|
|
|
|
// Ipv4Net.from_string parses cidr and creates new Ipv4Net.
|
|
// Allowed formats are:
|
|
//
|
|
// * single IP address without prefix length, 32 is applied;
|
|
// * network address with non-negative integer prefix length e.g. 172.16.16.0/24;
|
|
// * network address with host mask: 172.16.16.0/0.0.0.255;
|
|
// * network address with network mask: 172.16.16.0/255.255.255.0.
|
|
//
|
|
// If prefix length is greather than 32 and host bits is set in the network address
|
|
// the optional `host_address` field will be filled with this host address.
|
|
// The `network_address` field always will contain the real network address.
|
|
pub fn Ipv4Net.from_string(cidr string) !Ipv4Net {
|
|
if cidr.is_blank() {
|
|
return error('network address cannot be blank')
|
|
}
|
|
mut net_addr_str, mut prefix_str := '', ''
|
|
cidr_parts := cidr.split_nth('/', 2)
|
|
if cidr_parts.len == 1 {
|
|
net_addr_str, prefix_str = cidr_parts[0], '32'
|
|
} else {
|
|
net_addr_str, prefix_str = cidr_parts[0], cidr_parts[1]
|
|
}
|
|
mut net_addr := Ipv4Addr.from_string(net_addr_str) or {
|
|
return error('invalid IPv4 address in ${cidr}')
|
|
}
|
|
mut prefix_len := 0
|
|
mut host_mask := Ipv4Addr{}
|
|
mut net_mask := Ipv4Addr.from_u32(max_u32)
|
|
mut host_addr := ?Ipv4Addr(none)
|
|
if prefix_u64 := prefix_str.parse_uint(10, 32) {
|
|
prefix_len = int(prefix_u64)
|
|
if prefix_len < 32 {
|
|
net_mask = Ipv4Addr.from_u32(max_u32 ^ (max_u32 >> u32(prefix_len)))
|
|
}
|
|
host_mask = Ipv4Addr.from_u32(net_mask.u32() ^ max_u32)
|
|
} else {
|
|
mut mask := Ipv4Addr.from_string(prefix_str) or {
|
|
return error('invalid prefix length in ${cidr}')
|
|
}
|
|
if mask.is_netmask() || mask.addr == [4]u8{} || mask.addr == [4]u8{init: 255} {
|
|
net_mask = mask
|
|
host_mask = Ipv4Addr.from_u32(mask.u32() ^ max_u32)
|
|
prefix_len = 32 - host_mask.bit_len()
|
|
} else if mask.is_hostmask() {
|
|
host_mask = mask
|
|
prefix_len = 32 - mask.bit_len()
|
|
if prefix_len < 32 {
|
|
net_mask = Ipv4Addr.from_u32(max_u32 ^ (max_u32 >> u32(prefix_len)))
|
|
}
|
|
} else {
|
|
return error('${mask} is not valid host or network mask in ${cidr}')
|
|
}
|
|
}
|
|
if (net_addr.u32() & net_mask.u32()) != net_addr.u32() {
|
|
host_addr = Ipv4Addr{net_addr.u8_array_fixed()}
|
|
net_addr = Ipv4Addr.from_u32(net_addr.u32() & net_mask.u32())
|
|
}
|
|
broadcast := Ipv4Addr.from_u32(net_addr.u32() | host_mask.u32())
|
|
return Ipv4Net{
|
|
network_address: net_addr
|
|
network_mask: net_mask
|
|
host_mask: host_mask
|
|
broadcast_address: broadcast
|
|
host_address: host_addr
|
|
prefix_len: prefix_len
|
|
current: net_addr.u32()
|
|
}
|
|
}
|
|
|
|
// Ipv4Net.from_u32 creates new Ipv4Net from network *addr* with given *prefix* length.
|
|
pub fn Ipv4Net.from_u32(addr u32, prefix int) !Ipv4Net {
|
|
if prefix < 0 || prefix > 32 {
|
|
return error('prefix length must be in range 0-32, not ${prefix}')
|
|
}
|
|
mut host_addr := ?Ipv4Addr(none)
|
|
mut net_addr := addr
|
|
net_mask := max_u32 ^ (max_u32 >> prefix)
|
|
if (net_addr & net_mask) != net_addr {
|
|
mut net_addr_bytes := [4]u8{}
|
|
binary.big_endian_put_u32_fixed(mut net_addr_bytes, net_addr)
|
|
host_addr = Ipv4Addr{net_addr_bytes}
|
|
net_addr &= net_mask
|
|
}
|
|
host_mask := net_mask ^ max_u32
|
|
broadcast := net_addr | host_mask
|
|
return Ipv4Net{
|
|
network_address: Ipv4Addr.from_u32(net_addr)
|
|
network_mask: Ipv4Addr.from_u32(net_mask)
|
|
host_mask: Ipv4Addr.from_u32(host_mask)
|
|
broadcast_address: Ipv4Addr.from_u32(broadcast)
|
|
host_address: host_addr
|
|
prefix_len: prefix
|
|
current: net_addr
|
|
}
|
|
}
|
|
|
|
// str returns string representation of IPv4 network in CIDR format.
|
|
pub fn (n Ipv4Net) str() string {
|
|
return n.format(.with_prefix_len)
|
|
}
|
|
|
|
// format returns the IPv4 network as a string formatted according to the fmt rule.
|
|
pub fn (n Ipv4Net) format(fmt Ipv4NetFormat) string {
|
|
match fmt {
|
|
.with_prefix_len {
|
|
return n.network_address.str() + '/' + n.prefix_len.str()
|
|
}
|
|
.with_host_mask {
|
|
return n.network_address.str() + '/' + n.host_mask.str()
|
|
}
|
|
.with_network_mask {
|
|
return n.network_address.str() + '/' + n.network_mask.str()
|
|
}
|
|
}
|
|
}
|
|
|
|
// capacity returns a total number of addresses in the network.
|
|
pub fn (n Ipv4Net) capacity() u64 {
|
|
return u64(n.broadcast_address.u32() - n.network_address.u32()) + 1
|
|
}
|
|
|
|
// next implements an iterator that iterates over all addresses in network
|
|
// including network and broadcast addresses.
|
|
// Example:
|
|
// ```
|
|
// network := netaddr.Ipv4Net.from_string('10.0.10.2/29')!
|
|
// for addr in network {
|
|
// println(addr)
|
|
// }
|
|
// ```
|
|
pub fn (mut n Ipv4Net) next() ?Ipv4Addr {
|
|
if n.current >= n.broadcast_address.u32() + 1 {
|
|
return none
|
|
}
|
|
defer {
|
|
n.current++
|
|
}
|
|
return Ipv4Addr.from_u32(n.current)
|
|
}
|
|
|
|
// first returns the first usable host address in network.
|
|
pub fn (n Ipv4Net) first() Ipv4Addr {
|
|
if n.prefix_len in [31, 32] {
|
|
return n.network_address
|
|
}
|
|
return Ipv4Addr.from_u32(n.network_address.u32() + 1)
|
|
}
|
|
|
|
// last returns the last usable host address in network.
|
|
pub fn (n Ipv4Net) last() Ipv4Addr {
|
|
if n.prefix_len in [31, 32] {
|
|
return n.broadcast_address
|
|
}
|
|
return Ipv4Addr.from_u32(n.broadcast_address.u32() - 1)
|
|
}
|
|
|
|
// nth returns the Nth address in network. Supports negative indexes.
|
|
pub fn (n Ipv4Net) nth(num i64) !Ipv4Addr {
|
|
mut addr := Ipv4Addr{}
|
|
if num >= 0 {
|
|
addr = Ipv4Addr.from_u32(n.network_address.u32() + u32(num))
|
|
} else {
|
|
addr = Ipv4Addr.from_u32(n.broadcast_address.u32() + u32(num + 1))
|
|
}
|
|
if n.contains(addr) {
|
|
return addr
|
|
}
|
|
return error('unable to get ${num}th address')
|
|
}
|
|
|
|
// contains returns true if IP address is in the network.
|
|
pub fn (n Ipv4Net) contains(addr Ipv4Addr) bool {
|
|
return n.network_address.u32() <= addr.u32() && addr.u32() <= n.broadcast_address.u32()
|
|
}
|
|
|
|
// overlaps returns true if network partly contains in *other*,
|
|
// in other words if the networks addresses sets intersect.
|
|
pub fn (n Ipv4Net) overlaps(other Ipv4Net) bool {
|
|
return other.contains(n.network_address) || (other.contains(n.broadcast_address)
|
|
|| (n.contains(other.network_address) || (n.contains(other.broadcast_address))))
|
|
}
|
|
|
|
// subnets returns iterator that iterates over the network subnets partitioned by given *prefix* length.
|
|
// Example:
|
|
// ```
|
|
// network := netaddr.Ipv4Net.from_string('198.51.100.0/24')!
|
|
// subnets := network.subnets(26)!
|
|
// for subnet in subnets {
|
|
// println(subnet)
|
|
// }
|
|
// ```
|
|
pub fn (n Ipv4Net) subnets(prefix int) !Ipv4NetsIterator {
|
|
if prefix > 32 || prefix < n.prefix_len {
|
|
return error('prefix length must be in range ${n.prefix_len}-32, not ${prefix}')
|
|
}
|
|
return Ipv4NetsIterator{
|
|
prefix_len: prefix
|
|
step: (n.host_mask.u32() + 1) >> (prefix - n.prefix_len)
|
|
end: n.broadcast_address.u32()
|
|
current: n.network_address.u32()
|
|
}
|
|
}
|
|
|
|
// supernet returns IPv4 network containing the current network.
|
|
pub fn (n Ipv4Net) supernet(prefix int) !Ipv4Net {
|
|
if prefix < 0 || prefix > n.prefix_len {
|
|
return error('prefix length must be in range 0-${n.prefix_len}, not ${prefix}')
|
|
}
|
|
if prefix == 0 {
|
|
return n
|
|
}
|
|
net_addr := n.network_address.u32() & (n.network_mask.u32() << (n.prefix_len - prefix))
|
|
return Ipv4Net.from_u32(net_addr, prefix)!
|
|
}
|
|
|
|
// is_subnet_of returns true if *other* contains the network.
|
|
pub fn (n Ipv4Net) is_subnet_of(other Ipv4Net) bool {
|
|
return other.network_address.u32() <= n.network_address.u32()
|
|
&& other.broadcast_address.u32() >= n.broadcast_address.u32()
|
|
}
|
|
|
|
// is_supernet_of returns true if the network contains *other*.
|
|
pub fn (n Ipv4Net) is_supernet_of(other Ipv4Net) bool {
|
|
return n.network_address.u32() <= other.network_address.u32()
|
|
&& n.broadcast_address.u32() >= other.broadcast_address.u32()
|
|
}
|
|
|
|
// is_link_local returns true if the network is link-local.
|
|
pub fn (n Ipv4Net) is_link_local() bool {
|
|
return n.network_address.is_link_local() && n.broadcast_address.is_link_local()
|
|
}
|
|
|
|
// is_loopback returns true if this is a loopback network.
|
|
pub fn (n Ipv4Net) is_loopback() bool {
|
|
return n.network_address.is_loopback() && n.broadcast_address.is_loopback()
|
|
}
|
|
|
|
// is_multicast returns true if the network is reserved for multicast use.
|
|
pub fn (n Ipv4Net) is_multicast() bool {
|
|
return n.network_address.is_multicast() && n.broadcast_address.is_multicast()
|
|
}
|
|
|
|
// is_unicast returns true if the network is unicast.
|
|
pub fn (n Ipv4Net) is_unicast() bool {
|
|
return !n.is_multicast()
|
|
}
|
|
|
|
// is_shared returns true if the network is in shared address space.
|
|
pub fn (n Ipv4Net) is_shared() bool {
|
|
return n.network_address.is_shared() && n.broadcast_address.is_shared()
|
|
}
|
|
|
|
// is_private returns true if the network is not globally reachable.
|
|
pub fn (n Ipv4Net) is_private() bool {
|
|
return n.network_address.is_private() && n.broadcast_address.is_private()
|
|
}
|
|
|
|
// is_global return true if the network is globally reachable.
|
|
pub fn (n Ipv4Net) is_global() bool {
|
|
return !n.is_private()
|
|
}
|
|
|
|
// is_reserved returns true if the network is IETF reserved.
|
|
pub fn (n Ipv4Net) is_reserved() bool {
|
|
return n.network_address.is_reserved() && n.broadcast_address.is_reserved()
|
|
}
|
|
|
|
// is_unspecified returns true if the network is 0.0.0.0/32.
|
|
pub fn (n Ipv4Net) is_unspecified() bool {
|
|
return n.network_address.is_unspecified() && n.broadcast_address.is_unspecified()
|
|
}
|
|
|
|
// < returns true if the network is lesser than other network.
|
|
pub fn (n Ipv4Net) < (other Ipv4Net) bool {
|
|
if n.network_address != other.network_address {
|
|
return n.network_address.u32() < other.network_address.u32()
|
|
}
|
|
if n.network_mask != other.network_mask {
|
|
return n.network_mask.u32() < other.network_mask.u32()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// == returns true if networks equals.
|
|
pub fn (n Ipv4Net) == (other Ipv4Net) bool {
|
|
return n.network_address == other.network_address && n.network_mask == n.network_mask
|
|
}
|
|
|
|
pub enum Ipv4NetFormat {
|
|
with_prefix_len // e.g. 198.51.100.0/24
|
|
with_host_mask // e.g. 198.51.100.0/0.0.0.255
|
|
with_network_mask // e.g. 198.51.100.0/255.255.255.0
|
|
}
|
|
|
|
pub struct Ipv4NetsIterator {
|
|
prefix_len int
|
|
step u32
|
|
end u32
|
|
mut:
|
|
current u32
|
|
}
|
|
|
|
// next implements the iterator interface for IP network subnets.
|
|
pub fn (mut iter Ipv4NetsIterator) next() ?Ipv4Net {
|
|
if iter.current >= iter.end + 1 {
|
|
return none
|
|
}
|
|
defer {
|
|
iter.current += iter.step
|
|
}
|
|
return Ipv4Net.from_u32(iter.current, iter.prefix_len)!
|
|
}
|