diff options
author | Jo-Philipp Wich <jo@mein.io> | 2019-12-09 17:31:51 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2019-12-09 17:31:51 +0100 |
commit | f7a7f89e0ce324115098f4496e3e72d8bb63613a (patch) | |
tree | 28e837ad77446fb7070ccf56762ae66e0ff70593 /libs/luci-lib-ip/src/ip.c | |
parent | ea6d0d2e1feb78a3237b71b559c5c7d7cf57f9d5 (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.c | 64 |
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 }, |