mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-31 18:38:10 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			led_respec
			...
			v4.1.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5bd8fe1249 | ||
|   | 593cdba4d7 | ||
|   | f811aa6b73 | ||
|   | 7213dc6dcb | ||
|   | 7dd139cf3b | ||
|   | 544b687f46 | ||
|   | c27b015a63 | ||
|   | 02c2e6945b | ||
|   | e7cd5038ac | ||
|   | 34e4a01e25 | ||
|   | 462ff4f813 | ||
|   | 71b738f8ee | ||
|   | 4ad04c7948 | 
| @@ -200,7 +200,7 @@ | |||||||
| 			phy-mode = "sgmii"; | 			phy-mode = "sgmii"; | ||||||
| 			full-duplex; | 			full-duplex; | ||||||
| 			pause; | 			pause; | ||||||
| 			airoha,surge = <1>; | 			airoha,surge = <0>; | ||||||
| 			airoha,polarity = <2>; | 			airoha,polarity = <2>; | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -49,10 +49,12 @@ function detect_certificate_type() { | |||||||
| 	let issuer = pipe.read("all"); | 	let issuer = pipe.read("all"); | ||||||
| 	pipe.close(); | 	pipe.close(); | ||||||
|  |  | ||||||
| 	if (!match(issuer, /Telecom Infra Project Issuing CA/)) { | 	if (match(issuer, /OpenLAN Demo Birth CA/)) { | ||||||
| 		ulog(LOG_INFO, 'Certificate type is "Demo" \n'); | 		ulog(LOG_INFO, 'Certificate type is "Demo" \n'); | ||||||
| 		cds_server = 'discovery-qa.open-lan.org'; | 		cds_server = 'discovery-qa.open-lan.org'; | ||||||
| 		timeouts.expiry_threshold = 3 * 24 * 60 * 60; | 		timeouts.expiry_threshold = 3 * 24 * 60 * 60; | ||||||
|  | 	} else if (match(issuer, /OpenLAN Birth Issuing CA/)) { | ||||||
|  | 		ulog(LOG_INFO, 'Certificate type is "Production"\n'); | ||||||
| 	} else { | 	} else { | ||||||
| 		ulog(LOG_INFO, 'Certificate type is "TIP"\n'); | 		ulog(LOG_INFO, 'Certificate type is "TIP"\n'); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -16,9 +16,11 @@ function set_est_server() { | |||||||
| 	let issuer = pipe.read("all"); | 	let issuer = pipe.read("all"); | ||||||
| 	pipe.close(); | 	pipe.close(); | ||||||
|  |  | ||||||
| 	if (!match(issuer, /Telecom Infra Project Issuing CA/)) { | 	if (match(issuer, /OpenLAN Demo Birth CA/)) { | ||||||
| 		ulog(LOG_INFO, 'Certificate type is "Demo" \n'); | 		ulog(LOG_INFO, 'Certificate type is "Demo" \n'); | ||||||
| 		est_server = 'qaest.certificates.open-lan.org:8001'; | 		est_server = 'qaest.certificates.open-lan.org:8001'; | ||||||
|  | 	} else if (match(issuer, /OpenLAN Birth Issuing CA/)) { | ||||||
|  | 		ulog(LOG_INFO, 'Certificate type is "Production"\n'); | ||||||
| 	} else { | 	} else { | ||||||
| 		ulog(LOG_INFO, 'Certificate type is "TIP"\n'); | 		ulog(LOG_INFO, 'Certificate type is "TIP"\n'); | ||||||
| 	} | 	} | ||||||
| @@ -165,7 +167,7 @@ function fwtool() { | |||||||
| 	let issuer = pipe.read("all"); | 	let issuer = pipe.read("all"); | ||||||
| 	pipe.close(); | 	pipe.close(); | ||||||
|  |  | ||||||
| 	if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth CA/))) | 	if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth/))) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	ulog(LOG_INFO, 'The issuer is insta\n'); | 	ulog(LOG_INFO, 'The issuer is insta\n'); | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ let config; | |||||||
| let offline_timer; | let offline_timer; | ||||||
| let current_state; | let current_state; | ||||||
| let online = false; | let online = false; | ||||||
|  | let leds_off = false; | ||||||
|  |  | ||||||
| function self_healing() { | function self_healing() { | ||||||
| 	let heal_wifi = false; | 	let heal_wifi = false; | ||||||
| @@ -148,6 +149,13 @@ function online_handler() { | |||||||
|  |  | ||||||
| function config_load() { | function config_load() { | ||||||
| 	ulog(LOG_INFO, 'loading config\n'); | 	ulog(LOG_INFO, 'loading config\n'); | ||||||
|  |  | ||||||
|  | 	uci.load('system'); | ||||||
|  | 	let led_off_cfg = uci.get("system", "@system[0]", "leds_off"); | ||||||
|  | 	if (led_off_cfg == 1) { | ||||||
|  | 		leds_off = true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	uci.load('state'); | 	uci.load('state'); | ||||||
| 	config = uci.get_all('state'); | 	config = uci.get_all('state'); | ||||||
|  |  | ||||||
| @@ -191,7 +199,7 @@ function led_find(alias) { | |||||||
| function factory_reset_timeout() { | function factory_reset_timeout() { | ||||||
| 	let led = led_find('led-running'); | 	let led = led_find('led-running'); | ||||||
| 	if (led) | 	if (led) | ||||||
| 		led_write(led, 'trigger', 'default-on'); | 		led_write(led, 'trigger', leds-off ? 'none' : 'default-on'); | ||||||
| } | } | ||||||
|  |  | ||||||
| let blink_timer; | let blink_timer; | ||||||
| @@ -210,7 +218,7 @@ let state_handler = { | |||||||
| 	offline: function() { | 	offline: function() { | ||||||
| 		online = false; | 		online = false; | ||||||
| 		let led = led_find('led-running'); | 		let led = led_find('led-running'); | ||||||
| 		if (led) | 		if (!leds_off && led) | ||||||
| 			led_write(led, 'trigger', 'heartbeat'); | 			led_write(led, 'trigger', 'heartbeat'); | ||||||
| 		if (config.ui.offline_trigger) { | 		if (config.ui.offline_trigger) { | ||||||
| 			if (offline_timer) | 			if (offline_timer) | ||||||
| @@ -223,7 +231,7 @@ let state_handler = { | |||||||
| 	online: function() { | 	online: function() { | ||||||
| 		online = true; | 		online = true; | ||||||
| 		let led = led_find('led-running'); | 		let led = led_find('led-running'); | ||||||
| 		if (led) | 		if (!leds_off && led) | ||||||
| 			led_write(led, 'trigger', 'default-on'); | 			led_write(led, 'trigger', 'default-on'); | ||||||
| 		online_handler(); | 		online_handler(); | ||||||
| 		return 0; | 		return 0; | ||||||
|   | |||||||
| @@ -3,12 +3,6 @@ include $(TOPDIR)/rules.mk | |||||||
| PKG_NAME:=ucentral-tools | PKG_NAME:=ucentral-tools | ||||||
| PKG_RELEASE:=1 | PKG_RELEASE:=1 | ||||||
|  |  | ||||||
| PKG_SOURCE_URL=https://github.com/blogic/ucentral-tools.git |  | ||||||
| PKG_MIRROR_HASH:=9ae6a0cd431595871c233550427c4043c2ba7ddb3c5d87e46ab74a03b2b5a947 |  | ||||||
| PKG_SOURCE_PROTO:=git |  | ||||||
| PKG_SOURCE_DATE:=2021-01-28 |  | ||||||
| PKG_SOURCE_VERSION:=b013fc636e48d407870a46aaa68a09ed74de8d6f |  | ||||||
|  |  | ||||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||||
| PKG_LICENSE:=BSD-3-Clause | PKG_LICENSE:=BSD-3-Clause | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								feeds/ucentral/ucentral-tools/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								feeds/ucentral/ucentral-tools/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | cmake_minimum_required(VERSION 2.6) | ||||||
|  |  | ||||||
|  | PROJECT(openwifi-tools C) | ||||||
|  | INCLUDE(GNUInstallDirs) | ||||||
|  | ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) | ||||||
|  |  | ||||||
|  | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(firstcontact firstcontact.c) | ||||||
|  | TARGET_LINK_LIBRARIES(firstcontact curl crypto ssl ubox) | ||||||
|  | INSTALL(TARGETS firstcontact | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(dhcpdiscover dhcpdiscover.c) | ||||||
|  | INSTALL(TARGETS dhcpdiscover | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(dnsprobe dnsprobe.c) | ||||||
|  | TARGET_LINK_LIBRARIES(dnsprobe ubox resolv) | ||||||
|  | INSTALL(TARGETS dnsprobe | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(radiusprobe radiusprobe.c) | ||||||
|  | TARGET_LINK_LIBRARIES(radiusprobe radcli) | ||||||
|  | INSTALL(TARGETS radiusprobe | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(ip-collide ip-collide.c) | ||||||
|  | TARGET_LINK_LIBRARIES(ip-collide ubox) | ||||||
|  | INSTALL(TARGETS ip-collide | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
							
								
								
									
										1345
									
								
								feeds/ucentral/ucentral-tools/src/dhcpdiscover.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1345
									
								
								feeds/ucentral/ucentral-tools/src/dhcpdiscover.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										690
									
								
								feeds/ucentral/ucentral-tools/src/dnsprobe.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										690
									
								
								feeds/ucentral/ucentral-tools/src/dnsprobe.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,690 @@ | |||||||
|  | /* | ||||||
|  |  * nslookup_lede - musl compatible replacement for busybox nslookup | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2017 Jo-Philipp Wich <jo@mein.io> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  |  * purpose with or without fee is hereby granted, provided that the above | ||||||
|  |  * copyright notice and this permission notice appear in all copies. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | //config:config NSLOOKUP_OPENWRT | ||||||
|  | //config:	bool "nslookup_openwrt" | ||||||
|  | //config:	depends on !NSLOOKUP | ||||||
|  | //config:	default y | ||||||
|  | //config:	help | ||||||
|  | //config:	  nslookup is a tool to query Internet name servers (LEDE flavor). | ||||||
|  | //config: | ||||||
|  | //config:config FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS | ||||||
|  | //config:       bool "Enable long options" | ||||||
|  | //config:       default y | ||||||
|  | //config:       depends on NSLOOKUP_OPENWRT && LONG_OPTS | ||||||
|  | //config:       help | ||||||
|  | //config:         Support long options for the nslookup applet. | ||||||
|  |  | ||||||
|  | //applet:IF_NSLOOKUP_OPENWRT(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||||||
|  |  | ||||||
|  | //kbuild:lib-$(CONFIG_NSLOOKUP_OPENWRT) += nslookup_lede.o | ||||||
|  |  | ||||||
|  | //usage:#define nslookup_lede_trivial_usage | ||||||
|  | //usage:       "[HOST] [SERVER]" | ||||||
|  | //usage:#define nslookup_lede_full_usage "\n\n" | ||||||
|  | //usage:       "Query the nameserver for the IP address of the given HOST\n" | ||||||
|  | //usage:       "optionally using a specified DNS server" | ||||||
|  | //usage: | ||||||
|  | //usage:#define nslookup_lede_example_usage | ||||||
|  | //usage:       "$ nslookup localhost\n" | ||||||
|  | //usage:       "Server:     default\n" | ||||||
|  | //usage:       "Address:    default\n" | ||||||
|  | //usage:       "\n" | ||||||
|  | //usage:       "Name:       debian\n" | ||||||
|  | //usage:       "Address:    127.0.0.1\n" | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <resolv.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <poll.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <net/if.h> | ||||||
|  | #include <netdb.h> | ||||||
|  |  | ||||||
|  | #include <libubox/ulog.h> | ||||||
|  |  | ||||||
|  | #define ENABLE_FEATURE_IPV6	1 | ||||||
|  |  | ||||||
|  | typedef struct len_and_sockaddr { | ||||||
|  | 	socklen_t len; | ||||||
|  | 	union { | ||||||
|  | 		struct sockaddr sa; | ||||||
|  | 		struct sockaddr_in sin; | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 		struct sockaddr_in6 sin6; | ||||||
|  | #endif | ||||||
|  | 	} u; | ||||||
|  | } len_and_sockaddr; | ||||||
|  |  | ||||||
|  | struct ns { | ||||||
|  | 	const char *name; | ||||||
|  | 	len_and_sockaddr addr; | ||||||
|  | 	int failures; | ||||||
|  | 	int replies; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct query { | ||||||
|  | 	const char *name; | ||||||
|  | 	size_t qlen, rlen; | ||||||
|  | 	unsigned char query[512], reply[512]; | ||||||
|  | 	unsigned long latency; | ||||||
|  | 	int rcode, n_ns; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const char *rcodes[] = { | ||||||
|  | 	"NOERROR", | ||||||
|  | 	"FORMERR", | ||||||
|  | 	"SERVFAIL", | ||||||
|  | 	"NXDOMAIN", | ||||||
|  | 	"NOTIMP", | ||||||
|  | 	"REFUSED", | ||||||
|  | 	"YXDOMAIN", | ||||||
|  | 	"YXRRSET", | ||||||
|  | 	"NXRRSET", | ||||||
|  | 	"NOTAUTH", | ||||||
|  | 	"NOTZONE", | ||||||
|  | 	"RESERVED11", | ||||||
|  | 	"RESERVED12", | ||||||
|  | 	"RESERVED13", | ||||||
|  | 	"RESERVED14", | ||||||
|  | 	"RESERVED15", | ||||||
|  | 	"BADVERS" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static unsigned int default_port = 53; | ||||||
|  | static unsigned int default_retry = 1; | ||||||
|  | static unsigned int default_timeout = 2; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter) | ||||||
|  | { | ||||||
|  | 	ns_msg handle; | ||||||
|  | 	ns_rr rr; | ||||||
|  | 	int i, n, rdlen; | ||||||
|  | 	const char *format = NULL; | ||||||
|  | 	char astr[INET6_ADDRSTRLEN], dname[MAXDNAME]; | ||||||
|  | 	const unsigned char *cp; | ||||||
|  |  | ||||||
|  | 	if (ns_initparse(msg, len, &handle) != 0) { | ||||||
|  | 		//fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) { | ||||||
|  | 		if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) { | ||||||
|  | 			//fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno)); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		rdlen = ns_rr_rdlen(rr); | ||||||
|  |  | ||||||
|  | 		switch (ns_rr_type(rr)) | ||||||
|  | 		{ | ||||||
|  | 		case ns_t_a: | ||||||
|  | 			if (rdlen != 4) { | ||||||
|  | 				//fprintf(stderr, "Unexpected A record length\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr)); | ||||||
|  | 			printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 		case ns_t_aaaa: | ||||||
|  | 			if (rdlen != 16) { | ||||||
|  | 				//fprintf(stderr, "Unexpected AAAA record length\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr)); | ||||||
|  | 			printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr); | ||||||
|  | 			break; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 		case ns_t_ns: | ||||||
|  | 			if (!format) | ||||||
|  | 				format = "%s\tnameserver = %s\n"; | ||||||
|  | 			/* fall through */ | ||||||
|  |  | ||||||
|  | 		case ns_t_cname: | ||||||
|  | 			if (!format) | ||||||
|  | 				format = "%s\tcanonical name = %s\n"; | ||||||
|  | 			/* fall through */ | ||||||
|  |  | ||||||
|  | 		case ns_t_ptr: | ||||||
|  | 			if (!format) | ||||||
|  | 				format = "%s\tname = %s\n"; | ||||||
|  | 			if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||||||
|  | 				ns_rr_rdata(rr), dname, sizeof(dname)) < 0) { | ||||||
|  | 				//fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			printf(format, ns_rr_name(rr), dname); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case ns_t_mx: | ||||||
|  | 			if (rdlen < 2) { | ||||||
|  | 				fprintf(stderr, "MX record too short\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			n = ns_get16(ns_rr_rdata(rr)); | ||||||
|  | 			if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||||||
|  | 				ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) { | ||||||
|  | 				//fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case ns_t_txt: | ||||||
|  | 			if (rdlen < 1) { | ||||||
|  | 				//fprintf(stderr, "TXT record too short\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			n = *(unsigned char *)ns_rr_rdata(rr); | ||||||
|  | 			if (n > 0) { | ||||||
|  | 				memset(dname, 0, sizeof(dname)); | ||||||
|  | 				memcpy(dname, ns_rr_rdata(rr) + 1, n); | ||||||
|  | 				printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case ns_t_soa: | ||||||
|  | 			if (rdlen < 20) { | ||||||
|  | 				//fprintf(stderr, "SOA record too short\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			printf("%s\n", ns_rr_name(rr)); | ||||||
|  |  | ||||||
|  | 			cp = ns_rr_rdata(rr); | ||||||
|  | 			n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||||||
|  | 			                       cp, dname, sizeof(dname)); | ||||||
|  |  | ||||||
|  | 			if (n < 0) { | ||||||
|  | 				//fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			printf("\torigin = %s\n", dname); | ||||||
|  | 			cp += n; | ||||||
|  |  | ||||||
|  | 			n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||||||
|  | 			                       cp, dname, sizeof(dname)); | ||||||
|  |  | ||||||
|  | 			if (n < 0) { | ||||||
|  | 				//fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			printf("\tmail addr = %s\n", dname); | ||||||
|  | 			cp += n; | ||||||
|  |  | ||||||
|  | 			printf("\tserial = %lu\n", ns_get32(cp)); | ||||||
|  | 			cp += 4; | ||||||
|  |  | ||||||
|  | 			printf("\trefresh = %lu\n", ns_get32(cp)); | ||||||
|  | 			cp += 4; | ||||||
|  |  | ||||||
|  | 			printf("\tretry = %lu\n", ns_get32(cp)); | ||||||
|  | 			cp += 4; | ||||||
|  |  | ||||||
|  | 			printf("\texpire = %lu\n", ns_get32(cp)); | ||||||
|  | 			cp += 4; | ||||||
|  |  | ||||||
|  | 			printf("\tminimum = %lu\n", ns_get32(cp)); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa) | ||||||
|  | { | ||||||
|  | 	char *eptr, *hash, ifname[IFNAMSIZ]; | ||||||
|  | 	unsigned int port = default_port; | ||||||
|  | 	unsigned int scope = 0; | ||||||
|  |  | ||||||
|  | 	hash = strchr(addrstr, '#'); | ||||||
|  |  | ||||||
|  | 	if (hash) { | ||||||
|  | 		*hash++ = '\0'; | ||||||
|  | 		port = strtoul(hash, &eptr, 10); | ||||||
|  |  | ||||||
|  | 		if (eptr == hash || *eptr != '\0' || port > 65535) { | ||||||
|  | 			errno = EINVAL; | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	hash = strchr(addrstr, '%'); | ||||||
|  |  | ||||||
|  | 	if (hash) { | ||||||
|  | 		for (eptr = ++hash; *eptr != '\0' && *eptr != '#'; eptr++) { | ||||||
|  | 			if ((eptr - hash) >= IFNAMSIZ) { | ||||||
|  | 				errno = ENODEV; | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			ifname[eptr - hash] = *eptr; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ifname[eptr - hash] = '\0'; | ||||||
|  | 		scope = if_nametoindex(ifname); | ||||||
|  |  | ||||||
|  | 		if (scope == 0) { | ||||||
|  | 			errno = ENODEV; | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 	if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) { | ||||||
|  | 		lsa->u.sin6.sin6_family = AF_INET6; | ||||||
|  | 		lsa->u.sin6.sin6_port = htons(port); | ||||||
|  | 		lsa->u.sin6.sin6_scope_id = scope; | ||||||
|  | 		lsa->len = sizeof(lsa->u.sin6); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (!scope && inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) { | ||||||
|  | 		lsa->u.sin.sin_family = AF_INET; | ||||||
|  | 		lsa->u.sin.sin_port = htons(port); | ||||||
|  | 		lsa->len = sizeof(lsa->u.sin); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errno = EINVAL; | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static unsigned long mtime(void) | ||||||
|  | { | ||||||
|  | 	struct timespec ts; | ||||||
|  | 	clock_gettime(CLOCK_REALTIME, &ts); | ||||||
|  | 	return (unsigned long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | static void to_v4_mapped(len_and_sockaddr *a) | ||||||
|  | { | ||||||
|  | 	if (a->u.sa.sa_family != AF_INET) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	memcpy(a->u.sin6.sin6_addr.s6_addr + 12, | ||||||
|  | 	       &a->u.sin.sin_addr, 4); | ||||||
|  |  | ||||||
|  | 	memcpy(a->u.sin6.sin6_addr.s6_addr, | ||||||
|  | 	       "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); | ||||||
|  |  | ||||||
|  | 	a->u.sin6.sin6_family = AF_INET6; | ||||||
|  | 	a->u.sin6.sin6_flowinfo = 0; | ||||||
|  | 	a->u.sin6.sin6_scope_id = 0; | ||||||
|  | 	a->len = sizeof(a->u.sin6); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Function logic borrowed & modified from musl libc, res_msend.c | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  | 	int timeout = default_timeout * 1000, retry_interval, servfail_retry = 0; | ||||||
|  | 	len_and_sockaddr from = { }; | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 	int one = 1; | ||||||
|  | #endif | ||||||
|  | 	int recvlen = 0; | ||||||
|  | 	int n_replies = 0; | ||||||
|  | 	struct pollfd pfd; | ||||||
|  | 	unsigned long t0, t1, t2; | ||||||
|  | 	int nn, qn, next_query = 0; | ||||||
|  |  | ||||||
|  | 	from.u.sa.sa_family = AF_INET; | ||||||
|  | 	from.len = sizeof(from.u.sin); | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 	for (nn = 0; nn < n_ns; nn++) { | ||||||
|  | 		if (ns[nn].addr.u.sa.sa_family == AF_INET6) { | ||||||
|  | 			from.u.sa.sa_family = AF_INET6; | ||||||
|  | 			from.len = sizeof(from.u.sin6); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	/* Get local address and open/bind a socket */ | ||||||
|  | 	fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 	/* Handle case where system lacks IPv6 support */ | ||||||
|  | 	if (fd < 0 && from.u.sa.sa_family == AF_INET6 && errno == EAFNOSUPPORT) { | ||||||
|  | 		fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); | ||||||
|  | 		from.u.sa.sa_family = AF_INET; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (fd < 0) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	if (bind(fd, &from.u.sa, from.len) < 0) { | ||||||
|  | 		close(fd); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #if ENABLE_FEATURE_IPV6 | ||||||
|  | 	/* Convert any IPv4 addresses in a mixed environment to v4-mapped */ | ||||||
|  | 	if (from.u.sa.sa_family == AF_INET6) { | ||||||
|  | 		setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); | ||||||
|  |  | ||||||
|  | 		for (nn = 0; nn < n_ns; nn++) | ||||||
|  | 			to_v4_mapped(&ns[nn].addr); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	pfd.fd = fd; | ||||||
|  | 	pfd.events = POLLIN; | ||||||
|  | 	retry_interval = timeout / default_retry; | ||||||
|  | 	t0 = t2 = mtime(); | ||||||
|  | 	t1 = t2 - retry_interval; | ||||||
|  |  | ||||||
|  | 	for (; t2 - t0 < timeout; t2 = mtime()) { | ||||||
|  | 		if (t2 - t1 >= retry_interval) { | ||||||
|  | 			for (qn = 0; qn < n_queries; qn++) { | ||||||
|  | 				if (queries[qn].rlen) | ||||||
|  | 					continue; | ||||||
|  |  | ||||||
|  | 				for (nn = 0; nn < n_ns; nn++) { | ||||||
|  | 					sendto(fd, queries[qn].query, queries[qn].qlen, | ||||||
|  | 					       MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			t1 = t2; | ||||||
|  | 			servfail_retry = 2 * n_queries; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Wait for a response, or until time to retry */ | ||||||
|  | 		if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		while (1) { | ||||||
|  | 			recvlen = recvfrom(fd, queries[next_query].reply, | ||||||
|  | 			                   sizeof(queries[next_query].reply), 0, | ||||||
|  | 			                   &from.u.sa, &from.len); | ||||||
|  |  | ||||||
|  | 			/* read error */ | ||||||
|  | 			if (recvlen < 0) | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			/* Ignore non-identifiable packets */ | ||||||
|  | 			if (recvlen < 4) | ||||||
|  | 				continue; | ||||||
|  |  | ||||||
|  | 			/* Ignore replies from addresses we didn't send to */ | ||||||
|  | 			for (nn = 0; nn < n_ns; nn++) | ||||||
|  | 				if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0) | ||||||
|  | 					break; | ||||||
|  |  | ||||||
|  | 			if (nn >= n_ns) | ||||||
|  | 				continue; | ||||||
|  |  | ||||||
|  | 			/* Find which query this answer goes with, if any */ | ||||||
|  | 			for (qn = next_query; qn < n_queries; qn++) | ||||||
|  | 				if (!memcmp(queries[next_query].reply, queries[qn].query, 2)) | ||||||
|  | 					break; | ||||||
|  |  | ||||||
|  | 			if (qn >= n_queries || queries[qn].rlen) | ||||||
|  | 				continue; | ||||||
|  |  | ||||||
|  | 			queries[qn].rcode = queries[next_query].reply[3] & 15; | ||||||
|  | 			queries[qn].latency = mtime() - t0; | ||||||
|  | 			queries[qn].n_ns = nn; | ||||||
|  |  | ||||||
|  | 			ns[nn].replies++; | ||||||
|  |  | ||||||
|  | 			/* Only accept positive or negative responses; | ||||||
|  | 			 * retry immediately on server failure, and ignore | ||||||
|  | 			 * all other codes such as refusal. */ | ||||||
|  | 			switch (queries[qn].rcode) { | ||||||
|  | 			case 0: | ||||||
|  | 			case 3: | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			case 2: | ||||||
|  | 				if (servfail_retry && servfail_retry--) { | ||||||
|  | 					ns[nn].failures++; | ||||||
|  | 					sendto(fd, queries[qn].query, queries[qn].qlen, | ||||||
|  | 					       MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); | ||||||
|  | 				} | ||||||
|  | 				/* fall through */ | ||||||
|  |  | ||||||
|  | 			default: | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			/* Store answer */ | ||||||
|  | 			n_replies++; | ||||||
|  |  | ||||||
|  | 			queries[qn].rlen = recvlen; | ||||||
|  |  | ||||||
|  | 			if (qn == next_query) { | ||||||
|  | 				while (next_query < n_queries) { | ||||||
|  | 					if (!queries[next_query].rlen) | ||||||
|  | 						break; | ||||||
|  |  | ||||||
|  | 					next_query++; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				memcpy(queries[qn].reply, queries[next_query].reply, recvlen); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if (next_query >= n_queries) | ||||||
|  | 				return n_replies; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return n_replies; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr) | ||||||
|  | { | ||||||
|  | 	char portstr[sizeof("65535")], *p; | ||||||
|  | 	len_and_sockaddr a = { }; | ||||||
|  | 	struct ns *tmp; | ||||||
|  | 	struct addrinfo *ai, *aip, hints = { | ||||||
|  | 		.ai_flags = AI_NUMERICSERV, | ||||||
|  | 		.ai_socktype = SOCK_DGRAM | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	if (parse_nsaddr(addr, &a)) { | ||||||
|  | 		/* Maybe we got a domain name, attempt to resolve it using the standard | ||||||
|  | 		 * resolver routines */ | ||||||
|  |  | ||||||
|  | 		p = strchr(addr, '#'); | ||||||
|  | 		snprintf(portstr, sizeof(portstr), "%hu", | ||||||
|  | 		         (unsigned short)(p ? strtoul(p, NULL, 10) : default_port)); | ||||||
|  |  | ||||||
|  | 		if (!getaddrinfo(addr, portstr, &hints, &ai)) { | ||||||
|  | 			for (aip = ai; aip; aip = aip->ai_next) { | ||||||
|  | 				if (aip->ai_addr->sa_family != AF_INET && | ||||||
|  | 				    aip->ai_addr->sa_family != AF_INET6) | ||||||
|  | 					continue; | ||||||
|  |  | ||||||
|  | #if ! ENABLE_FEATURE_IPV6 | ||||||
|  | 				if (aip->ai_addr->sa_family != AF_INET) | ||||||
|  | 					continue; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 				tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); | ||||||
|  |  | ||||||
|  | 				if (!tmp) | ||||||
|  | 					return NULL; | ||||||
|  |  | ||||||
|  | 				*ns = tmp; | ||||||
|  |  | ||||||
|  | 				(*ns)[*n_ns].name = addr; | ||||||
|  | 				(*ns)[*n_ns].replies = 0; | ||||||
|  | 				(*ns)[*n_ns].failures = 0; | ||||||
|  | 				(*ns)[*n_ns].addr.len = aip->ai_addrlen; | ||||||
|  |  | ||||||
|  | 				memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen); | ||||||
|  |  | ||||||
|  | 				(*n_ns)++; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			freeaddrinfo(ai); | ||||||
|  |  | ||||||
|  | 			return &(*ns)[*n_ns]; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); | ||||||
|  |  | ||||||
|  | 	if (!tmp) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	*ns = tmp; | ||||||
|  |  | ||||||
|  | 	(*ns)[*n_ns].addr = a; | ||||||
|  | 	(*ns)[*n_ns].name = addr; | ||||||
|  | 	(*ns)[*n_ns].replies = 0; | ||||||
|  | 	(*ns)[*n_ns].failures = 0; | ||||||
|  |  | ||||||
|  | 	return &(*ns)[(*n_ns)++]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct query *add_query(struct query **queries, int *n_queries, | ||||||
|  |                                int type, const char *dname) | ||||||
|  | { | ||||||
|  | 	struct query *tmp; | ||||||
|  | 	ssize_t qlen; | ||||||
|  |  | ||||||
|  | 	tmp = realloc(*queries, sizeof(**queries) * (*n_queries + 1)); | ||||||
|  |  | ||||||
|  | 	if (!tmp) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	memset(&tmp[*n_queries], 0, sizeof(*tmp)); | ||||||
|  |  | ||||||
|  | 	qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL, | ||||||
|  | 	                   tmp[*n_queries].query, sizeof(tmp[*n_queries].query)); | ||||||
|  |  | ||||||
|  | 	tmp[*n_queries].qlen = qlen; | ||||||
|  | 	tmp[*n_queries].name = dname; | ||||||
|  | 	*queries = tmp; | ||||||
|  |  | ||||||
|  | 	return &tmp[(*n_queries)++]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	int rc = 1; | ||||||
|  | 	struct ns *ns = NULL; | ||||||
|  | 	struct query *queries = NULL; | ||||||
|  | 	int n_ns = 0, n_queries = 0; | ||||||
|  | 	int c = 0; | ||||||
|  |  | ||||||
|  | 	char *url = "telecominfraproject.com"; | ||||||
|  | 	char *server = "127.0.0.1"; | ||||||
|  | 	int v6 = 0; | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		int option = getopt(argc, argv, "u:s:i:6"); | ||||||
|  |  | ||||||
|  | 		if (option == -1) | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		switch (option) { | ||||||
|  | 		case '6': | ||||||
|  | 			v6 = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'u': | ||||||
|  | 			url = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 's': | ||||||
|  | 			server = optarg; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 		case 'h': | ||||||
|  | 			printf("Usage: dnsprobe OPTIONS\n" | ||||||
|  | 			       "  -6 - use ipv6\n" | ||||||
|  | 			       "  -u <url>\n" | ||||||
|  | 			       "  -s <server>\n"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "dnsprobe"); | ||||||
|  |  | ||||||
|  | 	ULOG_INFO("attempting to probe dns - %s %s %s\n", | ||||||
|  | 		url, server, v6 ? "ipv6" : ""); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	add_query(&queries, &n_queries, v6 ? T_AAAA : T_A, url); | ||||||
|  |  | ||||||
|  | 	add_ns(&ns, &n_ns, server); | ||||||
|  |  | ||||||
|  | 	rc = send_queries(&ns[0], 1, queries, n_queries); | ||||||
|  | 	if (rc <= 0) { | ||||||
|  | 		fprintf(stderr, "Failed to send queries: %s\n", strerror(errno)); | ||||||
|  | 		rc = -1; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (queries[0].rcode != 0) { | ||||||
|  | 		printf("** server can't find %s: %s\n", queries[0].name, | ||||||
|  | 		rcodes[queries[0].rcode]); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (queries[0].rlen) { | ||||||
|  | 		c = parse_reply(queries[0].reply, queries[0].rlen, NULL); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (c == 0) | ||||||
|  | 		printf("*** Can't find %s: No answer\n", queries[0].name); | ||||||
|  | 	else if (c < 0) | ||||||
|  | 		printf("*** Can't find %s: Parse error\n", queries[0].name); | ||||||
|  | 	else | ||||||
|  | 		rc = 0; | ||||||
|  |  | ||||||
|  | out: | ||||||
|  | 	if (n_ns) | ||||||
|  | 		free(ns); | ||||||
|  |  | ||||||
|  | 	if (n_queries) | ||||||
|  | 		free(queries); | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
							
								
								
									
										100
									
								
								feeds/ucentral/ucentral-tools/src/firstcontact.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								feeds/ucentral/ucentral-tools/src/firstcontact.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | #define _GNU_SOURCE | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <getopt.h> | ||||||
|  |  | ||||||
|  | #include <curl/curl.h> | ||||||
|  |  | ||||||
|  | #include <libubox/ulog.h> | ||||||
|  |  | ||||||
|  | static const char *file_cert = "/etc/open-wifi/client.pem"; | ||||||
|  | static const char *file_key  = "/etc/open-wifi/client_dec.key"; | ||||||
|  | static const char *file_json = "/etc/open-wifi/redirector.json"; | ||||||
|  | static const char *file_dbg  = "/tmp/firstcontact.hdr"; | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	FILE *fp_json; | ||||||
|  | 	FILE *fp_dbg; | ||||||
|  | 	CURLcode res; | ||||||
|  | 	CURL *curl; | ||||||
|  | 	char *devid = NULL; | ||||||
|  | 	char *url; | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		int option = getopt(argc, argv, "k:c:o:hi:"); | ||||||
|  |  | ||||||
|  | 		if (option == -1) | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		switch (option) { | ||||||
|  | 		case 'k': | ||||||
|  | 			file_key = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'c': | ||||||
|  | 			file_cert = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'o': | ||||||
|  | 			file_json = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'i': | ||||||
|  | 			devid = optarg; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 		case 'h': | ||||||
|  | 			printf("Usage: firstcontact OPTIONS\n" | ||||||
|  | 			       "  -k <keyfile>\n" | ||||||
|  | 			       "  -c <certfile>\n" | ||||||
|  | 			       "  -o <outfile>\n" | ||||||
|  | 			       "  -i <devid>\n"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!devid) { | ||||||
|  | 		fprintf(stderr, "missing devid\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "firstcontact"); | ||||||
|  | 	ULOG_INFO("attempting first contact\n"); | ||||||
|  |  | ||||||
|  | 	fp_dbg = fopen(file_dbg, "wb"); | ||||||
|  | 	fp_json = fopen(file_json, "wb"); | ||||||
|  | 	if (!fp_json) { | ||||||
|  | 		ULOG_ERR("failed to create %s\n", file_json); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	curl_global_init(CURL_GLOBAL_DEFAULT); | ||||||
|  | 	curl = curl_easy_init(); | ||||||
|  | 	if (!curl) { | ||||||
|  | 		ULOG_ERR("curl_easy_init failed\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (asprintf(&url, "https://clientauth.demo.one.digicert.com/iot/api/v2/device/%s", devid) < 0) { | ||||||
|  | 		ULOG_ERR("failed to assemble url\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_URL, url); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp_json); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp_dbg); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_SSLCERT, file_cert); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_SSLKEY, file_key); | ||||||
|  | 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); | ||||||
|  |  | ||||||
|  | 	res = curl_easy_perform(curl); | ||||||
|  | 	if (res != CURLE_OK) | ||||||
|  | 		ULOG_ERR("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); | ||||||
|  | 	else | ||||||
|  | 		ULOG_INFO("downloaded first contact data\n"); | ||||||
|  | 	curl_easy_cleanup(curl); | ||||||
|  | 	curl_global_cleanup(); | ||||||
|  |  | ||||||
|  | 	ulog_close(); | ||||||
|  |  | ||||||
|  | 	return (res != CURLE_OK); | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								feeds/ucentral/ucentral-tools/src/ip-collide.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								feeds/ucentral/ucentral-tools/src/ip-collide.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <net/if.h> | ||||||
|  |  | ||||||
|  | #include <libubox/list.h> | ||||||
|  | #include <libubox/ulog.h> | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | struct route { | ||||||
|  | 	struct list_head list; | ||||||
|  | 	char devname[64]; | ||||||
|  | 	uint32_t domain; | ||||||
|  | 	uint32_t mask; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct list_head routes = LIST_HEAD_INIT(routes); | ||||||
|  |  | ||||||
|  | static int parse_routes(void) | ||||||
|  | { | ||||||
|  | 	FILE *fp = fopen("/proc/net/route", "r"); | ||||||
|  | 	int flgs, ref, use, metric, mtu, win, ir; | ||||||
|  | 	struct route *route; | ||||||
|  | 	unsigned long g; | ||||||
|  | 	int r; | ||||||
|  |  | ||||||
|  | 	r = fscanf(fp, "%*[^\n]\n"); | ||||||
|  | 	if (r < 0) { | ||||||
|  | 		fprintf(stderr, "failed to parse routes\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	while (1) { | ||||||
|  | 		route = malloc(sizeof(*route)); | ||||||
|  | 		if (!route) | ||||||
|  | 			break; | ||||||
|  | 		memset(route, 0, sizeof(*route)); | ||||||
|  | 		r = fscanf(fp, "%63s%x%lx%X%d%d%d%x%d%d%d\n", | ||||||
|  | 			route->devname, &route->domain, &g, &flgs, &ref, &use, &metric, &route->mask, | ||||||
|  | 			&mtu, &win, &ir); | ||||||
|  | 		if (r != 11 && (r < 0) && feof(fp)) | ||||||
|  | 			break; | ||||||
|  | 		list_add(&route->list, &routes); | ||||||
|  | 		printf("1 %s %x %x\n", route->devname, ntohl(route->domain), ntohl(route->mask)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fclose(fp); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int find_collisions(void) | ||||||
|  | { | ||||||
|  | 	struct route *route; | ||||||
|  |  | ||||||
|  | 	list_for_each_entry(route, &routes, list) { | ||||||
|  | 		struct route *compare; | ||||||
|  |  | ||||||
|  | 		if (!route->domain || !route->mask) | ||||||
|  | 			continue; | ||||||
|  | 		list_for_each_entry(compare, &routes, list) { | ||||||
|  | 			if (!compare->domain || !compare->mask) | ||||||
|  | 				continue; | ||||||
|  | 			if (compare == route) | ||||||
|  | 				continue; | ||||||
|  | 			if (((route->domain & route->mask) == (compare->domain & route->mask)) || | ||||||
|  | 			    ((route->domain & compare->mask) == (compare->domain & compare->mask))) { | ||||||
|  | 				ULOG_ERR("collision detected\n"); | ||||||
|  | 				return 1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	ULOG_INFO("no collision detected\n"); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "ip-collide"); | ||||||
|  |  | ||||||
|  | 	parse_routes(); | ||||||
|  | 	if (!list_empty(&routes)) | ||||||
|  | 		return find_collisions(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								feeds/ucentral/ucentral-tools/src/radiusprobe.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								feeds/ucentral/ucentral-tools/src/radiusprobe.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <radcli/radcli.h> | ||||||
|  |  | ||||||
|  | int | ||||||
|  | main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	int result; | ||||||
|  | 	char username[128]; | ||||||
|  | 	char passwd[AUTH_PASS_LEN + 1]; | ||||||
|  | 	VALUE_PAIR *send, *received; | ||||||
|  | 	uint32_t service; | ||||||
|  | 	rc_handle *rh; | ||||||
|  |  | ||||||
|  | 	/* Not needed if you already used openlog() */ | ||||||
|  | 	rc_openlog("radiusprobe"); | ||||||
|  |  | ||||||
|  | 	if ((rh = rc_read_config("/tmp/radius.conf")) == NULL) | ||||||
|  | 		return ERROR_RC; | ||||||
|  |  | ||||||
|  | 	strcpy(username, "healthcheck"); | ||||||
|  | 	strcpy(passwd, "uCentral"); | ||||||
|  |  | ||||||
|  | 	send = NULL; | ||||||
|  |  | ||||||
|  | 	if (rc_avpair_add(rh, &send, PW_USER_NAME, username, -1, 0) == NULL) | ||||||
|  | 		return ERROR_RC; | ||||||
|  |  | ||||||
|  | 	if (rc_avpair_add(rh, &send, PW_USER_PASSWORD, passwd, -1, 0) == NULL) | ||||||
|  | 		return ERROR_RC; | ||||||
|  |  | ||||||
|  | 	service = PW_AUTHENTICATE_ONLY; | ||||||
|  | 	if (rc_avpair_add(rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) | ||||||
|  | 		return ERROR_RC; | ||||||
|  |  | ||||||
|  | 	result = rc_auth(rh, 0, send, &received, NULL); | ||||||
|  |  | ||||||
|  | 	if (result == OK_RC || result == REJECT_RC) { | ||||||
|  | 		fprintf(stderr, "RADIUS server OK\n"); | ||||||
|  | 		result = 0; | ||||||
|  | 	} else { | ||||||
|  | 		fprintf(stderr, "RADIUS server failure\n"); | ||||||
|  | 		result = -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
| @@ -2,17 +2,9 @@ include $(TOPDIR)/rules.mk | |||||||
|  |  | ||||||
| PKG_NAME:=udevmand | PKG_NAME:=udevmand | ||||||
| PKG_RELEASE:=1 | PKG_RELEASE:=1 | ||||||
|  |  | ||||||
| PKG_SOURCE_PROTO:=git |  | ||||||
| PKG_SOURCE_URL=https://github.com/blogic/udevmand.git |  | ||||||
| PKG_MIRROR_HASH:=6f6a5536656a64e1f38f03747ef06ab03b28ef797adc72a901eb7dbc6e45e496 |  | ||||||
| PKG_SOURCE_DATE:=20250704 |  | ||||||
| PKG_SOURCE_VERSION:=e56be31d7c341467cc26714dac5c6b450a612808 |  | ||||||
| CMAKE_INSTALL:=1 | CMAKE_INSTALL:=1 | ||||||
|  |  | ||||||
| PKG_LICENSE:=LGPL-2.1 | PKG_LICENSE:=LGPL-2.1 | ||||||
| PKG_LICENSE_FILES:= | PKG_LICENSE_FILES:= | ||||||
|  |  | ||||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||||
|  |  | ||||||
| include $(INCLUDE_DIR)/package.mk | include $(INCLUDE_DIR)/package.mk | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								feeds/ucentral/udevmand/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								feeds/ucentral/udevmand/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | cmake_minimum_required(VERSION 2.6) | ||||||
|  |  | ||||||
|  | include_directories(/usr/local/include/libnl-tiny) | ||||||
|  |  | ||||||
|  | PROJECT(udevmand C) | ||||||
|  | INCLUDE(GNUInstallDirs) | ||||||
|  | ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) | ||||||
|  |  | ||||||
|  | SET(LIBS ubox ubus json-c blobmsg_json uci nl-tiny) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(udevmand main.c mac.c neigh.c ubus.c blob.c netlink.c bridge.c dhcp.c netifd.c ethers.c netdev.c) | ||||||
|  | TARGET_LINK_LIBRARIES(udevmand ${LIBS}) | ||||||
|  | INSTALL(TARGETS udevmand | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
							
								
								
									
										502
									
								
								feeds/ucentral/udevmand/src/COPYING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								feeds/ucentral/udevmand/src/COPYING
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,502 @@ | |||||||
|  |                   GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 2.1, February 1999 | ||||||
|  |  | ||||||
|  |  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||||
|  |  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
|  | [This is the first released version of the Lesser GPL.  It also counts | ||||||
|  |  as the successor of the GNU Library Public License, version 2, hence | ||||||
|  |  the version number 2.1.] | ||||||
|  |  | ||||||
|  |                             Preamble | ||||||
|  |  | ||||||
|  |   The licenses for most software are designed to take away your | ||||||
|  | freedom to share and change it.  By contrast, the GNU General Public | ||||||
|  | Licenses are intended to guarantee your freedom to share and change | ||||||
|  | free software--to make sure the software is free for all its users. | ||||||
|  |  | ||||||
|  |   This license, the Lesser General Public License, applies to some | ||||||
|  | specially designated software packages--typically libraries--of the | ||||||
|  | Free Software Foundation and other authors who decide to use it.  You | ||||||
|  | can use it too, but we suggest you first think carefully about whether | ||||||
|  | this license or the ordinary General Public License is the better | ||||||
|  | strategy to use in any particular case, based on the explanations below. | ||||||
|  |  | ||||||
|  |   When we speak of free software, we are referring to freedom of use, | ||||||
|  | not price.  Our General Public Licenses are designed to make sure that | ||||||
|  | you have the freedom to distribute copies of free software (and charge | ||||||
|  | for this service if you wish); that you receive source code or can get | ||||||
|  | it if you want it; that you can change the software and use pieces of | ||||||
|  | it in new free programs; and that you are informed that you can do | ||||||
|  | these things. | ||||||
|  |  | ||||||
|  |   To protect your rights, we need to make restrictions that forbid | ||||||
|  | distributors to deny you these rights or to ask you to surrender these | ||||||
|  | rights.  These restrictions translate to certain responsibilities for | ||||||
|  | you if you distribute copies of the library or if you modify it. | ||||||
|  |  | ||||||
|  |   For example, if you distribute copies of the library, whether gratis | ||||||
|  | or for a fee, you must give the recipients all the rights that we gave | ||||||
|  | you.  You must make sure that they, too, receive or can get the source | ||||||
|  | code.  If you link other code with the library, you must provide | ||||||
|  | complete object files to the recipients, so that they can relink them | ||||||
|  | with the library after making changes to the library and recompiling | ||||||
|  | it.  And you must show them these terms so they know their rights. | ||||||
|  |  | ||||||
|  |   We protect your rights with a two-step method: (1) we copyright the | ||||||
|  | library, and (2) we offer you this license, which gives you legal | ||||||
|  | permission to copy, distribute and/or modify the library. | ||||||
|  |  | ||||||
|  |   To protect each distributor, we want to make it very clear that | ||||||
|  | there is no warranty for the free library.  Also, if the library is | ||||||
|  | modified by someone else and passed on, the recipients should know | ||||||
|  | that what they have is not the original version, so that the original | ||||||
|  | author's reputation will not be affected by problems that might be | ||||||
|  | introduced by others. | ||||||
|  |  | ||||||
|  |   Finally, software patents pose a constant threat to the existence of | ||||||
|  | any free program.  We wish to make sure that a company cannot | ||||||
|  | effectively restrict the users of a free program by obtaining a | ||||||
|  | restrictive license from a patent holder.  Therefore, we insist that | ||||||
|  | any patent license obtained for a version of the library must be | ||||||
|  | consistent with the full freedom of use specified in this license. | ||||||
|  |  | ||||||
|  |   Most GNU software, including some libraries, is covered by the | ||||||
|  | ordinary GNU General Public License.  This license, the GNU Lesser | ||||||
|  | General Public License, applies to certain designated libraries, and | ||||||
|  | is quite different from the ordinary General Public License.  We use | ||||||
|  | this license for certain libraries in order to permit linking those | ||||||
|  | libraries into non-free programs. | ||||||
|  |  | ||||||
|  |   When a program is linked with a library, whether statically or using | ||||||
|  | a shared library, the combination of the two is legally speaking a | ||||||
|  | combined work, a derivative of the original library.  The ordinary | ||||||
|  | General Public License therefore permits such linking only if the | ||||||
|  | entire combination fits its criteria of freedom.  The Lesser General | ||||||
|  | Public License permits more lax criteria for linking other code with | ||||||
|  | the library. | ||||||
|  |  | ||||||
|  |   We call this license the "Lesser" General Public License because it | ||||||
|  | does Less to protect the user's freedom than the ordinary General | ||||||
|  | Public License.  It also provides other free software developers Less | ||||||
|  | of an advantage over competing non-free programs.  These disadvantages | ||||||
|  | are the reason we use the ordinary General Public License for many | ||||||
|  | libraries.  However, the Lesser license provides advantages in certain | ||||||
|  | special circumstances. | ||||||
|  |  | ||||||
|  |   For example, on rare occasions, there may be a special need to | ||||||
|  | encourage the widest possible use of a certain library, so that it becomes | ||||||
|  | a de-facto standard.  To achieve this, non-free programs must be | ||||||
|  | allowed to use the library.  A more frequent case is that a free | ||||||
|  | library does the same job as widely used non-free libraries.  In this | ||||||
|  | case, there is little to gain by limiting the free library to free | ||||||
|  | software only, so we use the Lesser General Public License. | ||||||
|  |  | ||||||
|  |   In other cases, permission to use a particular library in non-free | ||||||
|  | programs enables a greater number of people to use a large body of | ||||||
|  | free software.  For example, permission to use the GNU C Library in | ||||||
|  | non-free programs enables many more people to use the whole GNU | ||||||
|  | operating system, as well as its variant, the GNU/Linux operating | ||||||
|  | system. | ||||||
|  |  | ||||||
|  |   Although the Lesser General Public License is Less protective of the | ||||||
|  | users' freedom, it does ensure that the user of a program that is | ||||||
|  | linked with the Library has the freedom and the wherewithal to run | ||||||
|  | that program using a modified version of the Library. | ||||||
|  |  | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow.  Pay close attention to the difference between a | ||||||
|  | "work based on the library" and a "work that uses the library".  The | ||||||
|  | former contains code derived from the library, whereas the latter must | ||||||
|  | be combined with the library in order to run. | ||||||
|  |  | ||||||
|  |                   GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  |  | ||||||
|  |   0. This License Agreement applies to any software library or other | ||||||
|  | program which contains a notice placed by the copyright holder or | ||||||
|  | other authorized party saying it may be distributed under the terms of | ||||||
|  | this Lesser General Public License (also called "this License"). | ||||||
|  | Each licensee is addressed as "you". | ||||||
|  |  | ||||||
|  |   A "library" means a collection of software functions and/or data | ||||||
|  | prepared so as to be conveniently linked with application programs | ||||||
|  | (which use some of those functions and data) to form executables. | ||||||
|  |  | ||||||
|  |   The "Library", below, refers to any such software library or work | ||||||
|  | which has been distributed under these terms.  A "work based on the | ||||||
|  | Library" means either the Library or any derivative work under | ||||||
|  | copyright law: that is to say, a work containing the Library or a | ||||||
|  | portion of it, either verbatim or with modifications and/or translated | ||||||
|  | straightforwardly into another language.  (Hereinafter, translation is | ||||||
|  | included without limitation in the term "modification".) | ||||||
|  |  | ||||||
|  |   "Source code" for a work means the preferred form of the work for | ||||||
|  | making modifications to it.  For a library, complete source code means | ||||||
|  | all the source code for all modules it contains, plus any associated | ||||||
|  | interface definition files, plus the scripts used to control compilation | ||||||
|  | and installation of the library. | ||||||
|  |  | ||||||
|  |   Activities other than copying, distribution and modification are not | ||||||
|  | covered by this License; they are outside its scope.  The act of | ||||||
|  | running a program using the Library is not restricted, and output from | ||||||
|  | such a program is covered only if its contents constitute a work based | ||||||
|  | on the Library (independent of the use of the Library in a tool for | ||||||
|  | writing it).  Whether that is true depends on what the Library does | ||||||
|  | and what the program that uses the Library does. | ||||||
|  |  | ||||||
|  |   1. You may copy and distribute verbatim copies of the Library's | ||||||
|  | complete source code as you receive it, in any medium, provided that | ||||||
|  | you conspicuously and appropriately publish on each copy an | ||||||
|  | appropriate copyright notice and disclaimer of warranty; keep intact | ||||||
|  | all the notices that refer to this License and to the absence of any | ||||||
|  | warranty; and distribute a copy of this License along with the | ||||||
|  | Library. | ||||||
|  |  | ||||||
|  |   You may charge a fee for the physical act of transferring a copy, | ||||||
|  | and you may at your option offer warranty protection in exchange for a | ||||||
|  | fee. | ||||||
|  |  | ||||||
|  |   2. You may modify your copy or copies of the Library or any portion | ||||||
|  | of it, thus forming a work based on the Library, and copy and | ||||||
|  | distribute such modifications or work under the terms of Section 1 | ||||||
|  | above, provided that you also meet all of these conditions: | ||||||
|  |  | ||||||
|  |     a) The modified work must itself be a software library. | ||||||
|  |  | ||||||
|  |     b) You must cause the files modified to carry prominent notices | ||||||
|  |     stating that you changed the files and the date of any change. | ||||||
|  |  | ||||||
|  |     c) You must cause the whole of the work to be licensed at no | ||||||
|  |     charge to all third parties under the terms of this License. | ||||||
|  |  | ||||||
|  |     d) If a facility in the modified Library refers to a function or a | ||||||
|  |     table of data to be supplied by an application program that uses | ||||||
|  |     the facility, other than as an argument passed when the facility | ||||||
|  |     is invoked, then you must make a good faith effort to ensure that, | ||||||
|  |     in the event an application does not supply such function or | ||||||
|  |     table, the facility still operates, and performs whatever part of | ||||||
|  |     its purpose remains meaningful. | ||||||
|  |  | ||||||
|  |     (For example, a function in a library to compute square roots has | ||||||
|  |     a purpose that is entirely well-defined independent of the | ||||||
|  |     application.  Therefore, Subsection 2d requires that any | ||||||
|  |     application-supplied function or table used by this function must | ||||||
|  |     be optional: if the application does not supply it, the square | ||||||
|  |     root function must still compute square roots.) | ||||||
|  |  | ||||||
|  | These requirements apply to the modified work as a whole.  If | ||||||
|  | identifiable sections of that work are not derived from the Library, | ||||||
|  | and can be reasonably considered independent and separate works in | ||||||
|  | themselves, then this License, and its terms, do not apply to those | ||||||
|  | sections when you distribute them as separate works.  But when you | ||||||
|  | distribute the same sections as part of a whole which is a work based | ||||||
|  | on the Library, the distribution of the whole must be on the terms of | ||||||
|  | this License, whose permissions for other licensees extend to the | ||||||
|  | entire whole, and thus to each and every part regardless of who wrote | ||||||
|  | it. | ||||||
|  |  | ||||||
|  | Thus, it is not the intent of this section to claim rights or contest | ||||||
|  | your rights to work written entirely by you; rather, the intent is to | ||||||
|  | exercise the right to control the distribution of derivative or | ||||||
|  | collective works based on the Library. | ||||||
|  |  | ||||||
|  | In addition, mere aggregation of another work not based on the Library | ||||||
|  | with the Library (or with a work based on the Library) on a volume of | ||||||
|  | a storage or distribution medium does not bring the other work under | ||||||
|  | the scope of this License. | ||||||
|  |  | ||||||
|  |   3. You may opt to apply the terms of the ordinary GNU General Public | ||||||
|  | License instead of this License to a given copy of the Library.  To do | ||||||
|  | this, you must alter all the notices that refer to this License, so | ||||||
|  | that they refer to the ordinary GNU General Public License, version 2, | ||||||
|  | instead of to this License.  (If a newer version than version 2 of the | ||||||
|  | ordinary GNU General Public License has appeared, then you can specify | ||||||
|  | that version instead if you wish.)  Do not make any other change in | ||||||
|  | these notices. | ||||||
|  |  | ||||||
|  |   Once this change is made in a given copy, it is irreversible for | ||||||
|  | that copy, so the ordinary GNU General Public License applies to all | ||||||
|  | subsequent copies and derivative works made from that copy. | ||||||
|  |  | ||||||
|  |   This option is useful when you wish to copy part of the code of | ||||||
|  | the Library into a program that is not a library. | ||||||
|  |  | ||||||
|  |   4. You may copy and distribute the Library (or a portion or | ||||||
|  | derivative of it, under Section 2) in object code or executable form | ||||||
|  | under the terms of Sections 1 and 2 above provided that you accompany | ||||||
|  | it with the complete corresponding machine-readable source code, which | ||||||
|  | must be distributed under the terms of Sections 1 and 2 above on a | ||||||
|  | medium customarily used for software interchange. | ||||||
|  |  | ||||||
|  |   If distribution of object code is made by offering access to copy | ||||||
|  | from a designated place, then offering equivalent access to copy the | ||||||
|  | source code from the same place satisfies the requirement to | ||||||
|  | distribute the source code, even though third parties are not | ||||||
|  | compelled to copy the source along with the object code. | ||||||
|  |  | ||||||
|  |   5. A program that contains no derivative of any portion of the | ||||||
|  | Library, but is designed to work with the Library by being compiled or | ||||||
|  | linked with it, is called a "work that uses the Library".  Such a | ||||||
|  | work, in isolation, is not a derivative work of the Library, and | ||||||
|  | therefore falls outside the scope of this License. | ||||||
|  |  | ||||||
|  |   However, linking a "work that uses the Library" with the Library | ||||||
|  | creates an executable that is a derivative of the Library (because it | ||||||
|  | contains portions of the Library), rather than a "work that uses the | ||||||
|  | library".  The executable is therefore covered by this License. | ||||||
|  | Section 6 states terms for distribution of such executables. | ||||||
|  |  | ||||||
|  |   When a "work that uses the Library" uses material from a header file | ||||||
|  | that is part of the Library, the object code for the work may be a | ||||||
|  | derivative work of the Library even though the source code is not. | ||||||
|  | Whether this is true is especially significant if the work can be | ||||||
|  | linked without the Library, or if the work is itself a library.  The | ||||||
|  | threshold for this to be true is not precisely defined by law. | ||||||
|  |  | ||||||
|  |   If such an object file uses only numerical parameters, data | ||||||
|  | structure layouts and accessors, and small macros and small inline | ||||||
|  | functions (ten lines or less in length), then the use of the object | ||||||
|  | file is unrestricted, regardless of whether it is legally a derivative | ||||||
|  | work.  (Executables containing this object code plus portions of the | ||||||
|  | Library will still fall under Section 6.) | ||||||
|  |  | ||||||
|  |   Otherwise, if the work is a derivative of the Library, you may | ||||||
|  | distribute the object code for the work under the terms of Section 6. | ||||||
|  | Any executables containing that work also fall under Section 6, | ||||||
|  | whether or not they are linked directly with the Library itself. | ||||||
|  |  | ||||||
|  |   6. As an exception to the Sections above, you may also combine or | ||||||
|  | link a "work that uses the Library" with the Library to produce a | ||||||
|  | work containing portions of the Library, and distribute that work | ||||||
|  | under terms of your choice, provided that the terms permit | ||||||
|  | modification of the work for the customer's own use and reverse | ||||||
|  | engineering for debugging such modifications. | ||||||
|  |  | ||||||
|  |   You must give prominent notice with each copy of the work that the | ||||||
|  | Library is used in it and that the Library and its use are covered by | ||||||
|  | this License.  You must supply a copy of this License.  If the work | ||||||
|  | during execution displays copyright notices, you must include the | ||||||
|  | copyright notice for the Library among them, as well as a reference | ||||||
|  | directing the user to the copy of this License.  Also, you must do one | ||||||
|  | of these things: | ||||||
|  |  | ||||||
|  |     a) Accompany the work with the complete corresponding | ||||||
|  |     machine-readable source code for the Library including whatever | ||||||
|  |     changes were used in the work (which must be distributed under | ||||||
|  |     Sections 1 and 2 above); and, if the work is an executable linked | ||||||
|  |     with the Library, with the complete machine-readable "work that | ||||||
|  |     uses the Library", as object code and/or source code, so that the | ||||||
|  |     user can modify the Library and then relink to produce a modified | ||||||
|  |     executable containing the modified Library.  (It is understood | ||||||
|  |     that the user who changes the contents of definitions files in the | ||||||
|  |     Library will not necessarily be able to recompile the application | ||||||
|  |     to use the modified definitions.) | ||||||
|  |  | ||||||
|  |     b) Use a suitable shared library mechanism for linking with the | ||||||
|  |     Library.  A suitable mechanism is one that (1) uses at run time a | ||||||
|  |     copy of the library already present on the user's computer system, | ||||||
|  |     rather than copying library functions into the executable, and (2) | ||||||
|  |     will operate properly with a modified version of the library, if | ||||||
|  |     the user installs one, as long as the modified version is | ||||||
|  |     interface-compatible with the version that the work was made with. | ||||||
|  |  | ||||||
|  |     c) Accompany the work with a written offer, valid for at | ||||||
|  |     least three years, to give the same user the materials | ||||||
|  |     specified in Subsection 6a, above, for a charge no more | ||||||
|  |     than the cost of performing this distribution. | ||||||
|  |  | ||||||
|  |     d) If distribution of the work is made by offering access to copy | ||||||
|  |     from a designated place, offer equivalent access to copy the above | ||||||
|  |     specified materials from the same place. | ||||||
|  |  | ||||||
|  |     e) Verify that the user has already received a copy of these | ||||||
|  |     materials or that you have already sent this user a copy. | ||||||
|  |  | ||||||
|  |   For an executable, the required form of the "work that uses the | ||||||
|  | Library" must include any data and utility programs needed for | ||||||
|  | reproducing the executable from it.  However, as a special exception, | ||||||
|  | the materials to be distributed need not include anything that is | ||||||
|  | normally distributed (in either source or binary form) with the major | ||||||
|  | components (compiler, kernel, and so on) of the operating system on | ||||||
|  | which the executable runs, unless that component itself accompanies | ||||||
|  | the executable. | ||||||
|  |  | ||||||
|  |   It may happen that this requirement contradicts the license | ||||||
|  | restrictions of other proprietary libraries that do not normally | ||||||
|  | accompany the operating system.  Such a contradiction means you cannot | ||||||
|  | use both them and the Library together in an executable that you | ||||||
|  | distribute. | ||||||
|  |  | ||||||
|  |   7. You may place library facilities that are a work based on the | ||||||
|  | Library side-by-side in a single library together with other library | ||||||
|  | facilities not covered by this License, and distribute such a combined | ||||||
|  | library, provided that the separate distribution of the work based on | ||||||
|  | the Library and of the other library facilities is otherwise | ||||||
|  | permitted, and provided that you do these two things: | ||||||
|  |  | ||||||
|  |     a) Accompany the combined library with a copy of the same work | ||||||
|  |     based on the Library, uncombined with any other library | ||||||
|  |     facilities.  This must be distributed under the terms of the | ||||||
|  |     Sections above. | ||||||
|  |  | ||||||
|  |     b) Give prominent notice with the combined library of the fact | ||||||
|  |     that part of it is a work based on the Library, and explaining | ||||||
|  |     where to find the accompanying uncombined form of the same work. | ||||||
|  |  | ||||||
|  |   8. You may not copy, modify, sublicense, link with, or distribute | ||||||
|  | the Library except as expressly provided under this License.  Any | ||||||
|  | attempt otherwise to copy, modify, sublicense, link with, or | ||||||
|  | distribute the Library is void, and will automatically terminate your | ||||||
|  | rights under this License.  However, parties who have received copies, | ||||||
|  | or rights, from you under this License will not have their licenses | ||||||
|  | terminated so long as such parties remain in full compliance. | ||||||
|  |  | ||||||
|  |   9. You are not required to accept this License, since you have not | ||||||
|  | signed it.  However, nothing else grants you permission to modify or | ||||||
|  | distribute the Library or its derivative works.  These actions are | ||||||
|  | prohibited by law if you do not accept this License.  Therefore, by | ||||||
|  | modifying or distributing the Library (or any work based on the | ||||||
|  | Library), you indicate your acceptance of this License to do so, and | ||||||
|  | all its terms and conditions for copying, distributing or modifying | ||||||
|  | the Library or works based on it. | ||||||
|  |  | ||||||
|  |   10. Each time you redistribute the Library (or any work based on the | ||||||
|  | Library), the recipient automatically receives a license from the | ||||||
|  | original licensor to copy, distribute, link with or modify the Library | ||||||
|  | subject to these terms and conditions.  You may not impose any further | ||||||
|  | restrictions on the recipients' exercise of the rights granted herein. | ||||||
|  | You are not responsible for enforcing compliance by third parties with | ||||||
|  | this License. | ||||||
|  |  | ||||||
|  |   11. If, as a consequence of a court judgment or allegation of patent | ||||||
|  | infringement or for any other reason (not limited to patent issues), | ||||||
|  | conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot | ||||||
|  | distribute so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you | ||||||
|  | may not distribute the Library at all.  For example, if a patent | ||||||
|  | license would not permit royalty-free redistribution of the Library by | ||||||
|  | all those who receive copies directly or indirectly through you, then | ||||||
|  | the only way you could satisfy both it and this License would be to | ||||||
|  | refrain entirely from distribution of the Library. | ||||||
|  |  | ||||||
|  | If any portion of this section is held invalid or unenforceable under any | ||||||
|  | particular circumstance, the balance of the section is intended to apply, | ||||||
|  | and the section as a whole is intended to apply in other circumstances. | ||||||
|  |  | ||||||
|  | It is not the purpose of this section to induce you to infringe any | ||||||
|  | patents or other property right claims or to contest validity of any | ||||||
|  | such claims; this section has the sole purpose of protecting the | ||||||
|  | integrity of the free software distribution system which is | ||||||
|  | implemented by public license practices.  Many people have made | ||||||
|  | generous contributions to the wide range of software distributed | ||||||
|  | through that system in reliance on consistent application of that | ||||||
|  | system; it is up to the author/donor to decide if he or she is willing | ||||||
|  | to distribute software through any other system and a licensee cannot | ||||||
|  | impose that choice. | ||||||
|  |  | ||||||
|  | This section is intended to make thoroughly clear what is believed to | ||||||
|  | be a consequence of the rest of this License. | ||||||
|  |  | ||||||
|  |   12. If the distribution and/or use of the Library is restricted in | ||||||
|  | certain countries either by patents or by copyrighted interfaces, the | ||||||
|  | original copyright holder who places the Library under this License may add | ||||||
|  | an explicit geographical distribution limitation excluding those countries, | ||||||
|  | so that distribution is permitted only in or among countries not thus | ||||||
|  | excluded.  In such case, this License incorporates the limitation as if | ||||||
|  | written in the body of this License. | ||||||
|  |  | ||||||
|  |   13. The Free Software Foundation may publish revised and/or new | ||||||
|  | versions of the Lesser General Public License from time to time. | ||||||
|  | Such new versions will be similar in spirit to the present version, | ||||||
|  | but may differ in detail to address new problems or concerns. | ||||||
|  |  | ||||||
|  | Each version is given a distinguishing version number.  If the Library | ||||||
|  | specifies a version number of this License which applies to it and | ||||||
|  | "any later version", you have the option of following the terms and | ||||||
|  | conditions either of that version or of any later version published by | ||||||
|  | the Free Software Foundation.  If the Library does not specify a | ||||||
|  | license version number, you may choose any version ever published by | ||||||
|  | the Free Software Foundation. | ||||||
|  |  | ||||||
|  |   14. If you wish to incorporate parts of the Library into other free | ||||||
|  | programs whose distribution conditions are incompatible with these, | ||||||
|  | write to the author to ask for permission.  For software which is | ||||||
|  | copyrighted by the Free Software Foundation, write to the Free | ||||||
|  | Software Foundation; we sometimes make exceptions for this.  Our | ||||||
|  | decision will be guided by the two goals of preserving the free status | ||||||
|  | of all derivatives of our free software and of promoting the sharing | ||||||
|  | and reuse of software generally. | ||||||
|  |  | ||||||
|  |                             NO WARRANTY | ||||||
|  |  | ||||||
|  |   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||||
|  | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||||||
|  | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | ||||||
|  | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | ||||||
|  | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | ||||||
|  | LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | ||||||
|  | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  |  | ||||||
|  |   16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | ||||||
|  | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | ||||||
|  | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | ||||||
|  | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | ||||||
|  | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | ||||||
|  | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | ||||||
|  | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | ||||||
|  | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||||||
|  | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  | DAMAGES. | ||||||
|  |  | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |            How to Apply These Terms to Your New Libraries | ||||||
|  |  | ||||||
|  |   If you develop a new library, and you want it to be of the greatest | ||||||
|  | possible use to the public, we recommend making it free software that | ||||||
|  | everyone can redistribute and change.  You can do so by permitting | ||||||
|  | redistribution under these terms (or, alternatively, under the terms of the | ||||||
|  | ordinary General Public License). | ||||||
|  |  | ||||||
|  |   To apply these terms, attach the following notices to the library.  It is | ||||||
|  | safest to attach them to the start of each source file to most effectively | ||||||
|  | convey the exclusion of warranty; and each file should have at least the | ||||||
|  | "copyright" line and a pointer to where the full notice is found. | ||||||
|  |  | ||||||
|  |     <one line to give the library's name and a brief idea of what it does.> | ||||||
|  |     Copyright (C) <year>  <name of author> | ||||||
|  |  | ||||||
|  |     This library is free software; you can redistribute it and/or | ||||||
|  |     modify it under the terms of the GNU Lesser General Public | ||||||
|  |     License as published by the Free Software Foundation; either | ||||||
|  |     version 2.1 of the License, or (at your option) any later version. | ||||||
|  |  | ||||||
|  |     This library is distributed in the hope that it will be useful, | ||||||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |     Lesser General Public License for more details. | ||||||
|  |  | ||||||
|  |     You should have received a copy of the GNU Lesser General Public | ||||||
|  |     License along with this library; if not, write to the Free Software | ||||||
|  |     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  | ||||||
|  | Also add information on how to contact you by electronic and paper mail. | ||||||
|  |  | ||||||
|  | You should also get your employer (if you work as a programmer) or your | ||||||
|  | school, if any, to sign a "copyright disclaimer" for the library, if | ||||||
|  | necessary.  Here is a sample; alter the names: | ||||||
|  |  | ||||||
|  |   Yoyodyne, Inc., hereby disclaims all copyright interest in the | ||||||
|  |   library `Frob' (a library for tweaking knobs) written by James Random Hacker. | ||||||
|  |  | ||||||
|  |   <signature of Ty Coon>, 1 April 1990 | ||||||
|  |   Ty Coon, President of Vice | ||||||
|  |  | ||||||
|  | That's all there is to it! | ||||||
							
								
								
									
										69
									
								
								feeds/ucentral/udevmand/src/blob.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								feeds/ucentral/udevmand/src/blob.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2017 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | struct blob_buf b = { 0 }; | ||||||
|  |  | ||||||
|  | static char *iftype_string[NUM_NL80211_IFTYPES] = { | ||||||
|  | 	[NL80211_IFTYPE_STATION] = "station", | ||||||
|  | 	[NL80211_IFTYPE_AP] = "ap", | ||||||
|  | 	[NL80211_IFTYPE_MONITOR] = "monitor", | ||||||
|  | 	[NL80211_IFTYPE_ADHOC] = "adhoc", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void blobmsg_add_iface(struct blob_buf *bbuf, char *name, int index) | ||||||
|  | { | ||||||
|  | 	static char _ifname[IF_NAMESIZE]; | ||||||
|  | 	char *ifname = if_indextoname(index, _ifname); | ||||||
|  |  | ||||||
|  | 	if (!ifname) | ||||||
|  | 		return; | ||||||
|  | 	blobmsg_add_string(&b, name, ifname); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void blobmsg_add_iftype(struct blob_buf *bbuf, const char *name, const uint32_t iftype) | ||||||
|  | { | ||||||
|  | 	if (iftype_string[iftype]) | ||||||
|  | 		blobmsg_add_string(&b, name, iftype_string[iftype]); | ||||||
|  | 	else | ||||||
|  | 		blobmsg_add_u32(&b, name, iftype); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void blobmsg_add_ipv4(struct blob_buf *bbuf, const char *name, const uint8_t* addr) | ||||||
|  | { | ||||||
|  | 	char ip[16]; | ||||||
|  |  | ||||||
|  | 	snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); | ||||||
|  | 	blobmsg_add_string(&b, name, ip); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void blobmsg_add_ipv6(struct blob_buf *bbuf, const char *name, const uint8_t* _addr) | ||||||
|  | { | ||||||
|  | 	const uint16_t* addr = (const uint16_t*) _addr; | ||||||
|  | 	char ip[40]; | ||||||
|  |  | ||||||
|  | 	snprintf(ip, sizeof(ip), "%x:%x:%x:%x:%x:%x:%x:%x", | ||||||
|  | 		ntohs(addr[0]), ntohs(addr[1]), ntohs(addr[2]), ntohs(addr[3]), | ||||||
|  | 		ntohs(addr[4]), ntohs(addr[5]), ntohs(addr[6]), ntohs(addr[7])); | ||||||
|  | 	blobmsg_add_string(&b, name, ip); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void blobmsg_add_mac(struct blob_buf *bbuf, const char *name, const uint8_t* addr) | ||||||
|  | { | ||||||
|  | 	char mac[18]; | ||||||
|  |  | ||||||
|  | 	snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", | ||||||
|  | 		addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); | ||||||
|  | 	blobmsg_add_string(&b, name, mac); | ||||||
|  | } | ||||||
							
								
								
									
										213
									
								
								feeds/ucentral/udevmand/src/bridge.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								feeds/ucentral/udevmand/src/bridge.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2020 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | #include <linux/if_bridge.h> | ||||||
|  |  | ||||||
|  | #define BR_MAX_ENTRY	2048 | ||||||
|  |  | ||||||
|  | static struct uloop_timeout bridge_timer; | ||||||
|  | static struct vlist_tree bridge_mac; | ||||||
|  | static LIST_HEAD(bridge_if); | ||||||
|  |  | ||||||
|  | struct bridge_if { | ||||||
|  | 	struct list_head list; | ||||||
|  |  | ||||||
|  | 	char name[IF_NAMESIZE]; | ||||||
|  | 	unsigned int port_no; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | bridge_read_mac(const char *bridge) | ||||||
|  | { | ||||||
|  | 	FILE *fd; | ||||||
|  | 	int i, cnt; | ||||||
|  | 	struct __fdb_entry fe[BR_MAX_ENTRY]; | ||||||
|  | 	char path[PATH_MAX]; | ||||||
|  |  | ||||||
|  | 	snprintf(path, PATH_MAX, "/sys/class/net/%s/brforward", bridge); | ||||||
|  | 	fd = fopen(path, "r"); | ||||||
|  | 	if (!fd) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	cnt = fread(fe, sizeof(struct __fdb_entry), BR_MAX_ENTRY, fd); | ||||||
|  | 	fclose(fd); | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < cnt; i++) { | ||||||
|  | 		struct bridge_mac *b = malloc(sizeof(*b)); | ||||||
|  | 		struct bridge_if *brif; | ||||||
|  |  | ||||||
|  | 		if (!b) | ||||||
|  | 			continue; | ||||||
|  | 		strncpy(b->bridge, bridge, IF_NAMESIZE); | ||||||
|  | 		strncpy(b->ifname, bridge, IF_NAMESIZE); | ||||||
|  | 		list_for_each_entry(brif, &bridge_if, list) | ||||||
|  | 			if (fe[i].port_no == brif->port_no) | ||||||
|  | 				strncpy(b->ifname, brif->name, IF_NAMESIZE); | ||||||
|  | 		memcpy(b->addr, fe[i].mac_addr, ETH_ALEN); | ||||||
|  | 		b->port_no = fe[i].port_no; | ||||||
|  | 		vlist_add(&bridge_mac, &b->vlist, (void *) b); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | bridge_dump_if(const char *bridge) | ||||||
|  | { | ||||||
|  | 	char path[PATH_MAX]; | ||||||
|  | 	void *c = NULL; | ||||||
|  | 	glob_t gl; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	snprintf(path, PATH_MAX, "/sys/class/net/%s/brif/*", bridge); | ||||||
|  | 	if (glob(path, GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT, NULL, &gl)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < gl.gl_pathc; i++) { | ||||||
|  | 		if (!c) | ||||||
|  | 			c = blobmsg_open_array(&b, "bridge"); | ||||||
|  |  | ||||||
|  | 		blobmsg_add_string(&b, NULL, basename(gl.gl_pathv[i])); | ||||||
|  | 	} | ||||||
|  | 	if (c) | ||||||
|  | 		blobmsg_close_array(&b, c); | ||||||
|  |  | ||||||
|  | 	globfree(&gl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | bridge_read_if(const char *bridge) | ||||||
|  | { | ||||||
|  | 	struct bridge_if *brif; | ||||||
|  | 	char path[PATH_MAX]; | ||||||
|  | 	glob_t gl; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	snprintf(path, PATH_MAX, "/sys/class/net/%s/brif/*", bridge); | ||||||
|  | 	if (glob(path, GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT, NULL, &gl)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < gl.gl_pathc; i++) { | ||||||
|  | 		unsigned int port_no; | ||||||
|  | 		FILE *fd; | ||||||
|  | 		int ret; | ||||||
|  |  | ||||||
|  | 		snprintf(path, PATH_MAX, "/sys/class/net/%s/brif/%s/port_no", bridge, basename(gl.gl_pathv[i])); | ||||||
|  | 		fd = fopen(path, "r"); | ||||||
|  | 		if (!fd) | ||||||
|  | 			continue; | ||||||
|  | 		ret = fscanf(fd, "0x%x", &port_no); | ||||||
|  | 		fclose(fd); | ||||||
|  | 		if (ret != 1) | ||||||
|  | 			continue; | ||||||
|  | 		brif = malloc(sizeof(*brif)); | ||||||
|  | 		if (!brif) | ||||||
|  | 			goto out; | ||||||
|  | 		strcpy(brif->name, basename(gl.gl_pathv[i])); | ||||||
|  | 		brif->port_no = port_no; | ||||||
|  | 		list_add(&brif->list, &bridge_if); | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	globfree(&gl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bridge_tout(struct uloop_timeout *t) | ||||||
|  | { | ||||||
|  | 	struct bridge_if *brif, *tmp; | ||||||
|  | 	glob_t gl; | ||||||
|  |  | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	if (glob("/sys/class/net/*", GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT, NULL, &gl)) | ||||||
|  | 		goto out; | ||||||
|  |  | ||||||
|  | 	list_for_each_entry_safe(brif, tmp, &bridge_if, list) { | ||||||
|  | 		list_del(&brif->list); | ||||||
|  | 		free(brif); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < gl.gl_pathc; i++) | ||||||
|  | 		bridge_read_if(basename(gl.gl_pathv[i])); | ||||||
|  | 	vlist_update(&bridge_mac); | ||||||
|  | 	for (i = 0; i < gl.gl_pathc; i++) | ||||||
|  | 		bridge_read_mac(basename(gl.gl_pathv[i])); | ||||||
|  | 	vlist_flush(&bridge_mac); | ||||||
|  | 	globfree(&gl); | ||||||
|  |  | ||||||
|  | out: | ||||||
|  | 	uloop_timeout_set(&bridge_timer, 1000); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bridge_cmp(const void *k1, const void *k2, void *ptr) | ||||||
|  | { | ||||||
|  | 	const struct bridge_mac *b1 = (const struct bridge_mac *)k1; | ||||||
|  | 	const struct bridge_mac *b2 = (const struct bridge_mac *)k2; | ||||||
|  |  | ||||||
|  | 	return memcmp(b1->addr, b2->addr, ETH_ALEN); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bridge_update(struct vlist_tree *tree, struct vlist_node *node_new, struct vlist_node *node_old) | ||||||
|  | { | ||||||
|  | 	struct bridge_mac *b1, *b2; | ||||||
|  |  | ||||||
|  | 	b1 = container_of(node_old, struct bridge_mac, vlist); | ||||||
|  | 	b2 = container_of(node_new, struct bridge_mac, vlist); | ||||||
|  |  | ||||||
|  | 	if (!!b1 != !!b2) { | ||||||
|  | 		struct bridge_mac *_b = b1 ? b1 : b2; | ||||||
|  |  | ||||||
|  | 		ULOG_INFO("%s fdb %s:%d "MAC_FMT"\n", b1 ? "del" : "new", _b->ifname, _b->port_no, MAC_VAR(_b->addr)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (b1) { | ||||||
|  | 		list_del(&b1->mac); | ||||||
|  | 		free(b1); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (b2) { | ||||||
|  | 		struct mac *mac = NULL; | ||||||
|  |  | ||||||
|  | 		mac = mac_find(b2->addr); | ||||||
|  | 		mac_update(mac, b2->bridge); | ||||||
|  | 		list_add(&b2->mac, &mac->bridge_mac); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void bridge_init(void) | ||||||
|  | { | ||||||
|  | 	bridge_timer.cb = bridge_tout; | ||||||
|  | 	uloop_timeout_set(&bridge_timer, 1000); | ||||||
|  | 	vlist_init(&bridge_mac, bridge_cmp, bridge_update); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void bridge_flush(void) | ||||||
|  | { | ||||||
|  | 	struct bridge_if *brif, *tmp; | ||||||
|  |  | ||||||
|  | 	vlist_flush_all(&bridge_mac); | ||||||
|  | 	list_for_each_entry_safe(brif, tmp, &bridge_if, list) { | ||||||
|  | 		list_del(&brif->list); | ||||||
|  | 		free(brif); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | bridge_mac_del(struct bridge_mac *b) | ||||||
|  | { | ||||||
|  | 	list_del(&b->mac); | ||||||
|  | 	vlist_delete(&bridge_mac, &b->vlist); | ||||||
|  | 	free(b); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										161
									
								
								feeds/ucentral/udevmand/src/dhcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								feeds/ucentral/udevmand/src/dhcp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	DHCP_MAC, | ||||||
|  | 	DHCP_IP, | ||||||
|  | 	DHCP_NAME, | ||||||
|  | 	DHCP_IFACE, | ||||||
|  | 	__DHCP_MAX | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct blobmsg_policy dhcp_policy[__DHCP_MAX] = { | ||||||
|  | 	[DHCP_MAC] = { .name = "mac", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | 	[DHCP_IP] = { .name = "ip", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | 	[DHCP_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | 	[DHCP_IFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct avl_tree dhcpv4_tree = AVL_TREE_INIT(dhcpv4_tree, avl_mac_cmp, false, NULL); | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | dhcpv4_add(uint8_t *addr, uint8_t *ip, char *name, char *iface) | ||||||
|  | { | ||||||
|  | 	struct dhcpv4 *dhcpv4; | ||||||
|  | 	struct mac *mac; | ||||||
|  |  | ||||||
|  | 	mac = mac_find(addr); | ||||||
|  |  | ||||||
|  | 	dhcpv4 = avl_find_element(&dhcpv4_tree, addr, dhcpv4, avl); | ||||||
|  | 	if (dhcpv4) { | ||||||
|  | 		list_del(&dhcpv4->mac); | ||||||
|  | 		avl_delete(&dhcpv4_tree, &dhcpv4->avl); | ||||||
|  | 		free(dhcpv4); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dhcpv4 = malloc(sizeof(*dhcpv4) + (name ? strlen(name) + 1 : 1)); | ||||||
|  |         if (!dhcpv4) | ||||||
|  |                 return; | ||||||
|  | 	if (iface) | ||||||
|  | 		strncpy(dhcpv4->iface, iface, sizeof(dhcpv4->iface)); | ||||||
|  | 	else | ||||||
|  | 		*dhcpv4->iface = '\0'; | ||||||
|  | 	if (name) | ||||||
|  | 		strcpy(dhcpv4->name, name); | ||||||
|  | 	else | ||||||
|  | 		*dhcpv4->name = '\0'; | ||||||
|  | 	memcpy(dhcpv4->ip, ip, 4); | ||||||
|  | 	memcpy(dhcpv4->addr, addr, ETH_ALEN); | ||||||
|  | 	dhcpv4->avl.key = dhcpv4->addr; | ||||||
|  |  | ||||||
|  | 	avl_insert(&dhcpv4_tree, &dhcpv4->avl); | ||||||
|  | 	list_add(&dhcpv4->mac, &mac->dhcpv4); | ||||||
|  | 	mac_update(mac, dhcpv4->iface); | ||||||
|  | 	ULOG_INFO("new dhcpv4 " MAC_FMT "/" IP_FMT " for %s\n", | ||||||
|  | 		  MAC_VAR(dhcpv4->addr), IP_VAR(ip), | ||||||
|  | 		  strlen(dhcpv4->name) ? dhcpv4->name : "<unknown>"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dhcpv4_ack(struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__DHCP_MAX]; | ||||||
|  | 	uint8_t addr[ETH_ALEN], ip[4]; | ||||||
|  | 	char *name = NULL; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(dhcp_policy, __DHCP_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (!tb[DHCP_MAC] || !tb[DHCP_IP] || !tb[DHCP_IFACE]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	ret = sscanf(blobmsg_get_string(tb[DHCP_MAC]), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | ||||||
|  | 		     &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]); | ||||||
|  | 	if (ret != 6) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	ret = sscanf(blobmsg_get_string(tb[DHCP_IP]), "%hhu.%hhu.%hhu.%hhu", | ||||||
|  | 		     &ip[0], &ip[1], &ip[2], &ip[3]); | ||||||
|  | 	if (ret != 4) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (tb[DHCP_NAME]) | ||||||
|  | 		name = blobmsg_get_string(tb[DHCP_NAME]); | ||||||
|  |  | ||||||
|  | 	dhcpv4_add(addr, ip, name, blobmsg_get_string(tb[DHCP_IFACE])); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dhcpv4_release(struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__DHCP_MAX]; | ||||||
|  | 	struct dhcpv4 *dhcpv4; | ||||||
|  | 	uint8_t addr[ETH_ALEN]; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(dhcp_policy, __DHCP_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (!tb[DHCP_MAC]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	ret = sscanf(blobmsg_get_string(tb[DHCP_MAC]), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | ||||||
|  | 		     &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]); | ||||||
|  | 	if (ret != 6) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	dhcpv4 = avl_find_element(&dhcpv4_tree, addr, dhcpv4, avl); | ||||||
|  | 	if (!dhcpv4) | ||||||
|  | 		return; | ||||||
|  | 	ULOG_INFO("del dhcpv4 " MAC_FMT "\n", MAC_VAR(dhcpv4->addr)); | ||||||
|  | 	list_del(&dhcpv4->mac); | ||||||
|  | 	avl_delete(&dhcpv4_tree, &dhcpv4->avl); | ||||||
|  | 	free(dhcpv4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dhcpv4_del(struct dhcpv4 *dhcpv4) | ||||||
|  | { | ||||||
|  | 	list_del(&dhcpv4->mac); | ||||||
|  | 	avl_delete(&dhcpv4_tree, &dhcpv4->avl); | ||||||
|  | 	free(dhcpv4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dhcp_init(void) | ||||||
|  | { | ||||||
|  | 	FILE *fp = fopen("/tmp/dhcp.leases", "r"); | ||||||
|  | 	char line[1024]; | ||||||
|  |  | ||||||
|  | 	if (!fp) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	while (fgets(line, sizeof(line), fp)) { | ||||||
|  | 		uint8_t addr[ETH_ALEN], ip[4]; | ||||||
|  | 		char hostname[256 + 1]; | ||||||
|  | 		long int timestamp; | ||||||
|  | 		int ret; | ||||||
|  |  | ||||||
|  | 		ret = sscanf(line, "%ld %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu.%hhu.%hhu.%hhu %s", | ||||||
|  | 			     ×tamp, | ||||||
|  | 			     &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5], | ||||||
|  | 			     &ip[0], &ip[1], &ip[2], &ip[3], | ||||||
|  | 			     hostname); | ||||||
|  | 		if (ret != 12) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		dhcpv4_add(addr, ip, hostname, NULL); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fclose(fp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dhcp_done(void) | ||||||
|  | { | ||||||
|  | 	struct dhcpv4 *d, *t; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element_safe(&dhcpv4_tree, d, avl, t) { | ||||||
|  | 		avl_delete(&dhcpv4_tree, &d->avl); | ||||||
|  | 		free(d); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								feeds/ucentral/udevmand/src/ethers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								feeds/ucentral/udevmand/src/ethers.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ethers_init(void) | ||||||
|  | { | ||||||
|  | 	char buf[512], *p; | ||||||
|  | 	int ret; | ||||||
|  | 	FILE *f; | ||||||
|  |  | ||||||
|  | 	f = fopen("/etc/ethers", "r"); | ||||||
|  |  | ||||||
|  | 	if (!f) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	while (fgets(buf, sizeof(buf), f)) { | ||||||
|  | 		uint8_t addr[ETH_ALEN]; | ||||||
|  | 		struct mac *mac; | ||||||
|  |  | ||||||
|  | 		p = strtok(buf, " \t\n"); | ||||||
|  | 		ret = sscanf(p, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | ||||||
|  | 			     &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]); | ||||||
|  | 		if (ret != 6) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		p = strtok(NULL, " \t\n"); | ||||||
|  |  | ||||||
|  | 		if (!p) | ||||||
|  | 			continue; | ||||||
|  | 		mac = mac_find(addr); | ||||||
|  | 		mac->ethers = strdup(p); | ||||||
|  | 		ULOG_INFO("new ethers " MAC_FMT" %s\n", MAC_VAR(addr), p); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fclose(f); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										158
									
								
								feeds/ucentral/udevmand/src/mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								feeds/ucentral/udevmand/src/mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | int | ||||||
|  | avl_mac_cmp(const void *k1, const void *k2, void *ptr) | ||||||
|  | { | ||||||
|  | 	return memcmp(k1, k2, ETH_ALEN); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct avl_tree mac_tree = AVL_TREE_INIT(mac_tree, avl_mac_cmp, false, NULL); | ||||||
|  |  | ||||||
|  | struct mac* | ||||||
|  | mac_find(uint8_t *addr) | ||||||
|  | { | ||||||
|  | 	struct mac *mac; | ||||||
|  | 	uint8_t *addr_buf; | ||||||
|  |  | ||||||
|  | 	mac = avl_find_element(&mac_tree, addr, mac, avl); | ||||||
|  |  | ||||||
|  | 	if (mac) | ||||||
|  | 		return mac; | ||||||
|  |  | ||||||
|  | 	mac = calloc_a(sizeof(struct mac), &addr_buf, 6); | ||||||
|  | 	if (!mac) | ||||||
|  | 		return NULL; | ||||||
|  | 	mac->addr = memcpy(addr_buf, addr, ETH_ALEN); | ||||||
|  | 	mac->avl.key = mac->addr; | ||||||
|  | 	mac->ethers = NULL; | ||||||
|  | 	*mac->interface = '\0'; | ||||||
|  | 	INIT_LIST_HEAD(&mac->neigh4); | ||||||
|  | 	INIT_LIST_HEAD(&mac->neigh6); | ||||||
|  | 	INIT_LIST_HEAD(&mac->dhcpv4); | ||||||
|  | 	INIT_LIST_HEAD(&mac->bridge_mac); | ||||||
|  |  | ||||||
|  | 	avl_insert(&mac_tree, &mac->avl); | ||||||
|  | 	ULOG_INFO("new mac "MAC_FMT"\n", MAC_VAR(mac->addr)); | ||||||
|  |  | ||||||
|  | 	return mac; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | mac_update(struct mac *mac, char *iface) | ||||||
|  | { | ||||||
|  | 	char *interface = interface_resolve(iface); | ||||||
|  |  | ||||||
|  | 	if (iface && *iface) | ||||||
|  | 		strncpy(mac->interface, interface, sizeof(mac->interface)); | ||||||
|  |  | ||||||
|  | 	clock_gettime(CLOCK_MONOTONIC, &mac->ts); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | mac_dump(struct mac *mac, int interface) | ||||||
|  | { | ||||||
|  | 	struct timespec ts; | ||||||
|  | 	time_t last_seen; | ||||||
|  | 	char buf[18]; | ||||||
|  | 	void *c, *d; | ||||||
|  |  | ||||||
|  | 	neigh_enum(); | ||||||
|  |  | ||||||
|  | 	clock_gettime(CLOCK_MONOTONIC, &ts); | ||||||
|  |  | ||||||
|  | 	last_seen = ts.tv_sec - mac->ts.tv_sec; | ||||||
|  | 	snprintf(buf, sizeof(buf), MAC_FMT, MAC_VAR(mac->addr)); | ||||||
|  | 	c = blobmsg_open_table(&b, buf); | ||||||
|  | 	if (interface && *mac->interface) | ||||||
|  | 		blobmsg_add_string(&b, "interface", mac->interface); | ||||||
|  | 	if (mac->ethers) | ||||||
|  | 		blobmsg_add_string(&b, "ethers", mac->ethers); | ||||||
|  | 	if (last_seen < 5 * 60) | ||||||
|  | 		blobmsg_add_u32(&b, "last_seen", last_seen); | ||||||
|  | 	else | ||||||
|  | 		blobmsg_add_u8(&b, "offline", 1); | ||||||
|  | 	if (!list_empty(&mac->neigh4)) { | ||||||
|  | 		struct neigh *neigh; | ||||||
|  |  | ||||||
|  | 		d = blobmsg_open_array(&b, "ipv4"); | ||||||
|  | 		list_for_each_entry(neigh, &mac->neigh4, list) | ||||||
|  | 			blobmsg_add_ipv4(&b, NULL, neigh->ip); | ||||||
|  | 		blobmsg_close_array(&b, d); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!list_empty(&mac->neigh6)) { | ||||||
|  | 		struct neigh *neigh; | ||||||
|  |  | ||||||
|  | 		d = blobmsg_open_array(&b, "ipv6"); | ||||||
|  | 		list_for_each_entry(neigh, &mac->neigh6, list) | ||||||
|  | 			blobmsg_add_ipv6(&b, NULL, neigh->ip); | ||||||
|  | 		blobmsg_close_array(&b, d); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!list_empty(&mac->dhcpv4)) { | ||||||
|  | 		struct dhcpv4 *dhcpv4; | ||||||
|  |  | ||||||
|  | 		d = blobmsg_open_array(&b, "dhcpv4"); | ||||||
|  | 		list_for_each_entry(dhcpv4, &mac->dhcpv4, mac) { | ||||||
|  | 			blobmsg_add_ipv4(&b, NULL, dhcpv4->ip); | ||||||
|  | 			if (strlen(dhcpv4->name) > 0) | ||||||
|  | 				blobmsg_add_string(&b, NULL, dhcpv4->name); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		blobmsg_close_array(&b, d); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!list_empty(&mac->bridge_mac)) { | ||||||
|  | 		struct bridge_mac *bridge_mac; | ||||||
|  |  | ||||||
|  | 		d = blobmsg_open_array(&b, "fdb"); | ||||||
|  | 		list_for_each_entry(bridge_mac, &mac->bridge_mac, mac) | ||||||
|  | 			blobmsg_add_string(&b, NULL, bridge_mac->ifname); | ||||||
|  | 		blobmsg_close_array(&b, d); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	blobmsg_close_array(&b, c); | ||||||
|  | 	neigh_flush(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | mac_flush(struct mac *mac, struct timespec ts) | ||||||
|  | { | ||||||
|  | 	time_t last_seen; | ||||||
|  |  | ||||||
|  | 	last_seen = ts.tv_sec - mac->ts.tv_sec; | ||||||
|  | 	if (last_seen <  60 * 60) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (!list_empty(&mac->dhcpv4)) { | ||||||
|  | 		struct dhcpv4 *dhcpv4, *t; | ||||||
|  |  | ||||||
|  | 		list_for_each_entry_safe(dhcpv4, t, &mac->dhcpv4, mac) | ||||||
|  | 			dhcpv4_del(dhcpv4); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!list_empty(&mac->bridge_mac)) { | ||||||
|  | 		struct bridge_mac *bridge_mac, *t; | ||||||
|  |  | ||||||
|  | 		list_for_each_entry_safe(bridge_mac, t, &mac->bridge_mac, mac) | ||||||
|  | 			bridge_mac_del(bridge_mac); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int | ||||||
|  | mac_dump_all(void) | ||||||
|  | { | ||||||
|  | 	struct mac *mac, *t; | ||||||
|  | 	struct timespec ts; | ||||||
|  |  | ||||||
|  | 	blob_buf_init(&b, 0); | ||||||
|  |  | ||||||
|  | 	avl_for_each_element(&mac_tree, mac, avl) | ||||||
|  | 		mac_dump(mac, 1); | ||||||
|  |  | ||||||
|  | 	clock_gettime(CLOCK_MONOTONIC, &ts); | ||||||
|  | 	avl_for_each_element_safe(&mac_tree, mac, avl, t) | ||||||
|  | 		mac_flush(mac, ts); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								feeds/ucentral/udevmand/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								feeds/ucentral/udevmand/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2020 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	ulog_open(ULOG_STDIO, LOG_DAEMON, "udevmand"); | ||||||
|  |  | ||||||
|  | 	uloop_init(); | ||||||
|  | 	ubus_init(); | ||||||
|  | 	ethers_init(); | ||||||
|  | 	neigh_init(); | ||||||
|  | 	bridge_init(); | ||||||
|  | 	dhcp_init(); | ||||||
|  | 	uloop_run(); | ||||||
|  | 	uloop_done(); | ||||||
|  | 	ubus_uninit(); | ||||||
|  | 	bridge_flush(); | ||||||
|  | 	blob_buf_free(&b); | ||||||
|  | 	neigh_done(); | ||||||
|  | 	interface_done(); | ||||||
|  | 	dhcp_done(); | ||||||
|  | 	iface_done(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										189
									
								
								feeds/ucentral/udevmand/src/neigh.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								feeds/ucentral/udevmand/src/neigh.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2020 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | static struct nl_socket rtnl_sock; | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | avl_ipv4_cmp(const void *k1, const void *k2, void *ptr) | ||||||
|  | { | ||||||
|  | 	return memcmp(k1, k2, 4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | avl_ipv6_cmp(const void *k1, const void *k2, void *ptr) | ||||||
|  | { | ||||||
|  | 	return memcmp(k1, k2, 16); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct avl_tree ip4_tree = AVL_TREE_INIT(ip4_tree, avl_ipv4_cmp, false, NULL); | ||||||
|  | static struct avl_tree ip6_tree = AVL_TREE_INIT(ip6_tree, avl_ipv6_cmp, false, NULL); | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | neigh_del(struct neigh *neigh) | ||||||
|  | { | ||||||
|  | 	struct avl_tree *ip_tree = &ip4_tree; | ||||||
|  |  | ||||||
|  | 	if (neigh->ip_ver == 6) | ||||||
|  | 		ip_tree = &ip6_tree; | ||||||
|  |  | ||||||
|  | 	uloop_timeout_cancel(&neigh->ageing); | ||||||
|  | 	list_del(&neigh->list); | ||||||
|  | 	avl_delete(ip_tree, &neigh->avl); | ||||||
|  | 	free(neigh); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | neigh_ageing_cb(struct uloop_timeout *t) | ||||||
|  | { | ||||||
|  | 	struct neigh *neigh = container_of(t, struct neigh, ageing); | ||||||
|  |  | ||||||
|  | 	neigh_del(neigh); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | neigh_handler(struct nlmsghdr *nh, int type) | ||||||
|  | { | ||||||
|  | 	uint8_t *lladdr, *dummy[6] = { 0 }; | ||||||
|  | 	uint8_t set[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||||||
|  | 	uint8_t clear[6] = { }; | ||||||
|  | 	uint8_t mcast[2] = { 0x33, 0x33 }; | ||||||
|  | 	struct ndmsg *ndm = nlmsg_data(nh); | ||||||
|  | 	struct nlattr *nda[__NDA_MAX]; | ||||||
|  | 	uint8_t *mac_buf, *ip_buf; | ||||||
|  | 	struct avl_tree *ip_tree; | ||||||
|  | 	struct neigh *neigh; | ||||||
|  | 	struct mac *mac; | ||||||
|  | 	int ip_len, ip_ver, refresh = 0; | ||||||
|  | 	void *dst; | ||||||
|  |  | ||||||
|  | 	nlmsg_parse(nh, sizeof(struct ndmsg), nda, NDA_MAX, NULL); | ||||||
|  | 	if (!nda[NDA_DST]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	dst = nla_data(nda[NDA_DST]); | ||||||
|  | 	lladdr = nda[NDA_LLADDR] ? nla_data(nda[NDA_LLADDR]) : dummy; | ||||||
|  | 	if (!memcmp(lladdr, set, 6) || !memcmp(lladdr, clear, 6) || !memcmp(lladdr, mcast, 2) || *lladdr == 0x1) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	mac = mac_find(lladdr); | ||||||
|  |  | ||||||
|  | 	switch (ndm->ndm_family) { | ||||||
|  | 	case AF_INET: | ||||||
|  | 		ip_len = ip_ver = 4; | ||||||
|  | 		ip_tree = &ip4_tree; | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case AF_INET6: | ||||||
|  | 		ip_len = 16; | ||||||
|  | 		ip_ver = 6; | ||||||
|  | 		ip_tree = &ip6_tree; | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	neigh = avl_find_element(ip_tree, dst, neigh, avl); | ||||||
|  |  | ||||||
|  | 	if (neigh) { | ||||||
|  | 		if (!type) | ||||||
|  | 			ULOG_INFO("del ipv%d for "MAC_FMT" on %s\n", neigh->ip_ver,  MAC_VAR(lladdr), neigh->ifname); | ||||||
|  | 		refresh = 1; | ||||||
|  | 		neigh_del(neigh); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!type) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	neigh = calloc_a(sizeof(struct neigh), | ||||||
|  | 		&ip_buf, ip_len, &mac_buf, 6); | ||||||
|  | 	if (!neigh) | ||||||
|  | 		return; | ||||||
|  | 	neigh->ip = memcpy(ip_buf, dst, ip_len); | ||||||
|  | 	neigh->iface = ndm->ndm_ifindex; | ||||||
|  | 	neigh->avl.key = neigh->ip; | ||||||
|  | 	neigh->ip_ver = ip_ver; | ||||||
|  | 	neigh->ageing.cb = neigh_ageing_cb; | ||||||
|  | 	uloop_timeout_set(&neigh->ageing, 24 * 60 * 60 * 1000); | ||||||
|  | 	if_indextoname(neigh->iface, neigh->ifname); | ||||||
|  | 	if (neigh->ip_ver == 4) | ||||||
|  | 		list_add(&neigh->list, &mac->neigh4); | ||||||
|  | 	else | ||||||
|  | 		list_add(&neigh->list, &mac->neigh6); | ||||||
|  | 	avl_insert(ip_tree, &neigh->avl); | ||||||
|  | 	mac_update(mac, neigh->ifname); | ||||||
|  | 	if (!refresh) | ||||||
|  | 		ULOG_INFO("new ipv%d for "MAC_FMT" on %s\n", neigh->ip_ver,  MAC_VAR(lladdr), neigh->ifname); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | neigh_netlink_cb(struct nl_msg *msg, void *arg) | ||||||
|  | { | ||||||
|  |  | ||||||
|  | 	struct nlmsghdr *nh = nlmsg_hdr(msg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	switch (nh->nlmsg_type) { | ||||||
|  | 	case RTM_NEWNEIGH: | ||||||
|  | 		neigh_handler(nh, 1); | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case RTM_DELNEIGH: | ||||||
|  | 		neigh_handler(nh, 0); | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return NL_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | neigh_enum(void) | ||||||
|  | { | ||||||
|  | 	struct rtgenmsg msg = { .rtgen_family = AF_UNSPEC }; | ||||||
|  |  | ||||||
|  | 	nl_send_simple(rtnl_sock.sock, RTM_GETNEIGH, NLM_F_DUMP, &msg, sizeof(msg)); | ||||||
|  | 	nl_wait_for_ack(rtnl_sock.sock); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | neigh_flush(void) | ||||||
|  | { | ||||||
|  | 	struct neigh *neigh, *tmp; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element_safe(&ip4_tree, neigh, avl, tmp) | ||||||
|  | 		neigh_del(neigh); | ||||||
|  | 	avl_for_each_element_safe(&ip6_tree, neigh, avl, tmp) | ||||||
|  | 		neigh_del(neigh); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int | ||||||
|  | neigh_init(void) | ||||||
|  | { | ||||||
|  | 	ULOG_INFO("open neigh netlink socket\n"); | ||||||
|  | 	if (!nl_status_socket(&rtnl_sock, NETLINK_ROUTE, neigh_netlink_cb, NULL)) { | ||||||
|  | 		ULOG_ERR("failed to open rtnl socket\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | neigh_done(void) | ||||||
|  | { | ||||||
|  | 	nl_socket_free(rtnl_sock.sock); | ||||||
|  | } | ||||||
							
								
								
									
										235
									
								
								feeds/ucentral/udevmand/src/netdev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								feeds/ucentral/udevmand/src/netdev.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | |||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | struct avl_tree iface_tree = AVL_TREE_INIT(iface_tree, avl_strcmp, false, NULL); | ||||||
|  |  | ||||||
|  | struct iface_ip { | ||||||
|  | 	struct list_head list; | ||||||
|  | 	char ip[]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct iface { | ||||||
|  | 	struct avl_node avl; | ||||||
|  | 	int alive; | ||||||
|  | 	char name[IF_NAMESIZE]; | ||||||
|  | 	struct rtnl_link_stats stats; | ||||||
|  | 	struct rtnl_link_stats stats_old; | ||||||
|  | 	struct list_head ipv6, ipv4; | ||||||
|  | 	uint8_t addr[ETH_ALEN]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct iface* | ||||||
|  | iface_find(char *name) | ||||||
|  | { | ||||||
|  | 	struct iface *iface = avl_find_element(&iface_tree, name, iface, avl); | ||||||
|  |  | ||||||
|  | 	if (iface) { | ||||||
|  | 		iface->alive = 1; | ||||||
|  | 		return iface; | ||||||
|  | 	} | ||||||
|  | 	iface = malloc(sizeof(*iface)); | ||||||
|  | 	if (!iface) | ||||||
|  | 		return NULL; | ||||||
|  | 	memset(iface, 0, sizeof(*iface)); | ||||||
|  | 	iface->alive = 1; | ||||||
|  | 	iface->avl.key = strcpy(iface->name, name); | ||||||
|  | 	INIT_LIST_HEAD(&iface->ipv4); | ||||||
|  | 	INIT_LIST_HEAD(&iface->ipv6); | ||||||
|  | 	avl_insert(&iface_tree, &iface->avl); | ||||||
|  | 	return iface; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | iface_done(void) | ||||||
|  | { | ||||||
|  | 	struct iface *i, *t; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element_safe(&iface_tree, i, avl, t) { | ||||||
|  | 		avl_delete(&iface_tree, &i->avl); | ||||||
|  | 		free(i); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | iface_get(void) | ||||||
|  | { | ||||||
|  | 	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); | ||||||
|  | 	struct ifaddrs *ifaddr, *ifa; | ||||||
|  | 	int n; | ||||||
|  |  | ||||||
|  | 	if (getifaddrs(&ifaddr) == -1) { | ||||||
|  | 		ULOG_ERR("failed to getifaddrs\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { | ||||||
|  | 		char host[NI_MAXHOST]; | ||||||
|  | 		struct iface *iface; | ||||||
|  | 		int family, s; | ||||||
|  |  | ||||||
|  | 		if (ifa->ifa_addr == NULL) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		if ((ifa->ifa_flags & IFF_UP) == 0) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		if (ifa->ifa_flags & IFF_LOOPBACK) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		family = ifa->ifa_addr->sa_family; | ||||||
|  |  | ||||||
|  | 		iface = iface_find(ifa->ifa_name); | ||||||
|  | 		if (!iface) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		if (family == AF_INET || family == AF_INET6) { | ||||||
|  | 			struct iface_ip *ip; | ||||||
|  |  | ||||||
|  | 			s = getnameinfo(ifa->ifa_addr, | ||||||
|  | 					(family == AF_INET) ? sizeof(struct sockaddr_in) : | ||||||
|  | 					sizeof(struct sockaddr_in6), | ||||||
|  | 					host, NI_MAXHOST, | ||||||
|  | 					NULL, 0, NI_NUMERICHOST); | ||||||
|  | 			if (s) | ||||||
|  | 				continue; | ||||||
|  | 			ip = malloc(sizeof(*ip) + strlen(host) + 1); | ||||||
|  | 			if (!ip) | ||||||
|  | 				continue; | ||||||
|  | 			strcpy(ip->ip, host); | ||||||
|  | 			if (family == AF_INET) | ||||||
|  | 				list_add(&ip->list, &iface->ipv4); | ||||||
|  | 			else | ||||||
|  | 				list_add(&ip->list, &iface->ipv6); | ||||||
|  | 		} else if (family == AF_PACKET && ifa->ifa_data != NULL) { | ||||||
|  | 			struct rtnl_link_stats *stats = ifa->ifa_data; | ||||||
|  | 			struct ifreq s; | ||||||
|  |  | ||||||
|  | 			memcpy(&iface->stats, stats, sizeof(*stats)); | ||||||
|  | 			memset(iface->addr, 0, ETH_ALEN); | ||||||
|  | 			if (fd) { | ||||||
|  | 				strcpy(s.ifr_name, ifa->ifa_name); | ||||||
|  | 				if (!ioctl(fd, SIOCGIFHWADDR, &s)) | ||||||
|  | 					memcpy(iface->addr, s.ifr_addr.sa_data, ETH_ALEN); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	freeifaddrs(ifaddr); | ||||||
|  | 	if (fd) | ||||||
|  | 		close(fd); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | iface_flush(void) | ||||||
|  | { | ||||||
|  | 	struct iface *iface, *tmp; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element_safe(&iface_tree, iface, avl, tmp) { | ||||||
|  | 		struct iface_ip *ip, *tmp; | ||||||
|  |  | ||||||
|  | 		list_for_each_entry_safe(ip, tmp, &iface->ipv4, list) { | ||||||
|  | 			list_del(&ip->list); | ||||||
|  | 			free(ip); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		list_for_each_entry_safe(ip, tmp, &iface->ipv6, list) { | ||||||
|  | 			list_del(&ip->list); | ||||||
|  | 			free(ip); | ||||||
|  | 		} | ||||||
|  | 		if (!iface->alive) { | ||||||
|  | 			avl_delete(&iface_tree, &iface->avl); | ||||||
|  | 			free(iface); | ||||||
|  | 		} else { | ||||||
|  | 			iface->alive = 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int get_counter_delta(__u32 new, __u32 old) | ||||||
|  | { | ||||||
|  | 	uint32_t delta; | ||||||
|  |  | ||||||
|  | 	if (new < old) | ||||||
|  | 		delta = UINT32_MAX - old + new; | ||||||
|  | 	else | ||||||
|  | 		delta = new - old; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	return delta > 0 ? delta : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | iface_dump(int delta) | ||||||
|  | { | ||||||
|  | 	struct iface *iface; | ||||||
|  |  | ||||||
|  | 	iface_get(); | ||||||
|  |  | ||||||
|  | 	blob_buf_init(&b, 0); | ||||||
|  | 	avl_for_each_element(&iface_tree, iface, avl) { | ||||||
|  | 		struct iface_ip *ip; | ||||||
|  | 		void *c, *d; | ||||||
|  |  | ||||||
|  | 		if (!iface->alive) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		c = blobmsg_open_table(&b, interface_resolve(iface->name)); | ||||||
|  | 		blobmsg_add_mac(&b, "hwaddr", iface->addr); | ||||||
|  | 		if (!list_empty(&iface->ipv4)) { | ||||||
|  | 			d = blobmsg_open_array(&b, "ipv4"); | ||||||
|  | 			list_for_each_entry(ip, &iface->ipv4, list) | ||||||
|  | 				blobmsg_add_string(&b, NULL, ip->ip); | ||||||
|  | 			blobmsg_close_array(&b, d); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (!list_empty(&iface->ipv4)) { | ||||||
|  | 			d = blobmsg_open_array(&b, "ipv6"); | ||||||
|  | 			list_for_each_entry(ip, &iface->ipv6, list) | ||||||
|  | 				blobmsg_add_string(&b, NULL, ip->ip); | ||||||
|  | 			blobmsg_close_array(&b, d); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		d = blobmsg_open_table(&b, "counters"); | ||||||
|  | 		blobmsg_add_u64(&b, "rx_packets", iface->stats.rx_packets); | ||||||
|  | 		blobmsg_add_u64(&b, "tx_packets", iface->stats.tx_packets); | ||||||
|  | 	        blobmsg_add_u64(&b, "rx_bytes", iface->stats.rx_bytes); | ||||||
|  | 	        blobmsg_add_u64(&b, "tx_bytes", iface->stats.tx_bytes); | ||||||
|  | 	        blobmsg_add_u64(&b, "rx_errors", iface->stats.rx_errors); | ||||||
|  | 	        blobmsg_add_u64(&b, "tx_errors", iface->stats.tx_errors); | ||||||
|  | 	        blobmsg_add_u64(&b, "rx_dropped", iface->stats.rx_dropped); | ||||||
|  | 	        blobmsg_add_u64(&b, "tx_dropped", iface->stats.tx_dropped); | ||||||
|  | 	        blobmsg_add_u64(&b, "multicast", iface->stats.multicast); | ||||||
|  | 	        blobmsg_add_u64(&b, "collisions", iface->stats.collisions); | ||||||
|  | 		blobmsg_close_table(&b, d); | ||||||
|  |  | ||||||
|  | 		if (delta) { | ||||||
|  | 			d = blobmsg_open_table(&b, "deltas"); | ||||||
|  | 			blobmsg_add_u32(&b, "rx_packets", | ||||||
|  | 				get_counter_delta(iface->stats.rx_packets, iface->stats_old.rx_packets)); | ||||||
|  | 			blobmsg_add_u32(&b, "tx_packets", | ||||||
|  | 				get_counter_delta(iface->stats.tx_packets, iface->stats_old.tx_packets)); | ||||||
|  | 		        blobmsg_add_u32(&b, "rx_bytes", | ||||||
|  | 				get_counter_delta(iface->stats.rx_bytes, iface->stats_old.rx_bytes)); | ||||||
|  | 		        blobmsg_add_u32(&b, "tx_bytes", | ||||||
|  | 				get_counter_delta(iface->stats.tx_bytes, iface->stats_old.tx_bytes)); | ||||||
|  | 		        blobmsg_add_u32(&b, "rx_errors", | ||||||
|  | 				get_counter_delta(iface->stats.rx_errors, iface->stats_old.rx_errors)); | ||||||
|  | 		        blobmsg_add_u32(&b, "tx_errors", | ||||||
|  | 				get_counter_delta(iface->stats.tx_errors, iface->stats_old.tx_errors)); | ||||||
|  | 		        blobmsg_add_u32(&b, "rx_dropped", | ||||||
|  | 				get_counter_delta(iface->stats.rx_dropped, iface->stats_old.rx_dropped)); | ||||||
|  | 		        blobmsg_add_u32(&b, "tx_dropped", | ||||||
|  | 				get_counter_delta(iface->stats.tx_dropped, iface->stats_old.tx_dropped)); | ||||||
|  | 		        blobmsg_add_u32(&b, "multicast", | ||||||
|  | 				get_counter_delta(iface->stats.multicast, iface->stats_old.multicast)); | ||||||
|  | 		        blobmsg_add_u32(&b, "collisions", | ||||||
|  | 				get_counter_delta(iface->stats.collisions, iface->stats_old.collisions)); | ||||||
|  | 			blobmsg_close_table(&b, d); | ||||||
|  | 		} | ||||||
|  | 		memcpy(&iface->stats_old, &iface->stats, sizeof(iface->stats)); | ||||||
|  |  | ||||||
|  | 		bridge_dump_if(iface->name); | ||||||
|  | 		blobmsg_close_table(&b, c); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	iface_flush(); | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								feeds/ucentral/udevmand/src/netifd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								feeds/ucentral/udevmand/src/netifd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | |||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	INTERFACE_DEVICE, | ||||||
|  | 	INTERFACE_IFACE, | ||||||
|  | 	__INTERFACE_MAX | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct blobmsg_policy interface_policy[__INTERFACE_MAX] = { | ||||||
|  | 	[INTERFACE_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | 	[INTERFACE_IFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct avl_tree interface_tree = AVL_TREE_INIT(interface_tree, avl_strcmp, false, NULL); | ||||||
|  |  | ||||||
|  | void | ||||||
|  | interface_update(struct blob_attr *msg, int raw) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__INTERFACE_MAX]; | ||||||
|  | 	struct interface *interface; | ||||||
|  | 	char *device, *iface, *device_buf, *iface_buf; | ||||||
|  |  | ||||||
|  | 	if (raw) | ||||||
|  | 		blobmsg_parse(interface_policy, __INTERFACE_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  | 	else | ||||||
|  | 		blobmsg_parse(interface_policy, __INTERFACE_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (!tb[INTERFACE_DEVICE] || !tb[INTERFACE_IFACE]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	iface = blobmsg_get_string(tb[INTERFACE_IFACE]); | ||||||
|  | 	device = blobmsg_get_string(tb[INTERFACE_DEVICE]); | ||||||
|  |  | ||||||
|  | 	interface = avl_find_element(&interface_tree, iface, interface, avl); | ||||||
|  | 	if (interface) { | ||||||
|  | 		avl_delete(&interface_tree, &interface->avl); | ||||||
|  | 		free(interface); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	interface = calloc_a(sizeof(struct neigh), | ||||||
|  | 			     &iface_buf, strlen(iface) + 1, | ||||||
|  | 			     &device_buf, strlen(device) + 1); | ||||||
|  |         if (!interface) | ||||||
|  |                 return; | ||||||
|  | 	interface->iface = strcpy(iface_buf, iface); | ||||||
|  | 	interface->device = strcpy(device_buf, device); | ||||||
|  | 	interface->avl.key = interface->iface; | ||||||
|  |  | ||||||
|  | 	avl_insert(&interface_tree, &interface->avl); | ||||||
|  |  | ||||||
|  | 	ULOG_INFO("new interface %s:%s\n", iface, device); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | interface_down(struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__INTERFACE_MAX]; | ||||||
|  | 	struct interface *interface; | ||||||
|  | 	char *iface; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(interface_policy, __INTERFACE_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (!tb[INTERFACE_IFACE]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	iface = blobmsg_get_string(tb[INTERFACE_IFACE]); | ||||||
|  |  | ||||||
|  | 	interface = avl_find_element(&interface_tree, iface, interface, avl); | ||||||
|  | 	if (!interface) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	ULOG_INFO("del interface %s\n", interface->iface); | ||||||
|  | 	avl_delete(&interface_tree, &interface->avl); | ||||||
|  | 	free(interface); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char* | ||||||
|  | interface_resolve(char *ifname) | ||||||
|  | { | ||||||
|  | 	struct interface *interface; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element(&interface_tree, interface, avl) | ||||||
|  | 		if (!strcmp(interface->device, ifname)) | ||||||
|  | 			return interface->iface; | ||||||
|  | 	return ifname; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int | ||||||
|  | interface_dump(void) | ||||||
|  | { | ||||||
|  | 	struct interface *interface; | ||||||
|  | 	struct mac *mac; | ||||||
|  |  | ||||||
|  | 	blob_buf_init(&b, 0); | ||||||
|  |  | ||||||
|  | 	avl_for_each_element(&interface_tree, interface, avl) { | ||||||
|  | 		void *c; | ||||||
|  |  | ||||||
|  | 		c = blobmsg_open_table(&b, interface->iface); | ||||||
|  | 		avl_for_each_element(&mac_tree, mac, avl) | ||||||
|  | 			if (!strcmp(interface->iface, mac->interface)) | ||||||
|  | 				mac_dump(mac, 0); | ||||||
|  | 		blobmsg_close_table(&b, c); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | interface_done(void) | ||||||
|  | { | ||||||
|  | 	struct interface *i, *t; | ||||||
|  |  | ||||||
|  | 	avl_for_each_element_safe(&interface_tree, i, avl, t) | ||||||
|  | 		free(i); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										113
									
								
								feeds/ucentral/udevmand/src/netlink.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								feeds/ucentral/udevmand/src/netlink.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2017 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | nl_handler_nl_status(struct uloop_fd *u, unsigned int statuss) | ||||||
|  | { | ||||||
|  | 	struct nl_socket *ev = container_of(u, struct nl_socket, uloop); | ||||||
|  | 	int err; | ||||||
|  | 	socklen_t errlen = sizeof(err); | ||||||
|  |  | ||||||
|  | 	if (!u->error) { | ||||||
|  | 		nl_recvmsgs_default(ev->sock); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) | ||||||
|  | 		goto abort; | ||||||
|  |  | ||||||
|  | 	switch(err) { | ||||||
|  | 	case ENOBUFS: | ||||||
|  | 		ev->bufsize *= 2; | ||||||
|  | 		if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) | ||||||
|  | 			goto abort; | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		goto abort; | ||||||
|  | 	} | ||||||
|  | 	u->error = false; | ||||||
|  | 	return; | ||||||
|  |  | ||||||
|  | abort: | ||||||
|  | 	uloop_fd_delete(&ev->uloop); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct nl_sock * | ||||||
|  | nl_create_socket(int protocol, int groups) | ||||||
|  | { | ||||||
|  | 	struct nl_sock *sock; | ||||||
|  |  | ||||||
|  | 	sock = nl_socket_alloc(); | ||||||
|  | 	if (!sock) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (groups) | ||||||
|  | 		nl_join_groups(sock, groups); | ||||||
|  |  | ||||||
|  | 	if (nl_connect(sock, protocol)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	return sock; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool | ||||||
|  | nl_raw_status_socket(struct nl_socket *ev, int protocol, int groups, | ||||||
|  | 		    uloop_fd_handler cb, int flags) | ||||||
|  | { | ||||||
|  | 	ev->sock = nl_create_socket(protocol, groups); | ||||||
|  | 	if (!ev->sock) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	ev->uloop.fd = nl_socket_get_fd(ev->sock); | ||||||
|  | 	ev->uloop.cb = cb; | ||||||
|  | 	if (uloop_fd_add(&ev->uloop, ULOOP_READ|flags)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool | ||||||
|  | nl_status_socket(struct nl_socket *ev, int protocol, | ||||||
|  | 		int (*cb)(struct nl_msg *msg, void *arg), void *priv) | ||||||
|  | { | ||||||
|  | 	if (!nl_raw_status_socket(ev, protocol, 0, nl_handler_nl_status, ULOOP_ERROR_CB)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	nl_socket_modify_cb(ev->sock, NL_CB_VALID, NL_CB_CUSTOM, cb, priv); | ||||||
|  | 	nl_socket_disable_seq_check(ev->sock); | ||||||
|  | 	ev->bufsize = 65535; | ||||||
|  | 	if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int genl_send_and_recv(struct nl_socket *ev, struct nl_msg * msg) | ||||||
|  | { | ||||||
|  | 	int ret = nl_send_auto_complete(ev->sock, msg); | ||||||
|  |  | ||||||
|  | 	nlmsg_free(msg); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  |  | ||||||
|  | 	nl_recvmsgs_default(ev->sock); | ||||||
|  | 	nl_wait_for_ack(ev->sock); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										253
									
								
								feeds/ucentral/udevmand/src/ubus.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								feeds/ucentral/udevmand/src/ubus.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,253 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2020 John Crispin <john@phrozen.org> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Lesser General Public License version 2.1 | ||||||
|  |  * as published by the Free Software Foundation | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "udevmand.h" | ||||||
|  |  | ||||||
|  | static struct ubus_auto_conn conn; | ||||||
|  | static struct ubus_subscriber subscriber; | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	EVENT_ID, | ||||||
|  | 	EVENT_PATH, | ||||||
|  | 	__EVENT_MAX | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct blobmsg_policy status_policy[__EVENT_MAX] = { | ||||||
|  | 	[EVENT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, | ||||||
|  | 	[EVENT_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct ubus_watch_list { | ||||||
|  | 	const char *path; | ||||||
|  | 	int wildcard; | ||||||
|  | } ubus_watch_list[] = { | ||||||
|  | 	{ | ||||||
|  | 		.path = "service", | ||||||
|  | 	}, { | ||||||
|  | 		.path = "dnsmasq", | ||||||
|  | 	}, { | ||||||
|  | 		.path = "network.interface", | ||||||
|  | 	}, { | ||||||
|  | 		.path = "network.status", | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | ubus_mac_cb(struct ubus_context *ctx, struct ubus_object *obj, | ||||||
|  | 	    struct ubus_request_data *req, const char *method, | ||||||
|  | 	    struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	if (!mac_dump_all()) | ||||||
|  | 		ubus_send_reply(ctx, req, b.head); | ||||||
|  | 	return UBUS_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*static int | ||||||
|  | ubus_interface_cb(struct ubus_context *ctx, struct ubus_object *obj, | ||||||
|  | 		  struct ubus_request_data *req, const char *method, | ||||||
|  | 		  struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	if (!interface_dump()) | ||||||
|  | 		ubus_send_reply(ctx, req, b.head); | ||||||
|  | 	return UBUS_STATUS_OK; | ||||||
|  | }*/ | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | ubus_port_cb(struct ubus_context *ctx, struct ubus_object *obj, | ||||||
|  | 	     struct ubus_request_data *req, const char *method, | ||||||
|  | 	     struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	enum { | ||||||
|  | 		DUMP_DELTA, | ||||||
|  | 		__DUMP_MAX | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	static const struct blobmsg_policy dump_policy[__DUMP_MAX] = { | ||||||
|  | 		[DUMP_DELTA] = { .name = "delta", .type = BLOBMSG_TYPE_INT32 }, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct blob_attr *tb[__DUMP_MAX]; | ||||||
|  | 	int delta = 0; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(dump_policy, __DUMP_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (tb[DUMP_DELTA]) | ||||||
|  | 		delta = blobmsg_get_u32(tb[DUMP_DELTA]); | ||||||
|  |  | ||||||
|  | 	iface_dump(delta); | ||||||
|  | 	ubus_send_reply(ctx, req, b.head); | ||||||
|  |  | ||||||
|  | 	return UBUS_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const struct ubus_method topology_methods[] = { | ||||||
|  | 	//UBUS_METHOD_NOARG("interface", ubus_interface_cb), | ||||||
|  |         UBUS_METHOD_NOARG("mac", ubus_mac_cb), | ||||||
|  | 	UBUS_METHOD_NOARG("port", ubus_port_cb), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct ubus_object_type ubus_object_type = | ||||||
|  | 	UBUS_OBJECT_TYPE("topology", topology_methods); | ||||||
|  |  | ||||||
|  | static struct ubus_object ubus_object = { | ||||||
|  | 	.name = "topology", | ||||||
|  | 	.type = &ubus_object_type, | ||||||
|  | 	.methods = topology_methods, | ||||||
|  | 	.n_methods = ARRAY_SIZE(topology_methods), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | ubus_watch_match(const char *path) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(ubus_watch_list); i++) { | ||||||
|  | 		int len = strlen(ubus_watch_list[i].path); | ||||||
|  |  | ||||||
|  | 		if (ubus_watch_list[i].wildcard && strncmp(path, ubus_watch_list[i].path, len)) | ||||||
|  | 			continue; | ||||||
|  | 		if (!ubus_watch_list[i].wildcard && strcmp(path, ubus_watch_list[i].path)) | ||||||
|  | 			continue; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	INTERFACE, | ||||||
|  | 	__INTERFACE_MAX, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct blobmsg_policy interface_policy[__INTERFACE_MAX] = { | ||||||
|  | 	[INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_netifd_dump_cb(struct ubus_request *req, | ||||||
|  | 		    int type, struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__INTERFACE_MAX]; | ||||||
|  | 	struct blob_attr *iter; | ||||||
|  | 	int rem; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(interface_policy, __INTERFACE_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  | 	if (!tb[INTERFACE]) | ||||||
|  | 		return; | ||||||
|  | 	blobmsg_for_each_attr(iter, tb[INTERFACE], rem) | ||||||
|  | 		interface_update(iter, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | ubus_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, | ||||||
|  | 	       struct ubus_request_data *req, const char *method, | ||||||
|  | 	       struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	if (0) { | ||||||
|  | 		char *str; | ||||||
|  |  | ||||||
|  | 		str = blobmsg_format_json(msg, true); | ||||||
|  | 		ULOG_INFO("Received ubus notify '%s': %s\n", method, str); | ||||||
|  | 		free(str); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!strcmp(method, "dhcp.ack")) | ||||||
|  | 		dhcpv4_ack(msg); | ||||||
|  |  | ||||||
|  | 	else if (!strcmp(method, "dhcp.release")) | ||||||
|  | 		dhcpv4_release(msg); | ||||||
|  |  | ||||||
|  | 	else if (!strcmp(method, "interface.update")) | ||||||
|  | 		interface_update(msg, 1); | ||||||
|  |  | ||||||
|  | 	else if (!strcmp(method, "interface.down")) | ||||||
|  | 		interface_down(msg); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_event_handler_cb(struct ubus_context *ctx,  struct ubus_event_handler *ev, | ||||||
|  | 	      const char *type, struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	struct blob_attr *tb[__EVENT_MAX]; | ||||||
|  | 	const char *path; | ||||||
|  | 	uint32_t id; | ||||||
|  |  | ||||||
|  | 	if (strcmp("ubus.object.add", type)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (0) { | ||||||
|  | 		char *str; | ||||||
|  |  | ||||||
|  | 		str = blobmsg_format_json(msg, true); | ||||||
|  | 		ULOG_INFO("Received ubus notify '%s': %s\n", type, str); | ||||||
|  | 		free(str); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(status_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  |  | ||||||
|  | 	if (!tb[EVENT_ID] || !tb[EVENT_PATH]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	path = blobmsg_get_string(tb[EVENT_PATH]); | ||||||
|  | 	id = blobmsg_get_u32(tb[EVENT_ID]); | ||||||
|  |  | ||||||
|  | 	if (!ubus_watch_match(path) && !ubus_subscribe(ctx, &subscriber, id)) | ||||||
|  | 		ULOG_INFO("Subscribe to %s (%u)\n", path, id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct ubus_event_handler ubus_event_handler = { .cb = ubus_event_handler_cb }; | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, | ||||||
|  | 		    void *priv) | ||||||
|  | { | ||||||
|  | 	char *path = strdup(obj->path); | ||||||
|  |  | ||||||
|  | 	if (!ubus_watch_match(path) && !ubus_subscribe(ctx, &subscriber, obj->id)) | ||||||
|  | 		ULOG_INFO("Subscribe to %s (%u)\n", path, obj->id); | ||||||
|  | 	free(path); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_connect_handler(struct ubus_context *ctx) | ||||||
|  | { | ||||||
|  | 	uint32_t netifd; | ||||||
|  |  | ||||||
|  | 	ubus_add_object(ctx, &ubus_object); | ||||||
|  |  | ||||||
|  | 	ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.add"); | ||||||
|  | 	ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.remove"); | ||||||
|  |  | ||||||
|  | 	subscriber.cb = ubus_notify_cb; | ||||||
|  | 	if (ubus_register_subscriber(ctx, &subscriber)) | ||||||
|  | 		ULOG_ERR("failed to register ubus subscriber\n"); | ||||||
|  |  | ||||||
|  | 	ubus_lookup(ctx, NULL, receive_list_result, NULL); | ||||||
|  |  | ||||||
|  | 	if (!ubus_lookup_id(ctx, "network.interface", &netifd)) | ||||||
|  | 		ubus_invoke(ctx, netifd, "dump", NULL, ubus_netifd_dump_cb, NULL, 5000); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ubus_init(void) | ||||||
|  | { | ||||||
|  | 	conn.cb = ubus_connect_handler; | ||||||
|  | 	ubus_auto_connect(&conn); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ubus_uninit(void) | ||||||
|  | { | ||||||
|  | 	ubus_auto_shutdown(&conn); | ||||||
|  | } | ||||||
							
								
								
									
										150
									
								
								feeds/ucentral/udevmand/src/udevmand.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								feeds/ucentral/udevmand/src/udevmand.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | |||||||
|  | #define _GNU_SOURCE | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <glob.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <ifaddrs.h> | ||||||
|  |  | ||||||
|  | #include <net/if.h> | ||||||
|  |  | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <linux/if_ether.h> | ||||||
|  | #include <linux/rtnetlink.h> | ||||||
|  | #include <linux/nl80211.h> | ||||||
|  | #include <linux/limits.h> | ||||||
|  | #include <linux/sockios.h> | ||||||
|  | #include <linux/ethtool.h> | ||||||
|  |  | ||||||
|  | #include <netlink/msg.h> | ||||||
|  | #include <netlink/attr.h> | ||||||
|  | #include <netlink/socket.h> | ||||||
|  | #include <netlink/genl/ctrl.h> | ||||||
|  | #include <netlink/genl/genl.h> | ||||||
|  |  | ||||||
|  | #include <libubox/avl.h> | ||||||
|  | #include <libubox/avl-cmp.h> | ||||||
|  | #include <libubox/uloop.h> | ||||||
|  | #include <libubox/utils.h> | ||||||
|  | #include <libubox/ulog.h> | ||||||
|  | #include <libubox/blobmsg_json.h> | ||||||
|  | #include <libubox/vlist.h> | ||||||
|  |  | ||||||
|  | #include <libubus.h> | ||||||
|  |  | ||||||
|  | #define MAC_FMT	"%02x:%02x:%02x:%02x:%02x:%02x" | ||||||
|  | #define MAC_VAR(x) x[0], x[1], x[2], x[3], x[4], x[5] | ||||||
|  |  | ||||||
|  | #define IP_FMT	"%d.%d.%d.%d" | ||||||
|  | #define IP_VAR(x) x[0], x[1], x[2], x[3] | ||||||
|  |  | ||||||
|  | struct nl_socket { | ||||||
|  | 	struct uloop_fd uloop; | ||||||
|  | 	struct nl_sock *sock; | ||||||
|  | 	int bufsize; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mac { | ||||||
|  | 	uint8_t *addr; | ||||||
|  | 	struct avl_node avl; | ||||||
|  | 	char interface[64]; | ||||||
|  | 	char *ethers; | ||||||
|  |  | ||||||
|  | 	struct timespec ts; | ||||||
|  | 	struct list_head neigh4; | ||||||
|  | 	struct list_head neigh6; | ||||||
|  | 	struct list_head dhcpv4; | ||||||
|  | 	struct list_head bridge_mac; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct neigh { | ||||||
|  | 	struct avl_node avl; | ||||||
|  | 	struct list_head list; | ||||||
|  |  | ||||||
|  | 	uint8_t *ip; | ||||||
|  | 	int ip_ver; | ||||||
|  | 	int iface; | ||||||
|  | 	char ifname[IF_NAMESIZE]; | ||||||
|  |  | ||||||
|  | 	struct uloop_timeout ageing; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct dhcpv4 { | ||||||
|  | 	struct avl_node avl; | ||||||
|  | 	struct list_head mac; | ||||||
|  |  | ||||||
|  | 	uint8_t addr[ETH_ALEN]; | ||||||
|  | 	uint8_t ip[4]; | ||||||
|  | 	char iface[IF_NAMESIZE]; | ||||||
|  | 	char name[]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct interface { | ||||||
|  | 	struct avl_node avl; | ||||||
|  |  | ||||||
|  | 	char *iface; | ||||||
|  | 	char *device; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct bridge_mac { | ||||||
|  | 	struct vlist_node vlist; | ||||||
|  | 	struct list_head mac; | ||||||
|  |  | ||||||
|  | 	char bridge[IF_NAMESIZE]; | ||||||
|  | 	char ifname[IF_NAMESIZE]; | ||||||
|  | 	uint8_t addr[ETH_ALEN]; | ||||||
|  | 	__u8 port_no; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int avl_mac_cmp(const void *k1, const void *k2, void *ptr); | ||||||
|  |  | ||||||
|  | extern struct avl_tree mac_tree; | ||||||
|  | int mac_dump_all(void); | ||||||
|  | void mac_dump(struct mac *mac, int interface); | ||||||
|  | struct mac* mac_find(uint8_t *addr); | ||||||
|  | void mac_update(struct mac *mac, char *iface); | ||||||
|  |  | ||||||
|  | int neigh_init(void); | ||||||
|  | void neigh_enum(void); | ||||||
|  | void neigh_flush(void); | ||||||
|  | void neigh_done(void); | ||||||
|  |  | ||||||
|  | bool nl_status_socket(struct nl_socket *ev, int protocol, | ||||||
|  | 		     int (*cb)(struct nl_msg *msg, void *arg), void *priv); | ||||||
|  | int genl_send_and_recv(struct nl_socket *ev, struct nl_msg * msg); | ||||||
|  |  | ||||||
|  | extern struct blob_buf b; | ||||||
|  | void blobmsg_add_iface(struct blob_buf *bbuf, char *name, int index); | ||||||
|  | void blobmsg_add_iftype(struct blob_buf *bbuf, const char *name, const uint32_t iftype); | ||||||
|  | void blobmsg_add_ipv4(struct blob_buf *bbuf, const char *name, const uint8_t* addr); | ||||||
|  | void blobmsg_add_ipv6(struct blob_buf *bbuf, const char *name, const uint8_t* addr); | ||||||
|  | void blobmsg_add_mac(struct blob_buf *bbuf, const char *name, const uint8_t* addr); | ||||||
|  |  | ||||||
|  | void ubus_init(void); | ||||||
|  | void ubus_uninit(void); | ||||||
|  |  | ||||||
|  | void bridge_init(void); | ||||||
|  | void bridge_dump_if(const char *bridge); | ||||||
|  | void bridge_flush(void); | ||||||
|  | void bridge_mac_del(struct bridge_mac *b); | ||||||
|  |  | ||||||
|  | void dhcpv4_ack(struct blob_attr *msg); | ||||||
|  | void dhcpv4_release(struct blob_attr *msg); | ||||||
|  | void dhcp_init(void); | ||||||
|  | void dhcp_done(void); | ||||||
|  | void dhcpv4_del(struct dhcpv4 *dhcpv4); | ||||||
|  |  | ||||||
|  | int interface_dump(void); | ||||||
|  | void interface_update(struct blob_attr *msg, int raw); | ||||||
|  | void interface_down(struct blob_attr *msg); | ||||||
|  | char *interface_resolve(char *device); | ||||||
|  | void interface_done(void); | ||||||
|  |  | ||||||
|  | void ethers_init(void); | ||||||
|  |  | ||||||
|  | void iface_done(void); | ||||||
|  | void iface_dump(int delta); | ||||||
| @@ -6,12 +6,6 @@ PKG_RELEASE:=1 | |||||||
| PKG_LICENSE:=GPL-2.0 | PKG_LICENSE:=GPL-2.0 | ||||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||||
|  |  | ||||||
| PKG_SOURCE_URL=https://github.com/blogic/udnssnoop.git |  | ||||||
| PKG_MIRROR_HASH:=afd17cc6aed4a151bc0f437b84491d751932a39f93f429418200e9e8be53dfad |  | ||||||
| PKG_SOURCE_PROTO:=git |  | ||||||
| PKG_SOURCE_DATE:=2021-04-12 |  | ||||||
| PKG_SOURCE_VERSION:=67e1e5f0bfc12222aa59c54e7066b1c00a680e56 |  | ||||||
|  |  | ||||||
| include $(INCLUDE_DIR)/package.mk | include $(INCLUDE_DIR)/package.mk | ||||||
| include $(INCLUDE_DIR)/cmake.mk | include $(INCLUDE_DIR)/cmake.mk | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								feeds/ucentral/udnssnoop/src/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								feeds/ucentral/udnssnoop/src/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | CMakeCache.txt | ||||||
|  | CMakeFiles/ | ||||||
|  | cmake_install.cmake | ||||||
|  | udnssnoop | ||||||
							
								
								
									
										16
									
								
								feeds/ucentral/udnssnoop/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								feeds/ucentral/udnssnoop/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | cmake_minimum_required(VERSION 2.6) | ||||||
|  |  | ||||||
|  | PROJECT(udnssnoop C) | ||||||
|  | INCLUDE(GNUInstallDirs) | ||||||
|  | ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) | ||||||
|  |  | ||||||
|  | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") | ||||||
|  |  | ||||||
|  | SET(SOURCES main.c dns.c) | ||||||
|  | SET(LIBS ubox ubus resolv) | ||||||
|  |  | ||||||
|  | ADD_EXECUTABLE(udnssnoop ${SOURCES}) | ||||||
|  | TARGET_LINK_LIBRARIES(udnssnoop ${LIBS}) | ||||||
|  | INSTALL(TARGETS udnssnoop | ||||||
|  | 	RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} | ||||||
|  | ) | ||||||
							
								
								
									
										215
									
								
								feeds/ucentral/udnssnoop/src/dns.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								feeds/ucentral/udnssnoop/src/dns.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | |||||||
|  | /* SPDX-License-Identifier: BSD-3-Clause */ | ||||||
|  |  | ||||||
|  | #include "dns.h" | ||||||
|  |  | ||||||
|  | static char name_buffer[MAX_NAME_LEN + 1]; | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | scan_name(const uint8_t *buffer, int len) | ||||||
|  | { | ||||||
|  | 	int offset = 0; | ||||||
|  |  | ||||||
|  | 	while (len && (*buffer != '\0')) { | ||||||
|  | 		int l = *buffer; | ||||||
|  |  | ||||||
|  | 		if (IS_COMPRESSED(l)) | ||||||
|  | 			return offset + 2; | ||||||
|  |  | ||||||
|  | 		if (l + 1 > len) return -1; | ||||||
|  | 		len -= l + 1; | ||||||
|  | 		offset += l + 1; | ||||||
|  | 		buffer += l + 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!len || !offset || (*buffer != '\0')) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	return offset + 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct dns_header* | ||||||
|  | dns_consume_header(uint8_t **data, int *len) | ||||||
|  | { | ||||||
|  | 	struct dns_header *h = (struct dns_header *) *data; | ||||||
|  |  | ||||||
|  | 	if (*len < sizeof(struct dns_header)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	h->id = be16_to_cpu(h->id); | ||||||
|  | 	h->flags = be16_to_cpu(h->flags); | ||||||
|  | 	h->questions = be16_to_cpu(h->questions); | ||||||
|  | 	h->answers = be16_to_cpu(h->answers); | ||||||
|  | 	h->authority = be16_to_cpu(h->authority); | ||||||
|  | 	h->additional = be16_to_cpu(h->additional); | ||||||
|  |  | ||||||
|  | 	*len -= sizeof(struct dns_header); | ||||||
|  | 	*data += sizeof(struct dns_header); | ||||||
|  |  | ||||||
|  | 	return h; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct dns_question* | ||||||
|  | dns_consume_question(uint8_t **data, int *len) | ||||||
|  | { | ||||||
|  | 	struct dns_question *q = (struct dns_question *) *data; | ||||||
|  |  | ||||||
|  | 	if (*len < sizeof(struct dns_question)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	q->type = be16_to_cpu(q->type); | ||||||
|  | 	q->class = be16_to_cpu(q->class); | ||||||
|  |  | ||||||
|  | 	*len -= sizeof(struct dns_question); | ||||||
|  | 	*data += sizeof(struct dns_question); | ||||||
|  |  | ||||||
|  | 	return q; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct dns_answer* | ||||||
|  | dns_consume_answer(uint8_t **data, int *len) | ||||||
|  | { | ||||||
|  | 	struct dns_answer *a = (struct dns_answer *) *data; | ||||||
|  |  | ||||||
|  | 	if (*len < sizeof(struct dns_answer)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	a->type = be16_to_cpu(a->type); | ||||||
|  | 	a->class = be16_to_cpu(a->class); | ||||||
|  | 	a->ttl = be32_to_cpu(a->ttl); | ||||||
|  | 	a->rdlength = be16_to_cpu(a->rdlength); | ||||||
|  |  | ||||||
|  | 	*len -= sizeof(struct dns_answer); | ||||||
|  | 	*data += sizeof(struct dns_answer); | ||||||
|  |  | ||||||
|  | 	return a; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static char * | ||||||
|  | dns_consume_name(const uint8_t *base, int blen, uint8_t **data, int *len) | ||||||
|  | { | ||||||
|  | 	int nlen = scan_name(*data, *len); | ||||||
|  |  | ||||||
|  | 	if (nlen < 1) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (dn_expand(base, base + blen, *data, name_buffer, MAX_NAME_LEN) < 0) { | ||||||
|  | 		perror("dns_consume_name/dn_expand"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	*len -= nlen; | ||||||
|  | 	*data += nlen; | ||||||
|  |  | ||||||
|  | 	return name_buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | parse_answer(uint8_t *buffer, int len, uint8_t **b, int *rlen) | ||||||
|  | { | ||||||
|  | 	char *name = dns_consume_name(buffer, len, b, rlen); | ||||||
|  | 	struct dns_answer *a; | ||||||
|  | 	uint8_t *rdata; | ||||||
|  | 	char ipbuf[33]; | ||||||
|  |  | ||||||
|  | 	if (*rlen < 0) { | ||||||
|  | 		fprintf(stderr, "dropping: bad answer - bad length\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	a = dns_consume_answer(b, rlen); | ||||||
|  | 	if (!a) { | ||||||
|  | 		fprintf(stderr, "dropping: bad answer - bad buffer\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!name) { | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ((a->class & ~CLASS_FLUSH) != CLASS_IN) { | ||||||
|  | 		fprintf(stderr, "dropping: class\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rdata = *b; | ||||||
|  | 	if (a->rdlength > *rlen) { | ||||||
|  | 		fprintf(stderr, "dropping: bad answer - bad rlen\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	*rlen -= a->rdlength; | ||||||
|  | 	*b += a->rdlength; | ||||||
|  |  | ||||||
|  | 	switch (a->type) { | ||||||
|  | 	case TYPE_A: | ||||||
|  | 		if (a->rdlength != 4) | ||||||
|  | 			return 0; | ||||||
|  |  | ||||||
|  | 		if (!inet_ntop(AF_INET, rdata, ipbuf, sizeof(ipbuf))) | ||||||
|  | 			return 0; | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case TYPE_AAAA: | ||||||
|  | 		if (a->rdlength != 16) | ||||||
|  | 			return 0; | ||||||
|  |  | ||||||
|  | 		if (!inet_ntop(AF_INET6, rdata, ipbuf, sizeof(ipbuf))) | ||||||
|  | 			return 0; | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ubus_notify_qosify(name, ipbuf, a->type, a->ttl); | ||||||
|  | 	printf("%s %s %" PRIu32 "\n", name, ipbuf, a->ttl); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | dns_handle_packet(uint8_t *buffer, int len) | ||||||
|  | { | ||||||
|  | 	struct dns_header *h; | ||||||
|  | 	uint8_t *b = buffer; | ||||||
|  | 	int rlen = len; | ||||||
|  |  | ||||||
|  | 	h = dns_consume_header(&b, &rlen); | ||||||
|  | 	if (!h) { | ||||||
|  | 		fprintf(stderr, "dropping: bad header\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!(h->flags & FLAG_RESPONSE)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (!h->answers) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	while (h->questions-- > 0) { | ||||||
|  | 		char *name = dns_consume_name(buffer, len, &b, &rlen); | ||||||
|  | 		struct dns_question *q; | ||||||
|  |  | ||||||
|  | 		if (!name || rlen < 0) | ||||||
|  | 			return; | ||||||
|  |  | ||||||
|  | 		q = dns_consume_question(&b, &rlen); | ||||||
|  | 		if (!q) { | ||||||
|  | 			fprintf(stderr, "dropping: bad question\n"); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (h->answers-- > 0) | ||||||
|  | 		if (parse_answer(buffer, len, &b, &rlen)) | ||||||
|  | 			return; | ||||||
|  |  | ||||||
|  | /*	while (h->authority-- > 0) | ||||||
|  | 		if (parse_answer(buffer, len, &b, &rlen)) | ||||||
|  | 			return; | ||||||
|  |  | ||||||
|  | 	while (h->additional-- > 0) | ||||||
|  | 		if (parse_answer(buffer, len, &b, &rlen)) | ||||||
|  | 			return; | ||||||
|  | */ | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								feeds/ucentral/udnssnoop/src/dns.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								feeds/ucentral/udnssnoop/src/dns.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | /* SPDX-License-Identifier: BSD-3-Clause */ | ||||||
|  |  | ||||||
|  | #ifndef _DNS_H__ | ||||||
|  | #define _DNS_H__ | ||||||
|  |  | ||||||
|  | #define _GNU_SOURCE | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include <netinet/ip.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <linux/if_ether.h> | ||||||
|  | #include <linux/filter.h> | ||||||
|  | #include <linux/udp.h> | ||||||
|  | #include <netinet/ip6.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <arpa/nameser.h> | ||||||
|  | #include <resolv.h> | ||||||
|  |  | ||||||
|  | #include <libubox/avl-cmp.h> | ||||||
|  | #include <libubox/utils.h> | ||||||
|  | #include <libubox/uloop.h> | ||||||
|  | #include <libubox/ulog.h> | ||||||
|  | #include <libubus.h> | ||||||
|  | #include <uci.h> | ||||||
|  | #include <uci_blob.h> | ||||||
|  |  | ||||||
|  | #define FLAG_RESPONSE		0x8000 | ||||||
|  | #define FLAG_AUTHORATIVE	0x0400 | ||||||
|  |  | ||||||
|  | #define TYPE_A			0x0001 | ||||||
|  | #define TYPE_PTR		0x000C | ||||||
|  | #define TYPE_TXT		0x0010 | ||||||
|  | #define TYPE_AAAA		0x001c | ||||||
|  | #define TYPE_SRV		0x0021 | ||||||
|  | #define TYPE_ANY		0x00ff | ||||||
|  |  | ||||||
|  | #define IS_COMPRESSED(x)	((x & 0xc0) == 0xc0) | ||||||
|  |  | ||||||
|  | #define CLASS_FLUSH		0x8000 | ||||||
|  | #define CLASS_UNICAST		0x8000 | ||||||
|  | #define CLASS_IN		0x0001 | ||||||
|  |  | ||||||
|  | #define MAX_NAME_LEN            8096 | ||||||
|  | #define MAX_DATA_LEN            8096 | ||||||
|  |  | ||||||
|  | struct vlan_hdr { | ||||||
|  | 	uint16_t h_vlan_TCI; | ||||||
|  | 	uint16_t h_vlan_encapsulated_proto; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct dns_header { | ||||||
|  | 	uint16_t id; | ||||||
|  | 	uint16_t flags; | ||||||
|  | 	uint16_t questions; | ||||||
|  | 	uint16_t answers; | ||||||
|  | 	uint16_t authority; | ||||||
|  | 	uint16_t additional; | ||||||
|  | } __attribute__((packed)); | ||||||
|  |  | ||||||
|  | struct dns_srv_data { | ||||||
|  | 	uint16_t priority; | ||||||
|  | 	uint16_t weight; | ||||||
|  | 	uint16_t port; | ||||||
|  | } __attribute__((packed)); | ||||||
|  |  | ||||||
|  | struct dns_answer { | ||||||
|  | 	uint16_t type; | ||||||
|  | 	uint16_t class; | ||||||
|  | 	uint32_t ttl; | ||||||
|  | 	uint16_t rdlength; | ||||||
|  | } __attribute__((packed)); | ||||||
|  |  | ||||||
|  | struct dns_question { | ||||||
|  | 	uint16_t type; | ||||||
|  | 	uint16_t class; | ||||||
|  | } __attribute__((packed)); | ||||||
|  |  | ||||||
|  | void dns_handle_packet(uint8_t *buffer, int len); | ||||||
|  | void ubus_notify_qosify(char *name, char *address, int type, int ttl); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										282
									
								
								feeds/ucentral/udnssnoop/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								feeds/ucentral/udnssnoop/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,282 @@ | |||||||
|  | /* SPDX-License-Identifier: BSD-3-Clause */ | ||||||
|  |  | ||||||
|  | /* code is derived from hapd proxy_arp snooping */ | ||||||
|  |  | ||||||
|  | #include "dns.h" | ||||||
|  |  | ||||||
|  |  /* sudo tcpdump -s 3000 -dd greater 96 and '(ip or ip6)' and '(udp or tcp)' and '(port 53)' */ | ||||||
|  | static struct sock_filter dns_sock_filter_insns[] = { | ||||||
|  | 	{ 0x80, 0, 0, 0x00000000 }, | ||||||
|  | 	{ 0x35, 0, 19, 0x00000060 }, | ||||||
|  | 	{ 0x28, 0, 0, 0x0000000c }, | ||||||
|  | 	{ 0x15, 0, 9, 0x00000800 }, | ||||||
|  | 	{ 0x30, 0, 0, 0x00000017 }, | ||||||
|  | 	{ 0x15, 0, 15, 0x00000011 }, | ||||||
|  | 	{ 0x28, 0, 0, 0x00000014 }, | ||||||
|  | 	{ 0x45, 13, 0, 0x00001fff }, | ||||||
|  | 	{ 0xb1, 0, 0, 0x0000000e }, | ||||||
|  | 	{ 0x48, 0, 0, 0x0000000e }, | ||||||
|  | 	{ 0x15, 9, 0, 0x00000035 }, | ||||||
|  | 	{ 0x48, 0, 0, 0x00000010 }, | ||||||
|  | 	{ 0x15, 7, 8, 0x00000035 }, | ||||||
|  | 	{ 0x15, 0, 7, 0x000086dd }, | ||||||
|  | 	{ 0x30, 0, 0, 0x00000014 }, | ||||||
|  | 	{ 0x15, 0, 5, 0x00000011 }, | ||||||
|  | 	{ 0x28, 0, 0, 0x00000036 }, | ||||||
|  | 	{ 0x15, 2, 0, 0x00000035 }, | ||||||
|  | 	{ 0x28, 0, 0, 0x00000038 }, | ||||||
|  | 	{ 0x15, 0, 1, 0x00000035 }, | ||||||
|  | 	{ 0x6, 0, 0, 0x00000bb8 }, | ||||||
|  | 	{ 0x6, 0, 0, 0x00000000 }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct sock_fprog sock_filter = { | ||||||
|  | 	.len = ARRAY_SIZE(dns_sock_filter_insns), | ||||||
|  | 	.filter = dns_sock_filter_insns, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct ubus_auto_conn conn; | ||||||
|  | static struct uloop_fd fd; | ||||||
|  | static struct blob_buf b; | ||||||
|  | static char *ifname; | ||||||
|  | static int qosify; | ||||||
|  |  | ||||||
|  | static struct ubus_object_type ubus_object_type = { | ||||||
|  | 	.name = "dnssnoop" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void ubus_state_handler(struct ubus_context *ctx, struct ubus_object *obj) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct ubus_object ubus_object = { | ||||||
|  | 	.name = "dnssnoop", | ||||||
|  | 	.type = &ubus_object_type, | ||||||
|  | 	.subscribe_cb = ubus_state_handler, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | proto_is_vlan(uint16_t h_proto) | ||||||
|  | { | ||||||
|  | 	return !!(h_proto == ETH_P_8021Q || | ||||||
|  | 		  h_proto == ETH_P_8021AD); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | consume_buffer(uint8_t **buf, int *len, int size) | ||||||
|  | { | ||||||
|  | 	if (size > *len) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	*buf += size; | ||||||
|  | 	*len -= size; | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | packet_handle(uint8_t *buf, int len) | ||||||
|  | { | ||||||
|  | 	struct ethhdr *eth = (struct ethhdr *)buf; | ||||||
|  | 	uint16_t h_proto; | ||||||
|  |  | ||||||
|  | 	if (consume_buffer(&buf, &len, sizeof(*eth))) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	h_proto = eth->h_proto; | ||||||
|  |  | ||||||
|  | 	if (proto_is_vlan(ntohs(h_proto))) { | ||||||
|  | 		struct vlan_hdr *vlanh = (struct vlan_hdr *)buf; | ||||||
|  |  | ||||||
|  | 		if (consume_buffer(&buf, &len, sizeof(struct vlan_hdr))) | ||||||
|  | 			return; | ||||||
|  |  | ||||||
|  | 		h_proto = vlanh->h_vlan_encapsulated_proto; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch (ntohs(eth->h_proto)) { | ||||||
|  | 	case ETH_P_IP: | ||||||
|  | 		if (consume_buffer(&buf, &len, sizeof(struct ip))) | ||||||
|  | 			return; | ||||||
|  | 		break; | ||||||
|  | 	case ETH_P_IPV6: | ||||||
|  | 		if (consume_buffer(&buf, &len, sizeof(struct ip6_hdr))) | ||||||
|  | 			return; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (consume_buffer(&buf, &len, sizeof(struct udphdr))) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	dns_handle_packet(buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | socket_fd_cb(struct uloop_fd *fd, unsigned int events) | ||||||
|  | { | ||||||
|  | 	uint8_t buf[8192]; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		int len = recvfrom(fd->fd, buf, sizeof(buf), MSG_DONTWAIT, NULL, NULL); | ||||||
|  |  | ||||||
|  | 		if (len <= 0) { | ||||||
|  | 			switch (errno) { | ||||||
|  | 			case EINTR: | ||||||
|  | 			case EAGAIN: | ||||||
|  | 				return; | ||||||
|  | 			default: | ||||||
|  | 				exit(1); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		packet_handle(buf, len); | ||||||
|  | 	} while (true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | socket_open(void) | ||||||
|  | { | ||||||
|  | 	int sock; | ||||||
|  |  | ||||||
|  | 	sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | ||||||
|  | 	if (sock == -1) { | ||||||
|  | 		ULOG_ERR("failed to open socket on %s\n", ifname); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname))) { | ||||||
|  | 		ULOG_ERR("failed to bind socket to %s\n", ifname); | ||||||
|  | 		close(sock); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, | ||||||
|  | 		       &sock_filter, sizeof(struct sock_fprog))) { | ||||||
|  | 		ULOG_ERR("failed to attach filter to %s\n", ifname); | ||||||
|  | 		close(sock); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sock; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | snoop_start(void) | ||||||
|  | { | ||||||
|  | 	int sock = socket_open(); | ||||||
|  | 	if (sock == -1) { | ||||||
|  | 		ULOG_ERR("failed to open socket on %s\n", ifname); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	fd.cb = socket_fd_cb; | ||||||
|  | 	fd.fd = sock; | ||||||
|  | 	uloop_fd_add(&fd, ULOOP_READ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ubus_notify_qosify(char *name, char *address, int type, int ttl) | ||||||
|  | { | ||||||
|  | 	if (!qosify) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	blob_buf_init(&b, 0); | ||||||
|  | 	switch (type) { | ||||||
|  | 	case TYPE_AAAA: | ||||||
|  | 		blobmsg_add_string(&b, "type", "AAAA"); | ||||||
|  | 		break; | ||||||
|  | 	case TYPE_A: | ||||||
|  | 		blobmsg_add_string(&b, "type", "A"); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	blobmsg_add_string(&b, "name", name); | ||||||
|  | 	blobmsg_add_string(&b, "address", address); | ||||||
|  |         blobmsg_add_u32(&b, "ttl", ttl); | ||||||
|  |  | ||||||
|  | 	ubus_invoke(&conn.ctx, qosify, "add_dns_host", b.head, NULL, NULL, 200); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_handle_status(struct ubus_context *ctx,  struct ubus_event_handler *ev, | ||||||
|  | 	      const char *type, struct blob_attr *msg) | ||||||
|  | { | ||||||
|  | 	enum { | ||||||
|  | 		EVENT_ID, | ||||||
|  | 		EVENT_PATH, | ||||||
|  | 		__EVENT_MAX | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	static const struct blobmsg_policy status_policy[__EVENT_MAX] = { | ||||||
|  | 		[EVENT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, | ||||||
|  | 		[EVENT_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct blob_attr *tb[__EVENT_MAX]; | ||||||
|  | 	uint32_t id; | ||||||
|  | 	char *path; | ||||||
|  |  | ||||||
|  | 	blobmsg_parse(status_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg)); | ||||||
|  | 	if (!tb[EVENT_ID] || !tb[EVENT_PATH]) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	path = blobmsg_get_string(tb[EVENT_PATH]); | ||||||
|  | 	id = blobmsg_get_u32(tb[EVENT_ID]); | ||||||
|  |  | ||||||
|  | 	if (strcmp(path, "qosify")) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (!strcmp("ubus.object.remove", type)) | ||||||
|  | 		qosify = 0; | ||||||
|  |  | ||||||
|  | 	if (!strcmp("ubus.object.add", type)) | ||||||
|  | 		qosify = id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_lookup_cb(struct ubus_context *ctx, struct ubus_object_data *obj, | ||||||
|  |                     void *priv) | ||||||
|  | { | ||||||
|  | 	if (strcmp(obj->path, "qosify")) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	qosify = obj->id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct ubus_event_handler ubus_status_handler = { .cb = ubus_handle_status }; | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | ubus_connect_handler(struct ubus_context *ctx) | ||||||
|  | { | ||||||
|  |         ULOG_NOTE("connected to ubus\n"); | ||||||
|  | 	ubus_add_object(ctx, &ubus_object); | ||||||
|  |  | ||||||
|  | 	ubus_register_event_handler(ctx, &ubus_status_handler, "ubus.object.add"); | ||||||
|  | 	ubus_register_event_handler(ctx, &ubus_status_handler, "ubus.object.remove"); | ||||||
|  |  | ||||||
|  | 	ubus_lookup(ctx, NULL, ubus_lookup_cb, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int | ||||||
|  | main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	if (argc != 2) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	ifname = argv[1]; | ||||||
|  |  | ||||||
|  | 	ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "udnssnoop"); | ||||||
|  |  | ||||||
|  | 	uloop_init(); | ||||||
|  |  | ||||||
|  | 	conn.cb = ubus_connect_handler; | ||||||
|  |         ubus_auto_connect(&conn); | ||||||
|  | 	snoop_start(); | ||||||
|  | 	uloop_run(); | ||||||
|  | 	uloop_done(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user