diff --git a/Makefile.inc1 b/Makefile.inc1 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -2898,8 +2898,10 @@ lib/libelf lib/libexpat \ lib/libfigpar \ ${_lib_libgssapi} \ + lib/libifconfig \ lib/libjail \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \ + lib/libsysdecode \ lib/libzstd \ ${_lib_casper} \ lib/ncurses/ncurses \ @@ -2936,6 +2938,7 @@ lib/libgeom__L: lib/libexpat__L lib/libsbuf__L lib/libkvm__L: lib/libelf__L +lib/libifconfig__L: lib/msun__L .if ${MK_RADIUS_SUPPORT} != "no" _lib_libradius= lib/libradius diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -3067,11 +3067,6 @@ OLD_DIRS+=usr/lib/clang/7.0.1 # 20190227: rename seq.h to seqc.h OLD_FILES+=usr/include/sys/seq.h -# 20190222: libifconfig made INTERNALLIB -OLD_FILES+=usr/lib/libprivateifconfig.a -OLD_FILES+=usr/lib/libprivateifconfig_p.a -OLD_FILES+=usr/lib32/libprivateifconfig.a -OLD_FILES+=usr/lib32/libprivateifconfig_p.a # 20190131: pfil(9) changed OLD_FILES+=usr/share/man/man9/pfil_hook_get.9.gz OLD_FILES+=usr/share/man/man9/pfil_rlock.9.gz diff --git a/lib/Makefile b/lib/Makefile --- a/lib/Makefile +++ b/lib/Makefile @@ -145,7 +145,7 @@ .if !defined(COMPAT_32BIT) SUBDIR+= flua -SUBDIR_DEPEND_flua= libjail +SUBDIR_DEPEND_flua= libifconfig libjail libsysdecode .endif # NB: keep these sorted by MK_* knobs diff --git a/lib/flua/Makefile b/lib/flua/Makefile --- a/lib/flua/Makefile +++ b/lib/flua/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR= libjail +SUBDIR= libifconfig libjail .include diff --git a/lib/flua/libifconfig/Makefile b/lib/flua/libifconfig/Makefile new file mode 100644 --- /dev/null +++ b/lib/flua/libifconfig/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +SHLIB_NAME= ifconfig.so +SHLIBDIR= ${LIBDIR}/flua + +SRCS+= lua_ifconfig.c + +CFLAGS+= \ + -I${SRCTOP}/contrib/lua/src \ + -I${SRCTOP}/lib/liblua \ + -I${SRCTOP}/lib/libifconfig \ + -I${LIBIFCONFIGDIR} \ + +LIBADD+= ifconfig sysdecode + +MAN= ifconfig.3lua + +.include diff --git a/lib/flua/libifconfig/ifconfig.3lua b/lib/flua/libifconfig/ifconfig.3lua new file mode 100644 --- /dev/null +++ b/lib/flua/libifconfig/ifconfig.3lua @@ -0,0 +1,793 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2020, Ryan Moeller +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 11, 2020 +.Dt IFCONFIG 3lua +.Os +.Sh NAME +.Nm open , +.Nm close , +.Nm error , +.Nm foreach_iface , +.Nm foreach_ifaddr , +.Nm get_description , +.Nm set_description , +.Nm unset_description , +.Nm set_name , +.Nm get_orig_name , +.Nm get_fib , +.Nm set_mtu , +.Nm get_mtu , +.Nm get_nd6 , +.Nm set_metric , +.Nm get_metric , +.Nm set_capabilities , +.Nm get_capabilities , +.Nm get_groups , +.Nm get_status , +.Nm get_media , +.Nm get_carp , +.Nm addr_info , +.Nm get_bridge_status , +.Nm get_lagg_status , +.Nm get_laggport_laggdev , +.Nm destroy , +.Nm create , +.Nm create_vlan , +.Nm set_vlantag , +.Nm list_cloners , +.Nm get_sfp_info , +.Nm get_sfp_vendor_info , +.Nm get_sfp_status , +.Nm get_sfp_dump +.Nd Lua binding to +.Xr ifconfig 3 +.Sh SYNOPSIS +.Bd -literal +local ifconfig = require('ifconfig') +.Ed +.Pp +.Bl -tag -width XXXX -compact +.It Dv ifcfg = ifconfig.open() +.It Dv ifcfg:close() +.It Dv errtype, errno, ioctlname = ifcfg:error() +.It Dv udata = ifcfg:foreach_iface(cb, udata ) +.It Dv udata = ifcfg:foreach_ifaddr(iface, cb, udata ) +.It Dv desc = ifcfg:get_description(name) +.It Dv res = ifcfg:set_description(name, desc ) +.It Dv res = ifcfg:unset_description(name) +.It Dv res = ifcfg:set_name(name, newname ) +.It Dv name = ifcfg:get_orig_name(name) +.It Dv fib = ifcfg:get_fib(name) +.It Dv res = ifcfg:set_mtu(name, mtu ) +.It Dv mtu = ifcfg:get_mtu(name) +.It Dv nd6 = ifcfg:get_nd6(name) +.It Dv res = ifcfg:set_metric(name, metric ) +.It Dv metric = ifcfg:get_metric(name) +.It Dv res = ifcfg:set_capabilities(name, caps ) +.It Dv caps = ifcfg:get_capabilities(name) +.It Dv groups = ifcfg:get_groups(name) +.It Dv status = ifcfg:get_status(name) +.It Dv media = ifcfg:get_media(name) +.It Dv carp = ifcfg:get_carp(name) +.It Dv ai = ifcfg:addr_info(addr) +.It Dv status = ifcfg:get_bridge_status(name) +.It Dv status = ifcfg:get_lagg_status(name) +.It Dv lagg = ifcfg:get_laggport_laggdev(name) +.It Dv res = ifcfg:destroy(name) +.It Dv name = ifcfg:create(name) +.It Dv name = ifcfg:create_vlan(name, vlandev, vlantag ) +.It Dv res = ifcfg:set_vlantag(name, vlandev, vlantag ) +.It Dv cloners = ifcfg:list_cloners() +.It Dv info = ifcfg:get_sfp_info(name) +.It Dv vendor_info = ifcfg:get_sfp_vendor_info(name) +.It Dv status = ifcfg:get_sfp_status(name) +.It Dv dump = ifcfg:get_sfp_dump(name) +.It Vt struct ifaddrs * +.El +.Sh DESCRIPTION +The +.Nm ifconfig +module is a binding to the +.Xr ifconfig 3 +library. +It provides a high-level interface for many socket ioctls that query and +control network interface configuration and status information. +.Bl -tag -width XXXX +.It Dv ifcfg = ifconfig.open() +Open a libifconfig handle. +The handle is a userdata object used to perform the various socket operations +underlying most libifconfig functionality. +An open handle will be closed when garbage collected, or it can be explicitly +closed with +.Fn close . +.It Dv ifcfg:close() +Explicitly close a handle opened by +.Fn open . +.It Dv errtype, errno, ioctlname = ifcfg:error() +Get information about the last error. +If the last operation was succcessful, +.Dv nil +is returned. +Otherwise, returns the error type, errno, and if applicable the name of the +ioctl that failed. +The error type is one of "OTHER", "IOCTL", or "SOCKET". +.It Dv udata = ifcfg:foreach_iface(cb, udata ) +Iterate over interfaces. +This is a higher-order fold operation, where +.Fa cb +is a callback function of the form +.Dl (handle, iface, udata) -> udata +which is called for each interface in succession. +.Pp +The +.Fa handle +passed to the callback is the handle that was used to invoke +.Fn foreach_iface . +.Pp +The +.Fa iface +passed to the callback is a +.Vt struct ifaddrs * +userdata object that is only valid until the callback function returns. +It must not be retained outside the scope of the callback. +.Pp +The +.Fa udata +parameter is an accumulator. +The initial value passed to +.Fn foreach_iface +is forwarded to the callback function on the first iteration. +The return value of the callback function is passed to the callback as +.Fa udata +on the next iteration. +The value returned from the callback function on the final iteration is +returned from +.Fn foreach_iface +on success. +.Pp +An error may be raised if iteration fails due to an internal error. +.It Dv udata = ifcfg:foreach_ifaddr(iface, cb, udata ) +Iterate over interface addresses. +This is a higher-order fold operation, where +.Fa cb +is a callback function of the form +.Dl (handle, ifaddr, udata) -> udata +which is called for each interface address in succession. +.Pp +The interface +.Fa iface +is the +.Vt struct ifaddrs * +userdata object passed to the callback function for +.Fn foreach_iface . +.Pp +The +.Fa handle +passed to the callback is the handle that was used to invoke +.Fn foreach_ifaddr . +.Pp +The +.Fa udata +parameter is an accumulator. +The initial value passed to +.Fn foreach_ifaddr +is forwarded to the callback function on the first iteration. +The return value of the callback function is passed to the callback as +.Fa udata +on the next iteration. +The value returned from the callback function on the final iteration is +returned from +.Fn foreach_ifaddr . +.It Dv desc = ifcfg:get_description(name) +Given the +.Fa name +of an interface, returns the description string if set, otherwise +.Dv nil . +.It Dv res = ifcfg:set_description(name, desc ) +Given the +.Fa name +of an interface, sets the description string to +.Fa desc . +.It Dv res = ifcfg:unset_description(name) +Given the +.Fa name +of an interface, unsets the description string if set. +.It Dv res = ifcfg:set_name(name, newname ) +Given the +.Fa name +of an interface, changes the interface name to +.Fa newname . +.It Dv name = ifcfg:get_orig_name(name) +Given the +.Fa name +of an interface, returns the original name string of the interface. +.It Dv fib = ifcfg:get_fib(name) +Given the +.Fa name +of an interface, returns the interface FIB number. +.It Dv res = ifcfg:set_mtu(name, mtu ) +Given the +.Fa name +of an interface, sets the interface MTU to +.Fa mtu . +.It Dv mtu = ifcfg:get_mtu(name) +Given the +.Fa name +of an interface, returns the interface MTU. +.It Dv nd6 = ifcfg:get_nd6(name) +Given the +.Fa name +of an interface, returns a list of the enabled ICMPv6 Neighbor Discovery +Protocol options. +.It Dv res = ifcfg:set_metric(name, metric ) +Given the +.Fa name +of an interface, sets the interface metric to +.Fa metric . +.It Dv metric = ifcfg:get_metric(name) +Given the +.Fa name +of an interface, returns the interface metric. +.It Dv res = ifcfg:set_capabilities(name, caps ) +Given the +.Fa name +of an interface, enables the list of capabilities given as strings in the list +.Fa caps +and disables the capabilities not in the list, returning true if successful or +false if an error occurs. +.It Dv caps = ifcfg:get_capabilities(name) +Given the +.Fa name +of an interface, returns a table of the form +.Bd -literal -compact +{ + enabled = { "", ... }, + supported = { "", ... } +} +.Ed +where +.Nm enabled +and +.Nm supported +are the lists of enabled and supported capabilities with each capability +represented by a string. +.It Dv groups = ifcfg:get_groups(name) +Given the +.Fa name +of an interface, returns a list of the names of the groups containing the +interface. +.It Dv status = ifcfg:get_status(name) +Given the +.Fa name +of an interface, returns the interface status as a string. +.It Dv media = ifcfg:get_media(name) +Given the +.Fa name +of an interface, returns a table of the form +.Bd -literal -compact +{ + current = {}, + active = {}, + supported = { {}, ... }, + status = "", + [ down_reason = ""|# ] +} +.Ed +where +.Nm current +and +.Nm active +are tables of the form +.Bd -literal -compact +{ + [ type = "",] + [ subtype = "",] + [ mode = "",] + options = { "", ... } +} +.Ed +and +.Nm supported +is a list of the same form of tables. +.It Dv carp = ifcfg:get_carp(name) +Given the +.Fa name +of an interface, returns a list of tables of the form +.Bd -literal -compact +{ + state = "", + vhid = #, + advbase = #, + advskew = # +} +.Ed +describing the interface +.Xr carp 4 +VHIDs. +.It Dv ai = ifcfg:addr_info(addr) +Given a +.Vt struct ifaddrs * +userdata object as +.Fa addr , +returns a table describing the address. +The format of the table depends on the type of address. +.Bl -inset +.It Nm AF_INET +addresses are described by a table of the form +.Bd -literal -compact +{ + inet = "", + netmask = "", + [ destination = "",] + [ broadcast = "",] + [ vhid = # ] +} +.Ed +.It Nm AF_INET6 +addresses are described by a table of the form +.Bd -literal -compact +{ + inet6 = "", + prefixlen = #, + flags = { "", ... }, + [ destination = "",] + [ scopeid = #,] + [ preferred_lifetime = #, valid_lifetime = #,] + [ vhid = # ] +} +.Ed +.It Nm AF_LINK +addresses are described by a table of the form +.Bd -literal -compact +{ + ether = "" +} +.Ed +for link types +.Nm IFT_ETHER , +.Nm IFT_L2VLAN , +and +.Nm IFT_BRIDGE +or +.Bd -literal -compact +{ + lladdr = "" +} +.Ed +for other link types. +.It Nm AF_LOCAL +addresses are described by a table of the form +.Bd -literal -compact +{ + path = "" +} +.Ed +.El +.It Dv status = ifcfg:get_bridge_status(name) +Given the +.Fa name +of a +.Xr bridge 4 +interface, returns a table of the form +.Bd -literal -compact +{ + address_cache_size = #, + address_cache_lifetime = #, + priority = #, + protocol = ""|#, + hello_time = #, + forward_delay = #, + hold_count = #, + max_age = #, + id = "", + root_id = "", + root_priority = #, + root_path_cost = #, + root_port = #, + members = { {}, ... } +} +.Ed +where +.Nm members +is a list of tables of the form +.Bd -literal -compact +{ + flags = { "", ... }, + ifmaxaddr = #, + port = #, + priority = #, + [ protocol = ""|#, role = ""|#, state = ""|#] +} +.Ed +.It Dv status = ifcfg:get_lagg_status(name) +Given the +.Fa name +of a +.Xr lagg 4 +interface, returns a table of the form +.Bd -literal -compact +{ + options = {}, + stats = {}, + ports = {} +} +.Ed +where +.Nm options +is a table of the form +.Bd -literal -compact +{ + laggproto = "", + lagghash = { "", ... }, + flags = { "", ... }, + flowid_shift = #, + [ rr_limit = # ] +} +.Ed +.Nm stats +is a table of the form +.Bd -literal -compact +{ + active = #, + flapping = # +} +.Ed +and +.Nm ports +is a table where the keys are the names of the laggports and the values are +tables of the form +.Bd -literal -compact +{ + flags = { "", ... }, + [ lacp_state = { "", ... } ] +} +.Ed +.It Dv lagg = ifcfg:get_laggport_laggdev(name) +Given the +.Fa name +of an interface, returns the parent lagg device name. +error occurs. +.It Dv res = ifcfg:destroy(name) +Given the +.Fa name +of an interface, destroys the interface. +.It Dv name = ifcfg:create(name) +Given the +.Fa name +of in interface, creates the interface and returns the name of the created +interface. +.It Dv name = ifcfg:create_vlan(name, vlandev, vlantag ) +Creates a +.Xr vlan 4 interface with the given +.Fa name +attached to +.Fa vlandev +and using the vlan number specified by +.Fa vlantag , +returning the name of the create interface. +occurs. +.It Dv res = ifcfg:set_vlantag(name, vlandev, vlantag ) +Given the +.Fa name +of an existing +.Xr vlan 4 +interface, sets the specified +.Fa vlandev +and +.Fa vlantag . +.It Dv cloners = ifcfg:list_cloners() +Returns a list of the names of all the interface cloners available on the +system. +.It Dv info = ifcfg:get_sfp_info(name) +Given the +.Fa name +of an interface, returns SFP module information as a table of the form +.Bd -literal -compact +{ + [ sfp_id = {},] + [ sfp_conn = {},] + [ sfp_eth_10g = {},] + [ sfp_eth = {},] + [ sfp_fc_len = {},] + [ sfp_cab_tech = {},] + [ sfp_fc_media = {},] + [ sfp_eth_1040g = {},] + [ sfp_eth_ext = {},] + [ sfp_rev = {}] +} +.Ed +where each field, if present, is a table of the form +.Bd -literal -compact +{ + description = "", + string = "", + value = # +} +.Ed +or +.Dv nil +if no SFP module is present. +.It Dv vendor_info = ifcfg:get_sfp_vendor_info(name) +Given the +.Fa name +of an interface, returns SFP module vendor information as a table of the form +.Bd -literal -compact +{ + name = "", + part_number = "", + serial_number = "", + date = "" +} +.Ed +or +.Dv nil +if no SFP module is present. +.It Dv status = ifcfg:get_sfp_status(name) +Given the +.Fa name +of an interface, returns SFP module status as a table of the form +.Bd -literal -compact +{ + voltage = #, + channels = { {}, ... }, + [ temperature = #,] + [ bitrate = #] +} +.Ed +where +.Nm channels +is a list of tables of the form +.Bd -literal -compact +{ + rx_power = {}, + tx_bias = {} +} +.Ed +.Nm rx_power +is a table of the form +.Bd -literal -compact +{ + value = #, + mW = #, + dBm = # +} +.Ed +.Nm tx_bias +is a table of the form +.Bd -literal -compact +{ + raw = #, + mA = # +} +.Ed +or +.Dv nil +if no SFP module is present. +.It Dv dump = ifcfg:get_sfp_dump(name) +Given the +.Fa name +of an interface, returns a userdata blob containing a dump of the SFP module's +I2C memory or +.Dv nil +if no SFP module is present. +The userdata blob coerces to a string formatted as a hex dump similar to +.Xr ifconfig 8 +with +.Fl vv . +.It Vt struct ifaddrs * +The +.Vt struct ifaddrs * +userdata type describes an interface and various address types associated with +it. +An object of this type provides two address-agnostic methods: +.Bl -tag -width XXXX +.It Dv name = ifaddr:name() +Get the name of the interface to which this address is assigned. +.It Dv flags = ifaddr:flags() +Get the flags set on the interface to which this address is assigned. +.El +Address-specific information is obtained by passing a +.Vt struct ifaddrs * +userdata object to the +.Fn addr_info +function. +.El +.Sh RETURN VALUES +The +.Fn open +function returns an open +.Vt ifconfig_handle_t +userdata object on success, or +.Dv nil +on error. +.Pp +The +.Fn error +function returns multiple values on success, or +.Dv nil +on error. +The values returned are the error type, errno, and the ioctl name if the error +type is "IOCTL". +The +.Fn foreach_iface +and +.Fn foreach_ifaddr +functions return the value returned by the +.Fa cb +function on the final iteration, or the initial +.Fa udata +argument if +.Fa cb +was not invoked. +.Pp +The functions +.Fn get_description , +.Fn get_orig_name , +.Fn get_status , +.Fn get_laggport_laggdev , +.Fn create , +and +.Fn create_vlan +return a string on success, or +.Dv nil +on error. +The string returned by +.Fn create +and +.Fn create_vlan +is the name of the created interface. +.Pp +The functions +.Fn get_fib , +.Fn get_mtu , +and +.Fn get_metric +return an integer on success, or +.Dv nil +on error. +.Pp +The functions +.Fn get_nd6 , +.Fn get_groups , +and +.Fn list_cloners +return a list of strings on success, or +.Dv nil +on error. +.Pp +The functions +.Fn get_capabilities , +.Fn get_media , +.Fn get_carp , +.Fn addr_info , +.Fn get_bridge_status , +.Fn get_lagg_status , +.Fn get_sfp_info , +.Fn get_sfp_vendor_info , +.Fn get_sfp_status , +and +.Fn get_sfp_dump +return a table described for each function in the +.Sx DESCRIPTION +section, or +.Dv nil +on error. +.Pp +The functions +.Fn set_description , +.Fn unset_description , +.Fn set_name , +.Fn set_mtu , +.Fn set_metric , +.Fn set_capabilities , +.Fn set_vlantag , +and +.Fn destroy +return +.Dv true +on success, or +.Dv false +on error. +.Pp +The +.Fn ifaddr:name +method returns a string and the +.Fn ifaddr:flags +method returns a list of strings. +.Sh EXAMPLES +Get a list of the names of all interfaces: +.Bd -literal -offset indent +local ifcfg = require('ifconfig').open() +local ucl = require('ucl') + +local ifnames = ifcfg:foreach_iface(function(ifcfg, iface, ifnames) + table.insert(ifnames, iface:name()) + return ifnames +end, {}) + +print(ucl.to_json(ifnames)) +.Ed +.Pp +Retrieve information about all SFP modules: +.Bd -literal -offset indent +local ifcfg = require('ifconfig').open() +local ucl = require('ucl') + +local modules = ifcfg:foreach_iface(function(ifcfg, iface, modules) + local name = iface:name() + local sfp = ifcfg:get_sfp_info(name) + if sfp then + modules[name] = { + info = sfp, + vendor_info = ifcfg:get_sfp_vendor_info(name), + status = ifcfg:get_sfp_status(name), + -- Not generally interesting, + -- but for completeness: + -- dump = tostring(ifcfg:get_sfp_dump(name)), + } + end + return modules +end, {}) + +print(ucl.to_json(modules)) +.Ed +.Pp +Get error information: +.Bd -literal -offset indent +local ifcfg = require('ifconfig').open() + +local status = ifcfg:get_status('usb') +if not status then + print(ifcfg:error()) + -- Prints: + -- IOCTL 6 SIOCGIFSTATUS +end + +local dump = ifcfg:get_sfp_dump('usb') +if not dump then + local errtype, errno, ioctlname = ifcfg:error() + assert(errno == 6) -- ENXIO + print(errtype .. ':' .. ioctlname .. ' failed') + -- Prints: + -- IOCTL:SIOCGI2C failed +end +.Ed +.Sh SEE ALSO +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm ifconfig +Lua module for flua first appeared in +.Fx 13.0 . +.Sh AUTHORS +.An Ryan Moeller , +with inspiration from +.Nx +gpio(3lua), by +.An Mark Balmer . diff --git a/lib/flua/libifconfig/lua_ifconfig.c b/lib/flua/libifconfig/lua_ifconfig.c new file mode 100644 --- /dev/null +++ b/lib/flua/libifconfig/lua_ifconfig.c @@ -0,0 +1,1956 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020, Ryan Moeller + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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, ifmedia_t media) +{ + const char *name, **opts; + + lua_newtable(L); + + name = ifconfig_media_get_type(media); + if (name != NULL) { + lua_pushstring(L, name); + lua_setfield(L, -2, "type"); + } + + name = ifconfig_media_get_subtype(media); + if (name != NULL) { + lua_pushstring(L, name); + lua_setfield(L, -2, "subtype"); + } + + name = ifconfig_media_get_mode(media); + if (name != NULL) { + lua_pushstring(L, name); + lua_setfield(L, -2, "mode"); + } + + lua_newtable(L); + opts = ifconfig_media_get_options(media); + if (opts != NULL) { + for (size_t i = 0; opts[i] != NULL; ++i) { + lua_pushstring(L, opts[i]); + lua_rawseti(L, -2, i); + } + free(opts); + } + lua_setfield(L, -2, "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; + + if (ifconfig_inet_get_addrinfo(h, ifa->ifa_name, ifa, &addr) != 0) { + lua_pushnil(L); + return; + } + + lua_newtable(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, -2, "inet"); + + if (addr.dst != NULL) { + lua_pushstring(L, inet_ntoa_r(addr.dst->sin_addr, addr_buf, + sizeof addr_buf)); + lua_setfield(L, -2, "destination"); + } + + lua_pushstring(L, inet_ntoa_r(addr.netmask->sin_addr, addr_buf, + sizeof addr_buf)); + lua_setfield(L, -2, "netmask"); + + if (addr.broadcast != NULL) { + lua_pushstring(L, inet_ntoa_r(addr.broadcast->sin_addr, + addr_buf, sizeof addr_buf)); + lua_setfield(L, -2, "broadcast"); + } + + if (addr.vhid != 0) { + lua_pushinteger(L, addr.vhid); + lua_setfield(L, -2, "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; + + if (ifconfig_inet6_get_addrinfo(h, ifa->ifa_name, ifa, &addr) != 0) { + lua_pushnil(L); + return; + } + + lua_newtable(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, -2, "inet6"); + + if (addr.dstin6 != NULL) { + inet_ntop(AF_INET6, addr.dstin6, addr_buf, sizeof addr_buf); + lua_pushstring(L, addr_buf); + lua_setfield(L, -2, "destination"); + } + + lua_pushinteger(L, addr.prefixlen); + lua_setfield(L, -2, "prefixlen"); + + if (addr.sin6->sin6_scope_id != 0) { + lua_pushinteger(L, addr.sin6->sin6_scope_id); + lua_setfield(L, -2, "scopeid"); + } + + push_flags_array(L, addr.flags, IN6BITS); + lua_setfield(L, -2, "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, -2, "preferred_lifetime"); + + lua_pushinteger(L, MAX(0l, + addr.lifetime.ia6t_expire - now.tv_sec)); + lua_setfield(L, -2, "valid_lifetime"); + } + + if (addr.vhid != 0) { + lua_pushinteger(L, addr.vhid); + lua_setfield(L, -2, "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 n; + + s.a = ifa->ifa_addr; + if (s.dl == NULL || s.dl->sdl_alen <= 0) { + lua_pushnil(L); + return; + } + + lua_newtable(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, -2, "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, -2, "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; + + lua_newtable(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, -2, "path"); +} + +/* + * Push a table describing SFP channel power. + */ +static void +push_channel_power(lua_State *L, uint16_t power) +{ + + lua_newtable(L); + + lua_pushinteger(L, power); + lua_setfield(L, -2, "value"); + + lua_pushnumber(L, power_mW(power)); + lua_setfield(L, -2, "mW"); + + lua_pushnumber(L, power_dBm(power)); + lua_setfield(L, -2, "dBm"); +} + +/* + * Push a table describing SFP channel bias current. + */ +static void +push_channel_bias(lua_State *L, uint16_t bias) +{ + + lua_newtable(L); + + lua_pushinteger(L, bias); + lua_setfield(L, -2, "raw"); + + lua_pushnumber(L, bias_mA(bias)); + lua_setfield(L, -2, "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, ""); + 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, *status; + struct ifmediareq *ifmr; + struct ifdownreason ifdr; + + 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"); + + status = ifconfig_media_get_status(ifmr); + lua_pushstring(L, status); + lua_setfield(L, 3, "status"); + + if (strcmp(status, "no carrier") == 0 && + ifconfig_media_get_downreason(h, name, &ifdr) == 0) { + switch (ifdr.ifdr_reason) { + case IFDR_REASON_MSG: + lua_pushstring(L, ifdr.ifdr_msg); + lua_setfield(L, 3, "down_reason"); + break; + case IFDR_REASON_VENDOR: + lua_pushinteger(L, ifdr.ifdr_vendor); + lua_setfield(L, 3, "down_reason"); + break; + default: + break; + } + } + + 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]; + 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); + + lua_pushinteger(L, bridge->cache_size); + lua_setfield(L, -2, "address_cache_size"); + + lua_pushinteger(L, bridge->cache_lifetime); + lua_setfield(L, -2, "address_cache_lifetime"); + + params = bridge->params; + + lua_pushinteger(L, params->ifbop_priority); + lua_setfield(L, -2, "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, -2, "protocol"); + + lua_pushinteger(L, params->ifbop_hellotime); + lua_setfield(L, -2, "hello_time"); + + lua_pushinteger(L, params->ifbop_fwddelay); + lua_setfield(L, -2, "forward_delay"); + + lua_pushinteger(L, params->ifbop_holdcount); + lua_setfield(L, -2, "hold_count"); + + lua_pushinteger(L, params->ifbop_maxage); + lua_setfield(L, -2, "max_age"); + + PV2ID(params->ifbop_bridgeid, bprio, lladdr); + lua_pushstring(L, ether_ntoa_r((struct ether_addr *)lladdr, addr_buf)); + lua_setfield(L, -2, "id"); + + PV2ID(params->ifbop_designated_root, bprio, lladdr); + lua_pushstring(L, ether_ntoa_r((struct ether_addr *)lladdr, addr_buf)); + lua_setfield(L, -2, "root_id"); + + lua_pushinteger(L, bprio); + lua_setfield(L, -2, "root_priority"); + + lua_pushinteger(L, params->ifbop_root_path_cost); + lua_setfield(L, -2, "root_path_cost"); + + lua_pushinteger(L, params->ifbop_root_port & 0xfff); + lua_setfield(L, -2, "root_port"); + + lua_newtable(L); + + for (size_t i = 0; i < bridge->members_count; ++i) { + member = bridge->members + i; + + lua_newtable(L); + + push_flags_array(L, member->ifbr_ifsflags, IFBIFBITS); + lua_setfield(L, -2, "flags"); + + lua_pushinteger(L, member->ifbr_addrmax); + lua_setfield(L, -2, "ifmaxaddr"); + + lua_pushinteger(L, member->ifbr_portno); + lua_setfield(L, -2, "port"); + + lua_pushinteger(L, member->ifbr_priority); + lua_setfield(L, -2, "priority"); + + lua_pushinteger(L, member->ifbr_path_cost); + lua_setfield(L, -2, "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, -2, "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, -2, "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, -2, "state"); + } + + lua_setfield(L, -2, member->ifbr_ifsname); + } + + lua_setfield(L, -2, "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 = ""; + + 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); + + 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, -2, "laggproto"); + + push_flags_array(L, lagg_status->rf->rf_flags & LAGG_F_HASHMASK, + LAGGHASHBITS); + lua_setfield(L, -2, "lagghash"); + + lua_newtable(L); + + push_flags_array(L, lagg_status->ro->ro_opts, LAGG_OPT_BITS); + lua_setfield(L, -2, "flags"); + + lua_pushinteger(L, lagg_status->ro->ro_flowid_shift); + lua_setfield(L, -2, "flowid_shift"); + + if (lagg_status->ra->ra_proto == LAGG_PROTO_ROUNDROBIN) { + lua_pushinteger(L, lagg_status->ro->ro_bkt); + lua_setfield(L, -2, "rr_limit"); + } + + lua_setfield(L, -2, "options"); + + lua_newtable(L); + + lua_pushinteger(L, lagg_status->ro->ro_active); + lua_setfield(L, -2, "active"); + + lua_pushinteger(L, lagg_status->ro->ro_flapping); + lua_setfield(L, -2, "flapping"); + + lua_setfield(L, -2, "stats"); + + lua_newtable(L); + + 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); + + push_flags_array(L, lagg_port->rp_flags, LAGG_PORT_BITS); + lua_setfield(L, -2, "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, -2, "lacp_state"); + } + + lua_setfield(L, -2, lagg_port->rp_portname); + } + + lua_setfield(L, -2, "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; + + 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); + +#define $(enu) \ + if (sfp.sfp_##enu != 0) { \ + lua_newtable(L); \ + lua_pushstring(L, ifconfig_enum_sfp_##enu##_description()); \ + lua_setfield(L, -2, "description"); \ + lua_pushstring(L, strings.sfp_##enu); \ + lua_setfield(L, -2, "string"); \ + lua_pushinteger(L, sfp.sfp_##enu); \ + lua_setfield(L, -2, "value"); \ + lua_setfield(L, -2, "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; + + 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); + + lua_pushstring(L, vendor.name); + lua_setfield(L, -2, "name"); + + lua_pushstring(L, vendor.pn); + lua_setfield(L, -2, "part_number"); + + lua_pushstring(L, vendor.sn); + lua_setfield(L, -2, "serial_number"); + + lua_pushstring(L, vendor.date); + lua_setfield(L, -2, "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; + + 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); + + if (-40.0 <= status.temp && status.temp <= 125.0) { + lua_pushnumber(L, status.temp); + lua_setfield(L, -2, "temperature"); + } + + lua_pushnumber(L, status.voltage); + lua_setfield(L, -2, "voltage"); + + if (status.bitrate != 0) { + lua_pushnumber(L, status.bitrate); + lua_setfield(L, -2, "bitrate"); + + channels = 4; + } + + lua_newtable(L); + + for (size_t chan = 0; chan < channels; ++chan) { + lua_newtable(L); + + push_channel_power(L, status.channel[chan].rx); + lua_setfield(L, -2, "rx_power"); + + push_channel_bias(L, status.channel[chan].tx); + lua_setfield(L, -2, "tx_bias"); + + lua_rawseti(L, -2, chan + 1); + } + + lua_setfield(L, -2, "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); +} diff --git a/lib/libifconfig/Makefile b/lib/libifconfig/Makefile --- a/lib/libifconfig/Makefile +++ b/lib/libifconfig/Makefile @@ -2,7 +2,7 @@ PACKAGE= lib${LIB} LIB= ifconfig -INTERNALLIB= true +PRIVATELIB= true LIBADD= m @@ -36,10 +36,10 @@ CLEANFILES+= ${GEN} -# If libifconfig become public uncomment those two lines -#INCSDIR= ${INCLUDEDIR} -#INCS= libifconfig.h libifconfig_sfp.h libifconfig_sfp_tables.h +INCSDIR= ${INCLUDEDIR} +INCS= libifconfig.h libifconfig_sfp.h libifconfig_sfp_tables.h +# Uncomment this line if libifconfig becomes public. #MAN= libifconfig.3 CFLAGS+= -I${.CURDIR} -I${.OBJDIR} diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -223,7 +223,7 @@ CRUNCH_ALIAS_chown= chgrp ################################################################## -CRUNCH_LIBS+= ${OBJTOP}/lib/libifconfig/libifconfig.a +CRUNCH_LIBS+= ${LDADD_ifconfig} CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS+=-I${OBJTOP}/lib/libifconfig CRUNCH_LIBS+= -lm diff --git a/share/examples/Makefile b/share/examples/Makefile --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -100,7 +100,7 @@ find_interface.c SE_DIRS+= flua -SE_FLUA= libjail.lua +SE_FLUA= libjail.lua libifconfig.lua SE_DIRS+= indent SE_INDENT= indent.pro diff --git a/share/examples/flua/libifconfig.lua b/share/examples/flua/libifconfig.lua new file mode 100755 --- /dev/null +++ b/share/examples/flua/libifconfig.lua @@ -0,0 +1,69 @@ +#!/usr/libexec/flua +--[[ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020, Ryan Moeller + * + * 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. + * + * $FreeBSD$ + */ +]]-- +-- ex: et sw=4: + +local ifcfg = require('ifconfig').open() + +local ifaces = ifcfg:foreach_iface(function(_, iface, ifs) + local name = iface:name() + ifs[name] = { + flags = iface:flags(), + media = ifcfg:get_media(name), + status = ifcfg:get_status(name), + capabilities = ifcfg:get_capabilities(name), + groups = ifcfg:get_groups(name), + metric = ifcfg:get_metric(name), + mtu = ifcfg:get_mtu(name), + nd6 = ifcfg:get_nd6(name), + bridge_status = ifcfg:get_bridge_status(name), + lagg_status = ifcfg:get_lagg_status(name), + laggdev = ifcfg:get_laggport_laggdev(name), + addresses = ifcfg:foreach_ifaddr(iface, function(_, addr, addrs) + table.insert(addrs, ifcfg:addr_info(addr)) + return addrs + end, {}), + sfp = (function() + local info = ifcfg:get_sfp_info(name) + return info and { + info = info, + vendor_info = ifcfg:get_sfp_vendor_info(name), + status = ifcfg:get_sfp_status(name), + -- Not generally interesting, + -- but for completeness: + -- dump = tostring(ifcfg:get_sfp_dump(name)), + } + end)(), + } + return ifs +end, {}) + +print(ucl.to_json(ifaces)) diff --git a/share/man/man3lua/intro.3lua b/share/man/man3lua/intro.3lua --- a/share/man/man3lua/intro.3lua +++ b/share/man/man3lua/intro.3lua @@ -51,11 +51,15 @@ .Fx are: .Bl -tag -width jail +.It Xr ifconfig 3lua +Wrapper for +.Xr ifconfig 3 . .It Xr jail 3lua Wrapper for .Xr jail 3 . .El .Sh SEE ALSO +.Xr ifconfig 3lua .Xr jail 3lua .Sh AUTHORS .An Ryan Moeller , diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -25,6 +25,7 @@ gtest_main \ heimipcc \ heimipcs \ + ifconfig \ ldns \ sqlite3 \ ssh \ @@ -39,7 +40,6 @@ cron \ elftc \ fifolog \ - ifconfig \ ipf \ kyua_cli \ kyua_drivers \ @@ -540,9 +540,6 @@ LIBSLDIR= ${_LIB_OBJTOP}/kerberos5/lib/libsl LIBSL?= ${LIBSLDIR}/libsl${PIE_SUFFIX}.a -LIBIFCONFIGDIR= ${_LIB_OBJTOP}/lib/libifconfig -LIBIFCONFIG?= ${LIBIFCONFIGDIR}/libifconfig${PIE_SUFFIX}.a - LIBIPFDIR= ${_LIB_OBJTOP}/sbin/ipf/libipf LIBIPF?= ${LIBIPFDIR}/libipf${PIE_SUFFIX}.a