mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
udhcprelay: add new package
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
36
feeds/ucentral/udhcprelay/Makefile
Normal file
36
feeds/ucentral/udhcprelay/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=udhcprelay
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/udhcprelay
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=DHCP Relay Daemon
|
||||
DEPENDS:=+libubox +libubus +kmod-ifb +tc +kmod-sched
|
||||
endef
|
||||
|
||||
define Package/udhcprelay/conffiles
|
||||
/etc/config/dhcprelay
|
||||
endef
|
||||
|
||||
define Package/udhcprelay/install
|
||||
$(INSTALL_DIR) \
|
||||
$(1)/usr/sbin \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/hotplug.d/net
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/udhcprelay $(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/dhcprelay.init $(1)/etc/init.d/dhcprelay
|
||||
$(INSTALL_DATA) ./files/dhcprelay.conf $(1)/etc/config/dhcprelay
|
||||
$(INSTALL_DATA) ./files/dhcprelay.hotplug $(1)/etc/hotplug.d/net/10-dhcprelay
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,udhcprelay))
|
||||
11
feeds/ucentral/udhcprelay/files/dhcprelay.conf
Normal file
11
feeds/ucentral/udhcprelay/files/dhcprelay.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
#config bridge
|
||||
# option name up
|
||||
# list vlans 2
|
||||
# list upstream wan
|
||||
#
|
||||
#config device
|
||||
# option disabled 1
|
||||
# option name eth0
|
||||
# option ingress 1
|
||||
# option egress 1
|
||||
|
||||
2
feeds/ucentral/udhcprelay/files/dhcprelay.hotplug
Normal file
2
feeds/ucentral/udhcprelay/files/dhcprelay.hotplug
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ubus call dhcprelay check_devices
|
||||
81
feeds/ucentral/udhcprelay/files/dhcprelay.init
Normal file
81
feeds/ucentral/udhcprelay/files/dhcprelay.init
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=40
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/udhcprelay
|
||||
|
||||
add_device() {
|
||||
local cfg="$1"
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return
|
||||
|
||||
config_get name "$cfg" name
|
||||
json_add_object "$name"
|
||||
|
||||
json_close_object
|
||||
}
|
||||
|
||||
add_string() {
|
||||
json_add_string "" "$@"
|
||||
}
|
||||
|
||||
add_array() {
|
||||
local cfg="$1"
|
||||
local name="$2"
|
||||
|
||||
json_add_array "$name"
|
||||
config_list_foreach "$cfg" "$name" add_string
|
||||
json_close_array
|
||||
}
|
||||
|
||||
add_bridge() {
|
||||
local cfg="$1"
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return
|
||||
|
||||
config_get name "$cfg" name
|
||||
json_add_object "$name"
|
||||
|
||||
add_array "$cfg" vlans
|
||||
add_array "$cfg" ignore
|
||||
add_array "$cfg" upstream
|
||||
|
||||
json_close_object
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
|
||||
config_load dhcprelay
|
||||
|
||||
json_add_object devices
|
||||
config_foreach add_device device
|
||||
json_close_object
|
||||
|
||||
json_add_object bridges
|
||||
config_foreach add_bridge bridge
|
||||
json_close_object
|
||||
|
||||
ubus call dhcprelay config "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger dhcprelay
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_set_param limits core="unlimited"
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for dhcprelay
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
16
feeds/ucentral/udhcprelay/src/CMakeLists.txt
Normal file
16
feeds/ucentral/udhcprelay/src/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
PROJECT(udhcprelay C)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno-address-of-packed-member -fwrapv -fno-strict-aliasing)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
SET(SOURCES main.c ubus.c dev.c dhcp.c relay.c)
|
||||
SET(LIBS ubox ubus)
|
||||
|
||||
ADD_EXECUTABLE(udhcprelay ${SOURCES})
|
||||
TARGET_LINK_LIBRARIES(udhcprelay ${LIBS})
|
||||
INSTALL(TARGETS udhcprelay
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
392
feeds/ucentral/udhcprelay/src/dev.c
Normal file
392
feeds/ucentral/udhcprelay/src/dev.c
Normal file
@@ -0,0 +1,392 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <netpacket/packet.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libubox/vlist.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include "dhcprelay.h"
|
||||
|
||||
#define APPEND(_buf, _ofs, _format, ...) _ofs += snprintf(_buf + _ofs, sizeof(_buf) - _ofs, _format, ##__VA_ARGS__)
|
||||
|
||||
static void dev_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
|
||||
struct vlist_node *node_old);
|
||||
|
||||
static struct uloop_fd ufd;
|
||||
static VLIST_TREE(devices, avl_strcmp, dev_update_cb, true, false);
|
||||
static AVL_TREE(bridges, avl_strcmp, false, NULL);
|
||||
static struct blob_attr *devlist;
|
||||
|
||||
static void
|
||||
dhcprelay_socket_cb(struct uloop_fd *fd, unsigned int events)
|
||||
{
|
||||
static uint8_t buf[8192];
|
||||
struct packet pkt = {
|
||||
.head = buf,
|
||||
.data = buf + 32,
|
||||
.end = &buf[sizeof(buf)],
|
||||
};
|
||||
struct sockaddr_ll sll;
|
||||
socklen_t socklen = sizeof(sll);
|
||||
int len;
|
||||
|
||||
retry:
|
||||
len = recvfrom(fd->fd, pkt.data, pkt.end - pkt.data, MSG_DONTWAIT, (struct sockaddr *)&sll, &socklen);
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
pkt.l2.ifindex = ntohl(*(uint32_t *)(pkt.data + 2));
|
||||
pkt.len = len;
|
||||
dhcprelay_packet_cb(&pkt);
|
||||
}
|
||||
|
||||
static int
|
||||
dhcprelay_open_socket(void)
|
||||
{
|
||||
struct sockaddr_ll sll = {
|
||||
.sll_family = AF_PACKET,
|
||||
.sll_protocol = htons(ETH_P_ALL),
|
||||
};
|
||||
int sock, yes = 1;
|
||||
|
||||
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (sock == -1) {
|
||||
ULOG_ERR("failed to create raw socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsockopt(sock, SOL_PACKET, PACKET_ORIGDEV, &yes, sizeof(yes));
|
||||
|
||||
sll.sll_ifindex = if_nametoindex(DHCPRELAY_IFB_NAME);
|
||||
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) {
|
||||
ULOG_ERR("failed to bind socket to "DHCPRELAY_IFB_NAME": %s\n",
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
ufd.fd = sock;
|
||||
ufd.cb = dhcprelay_socket_cb;
|
||||
uloop_fd_add(&ufd, ULOOP_READ);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
prepare_filter_cmd(char *buf, int len, const char *dev, int prio, bool add)
|
||||
{
|
||||
return snprintf(buf, len, "tc filter %s dev '%s' ingress prio %d",
|
||||
add ? "add" : "del", dev, prio);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_add_filter(struct device *dev, int prio, const char *match_string)
|
||||
{
|
||||
const char *ifname = dev->ifname;
|
||||
int ifindex = dev->ifindex;
|
||||
char buf[350];
|
||||
int ofs;
|
||||
|
||||
ofs = prepare_filter_cmd(buf, sizeof(buf), ifname, prio++, true);
|
||||
APPEND(buf, ofs,
|
||||
" %s flowid 1:1", match_string);
|
||||
if (!dev->upstream)
|
||||
APPEND(buf, ofs,
|
||||
" action pedit ex munge eth dst set ff:ff:%02x:%02x:%02x:%02x pipe",
|
||||
(uint8_t)(ifindex >> 24),
|
||||
(uint8_t)(ifindex >> 16),
|
||||
(uint8_t)(ifindex >> 8),
|
||||
(uint8_t)(ifindex));
|
||||
APPEND(buf, ofs,
|
||||
" action mirred ingress %s dev " DHCPRELAY_IFB_NAME "%s",
|
||||
dev->upstream ? "mirror" : "redirect",
|
||||
dev->upstream ? " continue" : "");
|
||||
dhcprelay_run_cmd(buf, false);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_dev_attach_filters(struct device *dev)
|
||||
{
|
||||
int prio = DHCPRELAY_PRIO_BASE;
|
||||
|
||||
dhcprelay_add_filter(dev, prio++,
|
||||
"protocol ip u32 match ip dport 67 0xffff");
|
||||
dhcprelay_add_filter(dev, prio++,
|
||||
"protocol 802.1Q u32 offset plus 4 match ip dport 67 0xffff");
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_dev_cleanup_filters(struct device *dev)
|
||||
{
|
||||
char buf[128];
|
||||
int i;
|
||||
|
||||
for (i = DHCPRELAY_PRIO_BASE; i < DHCPRELAY_PRIO_BASE + 2; i++) {
|
||||
prepare_filter_cmd(buf, sizeof(buf), dev->ifname, i, false);
|
||||
dhcprelay_run_cmd(buf, true);
|
||||
}
|
||||
dev->active = false;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_dev_attach(struct device *dev)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
dev->active = true;
|
||||
snprintf(buf, sizeof(buf), "tc qdisc add dev '%s' clsact", dev->ifname);
|
||||
dhcprelay_run_cmd(buf, true);
|
||||
|
||||
dhcprelay_dev_attach_filters(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
__dhcprelay_dev_check(struct device *dev)
|
||||
{
|
||||
int ifindex = if_nametoindex(dev->ifname);
|
||||
|
||||
if (ifindex == dev->ifindex)
|
||||
return;
|
||||
|
||||
dev->ifindex = ifindex;
|
||||
dhcprelay_dev_cleanup_filters(dev);
|
||||
if (ifindex)
|
||||
dhcprelay_dev_attach(dev);
|
||||
}
|
||||
|
||||
static void dev_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
|
||||
struct vlist_node *node_old)
|
||||
{
|
||||
struct device *dev = NULL, *dev_free = NULL;
|
||||
|
||||
if (node_old && node_new) {
|
||||
dev = container_of(node_old, struct device, node);
|
||||
dev_free = container_of(node_new, struct device, node);
|
||||
if (dev->upstream != dev_free->upstream) {
|
||||
dev->upstream = dev_free->upstream;
|
||||
dev->ifindex = 0;
|
||||
dhcprelay_dev_cleanup_filters(dev_free);
|
||||
}
|
||||
} else if (node_old) {
|
||||
dev_free = container_of(node_old, struct device, node);
|
||||
if (dev_free->active)
|
||||
dhcprelay_dev_cleanup_filters(dev_free);
|
||||
} else if (node_new) {
|
||||
dev = container_of(node_new, struct device, node);
|
||||
}
|
||||
|
||||
if (dev)
|
||||
__dhcprelay_dev_check(dev);
|
||||
if (dev_free)
|
||||
free(dev_free);
|
||||
}
|
||||
|
||||
void dhcprelay_dev_add(const char *name, bool upstream)
|
||||
{
|
||||
struct device *dev;
|
||||
int len;
|
||||
|
||||
dev = calloc(1, sizeof(*dev));
|
||||
len = snprintf(dev->ifname, sizeof(dev->ifname), "%s", name);
|
||||
if (!len || len > IFNAMSIZ) {
|
||||
free(dev);
|
||||
return;
|
||||
}
|
||||
dev->upstream = upstream;
|
||||
|
||||
vlist_add(&devices, &dev->node, dev->ifname);
|
||||
}
|
||||
|
||||
static struct device *
|
||||
dhcprelay_dev_get_by_index(int ifindex)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
vlist_for_each_element(&devices, dev, node) {
|
||||
if (dev->ifindex == ifindex)
|
||||
return dev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dhcprelay_dev_send(struct packet *pkt, int ifindex, const uint8_t *addr, uint16_t proto)
|
||||
{
|
||||
struct sockaddr_ll sll = {
|
||||
.sll_family = AF_PACKET,
|
||||
.sll_protocol = cpu_to_be16(ETH_P_ALL),
|
||||
.sll_ifindex = ifindex,
|
||||
};
|
||||
struct ifreq ifr = {};
|
||||
struct ethhdr *eth;
|
||||
struct device *dev;
|
||||
int fd = ufd.fd;
|
||||
|
||||
if (!ifindex)
|
||||
return;
|
||||
|
||||
dev = dhcprelay_dev_get_by_index(ifindex);
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
strncpy(ifr.ifr_name, dev->node.avl.key, sizeof(ifr.ifr_name));
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0 ||
|
||||
ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
|
||||
return;
|
||||
|
||||
eth = pkt_push(pkt, sizeof(*eth));
|
||||
if (!eth)
|
||||
return;
|
||||
|
||||
memcpy(eth->h_source, ifr.ifr_hwaddr.sa_data, sizeof(eth->h_source));
|
||||
memcpy(eth->h_dest, addr, sizeof(eth->h_dest));
|
||||
eth->h_proto = cpu_to_be16(proto);
|
||||
sendto(fd, pkt->data, pkt->len, 0, (struct sockaddr *)&sll, sizeof(sll));
|
||||
}
|
||||
|
||||
void dhcprelay_update_devices(void)
|
||||
{
|
||||
struct bridge_entry *br;
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
vlist_update(&devices);
|
||||
|
||||
avl_for_each_element(&bridges, br, node) {
|
||||
blobmsg_for_each_attr(cur, br->upstream, rem) {
|
||||
if (!blobmsg_check_attr(cur, false) ||
|
||||
blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
|
||||
continue;
|
||||
|
||||
dhcprelay_dev_add(blobmsg_get_string(cur), true);
|
||||
}
|
||||
|
||||
dhcprelay_ubus_query_bridge(br);
|
||||
}
|
||||
|
||||
blobmsg_for_each_attr(cur, devlist, rem) {
|
||||
if (!blobmsg_check_attr(cur, false) ||
|
||||
blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
|
||||
continue;
|
||||
|
||||
dhcprelay_dev_add(blobmsg_get_string(cur), false);
|
||||
}
|
||||
|
||||
vlist_flush(&devices);
|
||||
}
|
||||
|
||||
static void dhcprelay_bridge_add(struct blob_attr *data)
|
||||
{
|
||||
enum {
|
||||
BRCONF_ATTR_VLANS,
|
||||
BRCONF_ATTR_IGNORE,
|
||||
BRCONF_ATTR_UPSTREAM,
|
||||
__BRCONF_ATTR_MAX,
|
||||
};
|
||||
static const struct blobmsg_policy policy[__BRCONF_ATTR_MAX] = {
|
||||
[BRCONF_ATTR_VLANS] = { "vlans", BLOBMSG_TYPE_ARRAY },
|
||||
[BRCONF_ATTR_IGNORE] = { "ignore", BLOBMSG_TYPE_ARRAY },
|
||||
[BRCONF_ATTR_UPSTREAM] = { "upstream", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
struct blob_attr *tb[__BRCONF_ATTR_MAX];
|
||||
struct blob_atttr *vlan_buf, *ignore_buf, *upstream_buf;
|
||||
size_t vlan_size = 0, ignore_size = 0, upstream_size = 0;
|
||||
struct bridge_entry *br;
|
||||
char *name_buf;
|
||||
|
||||
blobmsg_parse(policy, __BRCONF_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
|
||||
|
||||
if (tb[BRCONF_ATTR_VLANS] &&
|
||||
blobmsg_check_array(tb[BRCONF_ATTR_VLANS], BLOBMSG_TYPE_UNSPEC) > 0)
|
||||
vlan_size = blob_pad_len(tb[BRCONF_ATTR_VLANS]);
|
||||
|
||||
if (tb[BRCONF_ATTR_IGNORE] &&
|
||||
blobmsg_check_array(tb[BRCONF_ATTR_IGNORE], BLOBMSG_TYPE_STRING) > 0)
|
||||
ignore_size = blob_pad_len(tb[BRCONF_ATTR_IGNORE]);
|
||||
|
||||
if (tb[BRCONF_ATTR_UPSTREAM] &&
|
||||
blobmsg_check_array(tb[BRCONF_ATTR_UPSTREAM], BLOBMSG_TYPE_STRING) > 0)
|
||||
upstream_size = blob_pad_len(tb[BRCONF_ATTR_UPSTREAM]);
|
||||
|
||||
br = calloc_a(sizeof(*br),
|
||||
&vlan_buf, vlan_size,
|
||||
&ignore_buf, ignore_size,
|
||||
&upstream_buf, upstream_size,
|
||||
&name_buf, strlen(blobmsg_name(data)) + 1);
|
||||
if (vlan_size)
|
||||
br->vlans = memcpy(vlan_buf, tb[BRCONF_ATTR_VLANS], vlan_size);
|
||||
if (ignore_size)
|
||||
br->ignore = memcpy(ignore_buf, tb[BRCONF_ATTR_IGNORE], ignore_size);
|
||||
if (upstream_size)
|
||||
br->upstream = memcpy(upstream_buf, tb[BRCONF_ATTR_UPSTREAM], upstream_size);
|
||||
|
||||
br->node.key = strcpy(name_buf, blobmsg_name(data));
|
||||
avl_insert(&bridges, &br->node);
|
||||
}
|
||||
|
||||
void dhcprelay_dev_config_update(struct blob_attr *br_attr, struct blob_attr *dev)
|
||||
{
|
||||
struct bridge_entry *br, *tmp;
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
avl_remove_all_elements(&bridges, br, node, tmp)
|
||||
free(br);
|
||||
|
||||
blobmsg_for_each_attr(cur, br_attr, rem) {
|
||||
if (!blobmsg_check_attr(cur, true) ||
|
||||
blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
|
||||
continue;
|
||||
|
||||
dhcprelay_bridge_add(cur);
|
||||
}
|
||||
|
||||
free(devlist);
|
||||
devlist = dev ? blob_memdup(dev) : NULL;
|
||||
|
||||
dhcprelay_update_devices();
|
||||
}
|
||||
|
||||
int dhcprelay_dev_init(void)
|
||||
{
|
||||
dhcprelay_dev_done();
|
||||
|
||||
if (dhcprelay_run_cmd("ip link add "DHCPRELAY_IFB_NAME" type ifb", false) ||
|
||||
dhcprelay_run_cmd("ip link set dev "DHCPRELAY_IFB_NAME" up", false) ||
|
||||
dhcprelay_open_socket())
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dhcprelay_dev_done(void)
|
||||
{
|
||||
if (ufd.registered) {
|
||||
uloop_fd_delete(&ufd);
|
||||
close(ufd.fd);
|
||||
}
|
||||
|
||||
dhcprelay_run_cmd("ip link del "DHCPRELAY_IFB_NAME, true);
|
||||
vlist_flush_all(&devices);
|
||||
}
|
||||
239
feeds/ucentral/udhcprelay/src/dhcp.c
Normal file
239
feeds/ucentral/udhcprelay/src/dhcp.c
Normal file
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
|
||||
#include <netinet/if_ether.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "dhcprelay.h"
|
||||
#include "msg.h"
|
||||
|
||||
static const char *
|
||||
dhcprelay_parse_ipv4(const void *buf, size_t len, uint16_t port, uint32_t *expire,
|
||||
size_t *tail, bool *response)
|
||||
{
|
||||
const struct dhcpv4_message *msg = buf;
|
||||
const uint8_t *pos, *end;
|
||||
uint32_t leasetime = 0, rebind = 0, renew = 0;
|
||||
char type = 0;
|
||||
|
||||
if (port != 67 && port != 68)
|
||||
return NULL;
|
||||
|
||||
if (len < sizeof(*msg))
|
||||
return NULL;
|
||||
|
||||
if (ntohl(msg->magic) != DHCPV4_MAGIC)
|
||||
return NULL;
|
||||
|
||||
pos = msg->options;
|
||||
end = buf + len;
|
||||
*tail = (const void *)pos - buf;
|
||||
|
||||
while (pos < end) {
|
||||
const uint8_t *opt;
|
||||
|
||||
opt = pos++;
|
||||
if (*opt == DHCPV4_OPT_PAD)
|
||||
continue;
|
||||
|
||||
if (*opt == DHCPV4_OPT_END)
|
||||
break;
|
||||
|
||||
if (pos >= end || 1 + *pos > end - pos)
|
||||
break;
|
||||
|
||||
pos += *pos + 1;
|
||||
if (pos >= end)
|
||||
break;
|
||||
|
||||
*tail = (const void *)pos - buf;
|
||||
switch (*opt) {
|
||||
case DHCPV4_OPT_MSG_TYPE:
|
||||
if (!opt[1])
|
||||
continue;
|
||||
type = opt[2];
|
||||
break;
|
||||
case DHCPV4_OPT_LEASETIME:
|
||||
if (opt[1] != 4)
|
||||
continue;
|
||||
leasetime = *((uint32_t *) &opt[2]);
|
||||
break;
|
||||
case DHCPV4_OPT_REBIND:
|
||||
if (opt[1] != 4)
|
||||
continue;
|
||||
rebind = *((uint32_t *) &opt[2]);
|
||||
break;
|
||||
case DHCPV4_OPT_RENEW:
|
||||
if (opt[1] != 4)
|
||||
continue;
|
||||
renew = *((uint32_t *) &opt[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (renew)
|
||||
*expire = renew;
|
||||
else if (rebind)
|
||||
*expire = rebind;
|
||||
else if (leasetime)
|
||||
*expire = leasetime;
|
||||
else
|
||||
*expire = 24 * 60 * 60;
|
||||
*expire = ntohl(*expire);
|
||||
|
||||
switch(type) {
|
||||
case DHCPV4_MSG_DISCOVER:
|
||||
return "discover";
|
||||
case DHCPV4_MSG_REQUEST:
|
||||
return "request";
|
||||
case DHCPV4_MSG_DECLINE:
|
||||
return "decline";
|
||||
case DHCPV4_MSG_RELEASE:
|
||||
return "release";
|
||||
case DHCPV4_MSG_INFORM:
|
||||
return "inform";
|
||||
|
||||
case DHCPV4_MSG_OFFER:
|
||||
case DHCPV4_MSG_ACK:
|
||||
case DHCPV4_MSG_NAK:
|
||||
*response = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
proto_is_vlan(uint16_t proto)
|
||||
{
|
||||
return proto == ETH_P_8021Q || proto == ETH_P_8021AD;
|
||||
}
|
||||
|
||||
static int
|
||||
dhcprelay_add_option(struct packet *pkt, uint32_t opt, const void *data, size_t len)
|
||||
{
|
||||
uint8_t *tail = pkt->data + pkt->dhcp_tail;
|
||||
|
||||
if (opt > 255 || len > 255)
|
||||
return -1;
|
||||
|
||||
if ((void *)tail + len + 1 > pkt->end)
|
||||
return -1;
|
||||
|
||||
*(tail++) = opt;
|
||||
*(tail++) = len;
|
||||
if (data && len)
|
||||
memcpy(tail, data, len);
|
||||
pkt->dhcp_tail += len + 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcprelay_add_options(struct packet *pkt, struct blob_attr *data)
|
||||
{
|
||||
static const struct blobmsg_policy policy[2] = {
|
||||
{ .type = BLOBMSG_TYPE_INT32 },
|
||||
{ .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
struct blob_attr *tb[2], *cur;
|
||||
int rem;
|
||||
|
||||
if (!data)
|
||||
goto out;
|
||||
|
||||
if (blobmsg_check_array(data, BLOBMSG_TYPE_ARRAY) < 0)
|
||||
return -1;
|
||||
|
||||
blobmsg_for_each_attr(cur, data, rem) {
|
||||
blobmsg_parse_array(policy, 2, tb, blobmsg_data(cur), blobmsg_len(cur));
|
||||
|
||||
if (!tb[0] || !tb[1])
|
||||
return -1;
|
||||
|
||||
if (!blobmsg_len(tb[1]))
|
||||
return -1;
|
||||
|
||||
if (dhcprelay_add_option(pkt, blobmsg_get_u32(tb[0]),
|
||||
blobmsg_get_string(tb[1]), blobmsg_len(tb[1]) - 1))
|
||||
return -1;
|
||||
}
|
||||
|
||||
out:
|
||||
dhcprelay_add_option(pkt, DHCPV4_OPT_END, NULL, 0);
|
||||
|
||||
pkt->len = pkt->dhcp_tail;
|
||||
if (pkt->len < 300) {
|
||||
pkt->len = 300;
|
||||
memset(pkt->data + pkt->dhcp_tail, 0, pkt->len - pkt->dhcp_tail);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dhcprelay_packet_cb(struct packet *pkt)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct ip *ip;
|
||||
struct udphdr *udp;
|
||||
uint16_t proto, port;
|
||||
const char *type;
|
||||
uint32_t rebind = 0;
|
||||
size_t tail = 0;
|
||||
bool response = false;
|
||||
|
||||
eth = pkt_pull(pkt, sizeof(*eth));
|
||||
if (!eth)
|
||||
return;
|
||||
|
||||
memcpy(pkt->l2.addr, eth->h_source, ETH_ALEN);
|
||||
|
||||
proto = be16_to_cpu(eth->h_proto);
|
||||
if (proto_is_vlan(proto)) {
|
||||
struct vlan_hdr *vlan;
|
||||
|
||||
vlan = pkt_pull(pkt, sizeof(*vlan));
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
pkt->l2.vlan_proto = proto;
|
||||
pkt->l2.vlan_tci = ntohs(vlan->tci);
|
||||
proto = be16_to_cpu(vlan->proto);
|
||||
}
|
||||
|
||||
if (proto != ETH_P_IP)
|
||||
return;
|
||||
|
||||
ip = pkt_peek(pkt, sizeof(struct ip));
|
||||
if (!ip)
|
||||
return;
|
||||
|
||||
if (!pkt_pull(pkt, ip->ip_hl * 4))
|
||||
return;
|
||||
|
||||
if (ip->ip_p != IPPROTO_UDP)
|
||||
return;
|
||||
|
||||
udp = pkt_pull(pkt, sizeof(struct udphdr));
|
||||
if (!udp)
|
||||
return;
|
||||
|
||||
port = ntohs(udp->uh_sport);
|
||||
type = dhcprelay_parse_ipv4(pkt->data, pkt->len, port, &rebind, &tail, &response);
|
||||
|
||||
if (response) {
|
||||
dhcprelay_handle_response(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type)
|
||||
return;
|
||||
|
||||
pkt->dhcp_tail = tail;
|
||||
dhcprelay_ubus_notify(type, pkt);
|
||||
}
|
||||
107
feeds/ucentral/udhcprelay/src/dhcprelay.h
Normal file
107
feeds/ucentral/udhcprelay/src/dhcprelay.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#ifndef __DHCPRELAY_H
|
||||
#define __DHCPRELAY_H
|
||||
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/ulog.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/vlist.h>
|
||||
#include <libubox/utils.h>
|
||||
#include <net/if.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DHCPRELAY_IFB_NAME "ifb-dhcprelay"
|
||||
#define DHCPRELAY_PRIO_BASE 0x130
|
||||
|
||||
struct packet_l2 {
|
||||
int ifindex;
|
||||
uint16_t vlan_tci;
|
||||
uint16_t vlan_proto;
|
||||
uint8_t addr[6];
|
||||
};
|
||||
|
||||
struct packet {
|
||||
struct packet_l2 l2;
|
||||
|
||||
uint16_t len;
|
||||
uint16_t dhcp_tail;
|
||||
|
||||
void *head;
|
||||
void *data;
|
||||
void *end;
|
||||
};
|
||||
|
||||
struct device {
|
||||
struct vlist_node node;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
|
||||
int ifindex;
|
||||
bool upstream;
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct bridge_entry {
|
||||
struct avl_node node;
|
||||
struct blob_attr *vlans, *ignore, *upstream;
|
||||
};
|
||||
|
||||
static inline void *pkt_push(struct packet *pkt, unsigned int len)
|
||||
{
|
||||
if (pkt->data - pkt->head < len)
|
||||
return NULL;
|
||||
|
||||
pkt->data -= len;
|
||||
pkt->len += len;
|
||||
|
||||
return pkt->data;
|
||||
}
|
||||
|
||||
static inline void *pkt_peek(struct packet *pkt, unsigned int len)
|
||||
{
|
||||
if (len > pkt->len)
|
||||
return NULL;
|
||||
|
||||
return pkt->data;
|
||||
}
|
||||
|
||||
static inline void *pkt_pull(struct packet *pkt, unsigned int len)
|
||||
{
|
||||
void *ret = pkt_peek(pkt, len);
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
pkt->data += len;
|
||||
pkt->len -= len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline const char *bridge_entry_name(struct bridge_entry *br)
|
||||
{
|
||||
return br->node.key;
|
||||
}
|
||||
|
||||
int dhcprelay_run_cmd(char *cmd, bool ignore_error);
|
||||
|
||||
int dhcprelay_dev_init(void);
|
||||
void dhcprelay_dev_done(void);
|
||||
void dhcprelay_dev_add(const char *name, bool upstream);
|
||||
void dhcprelay_dev_config_update(struct blob_attr *br, struct blob_attr *dev);
|
||||
void dhcprelay_dev_send(struct packet *pkt, int ifindex, const uint8_t *addr, uint16_t proto);
|
||||
void dhcprelay_update_devices(void);
|
||||
|
||||
void dhcprelay_ubus_init(void);
|
||||
void dhcprelay_ubus_done(void);
|
||||
void dhcprelay_ubus_notify(const char *type, struct packet *pkt);
|
||||
void dhcprelay_ubus_query_bridge(struct bridge_entry *br);
|
||||
|
||||
void dhcprelay_packet_cb(struct packet *pkt);
|
||||
void dhcprelay_handle_response(struct packet *pkt);
|
||||
int dhcprelay_forward_request(struct packet *pkt, struct blob_attr *data);
|
||||
int dhcprelay_add_options(struct packet *pkt, struct blob_attr *data);
|
||||
|
||||
#endif
|
||||
83
feeds/ucentral/udhcprelay/src/main.c
Normal file
83
feeds/ucentral/udhcprelay/src/main.c
Normal file
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "dhcprelay.h"
|
||||
|
||||
int dhcprelay_run_cmd(char *cmd, bool ignore_error)
|
||||
{
|
||||
char *argv[] = { "sh", "-c", cmd, NULL };
|
||||
bool first = true;
|
||||
int status = -1;
|
||||
char buf[512];
|
||||
int fds[2];
|
||||
FILE *f;
|
||||
int pid;
|
||||
|
||||
if (pipe(fds))
|
||||
return -1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
close(fds[0]);
|
||||
if (fds[1] != STDOUT_FILENO)
|
||||
dup2(fds[1], STDOUT_FILENO);
|
||||
if (fds[1] != STDERR_FILENO)
|
||||
dup2(fds[1], STDERR_FILENO);
|
||||
if (fds[1] > STDERR_FILENO)
|
||||
close(fds[1]);
|
||||
execv("/bin/sh", argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
|
||||
close(fds[1]);
|
||||
f = fdopen(fds[0], "r");
|
||||
if (!f) {
|
||||
close(fds[0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f) != NULL) {
|
||||
if (!strlen(buf))
|
||||
break;
|
||||
if (ignore_error)
|
||||
continue;
|
||||
if (first) {
|
||||
ULOG_WARN("Command: %s\n", cmd);
|
||||
first = false;
|
||||
}
|
||||
ULOG_WARN("%s%s", buf, strchr(buf, '\n') ? "" : "\n");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
out:
|
||||
while (waitpid(pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "udhcprelay");
|
||||
uloop_init();
|
||||
dhcprelay_ubus_init();
|
||||
dhcprelay_dev_init();
|
||||
|
||||
ulog_threshold(LOG_INFO);
|
||||
uloop_run();
|
||||
|
||||
dhcprelay_ubus_done();
|
||||
dhcprelay_dev_done();
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
93
feeds/ucentral/udhcprelay/src/msg.h
Normal file
93
feeds/ucentral/udhcprelay/src/msg.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
#ifndef __DHCPRELAY_MSG_H
|
||||
#define __DHCPRELAY_MSG_H
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct vlan_hdr {
|
||||
uint16_t tci;
|
||||
uint16_t proto;
|
||||
};
|
||||
|
||||
enum dhcpv4_msg {
|
||||
DHCPV4_MSG_DISCOVER = 1,
|
||||
DHCPV4_MSG_OFFER = 2,
|
||||
DHCPV4_MSG_REQUEST = 3,
|
||||
DHCPV4_MSG_DECLINE = 4,
|
||||
DHCPV4_MSG_ACK = 5,
|
||||
DHCPV4_MSG_NAK = 6,
|
||||
DHCPV4_MSG_RELEASE = 7,
|
||||
DHCPV4_MSG_INFORM = 8,
|
||||
DHCPV4_MSG_FORCERENEW = 9,
|
||||
};
|
||||
|
||||
enum dhcpv4_opt {
|
||||
DHCPV4_OPT_PAD = 0,
|
||||
DHCPV4_OPT_NETMASK = 1,
|
||||
DHCPV4_OPT_ROUTER = 3,
|
||||
DHCPV4_OPT_DNSSERVER = 6,
|
||||
DHCPV4_OPT_DOMAIN = 15,
|
||||
DHCPV4_OPT_MTU = 26,
|
||||
DHCPV4_OPT_BROADCAST = 28,
|
||||
DHCPV4_OPT_NTPSERVER = 42,
|
||||
DHCPV4_OPT_LEASETIME = 51,
|
||||
DHCPV4_OPT_MESSAGE = 53,
|
||||
DHCPV4_OPT_SERVERID = 54,
|
||||
DHCPV4_OPT_REQOPTS = 55,
|
||||
DHCPV4_OPT_RENEW = 58,
|
||||
DHCPV4_OPT_REBIND = 59,
|
||||
DHCPV4_OPT_IPADDRESS = 50,
|
||||
DHCPV4_OPT_MSG_TYPE = 53,
|
||||
DHCPV4_OPT_HOSTNAME = 12,
|
||||
DHCPV4_OPT_REQUEST = 17,
|
||||
DHCPV4_OPT_USER_CLASS = 77,
|
||||
DHCPV4_OPT_AUTHENTICATION = 90,
|
||||
DHCPV4_OPT_SEARCH_DOMAIN = 119,
|
||||
DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE = 145,
|
||||
DHCPV4_OPT_END = 255,
|
||||
};
|
||||
|
||||
struct dhcpv4_message {
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
struct in_addr ciaddr;
|
||||
struct in_addr yiaddr;
|
||||
struct in_addr siaddr;
|
||||
struct in_addr giaddr;
|
||||
uint8_t chaddr[16];
|
||||
char sname[64];
|
||||
char file[128];
|
||||
uint32_t magic;
|
||||
uint8_t options[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define DHCPV4_MAGIC 0x63825363
|
||||
|
||||
enum dhcpv6_opt {
|
||||
DHCPV6_MSG_SOLICIT = 1,
|
||||
DHCPV6_MSG_ADVERTISE = 2,
|
||||
DHCPV6_MSG_REQUEST = 3,
|
||||
DHCPV6_MSG_CONFIRM = 4,
|
||||
DHCPV6_MSG_RENEW = 5,
|
||||
DHCPV6_MSG_REBIND = 6,
|
||||
DHCPV6_MSG_REPLY = 7,
|
||||
DHCPV6_MSG_RELEASE = 8,
|
||||
DHCPV6_MSG_DECLINE = 9,
|
||||
DHCPV6_MSG_RECONFIGURE = 10,
|
||||
DHCPV6_MSG_INFORMATION_REQUEST = 11,
|
||||
DHCPV6_MSG_RELAY_FORW = 12,
|
||||
DHCPV6_MSG_RELAY_REPL = 13,
|
||||
};
|
||||
struct dhcpv6_message {
|
||||
uint8_t msg_type;
|
||||
uint8_t transaction_id[3];
|
||||
uint8_t options[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif
|
||||
472
feeds/ucentral/udhcprelay/src/relay.c
Normal file
472
feeds/ucentral/udhcprelay/src/relay.c
Normal file
@@ -0,0 +1,472 @@
|
||||
#include <netinet/if_ether.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <endian.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/usock.h>
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
#include <libubox/utils.h>
|
||||
#include <libubus.h>
|
||||
#include "dhcprelay.h"
|
||||
#include "msg.h"
|
||||
|
||||
struct dhcprelay_req_key {
|
||||
uint32_t xid;
|
||||
uint8_t addr[6];
|
||||
};
|
||||
|
||||
struct dhcprelay_req {
|
||||
struct avl_node node;
|
||||
struct uloop_timeout timeout;
|
||||
struct dhcprelay_req_key key;
|
||||
|
||||
struct packet_l2 l2;
|
||||
};
|
||||
|
||||
struct dhcprelay_local {
|
||||
struct avl_node node;
|
||||
struct in_addr addr;
|
||||
struct uloop_fd fd;
|
||||
|
||||
struct list_head conn_list;
|
||||
};
|
||||
|
||||
struct dhcprelay_conn {
|
||||
struct avl_node node;
|
||||
|
||||
struct dhcprelay_local *local;
|
||||
struct list_head local_list;
|
||||
struct sockaddr_in local_addr;
|
||||
|
||||
struct uloop_timeout timeout;
|
||||
struct uloop_fd fd;
|
||||
};
|
||||
|
||||
static int relay_req_cmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
return memcmp(k1, k2, sizeof(struct dhcprelay_req_key));
|
||||
}
|
||||
|
||||
static int in_addr_cmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
return memcmp(k1, k2, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
static AVL_TREE(requests, relay_req_cmp, false, NULL);
|
||||
static AVL_TREE(connections, avl_strcmp, false, NULL);
|
||||
static AVL_TREE(local_addr, in_addr_cmp, false, NULL);
|
||||
|
||||
static void
|
||||
dhcprelay_req_timeout_cb(struct uloop_timeout *t)
|
||||
{
|
||||
struct dhcprelay_req *req = container_of(t, struct dhcprelay_req, timeout);
|
||||
|
||||
avl_delete(&requests, &req->node);
|
||||
free(req);
|
||||
}
|
||||
|
||||
static void
|
||||
__dhcprelay_conn_free(struct dhcprelay_conn *conn)
|
||||
{
|
||||
uloop_timeout_cancel(&conn->timeout);
|
||||
avl_delete(&connections, &conn->node);
|
||||
uloop_fd_delete(&conn->fd);
|
||||
close(conn->fd.fd);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_local_free(struct dhcprelay_local *local)
|
||||
{
|
||||
struct dhcprelay_conn *conn;
|
||||
|
||||
while (!list_empty(&local->conn_list)) {
|
||||
conn = list_first_entry(&local->conn_list, struct dhcprelay_conn, local_list);
|
||||
list_del_init(&conn->local_list);
|
||||
__dhcprelay_conn_free(conn);
|
||||
}
|
||||
|
||||
avl_delete(&local_addr, &local->node);
|
||||
uloop_fd_delete(&local->fd);
|
||||
close(local->fd.fd);
|
||||
free(local);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_conn_free(struct dhcprelay_conn *conn)
|
||||
{
|
||||
struct dhcprelay_local *local = conn->local;
|
||||
|
||||
if (local) {
|
||||
list_del_init(&conn->local_list);
|
||||
if (list_empty(&local->conn_list))
|
||||
dhcprelay_local_free(local);
|
||||
}
|
||||
|
||||
__dhcprelay_conn_free(conn);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_conn_timeout_cb(struct uloop_timeout *t)
|
||||
{
|
||||
struct dhcprelay_conn *conn = container_of(t, struct dhcprelay_conn, timeout);
|
||||
|
||||
dhcprelay_conn_free(conn);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_req_key_init(struct dhcprelay_req_key *key, struct dhcpv4_message *msg)
|
||||
{
|
||||
key->xid = msg->xid;
|
||||
memcpy(key->addr, msg->chaddr, sizeof(key->addr));
|
||||
}
|
||||
|
||||
static struct dhcprelay_req *
|
||||
dhcprelay_req_from_pkt(struct packet *pkt)
|
||||
{
|
||||
struct dhcpv4_message *msg = pkt->data;
|
||||
struct dhcprelay_req_key key = {};
|
||||
struct dhcprelay_req *req;
|
||||
|
||||
dhcprelay_req_key_init(&key, msg);
|
||||
req = avl_find_element(&requests, &key, req, node);
|
||||
if (req)
|
||||
goto out;
|
||||
|
||||
req = calloc(1, sizeof(*req));
|
||||
memcpy(&req->key, &key, sizeof(key));
|
||||
req->node.key = &req->key;
|
||||
req->timeout.cb = dhcprelay_req_timeout_cb;
|
||||
avl_insert(&requests, &req->node);
|
||||
|
||||
out:
|
||||
req->l2 = pkt->l2;
|
||||
uloop_timeout_set(&req->timeout, 10 * 1000);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_forward_response(struct packet *pkt, struct dhcprelay_req *req,
|
||||
struct dhcpv4_message *msg)
|
||||
{
|
||||
uint16_t proto = ETH_P_IP;
|
||||
|
||||
if (req->l2.vlan_proto) {
|
||||
struct vlan_hdr *vlan;
|
||||
|
||||
vlan = pkt_push(pkt, sizeof(*vlan));
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
vlan->tci = cpu_to_be16(req->l2.vlan_tci);
|
||||
vlan->proto = cpu_to_be16(proto);
|
||||
proto = req->l2.vlan_proto;
|
||||
}
|
||||
|
||||
dhcprelay_dev_send(pkt, req->l2.ifindex, msg->chaddr, proto);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint8_t proto, uint32_t len)
|
||||
{
|
||||
uint64_t sum = 0;
|
||||
|
||||
sum += saddr;
|
||||
sum += daddr;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
sum += (proto + len) << 8;
|
||||
#else
|
||||
sum += proto + len;
|
||||
#endif
|
||||
|
||||
sum = (sum & 0xffffffff) + (sum >> 32);
|
||||
sum = (sum & 0xffffffff) + (sum >> 32);
|
||||
|
||||
return (uint32_t)sum;
|
||||
}
|
||||
|
||||
static inline uint32_t csum_add(uint32_t sum, uint32_t addend)
|
||||
{
|
||||
sum += addend;
|
||||
return sum + (sum < addend);
|
||||
}
|
||||
|
||||
static inline uint16_t csum_fold(uint32_t sum)
|
||||
{
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return (uint16_t)~sum;
|
||||
}
|
||||
|
||||
static uint32_t csum_partial(const void *buf, int len)
|
||||
{
|
||||
const uint16_t *data = buf;
|
||||
uint32_t sum = 0;
|
||||
|
||||
while (len > 1) {
|
||||
sum += *data++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len == 1)
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
sum += *(uint8_t *)data;
|
||||
#else
|
||||
sum += *(uint8_t *)data << 8;
|
||||
#endif
|
||||
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static int
|
||||
dhcprelay_add_ip_udp(struct packet *pkt)
|
||||
{
|
||||
struct dhcpv4_message *msg = pkt->data;
|
||||
struct udphdr *udp;
|
||||
struct ip *ip;
|
||||
uint16_t msg_len = pkt->len;
|
||||
uint16_t udp_len = sizeof(*udp) + msg_len;
|
||||
uint16_t ip_len = sizeof(*ip) + udp_len;
|
||||
uint32_t sum;
|
||||
|
||||
udp = pkt_push(pkt, sizeof(*udp));
|
||||
ip = pkt_push(pkt, sizeof(*ip));
|
||||
if (!udp || !ip)
|
||||
return -1;
|
||||
|
||||
ip->ip_v = 4;
|
||||
ip->ip_hl = sizeof(*ip) / 4;
|
||||
ip->ip_len = cpu_to_be16(ip_len);
|
||||
ip->ip_p = IPPROTO_UDP;
|
||||
ip->ip_src = msg->siaddr;
|
||||
if (msg->flags & cpu_to_be16((1 << 15)))
|
||||
ip->ip_dst = (struct in_addr){ ~0 };
|
||||
else
|
||||
ip->ip_dst = msg->yiaddr;
|
||||
|
||||
udp->uh_sport = cpu_to_be16(67);
|
||||
udp->uh_dport = cpu_to_be16(68);
|
||||
udp->uh_ulen = cpu_to_be16(udp_len);
|
||||
|
||||
udp->uh_sum = 0;
|
||||
udp->uh_ulen = cpu_to_be16(udp_len);
|
||||
sum = csum_tcpudp_nofold(*(uint32_t *)&ip->ip_src, *(uint32_t *)&ip->ip_dst,
|
||||
ip->ip_p, udp_len);
|
||||
sum = csum_add(sum, csum_partial(udp, sizeof(*udp)));
|
||||
sum = csum_add(sum, csum_partial(msg, msg_len));
|
||||
udp->uh_sum = csum_fold(sum);
|
||||
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = csum_fold(csum_partial(ip, sizeof(*ip)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dhcprelay_handle_response(struct packet *pkt)
|
||||
{
|
||||
struct dhcpv4_message *msg = pkt->data;
|
||||
struct dhcprelay_req_key key = {};
|
||||
struct dhcprelay_req *req;
|
||||
|
||||
if (pkt->len < sizeof(*msg))
|
||||
return;
|
||||
|
||||
dhcprelay_req_key_init(&key, msg);
|
||||
req = avl_find_element(&requests, &key, req, node);
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
if (dhcprelay_add_ip_udp(pkt))
|
||||
return;
|
||||
|
||||
dhcprelay_forward_response(pkt, req, msg);
|
||||
}
|
||||
|
||||
static int
|
||||
dhcprelay_read_relay_fd(int fd)
|
||||
{
|
||||
static struct {
|
||||
uint8_t l2[sizeof(struct ethhdr) + 4];
|
||||
struct ip ip;
|
||||
struct udphdr udp;
|
||||
uint8_t data[1500];
|
||||
} __packed buf = {};
|
||||
struct packet pkt = {
|
||||
.head = &buf,
|
||||
.data = buf.data,
|
||||
.end = &buf.data[sizeof(buf.data)],
|
||||
};
|
||||
ssize_t len;
|
||||
|
||||
retry:
|
||||
len = recv(fd, pkt.data, sizeof(pkt.data), 0);
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt.len = len;
|
||||
dhcprelay_handle_response(&pkt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_local_cb(struct uloop_fd *fd, unsigned int events)
|
||||
{
|
||||
struct dhcprelay_local *local = container_of(fd, struct dhcprelay_local, fd);
|
||||
|
||||
if (dhcprelay_read_relay_fd(fd->fd) < 0)
|
||||
dhcprelay_local_free(local);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_conn_cb(struct uloop_fd *fd, unsigned int events)
|
||||
{
|
||||
struct dhcprelay_conn *conn = container_of(fd, struct dhcprelay_conn, fd);
|
||||
|
||||
uloop_timeout_set(&conn->timeout, 30 * 1000);
|
||||
|
||||
if (dhcprelay_read_relay_fd(fd->fd) < 0)
|
||||
dhcprelay_conn_free(conn);
|
||||
}
|
||||
|
||||
static struct dhcprelay_local *
|
||||
dhcprelay_local_get(struct in_addr *addr)
|
||||
{
|
||||
struct dhcprelay_local *local;
|
||||
struct sockaddr_in sin = {
|
||||
.sin_addr = *addr,
|
||||
.sin_port = cpu_to_be16(67),
|
||||
};
|
||||
const int yes = 1;
|
||||
int fd;
|
||||
|
||||
local = avl_find_element(&local_addr, addr, local, node);
|
||||
if (local)
|
||||
return local;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local = calloc(1, sizeof(*local));
|
||||
local->fd.fd = fd;
|
||||
local->fd.cb = dhcprelay_local_cb;
|
||||
uloop_fd_add(&local->fd, ULOOP_READ);
|
||||
INIT_LIST_HEAD(&local->conn_list);
|
||||
local->node.key = &local->addr;
|
||||
memcpy(&local->addr, addr, sizeof(local->addr));
|
||||
avl_insert(&local_addr, &local->node);
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
static struct dhcprelay_conn *
|
||||
dhcprelay_conn_get(const char *addr)
|
||||
{
|
||||
struct dhcprelay_conn *conn;
|
||||
const char *host = addr;
|
||||
const char *port = "67";
|
||||
const char *sep;
|
||||
char *name_buf;
|
||||
socklen_t sl = sizeof(local_addr);
|
||||
int fd;
|
||||
|
||||
conn = avl_find_element(&connections, addr, conn, node);
|
||||
if (conn)
|
||||
goto out;
|
||||
|
||||
sep = strchr(addr, ':');
|
||||
if (sep) {
|
||||
char *buf = alloca(strlen(addr) + 1);
|
||||
int ofs = sep - addr;
|
||||
|
||||
buf[ofs++] = 0;
|
||||
host = buf;
|
||||
port = buf + ofs;
|
||||
}
|
||||
|
||||
fd = usock(USOCK_UDP | USOCK_IPV4ONLY | USOCK_NONBLOCK, host, port);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
conn = calloc_a(sizeof(*conn), &name_buf, strlen(addr) + 1);
|
||||
conn->fd.fd = fd;
|
||||
conn->fd.cb = dhcprelay_conn_cb;
|
||||
conn->timeout.cb = dhcprelay_conn_timeout_cb;
|
||||
uloop_fd_add(&conn->fd, ULOOP_READ);
|
||||
INIT_LIST_HEAD(&conn->local_list);
|
||||
conn->node.key = strcpy(name_buf, addr);
|
||||
getsockname(fd, (struct sockaddr *)&conn->local_addr, &sl);
|
||||
conn->local = dhcprelay_local_get(&conn->local_addr.sin_addr);
|
||||
if (conn->local)
|
||||
list_add_tail(&conn->local_list, &conn->local->conn_list);
|
||||
|
||||
avl_insert(&connections, &conn->node);
|
||||
|
||||
out:
|
||||
uloop_timeout_set(&conn->timeout, 30 * 1000);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
int dhcprelay_forward_request(struct packet *pkt, struct blob_attr *data)
|
||||
{
|
||||
enum {
|
||||
FWD_ATTR_ADDRESS,
|
||||
FWD_ATTR_OPTIONS,
|
||||
__FWD_ATTR_MAX,
|
||||
};
|
||||
static const struct blobmsg_policy policy[] = {
|
||||
[FWD_ATTR_ADDRESS] = { "address", BLOBMSG_TYPE_STRING },
|
||||
[FWD_ATTR_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
struct dhcpv4_message *msg = pkt->data;
|
||||
struct blob_attr *tb[__FWD_ATTR_MAX];
|
||||
struct dhcprelay_conn *conn;
|
||||
struct packet cur_pkt = *pkt;
|
||||
int ret;
|
||||
|
||||
blobmsg_parse(policy, __FWD_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
|
||||
|
||||
if (!tb[FWD_ATTR_ADDRESS])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
pkt = &cur_pkt;
|
||||
if (dhcprelay_add_options(pkt, tb[FWD_ATTR_OPTIONS]))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
conn = dhcprelay_conn_get(blobmsg_get_string(tb[FWD_ATTR_ADDRESS]));
|
||||
if (!conn)
|
||||
return UBUS_STATUS_CONNECTION_FAILED;
|
||||
|
||||
msg->giaddr = conn->local_addr.sin_addr;
|
||||
if (msg->hops++ >= 20)
|
||||
return 0;
|
||||
|
||||
dhcprelay_req_from_pkt(pkt);
|
||||
do {
|
||||
ret = send(conn->fd.fd, pkt->data, pkt->len, 0);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
288
feeds/ucentral/udhcprelay/src/ubus.c
Normal file
288
feeds/ucentral/udhcprelay/src/ubus.c
Normal file
@@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <libubus.h>
|
||||
|
||||
#include "dhcprelay.h"
|
||||
|
||||
enum {
|
||||
DS_CONFIG_BRIDGES,
|
||||
DS_CONFIG_DEVICES,
|
||||
__DS_CONFIG_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy dhcprelay_config_policy[__DS_CONFIG_MAX] = {
|
||||
[DS_CONFIG_BRIDGES] = { "bridges", BLOBMSG_TYPE_TABLE },
|
||||
[DS_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
|
||||
static struct blob_buf b;
|
||||
|
||||
static int
|
||||
dhcprelay_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__DS_CONFIG_MAX];
|
||||
|
||||
blobmsg_parse(dhcprelay_config_policy, __DS_CONFIG_MAX, tb,
|
||||
blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
dhcprelay_dev_config_update(tb[DS_CONFIG_BRIDGES], tb[DS_CONFIG_DEVICES]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
dhcprelay_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
dhcprelay_update_devices();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ubus_method dhcprelay_methods[] = {
|
||||
UBUS_METHOD("config", dhcprelay_ubus_config, dhcprelay_config_policy),
|
||||
UBUS_METHOD_NOARG("check_devices", dhcprelay_ubus_check_devices),
|
||||
};
|
||||
|
||||
static struct ubus_object_type dhcprelay_object_type =
|
||||
UBUS_OBJECT_TYPE("dhcprelay", dhcprelay_methods);
|
||||
|
||||
static struct ubus_object dhcprelay_object = {
|
||||
.name = "dhcprelay",
|
||||
.type = &dhcprelay_object_type,
|
||||
.methods = dhcprelay_methods,
|
||||
.n_methods = ARRAY_SIZE(dhcprelay_methods),
|
||||
};
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
ubus_add_object(ctx, &dhcprelay_object);
|
||||
}
|
||||
|
||||
static struct ubus_auto_conn conn;
|
||||
|
||||
void dhcprelay_ubus_init(void)
|
||||
{
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
}
|
||||
|
||||
void dhcprelay_ubus_done(void)
|
||||
{
|
||||
ubus_auto_shutdown(&conn);
|
||||
blob_buf_free(&b);
|
||||
}
|
||||
|
||||
static bool
|
||||
dhcprelay_vlan_match(struct blob_attr *vlans, int id)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
blobmsg_for_each_attr(cur, vlans, rem) {
|
||||
unsigned int vlan;
|
||||
|
||||
switch (blobmsg_type(cur)) {
|
||||
case BLOBMSG_TYPE_STRING:
|
||||
vlan = atoi(blobmsg_get_string(cur));
|
||||
break;
|
||||
case BLOBMSG_TYPE_INT32:
|
||||
vlan = blobmsg_get_u32(cur);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (vlan == id)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
dhcprelay_ignore_match(struct blob_attr *ignore, const char *name)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
if (!ignore)
|
||||
return false;
|
||||
|
||||
blobmsg_for_each_attr(cur, ignore, rem) {
|
||||
if (!strcmp(blobmsg_get_string(cur), name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_parse_port_list(struct bridge_entry *br, struct blob_attr *attr)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
|
||||
return;
|
||||
|
||||
blobmsg_for_each_attr(cur, attr, rem) {
|
||||
char *sep, *name = blobmsg_get_string(cur);
|
||||
bool tagged = false;
|
||||
|
||||
sep = strchr(name, '\n');
|
||||
if (sep)
|
||||
*sep = 0;
|
||||
|
||||
sep = strchr(name, ':');
|
||||
if (sep) {
|
||||
*sep = 0;
|
||||
tagged = strchr(sep + 1, 't');
|
||||
}
|
||||
|
||||
if (dhcprelay_ignore_match(br->ignore, name))
|
||||
continue;
|
||||
|
||||
if (tagged)
|
||||
continue;
|
||||
|
||||
dhcprelay_dev_add(blobmsg_get_string(cur), false);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_parse_vlan(struct bridge_entry *br, struct blob_attr *attr)
|
||||
{
|
||||
enum {
|
||||
VL_ATTR_ID,
|
||||
VL_ATTR_PORTS,
|
||||
__VL_ATTR_MAX,
|
||||
};
|
||||
struct blobmsg_policy policy[__VL_ATTR_MAX] = {
|
||||
[VL_ATTR_ID] = { "id", BLOBMSG_TYPE_INT32 },
|
||||
[VL_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
struct blob_attr *tb[__VL_ATTR_MAX];
|
||||
int id;
|
||||
|
||||
blobmsg_parse(policy, __VL_ATTR_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
|
||||
|
||||
if (!tb[VL_ATTR_ID] || !tb[VL_ATTR_PORTS])
|
||||
return;
|
||||
|
||||
id = blobmsg_get_u32(tb[VL_ATTR_ID]);
|
||||
if (!dhcprelay_vlan_match(br->vlans, id))
|
||||
return;
|
||||
|
||||
dhcprelay_parse_port_list(br, tb[VL_ATTR_PORTS]);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_parse_bridge_ports(struct bridge_entry *br, struct blob_attr *msg)
|
||||
{
|
||||
struct blobmsg_policy policy = { "bridge-members", BLOBMSG_TYPE_ARRAY };
|
||||
struct blob_attr *attr;
|
||||
|
||||
blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
dhcprelay_parse_port_list(br, attr);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcprelay_query_bridge_cb(struct ubus_request *req, int type,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blobmsg_policy policy = { "bridge-vlans", BLOBMSG_TYPE_ARRAY };
|
||||
struct bridge_entry *br = req->priv;
|
||||
struct blob_attr *attr, *cur;
|
||||
int rem;
|
||||
|
||||
if (!br->vlans) {
|
||||
dhcprelay_parse_bridge_ports(br, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
blobmsg_for_each_attr(cur, attr, rem)
|
||||
dhcprelay_parse_vlan(br, cur);
|
||||
}
|
||||
|
||||
void dhcprelay_ubus_query_bridge(struct bridge_entry *br)
|
||||
{
|
||||
struct ubus_request req;
|
||||
uint32_t id;
|
||||
|
||||
if (ubus_lookup_id(&conn.ctx, "network.device", &id))
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_string(&b, "name", bridge_entry_name(br));
|
||||
if (ubus_invoke_async(&conn.ctx, id, "status", b.head, &req))
|
||||
return;
|
||||
|
||||
req.priv = br;
|
||||
req.data_cb = dhcprelay_query_bridge_cb;
|
||||
ubus_complete_request(&conn.ctx, &req, 1000);
|
||||
}
|
||||
|
||||
static void dhcprelay_notify_cb(struct ubus_notify_request *req,
|
||||
int type, struct blob_attr *msg)
|
||||
{
|
||||
struct packet *pkt = req->req.priv;
|
||||
|
||||
dhcprelay_forward_request(pkt, msg);
|
||||
}
|
||||
|
||||
void dhcprelay_ubus_notify(const char *type, struct packet *pkt)
|
||||
{
|
||||
const uint8_t *msg = pkt->data;
|
||||
struct ubus_notify_request req;
|
||||
size_t len = pkt->len;
|
||||
char *buf;
|
||||
void *c;
|
||||
|
||||
if (!dhcprelay_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
|
||||
c = blobmsg_open_table(&b, "info");
|
||||
buf = blobmsg_alloc_string_buffer(&b, "device", IFNAMSIZ + 1);
|
||||
if (!if_indextoname(pkt->l2.ifindex, buf))
|
||||
return;
|
||||
blobmsg_add_string_buffer(&b);
|
||||
if (pkt->l2.vlan_proto) {
|
||||
blobmsg_add_u32(&b, "vlan_proto", pkt->l2.vlan_proto);
|
||||
blobmsg_add_u32(&b, "vlan_tci", pkt->l2.vlan_tci);
|
||||
}
|
||||
blobmsg_close_table(&b, c);
|
||||
|
||||
buf = blobmsg_alloc_string_buffer(&b, "packet", 2 * len + 1);
|
||||
while (len > 0) {
|
||||
buf += sprintf(buf, "%02x", *msg);
|
||||
msg++;
|
||||
len--;
|
||||
}
|
||||
blobmsg_add_string_buffer(&b);
|
||||
|
||||
ubus_notify_async(&conn.ctx, &dhcprelay_object, type, b.head, &req);
|
||||
req.data_cb = dhcprelay_notify_cb;
|
||||
req.req.priv = pkt;
|
||||
ubus_complete_request(&conn.ctx, &req.req, 5000);
|
||||
}
|
||||
147
feeds/ucentral/udhcprelay/src/utils.c
Normal file
147
feeds/ucentral/udhcprelay/src/utils.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <libubox/utils.h>
|
||||
#include "dhcprelay.h"
|
||||
|
||||
static inline uint32_t
|
||||
csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto)
|
||||
{
|
||||
uint64_t sum = 0;
|
||||
|
||||
sum += saddr;
|
||||
sum += daddr;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
sum += (proto + len) << 8;
|
||||
#else
|
||||
sum += proto + len;
|
||||
#endif
|
||||
|
||||
sum = (sum & 0xffffffff) + (sum >> 32);
|
||||
sum = (sum & 0xffffffff) + (sum >> 32);
|
||||
|
||||
return (uint32_t)sum;
|
||||
}
|
||||
|
||||
static inline uint32_t csum_add(uint32_t sum, uint32_t addend)
|
||||
{
|
||||
sum += addend;
|
||||
return sum + (sum < addend);
|
||||
}
|
||||
|
||||
static inline uint16_t csum_fold(uint32_t sum)
|
||||
{
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return (uint16_t)~sum;
|
||||
}
|
||||
|
||||
static uint32_t csum_partial(const void *buf, int len)
|
||||
{
|
||||
const uint16_t *data = buf;
|
||||
uint32_t sum = 0;
|
||||
|
||||
while (len > 1) {
|
||||
sum += *data++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len == 1)
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
sum += *(uint8_t *)data;
|
||||
#else
|
||||
sum += *(uint8_t *)data << 8;
|
||||
#endif
|
||||
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void fixup_udpv4(void *hdr, size_t hdrlen, const void *data, size_t len)
|
||||
{
|
||||
struct ip *ip = hdr;
|
||||
struct udphdr *udp = hdr + ip->ip_hl * 4;
|
||||
uint16_t udp_len = sizeof(*udp) + len;
|
||||
uint32_t sum;
|
||||
|
||||
if ((void *)&udp[1] > hdr + hdrlen)
|
||||
return;
|
||||
|
||||
udp->uh_sum = 0;
|
||||
udp->uh_ulen = htons(udp_len);
|
||||
sum = csum_tcpudp_nofold(*(uint32_t *)&ip->ip_src, *(uint32_t *)&ip->ip_dst,
|
||||
ip->ip_p, udp_len);
|
||||
sum = csum_add(sum, csum_partial(udp, sizeof(*udp)));
|
||||
sum = csum_add(sum, csum_partial(data, len));
|
||||
udp->uh_sum = csum_fold(sum);
|
||||
|
||||
ip->ip_len = htons(hdrlen + len);
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = csum_fold(csum_partial(ip, sizeof(*ip)));
|
||||
|
||||
#ifdef __APPLE__
|
||||
ip->ip_len = hdrlen + len;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fixup_udpv6(void *hdr, size_t hdrlen, const void *data, size_t len)
|
||||
{
|
||||
struct ip6_hdr *ip = hdr;
|
||||
struct udphdr *udp = hdr + sizeof(*ip);
|
||||
uint16_t udp_len = htons(sizeof(*udp) + len);
|
||||
|
||||
if ((void *)&udp[1] > hdr + hdrlen)
|
||||
return;
|
||||
|
||||
ip->ip6_plen = htons(sizeof(*udp) + len);
|
||||
udp->uh_sum = 0;
|
||||
udp->uh_ulen = udp_len;
|
||||
udp->uh_sum = csum_fold(csum_partial(hdr, sizeof(*ip) + sizeof(*udp)));
|
||||
|
||||
#ifdef __APPLE__
|
||||
ip->ip6_plen = sizeof(*udp) + len;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fixup_ip_udp_header(void *hdr, size_t hdrlen, const void *data, size_t len)
|
||||
{
|
||||
if (hdrlen >= sizeof(struct ip6_hdr) + sizeof(struct udphdr))
|
||||
fixup_udpv6(hdr, hdrlen, data, len);
|
||||
else if (hdrlen >= sizeof(struct ip) + sizeof(struct udphdr))
|
||||
fixup_udpv4(hdr, hdrlen, data, len);
|
||||
}
|
||||
|
||||
int sendto_rawudp(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
const struct sockaddr *sa = addr;
|
||||
struct iovec iov[2] = {
|
||||
{ .iov_base = ip_hdr, .iov_len = ip_hdrlen },
|
||||
{ .iov_base = (void *)data, .iov_len = len }
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_name = (void *)addr,
|
||||
.msg_iov = iov,
|
||||
.msg_iovlen = ARRAY_SIZE(iov),
|
||||
};
|
||||
|
||||
if (sa->sa_family == AF_INET6)
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||
|
||||
fixup_ip_udp_header(ip_hdr, ip_hdrlen, data, len);
|
||||
|
||||
return sendmsg(fd, &msg, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user