From 4f412a76c262acf099678fe97c058f295c749608 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 12 Mar 2018 08:18:11 +0100 Subject: luci-lib-ip: add MAC address calculation support Signed-off-by: Jo-Philipp Wich --- libs/luci-lib-ip/src/ip.c | 531 +++++++++++++++++++++++++++++------------ libs/luci-lib-ip/src/ip.luadoc | 343 +++++++++++++++++++++----- 2 files changed, 655 insertions(+), 219 deletions(-) (limited to 'libs') diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c index b91966c536..854a0c09c2 100644 --- a/libs/luci-lib-ip/src/ip.c +++ b/libs/luci-lib-ip/src/ip.c @@ -1,5 +1,5 @@ /* -Copyright 2015 Jo-Philipp Wich +Copyright 2015-2018 Jo-Philipp Wich 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,17 +297,21 @@ 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 + else if (family == AF_INET6) { p->family = AF_INET6; - p->bits = (bits < 0) ? 128 : bits; - p->len = sizeof(p->addr.v6); + p->bits = (bits < 0) ? AF_BITS(AF_INET6) : bits; p->addr.v6 = *(struct in6_addr *)addr; } + else + { + 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); lua_setmetatable(L, -2); @@ -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,45 +865,45 @@ static int _cidr_add_sub(lua_State *L, bool add) if (p1->family == p2->family) { - if (p1->family == AF_INET6) + if (p1->family == AF_INET) + { + a = ntohl(p1->addr.v4.s_addr); + b = ntohl(p2->addr.v4.s_addr); + + /* would over/underflow */ + if ((add && (UINT_MAX - a) < b) || (!add && a < b)) + { + r.addr.v4.s_addr = add * 0xFFFFFFFF; + ok = false; + } + else + { + r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); + } + } + else { - for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++) + for (i = 0, carry = 0; i < AF_BYTES(p1->family); 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; + BYTE(&r, i) = BYTE(p1, i) + BYTE(p2, i) + carry; + carry = (BYTE(p1, i) + 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)); + 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.v6, add * 0xFF, sizeof(r.addr.v6)); + memset(&r.addr.u8, add * 0xFF, AF_BYTES(r.family)); ok = false; } } - else - { - a = ntohl(p1->addr.v4.s_addr); - b = ntohl(p2->addr.v4.s_addr); - - /* would over/underflow */ - if ((add && (UINT_MAX - a) < b) || (!add && a < b)) - { - r.addr.v4.s_addr = add * 0xFFFFFFFF; - ok = false; - } - else - { - r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); - } - } } 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 = 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 = 128; + 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 }, diff --git a/libs/luci-lib-ip/src/ip.luadoc b/libs/luci-lib-ip/src/ip.luadoc index e32ae72f40..b1ecae1453 100644 --- a/libs/luci-lib-ip/src/ip.luadoc +++ b/libs/luci-lib-ip/src/ip.luadoc @@ -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 ]] ---[[ -Determine the route leading to the given destination. +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 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` ---[[ Fetch all routes, optionally matching the given criteria. @class function -@sort 5 +@sort 9 @name routes @param filter

Table containing one or more of the possible filter critera described below (optional)

@@ -258,7 +360,7 @@ end` ---[[ Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table @class function -@sort 6 +@sort 10 @name neighbors @param filter

Table containing one or more of the possible filter critera described below (optional)

@@ -306,7 +408,7 @@ A neighbour entry is a table containing the following fields: - + @@ -367,7 +469,7 @@ end)` ---[[ 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. - +
`mac`String containing the associated MAC addressMAC address `luci.ip.cidr` instance
`router`
`mac`String containing the link local address of the device in - dotted hex notationMAC address `luci.ip.cidr` instance representing the device ethernet + address
@usage
    @@ -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` ]] @@ -501,14 +605,52 @@ if addr:is6mapped4() then end` ]] +---[[ +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: -
    • An IPv4 address is always lower than an IPv6 address
    • +
      • An IPv4 address is always lower than an IPv6 address and IPv6 addresses +are considered lower than MAC addresses
      • Prefix sizes are ignored
      @class function -@sort 7 +@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: -
      • An IPv4 address is always lower than an IPv6 address
      • +
        • An IPv4 address is always lower than an IPv6 address and IPv6 addresses +are considered lower than MAC addresses
        • Prefix sizes are ignored
        @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.
      @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" + +addr6:add(256, true) -- true +print(addr6) -- "fe80::221:63f:fe75:ab17/64 + +addr6:add("ffff::", true) -- false (overflow) +print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64" -addr:add(256, true) -- true -print(addr) -- "fe80::221:63f:fe75:ab17/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" -addr:add("ffff::", true) -- false (overflow) -print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"` +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
        -
      • When substracting inplace: Return `true` if the substraction - succeded or `false` when the substraction underflowed.
      • +
      • When subtracting inplace: Return `true` if the subtraction + succeeded or `false` when the subtraction underflowed.
      • 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.
      + this instance minus the subtracted amount or the lowest address if + the subtraction underflowed.
    @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 ]] -- cgit v1.2.3