cidr_t from;
cidr_t src;
cidr_t dst;
+ struct ether_addr mac;
};
struct dump_state {
};
-static int _cidr_new(lua_State *L, int index, int family);
+static int _cidr_new(lua_State *L, int index, int family, bool mask);
static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p)
{
if (lua_type(L, index) == LUA_TUSERDATA)
return luaL_checkudata(L, index, LUCI_IP_CIDR);
- if (_cidr_new(L, index, p ? p->family : 0))
+ if (_cidr_new(L, index, p ? p->family : 0, false))
return lua_touserdata(L, -1);
luaL_error(L, "Invalid operand");
return bits;
}
-static int _cidr_new(lua_State *L, int index, int family)
+static int _cidr_new(lua_State *L, int index, int family, bool mask)
{
uint32_t n;
const char *addr;
if (family && cidr.family != family)
return 0;
+
+ if (mask)
+ cidr.bits = L_checkbits(L, index + 1, &cidr);
}
if (!(cidrp = lua_newuserdata(L, sizeof(*cidrp))))
static int cidr_new(lua_State *L)
{
- return _cidr_new(L, 1, 0);
+ return _cidr_new(L, 1, 0, true);
}
static int cidr_ipv4(lua_State *L)
{
- return _cidr_new(L, 1, AF_INET);
+ return _cidr_new(L, 1, AF_INET, true);
}
static int cidr_ipv6(lua_State *L)
{
- return _cidr_new(L, 1, AF_INET6);
+ return _cidr_new(L, 1, AF_INET6, true);
}
static int cidr_is4(lua_State *L)
out:
nl_cb_put(cb);
- return (s.index > 0 && s.callback == 0);
+ return (s.callback == 0);
}
static int route_get(lua_State *L)
}
+static bool diff_macaddr(struct ether_addr *mac1, struct ether_addr *mac2)
+{
+ struct ether_addr empty = { };
+
+ if (!memcmp(mac2, &empty, sizeof(empty)))
+ return false;
+
+ if (!mac1 || memcmp(mac1, mac2, sizeof(empty)))
+ return true;
+
+ return false;
+}
+
static int cb_dump_neigh(struct nl_msg *msg, void *arg)
{
char buf[32];
- struct ether_addr *addr;
+ struct ether_addr *mac;
+ struct in6_addr *dst;
struct dump_state *s = arg;
struct dump_filter *f = s->filter;
struct nlmsghdr *hdr = nlmsg_hdr(msg);
struct ndmsg *nd = NLMSG_DATA(hdr);
struct nlattr *tb[NDA_MAX+1];
+ int bitlen;
if (hdr->nlmsg_type != RTM_NEWNEIGH ||
(nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
return NL_SKIP;
+ nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
+
+ mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
+ dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
+
+ bitlen = (nd->ndm_family == AF_INET) ? 32 : 128;
+
if ((f->family && nd->ndm_family != f->family) ||
(f->iif && nd->ndm_ifindex != f->iif) ||
- (f->type && !(f->type & nd->ndm_state)))
+ (f->type && !(f->type & nd->ndm_state)) ||
+ diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) ||
+ diff_macaddr(mac, &f->mac))
goto out;
- nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
-
if (s->callback)
lua_pushvalue(s->L, 2);
L_setbool(s->L, "noarp", (nd->ndm_state & NUD_NOARP));
L_setbool(s->L, "permanent", (nd->ndm_state & NUD_PERMANENT));
- if (tb[NDA_DST])
- L_setaddr(s->L, "dest", nd->ndm_family, RTA_DATA(tb[NDA_DST]), -1);
+ if (dst)
+ L_setaddr(s->L, "dest", nd->ndm_family, dst, -1);
- if (tb[NDA_LLADDR])
+ if (mac)
{
- addr = RTA_DATA(tb[NDA_LLADDR]);
snprintf(buf, sizeof(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]);
+ mac->ether_addr_octet[0], mac->ether_addr_octet[1],
+ mac->ether_addr_octet[2], mac->ether_addr_octet[3],
+ mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
lua_pushstring(s->L, buf);
lua_setfield(s->L, -2, "mac");
static int neighbor_dump(lua_State *L)
{
+ cidr_t p;
const char *s;
- struct dump_filter filter = {
- .type = 0xFF & ~NUD_NOARP
- };
+ struct ether_addr *mac;
+ struct dump_filter filter = { .type = 0xFF & ~NUD_NOARP };
struct dump_state st = {
.callback = lua_isfunction(L, 2),
.pending = 1,
if ((s = L_getstr(L, 1, "dev")) != NULL)
filter.iif = if_nametoindex(s);
+
+ if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p))
+ filter.dst = p;
+
+ if ((s = L_getstr(L, 1, "mac")) != NULL &&
+ (mac = ether_aton(s)) != NULL)
+ filter.mac = *mac;
}
if (!sock)
out:
nl_cb_put(cb);
- return (st.index > 0 && st.callback == 0);
+ return (st.callback == 0);
+}
+
+
+static int cb_dump_link(struct nl_msg *msg, void *arg)
+{
+ char *p, *addr, buf[48];
+ struct dump_state *s = arg;
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ struct ifinfomsg *ifm = NLMSG_DATA(hdr);
+ struct nlattr *tb[IFLA_MAX+1];
+ int i, len;
+
+ if (hdr->nlmsg_type != RTM_NEWLINK)
+ return NL_SKIP;
+
+ nlmsg_parse(hdr, sizeof(*ifm), tb, IFLA_MAX, NULL);
+
+ L_setbool(s->L, "up", (ifm->ifi_flags & IFF_RUNNING));
+ L_setint(s->L, "type", ifm->ifi_type);
+ L_setstr(s->L, "name", if_indextoname(ifm->ifi_index, buf));
+
+ if (tb[IFLA_MTU])
+ L_setint(s->L, "mtu", RTA_U32(tb[IFLA_MTU]));
+
+ if (tb[IFLA_TXQLEN])
+ L_setint(s->L, "qlen", RTA_U32(tb[IFLA_TXQLEN]));
+
+ if (tb[IFLA_MASTER])
+ L_setdev(s->L, "master", tb[IFLA_MASTER]);
+
+ if (tb[IFLA_ADDRESS])
+ {
+ len = nla_len(tb[IFLA_ADDRESS]);
+ addr = nla_get_string(tb[IFLA_ADDRESS]);
+
+ if ((len * 3) <= sizeof(buf))
+ {
+ for (p = buf, i = 0; i < len; i++)
+ p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++);
+
+ L_setstr(s->L, "mac", buf);
+ }
+ }
+
+ s->pending = 0;
+ return NL_SKIP;
+}
+
+static int link_get(lua_State *L)
+{
+ const char *dev = luaL_checkstring(L, 1);
+ struct dump_state st = {
+ .pending = 1,
+ .L = L
+ };
+
+ if (!sock)
+ {
+ sock = nl_socket_alloc();
+ if (!sock)
+ return _error(L, -1, "Out of memory");
+
+ if (nl_connect(sock, NETLINK_ROUTE))
+ return _error(L, 0, NULL);
+ }
+
+ struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
+ struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+ struct ifinfomsg ifm = { .ifi_index = if_nametoindex(dev) };
+
+ if (!msg || !cb)
+ return 0;
+
+ nlmsg_append(msg, &ifm, sizeof(ifm), 0);
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_link, &st);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
+ nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
+
+ lua_newtable(L);
+
+ nl_send_auto_complete(sock, msg);
+
+ while (st.pending > 0)
+ nl_recvmsgs(sock, cb);
+
+ nlmsg_free(msg);
+ nl_cb_put(cb);
+
+ return 1;
}
{ "neighbors", neighbor_dump },
+ { "link", link_get },
+
{ }
};