luci-lib-ip: support scoped IPv6 addresses

Ref: https://github.com/openwrt/luci/issues/3380
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(cherry picked from commit f7a7f89e0c)
This commit is contained in:
Jo-Philipp Wich 2019-12-09 17:31:51 +01:00
parent 9ca48ecc93
commit 1d2b4c777f
2 changed files with 81 additions and 16 deletions

View file

@ -62,6 +62,7 @@ typedef struct {
struct ether_addr mac;
uint8_t u8[16];
} addr;
uint32_t scope;
uint16_t family;
int16_t bits;
} cidr_t;
@ -177,7 +178,7 @@ static bool parse_mask(int family, const char *mask, int16_t *bits)
static bool parse_cidr(const char *dest, cidr_t *pp)
{
char *p, buf[INET6_ADDRSTRLEN * 2 + 2];
char *p, *s, buf[INET6_ADDRSTRLEN * 2 + 2];
strncpy(buf, dest, sizeof(buf) - 1);
@ -186,6 +187,11 @@ static bool parse_cidr(const char *dest, cidr_t *pp)
if (p)
*p++ = 0;
s = strchr(buf, '%');
if (s)
*s++ = 0;
if (inet_pton(AF_INET, buf, &pp->addr.v4))
pp->family = AF_INET;
else if (inet_pton(AF_INET6, buf, &pp->addr.v6))
@ -195,6 +201,22 @@ static bool parse_cidr(const char *dest, cidr_t *pp)
else
return false;
if (s)
{
if (pp->family != AF_INET6)
return false;
if (!(pp->addr.v6.s6_addr[0] == 0xFE &&
pp->addr.v6.s6_addr[1] >= 0x80 &&
pp->addr.v6.s6_addr[2] <= 0xBF))
return false;
pp->scope = if_nametoindex(s);
if (pp->scope == 0)
return false;
}
if (p)
{
if (!parse_mask(pp->family, p, &pp->bits))
@ -210,7 +232,7 @@ static bool parse_cidr(const char *dest, cidr_t *pp)
static int format_cidr(lua_State *L, cidr_t *p)
{
char buf[INET6_ADDRSTRLEN];
char *s, buf[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 4];
if (p->family == AF_PACKET)
{
@ -229,13 +251,19 @@ static int format_cidr(lua_State *L, cidr_t *p)
}
else
{
inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf));
s = buf + strlen(buf);
if (p->scope != 0 && if_indextoname(p->scope, s + 1) != NULL) {
*s++ = '%';
s += strlen(s);
}
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)));
s += sprintf(s, "/%d", p->bits);
lua_pushstring(L, buf);
}
return 1;
@ -765,6 +793,25 @@ static int cidr_mapped4(lua_State *L)
return 1;
}
static int cidr_unscoped(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
cidr_t *p2;
if (p1->family != AF_INET6)
return 0;
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
*p2 = *p1;
p2->scope = 0;
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
return 1;
}
static int cidr_tolinklocal(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
@ -1601,6 +1648,7 @@ static const luaL_reg ip_cidr_methods[] = {
{ "mask", cidr_mask },
{ "broadcast", cidr_broadcast },
{ "mapped4", cidr_mapped4 },
{ "unscoped", cidr_unscoped },
{ "tomac", cidr_tomac },
{ "tolinklocal", cidr_tolinklocal },
{ "contains", cidr_contains },

View file

@ -837,6 +837,23 @@ instances which are not a mapped address, it will return nothing in this case.
print(addr:mapped4()) -- "172.16.19.1"`
]]
---[[
Derive unscoped IPv6 address of CIDR instance.
Construct a copy of the given IPv6 CIDR instance and drop the associated
address scope information.
This function has no effect on IPv4 instances or MAC address instances,
it will return nothing in this case.
@class function
@sort 19
@name cidr.unscoped
@return Return a new CIDR instance representing the unscoped IPv6 address.
@usage `local addr = luci.ip.new("fe80::1234%eth0")
print(addr:unscoped()) -- "fe80::1234"`
]]
---[[
Derive MAC address of IPv6 link local CIDR instance.
@ -848,7 +865,7 @@ instances which are not a link local address, it will return nothing in this
case.
@class function
@sort 19
@sort 20
@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.
@ -866,7 +883,7 @@ This function has no effect on IPv4 instances or IPv6 instances, it will return
nothing in this case.
@class function
@sort 20
@sort 21
@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")
@ -877,7 +894,7 @@ print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"`
Test whether CIDR contains given range.
@class function
@sort 21
@sort 22
@name cidr.contains
@param addr A `luci.ip.cidr` instance or a string convertible by
`luci.ip.new()` to test.
@ -902,7 +919,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 22
@sort 23
@name cidr.add
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertible by
@ -951,7 +968,7 @@ Subtract given amount from CIDR instance. If the result would under, the lowest
possible address is returned.
@class function
@sort 23
@sort 24
@name cidr.sub
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertible by
@ -999,7 +1016,7 @@ print(mac) -- "00:00:00:00:00:00"`
Calculate the lowest possible host address within this CIDR instance.
@class function
@sort 24
@sort 25
@name cidr.minhost
@return Returns a new CIDR instance representing the lowest host address
within this range.
@ -1017,7 +1034,7 @@ print(mac:minhost()) -- "00:14:22:01:00:01"`
Calculate the highest possible host address within this CIDR instance.
@class function
@sort 25
@sort 26
@name cidr.maxhost
@return Returns a new CIDR instance representing the highest host address
within this range.
@ -1042,7 +1059,7 @@ 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 26
@sort 27
@name cidr.string
@return Returns a string representing the range or address of this CIDR instance
]]