mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-31 02:17:58 +00:00 
			
		
		
		
	radius_gw_proxy: add support for a radius/gateway proxy
This will allow the AP to send radius requests via the websocket to the gateway for routing to the correct AAA. Fixes: WIFI-7328 Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
		
							
								
								
									
										25
									
								
								feeds/ucentral/radius-gw-proxy/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								feeds/ucentral/radius-gw-proxy/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| include $(TOPDIR)/rules.mk | ||||
|  | ||||
| PKG_NAME:=radius-gw-proxy | ||||
| PKG_RELEASE:=1 | ||||
|  | ||||
| PKG_LICENSE:=BSD-3-Clause | ||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||
|  | ||||
| include $(INCLUDE_DIR)/package.mk | ||||
| include $(INCLUDE_DIR)/cmake.mk | ||||
|  | ||||
| define Package/radius-gw-proxy | ||||
|   SECTION:=ucentral | ||||
|   CATEGORY:=uCentral | ||||
|   TITLE:=uCentral Gateway radius-gw-proxy | ||||
|   DEPENDS:=+libubox +libubus | ||||
| endef | ||||
|  | ||||
| define Package/radius-gw-proxy/install | ||||
| 	$(INSTALL_DIR) $(1)/usr/sbin | ||||
| 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/radius-gw-proxy $(1)/usr/sbin/ | ||||
| 	$(CP) ./files/* $(1) | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,radius-gw-proxy)) | ||||
							
								
								
									
										11
									
								
								feeds/ucentral/radius-gw-proxy/files/etc/init.d/radius-gw-proxy
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								feeds/ucentral/radius-gw-proxy/files/etc/init.d/radius-gw-proxy
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #!/bin/sh /etc/rc.common | ||||
|  | ||||
| START=99 | ||||
|  | ||||
| USE_PROCD=1 | ||||
|  | ||||
| start_service() { | ||||
| 	procd_open_instance | ||||
| 	procd_set_param command "/usr/sbin/radius-gw-proxy" | ||||
| 	procd_close_instance | ||||
| } | ||||
							
								
								
									
										30
									
								
								feeds/ucentral/radius-gw-proxy/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								feeds/ucentral/radius-gw-proxy/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| cmake_minimum_required(VERSION 2.6) | ||||
|  | ||||
| PROJECT(radius-gw-proxy C) | ||||
|  | ||||
| ADD_DEFINITIONS(-Wall -Werror) | ||||
| IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6) | ||||
| 	ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration) | ||||
| 	ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral) | ||||
| ENDIF() | ||||
| ADD_DEFINITIONS(-Os -std=gnu99 -g3 -Wmissing-declarations -Wno-unused-parameter) | ||||
|  | ||||
| SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") | ||||
|  | ||||
| SET(SOURCES main.c) | ||||
|  | ||||
| FIND_LIBRARY(ubus NAMES ubus) | ||||
| FIND_LIBRARY(ubox NAMES ubox) | ||||
|  | ||||
| FIND_PATH(ubox_include_dir libubox/uloop.h) | ||||
| FIND_PATH(ubus_include_dir NAMES libubus.h) | ||||
|  | ||||
| INCLUDE_DIRECTORIES(${ubox_include_dir} ${ubus_include_dir}) | ||||
|  | ||||
| ADD_EXECUTABLE(radius-gw-proxy ${SOURCES}) | ||||
|  | ||||
| TARGET_LINK_LIBRARIES(radius-gw-proxy ${ubox} ${ubus}) | ||||
|  | ||||
| INSTALL(TARGETS radius-gw-proxy | ||||
| 	RUNTIME DESTINATION sbin | ||||
| ) | ||||
							
								
								
									
										356
									
								
								feeds/ucentral/radius-gw-proxy/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								feeds/ucentral/radius-gw-proxy/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,356 @@ | ||||
| #define _GNU_SOURCE | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <net/if.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <libubox/uloop.h> | ||||
| #include <libubox/usock.h> | ||||
|  | ||||
| #include <libubox/avl.h> | ||||
| #include <libubox/avl-cmp.h> | ||||
|  | ||||
| #include <libubus.h> | ||||
|  | ||||
| #define RAD_PROX_BUFLEN		(4 * 1024) | ||||
|  | ||||
| #define TLV_STATION_ID		30 | ||||
| #define TLV_VENDOR		26 | ||||
| #define TLV_TIP_SERVER_V4	2 | ||||
|  | ||||
| #define TIP_VENDOR		58888 | ||||
|  | ||||
| enum socket_type { | ||||
| 	RADIUS_AUTH = 0, | ||||
| 	RADIUS_ACCT, | ||||
| 	RADIUS_DAS | ||||
| }; | ||||
|  | ||||
| struct radius_socket { | ||||
| 	struct uloop_fd fd; | ||||
| 	enum socket_type type; | ||||
| }; | ||||
|  | ||||
| struct radius_header { | ||||
| 	uint8_t code; | ||||
| 	uint8_t id; | ||||
| 	uint16_t len; | ||||
| 	char auth[16]; | ||||
| 	char avp[]; | ||||
| }; | ||||
|  | ||||
| struct radius_tlv { | ||||
| 	uint8_t id; | ||||
| 	uint8_t len; | ||||
| 	char data[]; | ||||
| }; | ||||
|  | ||||
| struct radius_station_id { | ||||
| 	char id[256]; | ||||
| 	enum socket_type type; | ||||
| }; | ||||
|  | ||||
| struct radius_station { | ||||
| 	struct avl_node avl; | ||||
| 	struct radius_station_id key; | ||||
| 	int port; | ||||
| }; | ||||
|  | ||||
| static struct ubus_auto_conn conn; | ||||
| static uint32_t ucentral; | ||||
| static struct blob_buf b; | ||||
|  | ||||
| static int | ||||
| avl_memcmp(const void *k1, const void *k2, void *ptr) | ||||
| { | ||||
| 	return memcmp(k1, k2, sizeof(struct radius_station_id)); | ||||
| } | ||||
|  | ||||
| static AVL_TREE(radius_stations, avl_memcmp, false, NULL); | ||||
|  | ||||
| static void | ||||
| radius_station_add(char *id, int port, enum socket_type type) | ||||
| { | ||||
| 	struct radius_station *station; | ||||
| 	struct radius_station_id key = { .type = type }; | ||||
|  | ||||
| 	strcpy(key.id, id); | ||||
| 	station = avl_find_element(&radius_stations, &key, station, avl); | ||||
|  | ||||
| 	if (!station) { | ||||
| 		printf("new station/port, adding to avl tree\n"); | ||||
| 		station = malloc(sizeof(*station)); | ||||
| 		memset(station, 0, sizeof(*station)); | ||||
| 		strcpy(station->key.id, id); | ||||
| 		station->key.type = type; | ||||
| 		station->avl.key = &station->key; | ||||
| 		avl_insert(&radius_stations, &station->avl); | ||||
| 	} | ||||
| 	station->port = port; | ||||
| } | ||||
|  | ||||
|  | ||||
| static char * | ||||
| b64(char *src, int len) | ||||
| { | ||||
| 	char *dst; | ||||
| 	int ret; | ||||
|  | ||||
| 	if (!src) | ||||
| 		return NULL; | ||||
| 	dst = malloc(len * 4); | ||||
| 	ret = b64_encode(src, len, dst, len * 4); | ||||
| 	if (ret < 1) { | ||||
| 		free(dst); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return dst; | ||||
| } | ||||
|  | ||||
| static void | ||||
| radius_forward(char *buf, char *server, enum socket_type type) | ||||
| { | ||||
| 	struct radius_header *hdr = (struct radius_header *) buf; | ||||
| 	struct ubus_request async = { }; | ||||
| 	char *data = b64(buf, hdr->len); | ||||
|  | ||||
| 	if (!data) | ||||
| 		return; | ||||
|  | ||||
| 	blob_buf_init(&b, 0); | ||||
| 	switch (type) { | ||||
| 	case RADIUS_AUTH: | ||||
| 		blobmsg_add_string(&b, "radius", "auth"); | ||||
| 		break; | ||||
| 	case RADIUS_ACCT: | ||||
| 		blobmsg_add_string(&b, "radius", "acct"); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	blobmsg_add_string(&b, "data", data); | ||||
| 	blobmsg_add_string(&b, "dst", server); | ||||
|  | ||||
| 	ubus_invoke_async(&conn.ctx, ucentral, "radius", b.head, &async); | ||||
| 	ubus_abort_request(&conn.ctx, &async); | ||||
|  | ||||
| 	free(data); | ||||
| } | ||||
|  | ||||
| static int | ||||
| radius_parse(char *buf, int len, int port, enum socket_type type) | ||||
| { | ||||
| 	struct radius_header *hdr = (struct radius_header *) buf; | ||||
| 	struct radius_tlv *station_id = NULL; | ||||
| 	char station_id_str[256] = {}; | ||||
| 	char server_ip_str[256] = {}; | ||||
| 	void *avp = hdr->avp; | ||||
|  | ||||
| 	hdr->len = ntohs(hdr->len); | ||||
|  | ||||
| 	if (hdr->len != len) { | ||||
| 		printf("invalid header length\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	printf("\tcode:%d, id:%d, len:%d\n", hdr->code, hdr->id, hdr->len); | ||||
|  | ||||
| 	len -= sizeof(*hdr); | ||||
|  | ||||
| 	while (len > 0) { | ||||
| 		struct radius_tlv *tlv = (struct radius_tlv *)avp; | ||||
|  | ||||
| 		if (len < tlv->len) { | ||||
| 			printf("invalid TLV length\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		if (tlv->id == TLV_STATION_ID) | ||||
| 			station_id = tlv; | ||||
| 		if (tlv->id == TLV_VENDOR && ntohl(*(uint32_t *) tlv->data) == TIP_VENDOR) { | ||||
| 			struct radius_tlv *vendor = (struct radius_tlv *) &tlv->data[6]; | ||||
|  | ||||
| 			if (vendor->id == TLV_TIP_SERVER_V4) | ||||
| 				strncpy(server_ip_str, vendor->data, vendor->len - 2); | ||||
| 		} | ||||
|  | ||||
| 		printf("\tID:%d, len:%d\n", tlv->id, tlv->len); | ||||
| 		avp += tlv->len; | ||||
| 		len -= tlv->len; | ||||
| 	} | ||||
|  | ||||
| 	if (!station_id) { | ||||
| 		printf("no station ID found\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!*server_ip_str) { | ||||
| 		printf("no server ip found\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	memcpy(station_id_str, station_id->data, station_id->len - 2); | ||||
| 	printf("\tcalling station id:%s, server ip:%s\n", station_id_str, server_ip_str); | ||||
| 	radius_station_add(station_id_str, port, type); | ||||
|  | ||||
| 	radius_forward(buf, server_ip_str, type); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| sock_recv(struct uloop_fd *u, unsigned int events) | ||||
| { | ||||
| 	static char buf[RAD_PROX_BUFLEN]; | ||||
| 	static char cmsg_buf[( CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1]; | ||||
| 	static struct sockaddr_in sin; | ||||
| 	char addr_str[INET_ADDRSTRLEN]; | ||||
| 	static struct iovec iov = { | ||||
| 		.iov_base = buf, | ||||
| 		.iov_len = sizeof(buf) | ||||
| 	}; | ||||
| 	static struct msghdr msg = { | ||||
| 		.msg_name = &sin, | ||||
| 		.msg_namelen = sizeof(sin), | ||||
| 		.msg_iov = &iov, | ||||
| 		.msg_iovlen = 1, | ||||
| 		.msg_control = cmsg_buf, | ||||
| 		.msg_controllen = sizeof(cmsg_buf), | ||||
| 	}; | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	struct radius_socket *sock = container_of(u, struct radius_socket, fd); | ||||
| 	int len; | ||||
|  | ||||
| 	do { | ||||
| 		struct in_pktinfo *pkti = NULL; | ||||
|  | ||||
| 		len = recvmsg(u->fd, &msg, 0); | ||||
| 		if (len < 0) { | ||||
| 			switch (errno) { | ||||
| 			case EAGAIN: | ||||
| 				return; | ||||
| 			case EINTR: | ||||
| 				continue; | ||||
| 			default: | ||||
| 				perror("recvmsg"); | ||||
| 				uloop_fd_delete(u); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | ||||
| 			if (cmsg->cmsg_type != IP_PKTINFO) | ||||
| 				continue; | ||||
|  | ||||
| 			pkti = (struct in_pktinfo *) CMSG_DATA(cmsg); | ||||
| 		} | ||||
|  | ||||
| 		if (!pkti) { | ||||
| 			printf("Received packet without ifindex\n"); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		inet_ntop(AF_INET, &sin.sin_addr, addr_str, sizeof(addr_str)); | ||||
| 		printf("RX: src:%s:%d, len=%d\n", addr_str, sin.sin_port, len); | ||||
| 		radius_parse(buf, len, sin.sin_port, sock->type); | ||||
| 	} while (1); | ||||
| } | ||||
|  | ||||
| static struct radius_socket * | ||||
| sock_open(char *port, enum socket_type type) | ||||
| { | ||||
| 	struct radius_socket *sock = malloc(sizeof(*sock)); | ||||
| 	int yes = 1; | ||||
|  | ||||
| 	if (!sock) | ||||
| 		return NULL; | ||||
|  | ||||
| 	memset(sock, 0, sizeof(*sock)); | ||||
|  | ||||
| 	sock->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK | | ||||
| 			    USOCK_NUMERIC | USOCK_IPV4ONLY, | ||||
| 			    "127.0.0.1", port); | ||||
| 	if (sock->fd.fd < 0) { | ||||
|                 perror("usock"); | ||||
| 		free(sock); | ||||
|                 return NULL; | ||||
|         } | ||||
|         if (setsockopt(sock->fd.fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) | ||||
| 		perror("setsockopt(IP_PKTINFO)"); | ||||
|  | ||||
| 	sock->type = type; | ||||
| 	sock->fd.cb = sock_recv; | ||||
|  | ||||
| 	uloop_fd_add(&sock->fd, ULOOP_READ); | ||||
|  | ||||
| 	return sock; | ||||
| } | ||||
|  | ||||
| static void | ||||
| ubus_event_handler_cb(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]; | ||||
| 	char *path; | ||||
| 	uint32_t id; | ||||
|  | ||||
| 	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, "ucentral")) | ||||
| 		return; | ||||
| 	if (!strcmp("ubus.object.remove", type)) | ||||
| 		ucentral = 0; | ||||
| 	else | ||||
| 		ucentral = id; | ||||
| } | ||||
|  | ||||
| static struct ubus_event_handler ubus_event_handler = { .cb = ubus_event_handler_cb }; | ||||
|  | ||||
| static void | ||||
| ubus_connect_handler(struct ubus_context *ctx) | ||||
| { | ||||
| 	ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.add"); | ||||
| 	ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.remove"); | ||||
|  | ||||
| 	ubus_lookup_id(ctx, "ucentral", &ucentral); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|  | ||||
| 	uloop_init(); | ||||
|  | ||||
| 	conn.cb = ubus_connect_handler; | ||||
| 	ubus_auto_connect(&conn); | ||||
|  | ||||
| 	sock_open("1812", RADIUS_AUTH); | ||||
| 	sock_open("1813", RADIUS_ACCT); | ||||
|  | ||||
| 	uloop_run(); | ||||
| 	uloop_end(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -4,10 +4,10 @@ PKG_NAME:=ucentral-client | ||||
| PKG_RELEASE:=1 | ||||
|  | ||||
| PKG_SOURCE_URL=https://github.com/blogic/ucentral-client.git | ||||
| PKG_MIRROR_HASH:=37a1b7393cf5d15dbcd4840d9ffb2b16bf5b43cdd5c0da955c744bfc10211cbc | ||||
| PKG_MIRROR_HASH:=ca93c95eaec7c4db45de3972db537ca9f022b00801d5df9813f135d9583d3f98 | ||||
| PKG_SOURCE_PROTO:=git | ||||
| PKG_SOURCE_DATE:=2022-01-10 | ||||
| PKG_SOURCE_VERSION:=6cb4485ab49c5ab9244fb55af9fd0e1801f154f4 | ||||
| PKG_SOURCE_VERSION:=e749518bf14bc11ee41c445b168a26ae3860a35b | ||||
|  | ||||
| PKG_LICENSE:=BSD-3-Clause | ||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||
|   | ||||
| @@ -4,11 +4,9 @@ PKG_NAME:=ucentral-schema | ||||
| PKG_RELEASE:=1 | ||||
|  | ||||
| PKG_SOURCE_URL=https://github.com/blogic/ucentral-schema.git | ||||
| PKG_MIRROR_HASH:=562e558135f2da69ae4ae0573813b06b61d3071a026d478678822b13d5b7dee0 | ||||
| #PKG_MIRROR_HASH:=cffdabf01601cd9508d77301fc9f6ab65a2f99ad4a35e3eeed31e9c6b161f63c | ||||
| PKG_SOURCE_PROTO:=git | ||||
| PKG_SOURCE_DATE:=2022-01-17 | ||||
| PKG_SOURCE_VERSION:=48292adfa238fb81bb3fe0c047e28541c6fe5875 | ||||
| PKG_SOURCE_VERSION:=992d376434a8c47557140da574ad40349e61aa32 | ||||
|  | ||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||
| PKG_LICENSE:=BSD-3-Clause | ||||
|   | ||||
| @@ -0,0 +1,91 @@ | ||||
| { | ||||
| 	"uuid": 2, | ||||
| 	"radios": [ | ||||
| 		{ | ||||
| 			"band": "5G", | ||||
| 			"country": "CA", | ||||
| 			"channel-mode": "HE", | ||||
| 			"channel-width": 80, | ||||
| 			"channel": 36 | ||||
| 		} | ||||
| 	], | ||||
|  | ||||
| 	"interfaces": [ | ||||
| 		{ | ||||
| 			"name": "WAN", | ||||
| 			"role": "upstream", | ||||
| 			"ethernet": [ | ||||
| 				{ | ||||
| 					"select-ports": [ | ||||
| 						"WAN*" | ||||
| 					] | ||||
| 				} | ||||
| 			], | ||||
| 			"ipv4": { | ||||
| 				"addressing": "dynamic" | ||||
| 			}, | ||||
| 			"ssids": [ | ||||
| 				{ | ||||
| 					"name": "OpenWifi", | ||||
| 					"wifi-bands": [ | ||||
| 						"2G", "5G" | ||||
| 					], | ||||
| 					"bss-mode": "ap", | ||||
| 					"encryption": { | ||||
| 						"proto": "wpa2", | ||||
| 						"ieee80211w": "optional" | ||||
| 					}, | ||||
| 					"radius": { | ||||
| 						"authentication": { | ||||
| 							"host": "192.168.178.192", | ||||
| 							"port": 1812, | ||||
| 							"secret": "secret" | ||||
| 						}, | ||||
| 						"accounting": { | ||||
| 							"host": "192.168.178.192", | ||||
| 							"port": 1813, | ||||
| 							"secret": "secret" | ||||
| 						} | ||||
| 					}, | ||||
| 					"services": [ "radius-gw-proxy" ] | ||||
| 				} | ||||
| 			] | ||||
| 		}, | ||||
| 		{ | ||||
| 			"name": "LAN", | ||||
| 			"role": "downstream", | ||||
| 			"services": [ "ssh" ], | ||||
| 			"ethernet": [ | ||||
| 				{ | ||||
| 					"select-ports": [ | ||||
| 						"LAN*" | ||||
| 					] | ||||
| 				} | ||||
| 			], | ||||
| 			"ipv4": { | ||||
| 				"addressing": "static", | ||||
| 				"subnet": "192.168.1.1/24", | ||||
| 				"dhcp": { | ||||
| 					"lease-first": 10, | ||||
| 					"lease-count": 100, | ||||
| 					"lease-time": "6h" | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	], | ||||
| 	"metrics": { | ||||
| 		"statistics": { | ||||
| 			"interval": 120, | ||||
| 			"types": [ "ssids", "lldp", "clients" ] | ||||
| 		}, | ||||
| 		"health": { | ||||
| 			"interval": 120 | ||||
| 		} | ||||
| 	}, | ||||
| 	"services": { | ||||
| 		"ssh": { | ||||
| 			"port": 22 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -30,6 +30,7 @@ packages: | ||||
|   - lldpd | ||||
|   - maverick | ||||
|   - opennds | ||||
|   - radius-gw-proxy | ||||
|   - radsecproxy | ||||
|   - ratelimit | ||||
|   - rtty-openssl | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 John Crispin
					John Crispin