MscmpSystNetwork (mscmp_syst_network v0.1.0)
Simple IP address handling and convenience functionality.
IP addresses in Erlang, and by extension Elixir, are expressed as tuples with an element reserved for each segment of the IP address. This works well enough, but departs from the standard CIDR notation used by most professionals. In fact, the Erlang/Elixir standard library for dealing with IP addresses only deals with addresses and sockets; excluded are representations or utilities for dealing with subnets.
This Component aims to make it simpler to work with IP addresses, allowing for CIDR notation parsing and for the ability to recognize subnets.
Naive IP Address Handling
This library exists only to provide some ease in handling IP addresses in some common manipulation scenarios. However, it is a minimal implementation of the most common kinds of operations and not a full-fledged and authoritative implementation of IP addressing or networking standards.
This library may allow the developer to present standards-breaking scenarios to which to library will happily provide answer, albeit invalid. The handling of special scenarios such as multicast, anycast, standards defined exclusions or special uses, or similar simply doesn't exist.
Therefore this library should not be relied upon as a source of IP networking knowledge.
Prior Work
The MscmpSystNetwork
library was inspired by and is a re-working of Lambda, Inc.'s' and Isaac
Yonemoto's net_address
Elixir library. The Muse
Systems Business Management System originally used the net_address
library, but the library
appears to no longer be maintained and so is being replaced by MscmpSystNetwork
in the MuseBMS
project. MscmpSystNetwork
does use some code from net_address
. MscmpSystNetwork
offers
significantly less functionality than net_address
as much of that library's functionality is not
needed in the MuseBMS.
This library is also influenced by Bryan Weber's inet_cidr
Elixir library, though to a much lesser extent than net_address
.
In the end, much of the heavy lifting is done by the Erlang :inet
library.
Summary
Parsing
Parses common IP address and subnet text expressions from a string.
Parses common IP address and subnet text expressions from a string, raising an exception when there are errors.
Handles the ~i sigil for IP addresses.
Turns an Erlang :inet.ip_address/0
tuple into either a
MscmpSystNetwork.Types.IpV4.t/0
or MscmpSystNetwork.Types.IpV6.t/0
struct.
Protocol Functions
Retrieves IP address from IP address structs or nil
if the struct only
represents a subnet/prefix.
Retrieves the network masking bits (IPv4 subnet mask or IPv6 prefix).
Retrieves the network identifying portion of an IP address.
Evaluates an IP address struct to see if it represents a specific host or not.
Tests to see if an IP host or subnet is contained by a specific subnet.
Tests if an IP address host or subnet is contained by the given range.
Evaluates an IP address struct to see if it represents an entire network or subnet rather than a host.
Converts an IP address struct implementing the MscmpSystNetwork.Protocol
to
its common string representation using CIDR notation.
Parsing
parse(addr_string)
@spec parse(String.t()) :: {:ok, MscmpSystNetwork.Types.addr_structs()} | {:error, MscmpSystError.t()}
Parses common IP address and subnet text expressions from a string.
For simple IP addresses, the expected format of the string parameter is the common format for such addresses. For IPv4 addresses, this would be "dotted-decimal" form. For IPv6 addresses, the expected string uses a textual representation of the address that complies with RFC 5952.
For subnet addresses the expected format in the CIDR addressing style appropriate for either IPv4 or IPv6 .
Individual host addresses may also be expressed in CIDR notation. IPv4 host
addresses should use the standard "/32" designation for mask bits and IPv6
hosts in CIDR notation should use a prefix of "/128". These are the
traditional or standard expressions for hosts when CIDR notation is used in
this way. Note that even when the simple addressing format is used structs
will have a :mask
value of 32 for IPv4 or 128 for IPv6.
On a successful parse, an :ok
tuple is returning including either an
MscmpSystNetwork.Types.IpV4.t/0
or MscmpSystNetwork.Types.IpV6.t/0
value. If the parse fails,
Parameters
addr_string
- Either a simple IP host address or an IP address or IP subnet in CIDR notation. Passed as a string.
Examples
IPv4 addresses
iex> MscmpSystNetwork.parse("192.168.10.10")
{:ok, %MscmpSystNetwork.Types.IpV4{address: {192, 168, 10, 10}, mask: 32}}
iex> MscmpSystNetwork.parse("10.1.1.10/32")
{:ok, %MscmpSystNetwork.Types.IpV4{address: {10, 1, 1, 10}, mask: 32}}
iex> MscmpSystNetwork.parse("10.1.1.11/8")
{:ok, %MscmpSystNetwork.Types.IpV4{address: {10, 1, 1, 11}, mask: 8}}
iex> MscmpSystNetwork.parse("172.16.0.0/16")
{:ok, %MscmpSystNetwork.Types.IpV4{address: {172, 16, 0, 0}, mask: 16}}
IPv4 Error Example
iex> MscmpSystNetwork.parse("192.618.10.14/32")
{:error,
%MscmpSystError{
code: :undefined_error,
message: "Failure parsing IP address or subnet address string.",
cause: %MatchError{term: {:error, :einval}}
}}
IPv6 addresses
iex> MscmpSystNetwork.parse("fd9b:77f8:714d:cabb::1")
{
:ok,
%MscmpSystNetwork.Types.IpV6{
address: {64923, 30712, 29005, 51899, 0, 0, 0, 1},
mask: 128
}
}
iex> MscmpSystNetwork.parse("fd9b:77f8:714d:cabb::20/128")
{
:ok,
%MscmpSystNetwork.Types.IpV6{
address: {64923, 30712, 29005, 51899, 0, 0, 0, 32},
mask: 128
}
}
iex> MscmpSystNetwork.parse("fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
{
:ok,
%MscmpSystNetwork.Types.IpV6{
address: {64923, 30712, 29005, 51899, 0, 0, 43879, 18},
mask: 64
}
}
iex> MscmpSystNetwork.parse("fd9b:77f8:714d:cabb::/64")
{
:ok,
%MscmpSystNetwork.Types.IpV6{
address: {64923, 30712, 29005, 51899, 0, 0, 0, 0},
mask: 64
}
}
IPv6 Error Example
iex> MscmpSystNetwork.parse("fd9b:77f8:714d:qqqq::z")
{:error,
%MscmpSystError{
code: :undefined_error,
message: "Failure parsing IP address or subnet address string.",
cause: %MatchError{term: {:error, :einval}}
}}
parse!(addr_string)
@spec parse!(String.t()) :: MscmpSystNetwork.Types.addr_structs()
Parses common IP address and subnet text expressions from a string, raising an exception when there are errors.
Outside of the possibility of raising an exception, this function works the
same as parse/1
in all other ways.
Parameters
addr_string
- Either a simple IP host address or an IP address or IP subnet in CIDR notation. Passed as a string.
Examples
IPv4 addresses
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.parse!("192.168.10.10")
%MscmpSystNetwork.Types.IpV4{address: {192, 168, 10, 10}, mask: 32}
iex> MscmpSystNetwork.parse!("10.1.1.11/8")
%MscmpSystNetwork.Types.IpV4{address: {10, 1, 1, 11}, mask: 8}
IPv4 Error Example
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.parse!("192.618.10.14/32")
** (MatchError) no match of right hand side value: {:error, :einval}
IPv6 addresses
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.parse!("fd9b:77f8:714d:cabb::1")
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 0, 1}, mask: 128}
iex> MscmpSystNetwork.parse!("fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 43879, 18}, mask: 64}
IPv6 Error Example
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.parse!("fd9b:77f8:714d:qqqq::z")
** (MatchError) no match of right hand side value: {:error, :einval}
sigil_i(addr_string, modifiers)
@spec sigil_i(String.t(), list()) :: MscmpSystNetwork.Types.addr_structs()
Handles the ~i sigil for IP addresses.
Currently there are no modifiers accepted by this sigil. The sigil is simply
a convenience which calls parse!/1
.
Returns either a MscmpSystNetwork.Types.IpV4.t/0
or
MscmpSystNetwork.Types.IpV6.t/0
struct.
Parameters
addr_string
- Either a simple IP host address or an IP address or IP subnet in CIDR notation. Passed as a string.modifiers
- Currently there are no modifiers which are to be used with the sigil. This parameter is currently ignored.
Examples
IPv4 Addresses
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> ~i"192.168.10.10"
%MscmpSystNetwork.Types.IpV4{address: {192, 168, 10, 10}, mask: 32}
iex> ~i"10.1.1.11/8"
%MscmpSystNetwork.Types.IpV4{address: {10, 1, 1, 11}, mask: 8}
IPv4 Error Example
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> ~i"192.618.10.14/32"
** (MatchError) no match of right hand side value: {:error, :einval}
IPv6 Addresses
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> ~i"fd9b:77f8:714d:cabb::1"
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 0, 1}, mask: 128}
iex> ~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64"
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 43879, 18}, mask: 64}
IPv6 Error Example
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> ~i"fd9b:77f8:714d:qqqq::z"
** (MatchError) no match of right hand side value: {:error, :einval}
to_struct(addr, mask \\ nil)
@spec to_struct( MscmpSystNetwork.Types.ipv4_addr(), MscmpSystNetwork.Types.ipv4_mask() | nil ) :: MscmpSystNetwork.Types.IpV4.t()
@spec to_struct( MscmpSystNetwork.Types.ipv6_addr(), MscmpSystNetwork.Types.ipv6_mask() | nil ) :: MscmpSystNetwork.Types.IpV6.t()
Turns an Erlang :inet.ip_address/0
tuple into either a
MscmpSystNetwork.Types.IpV4.t/0
or MscmpSystNetwork.Types.IpV6.t/0
struct.
Raises on error.
Parameters
addr
- a tuple representing either the IPv4 or IPv6 address to be used in constructing the new struct.mask
- represents either the bit length of the IPv4 subnet mask or the IPv6 prefix. This parameter is optional and if not provided or is nil will default to the single host value as appropriate for theaddr
type.
Examples
IPv4 Examples
iex> MscmpSystNetwork.to_struct({10, 1, 1, 15})
%MscmpSystNetwork.Types.IpV4{address: {10, 1, 1, 15}, mask: 32}
iex> MscmpSystNetwork.to_struct({10, 1, 0, 0}, 16)
%MscmpSystNetwork.Types.IpV4{address: {10, 1, 0, 0}, mask: 16}
IPv6 Examples
iex> MscmpSystNetwork.to_struct({64923, 30712, 29005, 51899, 0, 0, 0, 1})
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 0, 1}, mask: 128}
iex> MscmpSystNetwork.to_struct({64923, 30712, 29005, 51899, 0, 0, 0, 0}, 64)
%MscmpSystNetwork.Types.IpV6{address: {64923, 30712, 29005, 51899, 0, 0, 0, 0}, mask: 64}
Protocol Functions
get_host(addr_struct)
@spec get_host(MscmpSystNetwork.Types.IpV4.t()) :: MscmpSystNetwork.Types.ipv4_addr() | nil
@spec get_host(MscmpSystNetwork.Types.IpV6.t()) :: MscmpSystNetwork.Types.ipv6_addr() | nil
Retrieves IP address from IP address structs or nil
if the struct only
represents a subnet/prefix.
Results are returned in the form of an Erlang :inet.ip_address/0
tuple or
nil
when the address passed to the function doesn't represent a host
address.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct from which a host IP address is to be extracted.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_host(~i"192.168.20.125/24")
{192, 168, 20, 125}
iex> MscmpSystNetwork.get_host(~i"10.0.0.0/8")
nil
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_host(~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
{64923, 30712, 29005, 51899, 0, 0, 43879, 18}
iex> MscmpSystNetwork.get_host(~i"fd9b:77f8:714d:cabb::/64")
nil
get_netmask(addr_struct)
@spec get_netmask(MscmpSystNetwork.Types.IpV4.t()) :: MscmpSystNetwork.Types.ipv4_addr()
@spec get_netmask(MscmpSystNetwork.Types.IpV6.t()) :: MscmpSystNetwork.Types.ipv6_addr()
Retrieves the network masking bits (IPv4 subnet mask or IPv6 prefix).
Results are returned in the form of an Erlang :inet.ip_address/0
tuple.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct from which a network mask should be extracted.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_netmask(~i"192.168.20.125/24")
{255, 255, 255, 0}
iex> MscmpSystNetwork.get_netmask(~i"10.1.1.12/32")
{255, 255, 255, 255}
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_netmask(~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
{65535, 65535, 65535, 65535, 0, 0, 0, 0}
iex> MscmpSystNetwork.get_netmask(~i"fd9b:77f8:714d:cabb::20/128")
{65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}
get_network(addr_struct)
@spec get_network(MscmpSystNetwork.Types.IpV4.t()) :: MscmpSystNetwork.Types.ipv4_addr() | nil
@spec get_network(MscmpSystNetwork.Types.IpV6.t()) :: MscmpSystNetwork.Types.ipv6_addr() | nil
Retrieves the network identifying portion of an IP address.
Results are returned in the form of an Erlang :inet.ip_address/0
tuple or
nil
when the address passed to the function represents a single host.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct from which a network address should extracted.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_network(~i"192.168.20.125/24")
{192, 168, 20, 0}
iex> MscmpSystNetwork.get_network(~i"10.1.1.12/32")
nil
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.get_network(~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
{64923, 30712, 29005, 51899, 0, 0, 0, 0}
iex> MscmpSystNetwork.get_network(~i"fd9b:77f8:714d:cabb::20/128")
nil
host?(addr_struct)
@spec host?(MscmpSystNetwork.Types.addr_structs()) :: boolean()
Evaluates an IP address struct to see if it represents a specific host or not.
If the function finds that an identifiable host is represented by the struct, the function returns true. Otherwise false.
IPv4 broadcast addresses will also return false.
Other Non-address IPs
Multicast addresses are currently not detected as will be treated as regular IP addresses. Therefore this function will return true for multicast addresses where it should return false.
Other examples of this sort of issue may exist for other, similar special cases. You should not assume that the IP address business logic in this Component is authoritative or complete. Test any special cases specifically for compliance with the area in which you are working.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct to test for representing a specific host.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.host?(~i"192.168.20.125/24")
true
iex> MscmpSystNetwork.host?(~i"10.0.0.0/8")
false
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.host?(~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
true
iex> MscmpSystNetwork.host?(~i"fd9b:77f8:714d:cabb::/64")
false
in_network?(test_addr, network_addr)
@spec in_network?(MscmpSystNetwork.Types.IpV4.t(), MscmpSystNetwork.Types.IpV4.t()) :: boolean()
@spec in_network?(MscmpSystNetwork.Types.IpV6.t(), MscmpSystNetwork.Types.IpV6.t()) :: boolean()
Tests to see if an IP host or subnet is contained by a specific subnet.
True is returned when the test address is contained by the given network, otherwise false is returned. Any error raises an exception.
Parameters
test_addr
- any valid IP address struct. This struct can represent an individual host or a subnet.network_addr
- a valid IP address struct which only represents a network. Host addresses from which a network can be extracted such as10.1.1.113/24
will still cause an exception to be raised; only network only addresses such as10.1.1.0/24
are accepted. SeeMscmpSystNetwork.get_network/1
for extracting a network address from a host address with an identifiable network.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.in_network?(~i"10.1.1.10", ~i"10.1.0.0/16")
true
iex> MscmpSystNetwork.in_network?(~i"10.1.1.0/24", ~i"10.1.0.0/16")
true
iex> MscmpSystNetwork.in_network?(~i"10.1.1.0/24", ~i"10.1.1.0/24")
true
iex> MscmpSystNetwork.in_network?(~i"10.2.1.1/32", ~i"10.1.0.0/16")
false
IPv6 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.in_network?(
...> ~i"fd9b:77f8:714d:cabb::20/128", ~i"fd9b:77f8:714d:cabb::/64")
true
iex> MscmpSystNetwork.in_network?(
...> ~i"fd9b:77f8:714d:cabb:ab67::/68", ~i"fd9b:77f8:714d:cabb::/64")
true
iex> MscmpSystNetwork.in_network?(
...> ~i"fd9b:77f8:714d:cabb::/64", ~i"fd9b:77f8:714d:cabb::/64")
true
iex> MscmpSystNetwork.in_network?(
...> ~i"fd9b:77f8:714d:caab::20/128", ~i"fd9b:77f8:714d:cabb::/64")
false
in_range?(test_addr, low_addr, high_addr)
@spec in_range?( MscmpSystNetwork.Types.IpV4.t(), MscmpSystNetwork.Types.IpV4.t(), MscmpSystNetwork.Types.IpV4.t() ) :: boolean()
@spec in_range?( MscmpSystNetwork.Types.IpV6.t(), MscmpSystNetwork.Types.IpV6.t(), MscmpSystNetwork.Types.IpV6.t() ) :: boolean()
Tests if an IP address host or subnet is contained by the given range.
True is returned when the IP address is contained, otherwise false. Errors raise exceptions.
Parameters
test_addr
- a valid IP address struct which must be fully contained by the low and high IP addresses to obtain a true result.low_addr
- The low address of the range. If thelow_addr
value is a struct identifying a host, but from which a network could be extracted, it is treated as a host only. If the struct represents only a network or subnet, the network IP address itself is treated as the lowest IP address in the range; for example10.1.0.0/16
will treat10.1.0.0
as the lowest IP address in the range.high_addr
- the high address of the range. If thehigh_addr
value is a struct identifying a host, but from which a network could be extracted, it is treated as a host only. If the struct represents only a network or subnet, the network's largest possible IP address is considered the high address of the range; for example10.1.0.0/16
would consider10.1.255.255
as the highest address in the range.
The range boundaries are considered inclusive at both extremes.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.in_range?(~i"10.1.1.10", ~i"10.1.1.1", ~i"10.1.1.15")
true
iex> MscmpSystNetwork.in_range?(~i"10.1.1.0/24", ~i"10.1.0.0/16", ~i"10.1.2.254")
true
iex> MscmpSystNetwork.in_range?(~i"10.1.1.0/24", ~i"10.1.1.0/24", ~i"10.1.1.0/24")
true
iex> MscmpSystNetwork.in_range?(~i"10.3.1.1/32", ~i"10.1.0.0/16", ~i"10.2.0.0/16")
false
IPv6 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.in_range?(
...> ~i"fd9b:77f8:714d:cabb::20",
...> ~i"fd9b:77f8:714d:cabb::10",
...> ~i"fd9b:77f8:714d:cabb::30")
true
iex> MscmpSystNetwork.in_range?(
...> ~i"fd9b:77f8:714d:cabb:ab67::/68",
...> ~i"fd9b:77f8:714d:cabb::/64",
...> ~i"fd9b:77f8:714d:cabd::")
true
iex> MscmpSystNetwork.in_range?(
...> ~i"fd9b:77f8:714d:cabb::/64",
...> ~i"fd9b:77f8:714d:cabb::/64",
...> ~i"fd9b:77f8:714d:cabb::/64")
true
iex> MscmpSystNetwork.in_range?(
...> ~i"fd9b:77f8:714e:caab::20/128",
...> ~i"fd9b:77f8:714d:caba::/64",
...> ~i"fd9b:77f8:714d:cabc::/64")
false
network?(addr_struct)
@spec network?(MscmpSystNetwork.Types.addr_structs()) :: boolean()
Evaluates an IP address struct to see if it represents an entire network or subnet rather than a host.
This function only returns true when the supplied IP address represents only a network or subnet. False is returned when the provided IP address struct also contains an identifiable host or is only a host. False is even returned in cases where the CIDR notation would allow a network to be extracted from the provided IP address struct.
Other Non-address IPs
Multicast addresses are currently not detected as will be treated as regular IP addresses. Therefore this function will return true for multicast addresses where it should return false.
You should not assume that the IP address business logic in this Component is authoritative or complete. Test any special cases specifically for compliance with the area in which you are working.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct which to test as identifying a network exclusively.
Examples
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.network?(~i"192.168.20.125/24")
false
iex> MscmpSystNetwork.network?(~i"10.0.0.0/8")
true
IPv4 Examples
iex> import MscmpSystNetwork, only: [sigil_i: 2]
iex> MscmpSystNetwork.network?(~i"fd9b:77f8:714d:cabb:0000:0000:ab67:12/64")
false
iex> MscmpSystNetwork.network?(~i"fd9b:77f8:714d:cabb::/64")
true
to_string(addr_struct)
@spec to_string(MscmpSystNetwork.Types.addr_structs()) :: String.t()
Converts an IP address struct implementing the MscmpSystNetwork.Protocol
to
its common string representation using CIDR notation.
Parameters
addr_struct
- Either a validMscmpSystNetwork.Types.IpV4.t/0
orMscmpSystNetwork.Types.IpV6.t/0
struct which is to be converted to a string.
Examples
IPv4 Addresses
iex> my_host = %MscmpSystNetwork.Types.IpV4{address: {192, 168, 10, 10}, mask: 32}
iex> MscmpSystNetwork.to_string(my_host)
"192.168.10.10/32"
iex> my_subnet = %MscmpSystNetwork.Types.IpV4{address: {172, 16, 0, 0}, mask: 16}
iex> MscmpSystNetwork.to_string(my_subnet)
"172.16.0.0/16"
IPv6 Addresses
iex> my_host =
...> %MscmpSystNetwork.Types.IpV6{
...> address: {64923, 30712, 29005, 51899, 0, 0, 0, 1},
...> mask: 128
...> }
iex> MscmpSystNetwork.to_string(my_host)
"fd9b:77f8:714d:cabb::1/128"
iex> my_subnet =
...> %MscmpSystNetwork.Types.IpV6{
...> address: {64923, 30712, 29005, 51899, 0, 0, 43879, 18},
...> mask: 64
...> }
iex> MscmpSystNetwork.to_string(my_subnet)
"fd9b:77f8:714d:cabb::ab67:12/64"