Compare commits
10 Commits
ef6317e324
...
62ca2ad4a8
Author | SHA1 | Date |
---|---|---|
Linus Lüssing | 62ca2ad4a8 | |
Florian Maurer | 3d08b0fee8 | |
Matthias Schiffer | 53ea3b8977 | |
Matthias Schiffer | 28a35ea2c9 | |
Matthias Schiffer | dc99bbb906 | |
Christian Buschau | 2cf0156fd7 | |
Matthias Schiffer | ce2e6ac193 | |
Matthias Schiffer | 72efd369ed | |
Matthias Schiffer | c113a73912 | |
Matthias Schiffer | 8329130a7c |
|
@ -1 +0,0 @@
|
|||
template/nosysupgrade.c
|
|
@ -0,0 +1 @@
|
|||
template/subtarget.c
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <libplatforminfo.h>
|
||||
#include "../common.h"
|
||||
|
||||
|
||||
#define _STRINGIFY(s) #s
|
||||
#define STRINGIFY(s) _STRINGIFY(s)
|
||||
|
||||
|
||||
static char * model = NULL;
|
||||
|
||||
|
||||
__attribute__((constructor)) static void init(void) {
|
||||
model = read_line("/tmp/sysinfo/model");
|
||||
}
|
||||
|
||||
__attribute__((destructor)) static void deinit(void) {
|
||||
free(model);
|
||||
|
||||
model = NULL;
|
||||
}
|
||||
|
||||
|
||||
const char * platforminfo_get_board_name(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char * platforminfo_get_model(void) {
|
||||
return model;
|
||||
}
|
||||
|
||||
const char * platforminfo_get_image_name(void) {
|
||||
return STRINGIFY(TARGET) "-" STRINGIFY(SUBTARGET);
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <libplatforminfo.h>
|
||||
#include "../common.h"
|
||||
|
||||
|
||||
#define _STRINGIFY(s) #s
|
||||
#define STRINGIFY(s) _STRINGIFY(s)
|
||||
|
||||
|
||||
static char * model = NULL;
|
||||
|
||||
|
||||
__attribute__((constructor)) static void init(void) {
|
||||
model = read_line("/tmp/sysinfo/model");
|
||||
}
|
||||
|
||||
__attribute__((destructor)) static void deinit(void) {
|
||||
free(model);
|
||||
|
||||
model = NULL;
|
||||
}
|
||||
|
||||
|
||||
const char * platforminfo_get_board_name(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char * platforminfo_get_model(void) {
|
||||
return model;
|
||||
}
|
||||
|
||||
const char * platforminfo_get_image_name(void) {
|
||||
return STRINGIFY(TARGET) "-" STRINGIFY(SUBTARGET);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
template/subtarget.c
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (C) 2023 Linus Lüssing <linus.luessing@c0d3.blue>
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=brmldproxy
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2023-12-31
|
||||
PKG_SOURCE_URL=https://github.com/T-X/brmldproxy.git
|
||||
PKG_SOURCE_VERSION:=4d7fdb1a5c6e726b4c1930ad411d5571e09fa2f0
|
||||
PKG_MIRROR_HASH:=1541eeaf6ae2fb4390448f02c5486da708cfa4d6200be77f884b47a2c86a7d06
|
||||
|
||||
PKG_MAINTAINER:=Linus Lüssing <linus.luessing@c0d3.blue>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/brmldproxy
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Bridge MLD Proxy
|
||||
DEPENDS:=+tc
|
||||
endef
|
||||
|
||||
define Package/brmldproxy/description
|
||||
A userspace controlled MLD proxy implementation for a Linux bridge.
|
||||
The bridge itself will appear as a single multicast listening host
|
||||
to any MLD querier on a configured proxy port, acting in deputy
|
||||
for any other multicast listener behind adjacent bridge ports.
|
||||
This potentially reduces MLD report overhead.
|
||||
brmldproxy further allows to filter out specific multicast groups
|
||||
and bridge ports from its combined MLD report.
|
||||
endef
|
||||
|
||||
define Package/brmldproxy/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/brmldproxy $(1)/usr/sbin/
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,brmldproxy))
|
|
@ -0,0 +1,20 @@
|
|||
#config brmldproxy 'lan'
|
||||
# option disabled '1'
|
||||
# # The bridge to apply brmldproxy to. Either the
|
||||
# # bridge interface name or the UCI network interface
|
||||
# # section name.
|
||||
# option bridge 'lan'
|
||||
# # Currently only "ipv6" is supported, optional.
|
||||
# option family 'ipv6'
|
||||
# # bridge port to proxy to
|
||||
# list proxiedport 'wan0'
|
||||
# # bridge port to proxy from
|
||||
# list includedport 'lan0'
|
||||
# # bridge port to exclude from proxying
|
||||
# list excludedport 'lan1'
|
||||
# # multicast IP address (range) to exclude from proxying
|
||||
# list excludefilter 'ff00::/ff0e::'
|
||||
# list excludefilter 'ff0e::/64'
|
||||
# # multicast IP address (range) to include in proxying
|
||||
# # (includes ff0e::123 even though ff0e::/64 was excluded above)
|
||||
# list includefilter 'ff0e::123'
|
|
@ -0,0 +1,37 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (C) 2023 Linus Lüssing <linus.luessing@c0d3.blue>
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
[ -z "$INTERFACE" ] && exit 0
|
||||
[ "$ACTION" != "ifup" ] && [ "$ACTION" != "ifdown" ] && exit 0
|
||||
|
||||
/etc/init.d/brmldproxy enabled || exit 0
|
||||
|
||||
|
||||
brmldproxy_handle() {
|
||||
local cfg="$1"
|
||||
local disabled
|
||||
local bridge
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return 0
|
||||
|
||||
config_get bridge "$cfg" bridge
|
||||
|
||||
[ -z "$bridge" ] && return 0
|
||||
[ "$bridge" != "$INTERFACE" ] && return 0
|
||||
|
||||
if [ "$ACTION" = "ifup" ]; then
|
||||
/etc/init.d/brmldproxy start "$cfg" || return 0
|
||||
else
|
||||
/etc/init.d/brmldproxy stop "brmldproxy.$cfg" || return 0
|
||||
fi
|
||||
|
||||
# success, stop
|
||||
return 1
|
||||
}
|
||||
|
||||
config_load brmldproxy
|
||||
|
||||
config_foreach brmldproxy_handle brmldproxy
|
|
@ -0,0 +1,111 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (C) 2023 Linus Lüssing <linus.luessing@c0d3.blue>
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
START=19
|
||||
STOP=90
|
||||
|
||||
brmldproxy_start() {
|
||||
local cfg="$1"
|
||||
local namespace="$2"
|
||||
local disabled
|
||||
|
||||
local ifname
|
||||
local family
|
||||
local bridge
|
||||
local includedports
|
||||
local excludedports
|
||||
local proxiedports
|
||||
local includefilters
|
||||
local excludefilters
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return 0
|
||||
|
||||
config_get bridge "$cfg" "bridge"
|
||||
config_get family "$cfg" "family"
|
||||
config_get includedports "$cfg" "includedport"
|
||||
config_get excludedports "$cfg" "excludedport"
|
||||
config_get proxiedports "$cfg" "proxiedport"
|
||||
config_get includefilters "$cfg" "includefilter"
|
||||
config_get excludefilters "$cfg" "excludefilter"
|
||||
|
||||
[ -z "$bridge" ] && {
|
||||
echo "Error: no bridge specified for $cfg" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
. /lib/functions/network.sh
|
||||
|
||||
if network_get_device ifname "$bridge" && [ -n "$ifname" ]; then
|
||||
bridge="$ifname"
|
||||
fi
|
||||
|
||||
[ -n "$excludedports" ] && excludedports=$(echo "$excludedports" | sed 's/[^ ]* */-e &/g')
|
||||
[ -n "$includedports" ] && includedports=$(echo "$includedports" | sed 's/[^ ]* */-i &/g')
|
||||
[ -n "$proxiedports" ] && proxiedports=$(echo "$proxiedports" | sed 's/[^ ]* */-p &/g')
|
||||
[ -n "$includefilters" ] && includefilters=$(echo "$includefilters" | sed 's/[^ ]* */-I &/g')
|
||||
[ -n "$excludefilters" ] && excludefilters=$(echo "$excludefilters" | sed 's/[^ ]* */-E &/g')
|
||||
|
||||
[ -z "$namespace" ] && namespace="brmldproxy"
|
||||
|
||||
procd_open_instance "$namespace.$cfg"
|
||||
|
||||
procd_set_param command /usr/sbin/brmldproxy
|
||||
[ "${family}" = "ipv4" ] && procd_append_param command -4
|
||||
[ "${family}" = "ipv6" ] && procd_append_param command -6
|
||||
procd_append_param command -b "$bridge"
|
||||
[ -n "$excludedports" ] && procd_append_param command $excludedports
|
||||
[ -n "$includedports" ] && procd_append_param command $includedports
|
||||
[ -n "$proxiedports" ] && procd_append_param command $proxiedports
|
||||
[ -n "$includefilters" ] && procd_append_param command $includefilters
|
||||
[ -n "$excludefilters" ] && procd_append_param command $excludefilters
|
||||
|
||||
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
|
||||
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
start_service() {
|
||||
local cfg="$1"
|
||||
local namespace="$2"
|
||||
local instance_found=0
|
||||
|
||||
. /lib/functions/network.sh
|
||||
|
||||
# no procd boot startup, via hotplug or manual only
|
||||
[ $PPID -eq 1 ] && return 0
|
||||
|
||||
config_cb() {
|
||||
local type="$1"
|
||||
local name="$2"
|
||||
if [ "$type" = "brmldproxy" ]; then
|
||||
if [ -n "$cfg" -a "$cfg" = "$name" ]; then
|
||||
instance_found=1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
config_load brmldproxy
|
||||
|
||||
if [ -n "$cfg" ]; then
|
||||
[ "$instance_found" -gt 0 ] || return
|
||||
brmldproxy_start "$cfg" "$namespace"
|
||||
else
|
||||
config_foreach brmldproxy_start brmldproxy "$namespace"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
local cfg="$1"
|
||||
local namespace="$2"
|
||||
|
||||
[ -z "$namespace" ] && namespace="brmldproxy"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger brmldproxy
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=simple-tc
|
||||
PKG_VERSION:=2
|
||||
|
||||
PKG_CONFIG_DEPENDS := CONFIG_KERNEL_NET_SCH_TBF CONFIG_KERNEL_NET_SCH_INGRESS CONFIG_KERNEL_NET_CLS_BASIC KERNEL_NET_ACT_POLICE
|
||||
PKG_VERSION:=3
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
|
@ -11,7 +9,7 @@ define Package/simple-tc
|
|||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Simple bandwidth limiting
|
||||
DEPENDS:=+!KERNEL_NET_SCH_TBF:kmod-sched-core +!KERNEL_NET_SCH_INGRESS:kmod-sched-core +!KERNEL_NET_CLS_BASIC:kmod-sched +!KERNEL_NET_ACT_POLICE:kmod-sched +libnl-tiny
|
||||
DEPENDS:=+kmod-sched-core +kmod-sched-act-police +libnl-tiny
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=tunneldigger
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/wlanslovenija/tunneldigger.git
|
||||
PKG_SOURCE_DATE:=2020-05-17
|
||||
PKG_SOURCE_VERSION:=8995046a2ba8f111391e01e6bb38a352c0cedaa1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/tunneldigger
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
DEPENDS:=+libnl-tiny +kmod-l2tp +kmod-l2tp-eth +librt +libpthread
|
||||
TITLE:=L2TPv3 tunnel broker client
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-I$(STAGING_DIR)/usr/include/libnl-tiny \
|
||||
-I$(STAGING_DIR)/usr/include \
|
||||
-DLIBNL_TINY
|
||||
|
||||
define Build/Prepare
|
||||
$(call Build/Prepare/Default)
|
||||
$(CP) $(PKG_BUILD_DIR)/client/* $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Package/tunneldigger/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tunneldigger $(1)/usr/bin/tunneldigger
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/tunneldigger.init $(1)/etc/init.d/tunneldigger
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DATA) ./files/config.default $(1)/etc/config/tunneldigger
|
||||
endef
|
||||
|
||||
define Package/tunneldigger/conffiles
|
||||
/etc/config/tunneldigger
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,tunneldigger))
|
|
@ -1,10 +0,0 @@
|
|||
config broker
|
||||
list address 'x.y.z.w:8942'
|
||||
list address 'x.y.z.w:53'
|
||||
list address 'x.y.z.w:123'
|
||||
option uuid 'abcd'
|
||||
option group 'root'
|
||||
option interface 'l2tp0'
|
||||
option limit_bw_down '1024'
|
||||
option broker_selection 'usage'
|
||||
option enabled '0'
|
|
@ -1,97 +0,0 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
|
||||
. $IPKG_INSTROOT/lib/functions/network.sh
|
||||
|
||||
START=90
|
||||
|
||||
PIDPATH=/var/run
|
||||
tunnel_id=1
|
||||
|
||||
missing() {
|
||||
echo "Not starting tunneldigger - missing $1" >&2
|
||||
}
|
||||
|
||||
config_cb() {
|
||||
local cfg="$CONFIG_SECTION"
|
||||
config_get configname "$cfg" TYPE
|
||||
case "$configname" in
|
||||
broker)
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
config_get addresses "$cfg" address
|
||||
config_get uuid "$cfg" uuid
|
||||
config_get interface "$cfg" interface
|
||||
config_get group "$cfg" group
|
||||
config_get limit_bw_down "$cfg" limit_bw_down
|
||||
config_get hook_script "$cfg" hook_script
|
||||
config_get bind_interface "$cfg" bind_interface
|
||||
config_get broker_selection "$cfg" broker_selection
|
||||
|
||||
[ $enabled -eq 0 ] && return
|
||||
|
||||
local broker_opts=""
|
||||
for address in $addresses; do
|
||||
append broker_opts "-b ${address}"
|
||||
done
|
||||
|
||||
[ ! -z "${limit_bw_down}" ] && append broker_opts "-L ${limit_bw_down}"
|
||||
[ ! -z "${hook_script}" ] && append broker_opts "-s ${hook_script}"
|
||||
[ ! -z "${bind_interface}" ] && {
|
||||
# Resolve logical interface name.
|
||||
unset _bind_interface
|
||||
network_get_device _bind_interface "${bind_interface}" || _bind_interface="${bind_interface}"
|
||||
append broker_opts "-I ${_bind_interface}"
|
||||
}
|
||||
[ ! -z "${broker_selection}" ] && {
|
||||
# Set broker selection.
|
||||
case "${broker_selection}" in
|
||||
usage)
|
||||
append broker_opts "-a"
|
||||
;;
|
||||
first)
|
||||
append broker_opts "-g"
|
||||
;;
|
||||
random)
|
||||
append broker_opts "-r"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ -z "$uuid" ]; then
|
||||
missing uuid
|
||||
return
|
||||
elif [ -z "$interface" ]; then
|
||||
missing interface
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Starting tunneldigger on ${interface}"
|
||||
/sbin/start-stop-daemon -S -q -b -m -c root:${group} -p ${PIDPATH}/tunneldigger.${interface}.pid -x /usr/bin/tunneldigger -- -u ${uuid} -i ${interface} -t ${tunnel_id} ${broker_opts}
|
||||
|
||||
let tunnel_id++
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
start() {
|
||||
config_load tunneldigger
|
||||
}
|
||||
|
||||
stop() {
|
||||
for PIDFILE in `find ${PIDPATH}/ -name "tunneldigger\.*\.pid"`; do
|
||||
PID="$(cat ${PIDFILE})"
|
||||
IFACE="$(echo ${PIDFILE} | awk -F\/tunneldigger '{print $2}' | cut -d'.' -f2)"
|
||||
echo "Stopping tunneldigger for interface ${IFACE}"
|
||||
start-stop-daemon -K -q -p $PIDFILE
|
||||
while test -d "/proc/${PID}"; do
|
||||
echo " waiting for tunneldigger to stop"
|
||||
sleep 1
|
||||
done
|
||||
echo " tunneldigger stopped"
|
||||
done
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=uradvd
|
||||
PKG_VERSION:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/uradvd
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A tiny radvd
|
||||
DEPENDS:=@IPV6 +librt
|
||||
endef
|
||||
|
||||
define Package/uradvd/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/uradvd $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,uradvd))
|
|
@ -1,4 +0,0 @@
|
|||
all: uradvd
|
||||
|
||||
uradvd: uradvd.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lrt
|
|
@ -1,748 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#define MAX_PREFIXES 8
|
||||
#define MAX_RDNSS 3
|
||||
|
||||
/* These are in seconds */
|
||||
#define AdvValidLifetime 86400u
|
||||
#define AdvPreferredLifetime 14400u
|
||||
#define AdvDefaultLifetime 0u
|
||||
#define AdvCurHopLimit 64u
|
||||
#define AdvRDNSSLifetime 1200u
|
||||
|
||||
#define MinRtrAdvInterval 200u
|
||||
#define MaxRtrAdvInterval 600u
|
||||
|
||||
/* And these in milliseconds */
|
||||
#define MAX_RA_DELAY_TIME 500u
|
||||
#define MIN_DELAY_BETWEEN_RAS 3000u
|
||||
|
||||
|
||||
struct icmpv6_opt {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t data[6];
|
||||
};
|
||||
|
||||
|
||||
struct iface {
|
||||
bool ok;
|
||||
unsigned int ifindex;
|
||||
struct in6_addr ifaddr;
|
||||
uint8_t mac[6];
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) nd_opt_rdnss {
|
||||
uint8_t nd_opt_rdnss_type;
|
||||
uint8_t nd_opt_rdnss_len;
|
||||
uint16_t nd_opt_rdnss_reserved;
|
||||
uint32_t nd_opt_rdnss_lifetime;
|
||||
};
|
||||
|
||||
static struct global {
|
||||
struct iface iface;
|
||||
|
||||
struct timespec time;
|
||||
struct timespec next_advert;
|
||||
struct timespec next_advert_earliest;
|
||||
|
||||
int icmp_sock;
|
||||
int rtnl_sock;
|
||||
|
||||
const char *ifname;
|
||||
|
||||
uint16_t adv_default_lifetime;
|
||||
|
||||
size_t n_prefixes;
|
||||
struct in6_addr prefixes[MAX_PREFIXES];
|
||||
bool prefixes_onlink[MAX_PREFIXES];
|
||||
|
||||
size_t n_rdnss;
|
||||
struct in6_addr rdnss[MAX_RDNSS];
|
||||
} G = {
|
||||
.rtnl_sock = -1,
|
||||
.icmp_sock = -1,
|
||||
.adv_default_lifetime = AdvDefaultLifetime,
|
||||
};
|
||||
|
||||
|
||||
static inline void print_error(const char *prefix, const char *message, int err) {
|
||||
if (err)
|
||||
fprintf(stderr, "uradvd: %s: %s: %s\n", prefix, message, strerror(err));
|
||||
else
|
||||
fprintf(stderr, "uradvd: %s: %s\n", prefix, message);
|
||||
}
|
||||
|
||||
static inline void exit_error(const char *message, int err) {
|
||||
print_error("error", message, err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void exit_errno(const char *message) {
|
||||
exit_error(message, errno);
|
||||
}
|
||||
|
||||
static inline void warn_error(const char *message, int err) {
|
||||
print_error("error", message, err);
|
||||
}
|
||||
|
||||
static inline void warn_errno(const char *message) {
|
||||
warn_error(message, errno);
|
||||
}
|
||||
|
||||
|
||||
static inline void update_time(void) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &G.time);
|
||||
}
|
||||
|
||||
/* Compares two timespecs and returns true if tp1 is after tp2 */
|
||||
static inline bool timespec_after(const struct timespec *tp1, const struct timespec *tp2) {
|
||||
return (tp1->tv_sec > tp2->tv_sec ||
|
||||
(tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec));
|
||||
}
|
||||
|
||||
/* Returns (tp1 - tp2) in milliseconds */
|
||||
static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) {
|
||||
return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6;
|
||||
}
|
||||
|
||||
static inline void timespec_add(struct timespec *tp, unsigned int ms) {
|
||||
tp->tv_sec += ms/1000;
|
||||
tp->tv_nsec += (ms%1000) * 1e6;
|
||||
|
||||
if (tp->tv_nsec >= 1e9) {
|
||||
tp->tv_nsec -= 1e9;
|
||||
tp->tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline int setsockopt_int(int socket, int level, int option, int value) {
|
||||
return setsockopt(socket, level, option, &value, sizeof(value));
|
||||
}
|
||||
|
||||
|
||||
static void init_random(void) {
|
||||
unsigned int seed;
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
exit_errno("can't open /dev/urandom");
|
||||
|
||||
if (read(fd, &seed, sizeof(seed)) != sizeof(seed))
|
||||
exit_errno("can't read from /dev/urandom");
|
||||
|
||||
close(fd);
|
||||
|
||||
srandom(seed);
|
||||
}
|
||||
|
||||
static inline int rand_range(int min, int max) {
|
||||
unsigned int r = (unsigned int)random();
|
||||
return (r%(max-min) + min);
|
||||
}
|
||||
|
||||
static void init_icmp(void) {
|
||||
G.icmp_sock = socket(AF_INET6, SOCK_RAW|SOCK_NONBLOCK, IPPROTO_ICMPV6);
|
||||
if (G.icmp_sock < 0)
|
||||
exit_errno("can't open ICMP socket");
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_RAW, IPV6_CHECKSUM, 2);
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1);
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1);
|
||||
|
||||
struct icmp6_filter filter;
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
|
||||
setsockopt(G.icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
|
||||
}
|
||||
|
||||
static void init_rtnl(void) {
|
||||
G.rtnl_sock = socket(AF_NETLINK, SOCK_DGRAM|SOCK_NONBLOCK, NETLINK_ROUTE);
|
||||
if (G.rtnl_sock < 0)
|
||||
exit_errno("can't open RTNL socket");
|
||||
|
||||
struct sockaddr_nl snl = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR,
|
||||
};
|
||||
if (bind(G.rtnl_sock, (struct sockaddr *)&snl, sizeof(snl)) < 0)
|
||||
exit_errno("can't bind RTNL socket");
|
||||
}
|
||||
|
||||
|
||||
static void schedule_advert(bool nodelay) {
|
||||
struct timespec t = G.time;
|
||||
|
||||
if (nodelay)
|
||||
timespec_add(&t, rand_range(0, MAX_RA_DELAY_TIME));
|
||||
else
|
||||
timespec_add(&t, rand_range(MinRtrAdvInterval*1000, MaxRtrAdvInterval*1000));
|
||||
|
||||
if (timespec_after(&G.next_advert_earliest, &t))
|
||||
t = G.next_advert_earliest;
|
||||
|
||||
if (!nodelay || timespec_after(&G.next_advert, &t))
|
||||
G.next_advert = t;
|
||||
}
|
||||
|
||||
|
||||
static int join_multicast(void) {
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = {
|
||||
.s6_addr = {
|
||||
/* all-routers address */
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
}
|
||||
},
|
||||
.ipv6mr_interface = G.iface.ifindex,
|
||||
};
|
||||
|
||||
if (setsockopt(G.icmp_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == 0) {
|
||||
return 2;
|
||||
}
|
||||
else if (errno != EADDRINUSE) {
|
||||
warn_errno("can't join multicast group");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void update_interface(void) {
|
||||
struct iface old;
|
||||
|
||||
memcpy(&old, &G.iface, sizeof(struct iface));
|
||||
memset(&G.iface, 0, sizeof(struct iface));
|
||||
|
||||
/* Update ifindex */
|
||||
G.iface.ifindex = if_nametoindex(G.ifname);
|
||||
if (!G.iface.ifindex)
|
||||
return;
|
||||
|
||||
/* Update MAC address */
|
||||
struct ifreq ifr = {};
|
||||
strncpy(ifr.ifr_name, G.ifname, sizeof(ifr.ifr_name)-1);
|
||||
if (ioctl(G.icmp_sock, SIOCGIFHWADDR, &ifr) < 0)
|
||||
return;
|
||||
|
||||
memcpy(G.iface.mac, ifr.ifr_hwaddr.sa_data, sizeof(G.iface.mac));
|
||||
|
||||
struct ifaddrs *addrs, *addr;
|
||||
if (getifaddrs(&addrs) < 0) {
|
||||
warn_errno("getifaddrs");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&G.iface.ifaddr, 0, sizeof(G.iface.ifaddr));
|
||||
|
||||
for (addr = addrs; addr; addr = addr->ifa_next) {
|
||||
if (!addr->ifa_addr || addr->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
|
||||
const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)addr->ifa_addr;
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
|
||||
continue;
|
||||
|
||||
if (strncmp(addr->ifa_name, G.ifname, IFNAMSIZ-1) != 0)
|
||||
continue;
|
||||
|
||||
G.iface.ifaddr = in6->sin6_addr;
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&G.iface.ifaddr))
|
||||
return;
|
||||
|
||||
int joined = join_multicast();
|
||||
if (!joined)
|
||||
return;
|
||||
|
||||
setsockopt(G.icmp_sock, SOL_SOCKET, SO_BINDTODEVICE, G.ifname, strnlen(G.ifname, IFNAMSIZ-1));
|
||||
|
||||
G.iface.ok = true;
|
||||
|
||||
if (memcmp(&old, &G.iface, sizeof(struct iface)) != 0 || joined == 2)
|
||||
schedule_advert(true);
|
||||
}
|
||||
|
||||
|
||||
static bool handle_rtnl_link(uint16_t type, const struct ifinfomsg *msg) {
|
||||
switch (type) {
|
||||
case RTM_NEWLINK:
|
||||
if (!G.iface.ok)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_SETLINK:
|
||||
if ((unsigned)msg->ifi_index == G.iface.ifindex)
|
||||
return true;
|
||||
|
||||
if (!G.iface.ok)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
if (G.iface.ok && (unsigned)msg->ifi_index == G.iface.ifindex)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_rtnl_addr(uint16_t type, const struct ifaddrmsg *msg) {
|
||||
switch (type) {
|
||||
case RTM_NEWADDR:
|
||||
if (!G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
if (G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_rtnl_msg(uint16_t type, const void *data) {
|
||||
switch (type) {
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
case RTM_SETLINK:
|
||||
return handle_rtnl_link(type, data);
|
||||
|
||||
case RTM_NEWADDR:
|
||||
case RTM_DELADDR:
|
||||
return handle_rtnl_addr(type, data);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_rtnl(void) {
|
||||
char buffer[4096];
|
||||
|
||||
ssize_t len = recv(G.rtnl_sock, buffer, sizeof(buffer), 0);
|
||||
if (len < 0) {
|
||||
warn_errno("recv");
|
||||
return;
|
||||
}
|
||||
|
||||
const struct nlmsghdr *nh;
|
||||
for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
switch (nh->nlmsg_type) {
|
||||
case NLMSG_DONE:
|
||||
return;
|
||||
|
||||
case NLMSG_ERROR:
|
||||
exit_error("netlink error", 0);
|
||||
|
||||
default:
|
||||
if (handle_rtnl_msg(nh->nlmsg_type, NLMSG_DATA(nh))) {
|
||||
update_interface();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_pktinfo(struct msghdr *msg) {
|
||||
struct cmsghdr *cmsg = (struct cmsghdr*)((char*)msg->msg_control + msg->msg_controllen);
|
||||
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
|
||||
msg->msg_controllen += cmsg->cmsg_len;
|
||||
|
||||
struct in6_pktinfo pktinfo = {
|
||||
.ipi6_addr = G.iface.ifaddr,
|
||||
.ipi6_ifindex = G.iface.ifindex,
|
||||
};
|
||||
|
||||
memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
|
||||
}
|
||||
|
||||
|
||||
static void handle_solicit(void) {
|
||||
struct sockaddr_in6 addr;
|
||||
|
||||
uint8_t buffer[1500] __attribute__((aligned(8)));
|
||||
struct iovec vec = { .iov_base = buffer, .iov_len = sizeof(buffer) };
|
||||
|
||||
uint8_t cbuf[1024] __attribute__((aligned(8)));
|
||||
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &addr,
|
||||
.msg_namelen = sizeof(addr),
|
||||
.msg_iov = &vec,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cbuf,
|
||||
.msg_controllen = sizeof(cbuf),
|
||||
};
|
||||
|
||||
ssize_t len = recvmsg(G.icmp_sock, &msg, 0);
|
||||
if (len < (ssize_t)sizeof(struct nd_router_solicit)) {
|
||||
if (len < 0)
|
||||
warn_errno("recvmsg");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level != IPPROTO_IPV6)
|
||||
continue;
|
||||
|
||||
if (cmsg->cmsg_type != IPV6_HOPLIMIT)
|
||||
continue;
|
||||
|
||||
if (*(int*)CMSG_DATA(cmsg) != 255)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const struct nd_router_solicit *s = (struct nd_router_solicit *)buffer;
|
||||
if (s->nd_rs_hdr.icmp6_type != ND_ROUTER_SOLICIT || s->nd_rs_hdr.icmp6_code != 0)
|
||||
return;
|
||||
|
||||
const struct icmpv6_opt *opt = (struct icmpv6_opt *)(buffer + sizeof(struct nd_router_solicit)), *end = (struct icmpv6_opt *)(buffer+len);
|
||||
|
||||
for (; opt < end; opt += opt->length) {
|
||||
if (opt+1 < end)
|
||||
return;
|
||||
|
||||
if (!opt->length)
|
||||
return;
|
||||
|
||||
if (opt+opt->length < end)
|
||||
return;
|
||||
|
||||
if (opt->type == ND_OPT_SOURCE_LINKADDR && IN6_IS_ADDR_UNSPECIFIED(&addr.sin6_addr))
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt != end)
|
||||
return;
|
||||
|
||||
schedule_advert(true);
|
||||
}
|
||||
|
||||
static void send_advert(void) {
|
||||
if (!G.iface.ok)
|
||||
return;
|
||||
|
||||
struct nd_router_advert advert = {
|
||||
.nd_ra_hdr = {
|
||||
.icmp6_type = ND_ROUTER_ADVERT,
|
||||
.icmp6_dataun.icmp6_un_data8 = {AdvCurHopLimit, 0 /* Flags */, (G.adv_default_lifetime>>8) & 0xff, G.adv_default_lifetime & 0xff },
|
||||
},
|
||||
};
|
||||
|
||||
struct icmpv6_opt lladdr = {ND_OPT_SOURCE_LINKADDR, 1, {}};
|
||||
memcpy(lladdr.data, G.iface.mac, sizeof(G.iface.mac));
|
||||
|
||||
struct nd_opt_prefix_info prefixes[G.n_prefixes];
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < G.n_prefixes; i++) {
|
||||
uint8_t flags = ND_OPT_PI_FLAG_AUTO;
|
||||
|
||||
if (G.prefixes_onlink[i])
|
||||
flags |= ND_OPT_PI_FLAG_ONLINK;
|
||||
|
||||
prefixes[i] = (struct nd_opt_prefix_info){
|
||||
.nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION,
|
||||
.nd_opt_pi_len = 4,
|
||||
.nd_opt_pi_prefix_len = 64,
|
||||
.nd_opt_pi_flags_reserved = flags,
|
||||
.nd_opt_pi_valid_time = htonl(AdvValidLifetime),
|
||||
.nd_opt_pi_preferred_time = htonl(AdvPreferredLifetime),
|
||||
.nd_opt_pi_prefix = G.prefixes[i],
|
||||
};
|
||||
}
|
||||
|
||||
struct nd_opt_rdnss rdnss = {};
|
||||
uint8_t rdnss_ips[G.n_rdnss][16];
|
||||
|
||||
if (G.n_rdnss > 0) {
|
||||
rdnss.nd_opt_rdnss_type = 25;
|
||||
rdnss.nd_opt_rdnss_len = 1 + 2 * G.n_rdnss;
|
||||
rdnss.nd_opt_rdnss_lifetime = htonl(AdvRDNSSLifetime);
|
||||
|
||||
for (i = 0; i < G.n_rdnss; i++)
|
||||
memcpy(rdnss_ips[i], G.rdnss[i].s6_addr, 16);
|
||||
}
|
||||
|
||||
struct iovec vec[5] = {
|
||||
{ .iov_base = &advert, .iov_len = sizeof(advert) },
|
||||
{ .iov_base = &lladdr, .iov_len = sizeof(lladdr) },
|
||||
{ .iov_base = prefixes, .iov_len = sizeof(prefixes) },
|
||||
{ .iov_base = &rdnss, .iov_len = sizeof(rdnss) },
|
||||
{ .iov_base = rdnss_ips, .iov_len = sizeof(rdnss_ips) }
|
||||
};
|
||||
|
||||
struct sockaddr_in6 addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = {
|
||||
.s6_addr = {
|
||||
/* all-nodes address */
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
}
|
||||
},
|
||||
.sin6_scope_id = G.iface.ifindex,
|
||||
};
|
||||
|
||||
uint8_t cbuf[1024] __attribute__((aligned(8))) = {};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &addr,
|
||||
.msg_namelen = sizeof(addr),
|
||||
.msg_iov = vec,
|
||||
.msg_iovlen = G.n_rdnss > 0 ? 5 : 3,
|
||||
.msg_control = cbuf,
|
||||
.msg_controllen = 0,
|
||||
.msg_flags = 0,
|
||||
};
|
||||
|
||||
add_pktinfo(&msg);
|
||||
|
||||
if (sendmsg(G.icmp_sock, &msg, 0) < 0) {
|
||||
G.iface.ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
G.next_advert_earliest = G.time;
|
||||
timespec_add(&G.next_advert_earliest, MIN_DELAY_BETWEEN_RAS);
|
||||
|
||||
schedule_advert(false);
|
||||
}
|
||||
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: uradvd [-h] -i <interface> -a/-p <prefix> [ -a/-p <prefix> ... ] [ --default-lifetime <seconds> ] [ --rdnss <ip> ... ]\n");
|
||||
}
|
||||
|
||||
static void add_rdnss(const char *ip) {
|
||||
if (G.n_rdnss == MAX_RDNSS) {
|
||||
fprintf(stderr, "uradvd: error: maximum number of RDNSS IPs is %i.\n", MAX_RDNSS);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET6, ip, &G.rdnss[G.n_rdnss]) != 1) {
|
||||
fprintf(stderr, "uradvd: error: invalid RDNSS IP address %s.\n", ip);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
G.n_rdnss++;
|
||||
}
|
||||
|
||||
static void add_prefix(const char *prefix, bool adv_onlink) {
|
||||
if (G.n_prefixes == MAX_PREFIXES) {
|
||||
fprintf(stderr, "uradvd: error: maximum number of prefixes is %i.\n", MAX_PREFIXES);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const size_t len = strlen(prefix)+1;
|
||||
char prefix2[len];
|
||||
memcpy(prefix2, prefix, len);
|
||||
|
||||
char *slash = strchr(prefix2, '/');
|
||||
if (slash) {
|
||||
*slash = 0;
|
||||
if (strcmp(slash+1, "64") != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET6, prefix2, &G.prefixes[G.n_prefixes]) != 1)
|
||||
goto error;
|
||||
|
||||
static const uint8_t zero[8] = {};
|
||||
if (memcmp(G.prefixes[G.n_prefixes].s6_addr + 8, zero, 8) != 0)
|
||||
goto error;
|
||||
|
||||
G.prefixes_onlink[G.n_prefixes] = adv_onlink;
|
||||
|
||||
G.n_prefixes++;
|
||||
return;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "uradvd: error: invalid prefix %s (only prefixes of length 64 are supported).\n", prefix);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void parse_cmdline(int argc, char *argv[]) {
|
||||
int c;
|
||||
char *endptr;
|
||||
unsigned long val;
|
||||
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"default-lifetime", required_argument, 0, 0},
|
||||
{"rdnss", required_argument, 0, 1},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "i:a:p:h", long_options, &option_index)) != -1) {
|
||||
switch(c) {
|
||||
case 0: // --default-lifetime
|
||||
val = strtoul(optarg, &endptr, 0);
|
||||
|
||||
if (!*optarg || *endptr || val > UINT16_MAX)
|
||||
exit_error("invalid default lifetime\n", 0);
|
||||
|
||||
G.adv_default_lifetime = val;
|
||||
|
||||
break;
|
||||
|
||||
case 1: // --rdnss
|
||||
add_rdnss(optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (G.ifname)
|
||||
exit_error("multiple interfaces are not supported.\n", 0);
|
||||
|
||||
G.ifname = optarg;
|
||||
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
add_prefix(optarg, false);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
add_prefix(optarg, true);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
parse_cmdline(argc, argv);
|
||||
|
||||
if (!G.ifname || !G.n_prefixes)
|
||||
exit_error("interface and prefix arguments are required.\n", 0);
|
||||
|
||||
init_random();
|
||||
init_icmp();
|
||||
init_rtnl();
|
||||
|
||||
update_time();
|
||||
G.next_advert = G.next_advert_earliest = G.time;
|
||||
|
||||
update_interface();
|
||||
|
||||
while (true) {
|
||||
struct pollfd fds[2] = {
|
||||
{ .fd = G.icmp_sock, .events = POLLIN },
|
||||
{ .fd = G.rtnl_sock, .events = POLLIN },
|
||||
};
|
||||
|
||||
int timeout = -1;
|
||||
|
||||
if (G.iface.ok) {
|
||||
timeout = timespec_diff(&G.next_advert, &G.time);
|
||||
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
int ret = poll(fds, 2, timeout);
|
||||
if (ret < 0)
|
||||
exit_errno("poll");
|
||||
|
||||
update_time();
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
handle_solicit();
|
||||
if (fds[1].revents & POLLIN)
|
||||
handle_rtnl();
|
||||
|
||||
if (timespec_after(&G.time, &G.next_advert))
|
||||
send_advert();
|
||||
}
|
||||
}
|
|
@ -380,7 +380,7 @@ function WGPeerSelector:main()
|
|||
|
||||
if self:try_connect_to_peer(peer, timeout) then
|
||||
connected_peer = peer
|
||||
log(syslog.LOG_INFO, 'Connection established with '..peer.name..'.')
|
||||
log(syslog.LOG_INFO, 'Connection established with '..connected_peer.name..'.')
|
||||
end
|
||||
|
||||
elseif state == 'established' then
|
||||
|
@ -388,8 +388,8 @@ function WGPeerSelector:main()
|
|||
|
||||
if not connected_peer:has_recent_handshake() then
|
||||
connected_peer:uninstall_from_kernel()
|
||||
log(syslog.LOG_INFO, 'Connection to '..connected_peer.name..' lost.')
|
||||
connected_peer = nil
|
||||
log(syslog.LOG_INFO, 'Connection to '..peer.name..' lost.')
|
||||
else
|
||||
-- check connections every 5 seconds
|
||||
sleep(5)
|
||||
|
|
Loading…
Reference in New Issue