mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-31 10:28:06 +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); | ||||||
|  | } | ||||||
| @@ -12,12 +12,13 @@ diff --git a/package/network/services/dnsmasq/files/dnsmasq.init b/package/netwo | |||||||
| index 205bfb4cf6..415a6b013d 100644 | index 205bfb4cf6..415a6b013d 100644 | ||||||
| --- a/package/network/services/dnsmasq/files/dnsmasq.init | --- a/package/network/services/dnsmasq/files/dnsmasq.init | ||||||
| +++ b/package/network/services/dnsmasq/files/dnsmasq.init | +++ b/package/network/services/dnsmasq/files/dnsmasq.init | ||||||
| @@ -1106,6 +1106,8 @@ dnsmasq_start() | @@ -1106,6 +1106,9 @@ dnsmasq_start() | ||||||
|  		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg" |  		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg" | ||||||
|  	fi |  	fi | ||||||
|   |   | ||||||
| +	xappend "except-interface=ifb-dhcp" | +	xappend "except-interface=ifb-dhcp" | ||||||
| +	xappend "except-interface=spotfilter-ifb" | +	xappend "except-interface=spotfilter-ifb" | ||||||
|  | +	xappend "except-interface=ifb-dhcprelay" | ||||||
|   |   | ||||||
|  	echo >> $CONFIGFILE_TMP |  	echo >> $CONFIGFILE_TMP | ||||||
|  	config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg" |  	config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg" | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ packages: | |||||||
|   - ucentral-event |   - ucentral-event | ||||||
|   - ucentral-schema |   - ucentral-schema | ||||||
|   - ucentral-tools |   - ucentral-tools | ||||||
|  |   - udhcprelay | ||||||
|   - ugps |   - ugps | ||||||
|   - usteer2 |   - usteer2 | ||||||
|   - ucrun |   - ucrun | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 John Crispin
					John Crispin