Changeset View
Changeset View
Standalone View
Standalone View
lib/flua/libifconfig/lua_ifconfig.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause | |||||
* | |||||
* Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org> | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/param.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/un.h> | |||||
#include <arpa/inet.h> | |||||
#include <net/ethernet.h> | |||||
#include <net/if.h> | |||||
#include <net/if_bridgevar.h> | |||||
#include <net/if_dl.h> | |||||
#include <net/ieee8023ad_lacp.h> | |||||
#include <net/if_lagg.h> | |||||
#include <net/if_media.h> | |||||
#include <net/if_types.h> | |||||
#include <netinet/in.h> | |||||
#include <netinet/ip_carp.h> | |||||
#include <netinet6/in6_var.h> | |||||
#include <netinet6/nd6.h> | |||||
#include <assert.h> | |||||
#include <errno.h> | |||||
#include <ifaddrs.h> | |||||
#include <netdb.h> | |||||
#include <stdbool.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <sysdecode.h> | |||||
#include <sysexits.h> | |||||
#include <libifconfig.h> | |||||
#include <libifconfig_sfp.h> | |||||
#include <lua.h> | |||||
#include <lauxlib.h> | |||||
#include <lualib.h> | |||||
#define IFCONFIG_HANDLE_META "ifconfig_handle_t" | |||||
#define STRUCT_IFADDRS_META "struct ifaddrs *" | |||||
#define SFP_DUMP_META "struct ifconfig_sfp_dump" | |||||
#define IFFBITS \ | |||||
"\020" \ | |||||
"\001UP" \ | |||||
"\002BROADCAST" \ | |||||
"\003DEBUG" \ | |||||
"\004LOOPBACK" \ | |||||
"\005POINTOPOINT" \ | |||||
"\007RUNNING" \ | |||||
"\010NOARP" \ | |||||
"\011PROMISC" \ | |||||
"\012ALLMULTI" \ | |||||
"\013OACTIVE" \ | |||||
"\014SIMPLEX" \ | |||||
"\015LINK0" \ | |||||
"\016LINK1" \ | |||||
"\017LINK2" \ | |||||
"\020MULTICAST" \ | |||||
"\022PPROMISC" \ | |||||
"\023MONITOR" \ | |||||
"\024STATICARP" | |||||
#define IFCAPBITS \ | |||||
"\020" \ | |||||
"\001RXCSUM" \ | |||||
"\002TXCSUM" \ | |||||
"\003NETCONS" \ | |||||
"\004VLAN_MTU" \ | |||||
"\005VLAN_HWTAGGING" \ | |||||
"\006JUMBO_MTU" \ | |||||
"\007POLLING" \ | |||||
"\010VLAN_HWCSUM" \ | |||||
"\011TSO4" \ | |||||
"\012TSO6" \ | |||||
"\013LRO" \ | |||||
"\014WOL_UCAST" \ | |||||
"\015WOL_MCAST" \ | |||||
"\016WOL_MAGIC" \ | |||||
"\017TOE4" \ | |||||
"\020TOE6" \ | |||||
"\021VLAN_HWFILTER" \ | |||||
"\023VLAN_HWTSO" \ | |||||
"\024LINKSTATE" \ | |||||
"\025NETMAP" \ | |||||
"\026RXCSUM_IPV6" \ | |||||
"\027TXCSUM_IPV6" \ | |||||
"\031TXRTLMT" \ | |||||
"\032HWRXTSTMP" \ | |||||
"\033NOMAP" \ | |||||
"\034TXTLS4" \ | |||||
"\035TXTLS6" | |||||
#define IN6BITS \ | |||||
"\020" \ | |||||
"\001anycast" \ | |||||
"\002tenative" \ | |||||
"\003duplicated" \ | |||||
"\004detached" \ | |||||
"\005deprecated" \ | |||||
"\007nodad" \ | |||||
"\010autoconf" \ | |||||
"\011temporary" \ | |||||
"\012prefer_source" | |||||
#define ND6BITS \ | |||||
"\020" \ | |||||
"\001nud" \ | |||||
"\002accept_rtadv" \ | |||||
"\003prefer_source" \ | |||||
"\004ifdisabled" \ | |||||
"\005dont_set_ifroute" \ | |||||
"\006auto_linklocal" \ | |||||
"\007no_radr" \ | |||||
"\010no_prefer_iface" \ | |||||
"\011no_dad" \ | |||||
"\012ipv6_only" \ | |||||
"\013ipv6_only" | |||||
#define LAGGHASHBITS \ | |||||
"\020" \ | |||||
"\001l2" \ | |||||
"\002l3" \ | |||||
"\003l4" | |||||
static const char *carp_states[] = { CARP_STATES }; | |||||
static const struct lagg_protos lagg_protos[] = LAGG_PROTOS; | |||||
static const char *stp_states[] = { STP_STATES }; | |||||
static const char *stp_protos[] = { STP_PROTOS }; | |||||
static const char *stp_roles[] = { STP_ROLES }; | |||||
int luaopen_ifconfig(lua_State *); | |||||
/* | |||||
* Create an array of the set flags in v, using the kernel %b format bits. | |||||
*/ | |||||
static void | |||||
push_flags_array(lua_State *L, unsigned v, const char *bits) | |||||
{ | |||||
int i, len; | |||||
lua_newtable(L); | |||||
/* Skip numeric format. */ | |||||
++bits; | |||||
while ((i = *bits++) != '\0') { | |||||
if ((v & (1 << (i - 1))) != 0) { | |||||
for (len = 0; bits[len] > 32; ++len); | |||||
lua_pushlstring(L, bits, len); | |||||
lua_rawseti(L, -2, i); | |||||
bits += len; | |||||
} else { | |||||
for (; *bits > 32; ++bits); | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* Decode an array of the flags to set and return as an int. | |||||
*/ | |||||
static int | |||||
pop_flags_array(lua_State *L, int index, const char *bits) | |||||
{ | |||||
int v = 0; | |||||
/* Skip numeric format. */ | |||||
++bits; | |||||
lua_pushnil(L); /* first key */ | |||||
while (lua_next(L, index) != 0) { | |||||
const char *flag, *flags = bits; | |||||
int i, len; | |||||
luaL_checkstring(L, index + 2); | |||||
flag = lua_tostring(L, index + 2); | |||||
lua_pop(L, 1); | |||||
while ((i = *flags++) != '\0') { | |||||
for (len = 0; flags[len] > 32; ++len); | |||||
if (strncmp(flag, flags, len) == 0) { | |||||
v |= 1 << (i - 1); | |||||
break; | |||||
} | |||||
flags += len; | |||||
} | |||||
} | |||||
return (v); | |||||
} | |||||
/* | |||||
* Push a table describing media information. | |||||
*/ | |||||
static void | |||||
push_media_info(lua_State *L, int ifmw) | |||||
{ | |||||
char opts[80]; | |||||
int index; | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
lua_pushstring(L, ifconfig_media_get_type(ifmw)); | |||||
lua_setfield(L, index, "type"); | |||||
lua_pushstring(L, ifconfig_media_get_subtype(ifmw)); | |||||
lua_setfield(L, index, "subtype"); | |||||
ifconfig_media_get_options_string(ifmw, opts, sizeof opts); | |||||
lua_pushstring(L, opts); | |||||
lua_setfield(L, index, "options"); | |||||
} | |||||
/* | |||||
* Push a table describing an AF_INET address or nil if error. | |||||
*/ | |||||
static void | |||||
push_inet4_addrinfo(lua_State *L, ifconfig_handle_t *h, struct ifaddrs *ifa) | |||||
{ | |||||
char addr_buf[NI_MAXHOST]; | |||||
struct ifconfig_inet_addr addr; | |||||
int index; | |||||
if (ifconfig_inet_get_addrinfo(h, ifa->ifa_name, ifa, &addr) != 0) { | |||||
lua_pushnil(L); | |||||
return; | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
/* TODO: can this just be inet_ntoa? */ | |||||
inet_ntop(AF_INET, &addr.sin->sin_addr, addr_buf, sizeof addr_buf); | |||||
lua_pushstring(L, addr_buf); | |||||
lua_setfield(L, index, "inet"); | |||||
if (addr.dst != NULL) { | |||||
lua_pushstring(L, inet_ntoa_r(addr.dst->sin_addr, addr_buf, | |||||
sizeof addr_buf)); | |||||
lua_setfield(L, index, "destination"); | |||||
} | |||||
lua_pushstring(L, inet_ntoa_r(addr.netmask->sin_addr, addr_buf, | |||||
sizeof addr_buf)); | |||||
lua_setfield(L, index, "netmask"); | |||||
if (addr.broadcast != NULL) { | |||||
lua_pushstring(L, inet_ntoa_r(addr.broadcast->sin_addr, | |||||
addr_buf, sizeof addr_buf)); | |||||
lua_setfield(L, index, "broadcast"); | |||||
} | |||||
if (addr.vhid != 0) { | |||||
lua_pushinteger(L, addr.vhid); | |||||
lua_setfield(L, index, "vhid"); | |||||
} | |||||
} | |||||
/* | |||||
* Push a table describing an AF_INET6 address or nil if error. | |||||
*/ | |||||
static void | |||||
push_inet6_addrinfo(lua_State *L, ifconfig_handle_t *h, struct ifaddrs *ifa) | |||||
{ | |||||
char addr_buf[NI_MAXHOST]; | |||||
struct ifconfig_inet6_addr addr; | |||||
struct timespec now; | |||||
int index; | |||||
if (ifconfig_inet6_get_addrinfo(h, ifa->ifa_name, ifa, &addr) != 0) { | |||||
lua_pushnil(L); | |||||
return; | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
if (getnameinfo((struct sockaddr *)addr.sin6, addr.sin6->sin6_len, | |||||
addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST) != 0) | |||||
/* TODO: can this just be inet_ntoa? */ | |||||
inet_ntop(AF_INET6, &addr.sin6->sin6_addr, addr_buf, | |||||
sizeof addr_buf); | |||||
lua_pushstring(L, addr_buf); | |||||
lua_setfield(L, index, "inet6"); | |||||
if (addr.dstin6 != NULL) { | |||||
inet_ntop(AF_INET6, addr.dstin6, addr_buf, sizeof addr_buf); | |||||
lua_pushstring(L, addr_buf); | |||||
lua_setfield(L, index, "destination"); | |||||
} | |||||
lua_pushinteger(L, addr.prefixlen); | |||||
lua_setfield(L, index, "prefixlen"); | |||||
if (addr.sin6->sin6_scope_id != 0) { | |||||
lua_pushinteger(L, addr.sin6->sin6_scope_id); | |||||
lua_setfield(L, index, "scopeid"); | |||||
} | |||||
push_flags_array(L, addr.flags, IN6BITS); | |||||
lua_setfield(L, index, "flags"); | |||||
clock_gettime(CLOCK_MONOTONIC_FAST, &now); | |||||
if (addr.lifetime.ia6t_preferred || addr.lifetime.ia6t_expire) { | |||||
lua_pushinteger(L, MAX(0l, | |||||
addr.lifetime.ia6t_preferred - now.tv_sec)); | |||||
lua_setfield(L, index, "preferred_lifetime"); | |||||
lua_pushinteger(L, MAX(0l, | |||||
addr.lifetime.ia6t_expire - now.tv_sec)); | |||||
lua_setfield(L, index, "valid_lifetime"); | |||||
} | |||||
if (addr.vhid != 0) { | |||||
lua_pushinteger(L, addr.vhid); | |||||
lua_setfield(L, index, "vhid"); | |||||
} | |||||
} | |||||
/* | |||||
* Push a table describing an AF_LINK address or nil if error. | |||||
*/ | |||||
static void | |||||
push_link_addrinfo(lua_State *L, ifconfig_handle_t *h __unused, | |||||
struct ifaddrs *ifa) | |||||
{ | |||||
char addr_buf[NI_MAXHOST]; | |||||
union { | |||||
struct sockaddr *a; | |||||
struct sockaddr_dl *dl; | |||||
} s; | |||||
int index, n; | |||||
s.a = ifa->ifa_addr; | |||||
if (s.dl == NULL || s.dl->sdl_alen <= 0) { | |||||
lua_pushnil(L); | |||||
return; | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
switch (s.dl->sdl_type) { | |||||
case IFT_ETHER: | |||||
case IFT_L2VLAN: | |||||
case IFT_BRIDGE: | |||||
if (s.dl->sdl_alen != ETHER_ADDR_LEN) | |||||
break; | |||||
ether_ntoa_r((struct ether_addr *)LLADDR(s.dl), addr_buf); | |||||
lua_pushstring(L, addr_buf); | |||||
lua_setfield(L, index, "ether"); | |||||
break; | |||||
default: | |||||
n = s.dl->sdl_nlen > 0 ? s.dl->sdl_nlen + 1 : 0; | |||||
lua_pushstring(L, link_ntoa(s.dl) + n); /* FIXME: link_ntoa_r */ | |||||
lua_setfield(L, index, "lladdr"); | |||||
break; | |||||
} | |||||
} | |||||
/* | |||||
* Push a table describing an AF_LOCAL address. | |||||
*/ | |||||
static void | |||||
push_local_addrinfo(lua_State *L, ifconfig_handle_t *h __unused, | |||||
struct ifaddrs *ifa) | |||||
{ | |||||
union { | |||||
struct sockaddr *a; | |||||
struct sockaddr_un *un; | |||||
} s; | |||||
int index; | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
s.a = ifa->ifa_addr; | |||||
if (strlen(s.un->sun_path) == 0) | |||||
lua_pushstring(L, "-"); | |||||
else | |||||
lua_pushstring(L, s.un->sun_path); | |||||
lua_setfield(L, index, "path"); | |||||
} | |||||
/* | |||||
* Push a table describing SFP channel power. | |||||
*/ | |||||
static void | |||||
push_channel_power(lua_State *L, uint16_t power) | |||||
{ | |||||
int index; | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
lua_pushinteger(L, power); | |||||
lua_setfield(L, index, "value"); | |||||
lua_pushnumber(L, power_mW(power)); | |||||
lua_setfield(L, index, "mW"); | |||||
lua_pushnumber(L, power_dBm(power)); | |||||
lua_setfield(L, index, "dBm"); | |||||
} | |||||
/* | |||||
* Push a table describing SFP channel bias current. | |||||
*/ | |||||
static void | |||||
push_channel_bias(lua_State *L, uint16_t bias) | |||||
{ | |||||
int index; | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
lua_pushinteger(L, bias); | |||||
lua_setfield(L, index, "raw"); | |||||
lua_pushnumber(L, bias_mA(bias)); | |||||
lua_setfield(L, index, "mA"); | |||||
} | |||||
/* | |||||
* Check the stack for an ifconfig handle userdata at the given index. | |||||
*/ | |||||
static ifconfig_handle_t * | |||||
l_ifconfig_checkhandle(lua_State *L, int index) | |||||
{ | |||||
ifconfig_handle_t *h, **hp; | |||||
hp = luaL_checkudata(L, index, IFCONFIG_HANDLE_META); | |||||
assert(hp != NULL); | |||||
h = *hp; | |||||
luaL_argcheck(L, h != NULL, index, "invalid ifconfig handle"); | |||||
return (h); | |||||
} | |||||
/* | |||||
* Check the stack for an ifaddrs userdata at the given index. | |||||
*/ | |||||
static struct ifaddrs * | |||||
l_ifconfig_checkifaddrs(lua_State *L, int index) | |||||
{ | |||||
struct ifaddrs *ifa, **ifap; | |||||
ifap = luaL_checkudata(L, index, STRUCT_IFADDRS_META); | |||||
assert(ifap != NULL); | |||||
ifa = *ifap; | |||||
luaL_argcheck(L, ifa != NULL, index, "invalid ifaddr"); | |||||
return (ifa); | |||||
} | |||||
/* | |||||
* Invoke a callback function on the stack with the handle and accumulator | |||||
* from the stack and the given ifaddrs, leaving the handle, callback, and | |||||
* accumulator result on the stack after the call. | |||||
* | |||||
* This is used by the ifconfig_foreach_* functions to invoke a lua callback. | |||||
*/ | |||||
static void | |||||
foreach_cb(ifconfig_handle_t *h __unused, struct ifaddrs *ifa, void *udata) | |||||
{ | |||||
lua_State *L = udata; | |||||
struct ifaddrs **ifap; | |||||
/* Stack: h,cb,acc */ | |||||
/* Make copies of the callback and handle positioned for the call. */ | |||||
lua_pushvalue(L, 1); /* -> h,cb,acc,h */ | |||||
lua_pushvalue(L, 2); /* -> h,cb,acc,h,cb */ | |||||
lua_insert(L, 3); /* -> h,cb,cb,acc,h */ | |||||
lua_insert(L, 4); /* -> h,cb,cb,h,acc */ | |||||
/* Push the ifa userdata. */ | |||||
ifap = lua_newuserdata(L, sizeof ifa); /* -> h,cb,cb,h,acc,ifa */ | |||||
assert(ifap != NULL); | |||||
*ifap = ifa; | |||||
luaL_getmetatable(L, STRUCT_IFADDRS_META); | |||||
lua_setmetatable(L, -2); | |||||
/* Reposition the accumulator to be last so it can be optional. */ | |||||
lua_insert(L, 5); /* -> h,cb,cb,h,ifa,acc */ | |||||
/* Invoke the callback. */ | |||||
lua_call(L, 3, 1); /* -> h,cb,acc */ | |||||
} | |||||
/** Explicit close method | |||||
*/ | |||||
static int | |||||
l_ifconfig_handle_close(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h, **hp; | |||||
hp = luaL_checkudata(L, 1, IFCONFIG_HANDLE_META); | |||||
assert(hp != NULL); | |||||
h = *hp; | |||||
if (h != NULL) { | |||||
ifconfig_close(h); | |||||
*hp = NULL; | |||||
} | |||||
return (0); | |||||
} | |||||
/** Get error info | |||||
* @return errtype, errno, ioctlname? | |||||
*/ | |||||
static int | |||||
l_ifconfig_handle_error(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
ifconfig_errtype errtype; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
errtype = ifconfig_err_errtype(h); | |||||
switch (errtype) { | |||||
case OK: | |||||
lua_pushnil(L); | |||||
return (1); | |||||
case OTHER: | |||||
lua_pushstring(L, "OTHER"); | |||||
break; | |||||
case IOCTL: | |||||
lua_pushstring(L, "IOCTL"); | |||||
break; | |||||
case SOCKET: | |||||
lua_pushstring(L, "SOCKET"); | |||||
break; | |||||
default: | |||||
lua_pushstring(L, "<unknown>"); | |||||
break; | |||||
} | |||||
lua_pushinteger(L, ifconfig_err_errno(h)); | |||||
if (errtype == IOCTL) { | |||||
unsigned long req = ifconfig_err_ioctlreq(h); | |||||
lua_pushstring(L, sysdecode_ioctlname(req)); | |||||
return (3); | |||||
} | |||||
return (2); | |||||
} | |||||
/** Higher-order fold iterator over interfaces | |||||
* @param cb A callback function (handle, iface, udata) -> udata | |||||
* @param udata An initial accumulator value (may be nil) | |||||
* @return The final accumulator value returned by cb | |||||
*/ | |||||
static int | |||||
l_ifconfig_foreach_iface(lua_State *L) | |||||
{ | |||||
const int MAXARGS = 3; | |||||
ifconfig_handle_t *h; | |||||
int n; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checktype(L, 2, LUA_TFUNCTION); | |||||
/* Ensure the correct number of args. Extras are discarded. */ | |||||
n = lua_gettop(L); | |||||
if (n == (MAXARGS - 1)) | |||||
lua_pushnil(L); | |||||
else if (n > MAXARGS) | |||||
lua_pop(L, n - MAXARGS); | |||||
if (ifconfig_foreach_iface(h, foreach_cb, L) != 0) | |||||
return luaL_error(L, "iteration failed"); | |||||
return (1); | |||||
} | |||||
/** Higher-order fold iterator over interface addresses | |||||
* @param iface A STRUCT_IFADDRS_META userdata value from foreach_iface | |||||
* @param cb A callback function (handle, ifaddr, udata) -> udata | |||||
* @param udata An initial accumulator value (may be nil) | |||||
* @return The final accumulator value returned by cb | |||||
*/ | |||||
static int | |||||
l_ifconfig_foreach_ifaddr(lua_State *L) | |||||
{ | |||||
const int MAXARGS = 4; | |||||
ifconfig_handle_t *h; | |||||
struct ifaddrs *ifa; | |||||
int n; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
ifa = l_ifconfig_checkifaddrs(L, 2); | |||||
luaL_checktype(L, 3, LUA_TFUNCTION); | |||||
/* Ensure the correct number of args. Extras are discarded. */ | |||||
n = lua_gettop(L); | |||||
if (n == (MAXARGS - 1)) | |||||
lua_pushnil(L); | |||||
else if (n > MAXARGS) | |||||
lua_pop(L, n - MAXARGS); | |||||
lua_remove(L, 2); | |||||
ifconfig_foreach_ifaddr(h, ifa, foreach_cb, L); | |||||
return (1); | |||||
} | |||||
/** Get interface description | |||||
* @param name The name of an interface | |||||
* @return Description string if set, otherwise nil | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_description(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
char *desc; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_description(h, name, &desc) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, desc); | |||||
return (1); | |||||
} | |||||
/** Set interface description | |||||
* @param name The name of an interface | |||||
* @param desc Description string to set | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_description(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name, *desc; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkstring(L, 3); | |||||
name = lua_tostring(L, 2); | |||||
desc = lua_tostring(L, 3); | |||||
if (ifconfig_set_description(h, name, desc) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Unset interface description | |||||
* @param name The name of an interface | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_unset_description(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_unset_description(h, name) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Set interface name | |||||
* @param name The name of an interface | |||||
* @param newname A new name for the interface | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_name(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name, *newname; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkstring(L, 3); | |||||
name = lua_tostring(L, 2); | |||||
newname = lua_tostring(L, 3); | |||||
if (ifconfig_set_name(h, name, newname) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Get original interface name | |||||
* @param name The name of an interface | |||||
* @return Original interface name or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_orig_name(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
char *orig_name; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_orig_name(h, name, &orig_name) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, orig_name); | |||||
return (1); | |||||
} | |||||
#if 0 /* setfib not implemented in libifconfig? */ | |||||
/** Set interface fib | |||||
* @param name The name of an interface | |||||
* @param fib A fib number to use | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_fib(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
ptrdiff_t fib; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkinteger(L, 3); | |||||
name = lua_tostring(L, 2); | |||||
fib = lua_tointeger(L, 3); | |||||
luaL_argcheck(L, INT_MAX < fib || fib < INT_MIN, 3, "fib out of range"); | |||||
if (ifconfig_set_fib(h, name, fib) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
#endif | |||||
/** Get interface fib | |||||
* @param name The name of an interface | |||||
* @return fib of the interface or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_fib(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
int fib; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_fib(h, name, &fib) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushinteger(L, fib); | |||||
return (1); | |||||
} | |||||
/** Set interface mtu | |||||
* @param name The name of an interface | |||||
* @param mtu A mtu to use | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_mtu(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
ptrdiff_t mtu; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkinteger(L, 3); | |||||
name = lua_tostring(L, 2); | |||||
mtu = lua_tointeger(L, 3); | |||||
luaL_argcheck(L, INT_MIN <= mtu && mtu <= INT_MAX, 3, "mtu out of range"); | |||||
if (ifconfig_set_mtu(h, name, mtu) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Get interface mtu | |||||
* @param name The name of an interface | |||||
* @return mtu of the interface or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_mtu(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
int mtu; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_mtu(h, name, &mtu) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushinteger(L, mtu); | |||||
return (1); | |||||
} | |||||
/** Get interface nd6 info | |||||
* @param name The name of an interface | |||||
* @return An list of the enabled nd6 options or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_nd6(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct in6_ndireq nd; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_nd6(h, name, &nd) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
push_flags_array(L, nd.ndi.flags, ND6BITS); | |||||
return (1); | |||||
} | |||||
/** Set interface metric | |||||
* @param name The name of an interface | |||||
* @param metric A metric to use | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_metric(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
ptrdiff_t metric; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkinteger(L, 3); | |||||
name = lua_tostring(L, 2); | |||||
metric = lua_tointeger(L, 3); | |||||
luaL_argcheck(L, INT_MAX < metric || metric < INT_MIN, 3, | |||||
"metric out of range"); | |||||
if (ifconfig_set_metric(h, name, metric) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Get interface metric | |||||
* @param name The name of an interface | |||||
* @return metric of the interface or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_metric(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
int metric; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_metric(h, name, &metric) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushinteger(L, metric); | |||||
return (1); | |||||
} | |||||
/** Set interface capabilities | |||||
* @param name The name of an interface | |||||
* @param caps An array of the capabilities to be enabled on the interface | |||||
* (capabilities not listed will be disabled) | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_capabilities(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
int capability; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checktype(L, 3, LUA_TTABLE); | |||||
name = lua_tostring(L, 2); | |||||
capability = pop_flags_array(L, 3, IFCAPBITS); | |||||
if (ifconfig_set_capability(h, name, capability) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Get interface capabilities | |||||
* @param name The name of an interface | |||||
* @return A table of the enabled/supported capabilites or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_capabilities(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_capabilities capability; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_capability(h, name, &capability) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
lua_pushstring(L, "enabled"); | |||||
push_flags_array(L, capability.curcap, IFCAPBITS); | |||||
lua_rawset(L, 3); | |||||
lua_pushstring(L, "supported"); | |||||
push_flags_array(L, capability.reqcap, IFCAPBITS); | |||||
lua_rawset(L, 3); | |||||
return (1); | |||||
} | |||||
/** Get interface groups list | |||||
* @param name The name of an interface | |||||
* @return A table of the groups containing the interface or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_groups(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifgroupreq ifgr; | |||||
struct ifg_req *ifg; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_groups(h, name, &ifgr) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
for (ifg = ifgr.ifgr_groups; | |||||
ifg != NULL && ifgr.ifgr_len >= sizeof *ifg; | |||||
++ifg, ifgr.ifgr_len -= sizeof *ifg) { | |||||
if (strcmp(ifg->ifgrq_group, "all") == 0) | |||||
continue; | |||||
lua_pushstring(L, ifg->ifgrq_group); | |||||
lua_rawseti(L, 3, lua_rawlen(L, 3) + 1); | |||||
} | |||||
return (1); | |||||
} | |||||
/** Get interface status | |||||
* @param name The name of an interface | |||||
* @return Interface status as a string or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_status(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifstat status; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_get_ifstatus(h, name, &status) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, status.ascii); | |||||
return (1); | |||||
} | |||||
/** Get interface media info | |||||
* @param name The name of an interface | |||||
* @return A table describing the interface media or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_media(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifmediareq *ifmr; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_media_get_mediareq(h, name, &ifmr) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
push_media_info(L, ifmr->ifm_current); | |||||
lua_setfield(L, 3, "current"); | |||||
push_media_info(L, ifmr->ifm_active); | |||||
lua_setfield(L, 3, "active"); | |||||
lua_newtable(L); | |||||
for (int i = 0; i < ifmr->ifm_count; ++i) { | |||||
push_media_info(L, ifmr->ifm_ulist[i]); | |||||
lua_rawseti(L, 4, lua_rawlen(L, 4) + 1); | |||||
} | |||||
lua_setfield(L, 3, "supported"); | |||||
lua_pushstring(L, ifconfig_media_get_status(ifmr)); | |||||
lua_setfield(L, 3, "status"); | |||||
free(ifmr); | |||||
return (1); | |||||
} | |||||
/** Get interface carp info | |||||
* @param name | |||||
* @return A table describing the interface carp vhids or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_carp(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct carpreq carpr[CARP_MAXVHID]; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_carp_get_info(h, name, carpr, nitems(carpr)) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
for (int i = 0; i < carpr[0].carpr_count; ++i) { | |||||
lua_newtable(L); | |||||
lua_pushstring(L, carp_states[carpr[i].carpr_state]); | |||||
lua_setfield(L, 4, "state"); | |||||
lua_pushinteger(L, carpr[i].carpr_vhid); | |||||
lua_setfield(L, 4, "vhid"); | |||||
lua_pushinteger(L, carpr[i].carpr_advbase); | |||||
lua_setfield(L, 4, "advbase"); | |||||
lua_pushinteger(L, carpr[i].carpr_advskew); | |||||
lua_setfield(L, 4, "advskew"); | |||||
lua_rawseti(L, 3, lua_rawlen(L, 3) + 1); | |||||
} | |||||
return (1); | |||||
} | |||||
/** Get address info | |||||
* @param addr An ifaddrs userdata object | |||||
* @return Table describing the address or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_addr_info(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
struct ifaddrs *ifa; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
ifa = l_ifconfig_checkifaddrs(L, 2); | |||||
switch (ifa->ifa_addr->sa_family) { | |||||
case AF_INET: | |||||
push_inet4_addrinfo(L, h, ifa); | |||||
break; | |||||
case AF_INET6: | |||||
push_inet6_addrinfo(L, h, ifa); | |||||
break; | |||||
case AF_LINK: | |||||
push_link_addrinfo(L, h, ifa); | |||||
break; | |||||
case AF_LOCAL: | |||||
push_local_addrinfo(L, h, ifa); | |||||
break; | |||||
case AF_UNSPEC: | |||||
default: | |||||
lua_pushnil(L); | |||||
break; | |||||
} | |||||
return (1); | |||||
} | |||||
/** Get bridge interface status | |||||
* @param name The name of a bridge interface | |||||
* @return Table describing the status of the bridge or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_bridge_status(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_bridge_status *bridge; | |||||
struct ifbropreq *params; | |||||
struct ifbreq *member; | |||||
uint8_t lladdr[ETHER_ADDR_LEN]; | |||||
char addr_buf[NI_MAXHOST]; | |||||
int index; | |||||
uint16_t bprio; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_bridge_get_bridge_status(h, name, &bridge) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
lua_pushinteger(L, bridge->cache_size); | |||||
lua_setfield(L, index, "address_cache_size"); | |||||
lua_pushinteger(L, bridge->cache_lifetime); | |||||
lua_setfield(L, index, "address_cache_lifetime"); | |||||
params = bridge->params; | |||||
lua_pushinteger(L, params->ifbop_priority); | |||||
lua_setfield(L, index, "priority"); | |||||
if (params->ifbop_protocol < nitems(stp_protos)) | |||||
lua_pushstring(L, stp_protos[params->ifbop_protocol]); | |||||
else | |||||
lua_pushinteger(L, params->ifbop_protocol); | |||||
lua_setfield(L, index, "protocol"); | |||||
lua_pushinteger(L, params->ifbop_hellotime); | |||||
lua_setfield(L, index, "hello_time"); | |||||
lua_pushinteger(L, params->ifbop_fwddelay); | |||||
lua_setfield(L, index, "forward_delay"); | |||||
lua_pushinteger(L, params->ifbop_holdcount); | |||||
lua_setfield(L, index, "hold_count"); | |||||
lua_pushinteger(L, params->ifbop_maxage); | |||||
lua_setfield(L, index, "max_age"); | |||||
PV2ID(params->ifbop_bridgeid, bprio, lladdr); | |||||
lua_pushstring(L, ether_ntoa_r((struct ether_addr *)lladdr, addr_buf)); | |||||
lua_setfield(L, index, "id"); | |||||
PV2ID(params->ifbop_designated_root, bprio, lladdr); | |||||
lua_pushstring(L, ether_ntoa_r((struct ether_addr *)lladdr, addr_buf)); | |||||
lua_setfield(L, index, "root_id"); | |||||
lua_pushinteger(L, bprio); | |||||
lua_setfield(L, index, "root_priority"); | |||||
lua_pushinteger(L, params->ifbop_root_path_cost); | |||||
lua_setfield(L, index, "root_path_cost"); | |||||
lua_pushinteger(L, params->ifbop_root_port & 0xfff); | |||||
lua_setfield(L, index, "root_port"); | |||||
lua_newtable(L); | |||||
++index; | |||||
for (size_t i = 0; i < bridge->members_count; ++i) { | |||||
member = bridge->members + i; | |||||
lua_newtable(L); | |||||
++index; | |||||
push_flags_array(L, member->ifbr_ifsflags, IFBIFBITS); | |||||
lua_setfield(L, index, "flags"); | |||||
lua_pushinteger(L, member->ifbr_addrmax); | |||||
lua_setfield(L, index, "ifmaxaddr"); | |||||
lua_pushinteger(L, member->ifbr_portno); | |||||
lua_setfield(L, index, "port"); | |||||
lua_pushinteger(L, member->ifbr_priority); | |||||
lua_setfield(L, index, "priority"); | |||||
lua_pushinteger(L, member->ifbr_path_cost); | |||||
lua_setfield(L, index, "path_cost"); | |||||
if (member->ifbr_ifsflags & IFBIF_STP) { | |||||
if (member->ifbr_proto < nitems(stp_protos)) | |||||
lua_pushstring(L, | |||||
stp_protos[member->ifbr_proto]); | |||||
else | |||||
lua_pushinteger(L, member->ifbr_proto); | |||||
lua_setfield(L, index, "protocol"); | |||||
if (member->ifbr_role < nitems(stp_roles)) | |||||
lua_pushstring(L, | |||||
stp_roles[member->ifbr_role]); | |||||
else | |||||
lua_pushinteger(L, member->ifbr_role); | |||||
lua_setfield(L, index, "role"); | |||||
if (member->ifbr_state < nitems(stp_states)) | |||||
lua_pushstring(L, | |||||
stp_states[member->ifbr_state]); | |||||
else | |||||
lua_pushinteger(L, member->ifbr_state); | |||||
lua_setfield(L, index, "state"); | |||||
} | |||||
--index; | |||||
lua_setfield(L, index, member->ifbr_ifsname); | |||||
} | |||||
--index; | |||||
lua_setfield(L, index, "members"); | |||||
ifconfig_bridge_free_bridge_status(bridge); | |||||
return (1); | |||||
} | |||||
/** Get lagg interface status | |||||
* @param name The name of a lagg interface | |||||
* @return Table describing the status of the lagg or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_lagg_status(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_lagg_status *lagg_status; | |||||
const char *protoname = "<unknown>"; | |||||
int index; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_lagg_get_lagg_status(h, name, &lagg_status) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
for (size_t i = 0; i < nitems(lagg_protos); ++i) { | |||||
if (lagg_status->ra->ra_proto == lagg_protos[i].lpr_proto) { | |||||
protoname = lagg_protos[i].lpr_name; | |||||
break; | |||||
} | |||||
} | |||||
lua_pushstring(L, protoname); | |||||
lua_setfield(L, index, "laggproto"); | |||||
push_flags_array(L, lagg_status->rf->rf_flags & LAGG_F_HASHMASK, | |||||
LAGGHASHBITS); | |||||
lua_setfield(L, index, "lagghash"); | |||||
lua_newtable(L); | |||||
++index; | |||||
push_flags_array(L, lagg_status->ro->ro_opts, LAGG_OPT_BITS); | |||||
lua_setfield(L, index, "flags"); | |||||
lua_pushinteger(L, lagg_status->ro->ro_flowid_shift); | |||||
lua_setfield(L, index, "flowid_shift"); | |||||
if (lagg_status->ra->ra_proto == LAGG_PROTO_ROUNDROBIN) { | |||||
lua_pushinteger(L, lagg_status->ro->ro_bkt); | |||||
lua_setfield(L, index, "rr_limit"); | |||||
} | |||||
--index; | |||||
lua_setfield(L, index, "options"); | |||||
lua_newtable(L); | |||||
++index; | |||||
lua_pushinteger(L, lagg_status->ro->ro_active); | |||||
lua_setfield(L, index, "active"); | |||||
lua_pushinteger(L, lagg_status->ro->ro_flapping); | |||||
lua_setfield(L, index, "flapping"); | |||||
--index; | |||||
lua_setfield(L, index, "stats"); | |||||
lua_newtable(L); | |||||
++index; | |||||
for (int i = 0; i < lagg_status->ra->ra_ports; ++i) { | |||||
struct lagg_reqport *lagg_port = &lagg_status->ra->ra_port[i]; | |||||
lua_newtable(L); | |||||
++index; | |||||
lua_pushstring(L, lagg_port->rp_portname); | |||||
lua_setfield(L, index, "laggport"); | |||||
push_flags_array(L, lagg_port->rp_flags, LAGG_PORT_BITS); | |||||
lua_setfield(L, index, "flags"); | |||||
if (lagg_status->ra->ra_proto == LAGG_PROTO_LACP) { | |||||
struct lacp_opreq *lacp_port = (struct lacp_opreq *) | |||||
&lagg_port->rp_lacpreq; | |||||
push_flags_array(L, lacp_port->actor_state, | |||||
LACP_STATE_BITS); | |||||
lua_setfield(L, index, "lacp_state"); | |||||
} | |||||
--index; | |||||
lua_rawseti(L, index, lua_rawlen(L, index) + 1); | |||||
} | |||||
--index; | |||||
lua_setfield(L, index, "ports"); | |||||
ifconfig_lagg_free_lagg_status(lagg_status); | |||||
return (1); | |||||
} | |||||
/** Get the parent laggdev of a laggport interface | |||||
* @param name The name of a laggport interface | |||||
* @return Parent laggdev name or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_laggport_laggdev(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct lagg_reqport lagg_port; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_lagg_get_laggport_status(h, name, &lagg_port) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, lagg_port.rp_ifname); | |||||
return (1); | |||||
} | |||||
/** Destroy an interface | |||||
* @param name The name of an interface | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_destroy(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_destroy_interface(h, name) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Create an interface | |||||
* @param name The name of an interface | |||||
* @return Created interface name or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_create(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
char *ifname; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_create_interface(h, name, &ifname) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, ifname); | |||||
return (1); | |||||
} | |||||
/** Create a vlan interface | |||||
* @param name The name of a vlan interface to create | |||||
* @param vlandev The name of an interface to attach to | |||||
* @param vlantag A vlan number to use | |||||
* @return The name of the created interface or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_create_vlan(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name, *vlandev; | |||||
char *ifname; | |||||
ptrdiff_t vlantag; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkstring(L, 3); | |||||
luaL_checkinteger(L, 4); | |||||
name = lua_tostring(L, 2); | |||||
vlandev = lua_tostring(L, 3); | |||||
vlantag = lua_tointeger(L, 4); | |||||
luaL_argcheck(L, 0 <= vlantag && vlantag <= UINT16_MAX, 4, | |||||
"vlantag out of range"); | |||||
if (ifconfig_create_interface_vlan(h, name, &ifname, vlandev, vlantag) != 0) | |||||
lua_pushnil(L); | |||||
else | |||||
lua_pushstring(L, ifname); | |||||
return (1); | |||||
} | |||||
/** Set vlandev and vlantag for a vlan interface | |||||
* @param name The name of a vlan interface | |||||
* @param vlandev The name of the interface to attach to | |||||
* @param vlantag A vlan number to use | |||||
* @return true if success, false if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_set_vlantag(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name, *vlandev; | |||||
ptrdiff_t vlantag; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
luaL_checkstring(L, 3); | |||||
luaL_checkinteger(L, 4); | |||||
name = lua_tostring(L, 2); | |||||
vlandev = lua_tostring(L, 3); | |||||
vlantag = lua_tointeger(L, 4); | |||||
luaL_argcheck(L, 0 <= vlantag && vlantag <= UINT16_MAX, 4, | |||||
"vlantag out of range"); | |||||
if (ifconfig_set_vlantag(h, name, vlandev, vlantag) != 0) | |||||
lua_pushboolean(L, false); | |||||
else | |||||
lua_pushboolean(L, true); | |||||
return (1); | |||||
} | |||||
/** Get a list of all the interface cloners available on the system | |||||
* @return a list of the names of known interface cloners or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_list_cloners(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
char *cloners; | |||||
size_t cloners_count; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
if (ifconfig_list_cloners(h, &cloners, &cloners_count) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
for (size_t i = 0; i < cloners_count; ++i) { | |||||
const char *name = cloners + i * IFNAMSIZ; | |||||
lua_pushstring(L, name); | |||||
lua_rawseti(L, -2, i); | |||||
} | |||||
free(cloners); | |||||
return (1); | |||||
} | |||||
/** Get SFP module information | |||||
* @param name The name of an interface | |||||
* @return Table describing available info about an SFP module if present, | |||||
* or nil if no module present or an error occurred | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_sfp_info(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_sfp_info sfp; | |||||
struct ifconfig_sfp_info_strings strings; | |||||
int index; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_sfp_get_sfp_info(h, name, &sfp) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
ifconfig_sfp_get_sfp_info_strings(&sfp, &strings); | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
#define $(enu) \ | |||||
if (sfp.sfp_##enu != 0) { \ | |||||
lua_newtable(L); \ | |||||
++index; \ | |||||
lua_pushstring(L, ifconfig_enum_sfp_##enu##_description()); \ | |||||
lua_setfield(L, index, "description"); \ | |||||
lua_pushstring(L, strings.sfp_##enu); \ | |||||
lua_setfield(L, index, "string"); \ | |||||
lua_pushinteger(L, sfp.sfp_##enu); \ | |||||
lua_setfield(L, index, "value"); \ | |||||
--index; \ | |||||
lua_setfield(L, index, "sfp_"#enu); \ | |||||
} | |||||
$(id) | |||||
$(conn) | |||||
$(eth_10g) | |||||
$(eth) | |||||
$(fc_len) | |||||
$(cab_tech) | |||||
$(fc_media) | |||||
$(eth_1040g) | |||||
$(eth_ext) | |||||
$(rev) | |||||
#undef $ | |||||
return (1); | |||||
} | |||||
/** Get SFP module vendor info | |||||
* @param name The name of an interface | |||||
* @return Table of vendor info or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_sfp_vendor_info(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_sfp_vendor_info vendor; | |||||
int index; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_sfp_get_sfp_vendor_info(h, name, &vendor) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
lua_pushstring(L, vendor.name); | |||||
lua_setfield(L, index, "name"); | |||||
lua_pushstring(L, vendor.pn); | |||||
lua_setfield(L, index, "part_number"); | |||||
lua_pushstring(L, vendor.sn); | |||||
lua_setfield(L, index, "serial_number"); | |||||
lua_pushstring(L, vendor.date); | |||||
lua_setfield(L, index, "date"); | |||||
return (1); | |||||
} | |||||
/** Get SFP module status | |||||
* @param name The name of an interface | |||||
* @return Table describing module status if present | |||||
* or nil if no module or an error occurs | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_sfp_status(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_sfp_status status; | |||||
size_t channels = 1; | |||||
int index; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_sfp_get_sfp_status(h, name, &status) != 0) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
lua_newtable(L); | |||||
index = lua_gettop(L); | |||||
if (-40.0 <= status.temp && status.temp <= 125.0) { | |||||
lua_pushnumber(L, status.temp); | |||||
lua_setfield(L, index, "temperature"); | |||||
} | |||||
lua_pushnumber(L, status.voltage); | |||||
lua_setfield(L, index, "voltage"); | |||||
if (status.bitrate != 0) { | |||||
lua_pushnumber(L, status.bitrate); | |||||
lua_setfield(L, index, "bitrate"); | |||||
channels = 4; | |||||
} | |||||
lua_newtable(L); | |||||
++index; | |||||
for (size_t chan = 0; chan < channels; ++chan) { | |||||
lua_newtable(L); | |||||
++index; | |||||
push_channel_power(L, status.channel[chan].rx); | |||||
lua_setfield(L, index, "rx_power"); | |||||
push_channel_bias(L, status.channel[chan].tx); | |||||
lua_setfield(L, index, "tx_bias"); | |||||
--index; | |||||
lua_rawseti(L, index, chan + 1); | |||||
} | |||||
--index; | |||||
lua_setfield(L, index, "channels"); | |||||
return (1); | |||||
} | |||||
/** Get SFP module I2C dump | |||||
* @param name The name of an interface | |||||
* @return A userdata blob with the dump or nil if error | |||||
*/ | |||||
static int | |||||
l_ifconfig_get_sfp_dump(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h; | |||||
const char *name; | |||||
struct ifconfig_sfp_dump *dump; | |||||
h = l_ifconfig_checkhandle(L, 1); | |||||
luaL_checkstring(L, 2); | |||||
dump = lua_newuserdata(L, sizeof(*dump)); | |||||
assert(dump != NULL); | |||||
luaL_getmetatable(L, SFP_DUMP_META); | |||||
lua_setmetatable(L, -2); | |||||
name = lua_tostring(L, 2); | |||||
if (ifconfig_sfp_get_sfp_dump(h, name, dump) != 0) | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
static const struct luaL_Reg l_ifconfig_handle[] = { | |||||
{"close", l_ifconfig_handle_close}, | |||||
{"error", l_ifconfig_handle_error}, | |||||
{"foreach_iface", l_ifconfig_foreach_iface}, | |||||
{"foreach_ifaddr", l_ifconfig_foreach_ifaddr}, | |||||
{"get_description", l_ifconfig_get_description}, | |||||
{"set_description", l_ifconfig_set_description}, | |||||
{"unset_description", l_ifconfig_unset_description}, | |||||
{"set_name", l_ifconfig_set_name}, | |||||
{"get_orig_name", l_ifconfig_get_orig_name}, | |||||
#if 0 /* setfib not implemented in libifconfig? */ | |||||
{"set_fib", l_ifconfig_set_fib}, | |||||
#endif | |||||
{"get_fib", l_ifconfig_get_fib}, | |||||
{"set_mtu", l_ifconfig_set_mtu}, | |||||
{"get_mtu", l_ifconfig_get_mtu}, | |||||
{"get_nd6", l_ifconfig_get_nd6}, | |||||
{"set_metric", l_ifconfig_set_metric}, | |||||
{"get_metric", l_ifconfig_get_metric}, | |||||
{"set_capabilities", l_ifconfig_set_capabilities}, | |||||
{"get_capabilities", l_ifconfig_get_capabilities}, | |||||
{"get_groups", l_ifconfig_get_groups}, | |||||
{"get_status", l_ifconfig_get_status}, | |||||
{"get_media", l_ifconfig_get_media}, | |||||
{"get_carp", l_ifconfig_get_carp}, | |||||
{"addr_info", l_ifconfig_addr_info}, | |||||
{"get_bridge_status", l_ifconfig_get_bridge_status}, | |||||
{"get_lagg_status", l_ifconfig_get_lagg_status}, | |||||
{"get_laggport_laggdev", l_ifconfig_get_laggport_laggdev}, | |||||
{"destroy", l_ifconfig_destroy}, | |||||
{"create", l_ifconfig_create}, | |||||
{"create_vlan", l_ifconfig_create_vlan}, | |||||
{"set_vlantag", l_ifconfig_set_vlantag}, | |||||
{"list_cloners", l_ifconfig_list_cloners}, | |||||
{"get_sfp_info", l_ifconfig_get_sfp_info}, | |||||
{"get_sfp_vendor_info", l_ifconfig_get_sfp_vendor_info}, | |||||
{"get_sfp_status", l_ifconfig_get_sfp_status}, | |||||
{"get_sfp_dump", l_ifconfig_get_sfp_dump}, | |||||
{NULL, NULL} | |||||
}; | |||||
static void | |||||
l_ifconfig_handle_meta(lua_State *L) | |||||
{ | |||||
luaL_newmetatable(L, IFCONFIG_HANDLE_META); | |||||
/* metatable.__index = metatable */ | |||||
lua_pushvalue(L, -1); | |||||
lua_setfield(L, -2, "__index"); | |||||
/* Automatically close handle when garbage collected. */ | |||||
lua_pushcfunction(L, l_ifconfig_handle_close); | |||||
lua_setfield(L, -2, "__gc"); | |||||
luaL_setfuncs(L, l_ifconfig_handle, 0); | |||||
lua_pop(L, 1); | |||||
} | |||||
/** Get interface name | |||||
* @return Name of the interface as a string | |||||
*/ | |||||
static int | |||||
l_struct_ifaddrs_name(lua_State *L) | |||||
{ | |||||
struct ifaddrs *ifa; | |||||
ifa = l_ifconfig_checkifaddrs(L, 1); | |||||
lua_pushstring(L, ifa->ifa_name); | |||||
return (1); | |||||
} | |||||
/** Get interface flags | |||||
* @return List of flags set on this interface | |||||
*/ | |||||
static int | |||||
l_struct_ifaddrs_flags(lua_State *L) | |||||
{ | |||||
struct ifaddrs *ifa; | |||||
ifa = l_ifconfig_checkifaddrs(L, 1); | |||||
push_flags_array(L, ifa->ifa_flags, IFFBITS); | |||||
return (1); | |||||
} | |||||
static const struct luaL_Reg l_struct_ifaddrs[] = { | |||||
{"name", l_struct_ifaddrs_name}, | |||||
{"flags", l_struct_ifaddrs_flags}, | |||||
{NULL, NULL} | |||||
}; | |||||
static void | |||||
l_struct_ifaddrs_meta(lua_State *L) | |||||
{ | |||||
luaL_newmetatable(L, STRUCT_IFADDRS_META); | |||||
/* metatable.__index = metatable */ | |||||
lua_pushvalue(L, -1); | |||||
lua_setfield(L, -2, "__index"); | |||||
luaL_setfuncs(L, l_struct_ifaddrs, 0); | |||||
lua_pop(L, 1); | |||||
} | |||||
static size_t | |||||
hex_dump(char *dst, const uint8_t *src, size_t len) | |||||
{ | |||||
const size_t LINE_BYTES = 16; | |||||
char *p = dst; | |||||
for (size_t i = 0; i < len; ++i) { | |||||
char sep; | |||||
if (i == (len - 1)) | |||||
sep = '\n'; | |||||
else if (((i + 1) % LINE_BYTES) != 0) | |||||
sep = ' '; | |||||
else | |||||
sep = '\n'; | |||||
p += sprintf(p, "%02X%c", src[i], sep); | |||||
} | |||||
return (p - dst); | |||||
} | |||||
static int | |||||
l_sfp_dump_tostring(lua_State *L) | |||||
{ | |||||
struct ifconfig_sfp_dump *dump; | |||||
char *p, *buf; | |||||
dump = luaL_checkudata(L, 1, SFP_DUMP_META); | |||||
assert(dump != NULL); | |||||
p = buf = malloc(SFF_DUMP_SIZE * 4); /* should be sufficiently large */ | |||||
assert(p != NULL); | |||||
memset(p, 0, sizeof(buf)); | |||||
switch (ifconfig_sfp_dump_region_count(dump)) { | |||||
case 2: | |||||
p += sprintf(p, "SFF8436 DUMP (0xA0 0..81 range):\n"); | |||||
p += hex_dump(p, dump->data + QSFP_DUMP0_START, | |||||
QSFP_DUMP0_SIZE); | |||||
p += sprintf(p, "SFF8436 DUMP (0xA0 128..255 range):\n"); | |||||
p += hex_dump(p, dump->data + QSFP_DUMP1_START, | |||||
QSFP_DUMP1_SIZE); | |||||
break; | |||||
case 1: | |||||
p += sprintf(p, "SFF8472 DUMP (0xA0 0..127 range):\n"); | |||||
p += hex_dump(p, dump->data + SFP_DUMP_START, SFP_DUMP_SIZE); | |||||
break; | |||||
default: | |||||
p += sprintf(p, "UNIDENTIFIED DUMP (0xA0 0..255 range):\n"); | |||||
p += hex_dump(p, dump->data, sizeof(dump->data)); | |||||
break; | |||||
} | |||||
lua_pushstring(L, buf); | |||||
free(buf); | |||||
return (1); | |||||
} | |||||
static const struct luaL_Reg l_sfp_dump[] = { | |||||
{"__tostring", l_sfp_dump_tostring}, | |||||
{NULL, NULL} | |||||
}; | |||||
static void | |||||
l_sfp_dump_meta(lua_State *L) | |||||
{ | |||||
luaL_newmetatable(L, SFP_DUMP_META); | |||||
/* metatable.__index = metatable */ | |||||
lua_pushvalue(L, -1); | |||||
lua_setfield(L, -2, "__index"); | |||||
luaL_setfuncs(L, l_sfp_dump, 0); | |||||
lua_pop(L, 1); | |||||
} | |||||
static int | |||||
l_ifconfig_open(lua_State *L) | |||||
{ | |||||
ifconfig_handle_t *h, **hp; | |||||
if ((h = ifconfig_open()) == NULL) { | |||||
lua_pushnil(L); | |||||
return (1); | |||||
} | |||||
hp = lua_newuserdata(L, sizeof h); | |||||
assert(hp != NULL); | |||||
*hp = h; | |||||
luaL_getmetatable(L, IFCONFIG_HANDLE_META); | |||||
lua_setmetatable(L, -2); | |||||
return (1); | |||||
} | |||||
static const struct luaL_Reg l_ifconfig[] = { | |||||
{"open", l_ifconfig_open}, | |||||
{NULL, NULL} | |||||
}; | |||||
int | |||||
luaopen_ifconfig(lua_State *L) | |||||
{ | |||||
l_ifconfig_handle_meta(L); | |||||
l_struct_ifaddrs_meta(L); | |||||
l_sfp_dump_meta(L); | |||||
lua_newtable(L); | |||||
luaL_setfuncs(L, l_ifconfig, 0); | |||||
return (1); | |||||
} |