From 4f412a76c262acf099678fe97c058f295c749608 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 12 Mar 2018 08:18:11 +0100 Subject: [PATCH] luci-lib-ip: add MAC address calculation support Signed-off-by: Jo-Philipp Wich --- documentation/api/modules/luci.ip.cidr.html | 330 ++++++++++++++--- documentation/api/modules/luci.ip.html | 288 ++++++++++++++- libs/luci-lib-ip/src/ip.c | 531 ++++++++++++++++++++-------- libs/luci-lib-ip/src/ip.luadoc | 343 ++++++++++++++---- 4 files changed, 1228 insertions(+), 264 deletions(-) diff --git a/documentation/api/modules/luci.ip.cidr.html b/documentation/api/modules/luci.ip.cidr.html index ce8c56795..5a2b06ec6 100644 --- a/documentation/api/modules/luci.ip.cidr.html +++ b/documentation/api/modules/luci.ip.cidr.html @@ -260,6 +260,30 @@ Checks whether the CIDR instance is an IPv6 mapped IPv4 address + cidr:ismac () + + +Checks whether the CIDR instance is an ethernet MAC address range + + + + + cidr:ismaclocal () + + +Checks whether the CIDR instance is a locally administered (LAA) MAC address + + + + + cidr:ismacmcast () + + +Checks whether the CIDR instance is a multicast MAC address + + + + cidr:lower (addr) @@ -323,6 +347,20 @@ Derive mapped IPv4 address of CIDR instance. + cidr:tomac () + + +Derive MAC address of IPv6 link local CIDR instance. + + + + cidr:tolinklocal () + + +Derive IPv6 link local address from MAC address CIDR instance. + + + cidr:contains (addr) @@ -405,6 +443,10 @@ Checks whether the CIDR instance is an IPv4 address range cidr:is6 +
  • + cidr:ismac + + @@ -499,6 +541,10 @@ Checks whether the CIDR instance is an IPv6 address range cidr:is4 +
  • + cidr:ismac + + @@ -566,13 +612,108 @@ end +
    cidr:ismac ()
    +
    + + +Checks whether the CIDR instance is an ethernet MAC address range + + + + + + + + +

    Return value:

    +true if the CIDR is a MAC address range, else false + + + +

    See also:

    + + +
    + + + + +
    cidr:ismaclocal ()
    +
    + + +Checks whether the CIDR instance is a locally administered (LAA) MAC address + + + + + + +

    Usage:

    +
    local mac = luci.ip.new("02:C0:FF:EE:00:01") 
    +if mac:ismaclocal() then 
    +  print("Is an LAA MAC address") 
    +end
    + + + +

    Return value:

    +true if the MAC address sets the locally administered bit. + + + +
    + + + + +
    cidr:ismacmcast ()
    +
    + + +Checks whether the CIDR instance is a multicast MAC address + + + + + + +

    Usage:

    +
    local mac = luci.ip.new("01:00:5E:7F:00:10") 
    +if addr:ismacmcast() then 
    +  print("Is a multicast MAC address") 
    +end
    + + + +

    Return value:

    +true if the MAC address sets the multicast bit. + + + +
    + + + +
    cidr:lower (addr)
    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
      @@ -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 +print(addr:lower(luci.ip.new("192.168.200.1"))) -- true +print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true @@ -629,7 +771,8 @@ 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
        @@ -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 +print(addr:higher(luci.ip.new("192.168.200.1"))) -- false +print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false @@ -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 +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 @@ -752,8 +900,8 @@ else the current prefix size is returned.
      • 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)
      @@ -800,8 +948,8 @@ optional mask parameter.
    • 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)
    @@ -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 32 for IPv4 and 128 for IPv6. +set to 32 for IPv4, 128 for IPv6 or 48 for MAC addresses. @@ -877,8 +1025,8 @@ prefix size can be overridden by the optional mask parameter.
  • 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)
  • @@ -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.
    • - 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) + mask: Either a number containing the number of bits (0..32 for IPv4) or + a string containing a valid netmask (optional)
    @@ -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 +
    cidr:tomac ()
    +
    + + +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. + + + + + + +

    Usage:

    +
    local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9") 
    +print(addr:tomac()) -- "64:66:B3:47:E1:B9"
    + + + +

    Return value:

    +Return a new CIDR instance representing the MAC address if this + instance is an IPv6 link local address, else return nothing. + + + +
    + + + + +
    cidr:tolinklocal ()
    +
    + + +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. + + + + + + +

    Usage:

    +
    local mac = luci.ip.new("64:66:B3:47:E1:B9") 
    +print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"
    + + + +

    Return value:

    +Return a new CIDR instance representing the IPv6 link local address. + + + +
    + + + +
    cidr:contains (addr)
    @@ -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 +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 @@ -1059,30 +1278,40 @@ address space, the result is set to the highest possible address.

    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" 
    + 
    +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(256, true)          -- true 
    -print(addr)                  -- "fe80::221:63f:fe75:ab17/64 
    +mac:add(256, true)             -- true 
    +print(mac)                     -- "00:14:22:01:24:45" 
      
    -addr:add("ffff::", true)     -- false (overflow) 
    -print(addr)                  -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"
    +mac:add("FF:FF:0:0:0:0", true) -- false (overflow) +print(mac) -- "FF:FF:FF:FF:FF:FF"

    Return value:

      -
    • When adding inplace: Return true if the addition succeeded +
    • When adding inplace: Return true if the addition succeded or false when the addition overflowed.
    • 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" -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" +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"

      Return value:

        -
      • When subtracting inplace: Return true if the subtraction - succeeded or false when the subtraction 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 subtracted amount or the lowest address if + this instance minus the subtracted amount or the lowest address if the subtraction underflowed.
      @@ -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" +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" @@ -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" +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" @@ -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. diff --git a/documentation/api/modules/luci.ip.html b/documentation/api/modules/luci.ip.html index 549a55307..1f89626fa 100644 --- a/documentation/api/modules/luci.ip.html +++ b/documentation/api/modules/luci.ip.html @@ -232,6 +232,34 @@ Construct a new IPv6 luci.ip.cidr instance. + MAC (address, netmask) + + +Construct a new MAC luci.ip.cidr instance. + + + + checkip4 (address) + + +Verify an IPv4 address. + + + + checkip6 (address) + + +Verify an IPv6 address. + + + + checkmac (address) + + +Verify an ethernet MAC address. + + + route (address) @@ -334,6 +362,10 @@ address/mask range. IPv6 +
    • + MAC + +
    @@ -389,6 +421,10 @@ A luci.ip.cidr object representing the given IPv4 range. IPv6 +
  • + MAC + + @@ -444,6 +480,252 @@ A luci.ip.cidr object representing the given IPv6 range. IPv4 +
  • + MAC + + + + + + + + + +
    MAC (address, netmask)
    +
    + + +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. + + +

    Parameters

    +
      + +
    • + address: String containing a valid ethernet MAC address, optionally with +prefix size (CIDR notation) or mask separated by slash. +
    • + +
    • + 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) +
    • + +
    + + + + +

    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
    + + + +

    Return value:

    +A luci.ip.cidr object representing the given MAC address range. + + + +

    See also:

    + + +
    + + + + +
    checkip4 (address)
    +
    + + +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. + + +

    Parameters

    +
      + +
    • + address: String containing a valid IPv4 address or existing +luci.ip.cidr IPv4 instance. +
    • + +
    + + + + +

    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
    + + + +

    Return value:

    +A string representing the given IPv4 address. + + + +

    See also:

    + + +
    + + + + +
    checkip6 (address)
    +
    + + +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. + + +

    Parameters

    +
      + +
    • + address: String containing a valid IPv6 address or existing +luci.ip.cidr IPv6 instance. +
    • + +
    + + + + +

    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
    + + + +

    Return value:

    +A string representing the given IPv6 address. + + + +

    See also:

    + + +
    + + + + +
    checkmac (address)
    +
    + + +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. + + +

    Parameters

    +
      + +
    • + address: String containing a valid MAC address or existing luci.ip.cidr +MAC address instance. +
    • + +
    + + + + +

    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
    + + + +

    Return value:

    +A string representing the given MAC address. + + + +

    See also:

    +
    @@ -787,7 +1069,7 @@ A neighbour entry is a table containing the following fields: mac - String containing the associated MAC address + MAC address luci.ip.cidr instance router @@ -905,8 +1187,8 @@ described below is returned, else an empty table. mac - String containing the link local address of the device in - dotted hex notation + MAC address luci.ip.cidr instance representing the device ethernet + address diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c index b91966c53..854a0c09c 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 e32ae72f4..b1ecae145 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