luci-lib-ip: add MAC address calculation support

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2018-03-12 08:18:11 +01:00
parent 190b32452a
commit 4f412a76c2
4 changed files with 1232 additions and 268 deletions

View file

@ -256,6 +256,30 @@ Checks whether the CIDR instance is an IPv6 link local address
<td class="summary">
Checks whether the CIDR instance is an IPv6 mapped IPv4 address
</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.ismac">cidr:ismac</a>&nbsp;()</td>
<td class="summary">
Checks whether the CIDR instance is an ethernet MAC address range
</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.ismaclocal">cidr:ismaclocal</a>&nbsp;()</td>
<td class="summary">
Checks whether the CIDR instance is a locally administered (LAA) MAC address
</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.ismacmcast">cidr:ismacmcast</a>&nbsp;()</td>
<td class="summary">
Checks whether the CIDR instance is a multicast MAC address
</td>
</tr>
@ -322,6 +346,20 @@ Derive broadcast address of CIDR instance.</td>
Derive mapped IPv4 address of CIDR instance.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.tomac">cidr:tomac</a>&nbsp;()</td>
<td class="summary">
Derive MAC address of IPv6 link local CIDR instance.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.tolinklocal">cidr:tolinklocal</a>&nbsp;()</td>
<td class="summary">
Derive IPv6 link local address from MAC address CIDR instance.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#cidr.contains">cidr:contains</a>&nbsp;(addr)</td>
<td class="summary">
@ -405,6 +443,10 @@ Checks whether the CIDR instance is an IPv4 address range
cidr:is6
</a>
<li><a href="#cidr.ismac">
cidr:ismac
</a>
</ul>
</dd>
@ -499,6 +541,10 @@ Checks whether the CIDR instance is an IPv6 address range
cidr:is4
</a>
<li><a href="#cidr.ismac">
cidr:ismac
</a>
</ul>
</dd>
@ -566,13 +612,108 @@ end</pre>
<dt><a name="cidr.ismac"></a><strong>cidr:ismac</strong>&nbsp;()</dt>
<dd>
Checks whether the CIDR instance is an ethernet MAC address range
<h3>Return value:</h3>
<code>true</code> if the CIDR is a MAC address range, else <code>false</code>
<h3>See also:</h3>
<ul>
<li><a href="#cidr.is4">
cidr:is4
</a>
<li><a href="#cidr.is6">
cidr:is6
</a>
</ul>
</dd>
<dt><a name="cidr.ismaclocal"></a><strong>cidr:ismaclocal</strong>&nbsp;()</dt>
<dd>
Checks whether the CIDR instance is a locally administered (LAA) MAC address
<h3>Usage:</h3>
<pre>local mac = luci.ip.new("02:C0:FF:EE:00:01")
if mac:ismaclocal() then
print("Is an LAA MAC address")
end</pre>
<h3>Return value:</h3>
<code>true</code> if the MAC address sets the locally administered bit.
</dd>
<dt><a name="cidr.ismacmcast"></a><strong>cidr:ismacmcast</strong>&nbsp;()</dt>
<dd>
Checks whether the CIDR instance is a multicast MAC address
<h3>Usage:</h3>
<pre>local mac = luci.ip.new("01:00:5E:7F:00:10")
if addr:ismacmcast() then
print("Is a multicast MAC address")
end</pre>
<h3>Return value:</h3>
<code>true</code> if the MAC address sets the multicast bit.
</dd>
<dt><a name="cidr.lower"></a><strong>cidr:lower</strong>&nbsp;(addr)</dt>
<dd>
Checks whether this CIDR instance is lower than the given argument.
The comparisation follows these rules:
<ul><li>An IPv4 address is always lower than an IPv6 address</li>
<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@ -595,7 +736,8 @@ The comparisation follows these rules:
print(addr:lower(addr)) -- false
print(addr:lower("10.10.10.10/24")) -- false
print(addr:lower(luci.ip.new("::1"))) -- true
print(addr:lower(luci.ip.new("192.168.200.1"))) -- true</pre>
print(addr:lower(luci.ip.new("192.168.200.1"))) -- true
print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true</pre>
@ -629,7 +771,8 @@ print(addr:lower(luci.ip.new("192.168.200.1"))) -- true</pre>
Checks whether this CIDR instance is higher than the given argument.
The comparisation follows these rules:
<ul><li>An IPv4 address is always lower than an IPv6 address</li>
<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@ -652,7 +795,8 @@ The comparisation follows these rules:
print(addr:higher(addr)) -- false
print(addr:higher("10.10.10.10/24")) -- true
print(addr:higher(luci.ip.new("::1"))) -- false
print(addr:higher(luci.ip.new("192.168.200.1"))) -- false</pre>
print(addr:higher(luci.ip.new("192.168.200.1"))) -- false
print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false</pre>
@ -709,7 +853,11 @@ print(addr:equal(luci.ip.new("::1"))) -- false
local addr6 = luci.ip.new("::1")
print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true
print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false</pre>
print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:equal("0:14:22:1:23:45")) -- true
print(mac:equal(luci.ip.new("01:23:45:67:89:AB")) -- false</pre>
@ -752,8 +900,8 @@ else the current prefix size is returned.
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
netmask (optional)
for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
containing a valid netmask (optional)
</li>
</ul>
@ -800,8 +948,8 @@ optional mask parameter.
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
netmask (optional)
for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
containing a valid netmask (optional)
</li>
</ul>
@ -837,7 +985,7 @@ CIDR instance representing the network address
Derive host address of CIDR instance.
This function essentially constructs a copy of this CIDR with the prefix size
set to <code>32</code> for IPv4 and <code>128</code> for IPv6.
set to <code>32</code> for IPv4, <code>128</code> for IPv6 or <code>48</code> for MAC addresses.
@ -877,8 +1025,8 @@ prefix size can be overridden by the optional mask parameter.
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
netmask (optional)
for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
containing a valid netmask (optional)
</li>
</ul>
@ -913,8 +1061,8 @@ Derive broadcast address of CIDR instance.
Constructs a CIDR instance representing the broadcast address of this instance.
The used prefix size can be overridden by the optional mask parameter.
This function has no effect on IPv6 instances, it will return nothing in this
case.
This function has no effect on IPv6 or MAC address instances, it will return
nothing in this case.
@ -922,9 +1070,8 @@ case.
<ul>
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
netmask (optional)
mask: Either a number containing the number of bits (<code>0..32</code> for IPv4) or
a string containing a valid netmask (optional)
</li>
</ul>
@ -960,8 +1107,8 @@ Derive mapped IPv4 address of CIDR instance.
Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped
IPv4 address in this instance.
This function has no effect on IPv4 instances or IPv6 instances which are not a
mapped address, it will return nothing in this case.
This function has no effect on IPv4 instances, MAC address instances or IPv6
instances which are not a mapped address, it will return nothing in this case.
@ -985,6 +1132,74 @@ Return a new CIDR instance representing the IPv4 address if this
<dt><a name="cidr.tomac"></a><strong>cidr:tomac</strong>&nbsp;()</dt>
<dd>
Derive MAC address of IPv6 link local CIDR instance.
Constructs a CIDR instance representing the MAC address contained in the IPv6
link local address of this instance.
This function has no effect on IPv4 instances, MAC address instances or IPv6
instances which are not a link local address, it will return nothing in this
case.
<h3>Usage:</h3>
<pre>local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9")
print(addr:tomac()) -- "64:66:B3:47:E1:B9"</pre>
<h3>Return value:</h3>
Return a new CIDR instance representing the MAC address if this
instance is an IPv6 link local address, else return nothing.
</dd>
<dt><a name="cidr.tolinklocal"></a><strong>cidr:tolinklocal</strong>&nbsp;()</dt>
<dd>
Derive IPv6 link local address from MAC address CIDR instance.
Constructs a CIDR instance representing the IPv6 link local address of the
MAC address represented by this instance.
This function has no effect on IPv4 instances or IPv6 instances, it will return
nothing in this case.
<h3>Usage:</h3>
<pre>local mac = luci.ip.new("64:66:B3:47:E1:B9")
print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"</pre>
<h3>Return value:</h3>
Return a new CIDR instance representing the IPv6 link local address.
</dd>
<dt><a name="cidr.contains"></a><strong>cidr:contains</strong>&nbsp;(addr)</dt>
<dd>
@ -1014,7 +1229,11 @@ print(range:contains("10.0.0.0/8")) -- false
local range6 = luci.ip.new("fe80::/10")
print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true
print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false</pre>
print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false
local intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
print(intel_macs:contains("C0:B6:F9:A3:C:11")) -- true
print(intel_macs:contains("64:66:B3:47:E1:B9")) -- false</pre>
@ -1059,30 +1278,40 @@ address space, the result is set to the highest possible address.
<h3>Usage:</h3>
<pre>local addr = luci.ip.new("192.168.1.1/24")
print(addr:add(250)) -- "192.168.1.251/24"
print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
print(addr:add(250)) -- "192.168.1.251/24"
print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
addr:add(256, true) -- true
print(addr) -- "192.168.2.1/24
addr:add(256, true) -- true
print(addr) -- "192.168.2.1/24
addr:add("255.0.0.0", true) -- false (overflow)
print(addr) -- "255.255.255.255/24
addr:add("255.0.0.0", true) -- false (overflow)
print(addr) -- "255.255.255.255/24
local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
addr:add(256, true) -- true
print(addr) -- "fe80::221:63f:fe75:ab17/64
addr6:add(256, true) -- true
print(addr6) -- "fe80::221:63f:fe75:ab17/64
addr:add("ffff::", true) -- false (overflow)
print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"</pre>
addr6:add("ffff::", true) -- false (overflow)
print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:add(256)) -- "00:14:22:01:24:45"
print(mac:add("0:0:0:0:FF:0") -- "00:14:22:02:22:45"
mac:add(256, true) -- true
print(mac) -- "00:14:22:01:24:45"
mac:add("FF:FF:0:0:0:0", true) -- false (overflow)
print(mac) -- "FF:FF:FF:FF:FF:FF"</pre>
<h3>Return value:</h3>
<ul>
<li>When adding inplace: Return <code>true</code> if the addition succeeded
<li>When adding inplace: Return <code>true</code> if the addition succeded
or <code>false</code> when the addition overflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
this instance plus the added amount or the highest possible address if
@ -1099,7 +1328,7 @@ print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"</pr
<dd>
Subtract given amount from CIDR instance. If the result would under, the lowest
Subtract given amount from CIDR instance. If the result would under, the lowest
possible address is returned.
@ -1142,16 +1371,26 @@ addr:sub(256, true) -- true
print(addr) -- "fe80::221:63f:fe75:a917/64"
addr:sub("ffff::", true) -- false (underflow)
print(addr) -- "::/64"</pre>
print(addr) -- "::/64"
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:sub(256)) -- "00:14:22:01:22:45"
print(mac:sub("0:0:0:0:FF:0") -- "00:14:22:00:24:45"
mac:sub(256, true) -- true
print(mac) -- "00:14:22:01:22:45"
mac:sub("FF:FF:0:0:0:0", true) -- false (overflow)
print(mac) -- "00:00:00:00:00:00"</pre>
<h3>Return value:</h3>
<ul>
<li>When subtracting inplace: Return <code>true</code> if the subtraction
succeeded or <code>false</code> when the subtraction underflowed.</li>
<li>When subtracting inplace: Return <code>true</code> if the subtraction
succeeded or <code>false</code> when the subtraction underflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
this instance minus the subtracted amount or the lowest address if
this instance minus the subtracted amount or the lowest address if
the subtraction underflowed.</li></ul>
@ -1177,7 +1416,10 @@ Calculate the lowest possible host address within this CIDR instance.
print(addr:minhost()) -- "192.168.123.1"
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"</pre>
print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"
local mac = luci.ip.new("00:14:22:01:22:45/32")
print(mac:minhost()) -- "00:14:22:01:00:01"</pre>
@ -1208,7 +1450,10 @@ Calculate the highest possible host address within this CIDR instance.
print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast)
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"</pre>
print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"
local mac = luci.ip.new("00:14:22:01:22:45/32")
print(mac:maxhost()) -- "00:14:22:01:FF:FF"</pre>
@ -1229,8 +1474,9 @@ Returns a new CIDR instance representing the highest host address
Convert CIDR instance into string representation.
If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the
address is returned in the form "address/prefix" otherwise just "address".
If the prefix size of instance is less than 32 for IPv4, 128 for IPv6 or 48 for
MACs, the address is returned in the form "address/prefix" otherwise just
"address".
It is usually not required to call this function directly as CIDR objects
define it as __tostring function in the associated metatable.

View file

@ -231,6 +231,34 @@ Construct a new IPv4 luci.ip.cidr instance.</td>
Construct a new IPv6 luci.ip.cidr instance.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#MAC">MAC</a>&nbsp;(address, netmask)</td>
<td class="summary">
Construct a new MAC luci.ip.cidr instance.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#checkip4">checkip4</a>&nbsp;(address)</td>
<td class="summary">
Verify an IPv4 address.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#checkip6">checkip6</a>&nbsp;(address)</td>
<td class="summary">
Verify an IPv6 address.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#checkmac">checkmac</a>&nbsp;(address)</td>
<td class="summary">
Verify an ethernet MAC address.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#route">route</a>&nbsp;(address)</td>
<td class="summary">
@ -334,6 +362,10 @@ address/mask range.
IPv6
</a>
<li><a href="#MAC">
MAC
</a>
</ul>
</dd>
@ -389,6 +421,10 @@ A <code>luci.ip.cidr</code> object representing the given IPv4 range.
IPv6
</a>
<li><a href="#MAC">
MAC
</a>
</ul>
</dd>
@ -444,6 +480,252 @@ A <code>luci.ip.cidr</code> object representing the given IPv6 range.
IPv4
</a>
<li><a href="#MAC">
MAC
</a>
</ul>
</dd>
<dt><a name="MAC"></a><strong>MAC</strong>&nbsp;(address, netmask)</dt>
<dd>
Construct a new MAC luci.ip.cidr instance.
Throws an error if the given string does not represent a valid ethernet MAC
address or if the given optional mask is of a different family.
<h3>Parameters</h3>
<ul>
<li>
address: String containing a valid ethernet MAC address, optionally with
prefix size (CIDR notation) or mask separated by slash.
</li>
<li>
netmask: String containing a valid MAC address mask or number
containing a prefix size between <code>0</code> and <code>48</code> bit.
Overrides mask embedded in the first argument if specified. (optional)
</li>
</ul>
<h3>Usage:</h3>
<pre>intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/FF:FF:FF:0:0:0")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00", "FF:FF:FF:0:0:0")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24", 48) -- override mask</pre>
<h3>Return value:</h3>
A <code>luci.ip.cidr</code> object representing the given MAC address range.
<h3>See also:</h3>
<ul>
<li><a href="#IPv4">
IPv4
</a>
<li><a href="#IPv6">
IPv6
</a>
</ul>
</dd>
<dt><a name="checkip4"></a><strong>checkip4</strong>&nbsp;(address)</dt>
<dd>
Verify an IPv4 address.
Checks whether given argument is a preexisting luci.ip.cidr IPv4 address
instance or a string literal convertible to an IPv4 address and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
<h3>Parameters</h3>
<ul>
<li>
address: String containing a valid IPv4 address or existing
luci.ip.cidr IPv4 instance.
</li>
</ul>
<h3>Usage:</h3>
<pre>ipv4 = luci.ip.checkip4(luci.ip.new("127.0.0.1")) -- "127.0.0.1"
ipv4 = luci.ip.checkip4("127.0.0.1") -- "127.0.0.1"
ipv4 = luci.ip.checkip4("nonesense") -- nothing
ipv4 = luci.ip.checkip4(123) -- nothing
ipv4 = luci.ip.checkip4(nil) -- nothing
ipv4 = luci.ip.checkip4() -- nothing</pre>
<h3>Return value:</h3>
A string representing the given IPv4 address.
<h3>See also:</h3>
<ul>
<li><a href="#checkip6">
checkip6
</a>
<li><a href="#checkmac">
checkmac
</a>
</ul>
</dd>
<dt><a name="checkip6"></a><strong>checkip6</strong>&nbsp;(address)</dt>
<dd>
Verify an IPv6 address.
Checks whether given argument is a preexisting luci.ip.cidr IPv6 address
instance or a string literal convertible to an IPv6 address and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
<h3>Parameters</h3>
<ul>
<li>
address: String containing a valid IPv6 address or existing
luci.ip.cidr IPv6 instance.
</li>
</ul>
<h3>Usage:</h3>
<pre>ipv6 = luci.ip.checkip6(luci.ip.new("0:0:0:0:0:0:0:1")) -- "::1"
ipv6 = luci.ip.checkip6("0:0:0:0:0:0:0:1") -- "::1"
ipv6 = luci.ip.checkip6("nonesense") -- nothing
ipv6 = luci.ip.checkip6(123) -- nothing
ipv6 = luci.ip.checkip6(nil) -- nothing
ipv6 = luci.ip.checkip6() -- nothing</pre>
<h3>Return value:</h3>
A string representing the given IPv6 address.
<h3>See also:</h3>
<ul>
<li><a href="#checkip4">
checkip4
</a>
<li><a href="#checkmac">
checkmac
</a>
</ul>
</dd>
<dt><a name="checkmac"></a><strong>checkmac</strong>&nbsp;(address)</dt>
<dd>
Verify an ethernet MAC address.
Checks whether given argument is a preexisting luci.ip.cidr MAC address
instance or a string literal convertible to an ethernet MAC and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
<h3>Parameters</h3>
<ul>
<li>
address: String containing a valid MAC address or existing luci.ip.cidr
MAC address instance.
</li>
</ul>
<h3>Usage:</h3>
<pre>mac = luci.ip.checkmac(luci.ip.new("00-11-22-cc-dd-ee")) -- "00:11:22:CC:DD:EE"
mac = luci.ip.checkmac("00:11:22:cc:dd:ee") -- "00:11:22:CC:DD:EE"
mac = luci.ip.checkmac("nonesense") -- nothing
mac = luci.ip.checkmac(123) -- nothing
mac = luci.ip.checkmac(nil) -- nothing
mac = luci.ip.checkmac() -- nothing</pre>
<h3>Return value:</h3>
A string representing the given MAC address.
<h3>See also:</h3>
<ul>
<li><a href="#checkip4">
checkip4
</a>
<li><a href="#checkip6">
checkip6
</a>
</ul>
</dd>
@ -787,7 +1069,7 @@ A neighbour entry is a table containing the following fields:
</tr>
<tr>
<td><code>mac</code></td>
<td>String containing the associated MAC address</td>
<td>MAC address <code>luci.ip.cidr</code> instance</td>
</tr>
<tr>
<td><code>router</code></td>
@ -905,8 +1187,8 @@ described below is returned, else an empty table.
</tr>
<tr>
<td><code>mac</code></td>
<td>String containing the link local address of the device in
dotted hex notation</td>
<td>MAC address <code>luci.ip.cidr</code> instance representing the device ethernet
address</td>
</tr>
</table>

View file

@ -1,5 +1,5 @@
/*
Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
Copyright 2015-2018 Jo-Philipp Wich <jo@mein.io>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -42,6 +42,16 @@ limitations under the License.
#define RTA_INT(x) (*(int *)RTA_DATA(x))
#define RTA_U32(x) (*(uint32_t *)RTA_DATA(x))
#define AF_BITS(f) \
((f) == AF_INET ? 32 : \
((f) == AF_INET6 ? 128 : \
((f) == AF_PACKET ? 48 : 0)))
#define AF_BYTES(f) \
((f) == AF_INET ? 4 : \
((f) == AF_INET6 ? 16 : \
((f) == AF_PACKET ? 6 : 0)))
static int hz = 0;
static struct nl_sock *sock = NULL;
@ -49,11 +59,11 @@ typedef struct {
union {
struct in_addr v4;
struct in6_addr v6;
struct ether_addr mac;
uint8_t u8[16];
} addr;
int len;
int bits;
int family;
bool exact;
uint16_t family;
int16_t bits;
} cidr_t;
struct dump_filter {
@ -70,6 +80,8 @@ struct dump_filter {
cidr_t src;
cidr_t dst;
struct ether_addr mac;
bool from_exact;
bool dst_exact;
};
struct dump_state {
@ -95,29 +107,68 @@ static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p)
return NULL;
}
static bool parse_mask(int family, const char *mask, int *bits)
static bool parse_mac(const char *mac, struct ether_addr *ea)
{
unsigned long int n;
char *e, sep = 0;
int i;
for (i = 0; i < 6; i++)
{
if (i > 0)
{
if (sep == 0 && (mac[0] == ':' || mac[0] == '-'))
sep = mac[0];
if (sep == 0 || mac[0] != sep)
return false;
mac++;
}
n = strtoul(mac, &e, 16);
if (n > 0xFF)
return false;
mac += (e - mac);
ea->ether_addr_octet[i] = n;
}
if (mac[0] != 0)
return false;
return true;
}
static bool parse_mask(int family, const char *mask, int16_t *bits)
{
char *e;
struct in_addr m;
struct in6_addr m6;
union {
struct in_addr v4;
struct in6_addr v6;
struct ether_addr mac;
uint8_t u8[16];
} m;
if (family == AF_INET && inet_pton(AF_INET, mask, &m))
if (family == AF_INET && inet_pton(AF_INET, mask, &m.v4))
{
for (*bits = 0, m.s_addr = ntohl(m.s_addr);
*bits < 32 && (m.s_addr << *bits) & 0x80000000;
for (*bits = 0, m.v4.s_addr = ntohl(m.v4.s_addr);
*bits < AF_BITS(AF_INET) && (m.v4.s_addr << *bits) & 0x80000000;
++*bits);
}
else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6))
else if ((family == AF_INET6 && inet_pton(AF_INET6, mask, &m.v6)) ||
(family == AF_PACKET && parse_mac(mask, &m.mac)))
{
for (*bits = 0;
*bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128;
*bits < AF_BITS(family) && (m.u8[*bits / 8] << (*bits % 8)) & 128;
++*bits);
}
else
{
*bits = strtoul(mask, &e, 10);
if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128))
if (e == mask || *e != 0 || *bits > AF_BITS(family))
return false;
}
@ -127,7 +178,6 @@ static bool parse_mask(int family, const char *mask, int *bits)
static bool parse_cidr(const char *dest, cidr_t *pp)
{
char *p, buf[INET6_ADDRSTRLEN * 2 + 2];
uint8_t bitlen = 0;
strncpy(buf, dest, sizeof(buf) - 1);
@ -137,17 +187,11 @@ static bool parse_cidr(const char *dest, cidr_t *pp)
*p++ = 0;
if (inet_pton(AF_INET, buf, &pp->addr.v4))
{
bitlen = 32;
pp->family = AF_INET;
pp->len = sizeof(struct in_addr);
}
else if (inet_pton(AF_INET6, buf, &pp->addr.v6))
{
bitlen = 128;
pp->family = AF_INET6;
pp->len = sizeof(struct in6_addr);
}
else if (parse_mac(buf, &pp->addr.mac))
pp->family = AF_PACKET;
else
return false;
@ -158,12 +202,45 @@ static bool parse_cidr(const char *dest, cidr_t *pp)
}
else
{
pp->bits = bitlen;
pp->bits = AF_BITS(pp->family);
}
return true;
}
static int format_cidr(lua_State *L, cidr_t *p)
{
char buf[INET6_ADDRSTRLEN];
if (p->family == AF_PACKET)
{
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
p->addr.mac.ether_addr_octet[0],
p->addr.mac.ether_addr_octet[1],
p->addr.mac.ether_addr_octet[2],
p->addr.mac.ether_addr_octet[3],
p->addr.mac.ether_addr_octet[4],
p->addr.mac.ether_addr_octet[5]);
if (p->bits < AF_BITS(AF_PACKET))
lua_pushfstring(L, "%s/%d", buf, p->bits);
else
lua_pushstring(L, buf);
}
else
{
if (p->bits < AF_BITS(p->family))
lua_pushfstring(L, "%s/%d",
inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)),
p->bits);
else
lua_pushstring(L,
inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)));
}
return 1;
}
static int L_getint(lua_State *L, int index, const char *name)
{
int rv = 0;
@ -220,16 +297,20 @@ static void L_setaddr(struct lua_State *L, const char *name,
if (family == AF_INET)
{
p->family = AF_INET;
p->bits = (bits < 0) ? 32 : bits;
p->len = sizeof(p->addr.v4);
p->bits = (bits < 0) ? AF_BITS(AF_INET) : bits;
p->addr.v4 = *(struct in_addr *)addr;
}
else if (family == AF_INET6)
{
p->family = AF_INET6;
p->bits = (bits < 0) ? AF_BITS(AF_INET6) : bits;
p->addr.v6 = *(struct in6_addr *)addr;
}
else
{
p->family = AF_INET6;
p->bits = (bits < 0) ? 128 : bits;
p->len = sizeof(p->addr.v6);
p->addr.v6 = *(struct in6_addr *)addr;
p->family = AF_PACKET;
p->bits = (bits < 0) ? AF_BITS(AF_PACKET) : bits;
p->addr.mac = *(struct ether_addr *)addr;
}
luaL_getmetatable(L, LUCI_IP_CIDR);
@ -254,6 +335,7 @@ static void L_setdev(struct lua_State *L, const char *name,
static int L_checkbits(lua_State *L, int index, cidr_t *p)
{
int16_t s16;
int bits;
if (lua_gettop(L) < index || lua_isnil(L, index))
@ -264,13 +346,15 @@ static int L_checkbits(lua_State *L, int index, cidr_t *p)
{
bits = lua_tointeger(L, index);
if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128))
if (bits < 0 || bits > AF_BITS(p->family))
return luaL_error(L, "Invalid prefix size");
}
else if (lua_type(L, index) == LUA_TSTRING)
{
if (!parse_mask(p->family, lua_tostring(L, index), &bits))
if (!parse_mask(p->family, lua_tostring(L, index), &s16))
return luaL_error(L, "Invalid netmask format");
bits = s16;
}
else
{
@ -293,20 +377,26 @@ static int _cidr_new(lua_State *L, int index, int family, bool mask)
if (family == AF_INET6)
{
cidr.family = AF_INET6;
cidr.bits = 128;
cidr.len = sizeof(cidr.addr.v6);
cidr.addr.v6.s6_addr[12] = n;
cidr.addr.v6.s6_addr[13] = (n >> 8);
cidr.addr.v6.s6_addr[14] = (n >> 16);
cidr.addr.v6.s6_addr[15] = (n >> 24);
}
else
else if (family == AF_INET)
{
cidr.family = AF_INET;
cidr.bits = 32;
cidr.len = sizeof(cidr.addr.v4);
cidr.addr.v4.s_addr = n;
}
else
{
cidr.family = AF_PACKET;
cidr.addr.mac.ether_addr_octet[2] = n;
cidr.addr.mac.ether_addr_octet[3] = (n >> 8);
cidr.addr.mac.ether_addr_octet[4] = (n >> 16);
cidr.addr.mac.ether_addr_octet[5] = (n >> 24);
}
cidr.bits = AF_BITS(cidr.family);
}
else
{
@ -346,6 +436,62 @@ static int cidr_ipv6(lua_State *L)
return _cidr_new(L, 1, AF_INET6, true);
}
static int cidr_mac(lua_State *L)
{
return _cidr_new(L, 1, AF_PACKET, true);
}
static int cidr_check(lua_State *L, int family)
{
cidr_t cidr = { }, *cidrp;
const char *addr;
if (lua_type(L, 1) == LUA_TSTRING)
{
addr = lua_tostring(L, 1);
if (addr && parse_cidr(addr, &cidr) && cidr.family == family)
return format_cidr(L, &cidr);
}
else
{
cidrp = lua_touserdata(L, 1);
if (cidrp == NULL)
return 0;
if (!lua_getmetatable(L, 1))
return 0;
lua_getfield(L, LUA_REGISTRYINDEX, LUCI_IP_CIDR);
if (!lua_rawequal(L, -1, -2))
cidrp = NULL;
lua_pop(L, 2);
if (cidrp != NULL && cidrp->family == family)
return format_cidr(L, cidrp);
}
return 0;
}
static int cidr_checkip4(lua_State *L)
{
return cidr_check(L, AF_INET);
}
static int cidr_checkip6(lua_State *L)
{
return cidr_check(L, AF_INET6);
}
static int cidr_checkmac(lua_State *L)
{
return cidr_check(L, AF_PACKET);
}
static int cidr_is4(lua_State *L)
{
cidr_t *p = L_checkcidr(L, 1, NULL);
@ -424,6 +570,34 @@ static int cidr_is6linklocal(lua_State *L)
return 1;
}
static int cidr_ismac(lua_State *L)
{
cidr_t *p = L_checkcidr(L, 1, NULL);
lua_pushboolean(L, p->family == AF_PACKET);
return 1;
}
static int cidr_ismacmcast(lua_State *L)
{
cidr_t *p = L_checkcidr(L, 1, NULL);
lua_pushboolean(L, (p->family == AF_PACKET &&
(p->addr.mac.ether_addr_octet[0] & 0x1)));
return 1;
}
static int cidr_ismaclocal(lua_State *L)
{
cidr_t *p = L_checkcidr(L, 1, NULL);
lua_pushboolean(L, (p->family == AF_PACKET &&
(p->addr.mac.ether_addr_octet[0] & 0x2)));
return 1;
}
static int _cidr_cmp(lua_State *L)
{
cidr_t *a = L_checkcidr(L, 1, NULL);
@ -432,7 +606,7 @@ static int _cidr_cmp(lua_State *L)
if (a->family != b->family)
return (a->family - b->family);
return memcmp(&a->addr.v6, &b->addr.v6, a->len);
return memcmp(&a->addr.v6, &b->addr.v6, AF_BYTES(a->family));
}
static int cidr_lower(lua_State *L)
@ -475,24 +649,24 @@ static void _apply_mask(cidr_t *p, int bits, bool inv)
if (bits <= 0)
{
memset(&p->addr.v6, inv * 0xFF, p->len);
memset(&p->addr.u8, inv * 0xFF, AF_BYTES(p->family));
}
else if (p->family == AF_INET && bits <= 32)
else if (p->family == AF_INET && bits <= AF_BITS(AF_INET))
{
if (inv)
p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1);
p->addr.v4.s_addr |= ntohl((1 << (AF_BITS(AF_INET) - bits)) - 1);
else
p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1));
p->addr.v4.s_addr &= ntohl(~((1 << (AF_BITS(AF_INET) - bits)) - 1));
}
else if (p->family == AF_INET6 && bits <= 128)
else if (bits <= AF_BITS(p->family))
{
for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++)
for (i = 0; i < AF_BYTES(p->family); i++)
{
b = (bits > 8) ? 8 : bits;
if (inv)
p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b)));
p->addr.u8[i] |= ~((uint8_t)(0xFF << (8 - b)));
else
p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b));
p->addr.u8[i] &= (uint8_t)(0xFF << (8 - b));
bits -= b;
}
}
@ -507,7 +681,7 @@ static int cidr_network(lua_State *L)
return 0;
*p2 = *p1;
p2->bits = (p1->family == AF_INET) ? 32 : 128;
p2->bits = AF_BITS(p1->family);
_apply_mask(p2, bits, false);
luaL_getmetatable(L, LUCI_IP_CIDR);
@ -524,7 +698,7 @@ static int cidr_host(lua_State *L)
return 0;
*p2 = *p1;
p2->bits = (p1->family == AF_INET) ? 32 : 128;
p2->bits = AF_BITS(p1->family);
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
@ -539,7 +713,7 @@ static int cidr_mask(lua_State *L)
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
p2->bits = (p1->family == AF_INET) ? 32 : 128;
p2->bits = AF_BITS(p1->family);
p2->family = p1->family;
memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr));
@ -556,14 +730,14 @@ static int cidr_broadcast(lua_State *L)
cidr_t *p2;
int bits = L_checkbits(L, 2, p1);
if (p1->family == AF_INET6)
if (p1->family != AF_INET)
return 0;
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
*p2 = *p1;
p2->bits = (p1->family == AF_INET) ? 32 : 128;
p2->bits = AF_BITS(AF_INET);
_apply_mask(p2, bits, true);
luaL_getmetatable(L, LUCI_IP_CIDR);
@ -583,7 +757,7 @@ static int cidr_mapped4(lua_State *L)
return 0;
p2->family = AF_INET;
p2->bits = (p1->bits > 32) ? 32 : p1->bits;
p2->bits = (p1->bits > AF_BITS(AF_INET)) ? AF_BITS(AF_INET) : p1->bits;
memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4));
luaL_getmetatable(L, LUCI_IP_CIDR);
@ -591,6 +765,72 @@ static int cidr_mapped4(lua_State *L)
return 1;
}
static int cidr_tolinklocal(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
cidr_t *p2;
int i;
if (p1->family != AF_PACKET)
return 0;
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
p2->family = AF_INET6;
p2->bits = AF_BITS(AF_INET6);
p2->addr.u8[0] = 0xFE;
p2->addr.u8[1] = 0x80;
p2->addr.u8[8] = p1->addr.u8[0] ^ 0x02;
p2->addr.u8[9] = p1->addr.u8[1];
p2->addr.u8[10] = p1->addr.u8[2];
p2->addr.u8[11] = 0xFF;
p2->addr.u8[12] = 0xFE;
p2->addr.u8[13] = p1->addr.u8[3];
p2->addr.u8[14] = p1->addr.u8[4];
p2->addr.u8[15] = p1->addr.u8[5];
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
return 1;
}
static int cidr_tomac(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
cidr_t *p2;
int i;
if (p1->family != AF_INET6 ||
p1->addr.u8[0] != 0xFE ||
p1->addr.u8[1] != 0x80 ||
p1->addr.u8[2] != 0x00 ||
p1->addr.u8[3] != 0x00 ||
p1->addr.u8[4] != 0x00 ||
p1->addr.u8[5] != 0x00 ||
p1->addr.u8[6] != 0x00 ||
p1->addr.u8[7] != 0x00 ||
p1->addr.u8[11] != 0xFF ||
p1->addr.u8[12] != 0xFE)
return 0;
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
p2->family = AF_PACKET;
p2->bits = AF_BITS(AF_PACKET);
p2->addr.u8[0] = p1->addr.u8[8] ^ 0x02;
p2->addr.u8[1] = p1->addr.u8[9];
p2->addr.u8[2] = p1->addr.u8[10];
p2->addr.u8[3] = p1->addr.u8[13];
p2->addr.u8[4] = p1->addr.u8[14];
p2->addr.u8[5] = p1->addr.u8[15];
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
return 1;
}
static int cidr_contains(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
@ -603,15 +843,15 @@ static int cidr_contains(lua_State *L)
_apply_mask(&a, p1->bits, false);
_apply_mask(&b, p1->bits, false);
rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len);
rv = !memcmp(&a.addr.v6, &b.addr.v6, AF_BYTES(a.family));
}
lua_pushboolean(L, rv);
return 1;
}
#define S6_BYTE(a, i) \
(a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1]
#define BYTE(a, i) \
(a)->addr.u8[AF_BYTES((a)->family) - (i) - 1]
static int _cidr_add_sub(lua_State *L, bool add)
{
@ -625,30 +865,7 @@ static int _cidr_add_sub(lua_State *L, bool add)
if (p1->family == p2->family)
{
if (p1->family == AF_INET6)
{
for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++)
{
if (add)
{
S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry;
carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256;
}
else
{
S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry);
carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry));
}
}
/* would over/underflow */
if (carry)
{
memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6));
ok = false;
}
}
else
if (p1->family == AF_INET)
{
a = ntohl(p1->addr.v4.s_addr);
b = ntohl(p2->addr.v4.s_addr);
@ -664,6 +881,29 @@ static int _cidr_add_sub(lua_State *L, bool add)
r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b);
}
}
else
{
for (i = 0, carry = 0; i < AF_BYTES(p1->family); i++)
{
if (add)
{
BYTE(&r, i) = BYTE(p1, i) + BYTE(p2, i) + carry;
carry = (BYTE(p1, i) + BYTE(p2, i) + carry) / 256;
}
else
{
BYTE(&r, i) = (BYTE(p1, i) - BYTE(p2, i) - carry);
carry = (BYTE(p1, i) < (BYTE(p2, i) + carry));
}
}
/* would over/underflow */
if (carry)
{
memset(&r.addr.u8, add * 0xFF, AF_BYTES(r.family));
ok = false;
}
}
}
else
{
@ -705,22 +945,22 @@ static int cidr_minhost(lua_State *L)
_apply_mask(&r, r.bits, false);
if (r.family == AF_INET6 && r.bits < 128)
if (r.family == AF_INET && r.bits < AF_BITS(AF_INET))
{
r.bits = 128;
r.bits = AF_BITS(AF_INET);
r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1);
}
else if (r.bits < AF_BITS(r.family))
{
r.bits = AF_BITS(r.family);
for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++)
for (i = 0, carry = 1; i < AF_BYTES(r.family); i++)
{
rest = (S6_BYTE(&r, i) + carry) > 255;
S6_BYTE(&r, i) += carry;
rest = (BYTE(&r, i) + carry) > 255;
BYTE(&r, i) += carry;
carry = rest;
}
}
else if (r.family == AF_INET && r.bits < 32)
{
r.bits = 32;
r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1);
}
if (!(p = lua_newuserdata(L, sizeof(*p))))
return 0;
@ -739,14 +979,14 @@ static int cidr_maxhost(lua_State *L)
_apply_mask(&r, r.bits, true);
if (r.family == AF_INET && r.bits < 32)
if (r.family == AF_INET && r.bits < AF_BITS(AF_INET))
{
r.bits = 32;
r.bits = AF_BITS(AF_INET);
r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1);
}
else if (r.family == AF_INET6)
else
{
r.bits = 128;
r.bits = AF_BITS(r.family);
}
if (!(p = lua_newuserdata(L, sizeof(*p))))
@ -766,31 +1006,17 @@ static int cidr_gc (lua_State *L)
static int cidr_tostring (lua_State *L)
{
char buf[INET6_ADDRSTRLEN];
cidr_t *p = L_checkcidr(L, 1, NULL);
if ((p->family == AF_INET && p->bits < 32) ||
(p->family == AF_INET6 && p->bits < 128))
{
lua_pushfstring(L, "%s/%d",
inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)),
p->bits);
}
else
{
lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)));
}
return 1;
return format_cidr(L, p);
}
/*
* route functions
*/
static bool diff_prefix(int family, void *addr, int bits, cidr_t *p)
static bool diff_prefix(int family, void *addr, int bits, bool exact, cidr_t *p)
{
uint8_t i, b, r;
uint8_t i, b, r, *a;
uint32_t m;
if (!p->family)
@ -799,28 +1025,27 @@ static bool diff_prefix(int family, void *addr, int bits, cidr_t *p)
if (!addr || p->family != family || p->bits > bits)
return true;
if (family == AF_INET6)
if (family == AF_INET)
{
for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++)
m = p->bits ? htonl(~((1 << (AF_BITS(AF_INET) - p->bits)) - 1)) : 0;
if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m))
return true;
}
else
{
for (i = 0, a = addr, r = p->bits; i < AF_BYTES(p->family); i++)
{
b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0;
if ((((struct in6_addr *)addr)->s6_addr[i] & b) !=
(p->addr.v6.s6_addr[i] & b))
if ((a[i] & b) != (p->addr.u8[i] & b))
return true;
r -= ((r > 8) ? 8 : r);
}
}
else
{
m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0;
if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m))
return true;
}
return (p->exact && p->bits != bits);
return (exact && p->bits != bits);
}
static int cb_dump_route(struct nl_msg *msg, void *arg)
@ -848,7 +1073,7 @@ static int cb_dump_route(struct nl_msg *msg, void *arg)
dst = tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &def;
gw = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL;
bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32;
bitlen = AF_BITS(rt->rtm_family);
if ((f->type && rt->rtm_type != f->type) ||
(f->family && rt->rtm_family != f->family) ||
@ -857,10 +1082,14 @@ static int cb_dump_route(struct nl_msg *msg, void *arg)
(f->iif && iif != f->iif) ||
(f->oif && oif != f->oif) ||
(f->table && table != f->table) ||
diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) ||
diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, &f->dst) ||
diff_prefix(rt->rtm_family, gw, bitlen, &f->gw) ||
diff_prefix(rt->rtm_family, src, bitlen, &f->src))
diff_prefix(rt->rtm_family, from, rt->rtm_src_len,
f->from_exact, &f->from) ||
diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len,
f->dst_exact, &f->dst) ||
diff_prefix(rt->rtm_family, gw, bitlen,
false, &f->gw) ||
diff_prefix(rt->rtm_family, src, bitlen,
false, &f->src))
goto out;
if (s->callback)
@ -988,7 +1217,8 @@ static int _route_dump(lua_State *L, struct dump_filter *filter)
nlmsg_append(msg, &rtm, sizeof(rtm), 0);
if (filter->get)
nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6);
nla_put(msg, RTA_DST, AF_BYTES(filter->dst.family),
&filter->dst.addr.v6);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s);
@ -1063,10 +1293,10 @@ static int route_dump(lua_State *L)
filter.dst = p;
if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p))
filter.from = p, filter.from.exact = true;
filter.from = p, filter.from_exact = true;
if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p))
filter.dst = p, filter.dst.exact = true;
filter.dst = p, filter.dst_exact = true;
}
return _route_dump(L, &filter);
@ -1107,12 +1337,12 @@ static int cb_dump_neigh(struct nl_msg *msg, void *arg)
mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
bitlen = (nd->ndm_family == AF_INET) ? 32 : 128;
bitlen = AF_BITS(nd->ndm_family);
if ((f->family && nd->ndm_family != f->family) ||
(f->iif && nd->ndm_ifindex != f->iif) ||
(f->type && !(f->type & nd->ndm_state)) ||
diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) ||
diff_prefix(nd->ndm_family, dst, bitlen, false, &f->dst) ||
diff_macaddr(mac, &f->mac))
goto out;
@ -1140,15 +1370,7 @@ static int cb_dump_neigh(struct nl_msg *msg, void *arg)
L_setaddr(s->L, "dest", nd->ndm_family, dst, -1);
if (mac)
{
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
mac->ether_addr_octet[0], mac->ether_addr_octet[1],
mac->ether_addr_octet[2], mac->ether_addr_octet[3],
mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
lua_pushstring(s->L, buf);
lua_setfield(s->L, -2, "mac");
}
L_setaddr(s->L, "mac", AF_PACKET, mac, -1);
s->index++;
@ -1241,7 +1463,7 @@ out:
static int cb_dump_link(struct nl_msg *msg, void *arg)
{
char *p, *addr, buf[48];
char buf[48];
struct dump_state *s = arg;
struct nlmsghdr *hdr = nlmsg_hdr(msg);
struct ifinfomsg *ifm = NLMSG_DATA(hdr);
@ -1266,19 +1488,8 @@ static int cb_dump_link(struct nl_msg *msg, void *arg)
if (tb[IFLA_MASTER])
L_setdev(s->L, "master", tb[IFLA_MASTER]);
if (tb[IFLA_ADDRESS])
{
len = nla_len(tb[IFLA_ADDRESS]);
addr = nla_get_string(tb[IFLA_ADDRESS]);
if ((len * 3) <= sizeof(buf))
{
for (p = buf, i = 0; i < len; i++)
p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++);
L_setstr(s->L, "mac", buf);
}
}
if (tb[IFLA_ADDRESS] && nla_len(tb[IFLA_ADDRESS]) == AF_BYTES(AF_PACKET))
L_setaddr(s->L, "mac", AF_PACKET, nla_get_string(tb[IFLA_ADDRESS]), -1);
s->pending = 0;
return NL_SKIP;
@ -1333,13 +1544,18 @@ static const luaL_reg ip_methods[] = {
{ "new", cidr_new },
{ "IPv4", cidr_ipv4 },
{ "IPv6", cidr_ipv6 },
{ "MAC", cidr_mac },
{ "checkip4", cidr_checkip4 },
{ "checkip6", cidr_checkip6 },
{ "checkmac", cidr_checkmac },
{ "route", route_get },
{ "routes", route_dump },
{ "neighbors", neighbor_dump },
{ "link", link_get },
{ "link", link_get },
{ }
};
@ -1351,6 +1567,9 @@ static const luaL_reg ip_cidr_methods[] = {
{ "is6", cidr_is6 },
{ "is6linklocal", cidr_is6linklocal },
{ "is6mapped4", cidr_is6mapped4 },
{ "ismac", cidr_ismac },
{ "ismaclocal", cidr_ismaclocal },
{ "ismacmcast", cidr_ismacmcast },
{ "lower", cidr_lower },
{ "higher", cidr_higher },
{ "equal", cidr_equal },
@ -1360,7 +1579,9 @@ static const luaL_reg ip_cidr_methods[] = {
{ "mask", cidr_mask },
{ "broadcast", cidr_broadcast },
{ "mapped4", cidr_mapped4 },
{ "contains", cidr_contains },
{ "tomac", cidr_tomac },
{ "tolinklocal", cidr_tolinklocal },
{ "contains", cidr_contains },
{ "add", cidr_add },
{ "sub", cidr_sub },
{ "minhost", cidr_minhost },

View file

@ -27,6 +27,7 @@ addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::")
addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
@see IPv4
@see IPv6
@see MAC
]]
---[[
@ -47,6 +48,7 @@ addr = luci.ip.IPv4("10.24.0.1/255.255.255.0")
addr = luci.ip.IPv4("10.24.0.1", "255.255.255.0") -- separate netmask
addr = luci.ip.IPv4("10.24.0.1/24", 16) -- override netmask`
@see IPv6
@see MAC
]]
---[[
@ -67,12 +69,112 @@ addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::")
addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::")
addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
@see IPv4
@see MAC
]]
---[[
Construct a new MAC luci.ip.cidr instance.
Throws an error if the given string does not represent a valid ethernet MAC
address or if the given optional mask is of a different family.
@class function
@sort 4
@name MAC
@param address String containing a valid ethernet MAC address, optionally with
prefix size (CIDR notation) or mask separated by slash.
@param netmask String containing a valid MAC address mask or number
containing a prefix size between `0` and `48` bit.
Overrides mask embedded in the first argument if specified. (optional)
@return A `luci.ip.cidr` object representing the given MAC address range.
@usage `intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/FF:FF:FF:0:0:0")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00", "FF:FF:FF:0:0:0")
intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24", 48) -- override mask`
@see IPv4
@see IPv6
]]
---[[
Verify an IPv4 address.
Checks whether given argument is a preexisting luci.ip.cidr IPv4 address
instance or a string literal convertible to an IPv4 address and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
@class function
@sort 5
@name checkip4
@param address String containing a valid IPv4 address or existing
luci.ip.cidr IPv4 instance.
@return A string representing the given IPv4 address.
@usage `ipv4 = luci.ip.checkip4(luci.ip.new("127.0.0.1")) -- "127.0.0.1"
ipv4 = luci.ip.checkip4("127.0.0.1") -- "127.0.0.1"
ipv4 = luci.ip.checkip4("nonesense") -- nothing
ipv4 = luci.ip.checkip4(123) -- nothing
ipv4 = luci.ip.checkip4(nil) -- nothing
ipv4 = luci.ip.checkip4() -- nothing`
@see checkip6
@see checkmac
]]
---[[
Verify an IPv6 address.
Checks whether given argument is a preexisting luci.ip.cidr IPv6 address
instance or a string literal convertible to an IPv6 address and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
@class function
@sort 6
@name checkip6
@param address String containing a valid IPv6 address or existing
luci.ip.cidr IPv6 instance.
@return A string representing the given IPv6 address.
@usage `ipv6 = luci.ip.checkip6(luci.ip.new("0:0:0:0:0:0:0:1")) -- "::1"
ipv6 = luci.ip.checkip6("0:0:0:0:0:0:0:1") -- "::1"
ipv6 = luci.ip.checkip6("nonesense") -- nothing
ipv6 = luci.ip.checkip6(123) -- nothing
ipv6 = luci.ip.checkip6(nil) -- nothing
ipv6 = luci.ip.checkip6() -- nothing`
@see checkip4
@see checkmac
]]
---[[
Verify an ethernet MAC address.
Checks whether given argument is a preexisting luci.ip.cidr MAC address
instance or a string literal convertible to an ethernet MAC and returns a
plain Lua string containing the canonical representation of the address.
If the argument is not a valid address, returns nothing. This function is
intended to aid in safely verifying address literals without having to deal
with exceptions.
@class function
@sort 7
@name checkmac
@param address String containing a valid MAC address or existing luci.ip.cidr
MAC address instance.
@return A string representing the given MAC address.
@usage `mac = luci.ip.checkmac(luci.ip.new("00-11-22-cc-dd-ee")) -- "00:11:22:CC:DD:EE"
mac = luci.ip.checkmac("00:11:22:cc:dd:ee") -- "00:11:22:CC:DD:EE"
mac = luci.ip.checkmac("nonesense") -- nothing
mac = luci.ip.checkmac(123) -- nothing
mac = luci.ip.checkmac(nil) -- nothing
mac = luci.ip.checkmac() -- nothing`
@see checkip4
@see checkip6
]]
---[[
Determine the route leading to the given destination.
@class function
@sort 4
@sort 8
@name route
@param address A `luci.ip.cidr` instance or a string containing
a valid IPv4 or IPv6 range as specified by `luci.ip.new()`.
@ -178,7 +280,7 @@ end`</li>
---[[
Fetch all routes, optionally matching the given criteria.
@class function
@sort 5
@sort 9
@name routes
@param filter <p>Table containing one or more of the possible filter
critera described below (optional)</p><table>
@ -258,7 +360,7 @@ end`</li>
---[[
Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table
@class function
@sort 6
@sort 10
@name neighbors
@param filter <p>Table containing one or more of the possible filter
critera described below (optional)</p><table>
@ -306,7 +408,7 @@ A neighbour entry is a table containing the following fields:
</tr>
<tr>
<td>`mac`</td>
<td>String containing the associated MAC address</td>
<td>MAC address `luci.ip.cidr` instance</td>
</tr>
<tr>
<td>`router`</td>
@ -367,7 +469,7 @@ end)`</li>
---[[
Fetch basic device information
@class function
@sort 7
@sort 11
@name link
@param device String containing the network device to query
@return If the given interface is found, a table containing the fields
@ -403,8 +505,8 @@ described below is returned, else an empty table.
</tr>
<tr>
<td>`mac`</td>
<td>String containing the link local address of the device in
dotted hex notation</td>
<td>MAC address `luci.ip.cidr` instance representing the device ethernet
address</td>
</tr>
</table>
@usage <ul>
@ -430,6 +532,7 @@ Checks whether the CIDR instance is an IPv4 address range
@sort 1
@name cidr.is4
@see cidr.is6
@see cidr.ismac
@return `true` if the CIDR is an IPv4 range, else `false`
]]
@ -470,6 +573,7 @@ Checks whether the CIDR instance is an IPv6 address range
@sort 4
@name cidr.is6
@see cidr.is4
@see cidr.ismac
@return `true` if the CIDR is an IPv6 range, else `false`
]]
@ -502,13 +606,51 @@ end`
]]
---[[
Checks whether this CIDR instance is lower than the given argument.
The comparisation follows these rules:
<ul><li>An IPv4 address is always lower than an IPv6 address</li>
<li>Prefix sizes are ignored</li></ul>
Checks whether the CIDR instance is an ethernet MAC address range
@class function
@sort 7
@name cidr.ismac
@see cidr.is4
@see cidr.is6
@return `true` if the CIDR is a MAC address range, else `false`
]]
---[[
Checks whether the CIDR instance is a locally administered (LAA) MAC address
@class function
@sort 8
@name cidr.ismaclocal
@return `true` if the MAC address sets the locally administered bit.
@usage `local mac = luci.ip.new("02:C0:FF:EE:00:01")
if mac:ismaclocal() then
print("Is an LAA MAC address")
end`
]]
---[[
Checks whether the CIDR instance is a multicast MAC address
@class function
@sort 9
@name cidr.ismacmcast
@return `true` if the MAC address sets the multicast bit.
@usage `local mac = luci.ip.new("01:00:5E:7F:00:10")
if addr:ismacmcast() then
print("Is a multicast MAC address")
end`
]]
---[[
Checks whether this CIDR instance is lower than the given argument.
The comparisation follows these rules:
<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@class function
@sort 10
@name cidr.lower
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
@ -518,7 +660,8 @@ The comparisation follows these rules:
print(addr:lower(addr)) -- false
print(addr:lower("10.10.10.10/24")) -- false
print(addr:lower(luci.ip.new("::1"))) -- true
print(addr:lower(luci.ip.new("192.168.200.1"))) -- true`
print(addr:lower(luci.ip.new("192.168.200.1"))) -- true
print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true`
@see cidr.higher
@see cidr.equal
]]
@ -526,11 +669,12 @@ print(addr:lower(luci.ip.new("192.168.200.1"))) -- true`
---[[
Checks whether this CIDR instance is higher than the given argument.
The comparisation follows these rules:
<ul><li>An IPv4 address is always lower than an IPv6 address</li>
<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@class function
@sort 8
@sort 11
@name cidr.higher
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
@ -540,7 +684,8 @@ The comparisation follows these rules:
print(addr:higher(addr)) -- false
print(addr:higher("10.10.10.10/24")) -- true
print(addr:higher(luci.ip.new("::1"))) -- false
print(addr:higher(luci.ip.new("192.168.200.1"))) -- false`
print(addr:higher(luci.ip.new("192.168.200.1"))) -- false
print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false`
@see cidr.lower
@see cidr.equal
]]
@ -549,7 +694,7 @@ print(addr:higher(luci.ip.new("192.168.200.1"))) -- false`
Checks whether this CIDR instance is equal to the given argument.
@class function
@sort 9
@sort 12
@name cidr.equal
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
@ -562,7 +707,11 @@ print(addr:equal(luci.ip.new("::1"))) -- false
local addr6 = luci.ip.new("::1")
print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true
print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false`
print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:equal("0:14:22:1:23:45")) -- true
print(mac:equal(luci.ip.new("01:23:45:67:89:AB")) -- false`
@see cidr.lower
@see cidr.higher
]]
@ -573,11 +722,11 @@ If the optional mask parameter is given, the prefix size of this CIDR is altered
else the current prefix size is returned.
@class function
@sort 10
@sort 13
@name cidr.prefix
@param mask Either a number containing the number of bits (`0..32`
for IPv4, `0..128` for IPv6) or a string containing a valid
netmask (optional)
for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
containing a valid netmask (optional)
@return Bit count of the current prefix size
@usage `local range = luci.ip.new("192.168.1.1/255.255.255.0")
print(range:prefix()) -- 24
@ -597,11 +746,11 @@ with all host parts masked out. The used prefix size can be overridden by the
optional mask parameter.
@class function
@sort 11
@sort 14
@name cidr.network
@param mask Either a number containing the number of bits (`0..32`
for IPv4, `0..128` for IPv6) or a string containing a valid
netmask (optional)
for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
containing a valid netmask (optional)
@return CIDR instance representing the network address
@usage `local range = luci.ip.new("192.168.62.243/255.255.0.0")
print(range:network()) -- "192.168.0.0"
@ -616,10 +765,10 @@ print(range6:network()) -- "fd9b:62b3:9cc5::"`
Derive host address of CIDR instance.
This function essentially constructs a copy of this CIDR with the prefix size
set to `32` for IPv4 and `128` for IPv6.
set to `32` for IPv4, `128` for IPv6 or `48` for MAC addresses.
@class function
@sort 12
@sort 15
@name cidr.host
@return CIDR instance representing the host address
@usage `local range = luci.ip.new("172.19.37.45/16")
@ -634,11 +783,11 @@ Constructs a CIDR instance representing the netmask of this instance. The used
prefix size can be overridden by the optional mask parameter.
@class function
@sort 13
@sort 16
@name cidr.mask
@param mask Either a number containing the number of bits (`0..32`
for IPv4, `0..128` for IPv6) or a string containing a valid
netmask (optional)
for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
containing a valid netmask (optional)
@return CIDR instance representing the netmask
@usage `local range = luci.ip.new("172.19.37.45/16")
print(range:mask()) -- "255.255.0.0"
@ -652,15 +801,14 @@ Derive broadcast address of CIDR instance.
Constructs a CIDR instance representing the broadcast address of this instance.
The used prefix size can be overridden by the optional mask parameter.
This function has no effect on IPv6 instances, it will return nothing in this
case.
This function has no effect on IPv6 or MAC address instances, it will return
nothing in this case.
@class function
@sort 14
@sort 17
@name cidr.broadcast
@param mask Either a number containing the number of bits (`0..32`
for IPv4, `0..128` for IPv6) or a string containing a valid
netmask (optional)
@param mask Either a number containing the number of bits (`0..32` for IPv4) or
a string containing a valid netmask (optional)
@return Return a new CIDR instance representing the broadcast address if this
instance is an IPv4 range, else return nothing.
@usage `local range = luci.ip.new("172.19.37.45/16")
@ -675,11 +823,11 @@ Derive mapped IPv4 address of CIDR instance.
Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped
IPv4 address in this instance.
This function has no effect on IPv4 instances or IPv6 instances which are not a
mapped address, it will return nothing in this case.
This function has no effect on IPv4 instances, MAC address instances or IPv6
instances which are not a mapped address, it will return nothing in this case.
@class function
@sort 15
@sort 18
@name cidr.mapped4
@return Return a new CIDR instance representing the IPv4 address if this
instance is an IPv6 mapped IPv4 address, else return nothing.
@ -687,11 +835,47 @@ mapped address, it will return nothing in this case.
print(addr:mapped4()) -- "172.16.19.1"`
]]
---[[
Derive MAC address of IPv6 link local CIDR instance.
Constructs a CIDR instance representing the MAC address contained in the IPv6
link local address of this instance.
This function has no effect on IPv4 instances, MAC address instances or IPv6
instances which are not a link local address, it will return nothing in this
case.
@class function
@sort 19
@name cidr.tomac
@return Return a new CIDR instance representing the MAC address if this
instance is an IPv6 link local address, else return nothing.
@usage `local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9")
print(addr:tomac()) -- "64:66:B3:47:E1:B9"`
]]
---[[
Derive IPv6 link local address from MAC address CIDR instance.
Constructs a CIDR instance representing the IPv6 link local address of the
MAC address represented by this instance.
This function has no effect on IPv4 instances or IPv6 instances, it will return
nothing in this case.
@class function
@sort 20
@name cidr.tolinklocal
@return Return a new CIDR instance representing the IPv6 link local address.
@usage `local mac = luci.ip.new("64:66:B3:47:E1:B9")
print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"`
]]
---[[
Test whether CIDR contains given range.
@class function
@sort 16
@sort 21
@name cidr.contains
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to test.
@ -704,7 +888,11 @@ print(range:contains("10.0.0.0/8")) -- false
local range6 = luci.ip.new("fe80::/10")
print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true
print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false`
print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false
local intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
print(intel_macs:contains("C0:B6:F9:A3:C:11")) -- true
print(intel_macs:contains("64:66:B3:47:E1:B9")) -- false`
]]
---[[
@ -712,7 +900,7 @@ Add given amount to CIDR instance. If the result would overflow the maximum
address space, the result is set to the highest possible address.
@class function
@sort 17
@sort 22
@name cidr.add
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertable by
@ -726,32 +914,42 @@ address space, the result is set to the highest possible address.
this instance plus the added amount or the highest possible address if
the addition overflowed the available address space.</li></ul>
@usage `local addr = luci.ip.new("192.168.1.1/24")
print(addr:add(250)) -- "192.168.1.251/24"
print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
print(addr:add(250)) -- "192.168.1.251/24"
print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
addr:add(256, true) -- true
print(addr) -- "192.168.2.1/24
addr:add(256, true) -- true
print(addr) -- "192.168.2.1/24
addr:add("255.0.0.0", true) -- false (overflow)
print(addr) -- "255.255.255.255/24
addr:add("255.0.0.0", true) -- false (overflow)
print(addr) -- "255.255.255.255/24
local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
addr:add(256, true) -- true
print(addr) -- "fe80::221:63f:fe75:ab17/64
addr6:add(256, true) -- true
print(addr6) -- "fe80::221:63f:fe75:ab17/64
addr:add("ffff::", true) -- false (overflow)
print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"`
addr6:add("ffff::", true) -- false (overflow)
print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:add(256)) -- "00:14:22:01:24:45"
print(mac:add("0:0:0:0:FF:0") -- "00:14:22:02:22:45"
mac:add(256, true) -- true
print(mac) -- "00:14:22:01:24:45"
mac:add("FF:FF:0:0:0:0", true) -- false (overflow)
print(mac) -- "FF:FF:FF:FF:FF:FF"`
]]
---[[
Substract given amount from CIDR instance. If the result would under, the lowest
Subtract given amount from CIDR instance. If the result would under, the lowest
possible address is returned.
@class function
@sort 18
@sort 23
@name cidr.sub
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertable by
@ -759,11 +957,11 @@ possible address is returned.
@param inplace If `true`, modify this instance instead of returning
a new derived CIDR instance.
@return <ul>
<li>When substracting inplace: Return `true` if the substraction
succeded or `false` when the substraction underflowed.</li>
<li>When subtracting inplace: Return `true` if the subtraction
succeeded or `false` when the subtraction underflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
this instance minus the substracted amount or the lowest address if
the substraction underflowed.</li></ul>
this instance minus the subtracted amount or the lowest address if
the subtraction underflowed.</li></ul>
@usage `local addr = luci.ip.new("192.168.1.1/24")
print(addr:sub(256)) -- "192.168.0.1/24"
print(addr:sub("0.168.0.0")) -- "192.0.1.1/24"
@ -782,14 +980,24 @@ addr:sub(256, true) -- true
print(addr) -- "fe80::221:63f:fe75:a917/64"
addr:sub("ffff::", true) -- false (underflow)
print(addr) -- "::/64"`
print(addr) -- "::/64"
local mac = luci.ip.new("00:14:22:01:23:45")
print(mac:sub(256)) -- "00:14:22:01:22:45"
print(mac:sub("0:0:0:0:FF:0") -- "00:14:22:00:24:45"
mac:sub(256, true) -- true
print(mac) -- "00:14:22:01:22:45"
mac:sub("FF:FF:0:0:0:0", true) -- false (overflow)
print(mac) -- "00:00:00:00:00:00"`
]]
---[[
Calculate the lowest possible host address within this CIDR instance.
@class function
@sort 19
@sort 24
@name cidr.minhost
@return Returns a new CIDR instance representing the lowest host address
within this range.
@ -797,14 +1005,17 @@ Calculate the lowest possible host address within this CIDR instance.
print(addr:minhost()) -- "192.168.123.1"
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"`
print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"
local mac = luci.ip.new("00:14:22:01:22:45/32")
print(mac:minhost()) -- "00:14:22:01:00:01"`
]]
---[[
Calculate the highest possible host address within this CIDR instance.
@class function
@sort 20
@sort 25
@name cidr.maxhost
@return Returns a new CIDR instance representing the highest host address
within this range.
@ -812,20 +1023,24 @@ Calculate the highest possible host address within this CIDR instance.
print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast)
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"`
print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"
local mac = luci.ip.new("00:14:22:01:22:45/32")
print(mac:maxhost()) -- "00:14:22:01:FF:FF"`
]]
---[[
Convert CIDR instance into string representation.
If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the
address is returned in the form "address/prefix" otherwise just "address".
If the prefix size of instance is less than 32 for IPv4, 128 for IPv6 or 48 for
MACs, the address is returned in the form "address/prefix" otherwise just
"address".
It is usually not required to call this function directly as CIDR objects
define it as __tostring function in the associated metatable.
@class function
@sort 21
@sort 26
@name cidr.string
@return Returns a string representing the range or address of this CIDR instance
]]