summaryrefslogtreecommitdiffhomepage
path: root/libs/luci-lib-ip/src/ip.c
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-12-09 17:31:51 +0100
committerJo-Philipp Wich <jo@mein.io>2019-12-09 17:31:51 +0100
commitf7a7f89e0ce324115098f4496e3e72d8bb63613a (patch)
tree28e837ad77446fb7070ccf56762ae66e0ff70593 /libs/luci-lib-ip/src/ip.c
parentea6d0d2e1feb78a3237b71b559c5c7d7cf57f9d5 (diff)
luci-lib-ip: support scoped IPv6 addresses
Ref: https://github.com/openwrt/luci/issues/3380 Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'libs/luci-lib-ip/src/ip.c')
-rw-r--r--libs/luci-lib-ip/src/ip.c64
1 files changed, 56 insertions, 8 deletions
diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c
index 188a70f144..34a120d1ae 100644
--- a/libs/luci-lib-ip/src/ip.c
+++ b/libs/luci-lib-ip/src/ip.c
@@ -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 },