summaryrefslogtreecommitdiffhomepage
path: root/libs/luci-lib-luaneightbl/src/neightbl.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/luci-lib-luaneightbl/src/neightbl.c')
-rw-r--r--libs/luci-lib-luaneightbl/src/neightbl.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/libs/luci-lib-luaneightbl/src/neightbl.c b/libs/luci-lib-luaneightbl/src/neightbl.c
new file mode 100644
index 0000000000..987ec5e32e
--- /dev/null
+++ b/libs/luci-lib-luaneightbl/src/neightbl.c
@@ -0,0 +1,122 @@
+/*
+License:
+Copyright 2013 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <linux/rtnetlink.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+
+char *ether_ntoa_l (const struct ether_addr *addr, char *buf)
+{
+ sprintf (buf, "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr->ether_addr_octet[0], addr->ether_addr_octet[1],
+ addr->ether_addr_octet[2], addr->ether_addr_octet[3],
+ addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
+ return buf;
+}
+
+static int neightbl_get(lua_State *L) {
+ int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ struct sockaddr_nl kernel = {AF_NETLINK, 0, 0, 0};
+ if (connect(sock, (struct sockaddr*)&kernel, sizeof(kernel)))
+ goto error;
+
+ const char *ifname = luaL_checkstring(L, 1);
+ int ifindex = if_nametoindex(ifname);
+ if (ifindex <= 0)
+ goto error;
+
+ struct {
+ struct nlmsghdr hdr;
+ struct ndmsg ndm;
+ } req = {
+ {sizeof(req), RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP, 1, 0},
+ {AF_INET6, 0, 0, ifindex, 0, 0, 0},
+ };
+
+ if (send(sock, &req, sizeof(req), 0) != sizeof(req))
+ goto error;
+
+ lua_newtable(L);
+
+ char buf[8192];
+ struct nlmsghdr *nh = (struct nlmsghdr*)buf;
+ do {
+ ssize_t len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ lua_pop(L, 1);
+ goto error;
+ }
+
+
+ for (;NLMSG_OK(nh, (size_t)len) && nh->nlmsg_type == RTM_NEWNEIGH;
+ nh = NLMSG_NEXT(nh, len)) {
+ struct ndmsg *ndm = NLMSG_DATA(nh);
+ if (NLMSG_PAYLOAD(nh, 0) < sizeof(*ndm) || ndm->ndm_ifindex != ifindex)
+ continue;
+
+ ssize_t alen = NLMSG_PAYLOAD(nh, sizeof(*ndm));
+ char buf[INET6_ADDRSTRLEN] = {0}, *mac = NULL, str_buf[ETH_ALEN];
+ for (struct rtattr *rta = (struct rtattr*)&ndm[1]; RTA_OK(rta, alen);
+ rta = RTA_NEXT(rta, alen)) {
+ if (rta->rta_type == NDA_DST && RTA_PAYLOAD(rta) >= sizeof(struct in6_addr))
+ inet_ntop(AF_INET6, RTA_DATA(rta), buf, sizeof(buf));
+ else if (rta->rta_type == NDA_LLADDR && RTA_PAYLOAD(rta) >= 6)
+ mac = ether_ntoa_l(RTA_DATA(rta),str_buf);
+ }
+
+ if (mac)
+ lua_pushstring(L, mac);
+ else
+ lua_pushboolean(L, false);
+
+ lua_setfield(L, -2, buf);
+ }
+ } while (nh->nlmsg_type == RTM_NEWNEIGH);
+
+ close(sock);
+ return 1;
+
+error:
+ close(sock);
+ lua_pushnil(L);
+ lua_pushinteger(L, errno);
+ lua_pushstring(L, strerror(errno));
+ return 3;
+}
+
+
+static const luaL_reg R[] = {
+ {"get", neightbl_get},
+ {NULL, NULL}
+};
+
+
+int luaopen_neightbl(lua_State *l)
+{
+ luaL_register(l, "neightbl", R);
+ return 1;
+}