mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-28 17:12:22 +00:00
udhnssnoop: move the code directly into the repo
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
@@ -6,12 +6,6 @@ PKG_RELEASE:=1
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
PKG_SOURCE_URL=https://github.com/blogic/udnssnoop.git
|
||||
PKG_MIRROR_HASH:=afd17cc6aed4a151bc0f437b84491d751932a39f93f429418200e9e8be53dfad
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2021-04-12
|
||||
PKG_SOURCE_VERSION:=67e1e5f0bfc12222aa59c54e7066b1c00a680e56
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
|
||||
4
feeds/ucentral/udnssnoop/src/.gitignore
vendored
Normal file
4
feeds/ucentral/udnssnoop/src/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
udnssnoop
|
||||
16
feeds/ucentral/udnssnoop/src/CMakeLists.txt
Normal file
16
feeds/ucentral/udnssnoop/src/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
PROJECT(udnssnoop C)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
SET(SOURCES main.c dns.c)
|
||||
SET(LIBS ubox ubus resolv)
|
||||
|
||||
ADD_EXECUTABLE(udnssnoop ${SOURCES})
|
||||
TARGET_LINK_LIBRARIES(udnssnoop ${LIBS})
|
||||
INSTALL(TARGETS udnssnoop
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
215
feeds/ucentral/udnssnoop/src/dns.c
Normal file
215
feeds/ucentral/udnssnoop/src/dns.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
static char name_buffer[MAX_NAME_LEN + 1];
|
||||
|
||||
static int
|
||||
scan_name(const uint8_t *buffer, int len)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
while (len && (*buffer != '\0')) {
|
||||
int l = *buffer;
|
||||
|
||||
if (IS_COMPRESSED(l))
|
||||
return offset + 2;
|
||||
|
||||
if (l + 1 > len) return -1;
|
||||
len -= l + 1;
|
||||
offset += l + 1;
|
||||
buffer += l + 1;
|
||||
}
|
||||
|
||||
if (!len || !offset || (*buffer != '\0'))
|
||||
return -1;
|
||||
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
static struct dns_header*
|
||||
dns_consume_header(uint8_t **data, int *len)
|
||||
{
|
||||
struct dns_header *h = (struct dns_header *) *data;
|
||||
|
||||
if (*len < sizeof(struct dns_header))
|
||||
return NULL;
|
||||
|
||||
h->id = be16_to_cpu(h->id);
|
||||
h->flags = be16_to_cpu(h->flags);
|
||||
h->questions = be16_to_cpu(h->questions);
|
||||
h->answers = be16_to_cpu(h->answers);
|
||||
h->authority = be16_to_cpu(h->authority);
|
||||
h->additional = be16_to_cpu(h->additional);
|
||||
|
||||
*len -= sizeof(struct dns_header);
|
||||
*data += sizeof(struct dns_header);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct dns_question*
|
||||
dns_consume_question(uint8_t **data, int *len)
|
||||
{
|
||||
struct dns_question *q = (struct dns_question *) *data;
|
||||
|
||||
if (*len < sizeof(struct dns_question))
|
||||
return NULL;
|
||||
|
||||
q->type = be16_to_cpu(q->type);
|
||||
q->class = be16_to_cpu(q->class);
|
||||
|
||||
*len -= sizeof(struct dns_question);
|
||||
*data += sizeof(struct dns_question);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static struct dns_answer*
|
||||
dns_consume_answer(uint8_t **data, int *len)
|
||||
{
|
||||
struct dns_answer *a = (struct dns_answer *) *data;
|
||||
|
||||
if (*len < sizeof(struct dns_answer))
|
||||
return NULL;
|
||||
|
||||
a->type = be16_to_cpu(a->type);
|
||||
a->class = be16_to_cpu(a->class);
|
||||
a->ttl = be32_to_cpu(a->ttl);
|
||||
a->rdlength = be16_to_cpu(a->rdlength);
|
||||
|
||||
*len -= sizeof(struct dns_answer);
|
||||
*data += sizeof(struct dns_answer);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static char *
|
||||
dns_consume_name(const uint8_t *base, int blen, uint8_t **data, int *len)
|
||||
{
|
||||
int nlen = scan_name(*data, *len);
|
||||
|
||||
if (nlen < 1)
|
||||
return NULL;
|
||||
|
||||
if (dn_expand(base, base + blen, *data, name_buffer, MAX_NAME_LEN) < 0) {
|
||||
perror("dns_consume_name/dn_expand");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*len -= nlen;
|
||||
*data += nlen;
|
||||
|
||||
return name_buffer;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_answer(uint8_t *buffer, int len, uint8_t **b, int *rlen)
|
||||
{
|
||||
char *name = dns_consume_name(buffer, len, b, rlen);
|
||||
struct dns_answer *a;
|
||||
uint8_t *rdata;
|
||||
char ipbuf[33];
|
||||
|
||||
if (*rlen < 0) {
|
||||
fprintf(stderr, "dropping: bad answer - bad length\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
a = dns_consume_answer(b, rlen);
|
||||
if (!a) {
|
||||
fprintf(stderr, "dropping: bad answer - bad buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((a->class & ~CLASS_FLUSH) != CLASS_IN) {
|
||||
fprintf(stderr, "dropping: class\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rdata = *b;
|
||||
if (a->rdlength > *rlen) {
|
||||
fprintf(stderr, "dropping: bad answer - bad rlen\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*rlen -= a->rdlength;
|
||||
*b += a->rdlength;
|
||||
|
||||
switch (a->type) {
|
||||
case TYPE_A:
|
||||
if (a->rdlength != 4)
|
||||
return 0;
|
||||
|
||||
if (!inet_ntop(AF_INET, rdata, ipbuf, sizeof(ipbuf)))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case TYPE_AAAA:
|
||||
if (a->rdlength != 16)
|
||||
return 0;
|
||||
|
||||
if (!inet_ntop(AF_INET6, rdata, ipbuf, sizeof(ipbuf)))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
ubus_notify_qosify(name, ipbuf, a->type, a->ttl);
|
||||
printf("%s %s %" PRIu32 "\n", name, ipbuf, a->ttl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
dns_handle_packet(uint8_t *buffer, int len)
|
||||
{
|
||||
struct dns_header *h;
|
||||
uint8_t *b = buffer;
|
||||
int rlen = len;
|
||||
|
||||
h = dns_consume_header(&b, &rlen);
|
||||
if (!h) {
|
||||
fprintf(stderr, "dropping: bad header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(h->flags & FLAG_RESPONSE))
|
||||
return;
|
||||
|
||||
if (!h->answers)
|
||||
return;
|
||||
|
||||
while (h->questions-- > 0) {
|
||||
char *name = dns_consume_name(buffer, len, &b, &rlen);
|
||||
struct dns_question *q;
|
||||
|
||||
if (!name || rlen < 0)
|
||||
return;
|
||||
|
||||
q = dns_consume_question(&b, &rlen);
|
||||
if (!q) {
|
||||
fprintf(stderr, "dropping: bad question\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (h->answers-- > 0)
|
||||
if (parse_answer(buffer, len, &b, &rlen))
|
||||
return;
|
||||
|
||||
/* while (h->authority-- > 0)
|
||||
if (parse_answer(buffer, len, &b, &rlen))
|
||||
return;
|
||||
|
||||
while (h->additional-- > 0)
|
||||
if (parse_answer(buffer, len, &b, &rlen))
|
||||
return;
|
||||
*/
|
||||
}
|
||||
86
feeds/ucentral/udnssnoop/src/dns.h
Normal file
86
feeds/ucentral/udnssnoop/src/dns.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#ifndef _DNS_H__
|
||||
#define _DNS_H__
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/udp.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include <libubox/avl-cmp.h>
|
||||
#include <libubox/utils.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/ulog.h>
|
||||
#include <libubus.h>
|
||||
#include <uci.h>
|
||||
#include <uci_blob.h>
|
||||
|
||||
#define FLAG_RESPONSE 0x8000
|
||||
#define FLAG_AUTHORATIVE 0x0400
|
||||
|
||||
#define TYPE_A 0x0001
|
||||
#define TYPE_PTR 0x000C
|
||||
#define TYPE_TXT 0x0010
|
||||
#define TYPE_AAAA 0x001c
|
||||
#define TYPE_SRV 0x0021
|
||||
#define TYPE_ANY 0x00ff
|
||||
|
||||
#define IS_COMPRESSED(x) ((x & 0xc0) == 0xc0)
|
||||
|
||||
#define CLASS_FLUSH 0x8000
|
||||
#define CLASS_UNICAST 0x8000
|
||||
#define CLASS_IN 0x0001
|
||||
|
||||
#define MAX_NAME_LEN 8096
|
||||
#define MAX_DATA_LEN 8096
|
||||
|
||||
struct vlan_hdr {
|
||||
uint16_t h_vlan_TCI;
|
||||
uint16_t h_vlan_encapsulated_proto;
|
||||
};
|
||||
|
||||
struct dns_header {
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint16_t questions;
|
||||
uint16_t answers;
|
||||
uint16_t authority;
|
||||
uint16_t additional;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dns_srv_data {
|
||||
uint16_t priority;
|
||||
uint16_t weight;
|
||||
uint16_t port;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dns_answer {
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
uint32_t ttl;
|
||||
uint16_t rdlength;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dns_question {
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
} __attribute__((packed));
|
||||
|
||||
void dns_handle_packet(uint8_t *buffer, int len);
|
||||
void ubus_notify_qosify(char *name, char *address, int type, int ttl);
|
||||
|
||||
#endif
|
||||
282
feeds/ucentral/udnssnoop/src/main.c
Normal file
282
feeds/ucentral/udnssnoop/src/main.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
/* code is derived from hapd proxy_arp snooping */
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
/* sudo tcpdump -s 3000 -dd greater 96 and '(ip or ip6)' and '(udp or tcp)' and '(port 53)' */
|
||||
static struct sock_filter dns_sock_filter_insns[] = {
|
||||
{ 0x80, 0, 0, 0x00000000 },
|
||||
{ 0x35, 0, 19, 0x00000060 },
|
||||
{ 0x28, 0, 0, 0x0000000c },
|
||||
{ 0x15, 0, 9, 0x00000800 },
|
||||
{ 0x30, 0, 0, 0x00000017 },
|
||||
{ 0x15, 0, 15, 0x00000011 },
|
||||
{ 0x28, 0, 0, 0x00000014 },
|
||||
{ 0x45, 13, 0, 0x00001fff },
|
||||
{ 0xb1, 0, 0, 0x0000000e },
|
||||
{ 0x48, 0, 0, 0x0000000e },
|
||||
{ 0x15, 9, 0, 0x00000035 },
|
||||
{ 0x48, 0, 0, 0x00000010 },
|
||||
{ 0x15, 7, 8, 0x00000035 },
|
||||
{ 0x15, 0, 7, 0x000086dd },
|
||||
{ 0x30, 0, 0, 0x00000014 },
|
||||
{ 0x15, 0, 5, 0x00000011 },
|
||||
{ 0x28, 0, 0, 0x00000036 },
|
||||
{ 0x15, 2, 0, 0x00000035 },
|
||||
{ 0x28, 0, 0, 0x00000038 },
|
||||
{ 0x15, 0, 1, 0x00000035 },
|
||||
{ 0x6, 0, 0, 0x00000bb8 },
|
||||
{ 0x6, 0, 0, 0x00000000 },
|
||||
};
|
||||
|
||||
static const struct sock_fprog sock_filter = {
|
||||
.len = ARRAY_SIZE(dns_sock_filter_insns),
|
||||
.filter = dns_sock_filter_insns,
|
||||
};
|
||||
|
||||
static struct ubus_auto_conn conn;
|
||||
static struct uloop_fd fd;
|
||||
static struct blob_buf b;
|
||||
static char *ifname;
|
||||
static int qosify;
|
||||
|
||||
static struct ubus_object_type ubus_object_type = {
|
||||
.name = "dnssnoop"
|
||||
};
|
||||
|
||||
static void ubus_state_handler(struct ubus_context *ctx, struct ubus_object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
struct ubus_object ubus_object = {
|
||||
.name = "dnssnoop",
|
||||
.type = &ubus_object_type,
|
||||
.subscribe_cb = ubus_state_handler,
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
proto_is_vlan(uint16_t h_proto)
|
||||
{
|
||||
return !!(h_proto == ETH_P_8021Q ||
|
||||
h_proto == ETH_P_8021AD);
|
||||
}
|
||||
|
||||
static int
|
||||
consume_buffer(uint8_t **buf, int *len, int size)
|
||||
{
|
||||
if (size > *len)
|
||||
return -1;
|
||||
|
||||
*buf += size;
|
||||
*len -= size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
packet_handle(uint8_t *buf, int len)
|
||||
{
|
||||
struct ethhdr *eth = (struct ethhdr *)buf;
|
||||
uint16_t h_proto;
|
||||
|
||||
if (consume_buffer(&buf, &len, sizeof(*eth)))
|
||||
return;
|
||||
|
||||
h_proto = eth->h_proto;
|
||||
|
||||
if (proto_is_vlan(ntohs(h_proto))) {
|
||||
struct vlan_hdr *vlanh = (struct vlan_hdr *)buf;
|
||||
|
||||
if (consume_buffer(&buf, &len, sizeof(struct vlan_hdr)))
|
||||
return;
|
||||
|
||||
h_proto = vlanh->h_vlan_encapsulated_proto;
|
||||
}
|
||||
|
||||
switch (ntohs(eth->h_proto)) {
|
||||
case ETH_P_IP:
|
||||
if (consume_buffer(&buf, &len, sizeof(struct ip)))
|
||||
return;
|
||||
break;
|
||||
case ETH_P_IPV6:
|
||||
if (consume_buffer(&buf, &len, sizeof(struct ip6_hdr)))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (consume_buffer(&buf, &len, sizeof(struct udphdr)))
|
||||
return;
|
||||
|
||||
dns_handle_packet(buf, len);
|
||||
}
|
||||
|
||||
static void
|
||||
socket_fd_cb(struct uloop_fd *fd, unsigned int events)
|
||||
{
|
||||
uint8_t buf[8192];
|
||||
|
||||
do {
|
||||
int len = recvfrom(fd->fd, buf, sizeof(buf), MSG_DONTWAIT, NULL, NULL);
|
||||
|
||||
if (len <= 0) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
return;
|
||||
default:
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
packet_handle(buf, len);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
static int
|
||||
socket_open(void)
|
||||
{
|
||||
int sock;
|
||||
|
||||
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (sock == -1) {
|
||||
ULOG_ERR("failed to open socket on %s\n", ifname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname))) {
|
||||
ULOG_ERR("failed to bind socket to %s\n", ifname);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
|
||||
&sock_filter, sizeof(struct sock_fprog))) {
|
||||
ULOG_ERR("failed to attach filter to %s\n", ifname);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static void
|
||||
snoop_start(void)
|
||||
{
|
||||
int sock = socket_open();
|
||||
if (sock == -1) {
|
||||
ULOG_ERR("failed to open socket on %s\n", ifname);
|
||||
return;
|
||||
}
|
||||
fd.cb = socket_fd_cb;
|
||||
fd.fd = sock;
|
||||
uloop_fd_add(&fd, ULOOP_READ);
|
||||
}
|
||||
|
||||
void
|
||||
ubus_notify_qosify(char *name, char *address, int type, int ttl)
|
||||
{
|
||||
if (!qosify)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
switch (type) {
|
||||
case TYPE_AAAA:
|
||||
blobmsg_add_string(&b, "type", "AAAA");
|
||||
break;
|
||||
case TYPE_A:
|
||||
blobmsg_add_string(&b, "type", "A");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
blobmsg_add_string(&b, "address", address);
|
||||
blobmsg_add_u32(&b, "ttl", ttl);
|
||||
|
||||
ubus_invoke(&conn.ctx, qosify, "add_dns_host", b.head, NULL, NULL, 200);
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_handle_status(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
enum {
|
||||
EVENT_ID,
|
||||
EVENT_PATH,
|
||||
__EVENT_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy status_policy[__EVENT_MAX] = {
|
||||
[EVENT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
|
||||
[EVENT_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
struct blob_attr *tb[__EVENT_MAX];
|
||||
uint32_t id;
|
||||
char *path;
|
||||
|
||||
blobmsg_parse(status_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (!tb[EVENT_ID] || !tb[EVENT_PATH])
|
||||
return;
|
||||
|
||||
path = blobmsg_get_string(tb[EVENT_PATH]);
|
||||
id = blobmsg_get_u32(tb[EVENT_ID]);
|
||||
|
||||
if (strcmp(path, "qosify"))
|
||||
return;
|
||||
|
||||
if (!strcmp("ubus.object.remove", type))
|
||||
qosify = 0;
|
||||
|
||||
if (!strcmp("ubus.object.add", type))
|
||||
qosify = id;
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_lookup_cb(struct ubus_context *ctx, struct ubus_object_data *obj,
|
||||
void *priv)
|
||||
{
|
||||
if (strcmp(obj->path, "qosify"))
|
||||
return;
|
||||
|
||||
qosify = obj->id;
|
||||
}
|
||||
|
||||
static struct ubus_event_handler ubus_status_handler = { .cb = ubus_handle_status };
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
ULOG_NOTE("connected to ubus\n");
|
||||
ubus_add_object(ctx, &ubus_object);
|
||||
|
||||
ubus_register_event_handler(ctx, &ubus_status_handler, "ubus.object.add");
|
||||
ubus_register_event_handler(ctx, &ubus_status_handler, "ubus.object.remove");
|
||||
|
||||
ubus_lookup(ctx, NULL, ubus_lookup_cb, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
|
||||
ifname = argv[1];
|
||||
|
||||
ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "udnssnoop");
|
||||
|
||||
uloop_init();
|
||||
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
snoop_start();
|
||||
uloop_run();
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user