├── autogen.sh ├── redhat ├── SysV │ ├── radvd-tmpfs.conf │ ├── radvd.sysconfig │ ├── radvd.conf.empty │ ├── radvd.init │ └── radvd.spec.in └── systemd │ ├── radvd-tmpfs.conf │ ├── radvd.service │ └── radvd.spec.in ├── CHANGES ├── .clang-format ├── test ├── test_dnssl1.conf ├── print_safe_buffer.h ├── test_dnssl2.conf ├── test_rdnss.conf ├── test_dnssl3.conf ├── test_rdnss_long.conf ├── print_safe_buffer.c ├── test1.conf ├── test_build.sh ├── test_dnssl4.conf ├── test_dnssl6.conf ├── test_dnssl5.conf ├── check.c └── util.c ├── contrib └── unset_addrs.sh ├── Docker.autogen ├── copyright.blurb ├── autogen-container.sh ├── .travis.yml ├── netlink.h ├── .gitignore ├── log.h ├── .github └── workflows │ ├── bsd.yaml │ ├── buildroot.yaml │ ├── linux.yaml │ └── codeql.yaml ├── timer.c ├── radvd.service.in ├── pathnames.h ├── COPYRIGHT ├── radvdump.8.man ├── README ├── includes.h ├── RELEASE-PROCESS.md ├── recv.c ├── socket.c ├── TODO ├── radvd.conf.example ├── log.c ├── INTRO.html ├── scanner.l ├── radvd.8.man ├── device-bsd44.c ├── privsep-linux.c ├── device-common.c ├── util.c ├── Makefile.am ├── defaults.h ├── configure.ac ├── netlink.c ├── radvd.h ├── device-linux.c └── interface.c /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -vif 4 | 5 | -------------------------------------------------------------------------------- /redhat/SysV/radvd-tmpfs.conf: -------------------------------------------------------------------------------- 1 | d /var/run/radvd 0755 radvd radvd 2 | -------------------------------------------------------------------------------- /redhat/systemd/radvd-tmpfs.conf: -------------------------------------------------------------------------------- 1 | d /run/radvd 0755 radvd radvd 2 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radvd-project/radvd/HEAD/CHANGES -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 8 3 | UseTab: Always 4 | BreakBeforeBraces: Linux 5 | AllowShortIfStatementsOnASingleLine: false 6 | IndentCaseLabels: false 7 | ColumnLimit: 130 8 | -------------------------------------------------------------------------------- /redhat/SysV/radvd.sysconfig: -------------------------------------------------------------------------------- 1 | 2 | # No chroot; /var/run/radvd must be owned by -u. 3 | OPTIONS="-u radvd" 4 | 5 | # Chroot; directory structure under /var/chroot/radvd has to be populated. 6 | #OPTIONS="-u radvd -t /var/chroot/radvd" 7 | -------------------------------------------------------------------------------- /test/test_dnssl1.conf: -------------------------------------------------------------------------------- 1 | interface eth0 { 2 | # This is the max for a single DNS label 3 | DNSSL 4 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.com. 5 | { 6 | AdvDNSSLLifetime 1234; 7 | }; 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /test/print_safe_buffer.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../config.h" 5 | #include "../radvd.h" 6 | 7 | size_t snprint_safe_buffer(char *s, size_t size, struct safe_buffer const *sb); 8 | void print_safe_buffer(struct safe_buffer const *sb); 9 | -------------------------------------------------------------------------------- /contrib/unset_addrs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | if (( $# < 1 )) ; then 6 | echo "usage: $0 iface" 1>&2 7 | exit -1 8 | fi 9 | 10 | iface=$1 11 | 12 | ADDRS=$(ifconfig $iface | grep "inet6 addr:" | grep Scope:Global | awk '{print $3}') 13 | 14 | for a in $ADDRS ; do 15 | ip addr del $a dev $iface 16 | done 17 | 18 | -------------------------------------------------------------------------------- /test/test_dnssl2.conf: -------------------------------------------------------------------------------- 1 | interface eth0 { 2 | # Test for two DNSSL options in the same file. 3 | DNSSL 4 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.com. 5 | { 6 | AdvDNSSLLifetime 1234; 7 | }; 8 | DNSSL 9 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.net. 10 | { 11 | AdvDNSSLLifetime 1234; 12 | }; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /test/test_rdnss.conf: -------------------------------------------------------------------------------- 1 | # vim:set ts=2 sw=2 ft=perl noet : 2 | 3 | interface eth0 4 | { 5 | AdvSendAdvert on; 6 | RDNSS 1234:423:fefe:0493::2 7 | { 8 | # advised by logs to be <= 2*MaxRtrAdvInterval 9 | AdvRDNSSLifetime 45; 10 | }; 11 | 12 | DNSSL h.piggy.net 13 | { 14 | AdvDNSSLLifetime 45; 15 | }; 16 | 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /test/test_dnssl3.conf: -------------------------------------------------------------------------------- 1 | interface eth0 { 2 | # Test for two long DNSSLs in a single statement 3 | # Compare to previous test where the DNSSLs are in seperate option packets. 4 | DNSSL 5 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.com. 6 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.net. 7 | { 8 | AdvDNSSLLifetime 1234; 9 | }; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /Docker.autogen: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | # apt is meant to be used by humans, whereas apt-get is for scripting. 3 | RUN apt-get update && \ 4 | DEBIAN_FRONTEND=noninteractive apt-get install -qy \ 5 | autoconf \ 6 | automake \ 7 | gettext \ 8 | libtool \ 9 | gawk \ 10 | pkg-config \ 11 | make \ 12 | systemd 13 | VOLUME /workdir 14 | ENTRYPOINT cd /workdir && /bin/sh autogen.sh && ./configure -C && make distclean 15 | -------------------------------------------------------------------------------- /redhat/systemd/radvd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Router advertisement daemon for IPv6 3 | After=network-online.target 4 | Wants=network-online.target 5 | ConditionPathExists=/etc/radvd.conf 6 | 7 | [Service] 8 | EnvironmentFile=/etc/sysconfig/radvd 9 | ExecStart=/usr/sbin/radvd $OPTIONS 10 | Type=forking 11 | PIDFile=/run/radvd/radvd.pid 12 | ExecReload=/bin/kill -HUP $MAINPID 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /copyright.blurb: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | -------------------------------------------------------------------------------- /test/test_rdnss_long.conf: -------------------------------------------------------------------------------- 1 | # vim:set ts=2 sw=2 ft=perl noet : 2 | 3 | interface eth0 4 | { 5 | AdvSendAdvert on; 6 | # More than 3 RDNSS at this point causes radvd to reject the config 7 | RDNSS 2000::1 2000::2 2000::3 #2000::4 2000::5 2000::6 2000::6 2000::7 2000::8 8 | { 9 | # advised by logs to be <= 2*MaxRtrAdvInterval 10 | AdvRDNSSLifetime 45; 11 | }; 12 | 13 | DNSSL h.piggy.net 14 | { 15 | AdvDNSSLLifetime 45; 16 | }; 17 | 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /redhat/SysV/radvd.conf.empty: -------------------------------------------------------------------------------- 1 | # NOTE: there is no such thing as a working "by-default" configuration file. 2 | # At least the prefix needs to be specified. Please consult the radvd.conf(5) 3 | # man page and/or /usr/share/doc/radvd-*/radvd.conf.example for help. 4 | # 5 | # 6 | #interface eth0 7 | #{ 8 | # AdvSendAdvert on; 9 | # MinRtrAdvInterval 30; 10 | # MaxRtrAdvInterval 100; 11 | # prefix 2001:db8:1:0::/64 12 | # { 13 | # AdvOnLink on; 14 | # AdvAutonomous on; 15 | # AdvRouterAddr off; 16 | # }; 17 | # 18 | #}; 19 | -------------------------------------------------------------------------------- /autogen-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e -x 3 | IMAGE=radvd-autogen:latest 4 | IMAGE_PRESENT=0 5 | if docker images -q "${IMAGE}" |grep -sq . ; then 6 | IMAGE_PRESENT=1 7 | fi 8 | if [ "$IMAGE_PRESENT" -eq 0 ]; then 9 | docker build -f Docker.autogen -t "${IMAGE}" . 10 | fi 11 | CONTAINER_USER="$(id -u):$(id -g)" 12 | docker run \ 13 | --network=none \ 14 | --user="${CONTAINER_USER}" \ 15 | --mount=type=bind,src="${PWD}",dst=/workdir,ro=false \ 16 | --rm=true \ 17 | --privileged=false \ 18 | --security-opt=no-new-privileges \ 19 | --tty=false \ 20 | --interactive=false \ 21 | "${IMAGE}" 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | arch: 2 | - amd64 3 | - ppc64le 4 | 5 | language: c 6 | 7 | os: 8 | - linux 9 | #- osx 10 | 11 | dist: xenial 12 | 13 | compiler: 14 | - gcc 15 | - clang 16 | 17 | addons: 18 | apt: 19 | packages: 20 | - check 21 | - make 22 | homebrew: 23 | packages: 24 | - check 25 | 26 | script: 27 | - cd "$TRAVIS_BUILD_DIR" 28 | - ./autogen.sh 29 | - | 30 | if [[ "$TRAVIS_CPU_ARCH" == "ppc64le" ]]; then 31 | ./configure --with-check --without-stack-protector 32 | else 33 | ./configure --with-check 34 | fi 35 | - make -j 36 | - make check 37 | - make dist-xz 38 | - sudo make install 39 | -------------------------------------------------------------------------------- /netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * Reuben Hawkins 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #pragma once 17 | 18 | #include "radvd.h" 19 | 20 | int netlink_get_address_lifetimes(struct AdvPrefix const *prefix, unsigned int *preferred_lft, unsigned int *valid_lft); 21 | int netlink_get_device_addr_len(struct Interface *iface); 22 | void process_netlink_msg(int netlink_sock, struct Interface *ifaces, int icmp_sock); 23 | int netlink_socket(void); 24 | int prefix_match (struct AdvPrefix const *prefix, struct in6_addr *addr); 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | gram.c 3 | gram.dot 4 | gram.h 5 | libradvd-parser.a 6 | radvd 7 | radvd.8 8 | radvd.conf.5 9 | radvdump 10 | radvdump.8 11 | redhat/radvd.spec 12 | redhat/systemd/radvd.spec 13 | redhat/SysV/radvd.spec 14 | scanner.c 15 | scanner.h 16 | 17 | # Coverage 18 | cov 19 | coverage.info 20 | *.gcda 21 | *.gcno 22 | tags 23 | 24 | # Build artifacts 25 | .deps 26 | *.o 27 | *.Po 28 | 29 | # autotools files 30 | aclocal.m4 31 | ar-lib 32 | autom4te.cache/ 33 | compile 34 | config.guess 35 | config.h 36 | config.h.in 37 | config.log 38 | config.status 39 | config.sub 40 | configure 41 | depcomp 42 | install-sh 43 | Makefile 44 | Makefile.in 45 | missing 46 | stamp-h1 47 | test-driver 48 | ylwrap 49 | 50 | # quilt, tool for patches 51 | .pc/ 52 | 53 | # Editor droppings 54 | .vscode/ 55 | *~ 56 | *.swp 57 | *.rej 58 | *.orig 59 | 60 | # Test artifacts 61 | check_all 62 | check_all.log 63 | test-suite.log 64 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #pragma once 16 | 17 | #define L_NONE 0 18 | #define L_SYSLOG 1 19 | #define L_STDERR 2 20 | #define L_STDERR_SYSLOG 3 21 | #define L_LOGFILE 4 22 | #define L_STDERR_CLEAN 5 23 | #define L_UNSPEC 6 24 | 25 | #define LOG_TIME_FORMAT "%b %d %H:%M:%S" 26 | 27 | int log_open(int, char const *, char const *, int); 28 | void flog(int, char const *, ...) __attribute__((format(printf, 2, 3))); 29 | void dlog(int, int, char const *, ...) __attribute__((format(printf, 3, 4))); 30 | int log_close(void); 31 | int log_reopen(void); 32 | void set_debuglevel(int); 33 | int get_debuglevel(void); 34 | -------------------------------------------------------------------------------- /.github/workflows/bsd.yaml: -------------------------------------------------------------------------------- 1 | name: BSD 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | push: 7 | branches: 8 | - master 9 | tags: 10 | - 'v*' 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | freebsd: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout sources 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 1 24 | - name: Build 25 | # CodeQL security: uses should use an immutable tag or hash for untrusted actions, "@v1" may mutate 26 | uses: vmactions/freebsd-vm@v1.1.7 27 | with: 28 | # BSD make -j has a mandatory argument 29 | # Linux make -j means infinite jobs 30 | prepare: | 31 | pkg install -y autoconf automake bison flex pkgconf devel/check check 32 | run: | 33 | set -e # exit on any failure below 34 | set -x # show each step 35 | ./autogen.sh 36 | ./configure --with-check || exit 1 37 | make -j $(nproc) all || exit 1 38 | make -j $(nproc) check || exit 1 39 | make -j $(nproc) dist-xz || exit 1 40 | make -j $(nproc) install || exit 1 41 | -------------------------------------------------------------------------------- /test/print_safe_buffer.c: -------------------------------------------------------------------------------- 1 | 2 | #include "print_safe_buffer.h" 3 | #include "../config.h" 4 | #include "../radvd.h" 5 | 6 | #include 7 | 8 | void print_safe_buffer(struct safe_buffer const *sb) 9 | { 10 | char buf[4096]; 11 | snprint_safe_buffer(buf, sizeof(buf), sb); 12 | printf("%s", buf); 13 | } 14 | 15 | size_t snprint_safe_buffer(char *s, size_t size, struct safe_buffer const *sb) 16 | { 17 | size_t count = 0; 18 | int n; 19 | 20 | n = snprintf((s + count), (size - count), "unsigned char expected[] = { /* sb.allocated = %ld, sb.used = %ld */", sb->allocated, sb->used); 21 | if (n < 0 || n >= size - count) { 22 | return count; 23 | } 24 | count += n; 25 | 26 | char* nextline = "\n\t"; 27 | char* nextbyte = " "; 28 | for (size_t i = 0; i < sb->used; ++i) { 29 | char* nextspace = (i % 8 == 0) ? nextline : nextbyte; 30 | n = snprintf((s + count), (size - count), "%s0x%02x,", nextspace, sb->buffer[i]); 31 | if (n < 0 || n >= size - count) { 32 | return count; 33 | } 34 | count += n; 35 | } 36 | /* Do not remove the final byte's comma. Only JSON requires the comma is 37 | * removed, and this is not JSON. */ 38 | n = snprintf((s + count), (size - count), "\n};\n"); 39 | if (n < 0 || n >= size - count) { 40 | return count; 41 | } 42 | count += n; 43 | return count; 44 | } 45 | -------------------------------------------------------------------------------- /timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996-2000 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #include "config.h" 17 | #include "radvd.h" 18 | 19 | struct timespec next_timespec(double next) 20 | { 21 | struct timespec ts; 22 | 23 | clock_gettime(CLOCK_MONOTONIC, &ts); 24 | ts.tv_sec += (int)next; 25 | ts.tv_nsec += 1000000000ULL * (next - (int)next); 26 | return ts; 27 | } 28 | 29 | int64_t timespecdiff(struct timespec const *a, struct timespec const *b) 30 | { 31 | int64_t msec; 32 | msec = ((int64_t)a->tv_sec - b->tv_sec) * 1000; 33 | msec += ((int64_t)a->tv_nsec - b->tv_nsec) / (1000 * 1000); 34 | return msec; 35 | } 36 | 37 | /* Returns when the next time should expire in milliseconds. */ 38 | uint64_t next_time_msec(struct Interface const *iface) 39 | { 40 | struct timespec ts; 41 | int64_t diff_ms; 42 | clock_gettime(CLOCK_MONOTONIC, &ts); 43 | diff_ms = timespecdiff(&iface->times.next_multicast, &ts); 44 | if (diff_ms <= 0) 45 | return 0; 46 | return (uint64_t)diff_ms; 47 | } 48 | -------------------------------------------------------------------------------- /test/test1.conf: -------------------------------------------------------------------------------- 1 | 2 | interface eth0 { 3 | 4 | MaxRtrAdvInterval 1000; 5 | AdvLinkMTU 1234; 6 | 7 | AdvSendAdvert off; 8 | 9 | prefix fe80:1::/64 { 10 | AdvAutonomous on; 11 | AdvRouterAddr on; 12 | AdvValidLifetime infinity; 13 | AdvPreferredLifetime infinity; 14 | DeprecatePrefix on; 15 | DecrementLifetimes on; 16 | }; 17 | 18 | prefix fe80:2::/48 { 19 | AdvAutonomous off; 20 | AdvRouterAddr off; 21 | AdvValidLifetime 10000; 22 | AdvPreferredLifetime 1000; 23 | DeprecatePrefix off; 24 | DecrementLifetimes off; 25 | }; 26 | 27 | prefix fe80:2::/64 { 28 | }; 29 | 30 | route fe80:f:1::/48 { 31 | AdvRouteLifetime 10000; 32 | AdvRoutePreference low; 33 | RemoveRoute on; 34 | }; 35 | 36 | route fe80:f:2::/40 { 37 | AdvRouteLifetime infinity; 38 | AdvRoutePreference high; 39 | RemoveRoute off; 40 | }; 41 | 42 | route fe80:f:2::/32 { 43 | }; 44 | 45 | route fe80:f:2::/128 { 46 | }; 47 | 48 | DNSSL office.branch.example.com branch.example.com example.com { 49 | AdvDNSSLLifetime 1000; 50 | }; 51 | 52 | DNSSL office.branch.example.net branch.example.net example.net { 53 | AdvDNSSLLifetime 1099; 54 | }; 55 | 56 | DNSSL office.branch.example. branch.example. example. { 57 | AdvDNSSLLifetime 1100; 58 | }; 59 | 60 | RDNSS ff02::1 ff02::2 ff02::3 { 61 | AdvRDNSSLifetime 1234; 62 | }; 63 | RDNSS ff03::4 ff03::5 ff03::6 { 64 | AdvRDNSSLifetime 4567; 65 | }; 66 | 67 | lowpanco { 68 | AdvContextCompressionFlag on; 69 | AdvContextLength 50; 70 | AdvContextID 100; 71 | AdvLifeTime 1000; 72 | }; 73 | 74 | abro fe80::a200:0:0:1/64 { 75 | AdvVersionLow 10; 76 | AdvVersionHigh 2; 77 | AdvValidLifeTime 2; 78 | }; 79 | }; 80 | 81 | -------------------------------------------------------------------------------- /test/test_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Make this script exit with error if any command fails 4 | set -e 5 | 6 | # Make sure an argument is provided to this script 7 | if [ $# -ne 1 ] 8 | then 9 | echo "Usage: $0 build-distro-name" 10 | echo "'build-distro-name' is the Linux distribution name your are building radvd on." 11 | echo "Currently supported distributions are:" 12 | echo " alpine" 13 | echo " debian" 14 | echo " fedora" 15 | echo " opensuse" 16 | echo " ubuntu" 17 | fi 18 | 19 | # Install the right dependencies according to build host 20 | echo "Installing required dependencies for distribution '${1}'..." 21 | case $1 in 22 | # watch out, the naming of libbsd vs libbsd0 is not consistent between distros! 23 | alpine) 24 | apk update 25 | apk add alpine-sdk autoconf automake bison check-dev clang flex gettext libtool linux-headers sudo xz libbsd libbsd-dev 26 | ;; 27 | debian) 28 | apt update 29 | apt install -y autoconf automake bison build-essential check clang flex gettext libtool pkg-config sudo libbsd-dev libbsd0 30 | ;; 31 | fedora) 32 | sudo dnf install -y autoconf automake bison check-devel clang flex gettext libtool make pkgconfig xz libbsd-devel libbsd 33 | ;; 34 | opensuse) 35 | zypper refresh 36 | zypper --non-interactive install -t pattern devel_C_C++ 37 | zypper --non-interactive install check-devel clang sudo libbsd0 libbsd-devel 38 | ;; 39 | ubuntu) 40 | sudo apt update 41 | sudo apt install -y autoconf automake bison build-essential check clang flex gettext libtool pkg-config libbsd-dev libbsd0 42 | ;; 43 | *) 44 | echo "Unsupported distribution, build will be tried without installing any dependencies." 45 | ;; 46 | esac 47 | 48 | # Build radvd 49 | ./autogen.sh 50 | ./configure --with-check 51 | make -j 52 | make -j check 53 | make -j dist-xz 54 | sudo make -j install 55 | -------------------------------------------------------------------------------- /radvd.service.in: -------------------------------------------------------------------------------- 1 | # It's not recommended to modify this file in-place, because it 2 | # will be overwritten during upgrades. If you want to customize, 3 | # the best way is to use the "systemctl edit" command. 4 | 5 | [Unit] 6 | Description=Router advertisement daemon for IPv6 7 | Documentation=man:radvd(8) 8 | After=network.target 9 | ConditionPathExists=/etc/radvd.conf 10 | 11 | [Service] 12 | Type=forking 13 | ExecStartPre=@SBINDIR@/radvd --logmethod stderr_clean --configtest 14 | ExecStart=@SBINDIR@/radvd --logmethod stderr_clean 15 | ExecReload=@SBINDIR@/radvd --logmethod stderr_clean --configtest 16 | ExecReload=/bin/kill -HUP $MAINPID 17 | PIDFile=@PATH_RADVD_PID@ 18 | 19 | # Set the CPU scheduling policy to idle which is for running very low priority background jobs 20 | CPUSchedulingPolicy=idle 21 | 22 | # Allow for binding to low ports and doing raw network access 23 | CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW 24 | 25 | # Set up a new file system namespace and mounts private /tmp and /var/tmp directories 26 | # so this service cannot access the global directories and other processes cannot 27 | # access this service's directories. 28 | PrivateTmp=yes 29 | 30 | # Sets up a new /dev namespace for the executed processes and only adds API pseudo devices 31 | # such as /dev/null, /dev/zero or /dev/random (as well as the pseudo TTY subsystem) to it, 32 | # but no physical devices such as /dev/sda. 33 | PrivateDevices=yes 34 | 35 | # Mounts the /usr, /boot, and /etc directories read-only for processes invoked by this unit. 36 | ProtectSystem=full 37 | 38 | # The directories /home, /root and /run/user are made inaccessible and empty for processes 39 | # invoked by this unit. 40 | ProtectHome=yes 41 | 42 | # Ensures that the service process and all its children can never gain new privileges 43 | NoNewPrivileges=yes 44 | 45 | [Install] 46 | WantedBy=multi-user.target 47 | 48 | -------------------------------------------------------------------------------- /pathnames.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #pragma once 17 | 18 | #ifndef PATH_RADVD_CONF 19 | #define PATH_RADVD_CONF "/etc/radvd.conf" 20 | #endif 21 | 22 | #ifndef PATH_RADVD_PID 23 | #define PATH_RADVD_PID "/var/run/radvd.pid" 24 | #endif 25 | 26 | #ifndef PATH_RADVD_LOG 27 | #define PATH_RADVD_LOG "/var/log/radvd.log" 28 | #endif 29 | 30 | #define PATH_PROC_NET_IGMP6 "/proc/net/igmp6" 31 | 32 | #ifdef __linux__ 33 | #define SYSCTL_IP6_FORWARDING CTL_NET, NET_IPV6, NET_IPV6_CONF, NET_PROTO_CONF_ALL, NET_IPV6_FORWARDING 34 | #define SYSCTL_IP6_AUTOCONFIG CTL_NET, NET_IPV6, NET_IPV6_CONF, NET_PROTO_CONF_ALL, NET_IPV6_AUTOCONF 35 | #define PROC_SYS_IP6_FORWARDING "/proc/sys/net/ipv6/conf/all/forwarding" 36 | #define PROC_SYS_IP6_IFACE_FORWARDING "/proc/sys/net/ipv6/conf/%s/forwarding" 37 | #define PROC_SYS_IP6_AUTOCONFIG "/proc/sys/net/ipv6/conf/%s/autoconf" 38 | #define PROC_SYS_IP6_LINKMTU "/proc/sys/net/ipv6/conf/%s/mtu" 39 | #define PROC_SYS_IP6_CURHLIM "/proc/sys/net/ipv6/conf/%s/hop_limit" 40 | #define PROC_SYS_IP6_BASEREACHTIME_MS "/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms" 41 | #define PROC_SYS_IP6_BASEREACHTIME "/proc/sys/net/ipv6/neigh/%s/base_reachable_time" 42 | #define PROC_SYS_IP6_RETRANSTIMER_MS "/proc/sys/net/ipv6/neigh/%s/retrans_time_ms" 43 | #define PROC_SYS_IP6_RETRANSTIMER "/proc/sys/net/ipv6/neigh/%s/retrans_time" 44 | #else /* BSD */ 45 | #define SYSCTL_IP6_FORWARDING CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_FORWARDING 46 | #endif 47 | -------------------------------------------------------------------------------- /.github/workflows/buildroot.yaml: -------------------------------------------------------------------------------- 1 | name: Buildroot 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | push: 7 | branches: 8 | - master 9 | tags: 10 | - 'v*' 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | buildroot: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | defconfig_name: 22 | - qemu_x86_defconfig 23 | - qemu_x86_64_defconfig 24 | - raspberrypi4_defconfig 25 | - raspberrypi4_64_defconfig 26 | - qemu_ppc64le_pseries_defconfig 27 | - qemu_mips32r2_malta_defconfig 28 | - qemu_mips64_malta_defconfig 29 | - qemu_riscv32_virt_defconfig 30 | - qemu_riscv64_virt_defconfig 31 | libc_name: 32 | - glibc 33 | - uclibc 34 | - musl 35 | env: 36 | CI_VERSION: v1.0 37 | BUILDROOT_DIRECTORY_NAME: buildroot-${{ matrix.defconfig_name }}-${{ matrix.libc_name }} 38 | steps: 39 | - name: Retrieve prebuilt Buildroot image 40 | working-directory: /home/runner 41 | run: | 42 | wget https://github.com/radvd-project/radvd-ci/releases/download/${{ env.CI_VERSION }}/${{ env.BUILDROOT_DIRECTORY_NAME }}.tar.zst 43 | tar --zstd --strip-components=2 -xf ${{ env.BUILDROOT_DIRECTORY_NAME }}.tar.zst 44 | - name: Select the latest radvd upstream version 45 | working-directory: /home/runner/${{ env.BUILDROOT_DIRECTORY_NAME }}/package/radvd 46 | run: | 47 | # Get package sources from head of current branch 48 | sed -i "/RADVD_VERSION =/c\\RADVD_VERSION = ${GITHUB_SHA}" radvd.mk 49 | - name: Trigger a radvd package rebuild 50 | working-directory: /home/runner/${{ env.BUILDROOT_DIRECTORY_NAME }}/output/build 51 | run: rm -rf radvd* 52 | - name: Build 53 | working-directory: /home/runner/${{ env.BUILDROOT_DIRECTORY_NAME }} 54 | run: make -j 55 | -------------------------------------------------------------------------------- /redhat/SysV/radvd.init: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # 4 | # chkconfig: - 54 46 5 | # description: radvd is the router advertisement daemon for IPv6. It \ 6 | # listens to router solicitations and sends router \ 7 | # advertisements as described in "Neighbor Discovery for IP \ 8 | # Version 6 (IPv6)" (RFC 2461). With these advertisements \ 9 | # hosts can automatically configure their addresses and some \ 10 | # other parameters. They also can choose a default router \ 11 | # based on these advertisements. 12 | # 13 | # processname: radvd 14 | # pidfile: /var/run/radvd.pid 15 | # config: /etc/radvd.conf 16 | # config: /etc/sysconfig/radvd 17 | 18 | ### BEGIN INIT INFO 19 | # Provides: radvd 20 | # Short-Description: router advertisement daemon for IPv6 21 | ### END INIT INFO 22 | 23 | # Source function library. 24 | . /etc/rc.d/init.d/functions 25 | 26 | # Get config. 27 | . /etc/sysconfig/network 28 | 29 | [ -f /etc/sysconfig/radvd ] && . /etc/sysconfig/radvd 30 | 31 | RETVAL=0 32 | PROG="radvd" 33 | LOCKFILE=/var/lock/subsys/radvd 34 | 35 | # See how we were called. 36 | case "$1" in 37 | start) 38 | if [ ! -f /etc/radvd.conf ]; then 39 | echo $"Configuration file /etc/radvd.conf missing" 1>&2 40 | exit 6 41 | fi 42 | if [ `id -u` -ne 0 ]; then 43 | echo $"Insufficient privilege" 1>&2 44 | exit 4 45 | fi 46 | echo -n $"Starting $PROG: " 47 | daemon radvd $OPTIONS 48 | RETVAL=$? 49 | echo 50 | if [ $RETVAL -eq 0 ]; then 51 | touch $LOCKFILE 52 | else 53 | if [ -f $LOCKFILE ]; then 54 | RETVAL=0 55 | fi 56 | fi 57 | ;; 58 | stop) 59 | echo -n $"Stopping $PROG: " 60 | killproc radvd 61 | RETVAL=$? 62 | echo 63 | [ $RETVAL -eq 0 ] && rm -f $LOCKFILE 64 | ;; 65 | status) 66 | status radvd 67 | RETVAL=$? 68 | ;; 69 | restart) 70 | $0 stop 71 | $0 start 72 | RETVAL=$? 73 | ;; 74 | reload|force-reload) 75 | echo -n $"Reloading $PROG: " 76 | killproc radvd -HUP 77 | RETVAL=$? 78 | echo 79 | ;; 80 | condrestart|try-restart) 81 | if [ -f $LOCKFILE ]; then 82 | $0 stop 83 | $0 start 84 | RETVAL=$? 85 | fi 86 | ;; 87 | *) 88 | echo $"Usage: $0 {start|stop|status|restart|try-restart|reload|force-reload}" 89 | exit 2 90 | esac 91 | 92 | exit $RETVAL 93 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The author(s) grant permission for redistribution and use in source and 2 | binary forms, with or without modification, of the software and documentation 3 | provided that the following conditions are met: 4 | 5 | 0. If you receive a version of the software that is specifically labelled 6 | as not being for redistribution (check the version message and/or README), 7 | you are not permitted to redistribute that version of the software in any 8 | way or form. 9 | 1. All terms of all other applicable copyrights and licenses must be 10 | followed. 11 | 2. Redistributions of source code must retain the authors' copyright 12 | notice(s), this list of conditions, and the following disclaimer. 13 | 3. Redistributions in binary form must reproduce the authors' copyright 14 | notice(s), this list of conditions, and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 4. All advertising materials mentioning features or use of this software 17 | must display the following acknowledgement with the name(s) of the 18 | authors as specified in the copyright notice(s) substituted where 19 | indicated: 20 | 21 | This product includes software developed by the authors which are 22 | mentioned at the start of the source files and other contributors. 23 | 24 | 5. Neither the name(s) of the author(s) nor the names of its contributors 25 | may be used to endorse or promote products derived from this software 26 | without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY 29 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY 32 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 35 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /radvdump.8.man: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" 3 | .\" Authors: 4 | .\" Lars Fenneberg 5 | .\" Marko Myllynen 6 | .\" 7 | .\" This software is Copyright 1996 by the above mentioned author(s), 8 | .\" All Rights Reserved. 9 | .\" 10 | .\" The license which is distributed with this software in the file COPYRIGHT 11 | .\" applies to this software. If your distribution is missing this file, you 12 | .\" may request it from . 13 | .\" 14 | .\" 15 | .\" 16 | .TH RADVDUMP 8 "30 Dec 2024" "radvd @VERSION@" "" 17 | .SH NAME 18 | radvdump \- dump router advertisements 19 | .SH SYNOPSIS 20 | .B radvdump 21 | .B "[ \-vhfe ]" 22 | .BI "[ \-d " debuglevel " ]" 23 | 24 | .SH DESCRIPTION 25 | .B radvdump 26 | prints out the contents of incoming router advertisements sent by 27 | .B radvd 28 | or some other software implementing (parts of) 29 | "Neighbor Discovery for IP Version 6 (IPv6)" (RFC 4861). 30 | 31 | .SH OPTIONS 32 | 33 | For every one character option there is also a long option, which 34 | is listed right next to the "short" option name: 35 | 36 | .TP 37 | .BR "\-v" , " \-\-version" 38 | Displays the version of 39 | .I radvdump 40 | and then aborts. 41 | .TP 42 | .BR "\-h" , " \-\-help" 43 | Displays a short usage description and then aborts. 44 | .TP 45 | .BR "\-f" , " \-\-file\-format" 46 | Output received router advertisements in the format of the 47 | .B radvd 48 | configuration file. 49 | Since radvd 0.9, this is the default and the switch is provided 50 | for backward compatibility only. 51 | .TP 52 | .BR "\-e" , " \-\-exclude-defaults" 53 | Exclude default valued options from configuration file format. 54 | This option is ignored if option 55 | .B "\-f" 56 | is not set. 57 | .TP 58 | .BR "\-d " debuglevel, " \-\-debug " debuglevel 59 | With this option you turn on debugging information. The debugging level is 60 | an integer in the range from 1 to 4, from quiet to very verbose. A 61 | debugging level of 0 completely turns off debugging. 62 | 63 | The default debugging level is 0. 64 | 65 | .SH FILES 66 | 67 | .nf 68 | @sbindir@/radvdump 69 | .fi 70 | .SH BUGS 71 | 72 | There certainly are some bugs. If you find them or have other 73 | suggestions please contact Reuben Hawkins . 74 | 75 | .SH "SEE ALSO" 76 | 77 | .BR radvd (8), 78 | .BR radvd.conf (5) 79 | 80 | .SH AUTHORS 81 | 82 | .nf 83 | See radvd.8 manpage for authors 84 | 85 | .fi 86 | -------------------------------------------------------------------------------- /test/test_dnssl4.conf: -------------------------------------------------------------------------------- 1 | interface eth0 { 2 | # Very large option test, not quite the max possible (see dnssl6) 3 | DNSSL 4 | # 62 x char => encodes as 64 bytes per entry. 5 | # a-z + 0-9 = 36 entries 6 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. 7 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. 8 | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc. 9 | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd. 10 | eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee. 11 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff. 12 | gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg. 13 | hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh. 14 | iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii. 15 | jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj. 16 | kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk. 17 | llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll. 18 | mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm. 19 | nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn. 20 | oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo. 21 | pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp. 22 | qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq. 23 | rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr. 24 | ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss. 25 | tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt. 26 | uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu. 27 | vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv. 28 | wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww. 29 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. 30 | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy. 31 | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz. 32 | 00000000000000000000000000000000000000000000000000000000000000. 33 | 11111111111111111111111111111111111111111111111111111111111111. 34 | 22222222222222222222222222222222222222222222222222222222222222. 35 | 33333333333333333333333333333333333333333333333333333333333333. 36 | 44444444444444444444444444444444444444444444444444444444444444. # 31 including '4's 37 | { 38 | AdvDNSSLLifetime 1234; 39 | }; 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | Installation: 3 | ============= 4 | 5 | Run configure, e.g. 6 | 7 | ./configure --prefix=/usr/local --sysconfdir=/etc --mandir=/usr/share/man 8 | 9 | See configure --help for additional command line arguments. 10 | 11 | Run 'make' and 'make install'. On BSD, you may need to use 'gmake'. 12 | 13 | Configuration: 14 | ============== 15 | 16 | See INTRO.html, radvd.conf(8) and radvd.conf.example. 17 | 18 | Frequently Asked Questions: 19 | =========================== 20 | 21 | Setting up radvd is very simple, so the most frequently asked 22 | questions have been about what radvd _doesn't_ do... 23 | 24 | 1. How do I set up the router running radvd to automatically 25 | configure an address from the prefix advertised in Route 26 | Advertisements from upstream? 27 | 28 | -- You don't. By the specification, routers ignore RAs. 29 | You'll probably need to use manual configuration. But you 30 | can't use the same prefix on two links in any case unless you 31 | use something like proxy-ND (draft-ietf-ipv6-ndproxy-04.txt). 32 | You may need to re-think your topology; prefix delegation 33 | (e.g., manually or with RFC3633) may help. 34 | 35 | 2. How do I set up the router running radvd to automatically 36 | configure the interfaces to use an EUI64-based address? 37 | 38 | -- You don't. The design philosophy of radvd is that it's 39 | not the _router's_ configuration tool, but a route advertising 40 | daemon. You'll need to set up all the addresses, routes, etc. 41 | yourself. These tasks are something that system initscripts 42 | could possibly do instead. 43 | 44 | 3. I have a dynamic /48 prefix. How do I set up radvd to: 45 | a) set up interface addresses and routes on downstream 46 | interfaces, and 47 | b) advertise /64 prefixes from the /48 on downstream interfaces? 48 | 49 | -- For a), this isn't supported. For b), radvd includes special 50 | support for 6to4 upstream interface but assumes that the interface 51 | addresses/routes are set up manually. This should probably 52 | be done in the initscripts or manually. (Though if someone were 53 | to send a patch for b), it might be incorporated.) 54 | 55 | 4. How do I set up radvd to do either unicast or multicast routing? 56 | 57 | -- You don't. Radvd is not a routing or forwarding daemon. 58 | You need to set any appropriate routing/forwarding first, 59 | and then radvd to only advertise the prefixes to hosts as 60 | appropriate. 61 | -------------------------------------------------------------------------------- /test/test_dnssl6.conf: -------------------------------------------------------------------------------- 1 | interface eth0 { 2 | # This tests for the largest possible single option 3 | DNSSL 4 | # 62 x char => encodes as 64 bytes per entry. 5 | # a-z + 0-9 = 36 entries 6 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. 7 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. 8 | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc. 9 | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd. 10 | eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee. 11 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff. 12 | gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg. 13 | hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh. 14 | iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii. 15 | jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj. 16 | kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk. 17 | llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll. 18 | mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm. 19 | nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn. 20 | oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo. 21 | pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp. 22 | qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq. 23 | rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr. 24 | ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss. 25 | tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt. 26 | uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu. 27 | vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv. 28 | wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww. 29 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. 30 | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy. 31 | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz. 32 | 00000000000000000000000000000000000000000000000000000000000000. 33 | 11111111111111111111111111111111111111111111111111111111111111. 34 | 22222222222222222222222222222222222222222222222222222222222222. 35 | 33333333333333333333333333333333333333333333333333333333333333. 36 | 44444444444444444444444444444444444444444444444444444444444444. # 31 including '4's 37 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. # takes another 48 bytes (0x2e, 46 x "A", 0x00); 0x2e -> 46 38 | { 39 | AdvDNSSLLifetime 1234; 40 | }; 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /includes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #pragma once 16 | 17 | #include "config.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #ifndef HAVE_STRLCPY 30 | #include // strlcpy 31 | #endif 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #ifdef HAVE_INTTYPES_H 38 | #include 39 | #endif 40 | 41 | #ifdef HAVE_SYS_PARAM_H 42 | #include 43 | #else 44 | #ifdef HAVE_MACHINE_PARAM_H 45 | #include 46 | #endif 47 | #ifdef HAVE_MACHINE_LIMITS_H 48 | #include 49 | #endif 50 | #endif 51 | 52 | #if TIME_WITH_SYS_TIME 53 | #include 54 | #include 55 | #else 56 | #if HAVE_SYS_TIME_H 57 | #include 58 | #else 59 | #include 60 | #endif 61 | #endif 62 | 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | #include 69 | #include 70 | 71 | #include 72 | 73 | #include 74 | #include 75 | 76 | #include 77 | 78 | #ifdef HAVE_SYSCTL 79 | #include 80 | #endif 81 | 82 | #include 83 | 84 | #ifdef HAVE_NET_IF_DL_H 85 | #include 86 | #endif 87 | 88 | #ifdef HAVE_NET_IF_TYPES_H 89 | #include 90 | #endif 91 | 92 | #if defined(HAVE_NET_IF_ARP_H) && !defined(ARPHRD_ETHER) && !defined(HAVE_LINUX_IF_ARP_H) 93 | #include 94 | #endif /* defined(HAVE_NET_IF_ARP_H) && !defined(ARPHRD_ETHER) */ 95 | 96 | #ifdef HAVE_SYS_SOCKIO_H 97 | #include 98 | #endif 99 | 100 | #ifdef HAVE_GETOPT_H 101 | #include 102 | #endif 103 | 104 | #ifdef HAVE_IFADDRS_H 105 | #include 106 | #endif 107 | 108 | #ifdef HAVE_LINUX_IF_ARP_H 109 | #include 110 | #endif 111 | -------------------------------------------------------------------------------- /test/test_dnssl5.conf: -------------------------------------------------------------------------------- 1 | # This test should exceed the max 255x 8 octets possible RFC8106 section-5.2 2 | interface eth0 { 3 | DNSSL 4 | # 62 x char => encodes as 64 bytes per entry. 5 | # a-z + 0-9 = 36 entries 6 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. 7 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. 8 | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc. 9 | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd. 10 | eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee. 11 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff. 12 | gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg. 13 | hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh. 14 | iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii. 15 | jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj. 16 | kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk. 17 | llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll. 18 | mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm. 19 | nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn. 20 | oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo. 21 | pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp. 22 | qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq. 23 | rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr. 24 | ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss. 25 | tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt. 26 | uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu. 27 | vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv. 28 | wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww. 29 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. 30 | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy. 31 | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz. 32 | 00000000000000000000000000000000000000000000000000000000000000. 33 | 11111111111111111111111111111111111111111111111111111111111111. 34 | 22222222222222222222222222222222222222222222222222222222222222. 35 | 33333333333333333333333333333333333333333333333333333333333333. 36 | 44444444444444444444444444444444444444444444444444444444444444. # 31 including '4's 37 | 55555555555555555555555555555555555555555555555555555555555555. # 32 including '5's 38 | #66666666666666666666666666666666666666666666666666666666666666. 39 | #77777777777777777777777777777777777777777777777777777777777777. 40 | #88888888888888888888888888888888888888888888888888888888888888. 41 | #99999999999999999999999999999999999999999999999999999999999999. 42 | { 43 | AdvDNSSLLifetime 1234; 44 | }; 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /RELEASE-PROCESS.md: -------------------------------------------------------------------------------- 1 | # Rough release process 2 | 3 | ## Update `CHANGES` 4 | 5 | Go through `git log` and ensure that each relevant change is documented in 6 | the `CHANGES file. 7 | 8 | ## Ensure version consistency 9 | 10 | The version identifier needs to be consistent amongst the `CHANGES` file, the 11 | `configure.ac` file, and the git tag. First, determine the currently-configured 12 | version identifier, such as by running: 13 | 14 | ``` 15 | grep AC_INIT configure.ac | cut -d[ -f 2 | cut -d] -f 1 16 | grep Release CHANGES | head -1 17 | ``` 18 | 19 | When preparing a release candidate build, the version string should end with 20 | `_rcN`, where N is the candidate build number. 21 | 22 | Conventionally, the `CHANGES` file will contain a string in the format 23 | `v`, such as `v2.20_rc1` or `v2.20`, while the git tag and 24 | `configure.ac` file will contain a string in the format ``, such as 25 | `2.20_rc1` or `2.20.` 26 | 27 | Edit the `CHANGES` file and note the new version identifier. 28 | 29 | ## Update `configure.ac` 30 | 31 | After manually updating the `CHANGES` file, update the `configure.ac` file 32 | with a matching version identifier, such as: 33 | 34 | ``` 35 | export VERSION="$(grep Release CHANGES | head -1 | sed s/'.*Release v'//g)" 36 | echo "New version identifier is: $VERSION" 37 | sed -i -e "/^AC_INIT/s,\[.*\],[$VERSION],g" configure.ac 38 | ``` 39 | 40 | ## Validate, commit, and tag 41 | 42 | Next, examine the changes to ensure accuracy: 43 | 44 | ``` 45 | git diff CHANGES configure.ac 46 | ``` 47 | 48 | If everything looks good, commit the changes and create the tag. Note that 49 | this will create a signed tag, so ensure that you have GPG configured 50 | appropriately. 51 | 52 | ``` 53 | git commit -s -m "Release ${VERSION}" CHANGES configure.ac 54 | git tag -s v${VERSION} -m "$VERSION" 55 | ``` 56 | 57 | ## Build release archives 58 | 59 | ### Clean up Docker environment 60 | 61 | To build the release archives, first delete the container manually to ensure 62 | the build works with a clean container (this command may fail if hte container 63 | does not exist): 64 | 65 | ``` 66 | docker rmi radvd-autogen:latest 67 | ``` 68 | 69 | ### Perform a package build 70 | 71 | The `autogen-container.sh` script will run `autoreconf` in a clean environment. 72 | Afterward, the `./configure` script can be run in order to configure the build 73 | environment. Finally, `make packages` will create package archives suitable 74 | for release. 75 | 76 | ``` 77 | ./autogen-container.sh 78 | ./configure 79 | make packages 80 | ``` 81 | 82 | ### Release the new version on GitHub 83 | 84 | To perform this step, first install and configure the 85 | [GitHub CLI](https://cli.github.com/). 86 | 87 | ``` 88 | gh release create v${VERSION} radvd-${VERSION}.tar.{xz,gz}{,.asc,.sha256,.sha512}` 89 | ``` 90 | -------------------------------------------------------------------------------- /recv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #include "config.h" 17 | #include "includes.h" 18 | #include "radvd.h" 19 | 20 | int recv_rs_ra(int sock, unsigned char *msg, struct sockaddr_in6 *addr, struct in6_pktinfo **pkt_info, int *hoplimit, 21 | unsigned char *chdr) 22 | { 23 | struct iovec iov; 24 | iov.iov_len = MSG_SIZE_RECV; 25 | iov.iov_base = (caddr_t)msg; 26 | 27 | struct msghdr mhdr; 28 | memset(&mhdr, 0, sizeof(mhdr)); 29 | mhdr.msg_name = (caddr_t)addr; 30 | mhdr.msg_namelen = sizeof(*addr); 31 | mhdr.msg_iov = &iov; 32 | mhdr.msg_iovlen = 1; 33 | mhdr.msg_control = (void *)chdr; 34 | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); 35 | 36 | int len = recvmsg(sock, &mhdr, 0); 37 | 38 | if (len < 0) { 39 | if (errno != EINTR) 40 | flog(LOG_ERR, "recvmsg: %s", strerror(errno)); 41 | 42 | return len; 43 | } 44 | 45 | *hoplimit = 255; 46 | 47 | for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mhdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) { 48 | if (cmsg->cmsg_level != IPPROTO_IPV6) 49 | continue; 50 | 51 | switch (cmsg->cmsg_type) { 52 | #ifdef IPV6_HOPLIMIT 53 | case IPV6_HOPLIMIT: 54 | if ((cmsg->cmsg_len == CMSG_LEN(sizeof(int))) && (*(int *)CMSG_DATA(cmsg) >= 0) && 55 | (*(int *)CMSG_DATA(cmsg) < 256)) { 56 | *hoplimit = *(int *)CMSG_DATA(cmsg); 57 | } else { 58 | flog(LOG_ERR, "received a bogus IPV6_HOPLIMIT from the kernel! len=%d, data=%d", 59 | (int)cmsg->cmsg_len, *(int *)CMSG_DATA(cmsg)); 60 | return -1; 61 | } 62 | break; 63 | #endif /* IPV6_HOPLIMIT */ 64 | case IPV6_PKTINFO: 65 | if ((cmsg->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) && 66 | ((struct in6_pktinfo *)CMSG_DATA(cmsg))->ipi6_ifindex) { 67 | *pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 68 | } else { 69 | flog(LOG_ERR, "received a bogus IPV6_PKTINFO from the kernel! len=%d, index=%d", 70 | (int)cmsg->cmsg_len, ((struct in6_pktinfo *)CMSG_DATA(cmsg))->ipi6_ifindex); 71 | return -1; 72 | } 73 | break; 74 | } 75 | } 76 | 77 | char if_namebuf[IF_NAMESIZE] = {""}; 78 | char *if_name = 0; 79 | if (pkt_info && *pkt_info) { 80 | if_name = if_indextoname((*pkt_info)->ipi6_ifindex, if_namebuf); 81 | } 82 | if (!if_name) { 83 | if_name = "unknown interface"; 84 | } 85 | dlog(LOG_DEBUG, 5, "%s recvmsg len=%d", if_name, len); 86 | 87 | return len; 88 | } 89 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #include "config.h" 17 | #include "includes.h" 18 | #include "radvd.h" 19 | 20 | /* Note: these are applicable to receiving sockopts only */ 21 | #if defined IPV6_HOPLIMIT && !defined IPV6_RECVHOPLIMIT 22 | #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT 23 | #endif 24 | 25 | #if defined IPV6_PKTINFO && !defined IPV6_RECVPKTINFO 26 | #define IPV6_RECVPKTINFO IPV6_PKTINFO 27 | #endif 28 | 29 | int open_icmpv6_socket(void) 30 | { 31 | int sock; 32 | struct icmp6_filter filter; 33 | int err; 34 | 35 | sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 36 | if (sock < 0) { 37 | flog(LOG_ERR, "can't create socket(AF_INET6): %s", strerror(errno)); 38 | return -1; 39 | } 40 | 41 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, (int[]){1}, sizeof(int)); 42 | if (err < 0) { 43 | flog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %s", strerror(errno)); 44 | return -1; 45 | } 46 | 47 | #ifdef __linux__ 48 | err = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, (int[]){2}, sizeof(int)); 49 | #else 50 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, (int[]){2}, sizeof(int)); 51 | #endif 52 | if (err < 0) { 53 | flog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %s", strerror(errno)); 54 | return -1; 55 | } 56 | 57 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (int[]){255}, sizeof(int)); 58 | if (err < 0) { 59 | flog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); 60 | return -1; 61 | } 62 | 63 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (int[]){255}, sizeof(int)); 64 | if (err < 0) { 65 | flog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); 66 | return -1; 67 | } 68 | 69 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (int[]){0}, sizeof(int)); 70 | if (err < 0) { 71 | flog(LOG_ERR, "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); 72 | return -1; 73 | } 74 | 75 | #ifdef IPV6_RECVHOPLIMIT 76 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (int[]){1}, sizeof(int)); 77 | if (err < 0) { 78 | flog(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT): %s", strerror(errno)); 79 | return -1; 80 | } 81 | #endif 82 | 83 | /* 84 | * setup ICMP filter 85 | */ 86 | 87 | ICMP6_FILTER_SETBLOCKALL(&filter); 88 | ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); 89 | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); 90 | 91 | err = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); 92 | if (err < 0) { 93 | flog(LOG_ERR, "setsockopt(ICMPV6_FILTER): %s", strerror(errno)); 94 | return -1; 95 | } 96 | 97 | return sock; 98 | } 99 | -------------------------------------------------------------------------------- /redhat/systemd/radvd.spec.in: -------------------------------------------------------------------------------- 1 | %define RADVD_UID 75 2 | 3 | Summary: A Router Advertisement daemon 4 | Name: radvd 5 | Version: @VERSION@ 6 | Release: 1%{?dist} 7 | # The code includes the advertising clause, so it's GPL-incompatible 8 | License: BSD with advertising 9 | Group: System Environment/Daemons 10 | URL: http://www.litech.org/radvd/ 11 | Source: http://www.litech.org/radvd/dist/%{name}-%{version}.tar.gz 12 | 13 | BuildRequires: gcc 14 | BuildRequires: bison 15 | BuildRequires: flex 16 | BuildRequires: flex-static 17 | BuildRequires: pkgconfig 18 | BuildRequires: check-devel 19 | BuildRequires: systemd 20 | BuildRequires: libbsd-devel 21 | %{?systemd_requires} 22 | Requires(pre): shadow-utils 23 | # SuSE calls it libbsd0, Fedora calls it libbsd 24 | Requires: libbsd0 25 | 26 | %description 27 | radvd is the router advertisement daemon for IPv6. It listens to router 28 | solicitations and sends router advertisements as described in "Neighbor 29 | Discovery for IP Version 6 (IPv6)" (RFC 2461). With these advertisements 30 | hosts can automatically configure their addresses and some other 31 | parameters. They also can choose a default router based on these 32 | advertisements. 33 | 34 | Install radvd if you are setting up IPv6 network and/or Mobile IPv6 35 | services. 36 | 37 | %prep 38 | %setup -q 39 | 40 | for F in CHANGES; do 41 | iconv -f iso-8859-1 -t utf-8 < "$F" > "${F}.new" 42 | touch -r "$F" "${F}.new" 43 | mv "${F}.new" "$F" 44 | done 45 | 46 | %build 47 | export CFLAGS="$RPM_OPT_FLAGS -fPIE " 48 | export LDFLAGS='-pie -Wl,-z,relro,-z,now,-z,noexecstack,-z,nodlopen' 49 | %configure --with-pidfile=/run/radvd/radvd.pid 50 | make %{?_smp_mflags} 51 | 52 | %install 53 | make DESTDIR=%{buildroot} install 54 | 55 | mkdir -p %{buildroot}%{_sysconfdir}/sysconfig 56 | mkdir -p %{buildroot}/run/radvd 57 | mkdir -p %{buildroot}%{_unitdir} 58 | 59 | install -m 644 redhat/radvd.conf.empty %{buildroot}%{_sysconfdir}/radvd.conf 60 | install -m 644 redhat/radvd.sysconfig %{buildroot}%{_sysconfdir}/sysconfig/radvd 61 | 62 | install -d -m 755 %{buildroot}%{_tmpfilesdir} 63 | install -p -m 644 %{SOURCE1} %{buildroot}%{_tmpfilesdir}/radvd.conf 64 | install -p -m 644 redhat/radvd-tmpfs.conf %{buildroot}%{_tmpfilesdir}/radvd.conf 65 | install -m 644 redhat/radvd.service %{buildroot}%{_unitdir} 66 | 67 | %check 68 | make check 69 | 70 | %postun 71 | %systemd_postun_with_restart radvd.service 72 | 73 | %post 74 | %systemd_post radvd.service 75 | 76 | %preun 77 | %systemd_preun radvd.service 78 | 79 | # Static UID and GID defined by /usr/share/doc/setup-*/uidgid 80 | %pre 81 | getent group radvd >/dev/null || groupadd -r -g %RADVD_UID radvd 82 | getent passwd radvd >/dev/null || \ 83 | useradd -r -u %RADVD_UID -g radvd -d / -s /sbin/nologin -c "radvd user" radvd 84 | exit 0 85 | 86 | %files 87 | %doc CHANGES COPYRIGHT INTRO.html README TODO 88 | %{_unitdir}/radvd.service 89 | %config(noreplace) %{_sysconfdir}/radvd.conf 90 | %config(noreplace) %{_sysconfdir}/sysconfig/radvd 91 | %{_tmpfilesdir}/radvd.conf 92 | %dir %attr(755,radvd,radvd) /run/radvd/ 93 | %doc radvd.conf.example 94 | %{_mandir}/*/* 95 | %{_sbindir}/radvd 96 | %{_sbindir}/radvdump 97 | 98 | -------------------------------------------------------------------------------- /redhat/SysV/radvd.spec.in: -------------------------------------------------------------------------------- 1 | 2 | %define initdir %{_sysconfdir}/rc.d/init.d 3 | 4 | %define RADVD_UID 75 5 | 6 | Summary: A Router Advertisement daemon 7 | Name: radvd 8 | Version: @VERSION@ 9 | Release: 1 10 | # The code includes the advertising clause, so it's GPL-incompatible 11 | License: BSD with advertising 12 | Group: System Environment/Daemons 13 | URL: http://www.litech.org/radvd/ 14 | Source: http://www.litech.org/radvd/dist/%{name}-%{version}.tar.gz 15 | Requires(postun): chkconfig, initscripts 16 | Requires(preun): chkconfig, initscripts 17 | Requires(post): chkconfig 18 | Requires(pre): /usr/sbin/useradd 19 | # SuSE calls it libbsd0, Fedora calls it libbsd 20 | Requires: libbsd0 21 | BuildRequires: flex, byacc, libbsd-devel 22 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 23 | 24 | %description 25 | radvd is the router advertisement daemon for IPv6. It listens to router 26 | solicitations and sends router advertisements as described in "Neighbor 27 | Discovery for IP Version 6 (IPv6)" (RFC 2461). With these advertisements 28 | hosts can automatically configure their addresses and some other 29 | parameters. They also can choose a default router based on these 30 | advertisements. 31 | 32 | Install radvd if you are setting up IPv6 network and/or Mobile IPv6 33 | services. 34 | 35 | %prep 36 | %setup -q 37 | 38 | %build 39 | export CFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIE -fno-strict-aliasing" 40 | export LDFLAGS='-pie -Wl,-z,relro,-z,now,-z,noexecstack,-z,nodlopen' 41 | %configure --with-pidfile=%{_localstatedir}/run/radvd/radvd.pid 42 | make 43 | # make %{?_smp_mflags} 44 | # Parallel builds still fail because seds that transform y.tab.x into 45 | # scanner/gram.x are not executed before compile of scanner/gram.x 46 | # 47 | 48 | %install 49 | [ $RPM_BUILD_ROOT != "/" ] && rm -rf $RPM_BUILD_ROOT 50 | 51 | make DESTDIR=$RPM_BUILD_ROOT install 52 | 53 | mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig 54 | mkdir -p $RPM_BUILD_ROOT%{initdir} 55 | mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/radvd 56 | 57 | install -m 644 redhat/radvd.conf.empty $RPM_BUILD_ROOT%{_sysconfdir}/radvd.conf 58 | install -m 755 redhat/radvd.init $RPM_BUILD_ROOT%{initdir}/radvd 59 | install -m 644 redhat/radvd.sysconfig $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/radvd 60 | 61 | install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/tmpfiles.d 62 | install -p -m 644 redhat/radvd-tmpfs.conf $RPM_BUILD_ROOT%{_sysconfdir}/tmpfiles.d/radvd.conf 63 | 64 | %clean 65 | [ $RPM_BUILD_ROOT != "/" ] && rm -rf $RPM_BUILD_ROOT 66 | 67 | %postun 68 | if [ "$1" -ge "1" ]; then 69 | /sbin/service radvd condrestart >/dev/null 2>&1 70 | fi 71 | 72 | %post 73 | /sbin/chkconfig --add radvd 74 | 75 | %preun 76 | if [ $1 = 0 ]; then 77 | /sbin/service radvd stop >/dev/null 2>&1 78 | /sbin/chkconfig --del radvd 79 | fi 80 | 81 | %pre 82 | getent group radvd >/dev/null || groupadd -g %RADVD_UID -r radvd 83 | getent passwd radvd >/dev/null || \ 84 | useradd -r -u %RADVD_UID -g radvd -d / -s /sbin/nologin -c "radvd user" radvd 85 | exit 0 86 | 87 | %files 88 | %defattr(-,root,root,-) 89 | %doc COPYRIGHT README CHANGES INTRO.html TODO 90 | %config(noreplace) %{_sysconfdir}/radvd.conf 91 | %config(noreplace) %{_sysconfdir}/sysconfig/radvd 92 | %config(noreplace) %{_sysconfdir}/tmpfiles.d/radvd.conf 93 | %{initdir}/radvd 94 | %dir %attr(-,radvd,radvd) %{_localstatedir}/run/radvd/ 95 | %doc radvd.conf.example 96 | %{_mandir}/*/* 97 | %{_sbindir}/radvd 98 | %{_sbindir}/radvdump 99 | -------------------------------------------------------------------------------- /.github/workflows/linux.yaml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | push: 7 | branches: 8 | - master 9 | tags: 10 | - 'v*' 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | alpine: 18 | strategy: 19 | matrix: 20 | compiler: [clang, gcc] 21 | runs-on: ubuntu-latest 22 | container: alpine:latest 23 | steps: 24 | - name: Checkout sources 25 | uses: actions/checkout@v4 26 | - name: Build 27 | env: 28 | CC: ${{ matrix.compiler }} 29 | run: | 30 | test/test_build.sh alpine 31 | - uses: actions/upload-artifact@v4 32 | if: always() 33 | with: 34 | name: alpine-${{ matrix.compiler }}-log-file 35 | path: ./test-suite.log 36 | 37 | debian: 38 | strategy: 39 | matrix: 40 | compiler: [clang, gcc] 41 | runs-on: ubuntu-latest 42 | container: debian:stable 43 | steps: 44 | - name: Checkout sources 45 | uses: actions/checkout@v4 46 | - name: Build 47 | env: 48 | CC: ${{ matrix.compiler }} 49 | run: | 50 | test/test_build.sh debian 51 | - uses: actions/upload-artifact@v4 52 | if: always() 53 | with: 54 | name: debian-${{ matrix.compiler }}-log-file 55 | path: ./test-suite.log 56 | 57 | fedora: 58 | strategy: 59 | matrix: 60 | compiler: [clang, gcc] 61 | runs-on: ubuntu-latest 62 | container: fedora:latest 63 | steps: 64 | - name: Checkout sources 65 | uses: actions/checkout@v4 66 | - name: Install build dependency 67 | run: | 68 | dnf install --assumeyes awk 69 | - name: Build 70 | env: 71 | CC: ${{ matrix.compiler }} 72 | run: | 73 | test/test_build.sh fedora 74 | - uses: actions/upload-artifact@v4 75 | if: always() 76 | with: 77 | name: fedora-${{ matrix.compiler }}-log-file 78 | path: ./test-suite.log 79 | 80 | opensuse: 81 | strategy: 82 | matrix: 83 | compiler: [clang, gcc] 84 | runs-on: ubuntu-latest 85 | container: opensuse/leap:latest 86 | steps: 87 | - name: Install dependencies required by the checkout action 88 | run: | 89 | zypper --non-interactive refresh 90 | zypper --non-interactive install gzip tar 91 | - name: Checkout sources 92 | uses: actions/checkout@v4 93 | - name: Build 94 | env: 95 | CC: ${{ matrix.compiler }} 96 | run: | 97 | test/test_build.sh opensuse 98 | - uses: actions/upload-artifact@v4 99 | if: always() 100 | with: 101 | name: opensuse-${{ matrix.compiler }}-log-file 102 | path: ./test-suite.log 103 | 104 | ubuntu: 105 | strategy: 106 | matrix: 107 | ubuntu_version: [ubuntu-22.04, ubuntu-24.04] 108 | compiler: [clang, gcc] 109 | runs-on: ${{ matrix.ubuntu_version }} 110 | steps: 111 | - name: Checkout sources 112 | uses: actions/checkout@v4 113 | - name: Build 114 | env: 115 | CC: ${{ matrix.compiler }} 116 | run: | 117 | test/test_build.sh ubuntu 118 | - uses: actions/upload-artifact@v4 119 | if: always() 120 | with: 121 | name: ${{ matrix.ubuntu_version }}-${{ matrix.compiler }}-log-file 122 | path: ./test-suite.log 123 | -------------------------------------------------------------------------------- /test/check.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef HAVE_GETOPT_H 11 | #include 12 | #endif 13 | 14 | static void usage(char const *pname); 15 | static void version(void); 16 | Suite *util_suite(); 17 | Suite *send_suite(); 18 | 19 | #ifdef HAVE_GETOPT_LONG 20 | 21 | /* clang-format off */ 22 | static char usage_str[] = { 23 | "\n" 24 | " -h, --help Print the help and quit.\n" 25 | " -m, --mode The print mode (SILENT, MINIMAL, NORMAL, VERBOSE)\n" 26 | " -s, --suite=suite The suites to run.\n" 27 | " -t, --test=test The unit tests to run.\n" 28 | " -v, --version Print the version and quit.\n" 29 | }; 30 | 31 | static struct option prog_opt[] = { 32 | {"help", 0, 0, 'h'}, 33 | {"mode", 1, 0, 'm'}, 34 | {"suite", 1, 0, 's'}, 35 | {"test", 1, 0, 't'}, 36 | {"version", 0, 0, 'v'}, 37 | {NULL, 0, 0, 0} 38 | }; 39 | 40 | #else 41 | 42 | static char usage_str[] = { 43 | "[-hv] [-m mode] [-s suite] [-t test]" 44 | }; 45 | /* clang-format on */ 46 | 47 | #endif 48 | 49 | struct options { 50 | char *suite; 51 | char *test; 52 | int mode; 53 | }; 54 | 55 | static void process_command_line_args(int argc, char *argv[], struct options *options) 56 | { 57 | char const *pname = ((pname = strrchr(argv[0], '/')) != NULL) ? pname + 1 : argv[0]; 58 | int c; 59 | char *suite = 0; 60 | char *test = 0; 61 | int mode = CK_VERBOSE; 62 | 63 | /* parse args */ 64 | #define OPTIONS_STR "s:t:m:vh" 65 | #ifdef HAVE_GETOPT_LONG 66 | int opt_idx; 67 | while ((c = getopt_long(argc, argv, OPTIONS_STR, prog_opt, &opt_idx)) > 0) 68 | #else 69 | while ((c = getopt(argc, argv, OPTIONS_STR)) > 0) 70 | #endif 71 | { 72 | switch (c) { 73 | case 'm': 74 | if (0 == strcmp(optarg, "SILENT")) { 75 | mode = CK_SILENT; 76 | } else if (0 == strcmp(optarg, "MINIMAL")) { 77 | mode = CK_MINIMAL; 78 | } else if (0 == strcmp(optarg, "NORMAL")) { 79 | mode = CK_NORMAL; 80 | } else if (0 == strcmp(optarg, "VERBOSE")) { 81 | mode = CK_VERBOSE; 82 | } else if (0 == strcmp(optarg, "ENV")) { 83 | mode = CK_ENV; 84 | } else { 85 | fprintf(stderr, "%s: mode, \"%s\", unknown.\n", pname, optarg); 86 | exit(1); 87 | } 88 | break; 89 | case 's': 90 | if (suite) 91 | free(suite); 92 | suite = strdup(optarg); 93 | break; 94 | case 't': 95 | if (test) 96 | free(test); 97 | test = strdup(optarg); 98 | break; 99 | case 'v': 100 | version(); 101 | break; 102 | case 'h': 103 | usage(pname); 104 | #ifdef HAVE_GETOPT_LONG 105 | case ':': 106 | fprintf(stderr, "%s: option %s: parameter expected\n", pname, prog_opt[opt_idx].name); 107 | exit(1); 108 | #endif 109 | case '?': 110 | exit(1); 111 | } 112 | } 113 | 114 | if (options) { 115 | options->suite = suite; 116 | options->test = test; 117 | options->mode = mode; 118 | } 119 | } 120 | 121 | int main(int argc, char *argv[]) 122 | { 123 | srand((unsigned int)time(NULL)); 124 | 125 | struct options options = {0, 0, 0}; 126 | process_command_line_args(argc, argv, &options); 127 | 128 | SRunner *sr = srunner_create(util_suite()); 129 | srunner_add_suite(sr, send_suite()); 130 | srunner_run(sr, options.suite, options.test, options.mode); 131 | int number_failed = srunner_ntests_failed(sr); 132 | srunner_free(sr); 133 | 134 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 135 | } 136 | 137 | static void usage(char const *pname) 138 | { 139 | fprintf(stderr, "usage: %s %s\n", pname, usage_str); 140 | exit(1); 141 | } 142 | 143 | static void version(void) 144 | { 145 | fprintf(stderr, "Version: %s\n\n", VERSION); 146 | exit(0); 147 | } 148 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | NOTE WELL 3 | ========= 4 | 5 | *NONE* of the items are being actively worked on. 6 | So, if you're interested in one of these features, 7 | it means you'll have to submit a patch... :-) 8 | 9 | 10 | read list of interfaces with SIOCGIFCONF (AF_INET6 support in the kernel would 11 | be nice) or from /proc 12 | 13 | support for few protocol constants defined in RFC 4861 Sec 10 is missing. 14 | 15 | consider whether UnicastOnly flag should be detected by radvdump. 16 | 17 | The following parts of RFC4861 are not implemented: 18 | - section 6.2.5: when AdvSendAdvertisements changes to FALSE, we don't send 19 | a final RA with zero Router Lifetime (we just send it when shutting down). 20 | (SHOULD) 21 | - section 6.2.8: if the link-local address of the router changes, it should 22 | multicast a few RAs from the old address with zero router lifetime, and a 23 | few from the new address. (SHOULD). 24 | 25 | Consider whether to support RFC 4286 (Multicast Router Discovery). 26 | 27 | Consider whether to support multiple IPv4 addresses with Base6to4Interface 28 | (currently the code just picks one). 29 | 30 | Consider whether to support multiple prefixes and routes with a single 31 | configuration line (instead of having to specify each prefix/route 32 | separately) somewhat similar to how RDNSS configuration already supports. 33 | 34 | Consider whether to support a generalization of Base6to4Interface for 35 | arbitrary IPv6 prefixes, to be used for automatic generation of downstream 36 | prefixes. Also consider whether this would need to support multiple IPv6 37 | prefixes on the upstream interfaces. See question 3 in README for more on 38 | this. 39 | 40 | Use getifaddrs() instead of ioctl SIOCGIFADDR and other friends. The 41 | problem with this is that e.g. RHL73's glibc didn't support getifaddrs(), 42 | and before glibc 2.3.3 getifaddrs() didn't use netlink so it didn't work 43 | well with IPv6. These old compat concerns are likely moot now.. 44 | 45 | Implement Secure Neighbor Discovery (RFC 3971). 46 | 47 | Interface reconfiguration is only supported on Linux. Consider whether to 48 | support that (and privilege separation as a result) on BSD as well. 49 | 50 | The restrictions of the (privilege separation) master process could 51 | be restricted as currently it basically only needs to write to /proc, 52 | do some logging and nothing else. 53 | 54 | Triggered by: https://bugzilla.redhat.com/show_bug.cgi?id=554125 55 | Known problems (probably can't do anything about these): 56 | - If interface MAC address changes, radvd will keep sending the old MAC 57 | in its LL option (until HUP signal). The kernel will keep sending 58 | Ethernet frames with the old MAC until interface is flapped. 59 | More: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508466 60 | 61 | AdvRASolicitedUnicast: Further improvement is possible. 62 | - If we get a RS and are about to send a multicast RA, then we can further save 63 | battery on the client device by NOT sending a unicast response, and just 64 | letting the client wait for the multicast response. This should be 65 | implemented as a new time value, specifying the maximum amount of time 66 | remaining to as a deciding factor between send-solicited-RA-now vs 67 | wait-for-next-multicast. 68 | - The above can also form part of rate-limiting of unicast RA responses when 69 | network topology has recently changed. 70 | 71 | The following parts of RFC7222 are not yet implemented: 72 | - Section 5.1.3: Networks that serve battery-powered devices SHOULD NOT send 73 | multicast RAs too frequently (see Section 4) unless the information in the RA 74 | packet has substantially changed. If there is a desire to ensure that hosts 75 | pick up configuration changes quickly, those networks MAY send frequent 76 | Router Advertisements for a limited period of time (e.g., not more than one 77 | minute) immediately after a configuration change. 78 | -------------------------------------------------------------------------------- /radvd.conf.example: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 3 | # NOTE NOTE 4 | # NOTE This is an EXAMPLE, which serves only to demonstrate the NOTE 5 | # NOTE syntax of radvd.conf, and is not meant to be used for a NOTE 6 | # NOTE real radvd configuration. NOTE 7 | # NOTE NOTE 8 | # NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 9 | # 10 | 11 | interface lo 12 | { 13 | AdvSendAdvert on; 14 | 15 | # This may be needed on some interfaces which are not active when 16 | # radvd starts, but become available later on; see man page for details. 17 | 18 | # IgnoreIfMissing on; 19 | 20 | # 21 | # These settings cause advertisements to be sent every 3-10 seconds. This 22 | # range is good for 6to4 with a dynamic IPv4 address, but can be greatly 23 | # increased when not using 6to4 prefixes. 24 | # 25 | 26 | MinRtrAdvInterval 3; 27 | MaxRtrAdvInterval 10; 28 | 29 | # 30 | # You can use AdvDefaultPreference setting to advertise the preference of 31 | # the router for the purposes of default router determination. 32 | # NOTE: This feature is still being specified and is not widely supported! 33 | # 34 | AdvDefaultPreference low; 35 | 36 | # 37 | # Disable Mobile IPv6 support 38 | # 39 | AdvHomeAgentFlag off; 40 | 41 | # 42 | # example of a standard prefix 43 | # 44 | prefix 2001:db8:1:0::/64 45 | { 46 | AdvOnLink on; 47 | AdvAutonomous on; 48 | AdvRouterAddr off; 49 | }; 50 | 51 | # 52 | # example of a 6to4 prefix 53 | # 54 | # Note that the first 48 bits are specified here as zeros. These will be 55 | # replaced with the appropriate 6to4 address when radvd starts or is 56 | # reconfigured. Be sure that the SLA ID (1234 in this case) is specified 57 | # here! 58 | # 59 | prefix 0:0:0:1234::/64 60 | { 61 | AdvOnLink on; 62 | AdvAutonomous on; 63 | AdvRouterAddr off; 64 | 65 | # 66 | # This setting causes radvd to replace the first 48 bits of the prefix 67 | # with the 6to4 address generated from the specified interface. For example, 68 | # if the address of ppp0 is 192.0.2.25 when radvd configures itself, this 69 | # prefix will be advertised as 2002:C000:0219:1234::/64. 70 | # 71 | # If ppp0 is not available at configuration time, this prefix will not be 72 | # advertised, but other prefixes listed in the configuration will be 73 | # advertised as usual. 74 | # 75 | # When using the Base6to4Interface option, make sure radvd receives a 76 | # SIGHUP every time the ppp0 interface goes up, down, or is assigned a 77 | # new IPv4 address. The SIGHUP will cause radvd to recognize that the 78 | # ppp0 interface has changed and will adjust the advertisements 79 | # accordingly. 80 | # 81 | 82 | Base6to4Interface ppp0; 83 | 84 | # 85 | # If the IP address of ppp0 is assigned dynamically, be sure to set the 86 | # lifetimes for this prefix to be small. Otherwise, hosts on your network 87 | # may continue to use a prefix that no longer corresponds to the address 88 | # on ppp0! 89 | # 90 | AdvPreferredLifetime 120; 91 | AdvValidLifetime 300; 92 | }; 93 | # 94 | # example of a more specific route 95 | # NOTE: This feature is not very widely supported! You may also need to 96 | # enable it manually (e.g. on Linux, change the value of 97 | # sysctl accept_ra_rt_info_max_plen to 48 or 64) 98 | # 99 | route 2001:db0:fff::/48 100 | { 101 | AdvRoutePreference high; 102 | AdvRouteLifetime 3600; 103 | }; 104 | 105 | # 106 | # RDNSS 107 | # NOTE: This feature is not very widely implemented. 108 | # 109 | RDNSS 2001:db8::1 2001:db8::2 110 | { 111 | AdvRDNSSLifetime 30; 112 | }; 113 | 114 | # 115 | # DNS Search Lists 116 | # 117 | DNSSL branch.example.com example.com 118 | { 119 | AdvDNSSLLifetime 30; 120 | }; 121 | 122 | # 123 | # RFC8908 Captive-Portal API URL 124 | # 125 | # See RFC8952 Captive Portal Architecture, RFC8910 Captive-Portal 126 | # Identification in DHCP and Router Advertisements (RAs) and 127 | # RFC8908 Captive Portal API for more information. 128 | # 129 | 130 | # AdvCaptivePortalAPI "https://portal.example.net/api/capport.json"; 131 | 132 | }; 133 | 134 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file 10 | * COPYRIGHT applies to this software. If your distribution is missing 11 | * this file, you may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "includes.h" 17 | #include "radvd.h" 18 | 19 | __attribute__((format(printf, 2, 0))) static int vlog(int prio, char const *format, va_list ap); 20 | 21 | static int log_method = L_NONE; 22 | static char const *log_ident; 23 | static char const *log_file; 24 | static FILE *log_file_fd; 25 | static int log_facility; 26 | static int debug_level = 0; 27 | 28 | int log_open(int method, char const *ident, char const *log, int facility) 29 | { 30 | log_method = method; 31 | log_ident = ident; 32 | 33 | switch (log_method) { 34 | case L_NONE: 35 | case L_UNSPEC: 36 | case L_STDERR: 37 | break; 38 | case L_STDERR_CLEAN: 39 | /* fallthrough */ 40 | case L_STDERR_SYSLOG: 41 | /* fallthrough */ 42 | case L_SYSLOG: 43 | if (facility == -1) 44 | log_facility = LOG_DAEMON; 45 | else 46 | log_facility = facility; 47 | 48 | openlog(log_ident, LOG_PID, log_facility); 49 | break; 50 | case L_LOGFILE: 51 | if (!log) { 52 | fprintf(stderr, "%s (%d): no logfile specified\n", log_ident, getpid()); 53 | return -1; 54 | } 55 | log_file = log; 56 | if ((log_file_fd = fopen(log_file, "a")) == NULL) { 57 | fprintf(stderr, "%s (%d): can't open %s: %s\n", log_ident, getpid(), log_file, strerror(errno)); 58 | return -1; 59 | } 60 | break; 61 | default: 62 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 63 | log_method = L_NONE; 64 | return -1; 65 | } 66 | return 0; 67 | } 68 | 69 | /* note: [dfv]log() is also called from root context */ 70 | __attribute__((format(printf, 2, 0))) static int vlog(int prio, char const *format, va_list ap) 71 | { 72 | char tstamp[64], buff[1024]; 73 | struct tm *tm; 74 | time_t current; 75 | 76 | vsnprintf(buff, sizeof(buff), format, ap); 77 | 78 | switch (log_method) { 79 | case L_NONE: 80 | case L_UNSPEC: 81 | break; 82 | case L_SYSLOG: 83 | syslog(prio, "%s", buff); 84 | break; 85 | case L_STDERR_SYSLOG: 86 | syslog(prio, "%s", buff); 87 | if (debug_level < prio) /* fall through for messages with high priority */ 88 | break; 89 | case L_STDERR: 90 | current = time(NULL); 91 | tm = localtime(¤t); 92 | (void)strftime(tstamp, sizeof(tstamp), LOG_TIME_FORMAT, tm); 93 | 94 | fprintf(stderr, "[%s] %s (%d): %s\n", tstamp, log_ident, getpid(), buff); 95 | fflush(stderr); 96 | break; 97 | case L_STDERR_CLEAN: 98 | fprintf(stderr, "%s\n", buff); 99 | fflush(stderr); 100 | break; 101 | case L_LOGFILE: 102 | current = time(NULL); 103 | tm = localtime(¤t); 104 | (void)strftime(tstamp, sizeof(tstamp), LOG_TIME_FORMAT, tm); 105 | 106 | fprintf(log_file_fd, "[%s] %s (%d): %s\n", tstamp, log_ident, getpid(), buff); 107 | fflush(log_file_fd); 108 | break; 109 | default: 110 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 111 | log_method = L_NONE; 112 | return -1; 113 | } 114 | return 0; 115 | } 116 | 117 | void dlog(int prio, int level, char const *format, ...) 118 | { 119 | if (debug_level < level) 120 | return; 121 | 122 | va_list ap; 123 | va_start(ap, format); 124 | vlog(prio, format, ap); 125 | va_end(ap); 126 | } 127 | 128 | void flog(int prio, char const *format, ...) 129 | { 130 | va_list ap; 131 | 132 | va_start(ap, format); 133 | vlog(prio, format, ap); 134 | va_end(ap); 135 | } 136 | 137 | int log_close(void) 138 | { 139 | switch (log_method) { 140 | case L_NONE: 141 | case L_UNSPEC: 142 | case L_STDERR: 143 | break; 144 | case L_STDERR_SYSLOG: 145 | case L_SYSLOG: 146 | closelog(); 147 | break; 148 | case L_LOGFILE: 149 | fclose(log_file_fd); 150 | break; 151 | default: 152 | fprintf(stderr, "%s (%d): unknown logging method: %d\n", log_ident, getpid(), log_method); 153 | log_method = L_NONE; 154 | return -1; 155 | } 156 | return 0; 157 | } 158 | 159 | void set_debuglevel(int level) { debug_level = level; } 160 | 161 | int get_debuglevel(void) { return debug_level; } 162 | -------------------------------------------------------------------------------- /INTRO.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RADVD Introduction 7 | 8 | 9 | 10 |

RADVD Introduction

11 | 12 |

13 | by Lars Fenneberg et al. Updated 14 | 2002-01-12. 15 |

16 | 17 |

18 | IPv6 has a lot more 19 | support for autoconfiguration than 20 | IPv4. But for this 21 | autoconfiguration to work on the hosts of a network, the routers 22 | of the local network have to run a program which answers the 23 | autoconfiguration requests of the hosts. 24 |

25 | 26 |

27 | On Linux this program is called 28 | radvd, which stands for 29 | Router ADVertisement Daemon. This daemon listens to Router Solicitations 30 | (RS) and answers with Router Advertisement (RA). Furthermore unsolicited 31 | RAs are also send from time to time. 32 | RFC 4861 defines most 33 | functions of radvd. 34 |

35 | 36 |

37 | Router Advertisements contain information, which is used by hosts to 38 | configure their interfaces. This information includes address prefixes, 39 | the MTU of the link and information about default routers. 40 |

41 | 42 |

43 | Of course the routers can't autoconfigure themselves, so the information 44 | on the routers has to be provided by the administrator of the system. 45 | This is done by manually configuring the interfaces and routes and by 46 | configuring the router advertisement daemon. 47 |

48 | 49 |

50 | A small and simple configuration file for radvd might look like this: 51 |

52 | 53 |

54 |

 55 | 
 56 | interface eth0
 57 | {
 58 |         AdvSendAdvert on;
 59 |         prefix 2001:db8:0:1::/64
 60 |         {
 61 |                 AdvOnLink on;
 62 |                 AdvAutonomous on;
 63 |         };
 64 | };
 65 | 
 66 | 
67 | 68 |

69 | It says that radvd should advertise (AdvSendAdvert on) the prefix 70 | 2001:db8:0:1:: which has a lenght of 64 on the interface eth0. 71 | Also the prefix is marked as autonomous (AdvAutonomous on) and as on-link 72 | (AdvOnLink on). The both currently default to enabled but are included 73 | here for introductory purposes; it isn't necessary to define them. 74 | All the other options are left on their default values. 75 |

76 | 77 |

78 | Autonomous means that the prefix can be used for automatic address 79 | configuration and on-link means that the hosts can assume that all the hosts 80 | which use this prefix are reachable via the interface on which the host 81 | received this RA. 82 |

83 | 84 |

85 | The prefix must be 64 bits long (apart from very few exceptions), as dictated by 86 | RFC 2464 and other 87 | standards for different link-layer technologies. For more 88 | details, see RFC 4862 89 | (IPv6 Stateless Address Autoconfiguration) and 90 | RFC 2464 91 | (Transmission of IPv6 Packets over Ethernet Networks). For more information 92 | on configuring radvd please look at the manual pages which are included in 93 | the radvd distribution. 94 |

95 | 96 |

97 | So, when an interface on a host is UPed and a RA is received, the host 98 | can configure an address on the interface by using the prefix and 99 | appending the EUI-64 identifier derived from the hardware address 100 | (also called link-layer token). The EUI-64 identifier is simply appended 101 | after the prefix. For example: 102 |

103 | 104 |

105 |

106 | 
107 |    Announced prefix:    2001:db8:0:1::
108 | 
109 |    MAC address:         00:07:E9:7B:02:59
110 | 
111 |    EUI-64 identifier:   0207:e9ff:fe7b:259
112 | 
113 |    Configured address:  2001:db8:0:1:207:e9ff:fe7b:259
114 | 
115 | 
116 | 117 |

118 | The host can also choose a default router by examining the RA. 119 | The rest works automatically. 120 |

121 | 122 |

123 | So now we've configured radvd, but we still need to configure the interfaces 124 | and set the routes (on the router). There's a lot of good material on 125 | setting up IPv6, and the reader is encouraged to have a look 126 | at it; for example: 127 |

128 | 129 |

130 |

134 | 135 |
136 | 137 |

138 | Copyright © 1997 Lars Fenneberg 139 |

140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Advanced" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | schedule: 20 | - cron: '43 5 * * 3' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: c-cpp 47 | build-mode: manual 48 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 49 | # Use `c-cpp` to analyze code written in C, C++ or both 50 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 51 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 52 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 53 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 54 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 55 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 56 | steps: 57 | - name: Checkout repository 58 | uses: actions/checkout@v4 59 | 60 | # Initializes the CodeQL tools for scanning. 61 | - name: Initialize CodeQL 62 | uses: github/codeql-action/init@v3 63 | env: 64 | CODEQL_EXTRACTOR_CPP_AUTOINSTALL_DEPENDENCIES: false 65 | with: 66 | languages: ${{ matrix.language }} 67 | build-mode: ${{ matrix.build-mode }} 68 | dependency-caching: true 69 | 70 | # If you wish to specify custom queries, you can do so here or in a config file. 71 | # By default, queries listed here will override any specified in a config file. 72 | # Prefix the list here with "+" to use these queries and those in the config file. 73 | 74 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 75 | # queries: security-extended,security-and-quality 76 | 77 | # If the analyze step fails for one of the languages you are analyzing with 78 | # "We were unable to automatically build your code", modify the matrix above 79 | # to set the build mode to "manual" for that language. Then modify this step 80 | # to build your code. 81 | # ℹ️ Command-line programs to run using the OS shell. 82 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 83 | - if: matrix.build-mode == 'manual' 84 | shell: bash 85 | run: | 86 | sudo apt-get -qy update || exit 1 87 | DEBIAN_FRONTEND=noninteractive sudo apt-get -qy install autoconf automake bison build-essential check clang flex gettext libtool pkg-config libbsd-dev libbsd0 systemd || exit 1 88 | #autoupdate -v # Scans run on newer Ubuntu deliberately # Disable to verify behavior 89 | ./autogen.sh || exit 1 90 | ./configure --with-check || exit 1 91 | make -j all || exit 1 # If both all & check_all are together, triggers race condition w/ scanner.l & gram.y 92 | make -j check_all || exit 1 93 | make check || exit 1 94 | 95 | - name: Perform CodeQL Analysis 96 | uses: github/codeql-action/analyze@v3 97 | with: 98 | category: "/language:${{matrix.language}}" 99 | 100 | -------------------------------------------------------------------------------- /scanner.l: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996-2000 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | %option nounput noinput noyywrap yylineno caseless 17 | 18 | %{ 19 | #include "config.h" 20 | #include "includes.h" 21 | #include "log.h" 22 | #include "gram.h" 23 | 24 | int num_lines; 25 | 26 | %} 27 | 28 | digit [0-9] 29 | number ({digit})+ 30 | snum -?({digit})+ 31 | decimal ({number}"."{number}) 32 | hexdigit ([a-f]|[A-F]|[0-9]) 33 | addr1 {hexdigit}{1,4}":"({hexdigit}{1,4}":")*(":"{hexdigit}{1,4})+ 34 | addr2 {hexdigit}{1,4}(":"{hexdigit}{1,4})*"::" 35 | addr3 ({hexdigit}{1,4}":"){7}{hexdigit}{1,4} 36 | addr ({addr1}|{addr2}|{addr3}|"::") 37 | naddr ("!"{addr}) 38 | whitespace ([ \t])+ 39 | string [a-zA-Z0-9`~!@#$%\^&*()_\-+=:\[\]<>,\.?\\]+|L?\"(\\.|[^\\"])*\" 40 | %% 41 | 42 | #.*$ {/* ignore comments */} 43 | \n {num_lines++;} 44 | {whitespace} {} 45 | 46 | interface { return T_INTERFACE; } 47 | prefix { return T_PREFIX; } 48 | route { return T_ROUTE; } 49 | RDNSS { return T_RDNSS; } 50 | DNSSL { return T_DNSSL; } 51 | clients { return T_CLIENTS; } 52 | lowpanco { return T_LOWPANCO; } 53 | abro { return T_ABRO; } 54 | nat64prefix { return T_NAT64PREFIX; } 55 | autoignoreprefixes { return T_AUTOIGNOREPREFIX; } 56 | 57 | AdvRASrcAddress { return T_RASRCADDRESS; } 58 | 59 | IgnoreIfMissing { return T_IgnoreIfMissing; } 60 | AdvSendAdvert { return T_AdvSendAdvert; } 61 | MaxRtrAdvInterval { return T_MaxRtrAdvInterval; } 62 | MinRtrAdvInterval { return T_MinRtrAdvInterval; } 63 | AdvManagedFlag { return T_AdvManagedFlag; } 64 | AdvOtherConfigFlag { return T_AdvOtherConfigFlag; } 65 | AdvLinkMTU { return T_AdvLinkMTU; } 66 | AdvRAMTU { return T_AdvRAMTU; } 67 | AdvReachableTime { return T_AdvReachableTime; } 68 | AdvRetransTimer { return T_AdvRetransTimer; } 69 | AdvCurHopLimit { return T_AdvCurHopLimit; } 70 | AdvDefaultLifetime { return T_AdvDefaultLifetime; } 71 | AdvDefaultPreference { return T_AdvDefaultPreference; } 72 | AdvSourceLLAddress { return T_AdvSourceLLAddress; } 73 | RemoveAdvOnExit { return T_RemoveAdvOnExit; } 74 | 75 | AdvOnLink { return T_AdvOnLink; } 76 | AdvAutonomous { return T_AdvAutonomous; } 77 | AdvValidLifetime { return T_AdvValidLifetime; } 78 | AdvPreferredLifetime { return T_AdvPreferredLifetime; } 79 | DeprecatePrefix { return T_DeprecatePrefix; } 80 | DecrementLifetimes { return T_DecrementLifetimes; } 81 | 82 | AdvRouterAddr { return T_AdvRouterAddr; } 83 | AdvHomeAgentFlag { return T_AdvHomeAgentFlag; } 84 | AdvIntervalOpt { return T_AdvIntervalOpt; } 85 | AdvHomeAgentInfo { return T_AdvHomeAgentInfo; } 86 | UnicastOnly { return T_UnicastOnly; } 87 | UnrestrictedUnicast { return T_UnrestrictedUnicast; } 88 | AdvRASolicitedUnicast { return T_AdvRASolicitedUnicast; } 89 | AdvCaptivePortalAPI { return T_AdvCaptivePortalAPI; } 90 | 91 | Base6Interface { return T_Base6Interface; } 92 | Base6to4Interface { return T_Base6to4Interface; } 93 | 94 | HomeAgentPreference { return T_HomeAgentPreference; } 95 | HomeAgentLifetime { return T_HomeAgentLifetime; } 96 | 97 | AdvRoutePreference { return T_AdvRoutePreference; } 98 | AdvRouteLifetime { return T_AdvRouteLifetime; } 99 | RemoveRoute { return T_RemoveRoute; } 100 | 101 | AdvRDNSSPreference { return T_AdvRDNSSPreference; } 102 | AdvRDNSSOpen { return T_AdvRDNSSOpenFlag; } 103 | AdvRDNSSLifetime { return T_AdvRDNSSLifetime; } 104 | FlushRDNSS { return T_FlushRDNSS; } 105 | 106 | AdvDNSSLLifetime { return T_AdvDNSSLLifetime; } 107 | FlushDNSSL { return T_FlushDNSSL; } 108 | 109 | MinDelayBetweenRAs { return T_MinDelayBetweenRAs; } 110 | 111 | AdvMobRtrSupportFlag { return T_AdvMobRtrSupportFlag; } 112 | 113 | AdvContextLength { return T_AdvContextLength; } 114 | AdvContextCompressionFlag { return T_AdvContextCompressionFlag; } 115 | AdvContextID { return T_AdvContextID; } 116 | AdvLifeTime { return T_AdvLifeTime; } 117 | AdvContextPrefix { return T_AdvContextPrefix; } 118 | 119 | AdvVersionLow { return T_AdvVersionLow; } 120 | AdvVersionHigh { return T_AdvVersionHigh; } 121 | Adv6LBRaddress { return T_Adv6LBRaddress; } 122 | 123 | {addr} { 124 | static struct in6_addr addr; 125 | if (inet_pton(AF_INET6, yytext, &addr) < 1) { 126 | return T_BAD_TOKEN; 127 | } 128 | 129 | yylval.addr = &addr; 130 | return IPV6ADDR; 131 | } 132 | 133 | {naddr} { 134 | static struct in6_addr addr; 135 | if (inet_pton(AF_INET6, &yytext[1], &addr) < 1) { 136 | return T_BAD_TOKEN; 137 | } 138 | 139 | yylval.addr = &addr; 140 | return NOT_IPV6ADDR; 141 | } 142 | 143 | {number} { 144 | unsigned long lnum; 145 | char *endp; 146 | lnum = strtoul(yytext, &endp, 10); 147 | if (*yytext == '\0' || *endp != '\0') 148 | return T_BAD_TOKEN; 149 | if (lnum > 0xFFFFFFFFUL) 150 | return T_BAD_TOKEN; /* XXX */ 151 | yylval.num = lnum; 152 | return NUMBER; 153 | } 154 | 155 | {snum} { yylval.snum = atoi(yytext); return SIGNEDNUMBER; } 156 | 157 | {decimal} { yylval.dec = atof(yytext); return DECIMAL; } 158 | 159 | infinity { return INFINITY; } 160 | 161 | on { yylval.num = 1; return SWITCH; } 162 | 163 | off { yylval.num = 0; return SWITCH; } 164 | 165 | low { yylval.snum = -1; return SIGNEDNUMBER; } 166 | 167 | medium { yylval.snum = 0; return SIGNEDNUMBER; } 168 | 169 | high { yylval.snum = 1; return SIGNEDNUMBER; } 170 | 171 | {string} { 172 | static char string[256]; 173 | 174 | strncpy(string, yytext, sizeof(string)); 175 | string[sizeof(string)-1] = '\0'; 176 | yylval.str = string; 177 | return STRING; 178 | } 179 | 180 | "{"|"}"|";"|"/" { return *yytext; } 181 | 182 | . { return T_BAD_TOKEN; } 183 | %% 184 | -------------------------------------------------------------------------------- /radvd.8.man: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" 3 | .\" Authors: 4 | .\" Lars Fenneberg 5 | .\" 6 | .\" This software is Copyright 1996-2000 by the above mentioned author(s), 7 | .\" All Rights Reserved. 8 | .\" 9 | .\" The license which is distributed with this software in the file COPYRIGHT 10 | .\" applies to this software. If your distribution is missing this file, you 11 | .\" may request it from . 12 | .\" 13 | .\" 14 | .\" 15 | .TH RADVD 8 "30 Dec 2024" "radvd @VERSION@" "" 16 | .SH NAME 17 | radvd \- router advertisement daemon for IPv6 18 | .SH SYNOPSIS 19 | .B radvd 20 | .B "[ \-hsvc ]" 21 | .BI "[ \-d " debuglevel " ]" 22 | .BI "[ \-C " configfile " ]" 23 | .BI "[ \-p " pidfile " ]" 24 | .BI "[ \-m " logmethod " ]" 25 | .BI "[ \-l " logfile " ]" 26 | .BI "[ \-n " nodaemon " ]" 27 | .BI "[ \-f " facility " ]" 28 | .BI "[ \-t " chrootdir " ]" 29 | .BI "[ \-u " username " ]" 30 | 31 | .SH DESCRIPTION 32 | .B radvd 33 | is the router advertisement daemon for IPv6. It listens to router 34 | solicitations and sends router advertisements as described in 35 | "Neighbor Discovery for IP Version 6 (IPv6)" (RFC 4861). 36 | With these advertisements hosts can automatically configure their 37 | addresses and some other parameters. It also defines "Neighbor Discovery 38 | Optimization for IPv6 over Low-Power Wireless Personal Area Networks (6LoWPANs)" (RFC6775). 39 | They also can choose a default router based on these advertisements. 40 | 41 | The configuration file must not be writable by others, and if 42 | non-root operation is requested, not even by self/own group. 43 | 44 | .SH OPTIONS 45 | 46 | For every one character option there is also a long option, which 47 | is listed right next to the "short" option name: 48 | 49 | .TP 50 | .BR "\-v" , " \-\-version" 51 | Displays the version of 52 | .I radvd 53 | and then aborts. 54 | .TP 55 | .BR "\-h" , " \-\-help" 56 | Displays a short usage description and then aborts. 57 | .TP 58 | .BR "\-c" , " \-\-configtest" 59 | Test configuration and do startup tests and then exit. 60 | .TP 61 | .BR "\-n" , " \-\-nodaemon" 62 | Prevent the daemonizing. 63 | .TP 64 | .BR "\-d " debuglevel, " \-\-debug " debuglevel 65 | With this option you turn on debugging information. The debugging level is 66 | an integer in the range from 1 to 5, from quiet to very verbose. A 67 | debugging level of 0 completely turns off debugging. If a debugging level 68 | greater than 0 is used, 69 | .I radvd 70 | doesn't background itself on start. The default debugging level is 0. 71 | .TP 72 | .BR "\-C " configfile, " \-\-config " configfile 73 | Specifies an alternate config file. Normally the compiled in default 74 | .I @PATH_RADVD_CONF@ 75 | is used. 76 | .TP 77 | .BR "\-p " pidfile, " \-\-pidfile " pidfile 78 | Specifies an alternate pidfile. Normally the compiled in default 79 | .I @PATH_RADVD_PID@ 80 | is used. 81 | .TP 82 | .BR "\-m " method, " \-\-logmethod " method 83 | Specifies the logging method to use. Possibly values are: 84 | .RS 85 | .TP 86 | .B none 87 | Completely disables any logging. 88 | .TP 89 | .B logfile 90 | Logs to the logfile which is specified by the 91 | .I \-l 92 | option. If no logfile is specified on the command line, then 93 | a compiled in default is used (see next option). 94 | .TP 95 | .B stderr 96 | Logs to standard error. 97 | .TP 98 | .B stderr_syslog 99 | Logs only the high messages (of at least LOG_ERR priority) to standard 100 | error, and everything to syslog (default method). 101 | .TP 102 | .B syslog 103 | Logs to syslog. 104 | .RE 105 | .PP 106 | .TP 107 | .BR "\-l " logfile, " \-\-logfile " logfile 108 | Specifies the logfile to use when using the logging method 109 | .IR logfile . 110 | The default logfile is 111 | .IR @PATH_RADVD_LOG@ . 112 | .TP 113 | .BR "\-f " facility, " \-\-facility " facility 114 | Specifies the facility (as an integer) when using syslog logging. Default 115 | is @LOG_FACILITY@. 116 | .TP 117 | .BR "\-t " chrootdir, " \-\-chrootdir " chrootdir 118 | If specified, switches to 119 | .I chrootdir 120 | before doing anything else. This directory and its 121 | subdirectories must have been populated first. 122 | For security reasons, 123 | .I \-u 124 | must always be used when using 125 | .IR chrootdir . 126 | 127 | Note that on Linux 128 | .I radvd 129 | requires access to the 130 | .I /proc 131 | filesystem, 132 | so it is more challenging to set up the chroot environment. 133 | .TP 134 | .BR "\-u " username, " \-\-username " username 135 | If specified, drops root privileges and changes user ID to 136 | .I username 137 | and group ID to the primary group of 138 | .IR username . 139 | This is recommended for security reasons. 140 | You might also need to use 141 | .I \-p 142 | to point to a file in a 143 | .I username 144 | -writable directory (e.g. /var/run/radvd/radvd.pid). 145 | .SH FILES 146 | 147 | .nf 148 | @sbindir@/radvd 149 | @PATH_RADVD_CONF@ 150 | @PATH_RADVD_PID@ 151 | @PATH_RADVD_LOG@ 152 | .fi 153 | .SH BUGS 154 | 155 | There certainly are some bugs. If you find them or have other 156 | suggestions please contact Reuben Hawkins . 157 | 158 | .SH "SEE ALSO" 159 | 160 | .BR radvd.conf (5), 161 | .BR radvdump (8) 162 | .SH AUTHORS 163 | 164 | .nf 165 | Pedro Roque - wrote first version for Linux 166 | Lars Fenneberg - previous maintainer 167 | Nathan Lutchansky - previous maintainer 168 | Pekka Savola - previous maintainer 169 | Craig Metz - port to NRL's IPv6 code for BSD4.4 170 | Marko Myllynen - RFC 2461 update, Mobile IPv6 support 171 | Jim Paris - Privilege separation support 172 | Reuben Hawkins - previous maintainer 173 | Pierre Ossman - RFC6106 (DNSSL) support 174 | Varka Bhadram - 6LoWPAN-ND (RFC6775) support 175 | Robin H. Johnson - current maintainer, RA splitting per RFC 6980 & RFC4861#6.2.3 176 | Radek Zajic - NAT64 pref64 support (RFC8781) 177 | Geert Stappers - contributor 178 | 179 | .fi 180 | -------------------------------------------------------------------------------- /device-bsd44.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Craig Metz 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "defaults.h" 17 | #include "includes.h" 18 | #include "radvd.h" 19 | 20 | /* 21 | * this function gets the hardware type and address of an interface, 22 | * determines the link layer token length and checks it against 23 | * the defined prefixes 24 | */ 25 | int update_device_info(int sock, struct Interface *iface) 26 | { 27 | struct ifaddrs *addresses = 0; 28 | 29 | struct ifreq ifr; 30 | 31 | memset(&ifr, 0, sizeof(ifr)); 32 | strlcpy(ifr.ifr_name, iface->props.name, sizeof(ifr.ifr_name)); 33 | 34 | if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) { 35 | flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed for %s: %s", iface->props.name, strerror(errno)); 36 | goto ret; 37 | } 38 | 39 | dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->props.name, ifr.ifr_mtu); 40 | iface->sllao.if_maxmtu = ifr.ifr_mtu; 41 | 42 | /* RFC 2460: 5. Packet Size Issues */ 43 | iface->props.max_ra_option_size = iface->AdvRAMTU; 44 | iface->props.max_ra_option_size = MIN(iface->props.max_ra_option_size, MAX(iface->sllao.if_maxmtu, RFC2460_MIN_MTU)); 45 | 46 | if (getifaddrs(&addresses) != 0) { 47 | flog(LOG_ERR, "getifaddrs failed: %s(%d)", strerror(errno), errno); 48 | goto ret; 49 | } 50 | 51 | for (struct ifaddrs *ifa = addresses; ifa != NULL; ifa = ifa->ifa_next) { 52 | if (strcmp(ifa->ifa_name, iface->props.name) != 0) 53 | continue; 54 | 55 | if (ifa->ifa_addr == NULL) 56 | continue; 57 | 58 | if (ifa->ifa_addr->sa_family != AF_LINK) 59 | continue; 60 | 61 | struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr; 62 | 63 | if (dl->sdl_alen > sizeof(iface->props.if_addr)) { 64 | flog(LOG_ERR, "address length %d too big for %s", dl->sdl_alen, iface->props.name); 65 | goto ret; 66 | } 67 | 68 | memcpy(iface->sllao.if_hwaddr, LLADDR(dl), dl->sdl_alen); 69 | iface->sllao.if_hwaddr_len = dl->sdl_alen << 3; 70 | 71 | switch (dl->sdl_type) { 72 | case IFT_ETHER: 73 | case IFT_ISO88023: 74 | iface->sllao.if_prefix_len = 64; 75 | iface->props.max_ra_option_size -= 14; /* RFC 2464 */ 76 | break; 77 | case IFT_FDDI: 78 | iface->sllao.if_prefix_len = 64; 79 | iface->props.max_ra_option_size -= 22; /* RFC 2109 */ 80 | break; 81 | default: 82 | iface->sllao.if_prefix_len = -1; 83 | iface->sllao.if_maxmtu = -1; 84 | /* Assume fragmentation handled at a lower layer. */ 85 | iface->props.max_ra_option_size -= 0; 86 | break; 87 | } 88 | 89 | dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->props.name, iface->sllao.if_hwaddr_len); 90 | 91 | dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->props.name, iface->sllao.if_prefix_len); 92 | 93 | if (iface->sllao.if_prefix_len != -1) { 94 | char zero[sizeof(iface->props.if_addr)]; 95 | memset(zero, 0, dl->sdl_alen); 96 | if (!memcmp(iface->sllao.if_hwaddr, zero, dl->sdl_alen)) 97 | flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!", iface->props.name); 98 | } 99 | 100 | struct AdvPrefix *prefix = iface->AdvPrefixList; 101 | while (prefix) { 102 | if ((iface->sllao.if_prefix_len != -1) && (iface->sllao.if_prefix_len != prefix->PrefixLen)) { 103 | flog(LOG_WARNING, "prefix length should be %d for %s", iface->sllao.if_prefix_len, 104 | iface->props.name); 105 | } 106 | 107 | prefix = prefix->next; 108 | } 109 | 110 | freeifaddrs(addresses); 111 | // Regardless of link-layer, every RA message will have an IPV6 header & RA header 112 | iface->props.max_ra_option_size -= sizeof(struct ip6_hdr); 113 | iface->props.max_ra_option_size -= sizeof(struct nd_router_advert); 114 | return 0; 115 | } 116 | 117 | ret: 118 | iface->sllao.if_maxmtu = -1; 119 | iface->sllao.if_hwaddr_len = -1; 120 | iface->sllao.if_prefix_len = -1; 121 | 122 | if (addresses != 0) 123 | freeifaddrs(addresses); 124 | 125 | return -1; 126 | } 127 | 128 | int setup_allrouters_membership(int sock, struct Interface *iface) 129 | { 130 | struct ipv6_mreq mreq; 131 | 132 | memset(&mreq, 0, sizeof(mreq)); 133 | mreq.ipv6mr_interface = iface->props.if_index; 134 | 135 | /* all-routers multicast address */ 136 | if (inet_pton(AF_INET6, "ff02::2", 137 | &mreq.ipv6mr_multiaddr.s6_addr) != 1) { 138 | flog(LOG_ERR, "inet_pton failed"); 139 | return -1; 140 | } 141 | 142 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 143 | &mreq, sizeof(mreq)) < 0 && errno != EADDRINUSE) { 144 | flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->props.name); 145 | return -1; 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | int cleanup_allrouters_membership(int sock, struct Interface *iface) 152 | { 153 | /* no cleanup necessary */ 154 | return 0; 155 | } 156 | 157 | int set_interface_linkmtu(const char *iface, uint32_t mtu) 158 | { 159 | dlog(LOG_DEBUG, 4, "setting LinkMTU (%u) for %s is not supported", mtu, iface); 160 | return -1; 161 | } 162 | 163 | int set_interface_curhlim(const char *iface, uint8_t hlim) 164 | { 165 | dlog(LOG_DEBUG, 4, "setting CurHopLimit (%u) for %s is not supported", hlim, iface); 166 | return -1; 167 | } 168 | 169 | int set_interface_reachtime(const char *iface, uint32_t rtime) 170 | { 171 | dlog(LOG_DEBUG, 4, "setting BaseReachableTime (%u) for %s is not supported", rtime, iface); 172 | return -1; 173 | } 174 | 175 | int set_interface_retranstimer(const char *iface, uint32_t rettimer) 176 | { 177 | dlog(LOG_DEBUG, 4, "setting RetransTimer (%u) for %s is not supported", rettimer, iface); 178 | return -1; 179 | } 180 | 181 | int check_ip6_forwarding(void) 182 | { 183 | dlog(LOG_DEBUG, 4, "checking ipv6 forwarding not supported"); 184 | return 0; 185 | } 186 | 187 | int check_ip6_iface_forwarding(const char *iface) 188 | { 189 | dlog(LOG_DEBUG, 4, "checking ipv6 forwarding of interface not supported"); 190 | return 1; 191 | } 192 | -------------------------------------------------------------------------------- /privsep-linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Jim Paris 5 | * Pedro Roque 6 | * Lars Fenneberg 7 | * 8 | * This software is Copyright 1996,1997,2008 by the above mentioned author(s), 9 | * All Rights Reserved. 10 | * 11 | * The license which is distributed with this software in the file COPYRIGHT 12 | * applies to this software. If your distribution is missing this file, you 13 | * may request it from . 14 | * 15 | */ 16 | 17 | #include "config.h" 18 | #include "includes.h" 19 | #include "pathnames.h" 20 | #include "radvd.h" 21 | 22 | static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val); 23 | static void privsep_read_loop(void); 24 | 25 | /* For reading or writing, depending on process */ 26 | static int pfd = -1; 27 | 28 | void privsep_set_write_fd(int fd) { pfd = fd; } 29 | 30 | /* Command types */ 31 | enum privsep_type { 32 | SET_INTERFACE_LINKMTU, 33 | SET_INTERFACE_CURHLIM, 34 | SET_INTERFACE_REACHTIME, 35 | SET_INTERFACE_RETRANSTIMER, 36 | }; 37 | 38 | /* Command sent over pipe is a fixed size binary structure. */ 39 | struct privsep_command { 40 | int type; 41 | char iface[IFNAMSIZ]; 42 | uint32_t val; 43 | }; 44 | 45 | /* Privileged read loop */ 46 | static void privsep_read_loop(void) 47 | { 48 | while (1) { 49 | struct privsep_command cmd; 50 | int ret = readn(pfd, &cmd, sizeof(cmd)); 51 | if (ret <= 0) { 52 | /* Error or EOF, give up */ 53 | if (ret < 0) { 54 | flog(LOG_ERR, "Exiting, privsep_read_loop had readn error: %s", strerror(errno)); 55 | } else { 56 | flog(LOG_ERR, "Exiting, privsep_read_loop had readn return 0 bytes"); 57 | } 58 | } 59 | if (ret != sizeof(cmd)) { 60 | /* Short read, ignore */ 61 | return; 62 | } 63 | 64 | cmd.iface[IFNAMSIZ - 1] = '\0'; 65 | 66 | switch (cmd.type) { 67 | 68 | case SET_INTERFACE_LINKMTU: 69 | if (cmd.val < MIN_AdvLinkMTU || cmd.val > MAX_AdvLinkMTU) { 70 | flog(LOG_ERR, "(privsep) %s: LinkMTU (%u) is not within the defined bounds, ignoring", cmd.iface, 71 | cmd.val); 72 | break; 73 | } 74 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", cmd.val); 75 | break; 76 | 77 | case SET_INTERFACE_CURHLIM: 78 | if (cmd.val < MIN_AdvCurHopLimit || cmd.val > MAX_AdvCurHopLimit) { 79 | flog(LOG_ERR, "(privsep) %s: CurHopLimit (%u) is not within the defined bounds, ignoring", 80 | cmd.iface, cmd.val); 81 | break; 82 | } 83 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", cmd.val); 84 | break; 85 | 86 | case SET_INTERFACE_REACHTIME: 87 | if (cmd.val < MIN_AdvReachableTime || cmd.val > MAX_AdvReachableTime) { 88 | flog(LOG_ERR, "(privsep) %s: BaseReachableTimer (%u) is not within the defined bounds, ignoring", 89 | cmd.iface, cmd.val); 90 | break; 91 | } 92 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME_MS, "BaseReachableTimer (ms)", cmd.val); 93 | if (ret == 0) 94 | break; 95 | set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", cmd.val / 1000); 96 | break; 97 | 98 | case SET_INTERFACE_RETRANSTIMER: 99 | if (cmd.val < MIN_AdvRetransTimer || cmd.val > MAX_AdvRetransTimer) { 100 | flog(LOG_ERR, "(privsep) %s: RetransTimer (%u) is not within the defined bounds, ignoring", 101 | cmd.iface, cmd.val); 102 | break; 103 | } 104 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER_MS, "RetransTimer (ms)", cmd.val); 105 | if (ret == 0) 106 | break; 107 | set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer", 108 | cmd.val / 1000 * USER_HZ); /* XXX user_hz */ 109 | break; 110 | 111 | default: 112 | /* Bad command */ 113 | break; 114 | } 115 | } 116 | } 117 | 118 | void privsep_init(int fd) 119 | { 120 | /* This will be the privileged child */ 121 | pfd = fd; 122 | privsep_read_loop(); 123 | close(pfd); 124 | flog(LOG_ERR, "Exiting, privsep_read_loop is complete."); 125 | } 126 | 127 | /* Interface calls for the unprivileged process */ 128 | int privsep_interface_linkmtu(const char *iface, uint32_t mtu) 129 | { 130 | struct privsep_command cmd; 131 | cmd.type = SET_INTERFACE_LINKMTU; 132 | memset(&cmd.iface, 0, sizeof(cmd.iface)); 133 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); 134 | cmd.val = mtu; 135 | 136 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 137 | return -1; 138 | return 0; 139 | } 140 | 141 | int privsep_interface_curhlim(const char *iface, uint32_t hlim) 142 | { 143 | struct privsep_command cmd; 144 | cmd.type = SET_INTERFACE_CURHLIM; 145 | memset(&cmd.iface, 0, sizeof(cmd.iface)); 146 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); 147 | cmd.val = hlim; 148 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 149 | return -1; 150 | return 0; 151 | } 152 | 153 | int privsep_interface_reachtime(const char *iface, uint32_t rtime) 154 | { 155 | struct privsep_command cmd; 156 | cmd.type = SET_INTERFACE_REACHTIME; 157 | memset(&cmd.iface, 0, sizeof(cmd.iface)); 158 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); 159 | cmd.val = rtime; 160 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 161 | return -1; 162 | return 0; 163 | } 164 | 165 | int privsep_interface_retranstimer(const char *iface, uint32_t rettimer) 166 | { 167 | struct privsep_command cmd; 168 | cmd.type = SET_INTERFACE_RETRANSTIMER; 169 | memset(&cmd.iface, 0, sizeof(cmd.iface)); 170 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); 171 | cmd.val = rettimer; 172 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 173 | return -1; 174 | return 0; 175 | } 176 | 177 | /* note: also called from the root context */ 178 | static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val) 179 | { 180 | int retval = -1; 181 | FILE *fp = 0; 182 | char *spath = strdupf(var, iface); 183 | int fd = -1 ; 184 | 185 | /* No path traversal */ 186 | // TODO: if interface names contain '/' in future, this may break. 187 | if (!iface[0] || !strcmp(iface, ".") || !strcmp(iface, "..") || strchr(iface, '/')) 188 | goto errmsg; 189 | 190 | // Open the file for writing, ONLY if it already exists. 191 | // explicitly ensure that O_CREAT is *NOT* set. 192 | fd = open(spath, O_WRONLY & (~O_CREAT), 0); 193 | if (fd == -1) { 194 | // If the file does NOT exist, this is non-fatal; as we fallback to a 195 | // different filename at a higher level. 196 | // ensure we cleanup and return -1 to the higher level 197 | if (errno == ENOENT) 198 | goto cleanup; 199 | // If we got some other error, e.g. ENOACCESS 200 | goto errmsg; 201 | } 202 | 203 | // We know the file exists now, write to it. 204 | fp = fdopen(fd, "w"); 205 | if (!fp) 206 | goto errmsg; 207 | 208 | if (fprintf(fp, "%u", val) > 0) { 209 | retval = 0; 210 | } 211 | 212 | errmsg: 213 | if (name && retval != 0) 214 | flog(LOG_ERR, "failed to set %s (%u) for %s: %s", name, val, iface, strerror(errno)); 215 | cleanup: 216 | 217 | if (fp) 218 | fclose(fp); 219 | if (fd != -1) 220 | close(fd); 221 | 222 | free(spath); 223 | 224 | return retval; 225 | } 226 | -------------------------------------------------------------------------------- /device-common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "defaults.h" 17 | #include "includes.h" 18 | #include "pathnames.h" 19 | #include "radvd.h" 20 | 21 | int check_device(int sock, struct Interface *iface) 22 | { 23 | struct ifreq ifr; 24 | memset(&ifr, 0, sizeof(ifr)); 25 | strlcpy(ifr.ifr_name, iface->props.name, sizeof(ifr.ifr_name)); 26 | 27 | if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { 28 | flog(LOG_ERR, "ioctl(SIOCGIFFLAGS) failed on %s: %s", iface->props.name, strerror(errno)); 29 | return -1; 30 | } else { 31 | dlog(LOG_ERR, 5, "ioctl(SIOCGIFFLAGS) succeeded on %s", iface->props.name); 32 | } 33 | 34 | if (!(ifr.ifr_flags & IFF_UP)) { 35 | dlog(LOG_ERR, 4, "%s is not up", iface->props.name); 36 | return -1; 37 | } else { 38 | dlog(LOG_ERR, 4, "%s is up", iface->props.name); 39 | } 40 | 41 | if (!(ifr.ifr_flags & IFF_RUNNING)) { 42 | dlog(LOG_ERR, 4, "%s is not running", iface->props.name); 43 | return -1; 44 | } else { 45 | dlog(LOG_ERR, 4, "%s is running", iface->props.name); 46 | } 47 | 48 | if (!iface->UnicastOnly && 49 | !(ifr.ifr_flags & (IFF_MULTICAST | IFF_POINTOPOINT))) { 50 | flog(LOG_INFO, 51 | "%s does not support multicast or point-to-point, forcing UnicastOnly", 52 | iface->props.name); 53 | iface->UnicastOnly = 1; 54 | } else { 55 | dlog(LOG_ERR, 4, "%s supports multicast or is point-to-point", 56 | iface->props.name); 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | int get_v4addr(const char *ifn, unsigned int *dst) 63 | { 64 | 65 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 66 | if (fd < 0) { 67 | flog(LOG_ERR, "create socket for IPv4 ioctl failed on %s: %s", ifn, strerror(errno)); 68 | return -1; 69 | } 70 | 71 | struct ifreq ifr; 72 | memset(&ifr, 0, sizeof(ifr)); 73 | strlcpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name)); 74 | ifr.ifr_addr.sa_family = AF_INET; 75 | 76 | if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { 77 | flog(LOG_ERR, "ioctl(SIOCGIFADDR) failed on %s: %s", ifn, strerror(errno)); 78 | close(fd); 79 | return -1; 80 | } 81 | 82 | struct sockaddr_in *addr = (struct sockaddr_in *)(&ifr.ifr_addr); 83 | 84 | dlog(LOG_DEBUG, 3, "%s IPv4 address is: %s", ifn, inet_ntoa(addr->sin_addr)); 85 | 86 | *dst = addr->sin_addr.s_addr; 87 | 88 | close(fd); 89 | 90 | return 0; 91 | } 92 | 93 | static int cmp_iface_addrs(void const *a, void const *b) { return memcmp(a, b, sizeof(struct in6_addr)); } 94 | 95 | /* 96 | * Return first IPv6 link local addr in if_addr. 97 | * Return all the IPv6 addresses in if_addrs in ascending 98 | * order. 99 | * Return value is -1 if there was no link local addr. 100 | * otherwise return value is count of addres in if_addrs 101 | * not including the all zero (unspecified) addr at the 102 | * end of the list. 103 | */ 104 | int get_iface_addrs(char const *name, struct in6_addr *if_addr, struct in6_addr **if_addrs) 105 | { 106 | struct ifaddrs *addresses = 0; 107 | int link_local_set = 0; 108 | int i = 0; 109 | 110 | if (getifaddrs(&addresses) != 0) { 111 | flog(LOG_ERR, "getifaddrs failed on %s: %s", name, strerror(errno)); 112 | } else { 113 | for (struct ifaddrs *ifa = addresses; ifa != NULL; ifa = ifa->ifa_next) { 114 | 115 | if (!ifa->ifa_addr) 116 | continue; 117 | 118 | if (ifa->ifa_addr->sa_family != AF_INET6) 119 | continue; 120 | 121 | struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)ifa->ifa_addr; 122 | 123 | /* Skip if it is not the interface we're looking for. */ 124 | if (strcmp(ifa->ifa_name, name) != 0) 125 | continue; 126 | 127 | *if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr)); 128 | (*if_addrs)[i++] = a6->sin6_addr; 129 | 130 | /* Skip if it is not a linklocal address or link locak address already found*/ 131 | uint8_t const ll_prefix[] = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; 132 | if (link_local_set || 0 != memcmp(&(a6->sin6_addr), ll_prefix, sizeof(ll_prefix))) 133 | continue; 134 | 135 | if (if_addr) 136 | memcpy(if_addr, &(a6->sin6_addr), sizeof(struct in6_addr)); 137 | 138 | link_local_set = 1; 139 | } 140 | } 141 | 142 | if (addresses) 143 | freeifaddrs(addresses); 144 | 145 | /* last item in the list is all zero (unspecified) address */ 146 | *if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr)); 147 | memset(&(*if_addrs)[i], 0, sizeof(struct in6_addr)); 148 | 149 | /* Sort the addresses so the output is predictable. */ 150 | qsort(*if_addrs, i, sizeof(struct in6_addr), cmp_iface_addrs); 151 | 152 | if (!link_local_set) 153 | return -1; 154 | 155 | return i; 156 | } 157 | 158 | /* 159 | * Saves the first link local address seen on the specified interface to iface->if_addr 160 | * and builds a list of all the other addrs. 161 | */ 162 | int setup_iface_addrs(struct Interface *iface) 163 | { 164 | int rc = get_iface_addrs(iface->props.name, &iface->props.if_addr, &iface->props.if_addrs); 165 | 166 | if (-1 != rc) { 167 | iface->props.addrs_count = rc; 168 | char addr_str[INET6_ADDRSTRLEN]; 169 | addrtostr(&iface->props.if_addr, addr_str, sizeof(addr_str)); 170 | dlog(LOG_DEBUG, 4, "%s linklocal address: %s", iface->props.name, addr_str); 171 | for (int i = 0; i < rc; ++i) { 172 | addrtostr(&iface->props.if_addrs[i], addr_str, sizeof(addr_str)); 173 | dlog(LOG_DEBUG, 4, "%s address: %s", iface->props.name, addr_str); 174 | } 175 | /* AdvRASrcAddress: allow operator selection of RA source address */ 176 | if (iface->AdvRASrcAddressList != NULL) { 177 | iface->props.if_addr_rasrc = NULL; 178 | for (struct AdvRASrcAddress *current = iface->AdvRASrcAddressList; current; current = current->next) { 179 | for (int i = 0; i < iface->props.addrs_count; i++) { 180 | struct in6_addr cmp_addr = iface->props.if_addrs[i]; 181 | if (0 == memcmp(&cmp_addr, ¤t->address, sizeof(struct in6_addr))) { 182 | addrtostr(&(cmp_addr), addr_str, sizeof(addr_str)); 183 | dlog(LOG_DEBUG, 4, "AdvRASrcAddress selecting: %s", addr_str); 184 | iface->props.if_addr_rasrc = &iface->props.if_addrs[i]; 185 | break; 186 | } 187 | } 188 | if (NULL != iface->props.if_addr_rasrc) 189 | break; 190 | } 191 | } else { 192 | /* AdvRASrcAddress default: Just take the first link-local */ 193 | iface->props.if_addr_rasrc = &iface->props.if_addr; 194 | } 195 | } else { 196 | if (iface->IgnoreIfMissing) 197 | dlog(LOG_DEBUG, 4, "no linklocal address configured on %s", iface->props.name); 198 | else 199 | flog(LOG_ERR, "no linklocal address configured on %s", iface->props.name); 200 | } 201 | 202 | return rc; 203 | } 204 | 205 | int update_device_index(struct Interface *iface) 206 | { 207 | int index = if_nametoindex(iface->props.name); 208 | 209 | if (0 == index) { 210 | /* Yes, if_nametoindex returns zero on failure. 2014/01/16 */ 211 | flog(LOG_ERR, "%s not found: %s", iface->props.name, strerror(errno)); 212 | return -1; 213 | } 214 | 215 | if (iface->props.if_index != index) { 216 | dlog(LOG_DEBUG, 4, "%s if_index changed from %d to %d", iface->props.name, iface->props.if_index, index); 217 | iface->props.if_index = index; 218 | } 219 | 220 | return 0; 221 | } 222 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "includes.h" 17 | #include "radvd.h" 18 | 19 | #ifdef UNIT_TEST 20 | #include "test/util.c" 21 | #endif 22 | 23 | struct safe_buffer *new_safe_buffer(void) 24 | { 25 | struct safe_buffer *sb = malloc(sizeof(struct safe_buffer)); 26 | *sb = SAFE_BUFFER_INIT; 27 | sb->should_free = 1; 28 | return sb; 29 | } 30 | 31 | void safe_buffer_free(struct safe_buffer *sb) 32 | { 33 | if (sb && sb->buffer) { 34 | free(sb->buffer); 35 | sb->buffer = NULL; 36 | } 37 | 38 | if (sb && sb->should_free) { 39 | free(sb); 40 | sb = NULL; 41 | } 42 | } 43 | 44 | /** 45 | * Requests that the safe_buffer capacity be least n bytes in size. 46 | * 47 | * If n is greater than the current capacity, the function causes the container 48 | * to reallocate its storage increasing its capacity to n (or greater). 49 | * 50 | * In all other cases, the function call does not cause a reallocation and the 51 | * capacity is not affected. 52 | * 53 | * @param sb safe_buffer to enlarge 54 | * @param b Minimum capacity for the safe_buffer. 55 | */ 56 | void safe_buffer_resize(struct safe_buffer *sb, size_t n) 57 | { 58 | const int blocksize = 1 << 6; // MUST BE POWER OF 2. 59 | if (sb->allocated < n) { 60 | if (n % blocksize > 0) { 61 | n |= (blocksize - 1); // Set all the low bits 62 | n++; 63 | } 64 | if (n > 64 * 1024) { 65 | flog(LOG_ERR, "Requested buffer too large for any possible IPv6 ND, even with jumbogram. Exiting."); 66 | exit(1); 67 | } 68 | sb->allocated = n; 69 | sb->buffer = realloc(sb->buffer, sb->allocated); 70 | } 71 | } 72 | 73 | size_t safe_buffer_pad(struct safe_buffer *sb, size_t count) 74 | { 75 | safe_buffer_resize(sb, sb->used + count); 76 | memset(&sb->buffer[sb->used], (uint8_t)0, count); 77 | sb->used += count; 78 | return count; 79 | } 80 | 81 | size_t safe_buffer_append(struct safe_buffer *sb, void const *v, size_t count) 82 | { 83 | if (sb) { 84 | unsigned const char *m = (unsigned const char *)v; 85 | safe_buffer_resize(sb, sb->used + count); 86 | memcpy(&sb->buffer[sb->used], m, count); 87 | sb->used += count; 88 | } 89 | 90 | return count; 91 | } 92 | 93 | /** 94 | * Create a new safe_buffer_list 95 | * 96 | * @return new safe_buffer_list, with a safe_buffer on the heap. 97 | */ 98 | struct safe_buffer_list *new_safe_buffer_list(void) 99 | { 100 | struct safe_buffer_list *sbl = malloc(sizeof(struct safe_buffer_list)); 101 | sbl->sb = new_safe_buffer(); 102 | sbl->next = NULL; 103 | return sbl; 104 | } 105 | 106 | /** 107 | * Ensure list tail has an empty buffer ready to accept data. 108 | * 109 | * If the present element is empty of data, just return it. 110 | * Otherwise return a new safe_buffer_list ready to accept data. 111 | * 112 | * @param sbl safe_buffer_list. 113 | * @return new tail of list. 114 | */ 115 | struct safe_buffer_list *safe_buffer_list_append(struct safe_buffer_list *sbl) 116 | { 117 | // Only allocate a new entry if this one has bytes in it. 118 | if (sbl->sb && sbl->sb->used > 0) { 119 | struct safe_buffer_list *next = new_safe_buffer_list(); 120 | sbl->next = next; 121 | sbl = next; 122 | } 123 | return sbl; 124 | } 125 | 126 | /** 127 | * Convert an entire safe_buffer_list to a single safe_buffer. 128 | * 129 | * @param sbl safe_buffer_list source. 130 | * @param sb safe_buffer destination. 131 | */ 132 | void safe_buffer_list_to_safe_buffer(struct safe_buffer_list *sbl, struct safe_buffer *sb) 133 | { 134 | struct safe_buffer_list *cur; 135 | for (cur = sbl; cur; cur = cur->next) { 136 | if (cur->sb) 137 | safe_buffer_append(sb, cur->sb->buffer, cur->sb->used); 138 | } 139 | } 140 | 141 | /** 142 | * Free all memory used by a safe_buffer_list 143 | * 144 | * @param sbl safe_buffer_list to free. 145 | */ 146 | void safe_buffer_list_free(struct safe_buffer_list *sbl) 147 | { 148 | struct safe_buffer_list *next; 149 | for (struct safe_buffer_list *current = sbl; current; current = next) { 150 | if (current->sb) { 151 | safe_buffer_free(current->sb); 152 | current->sb = NULL; 153 | } 154 | next = current->next; 155 | free(current); 156 | } 157 | } 158 | 159 | __attribute__((format(printf, 1, 2))) char *strdupf(char const *format, ...) 160 | { 161 | va_list va; 162 | va_start(va, format); 163 | char *strp = 0; 164 | int rc = vasprintf(&strp, format, va); 165 | if (rc == -1 || !strp) { 166 | flog(LOG_ERR, "vasprintf failed: %s", strerror(errno)); 167 | exit(-1); 168 | } 169 | va_end(va); 170 | 171 | return strp; 172 | } 173 | 174 | double rand_between(double lower, double upper) { return ((upper - lower) / (RAND_MAX + 1.0) * rand() + lower); } 175 | 176 | /* This assumes that str is not null and str_size > 0 */ 177 | void addrtostr(struct in6_addr const *addr, char *str, size_t str_size) 178 | { 179 | const char *res; 180 | 181 | res = inet_ntop(AF_INET6, (void const *)addr, str, str_size); 182 | 183 | if (res == NULL) { 184 | flog(LOG_ERR, "addrtostr: inet_ntop: %s", strerror(errno)); 185 | strncpy(str, "[invalid address]", str_size); 186 | str[str_size - 1] = '\0'; 187 | } 188 | } 189 | 190 | /* Check if an in6_addr exists in the rdnss list */ 191 | int check_rdnss_presence(struct AdvRDNSS *rdnss, struct in6_addr *addr) 192 | { 193 | while (rdnss) { 194 | for (int i = 0; i < rdnss->AdvRDNSSNumber; i++) { 195 | if (!memcmp(&rdnss->AdvRDNSSAddr[i], addr, sizeof(struct in6_addr))) 196 | return 1; /* rdnss address found in the list */ 197 | } 198 | rdnss = rdnss->next; 199 | } 200 | return 0; 201 | } 202 | 203 | /* Check if a suffix exists in the dnssl list */ 204 | int check_dnssl_presence(struct AdvDNSSL *dnssl, const char *suffix) 205 | { 206 | while (dnssl) { 207 | for (int i = 0; i < dnssl->AdvDNSSLNumber; ++i) { 208 | if (0 == strcmp(dnssl->AdvDNSSLSuffixes[i], suffix)) 209 | return 1; /* suffix found in the list */ 210 | } 211 | dnssl = dnssl->next; 212 | } 213 | return 0; 214 | } 215 | 216 | /* Like read(), but retries in case of partial read */ 217 | ssize_t readn(int fd, void *buf, size_t count) 218 | { 219 | size_t n = 0; 220 | while (count > 0) { 221 | int r = read(fd, buf, count); 222 | if (r < 0) { 223 | if (errno == EINTR) 224 | continue; 225 | return r; 226 | } 227 | if (r == 0) 228 | return n; 229 | buf = (char *)buf + r; 230 | count -= r; 231 | n += r; 232 | } 233 | return n; 234 | } 235 | 236 | /* Like write(), but retries in case of partial write */ 237 | ssize_t writen(int fd, const void *buf, size_t count) 238 | { 239 | size_t n = 0; 240 | while (count > 0) { 241 | int r = write(fd, buf, count); 242 | if (r < 0) { 243 | if (errno == EINTR) 244 | continue; 245 | return r; 246 | } 247 | if (r == 0) 248 | return n; 249 | buf = (const char *)buf + r; 250 | count -= r; 251 | n += r; 252 | } 253 | return n; 254 | } 255 | 256 | int countbits(int b) 257 | { 258 | int count; 259 | 260 | for (count = 0; b != 0; count++) { 261 | b &= b - 1; // this clears the LSB-most set bit 262 | } 263 | 264 | return (count); 265 | } 266 | 267 | int count_mask(struct sockaddr_in6 *m) 268 | { 269 | struct in6_addr *in6 = &m->sin6_addr; 270 | int i; 271 | int count = 0; 272 | 273 | for (i = 0; i < 16; ++i) { 274 | count += countbits(in6->s6_addr[i]); 275 | } 276 | return count; 277 | } 278 | 279 | struct in6_addr get_prefix6(struct in6_addr const *addr, struct in6_addr const *mask) 280 | { 281 | struct in6_addr prefix = *addr; 282 | int i = 0; 283 | 284 | for (; i < 16; ++i) { 285 | prefix.s6_addr[i] &= mask->s6_addr[i]; 286 | } 287 | 288 | return prefix; 289 | } 290 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Authors: 4 | # Lars Fenneberg 5 | # 6 | # This software is Copyright 1996,1997 by the above mentioned author(s), 7 | # All Rights Reserved. 8 | # 9 | # The license which is distributed with this software in the file COPYRIGHT 10 | # applies to this software. If your distribution is missing this file, you 11 | # may request it from . 12 | # 13 | 14 | 15 | # $(CC) $(DEFS) -I. [$(mumble_CPPFLAGS)|$(AM_CPPFLAGS)] $(CPPFLAGS) [$(mumble_CFLAGS)|$(AM_CFLAGS)] $(CFLAGS) 16 | # mumble_CPPFLAGS override AM_CPPFLAGS 17 | # CPP is C PreProcessor 18 | 19 | AUTOMAKE_OPTIONS = 1.2 foreign subdir-objects 20 | 21 | ### Compiler flags and such ### 22 | 23 | DEFS = \ 24 | -DINET6=1 \ 25 | -DLOG_FACILITY=$(LOG_FACILITY) \ 26 | -DPATH_RADVD_CONF=\"$(PATH_RADVD_CONF)\" \ 27 | -DPATH_RADVD_LOG=\"$(PATH_RADVD_LOG)\" \ 28 | -DPATH_RADVD_PID=\"$(PATH_RADVD_PID)\" \ 29 | -DVERSION=\"$(VERSION)\" 30 | 31 | ENABLE_WARNINGS = \ 32 | -Wall \ 33 | -Wcast-qual \ 34 | -Wmissing-declarations \ 35 | -Wmissing-prototypes \ 36 | -Wpointer-arith \ 37 | -Wstrict-prototypes 38 | 39 | DISABLE_WARNINGS = \ 40 | -Wno-strict-aliasing \ 41 | -Wno-cast-align \ 42 | -Wno-strict-overflow 43 | 44 | AM_CFLAGS = \ 45 | -fno-strict-aliasing \ 46 | $(ENABLE_WARNINGS) \ 47 | $(DISABLE_WARNINGS) 48 | 49 | if HAVE_STACK_PROTECTOR 50 | AM_CFLAGS += \ 51 | -fstack-protector 52 | endif 53 | 54 | ### CPP is C PreProcessor ### 55 | 56 | AM_YFLAGS = -d 57 | 58 | noinst_LIBRARIES = libradvd-parser.a 59 | 60 | radvd_LDADD = \ 61 | libradvd-parser.a \ 62 | @CONDITIONAL_SOURCES@ 63 | 64 | ### Programs and sources ### 65 | 66 | scanner.c: gram.h 67 | gram.h: gram.c 68 | 69 | libradvd_parser_a_SOURCES = \ 70 | gram.h \ 71 | gram.y \ 72 | scanner.l 73 | 74 | sbin_PROGRAMS = \ 75 | radvd \ 76 | radvdump 77 | 78 | EXTRA_radvd_SOURCES = \ 79 | device-bsd44.c \ 80 | device-linux.c \ 81 | netlink.c \ 82 | netlink.h \ 83 | privsep-linux.c 84 | 85 | radvd_SOURCES = \ 86 | defaults.h \ 87 | includes.h \ 88 | log.c \ 89 | log.h \ 90 | pathnames.h \ 91 | radvd.h \ 92 | recv.c \ 93 | socket.c \ 94 | util.c \ 95 | device-common.c \ 96 | interface.c \ 97 | process.c \ 98 | radvd.c \ 99 | send.c \ 100 | timer.c 101 | 102 | radvdump_SOURCES = \ 103 | defaults.h \ 104 | includes.h \ 105 | log.c \ 106 | log.h \ 107 | pathnames.h \ 108 | radvd.h \ 109 | recv.c \ 110 | socket.c \ 111 | util.c \ 112 | radvdump.c 113 | 114 | ### coverage ### 115 | 116 | if HAVE_CHECK 117 | cov: check 118 | gcov *.c *.h *.l *.y 119 | lcov --capture --directory . --output-file coverage.info 120 | genhtml coverage.info --output-directory cov 121 | else 122 | cov: 123 | $(error libcheck not available) 124 | endif 125 | 126 | if HAVE_CHECK 127 | else 128 | check: 129 | $(error libcheck not available) 130 | endif 131 | 132 | ### man pages ### 133 | 134 | SUFFIXES = .man 135 | 136 | .man: 137 | $(AM_V_GEN)$(SED) \ 138 | -e 's,@''VERSION''@,$(VERSION),g' \ 139 | -e 's,@''sbindir''@,$(sbindir),g' \ 140 | -e 's,@''PATH_RADVD_CONF''@,$(PATH_RADVD_CONF),g' \ 141 | -e 's,@''PATH_RADVD_PID''@,$(PATH_RADVD_PID),g' \ 142 | -e 's,@''PATH_RADVD_LOG''@,$(PATH_RADVD_LOG),g' \ 143 | -e 's,@''LOG_FACILITY''@,$(LOG_FACILITY),g' \ 144 | $< > $@ 145 | 146 | man_MANS = \ 147 | radvd.8 \ 148 | radvd.conf.5 \ 149 | radvdump.8 150 | 151 | 152 | ### build support ### 153 | 154 | indent: 155 | clang-format -i *.c *.h test/*.c test/*.h 156 | 157 | EXTRA_DIST = \ 158 | autogen-container.sh \ 159 | autogen.sh \ 160 | CHANGES \ 161 | .clang-format \ 162 | contrib/95-radvd-gen \ 163 | contrib/unset_addrs.sh \ 164 | COPYRIGHT \ 165 | copyright.blurb \ 166 | Docker.autogen \ 167 | .github/workflows/bsd.yaml \ 168 | .github/workflows/buildroot.yaml \ 169 | .github/workflows/linux.yaml \ 170 | .gitignore \ 171 | INTRO.html \ 172 | radvd.8.man \ 173 | radvd.conf.5.man \ 174 | radvd.conf.example \ 175 | radvd.service.in \ 176 | radvdump.8.man \ 177 | README \ 178 | RELEASE-PROCESS.md \ 179 | redhat/systemd/radvd.service \ 180 | redhat/systemd/radvd.spec \ 181 | redhat/systemd/radvd-tmpfs.conf \ 182 | redhat/SysV/radvd.conf.empty \ 183 | redhat/SysV/radvd.init \ 184 | redhat/SysV/radvd.spec \ 185 | redhat/SysV/radvd.sysconfig \ 186 | redhat/SysV/radvd-tmpfs.conf \ 187 | test/check.c \ 188 | test/print_safe_buffer.c \ 189 | test/print_safe_buffer.h \ 190 | test/send.c \ 191 | test/test1.conf \ 192 | test/test_build.sh \ 193 | test/test_dnssl1.conf \ 194 | test/test_dnssl2.conf \ 195 | test/test_dnssl3.conf \ 196 | test/test_dnssl4.conf \ 197 | test/test_dnssl5.conf \ 198 | test/test_dnssl6.conf \ 199 | test/test_rdnss.conf \ 200 | test/test_rdnss_long.conf \ 201 | test/util.c \ 202 | TODO \ 203 | .travis.yml 204 | 205 | EXTENSIONS = gz bz2 lz lzma xz tarZ shar zip 206 | HASHES = sha256 sha512 207 | 208 | # Generate clearsigned checksum files. 209 | # Generate detached signatures of the tarballs. 210 | sign: 211 | $(AM_V_GEN)for e in $(EXTENSIONS); do \ 212 | if [ -f radvd-$(VERSION).tar.$$e ]; then \ 213 | gpg --armor --sign --detach-sign radvd-$(VERSION).tar.$$e; \ 214 | for h in $(HASHES); do \ 215 | $${h}sum --tag radvd-$(VERSION).tar.$$e > radvd-$(VERSION).tar.$$e.$$h || exit 1; \ 216 | gpg --clear-sign radvd-$(VERSION).tar.$$e.$$h || exit 1; \ 217 | mv -f radvd-$(VERSION).tar.$$e.$$h.asc radvd-$(VERSION).tar.$$e.$$h || exit 1; \ 218 | done; \ 219 | fi; \ 220 | done 221 | 222 | # Verify clearsigned checksum files. 223 | # Verify detached signatures of the tarballs. 224 | # 225 | # Be careful to verify the clearsign, take ONLY the signed part, and then 226 | # verify the checksum contained in that (ignore checksums OUTSIDE the 227 | # clearsigned part). 228 | verify: 229 | $(AM_V_GEN)for e in $(EXTENSIONS); do \ 230 | if [ -f radvd-$(VERSION).tar.$$e ]; then \ 231 | gpg --verify radvd-$(VERSION).tar.$$e.asc; \ 232 | for h in $(HASHES); do \ 233 | rm -f radvd-$(VERSION).tar.$$e.$$h.verified || exit 1; \ 234 | gpg --output radvd-$(VERSION).tar.$$e.$$h.verified --verify radvd-$(VERSION).tar.$$e.$$h || exit 1; \ 235 | if ! $${h}sum -c radvd-$(VERSION).tar.$$e.$$h.verified ; then \ 236 | rm -f radvd-$(VERSION).tar.$$e.$$h.verified; \ 237 | exit 1; \ 238 | fi; \ 239 | rm -f radvd-$(VERSION).tar.$$e.$$h.verified; \ 240 | done; \ 241 | fi; \ 242 | done 243 | 244 | html: 245 | $(AM_V_GEN)echo "
    "; \ 246 | for e in $(EXTENSIONS); do \ 247 | if [ -f radvd-$(VERSION).tar.$$e ]; then \ 248 | echo "
  • "; \ 249 | echo "radvd-$(VERSION).tar.$$e"; \ 250 | for h in $(HASHES); do \ 251 | echo "[$$h]"; \ 252 | done; \ 253 | echo "[GPG]"; \ 254 | echo "
  • "; \ 255 | fi; \ 256 | done; \ 257 | echo "
" 258 | 259 | packages: 260 | @if [[ "$$(git diff | wc -l)" != "0" ]] ; then printf "\n\n\tYou have local changes in the working copy...\n\n\n" && git diff && false ; fi 261 | @if [[ "$$(git rev-parse HEAD)" != "$$(git rev-parse v$(VERSION)^{commit})" ]] ; then printf "\n\n\tv$(VERSION) tag missing, or not checked out...\n\n\n" && false ; fi 262 | rm -f radvd-$(VERSION).tar* 263 | $(MAKE) dist-gzip 264 | rm -rf radvd-$(VERSION) 265 | tar zfx radvd-$(VERSION).tar.gz 266 | cd radvd-$(VERSION) && ./configure --with-check && $(MAKE) check 267 | rm -rf radvd-$(VERSION) 268 | $(MAKE) dist-xz 269 | $(MAKE) sign 270 | $(MAKE) verify 271 | $(MAKE) html 272 | @printf "\n\n\tDont forget to push the v$(VERSION) tag and this branch to origin (git push origin v$(VERSION) master)\n\n\n" 273 | 274 | 275 | dist-hook: 276 | rm -f $(distdir)/gram.c 277 | rm -f $(distdir)/gram.h 278 | rm -f $(distdir)/scanner.c 279 | 280 | CLEANFILES = \ 281 | radvd.8 \ 282 | radvd.conf.5 \ 283 | radvdump.8 \ 284 | gram.c \ 285 | gram.h \ 286 | gram.dot \ 287 | scanner.c \ 288 | scanner.h \ 289 | gram.dot 290 | 291 | ### Explicit Dependencies ### 292 | 293 | radvd_DEPENDENCIES = \ 294 | @CONDITIONAL_SOURCES@ \ 295 | libradvd-parser.a 296 | 297 | ### make check ### 298 | 299 | TESTS = check_all 300 | 301 | check_PROGRAMS = check_all 302 | 303 | EXTRA_check_all_SOURCES = \ 304 | device-bsd44.c \ 305 | device-linux.c \ 306 | netlink.c \ 307 | netlink.h \ 308 | privsep-linux.c 309 | 310 | check_all_SOURCES = \ 311 | test/print_safe_buffer.h \ 312 | test/print_safe_buffer.c \ 313 | test/check.c \ 314 | device-common.c \ 315 | interface.c \ 316 | log.c \ 317 | send.c \ 318 | timer.c \ 319 | util.c 320 | 321 | check_all_CFLAGS = \ 322 | @CHECK_CFLAGS@ \ 323 | -DUNIT_TEST 324 | 325 | check_all_LDADD = \ 326 | @CHECK_LIBS@ \ 327 | @CONDITIONAL_SOURCES@ \ 328 | libradvd-parser.a 329 | 330 | DISTCHECK_CONFIGURE_FLAGS = \ 331 | --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) 332 | 333 | if HAVE_SYSTEMD 334 | systemdsystemunit_DATA = radvd.service 335 | endif 336 | 337 | BUILT_SOURCES = gram.h scanner.c gram.c 338 | -------------------------------------------------------------------------------- /test/util.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | /* 5 | * https://libcheck.github.io/check/ 6 | * https://libcheck.github.io/check/doc/check_html/check_3.html 7 | * http://entrenchant.blogspot.com/2010/08/unit-testing-in-c.html 8 | */ 9 | 10 | START_TEST(test_safe_buffer) 11 | { 12 | struct safe_buffer sb = SAFE_BUFFER_INIT; 13 | ck_assert_ptr_eq(0, sb.buffer); 14 | ck_assert_int_eq(0, sb.allocated); 15 | ck_assert_int_eq(0, sb.used); 16 | ck_assert_int_eq(0, sb.should_free); 17 | // safe_buffer_free(&sb); // Do not free non-malloc'd 18 | 19 | struct safe_buffer *sbptr = new_safe_buffer(); 20 | ck_assert_ptr_eq(0, sbptr->buffer); 21 | ck_assert_int_eq(0, sbptr->allocated); 22 | ck_assert_int_eq(0, sbptr->used); 23 | ck_assert_int_ne(0, sbptr->should_free); 24 | safe_buffer_free(sbptr); 25 | } 26 | END_TEST 27 | 28 | START_TEST(test_safe_buffer_resize) 29 | { 30 | struct safe_buffer *sb = new_safe_buffer(); 31 | for (int i = 0; i < 5000; i += 17) { 32 | safe_buffer_resize(sb, i); 33 | ck_assert_int_ge(sb->allocated, i); 34 | } 35 | safe_buffer_free(sb); 36 | } 37 | END_TEST 38 | 39 | START_TEST(test_safe_buffer_append) 40 | { 41 | struct safe_buffer *sb = new_safe_buffer(); 42 | const char array[] = {"This is a test"}; 43 | safe_buffer_append(sb, array, sizeof(array)); 44 | ck_assert_str_eq((const char*)(sb->buffer), array); 45 | ck_assert_int_eq(sb->used, sizeof(array)); 46 | safe_buffer_free(sb); 47 | } 48 | END_TEST 49 | 50 | START_TEST(test_safe_buffer_append2) 51 | { 52 | struct safe_buffer *sb = new_safe_buffer(); 53 | char array[] = {"This is a test"}; 54 | safe_buffer_append(sb, array, sizeof(array)); 55 | safe_buffer_append(sb, array, sizeof(array)); 56 | ck_assert_str_eq((const char*)(sb->buffer), array); 57 | ck_assert_str_eq((const char*)(sb->buffer + sizeof(array)), array); 58 | ck_assert_int_eq(sb->used, 2 * sizeof(array)); 59 | 60 | safe_buffer_free(sb); 61 | } 62 | END_TEST 63 | 64 | START_TEST(test_safe_buffer_pad) 65 | { 66 | struct safe_buffer *sb = new_safe_buffer(); 67 | char array[] = {"This is a test"}; 68 | safe_buffer_append(sb, array, sizeof(array)); 69 | safe_buffer_pad(sb, 10); 70 | ck_assert_str_eq((const char*)(sb->buffer), array); 71 | ck_assert_int_eq(sb->used, 10 + sizeof(array)); 72 | 73 | safe_buffer_free(sb); 74 | } 75 | END_TEST 76 | 77 | START_TEST(test_safe_buffer_list) 78 | { 79 | struct safe_buffer_list *sbl = new_safe_buffer_list(); 80 | ck_assert_ptr_ne(0, sbl->sb); 81 | ck_assert_ptr_eq(0, sbl->sb->buffer); 82 | ck_assert_int_eq(0, sbl->sb->used); 83 | ck_assert_ptr_eq(0, sbl->next); 84 | safe_buffer_list_free(sbl); 85 | } 86 | END_TEST 87 | 88 | START_TEST(test_safe_buffer_list_to_safe_buffer) 89 | { 90 | struct safe_buffer_list *sbl = new_safe_buffer_list(); 91 | struct safe_buffer *sb = new_safe_buffer(); 92 | char array[] = {"This is a test"}; 93 | 94 | safe_buffer_append(sbl->sb, array, sizeof(array)); 95 | sbl->next = new_safe_buffer_list(); 96 | safe_buffer_append(sbl->next->sb, array, sizeof(array)); 97 | 98 | safe_buffer_list_to_safe_buffer(sbl, sb); 99 | 100 | ck_assert_str_eq((const char*)(sb->buffer), array); 101 | ck_assert_str_eq((const char*)(sb->buffer + sizeof(array)), array); 102 | ck_assert_int_eq(sb->used, 2 * sizeof(array)); 103 | 104 | safe_buffer_free(sb); 105 | safe_buffer_list_free(sbl); 106 | } 107 | END_TEST 108 | 109 | START_TEST(test_addrtostr) 110 | { 111 | char buffer[INET6_ADDRSTRLEN] = {""}; 112 | struct in6_addr addr = {{{0xfe, 0x80, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x12, 0x34}}}; 113 | addrtostr(&addr, buffer, sizeof(buffer)); 114 | ck_assert_str_eq(buffer, "fe80:fe80::ff00:1234"); 115 | } 116 | END_TEST 117 | 118 | START_TEST(test_addrtostr_overflow) 119 | { 120 | char buffer[18] = {""}; 121 | struct in6_addr addr = {{{0xfe, 0x80, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x12, 0x34}}}; 122 | addrtostr(&addr, buffer, sizeof(buffer)); 123 | ck_assert_str_eq(buffer, "[invalid address]"); 124 | } 125 | END_TEST 126 | 127 | START_TEST(test_strdupf) 128 | { 129 | char *str = strdupf("%d %s %zu %c %% char", 1234, "str", (size_t)10, 'c'); 130 | 131 | ck_assert_str_eq(str, "1234 str 10 c % char"); 132 | 133 | free(str); 134 | } 135 | END_TEST 136 | 137 | START_TEST(test_readn) 138 | { 139 | int fd = open("/dev/zero", O_RDONLY); 140 | 141 | ck_assert_int_ne(fd, -1); 142 | 143 | char buffer[10000]; 144 | 145 | memset(buffer, 1, sizeof(buffer)); 146 | 147 | int count = readn(fd, buffer, sizeof(buffer)); 148 | 149 | ck_assert_int_eq(count, 10000); 150 | 151 | for (int i = 0; i < sizeof(buffer); ++i) { 152 | ck_assert_int_eq(buffer[i], 0); 153 | } 154 | } 155 | END_TEST 156 | 157 | START_TEST(test_writen) 158 | { 159 | int fd = open("/dev/null", O_WRONLY); 160 | 161 | ck_assert_int_ne(fd, -1); 162 | 163 | char buffer[10000]; 164 | 165 | memset(buffer, 1, sizeof(buffer)); 166 | 167 | int count = writen(fd, buffer, sizeof(buffer)); 168 | 169 | ck_assert_int_eq(count, 10000); 170 | } 171 | END_TEST 172 | 173 | static struct Interface *iface = 0; 174 | 175 | static void iface_setup(void) 176 | { 177 | ck_assert_ptr_eq(0, iface); 178 | iface = readin_config("test/test1.conf"); 179 | ck_assert_ptr_ne(0, iface); 180 | } 181 | 182 | static void iface_teardown(void) 183 | { 184 | ck_assert_ptr_ne(0, iface); 185 | free_ifaces(iface); 186 | iface = 0; 187 | } 188 | 189 | START_TEST(test_check_dnssl_presence) 190 | { 191 | int rc = check_dnssl_presence(iface->AdvDNSSLList, "example.com"); 192 | ck_assert_int_ne(0, rc); 193 | 194 | rc = check_dnssl_presence(iface->AdvDNSSLList, "office.branch.example.net"); 195 | ck_assert_int_ne(0, rc); 196 | 197 | rc = check_dnssl_presence(iface->AdvDNSSLList, "example.au"); 198 | ck_assert_int_eq(0, rc); 199 | } 200 | END_TEST 201 | 202 | START_TEST(test_check_rdnss_presence) 203 | { 204 | struct in6_addr addr; 205 | int rc; 206 | 207 | /* The next three should be found */ 208 | addr = (struct in6_addr){{{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}; 209 | rc = check_rdnss_presence(iface->AdvRDNSSList, &addr); 210 | ck_assert_int_ne(0, rc); 211 | 212 | addr = (struct in6_addr){{{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}}}; 213 | rc = check_rdnss_presence(iface->AdvRDNSSList, &addr); 214 | ck_assert_int_ne(0, rc); 215 | 216 | addr = (struct in6_addr){{{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3}}}; 217 | rc = check_rdnss_presence(iface->AdvRDNSSList, &addr); 218 | ck_assert_int_ne(0, rc); 219 | 220 | /* The next one should *not* be found */ 221 | addr = (struct in6_addr){{{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6}}}; 222 | rc = check_rdnss_presence(iface->AdvRDNSSList, &addr); 223 | ck_assert_int_eq(0, rc); 224 | } 225 | END_TEST 226 | 227 | START_TEST(test_rand_between) 228 | { 229 | int const RAND_TEST_MAX = 1000; 230 | for (int i = 0; i < RAND_TEST_MAX; ++i) { 231 | double const min = -1; 232 | double const max = 1; 233 | double d = rand_between(min, max); 234 | ck_assert(min <= d); 235 | ck_assert(max >= d); 236 | } 237 | for (int i = 0; i < RAND_TEST_MAX; ++i) { 238 | double const min = -1000; 239 | double const max = 1000; 240 | double d = rand_between(min, max); 241 | ck_assert(min <= d); 242 | ck_assert(max >= d); 243 | } 244 | for (int i = 0; i < RAND_TEST_MAX; ++i) { 245 | double const min = 100; 246 | double const max = 200; 247 | double d = rand_between(min, max); 248 | ck_assert(min <= d); 249 | ck_assert(max >= d); 250 | } 251 | } 252 | END_TEST 253 | 254 | START_TEST(test_cfg_removal_with_sighup) 255 | { 256 | struct Interface *tmpIface = NULL; 257 | 258 | tmpIface = readin_config("test/test1.conf"); 259 | ck_assert(tmpIface); 260 | 261 | free_ifaces(tmpIface); 262 | 263 | tmpIface = readin_config("test/file_that_should_not_exists.conf"); 264 | ck_assert(!tmpIface); 265 | } 266 | END_TEST 267 | 268 | Suite *util_suite(void) 269 | { 270 | TCase *tc_safe_buffer = tcase_create("safe_buffer"); 271 | tcase_add_test(tc_safe_buffer, test_safe_buffer); 272 | tcase_add_test(tc_safe_buffer, test_safe_buffer_resize); 273 | tcase_add_test(tc_safe_buffer, test_safe_buffer_append); 274 | tcase_add_test(tc_safe_buffer, test_safe_buffer_append2); 275 | tcase_add_test(tc_safe_buffer, test_safe_buffer_pad); 276 | 277 | TCase *tc_safe_buffer_list = tcase_create("safe_buffer_list"); 278 | tcase_add_test(tc_safe_buffer_list, test_safe_buffer_list); 279 | tcase_add_test(tc_safe_buffer_list, test_safe_buffer_list_to_safe_buffer); 280 | 281 | TCase *tc_str = tcase_create("str"); 282 | tcase_add_test(tc_str, test_addrtostr); 283 | tcase_add_test(tc_str, test_addrtostr_overflow); 284 | tcase_add_test(tc_str, test_strdupf); 285 | 286 | TCase *tc_ion = tcase_create("ion"); 287 | tcase_add_test(tc_ion, test_readn); 288 | tcase_add_test(tc_ion, test_writen); 289 | 290 | TCase *tc_presence = tcase_create("presence"); 291 | tcase_add_unchecked_fixture(tc_presence, iface_setup, iface_teardown); 292 | tcase_add_test(tc_presence, test_check_dnssl_presence); 293 | tcase_add_test(tc_presence, test_check_rdnss_presence); 294 | 295 | TCase *tc_misc = tcase_create("misc"); 296 | tcase_add_test(tc_misc, test_rand_between); 297 | tcase_add_test(tc_misc, test_cfg_removal_with_sighup); 298 | 299 | Suite *s = suite_create("util"); 300 | suite_add_tcase(s, tc_safe_buffer); 301 | suite_add_tcase(s, tc_safe_buffer_list); 302 | suite_add_tcase(s, tc_str); 303 | suite_add_tcase(s, tc_ion); 304 | suite_add_tcase(s, tc_presence); 305 | suite_add_tcase(s, tc_misc); 306 | 307 | return s; 308 | } 309 | -------------------------------------------------------------------------------- /defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #ifndef DEFAULTS_H 16 | #define DEFAULTS_H 17 | 18 | #include "config.h" 19 | #include "includes.h" 20 | #include "radvd.h" 21 | 22 | /* maximum message size for incoming and outgoing RSs and RAs */ 23 | #define MSG_SIZE_RECV 1500 24 | #define MSG_SIZE_SEND 1452 25 | #define RFC2460_MIN_MTU 1280 /* RFC2460 5. Packet Size Issues: lowest valid MTU supported by IPv6 */ 26 | 27 | #define MAX2(X, Y) (((X) >= (Y)) ? (X) : (Y)) 28 | 29 | /* Router Configuration Variables: */ 30 | 31 | /* For each multicast interface: */ 32 | 33 | #define DFLT_IgnoreIfMissing 1 34 | #define DFLT_AdvSendAdv 0 35 | #define DFLT_MaxRtrAdvInterval 600 36 | #define DFLT_MinRtrAdvInterval(iface) (0.33 * (iface)->MaxRtrAdvInterval) 37 | #define DFLT_AdvManagedFlag 0 38 | #define DFLT_AdvOtherConfigFlag 0 39 | #define DFLT_AdvLinkMTU 0 40 | #define DFLT_AdvReachableTime 0 41 | #define DFLT_AdvRetransTimer 0 42 | #define DFLT_AdvCurHopLimit \ 43 | 64 /* as per RFC 1700 or the \ 44 | next incarnation of it :) */ 45 | #define DFLT_AdvDefaultLifetime(iface) MAX2(1, (int)(3.0 * (iface)->MaxRtrAdvInterval)) 46 | #define DFLT_MinDelayBetweenRAs MIN_DELAY_BETWEEN_RAS 47 | #define DFLT_AdvDefaultPreference 0 48 | #define DFLT_AdvRAMTU RFC2460_MIN_MTU 49 | #define DFLT_UnicastOnly 0 50 | #define DFLT_UnrestrictedUnicast 0 51 | #define DFLT_AdvRASolicitedUnicast 1 52 | #define DFLT_RemoveAdvOnExit 1 53 | 54 | /* Options sent with RA */ 55 | 56 | #define DFLT_AdvSourceLLAddress 1 57 | 58 | /* Each prefix has an associated: */ 59 | 60 | #define DFLT_AdvValidLifetime 86400 /* seconds */ 61 | #define DFLT_AdvOnLinkFlag 1 62 | #define DFLT_AdvPreferredLifetime 14400 /* seconds */ 63 | #define DFLT_AdvAutonomousFlag 1 64 | #define DFLT_DeprecatePrefixFlag 0 65 | #define DFLT_DecrementLifetimesFlag 0 66 | 67 | /* RFC8781 section 4.1; this is the non-scaled value (8191 << 3) */ 68 | #define DFLT_NAT64MaxValidLifetime 65528 /* seconds */ 69 | 70 | /* Each route has an associated: */ 71 | #define DFLT_AdvRouteLifetime(iface) (3 * (iface)->MaxRtrAdvInterval) 72 | 73 | #define DFLT_AdvRoutePreference 0 /* medium */ 74 | #define DFLT_RemoveRouteFlag 1 75 | 76 | /* RDNSS 77 | * https://tools.ietf.org/html/rfc8106#section-5.1 78 | * "The value of Lifetime SHOULD by default be at least 3 * MaxRtrAdvInterval" 79 | * RFC8106 does NOT specify bounds 80 | * 81 | * Note: RFC6106 handled this differently: 82 | * https://tools.ietf.org/html/rfc6106#section-5.1 83 | * Specifies bounds of MaxRtrAdvInterval <= Lifetime <= 2*MaxRtrAdvInterval 84 | * but does NOT specify a default value 85 | */ 86 | #define DFLT_AdvRDNSSLifetime(iface) (3 * (iface)->MaxRtrAdvInterval) 87 | #define DFLT_FlushRDNSSFlag 1 88 | 89 | /* DNSSL 90 | * https://tools.ietf.org/html/rfc8106#section-5.2 91 | * "Lifetime SHOULD by default be at least 3 * MaxRtrAdvInterval" 92 | * RFC8106 does NOT specify bounds 93 | * 94 | * Note: RFC6106 handled this differently: 95 | * https://tools.ietf.org/html/rfc6106#section-5.2 96 | * Specifies bounds of MaxRtrAdvInterval <= Lifetime <= 2*MaxRtrAdvInterval 97 | * but does NOT specify a default value 98 | */ 99 | #define DFLT_AdvDNSSLLifetime(iface) (3 * (iface)->MaxRtrAdvInterval) 100 | #define DFLT_FlushDNSSLFlag 1 101 | 102 | /* Protocol (RFC4861) constants: */ 103 | 104 | /* Router constants: */ 105 | 106 | #define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 107 | #define MAX_INITIAL_RTR_ADVERTISEMENTS 3 108 | #define MAX_FINAL_RTR_ADVERTISEMENTS 3 109 | #define MIN_DELAY_BETWEEN_RAS 3.0 110 | #define MIN_DELAY_BETWEEN_RAS_MIPv6 (30.0 / 1000.0) 111 | #define MAX_RA_DELAY_SECONDS (0.5) 112 | 113 | /* Host constants: */ 114 | 115 | #define MAX_RTR_SOLICITATION_DELAY 1 116 | #define RTR_SOLICITATION_INTERVAL 4 117 | #define MAX_RTR_SOLICITATIONS 3 118 | 119 | /* Node constants: */ 120 | 121 | #define MAX_MULTICAST_SOLICIT 3 122 | #define MAX_UNICAST_SOLICIT 3 123 | #define MAX_ANYCAST_DELAY_TIME 1 124 | #define MAX_NEIGHBOR_ADVERTISEMENT 3 125 | #define REACHABLE_TIME 30000 /* milliseconds */ 126 | #define RETRANS_TIMER 1000 /* milliseconds */ 127 | #define DELAY_FIRST_PROBE_TIME 5 128 | #define MIN_RANDOM_FACTOR (1.0 / 2.0) 129 | #define MAX_RANDOM_FACTOR (3.0 / 2.0) 130 | 131 | /* MAX and MIN (RFC4861 and RFC8316), Mobile IPv6 extensions will override if in use */ 132 | 133 | #define MIN_MaxRtrAdvInterval 4 134 | #define MAX_MaxRtrAdvInterval 65535 /* respecting RFC8316 Section 4 */ 135 | 136 | #define MIN_MinRtrAdvInterval 3 137 | #define MAX_MinRtrAdvInterval(iface) (0.75 * (iface)->MaxRtrAdvInterval) 138 | 139 | #define MIN_AdvDefaultLifetime(iface) (MAX2(1, (iface)->MaxRtrAdvInterval)) 140 | #define MAX_AdvDefaultLifetime 65535 /* respecting RFC8316 Section 4 */ 141 | 142 | #define MIN_AdvLinkMTU RFC2460_MIN_MTU 143 | #define MAX_AdvLinkMTU 131072 144 | 145 | #define MIN_AdvRAMTU MIN_AdvLinkMTU 146 | #define MAX_AdvRAMTU MAX_AdvLinkMTU 147 | 148 | #define MIN_AdvReachableTime 100 149 | #define MAX_AdvReachableTime 3600000 /* 1 hour in milliseconds */ 150 | 151 | #define MIN_AdvRetransTimer 10 152 | #define MAX_AdvRetransTimer 3600000 153 | 154 | #define MIN_AdvCurHopLimit 2 155 | #define MAX_AdvCurHopLimit 255 156 | 157 | #define MAX_PrefixLen 128 158 | 159 | /* SLAAC (RFC4862) Constants and Derived Values */ 160 | #define MIN_AdvValidLifetime 7200 /* 2 hours in secs */ 161 | 162 | /* 163 | * Mobile IPv6 extensions, off by default 164 | */ 165 | 166 | #define DFLT_AdvRouterAddr 0 167 | #define DFLT_AdvHomeAgentFlag 0 168 | #define DFLT_AdvIntervalOpt 0 169 | #define DFLT_AdvHomeAgentInfo 0 170 | 171 | /* Option types (defined also at least in glibc 2.2's netinet/icmp6.h) */ 172 | 173 | #ifndef ND_OPT_RTR_ADV_INTERVAL 174 | #define ND_OPT_RTR_ADV_INTERVAL 7 175 | #endif 176 | #ifndef ND_OPT_HOME_AGENT_INFO 177 | #define ND_OPT_HOME_AGENT_INFO 8 178 | #endif 179 | 180 | /* de-facto codepoint used by many implementations was '9', 181 | the official IANA assignment is '24' */ 182 | #undef ND_OPT_ROUTE_INFORMATION 183 | #define ND_OPT_ROUTE_INFORMATION 24 184 | 185 | /* XXX: some libc's like KAME already had nd_opt_route_info! */ 186 | struct nd_opt_route_info_local { /* route information */ 187 | uint8_t nd_opt_ri_type; 188 | uint8_t nd_opt_ri_len; 189 | uint8_t nd_opt_ri_prefix_len; 190 | uint8_t nd_opt_ri_flags_reserved; 191 | uint32_t nd_opt_ri_lifetime; 192 | struct in6_addr nd_opt_ri_prefix; 193 | }; 194 | 195 | /* the reserved field is 8 bits and we're interested of the middle two: 000xx000 */ 196 | #define ND_OPT_RI_PRF_SHIFT 3 197 | #define ND_OPT_RI_PRF_MASK (3 << ND_OPT_RI_PRF_SHIFT) /* 00011000 = 0x18 */ 198 | 199 | #undef ND_OPT_RDNSS_INFORMATION 200 | #define ND_OPT_RDNSS_INFORMATION 25 201 | 202 | /* */ 203 | struct nd_opt_rdnss_info_local { 204 | uint8_t nd_opt_rdnssi_type; 205 | uint8_t nd_opt_rdnssi_len; 206 | uint16_t nd_opt_rdnssi_pref_flag_reserved; 207 | uint32_t nd_opt_rdnssi_lifetime; 208 | struct in6_addr nd_opt_rdnssi_addr[]; 209 | }; 210 | /* pref/flag/reserved field : yyyyx00000000000 (big endian) - 00000000yyyyx000 (little indian); where yyyy = pref, x = flag */ 211 | #if BYTE_ORDER == BIG_ENDIAN 212 | #define ND_OPT_RDNSSI_PREF_SHIFT 12 213 | #else 214 | #define ND_OPT_RDNSSI_PREF_SHIFT 4 215 | #endif 216 | #define ND_OPT_RDNSSI_PREF_MASK (0xf << ND_OPT_RDNSSI_PREF_SHIFT) 217 | 218 | #undef ND_OPT_DNSSL_INFORMATION 219 | #define ND_OPT_DNSSL_INFORMATION 31 220 | 221 | /* */ 222 | struct nd_opt_dnssl_info_local { 223 | uint8_t nd_opt_dnssli_type; 224 | uint8_t nd_opt_dnssli_len; 225 | uint16_t nd_opt_dnssli_reserved; 226 | uint32_t nd_opt_dnssli_lifetime; 227 | unsigned char nd_opt_dnssli_suffixes[]; 228 | }; 229 | 230 | /* Flags */ 231 | 232 | #ifndef ND_RA_FLAG_HOME_AGENT 233 | #define ND_RA_FLAG_HOME_AGENT 0x20 234 | #endif 235 | #ifndef ND_OPT_PI_FLAG_RADDR 236 | #define ND_OPT_PI_FLAG_RADDR 0x20 237 | #endif 238 | #ifndef ND_OPT_RDNSSI_FLAG_S 239 | #if BYTE_ORDER == BIG_ENDIAN 240 | #define ND_OPT_RDNSSI_FLAG_S 0x0800 241 | #else 242 | #define ND_OPT_RDNSSI_FLAG_S 0x0008 243 | #endif 244 | #endif 245 | 246 | /* Captive Portal RFC 8910 */ 247 | 248 | #ifndef ND_OPT_CAPTIVE_PORTAL 249 | #define ND_OPT_CAPTIVE_PORTAL 37 250 | #endif 251 | 252 | /* TODO Secure ND CGA RFC 3971, 5.1 */ 253 | /* TODO Secure ND RSA Signature RFC 3971, 5.2 */ 254 | 255 | /* Secure ND Timestamp RFC 3971, 5.3.1 */ 256 | #ifndef ND_OPT_TIMESTAMP 257 | #define ND_OPT_TIMESTAMP 13 258 | #endif 259 | 260 | /* TODO Secure ND Nonce RFC 3971, 5.3.2 */ 261 | 262 | /* Configurable values */ 263 | 264 | #define DFLT_HomeAgentPreference 0 265 | #define DFLT_HomeAgentLifetime(iface) ((iface)->ra_header_info.AdvDefaultLifetime) 266 | 267 | /* Other */ 268 | 269 | #define MIN_MinRtrAdvInterval_MIPv6 (3.0 / 100.0) 270 | #define MIN_MaxRtrAdvInterval_MIPv6 (7.0 / 100.0) 271 | #define RTR_SOLICITATION_INTERVAL_MIPv6 1 /* Recommended value by MIPv6 */ 272 | 273 | #define Cautious_MaxRtrAdvInterval (2.0 / 10.0) 274 | #define Cautious_MaxRtrAdvInterval_Leeway (2.0 / 100.0) 275 | 276 | #define MIN_HomeAgentLifetime 1 /* 0 must NOT be used */ 277 | #define MAX_HomeAgentLifetime 65520 /* 18.2 hours in secs */ 278 | 279 | /* #define MAX_RTR_SOLICITATIONS This MAY be ignored by MIPv6 */ 280 | 281 | /* NEMO extensions, off by default */ 282 | #define DFLT_AdvMobRtrSupportFlag 0 283 | 284 | /* Flags */ 285 | 286 | #ifndef ND_OPT_HAI_FLAG_SUPPORT_MR 287 | #if BYTE_ORDER == BIG_ENDIAN 288 | #define ND_OPT_HAI_FLAG_SUPPORT_MR 0x8000 289 | #else /* BYTE_ORDER == LITTLE_ENDIAN */ 290 | #define ND_OPT_HAI_FLAG_SUPPORT_MR 0x0080 291 | #endif 292 | #endif 293 | 294 | #endif 295 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl 3 | dnl Authors: 4 | dnl Lars Fenneberg 5 | dnl 6 | dnl This software is Copyright 1996-2000 by the above mentioned author(s), 7 | dnl All Rights Reserved. 8 | dnl 9 | dnl The license which is distributed with this software in the file COPYRIGHT 10 | dnl applies to this software. If your distribution is missing this file, you 11 | dnl may request it from . 12 | dnl 13 | dnl 14 | 15 | dnl If adding rcX to version, be sure to separate with a '-' 16 | AC_INIT([radvd],[2.20]) 17 | AC_CONFIG_SRCDIR(radvd.c) 18 | AC_CANONICAL_TARGET 19 | AM_INIT_AUTOMAKE 20 | 21 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) 22 | 23 | 24 | AC_MSG_CHECKING([architecture]) 25 | case "$target" in 26 | *linux*) 27 | AC_MSG_RESULT(linux) 28 | AC_DEFINE([_GNU_SOURCE], [], [whether compiling on Linux, glibc>=2.8 does not expose in6_pktinfo otherwise..]) 29 | arch=linux 30 | ;; 31 | *bsd*) 32 | AC_MSG_RESULT(bsd44) 33 | arch=bsd44 34 | ;; 35 | *darwin*) 36 | AC_DEFINE([__APPLE_USE_RFC_2292], [1], [OS X 10.9 wont build radvd without this]) 37 | AC_MSG_RESULT([bsd44 (darwin)]) 38 | arch=bsd44 39 | ;; 40 | *) 41 | AC_MSG_RESULT(unknown: $target) 42 | AC_MSG_ERROR( 43 | [currently only Linux and BSD 4.4 with NRLs IPv6 code are 44 | supported. If you have such a system and it is not guessed correctly 45 | you must specify it with --target on the configure command line]) 46 | ;; 47 | esac 48 | 49 | dnl Determine CC and preset CFLAGS 50 | AC_PROG_CC_C99 51 | AC_PROG_RANLIB 52 | AM_PROG_AR 53 | 54 | AC_ARG_WITH([stack-protector], AS_HELP_STRING([--without-stack-protector], [Build without -fstack-protector]),[],[with_stack_protector=yes]) 55 | AM_CONDITIONAL(HAVE_STACK_PROTECTOR, test x"$with_stack_protector" = xyes) 56 | 57 | AC_ARG_WITH([check], AS_HELP_STRING([--without-check], [Build without check unit testing framework]),[],[with_check=no]) 58 | # AC_ARG_WITH provides HAVE_CHECK*; but that does not say if the library is present 59 | check_version=0.9.4 60 | AS_IF([test "x$with_check" = "xyes"], [ 61 | # only check_all gets linked; NOT radvd / radvdump 62 | # PKG_CHECK_MODULES's default behavior does not export any variable that 63 | # says if the module is present or not. $pkg_cv_CHECK_{CFLAGS,LIBS} are 64 | # also taken from either env or package, but the package can have them 65 | # empty, so we must define the true/false. 66 | # See https://autotools.info/pkgconfig/pkg_check_modules.html 67 | PKG_CHECK_MODULES([CHECK], [check >= ${check_version}], [found_yes=yes], [found_check=no]) 68 | ]) 69 | AM_CONDITIONAL(HAVE_CHECK, test x"$with_check" = xyes) 70 | if test "x$found_check" = xno && test "x$with_check" = xyes; then 71 | AC_MSG_ERROR("check >= ${check_version} required but not found") 72 | fi 73 | 74 | dnl Determine of netlink is available 75 | AC_MSG_CHECKING(netlink) 76 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 77 | #include 78 | #include 79 | #include 80 | #include 81 | ]], [[ 82 | int 83 | main(int argc, char * argv[]) 84 | { 85 | int sock; 86 | struct sockaddr_nl snl; 87 | 88 | sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 89 | 90 | snl.nl_family = AF_NETLINK; 91 | snl.nl_groups = RTMGRP_LINK; 92 | 93 | return 0; 94 | } 95 | ]])],[ 96 | AC_DEFINE(HAVE_NETLINK, 1, [Linux netlink]) 97 | CONDITIONAL_SOURCES=netlink.${OBJEXT} 98 | AC_MSG_RESULT(yes) 99 | ],[ 100 | AC_MSG_RESULT(no) 101 | ]) 102 | 103 | dnl clock_gettime is in librt for glibc <2.17 104 | AC_SEARCH_LIBS(clock_gettime, rt) 105 | 106 | AC_CHECK_FUNCS(strlcpy, found_strlcpy=yes, found_strlcpy=no) 107 | if test "x$found_strlcpy" = xno; then 108 | dnl check libbsd for strlcpy 109 | PKG_CHECK_MODULES([BSD], [libbsd >= 0]) 110 | AC_SEARCH_LIBS(strlcpy, bsd, 111 | [ 112 | found_bsd_strlcpy=yes 113 | CFLAGS="$BSD_CFLAGS $CFLAGS" 114 | LIBS="$BSD_LIBS $LIBS" 115 | ], 116 | found_bsd_strlcpy=no) 117 | fi 118 | 119 | if test "x$found_strlcpy" = xno && test "x$found_bsd_strlcpy" = xno; then 120 | AC_MSG_ERROR(cannot find strlcpy - upgrade your libc or install libbsd) 121 | fi 122 | 123 | dnl Needed for normal compile 124 | AC_PROG_INSTALL 125 | AC_PATH_PROG(RM, rm, NOTFOUND) 126 | if test "x$RM" = xNOTFOUND; then 127 | AC_MSG_ERROR(can not find rm in your path - check PATH) 128 | fi 129 | AC_PATH_PROG(SED, sed, NOTFOUND) 130 | if test "x$SED" = xNOTFOUND; then 131 | AC_MSG_ERROR(can not find sed in your path - check PATH) 132 | fi 133 | dnl Not needed 134 | AC_PATH_PROG(LN, ln) 135 | AC_PROG_YACC 136 | AM_PROG_LEX(noyywrap) 137 | AC_PATH_PROG(TAR, tar) 138 | AC_PATH_PROG(GZIP, gzip) 139 | 140 | dnl Check where to put the logfile 141 | AC_MSG_CHECKING(where to put logfile) 142 | AC_ARG_WITH(logfile, 143 | [ --with-logfile Path to the radvd logfile [/var/log/radvd.log]], 144 | PATH_RADVD_LOG=$withval, 145 | PATH_RADVD_LOG=/var/log/radvd.log) 146 | AC_MSG_RESULT($PATH_RADVD_LOG) 147 | 148 | dnl Check where to put the pidfile 149 | AC_MSG_CHECKING(where to put pidfile) 150 | AC_ARG_WITH(pidfile, 151 | [ --with-pidfile Path to the radvd pidfile [/var/run/radvd.pid]], 152 | PATH_RADVD_PID=$withval, 153 | PATH_RADVD_PID=/var/run/radvd.pid) 154 | AC_MSG_RESULT($PATH_RADVD_PID) 155 | 156 | dnl Check where to put the configfile 157 | AC_MSG_CHECKING(where to find configfile) 158 | AC_ARG_WITH(configfile, 159 | [ --with-configfile Path to the radvd config file [SYSCONF/radvd.conf]], 160 | PATH_RADVD_CONF=$withval, 161 | [eval PATH_RADVD_CONF=${sysconfdir}/radvd.conf]) 162 | PATH_RADVD_CONF=$(echo $PATH_RADVD_CONF | sed 's/NONE//1') 163 | AC_MSG_RESULT($PATH_RADVD_CONF) 164 | 165 | dnl Checking which syslog facility to use 166 | AC_MSG_CHECKING(which syslog facility to use) 167 | AC_ARG_WITH(facility, 168 | [ --with-facility Syslog facility to use when using syslog logging], 169 | LOG_FACILITY=$withval, 170 | LOG_FACILITY=LOG_DAEMON) 171 | AC_MSG_RESULT($LOG_FACILITY) 172 | 173 | dnl Checks for libraries. 174 | 175 | AC_CHECK_LIB(c, inet_ntop,, 176 | AC_CHECK_LIB(inet6, inet_ntop, 177 | LIBS="$LIBS -linet6" 178 | , 179 | AC_MSG_ERROR(can not continue without libinet6.a library - check your LDFLAGS) 180 | ) 181 | ) 182 | # prevent caching 183 | unset ac_cv_lib_inet6_inet_ntop 184 | 185 | dnl Checks for header files. 186 | AC_HEADER_STDC 187 | AC_CHECK_HEADERS( \ 188 | getopt.h \ 189 | ifaddrs.h \ 190 | linux/if_arp.h \ 191 | machine/limits.h \ 192 | machine/param.h \ 193 | net/if_arp.h \ 194 | net/if_dl.h \ 195 | net/if_types.h \ 196 | sys/param.h \ 197 | sys/sockio.h \ 198 | sys/time.h \ 199 | time.h \ 200 | ) 201 | AC_HEADER_TIME 202 | 203 | dnl Checks for typedefs, structures, and compiler characteristics. 204 | AC_MSG_CHECKING(whether struct sockaddr_in6 has sin6_scope_id) 205 | AC_COMPILE_IFELSE( 206 | [AC_LANG_PROGRAM([[ 207 | #include 208 | #include 209 | #include 210 | ]], [[ 211 | static struct sockaddr_in6 ac_sin6; 212 | uint32_t ac_size = sizeof (ac_sin6.sin6_scope_id); 213 | ]])], 214 | [ 215 | AC_MSG_RESULT(yes); 216 | AC_DEFINE( 217 | [HAVE_SIN6_SCOPE_ID], 218 | 1, 219 | [whether struct sockaddr_in6 has sin6_scope_id] 220 | ) 221 | ], 222 | [AC_MSG_RESULT(no)] 223 | ) 224 | 225 | AC_MSG_CHECKING(whether struct in6_addr has u6_addrXX and defines s6_addrXX) 226 | AC_COMPILE_IFELSE( 227 | [AC_LANG_PROGRAM( 228 | [[ 229 | #include 230 | #include 231 | ]], [[ 232 | static struct in6_addr in6_u; 233 | uint16_t u = in6_u.s6_addr16[0]; 234 | ]] 235 | )], 236 | [ 237 | AC_MSG_RESULT(yes); 238 | AC_DEFINE([HAVE_IN6_ADDR_S6_ADDR], 1, [whether struct in6_addr has u6_addrXX and defines s6_addrXX]) 239 | ], 240 | [AC_MSG_RESULT(no)] 241 | ) 242 | 243 | dnl Checks for library functions. 244 | AC_CHECK_FUNCS(getopt_long) 245 | AC_CHECK_FUNCS(ppoll) 246 | AC_CHECK_FUNCS(sysctl) 247 | 248 | CONDITIONAL_SOURCES="device-${arch}.${OBJEXT} ${CONDITIONAL_SOURCES}" 249 | if test x${arch} = xlinux ; then 250 | CONDITIONAL_SOURCES="privsep-${arch}.${OBJEXT} ${CONDITIONAL_SOURCES}" 251 | AC_DEFINE(USE_PRIVSEP, 1, [Use privsep]) 252 | fi 253 | AC_SUBST(CONDITIONAL_SOURCES) 254 | 255 | AC_SUBST(VERSION) 256 | AC_SUBST(PATH_RADVD_CONF) 257 | AC_SUBST(PATH_RADVD_PID) 258 | AC_SUBST(PATH_RADVD_LOG) 259 | AC_SUBST(LOG_FACILITY) 260 | 261 | # Default value for sbindir 262 | prefix_temp=$prefix 263 | exec_prefix_temp=$exec_prefix 264 | 265 | test "${prefix}" = "NONE" && prefix="${ac_default_prefix}" 266 | test "${exec_prefix}" = "NONE" && exec_prefix='${prefix}' 267 | 268 | # Initial Value is $exec_prefix/sbin 269 | sbintemp="${sbindir}" 270 | 271 | # Expands to $prefix/sbin 272 | eval sbintemp=\"${sbintemp}\" 273 | # Expands to /usr/local/sbin or /usr/sbin if --prefix is passed 274 | eval sbintemp=\"${sbintemp}\" 275 | SBINDIR=${sbintemp} 276 | 277 | AC_SUBST(SBINDIR) 278 | 279 | PKG_PROG_PKG_CONFIG 280 | AC_ARG_WITH([systemdsystemunitdir], 281 | [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, 282 | [with_systemdsystemunitdir=auto]) 283 | AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ 284 | def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) 285 | 286 | AS_IF([test "x$def_systemdsystemunitdir" = "x"], 287 | [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], 288 | [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) 289 | with_systemdsystemunitdir=no], 290 | [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) 291 | AS_IF([test "x$with_systemdsystemunitdir" != "xno"], 292 | [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) 293 | AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) 294 | 295 | AM_CONFIG_HEADER(config.h) 296 | AC_CONFIG_FILES([Makefile \ 297 | radvd.service \ 298 | redhat/systemd/radvd.spec \ 299 | redhat/SysV/radvd.spec \ 300 | ]) 301 | AC_OUTPUT 302 | 303 | cat << EOF 304 | 305 | Your build configuration: 306 | 307 | CPPFLAGS = $CPPFLAGS 308 | CFLAGS = $CFLAGS 309 | LDFLAGS = $LDFLAGS 310 | Arch = ${arch} 311 | Extras: ${CONDITIONAL_SOURCES} 312 | prefix: $prefix 313 | PID file: $PATH_RADVD_PID 314 | Log file: $PATH_RADVD_LOG 315 | Config file: $PATH_RADVD_CONF 316 | Radvd version: $VERSION 317 | 318 | EOF 319 | 320 | -------------------------------------------------------------------------------- /netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * Reuben Hawkins 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #include "netlink.h" 17 | #include "config.h" 18 | #include "includes.h" 19 | #include "log.h" 20 | #include "radvd.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef SOL_NETLINK 33 | #define SOL_NETLINK 270 34 | #endif 35 | 36 | struct iplink_req { 37 | struct nlmsghdr n; 38 | struct ifinfomsg i; 39 | char buf[1024]; 40 | }; 41 | 42 | struct ipaddr_req { 43 | struct nlmsghdr n; 44 | struct ifaddrmsg r; 45 | }; 46 | 47 | int prefix_match(struct AdvPrefix const *prefix, struct in6_addr *addr) { 48 | if ((prefix->PrefixLen % 8) == 0) { 49 | return !memcmp(&prefix->Prefix, addr, prefix->PrefixLen/8); 50 | } else { 51 | int i; 52 | for (i = 0; i < prefix->PrefixLen; i++) { 53 | char mask = 1 << (8 - (i % 8)); 54 | int index = i / 8; 55 | if ((prefix->Prefix.s6_addr[index] & mask) != 56 | (addr->s6_addr[index] & mask)) 57 | return 0; 58 | } 59 | return 1; 60 | } 61 | } 62 | 63 | int netlink_get_address_lifetimes(struct AdvPrefix const *prefix, unsigned int *preferred_lft, unsigned int *valid_lft) { 64 | int ret = 0; 65 | struct ipaddr_req req = {}; 66 | struct nlmsghdr *retmsg; 67 | struct ifaddrmsg *retaddr; 68 | struct rtattr *tb; 69 | int attrlen; 70 | char buf[32768]; 71 | unsigned int valid = 0; 72 | unsigned int preferred = 0; 73 | int sock = -1; 74 | int len; 75 | 76 | memset(&req, 0, sizeof(req)); 77 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); 78 | req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; 79 | req.n.nlmsg_type = RTM_GETADDR; 80 | req.r.ifa_family = AF_INET6; 81 | 82 | sock = netlink_socket(); 83 | if (sock == -1) 84 | return ret; 85 | 86 | /* Send and receive the netlink message */ 87 | len = send(sock, &req, req.n.nlmsg_len, 0); 88 | if (len == -1) { 89 | flog(LOG_ERR, "netlink: send for address lifetimes failed: %s", strerror(errno)); 90 | close (sock); 91 | return ret; 92 | } 93 | 94 | len = recv(sock, buf, sizeof(buf), 0); 95 | if (len == -1) { 96 | flog(LOG_ERR, "netlink: recv for address lifetimes failed: %s", strerror(errno)); 97 | close (sock); 98 | return ret; 99 | } 100 | 101 | retmsg = (struct nlmsghdr *)buf; 102 | 103 | while NLMSG_OK(retmsg, len) { 104 | retaddr = (struct ifaddrmsg *)NLMSG_DATA(retmsg); 105 | tb = (struct rtattr *)IFA_RTA(retaddr); 106 | 107 | attrlen = IFA_PAYLOAD(retmsg); 108 | 109 | char addr[INET6_ADDRSTRLEN]; 110 | int found = 0; 111 | 112 | while RTA_OK(tb, attrlen) { 113 | if (tb->rta_type == IFA_ADDRESS) { 114 | /* Test if the address matches the prefix we are searching for */ 115 | struct in6_addr *tmp = RTA_DATA(tb); 116 | if (prefix_match(prefix, tmp)) { 117 | inet_ntop(AF_INET6, RTA_DATA(tb), addr, sizeof(addr)); 118 | found = 1; 119 | } else { 120 | found = 0; 121 | } 122 | } 123 | 124 | /** 125 | * If we have matched an address, retrieve and update the valid and preferred lifetimes for that prefix. 126 | */ 127 | if(found && tb->rta_type == IFA_CACHEINFO) { 128 | struct ifa_cacheinfo *cache_info = (struct ifa_cacheinfo *)RTA_DATA(tb); 129 | if (cache_info->ifa_valid > valid) { 130 | valid = cache_info->ifa_valid; 131 | } 132 | 133 | if (cache_info->ifa_prefered > preferred) { 134 | preferred = cache_info->ifa_prefered; 135 | } 136 | /* Reset found flag, in case more than one address exists on the same prefix */ 137 | found = 0; 138 | /* At lease 1 lifetime have been found, return value is true */ 139 | ret = 1; 140 | } 141 | 142 | tb = RTA_NEXT(tb, attrlen); 143 | } 144 | retmsg = NLMSG_NEXT(retmsg, len); 145 | } 146 | 147 | *valid_lft = valid; 148 | *preferred_lft = preferred; 149 | 150 | close (sock); 151 | return ret; 152 | } 153 | 154 | int netlink_get_device_addr_len(struct Interface *iface) 155 | { 156 | struct iplink_req req = {}; 157 | struct iovec iov = {&req, sizeof(req)}; 158 | struct sockaddr_nl sa = {}; 159 | struct msghdr msg = {.msg_name=(void *)&sa, .msg_namelen=sizeof(sa), .msg_iov=&iov, .msg_iovlen=1, .msg_control=NULL, .msg_controllen=0, .msg_flags=0}; 160 | int sock, len, addr_len = -1; 161 | unsigned short type; 162 | char answer[32768]; 163 | struct rtattr *tb; 164 | 165 | /* nl_pid (for linux kernel) and nl_groups (unicast) should be zero */ 166 | sa.nl_family = AF_NETLINK; 167 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 168 | req.n.nlmsg_flags = NLM_F_REQUEST; 169 | req.n.nlmsg_type = RTM_GETLINK; 170 | req.i.ifi_index = iface->props.if_index; 171 | 172 | sock = netlink_socket(); 173 | if (sock == -1) 174 | return -1; 175 | 176 | len = sendmsg(sock, &msg, 0); 177 | if (len == -1) { 178 | flog(LOG_ERR, "netlink: sendmsg for addr_len failed: %s", strerror(errno)); 179 | goto out; 180 | } 181 | 182 | iov.iov_base = answer; 183 | iov.iov_len = sizeof(answer); 184 | len = recvmsg(sock, &msg, 0); 185 | if (len == -1) { 186 | flog(LOG_ERR, "netlink: recvmsg for addr_len failed: %s", strerror(errno)); 187 | goto out; 188 | } 189 | 190 | if (len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) 191 | goto out; 192 | len -= NLMSG_LENGTH(sizeof(struct ifinfomsg)); 193 | 194 | tb = (struct rtattr *)(answer + NLMSG_LENGTH(sizeof(struct ifinfomsg))); 195 | while (RTA_OK(tb, len)) { 196 | type = tb->rta_type & ~NLA_F_NESTED; 197 | if (type == IFLA_ADDRESS) { 198 | addr_len = RTA_PAYLOAD(tb); 199 | break; 200 | } 201 | tb = RTA_NEXT(tb, len); 202 | } 203 | 204 | out: 205 | close(sock); 206 | 207 | return addr_len; 208 | } 209 | 210 | void process_netlink_msg(int netlink_sock, struct Interface *ifaces, int icmp_sock) 211 | { 212 | char buf[4096]; 213 | struct iovec iov = {buf, sizeof(buf)}; 214 | struct sockaddr_nl sa; 215 | struct msghdr msg = {.msg_name=(void *)&sa, .msg_namelen=sizeof(sa), .msg_iov=&iov, .msg_iovlen=1, .msg_control=NULL, .msg_controllen=0, .msg_flags=0}; 216 | int len = recvmsg(netlink_sock, &msg, 0); 217 | if (len == -1) { 218 | flog(LOG_ERR, "netlink: recvmsg failed: %s", strerror(errno)); 219 | } 220 | 221 | for (struct nlmsghdr *nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { 222 | char ifnamebuf[IF_NAMESIZE]; 223 | /* The end of multipart message. */ 224 | if (nh->nlmsg_type == NLMSG_DONE) 225 | return; 226 | 227 | if (nh->nlmsg_type == NLMSG_ERROR) { 228 | flog(LOG_ERR, "netlink: unknown error"); 229 | abort(); 230 | } 231 | 232 | /* Continue with parsing payload. */ 233 | if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK || nh->nlmsg_type == RTM_SETLINK) { 234 | struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nh); 235 | const char *ifname = if_indextoname(ifinfo->ifi_index, ifnamebuf); 236 | 237 | struct rtattr *rta = IFLA_RTA(NLMSG_DATA(nh)); 238 | int rta_len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); 239 | for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) { 240 | if (rta->rta_type == IFLA_OPERSTATE || rta->rta_type == IFLA_LINKMODE) { 241 | if (ifinfo->ifi_flags & IFF_RUNNING) { 242 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, flags is running", ifname, 243 | ifinfo->ifi_index); 244 | } else { 245 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, flags is *NOT* running", ifname, 246 | ifinfo->ifi_index); 247 | } 248 | } 249 | } 250 | 251 | /* Reinit the interfaces which need it. */ 252 | struct Interface *iface; 253 | switch (nh->nlmsg_type) { 254 | case RTM_NEWLINK: 255 | iface = find_iface_by_name(ifaces, ifname); 256 | break; 257 | default: 258 | iface = find_iface_by_index(ifaces, ifinfo->ifi_index); 259 | break; 260 | } 261 | if (iface) { 262 | if (nh->nlmsg_type == RTM_DELLINK) { 263 | dlog(LOG_INFO, 4, "netlink: %s removed, cleaning up", iface->props.name); 264 | cleanup_iface(icmp_sock, iface); 265 | } 266 | else { 267 | touch_iface(iface); 268 | } 269 | } 270 | 271 | } else if (nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR) { 272 | struct ifaddrmsg *ifaddr = (struct ifaddrmsg *)NLMSG_DATA(nh); 273 | const char *ifname = if_indextoname(ifaddr->ifa_index, ifnamebuf); 274 | 275 | switch (nh->nlmsg_type) { 276 | 277 | case RTM_DELADDR: 278 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, address deleted", ifname, ifaddr->ifa_index); 279 | break; 280 | 281 | case RTM_NEWADDR: 282 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, new address", ifname, ifaddr->ifa_index); 283 | break; 284 | 285 | default: 286 | flog(LOG_ERR, "netlink: unhandled event: %d", nh->nlmsg_type); 287 | break; 288 | } 289 | 290 | struct Interface *iface = find_iface_by_index(ifaces, ifaddr->ifa_index); 291 | if (iface) { 292 | struct in6_addr *if_addrs = NULL; 293 | int count = get_iface_addrs(iface->props.name, NULL, &if_addrs); 294 | 295 | if (count != iface->props.addrs_count || 296 | 0 != memcmp(if_addrs, iface->props.if_addrs, count * sizeof(struct in6_addr))) { 297 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, addresses are different", ifname, 298 | ifaddr->ifa_index); 299 | touch_iface(iface); 300 | } else { 301 | dlog(LOG_DEBUG, 3, "netlink: %s, ifindex %d, addresses are the same", ifname, 302 | ifaddr->ifa_index); 303 | } 304 | free(if_addrs); 305 | } 306 | } 307 | } 308 | } 309 | 310 | int netlink_socket(void) 311 | { 312 | int sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 313 | if (sock == -1) { 314 | flog(LOG_ERR, "Unable to open netlink socket: %s", strerror(errno)); 315 | return -1; 316 | } 317 | #if defined SOL_NETLINK && defined NETLINK_NO_ENOBUFS 318 | else if (setsockopt(sock, SOL_NETLINK, NETLINK_NO_ENOBUFS, (int[]){1}, sizeof(int)) < 0) { 319 | flog(LOG_ERR, "Unable to setsockopt NETLINK_NO_ENOBUFS: %s", strerror(errno)); 320 | } 321 | #endif 322 | struct sockaddr_nl snl; 323 | memset(&snl, 0, sizeof(snl)); 324 | snl.nl_family = AF_NETLINK; 325 | snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR; 326 | 327 | int rc = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); 328 | if (rc == -1) { 329 | flog(LOG_ERR, "Unable to bind netlink socket: %s", strerror(errno)); 330 | close(sock); 331 | sock = -1; 332 | } 333 | 334 | return sock; 335 | } 336 | -------------------------------------------------------------------------------- /radvd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Pedro Roque 5 | * Lars Fenneberg 6 | * 7 | * This software is Copyright 1996,1997 by the above mentioned author(s), 8 | * All Rights Reserved. 9 | * 10 | * The license which is distributed with this software in the file COPYRIGHT 11 | * applies to this software. If your distribution is missing this file, you 12 | * may request it from . 13 | * 14 | */ 15 | 16 | #pragma once 17 | 18 | #include "config.h" 19 | #include "defaults.h" 20 | #include "includes.h" 21 | #include "log.h" 22 | 23 | #define CONTACT_EMAIL "Reuben Hawkins " 24 | 25 | extern int sock; 26 | 27 | extern int disableigmp6check; 28 | 29 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 30 | 31 | struct AdvPrefix; 32 | struct NAT64Prefix; 33 | struct AutogenIgnorePrefix; 34 | struct Clients; 35 | 36 | #define HWADDR_MAX 16 37 | #define USER_HZ 100 38 | 39 | struct safe_buffer { 40 | int should_free; 41 | size_t allocated; 42 | size_t used; 43 | unsigned char *buffer; 44 | }; 45 | 46 | #define SAFE_BUFFER_INIT \ 47 | (struct safe_buffer) { .should_free = 0, .allocated = 0, .used = 0, .buffer = 0 } 48 | 49 | struct safe_buffer_list { 50 | struct safe_buffer *sb; 51 | struct safe_buffer_list *next; 52 | }; 53 | 54 | struct Interface { 55 | struct Interface *next; 56 | 57 | int IgnoreIfMissing; 58 | int AdvSendAdvert; 59 | double MaxRtrAdvInterval; 60 | double MinRtrAdvInterval; 61 | double MinDelayBetweenRAs; 62 | int AdvSourceLLAddress; 63 | int RemoveAdvOnExit; 64 | int UnicastOnly; 65 | int UnrestrictedUnicast; 66 | int AdvRASolicitedUnicast; 67 | char *AdvCaptivePortalAPI; 68 | struct Clients *ClientList; 69 | 70 | struct state_info { 71 | int ready; /* Info whether this interface has been initialized successfully */ 72 | int changed; /* Info whether this interface's settings have changed */ 73 | int cease_adv; 74 | uint32_t racount; // count of non-unicast initial router adv 75 | } state_info; 76 | 77 | struct properties { 78 | char name[IFNAMSIZ]; /* interface name */ 79 | unsigned int if_index; 80 | struct in6_addr if_addr; /* the first link local addr */ 81 | struct in6_addr *if_addrs; /* all the addrs */ 82 | int addrs_count; 83 | struct in6_addr *if_addr_rasrc; /* selected AdvRASrcAddress or NULL */ 84 | uint32_t max_ra_option_size; 85 | } props; 86 | 87 | struct ra_header_info { 88 | int AdvManagedFlag; 89 | int AdvOtherConfigFlag; 90 | uint8_t AdvCurHopLimit; 91 | int AdvHomeAgentFlag; 92 | int32_t AdvDefaultLifetime; /* XXX: really uint16_t but we need to use -1 */ 93 | int AdvDefaultPreference; 94 | uint32_t AdvReachableTime; 95 | uint32_t AdvRetransTimer; 96 | } ra_header_info; 97 | 98 | struct times { 99 | struct timespec last_multicast; 100 | struct timespec next_multicast; 101 | struct timespec last_ra_time; 102 | } times; 103 | 104 | struct AdvPrefix *AdvPrefixList; 105 | struct AdvRoute *AdvRouteList; 106 | struct AdvRDNSS *AdvRDNSSList; 107 | struct AdvDNSSL *AdvDNSSLList; 108 | 109 | struct NAT64Prefix *NAT64PrefixList; 110 | 111 | struct AutogenIgnorePrefix *IgnorePrefixList; 112 | 113 | uint32_t AdvLinkMTU; /* XXX: sllao also has an if_maxmtu value...Why? */ 114 | uint32_t AdvRAMTU; /* MTU used for RA */ 115 | 116 | struct sllao { 117 | uint8_t if_hwaddr[HWADDR_MAX]; 118 | int if_hwaddr_len; 119 | int if_prefix_len; 120 | int if_maxmtu; 121 | } sllao; 122 | 123 | struct mipv6 { 124 | /* Mobile IPv6 extensions */ 125 | int AdvIntervalOpt; 126 | int AdvHomeAgentInfo; 127 | 128 | uint16_t HomeAgentPreference; 129 | int32_t HomeAgentLifetime; /* XXX: really uint16_t but we need to use -1 */ 130 | 131 | /* NEMO extensions */ 132 | int AdvMobRtrSupportFlag; 133 | } mipv6; 134 | 135 | struct AdvLowpanCo *AdvLowpanCoList; 136 | struct AdvAbro *AdvAbroList; 137 | 138 | struct AdvRASrcAddress *AdvRASrcAddressList; 139 | 140 | int lineno; /* On what line in the config file was this iface defined? */ 141 | }; 142 | 143 | struct Clients { 144 | struct in6_addr Address; 145 | int ignored; 146 | struct Clients *next; 147 | }; 148 | 149 | struct AdvPrefix { 150 | struct in6_addr Prefix; 151 | uint8_t PrefixLen; 152 | 153 | int AdvOnLinkFlag; 154 | int AdvAutonomousFlag; 155 | uint32_t AdvValidLifetime; 156 | uint32_t AdvPreferredLifetime; 157 | int DeprecatePrefixFlag; 158 | int DecrementLifetimesFlag; 159 | 160 | uint32_t curr_validlft; 161 | uint32_t curr_preferredlft; 162 | 163 | /* Mobile IPv6 extensions */ 164 | int AdvRouterAddr; 165 | 166 | /* 6to4 etc. extensions */ 167 | char if6to4[IFNAMSIZ]; 168 | 169 | /* Select prefixes from this interface. */ 170 | char if6[IFNAMSIZ]; 171 | 172 | struct AdvPrefix *next; 173 | }; 174 | 175 | struct NAT64Prefix { 176 | struct in6_addr Prefix; 177 | uint8_t PrefixLen; 178 | 179 | uint32_t AdvValidLifetime; 180 | uint32_t curr_validlft; 181 | 182 | struct NAT64Prefix *next; 183 | }; 184 | 185 | struct AutogenIgnorePrefix { 186 | struct in6_addr Prefix; 187 | struct in6_addr Mask; 188 | 189 | struct AutogenIgnorePrefix *next; 190 | }; 191 | 192 | /* More-Specific Routes extensions */ 193 | 194 | struct AdvRoute { 195 | struct in6_addr Prefix; 196 | uint8_t PrefixLen; 197 | 198 | int AdvRoutePreference; 199 | uint32_t AdvRouteLifetime; 200 | int RemoveRouteFlag; 201 | 202 | struct AdvRoute *next; 203 | }; 204 | 205 | /* Options for DNS configuration */ 206 | 207 | struct AdvRDNSS { 208 | int AdvRDNSSNumber; 209 | uint32_t AdvRDNSSLifetime; 210 | int FlushRDNSSFlag; 211 | struct in6_addr *AdvRDNSSAddr; 212 | 213 | struct AdvRDNSS *next; 214 | }; 215 | 216 | struct AdvDNSSL { 217 | uint32_t AdvDNSSLLifetime; 218 | 219 | int AdvDNSSLNumber; 220 | int FlushDNSSLFlag; 221 | char **AdvDNSSLSuffixes; 222 | 223 | struct AdvDNSSL *next; 224 | }; 225 | 226 | /* Options for 6lopan configuration */ 227 | 228 | struct AdvLowpanCo { 229 | uint8_t ContextLength; 230 | uint8_t ContextCompressionFlag; 231 | uint8_t AdvContextID; 232 | uint16_t AdvLifeTime; 233 | struct in6_addr AdvContextPrefix; 234 | 235 | struct AdvLowpanCo *next; 236 | }; 237 | 238 | struct AdvAbro { 239 | uint16_t Version[2]; 240 | uint16_t ValidLifeTime; 241 | struct in6_addr LBRaddress; 242 | 243 | struct AdvAbro *next; 244 | }; 245 | 246 | struct AdvRASrcAddress { 247 | struct in6_addr address; 248 | 249 | struct AdvRASrcAddress *next; 250 | }; 251 | 252 | /* Mobile IPv6 extensions */ 253 | 254 | struct AdvInterval { 255 | uint8_t type; 256 | uint8_t length; 257 | uint16_t reserved; 258 | uint32_t adv_ival; 259 | }; 260 | 261 | struct HomeAgentInfo { 262 | uint8_t type; 263 | uint8_t length; 264 | uint16_t flags_reserved; 265 | uint16_t preference; 266 | uint16_t lifetime; 267 | }; 268 | 269 | /* Uclibc : include/netinet/icmpv6.h - Added by Bhadram*/ 270 | #define ND_OPT_ARO 33 271 | #define ND_OPT_6CO 34 272 | #define ND_OPT_ABRO 35 273 | 274 | struct nd_opt_abro { 275 | uint8_t nd_opt_abro_type; 276 | uint8_t nd_opt_abro_len; 277 | uint16_t nd_opt_abro_ver_low; 278 | uint16_t nd_opt_abro_ver_high; 279 | uint16_t nd_opt_abro_valid_lifetime; 280 | struct in6_addr nd_opt_abro_6lbr_address; 281 | }; 282 | 283 | struct nd_opt_6co { 284 | uint8_t nd_opt_6co_type; 285 | uint8_t nd_opt_6co_len; 286 | uint8_t nd_opt_6co_context_len; 287 | uint8_t nd_opt_6co_res_c_cid; /* [ res=3-bits | c=1-bit | cid=4-bits ] */ 288 | uint16_t nd_opt_6co_reserved; 289 | uint16_t nd_opt_6co_valid_lifetime; 290 | struct in6_addr nd_opt_6co_con_prefix; 291 | }; /*Added by Bhadram */ 292 | 293 | /* Pref64 option type (RFC8781, section 4) */ 294 | #ifndef ND_OPT_PREF64 295 | #define ND_OPT_PREF64 38 296 | #endif 297 | 298 | struct nd_opt_nat64prefix_info { 299 | uint8_t nd_opt_pi_type; 300 | uint8_t nd_opt_pi_len; 301 | uint16_t nd_opt_pi_lifetime_preflen; 302 | unsigned char nd_opt_pi_nat64prefix[12]; 303 | }; 304 | 305 | /* gram.y */ 306 | struct Interface *readin_config(char const *fname); 307 | 308 | /* radvd.c */ 309 | 310 | /* timer.c */ 311 | int expired(struct Interface const *iface); 312 | int64_t timespecdiff(struct timespec const *a, struct timespec const *b); 313 | struct timespec next_timespec(double next); 314 | uint64_t next_time_msec(struct Interface const *iface); 315 | 316 | /* device.c */ 317 | int check_device(int sock, struct Interface *); 318 | int check_ip6_forwarding(void); 319 | int check_ip6_iface_forwarding(const char *iface); 320 | int get_v4addr(const char *, unsigned int *); 321 | int set_interface_curhlim(const char *, uint8_t); 322 | int set_interface_linkmtu(const char *, uint32_t); 323 | int set_interface_reachtime(const char *, uint32_t); 324 | int set_interface_retranstimer(const char *, uint32_t); 325 | int setup_allrouters_membership(int sock, struct Interface *); 326 | int cleanup_allrouters_membership(int sock, struct Interface *iface); 327 | int setup_iface_addrs(struct Interface *); 328 | int update_device_index(struct Interface *iface); 329 | int update_device_info(int sock, struct Interface *); 330 | int get_iface_addrs(char const *name, struct in6_addr *if_addr, /* the first link local addr */ 331 | struct in6_addr **if_addrs /* all the addrs */ 332 | ); 333 | 334 | /* interface.c */ 335 | int check_iface(struct Interface *); 336 | int setup_iface(int sock, struct Interface *iface); 337 | int cleanup_iface(int sock, struct Interface *iface); 338 | struct Interface *find_iface_by_index(struct Interface *iface, int index); 339 | struct Interface *find_iface_by_name(struct Interface *iface, const char *name); 340 | struct Interface *find_iface_by_time(struct Interface *iface_list); 341 | void dnssl_init_defaults(struct AdvDNSSL *, struct Interface *); 342 | void for_each_iface(struct Interface *ifaces, void (*foo)(struct Interface *iface, void *), void *data); 343 | void free_ifaces(struct Interface *ifaces); 344 | void nat64prefix_init_defaults(struct NAT64Prefix *, struct Interface *); 345 | void iface_init_defaults(struct Interface *); 346 | void prefix_init_defaults(struct AdvPrefix *); 347 | void rdnss_init_defaults(struct AdvRDNSS *, struct Interface *); 348 | void reschedule_iface(struct Interface *iface, double next); 349 | void route_init_defaults(struct AdvRoute *, struct Interface *); 350 | void touch_iface(struct Interface *iface); 351 | 352 | /* socket.c */ 353 | int open_icmpv6_socket(void); 354 | 355 | /* send.c */ 356 | int send_ra_forall(int sock, struct Interface *iface, struct in6_addr *dest); 357 | 358 | /* process.c */ 359 | void process(int sock, struct Interface *, unsigned char *, int, struct sockaddr_in6 *, struct in6_pktinfo *, int); 360 | 361 | /* recv.c */ 362 | int recv_rs_ra(int sock, unsigned char *, struct sockaddr_in6 *, struct in6_pktinfo **, int *, unsigned char *); 363 | 364 | /* util.c */ 365 | int countbits(int b); 366 | int count_mask(struct sockaddr_in6 *m); 367 | struct in6_addr get_prefix6(struct in6_addr const *addr, struct in6_addr const *mask); 368 | char *strdupf(char const *format, ...) __attribute__((format(printf, 1, 2))); 369 | double rand_between(double, double); 370 | int check_dnssl_presence(struct AdvDNSSL *, const char *); 371 | int check_rdnss_presence(struct AdvRDNSS *, struct in6_addr *); 372 | void safe_buffer_resize(struct safe_buffer *sb, size_t new_capacity); 373 | size_t safe_buffer_append(struct safe_buffer *sb, void const *m, size_t count); 374 | size_t safe_buffer_pad(struct safe_buffer *sb, size_t count); 375 | ssize_t readn(int fd, void *buf, size_t count); 376 | ssize_t writen(int fd, const void *buf, size_t count); 377 | struct safe_buffer *new_safe_buffer(void); 378 | void addrtostr(struct in6_addr const *, char *, size_t); 379 | void safe_buffer_free(struct safe_buffer *sb); 380 | struct safe_buffer_list *new_safe_buffer_list(void); 381 | void safe_buffer_list_free(struct safe_buffer_list *sbl); 382 | struct safe_buffer_list *safe_buffer_list_append(struct safe_buffer_list *sbl); 383 | void safe_buffer_list_to_safe_buffer(struct safe_buffer_list *sbl, struct safe_buffer *sb); 384 | 385 | /* privsep.c */ 386 | int privsep_interface_curhlim(const char *iface, uint32_t hlim); 387 | int privsep_interface_linkmtu(const char *iface, uint32_t mtu); 388 | int privsep_interface_reachtime(const char *iface, uint32_t rtime); 389 | int privsep_interface_retranstimer(const char *iface, uint32_t rettimer); 390 | void privsep_init(int); 391 | void privsep_set_write_fd(int); 392 | 393 | /* 394 | * compat hacks in case libc and kernel get out of sync: 395 | * 396 | * glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and change IPV6_PKTINFO 397 | * This is only supported in Linux kernel >= 2.6.14 398 | * 399 | * This is only an approximation because the kernel version that libc was compiled against 400 | * could be older or newer than the one being run. But this should not be a problem -- 401 | * we just keep using the old kernel interface. 402 | * 403 | * these are placed here because they're needed in all of socket.c, recv.c and send.c 404 | */ 405 | #ifdef __linux__ 406 | #if defined IPV6_RECVHOPLIMIT || defined IPV6_RECVPKTINFO 407 | #include 408 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) 409 | #if defined IPV6_RECVHOPLIMIT && defined IPV6_2292HOPLIMIT 410 | #undef IPV6_RECVHOPLIMIT 411 | #define IPV6_RECVHOPLIMIT IPV6_2292HOPLIMIT 412 | #endif 413 | #if defined IPV6_RECVPKTINFO && defined IPV6_2292PKTINFO 414 | #undef IPV6_RECVPKTINFO 415 | #undef IPV6_PKTINFO 416 | #define IPV6_RECVPKTINFO IPV6_2292PKTINFO 417 | #define IPV6_PKTINFO IPV6_2292PKTINFO 418 | #endif 419 | #endif 420 | #endif 421 | #endif 422 | -------------------------------------------------------------------------------- /device-linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "defaults.h" 17 | #include "includes.h" 18 | #include "netlink.h" 19 | #include "pathnames.h" 20 | #include "radvd.h" 21 | 22 | #ifndef IPV6_ADDR_LINKLOCAL 23 | #define IPV6_ADDR_LINKLOCAL 0x0020U 24 | #endif 25 | 26 | #ifndef ARPHRD_6LOWPAN 27 | #define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ 28 | #endif 29 | 30 | static char const *hwstr(unsigned short sa_family); 31 | uint32_t get_interface_linkmtu(const char *); 32 | 33 | /* 34 | * this function gets the hardware type and address of an interface, 35 | * determines the link layer token length and checks it against 36 | * the defined prefixes 37 | */ 38 | int update_device_info(int sock, struct Interface *iface) 39 | { 40 | struct ifreq ifr; 41 | memset(&ifr, 0, sizeof(ifr)); 42 | strlcpy(ifr.ifr_name, iface->props.name, sizeof(ifr.ifr_name)); 43 | 44 | if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) { 45 | flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed on %s: %s", iface->props.name, strerror(errno)); 46 | return -1; 47 | } 48 | 49 | iface->sllao.if_maxmtu = ifr.ifr_mtu; 50 | dlog(LOG_DEBUG, 3, "%s mtu: %d", iface->props.name, ifr.ifr_mtu); 51 | 52 | /* RFC 2460: 5. Packet Size Issues */ 53 | /* Get the smallest MTU between the SIOCGIFMTU value and the protocol MTU 54 | * /proc/sys/net/ipv6/conf/eth0/mtu 55 | * Because the protocol MTU _may_ be different than the physical link MTU 56 | * 57 | * If Link-layer MTU <= 1280: use 1280 (enforced by iface->AdvRAMTU) 58 | * If Link-layer MTU > 1280: use the lower of: 59 | * - RA MTU 60 | * - link-layer MTU 61 | * - per-protocol MTU 62 | */ 63 | iface->props.max_ra_option_size = iface->AdvRAMTU; 64 | iface->props.max_ra_option_size = MIN(iface->props.max_ra_option_size, MAX(iface->sllao.if_maxmtu, RFC2460_MIN_MTU)); 65 | iface->props.max_ra_option_size = 66 | MIN(iface->props.max_ra_option_size, MAX(get_interface_linkmtu(iface->props.name), RFC2460_MIN_MTU)); 67 | 68 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { 69 | flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed on %s: %s", iface->props.name, strerror(errno)); 70 | return -1; 71 | } 72 | 73 | dlog(LOG_DEBUG, 3, "%s hardware type: %s", iface->props.name, hwstr(ifr.ifr_hwaddr.sa_family)); 74 | 75 | switch (ifr.ifr_hwaddr.sa_family) { 76 | case ARPHRD_ETHER: 77 | iface->sllao.if_hwaddr_len = 48; 78 | iface->sllao.if_prefix_len = 64; 79 | char hwaddr[3 * 6]; 80 | sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr.ifr_hwaddr.sa_data[0], 81 | (unsigned char)ifr.ifr_hwaddr.sa_data[1], (unsigned char)ifr.ifr_hwaddr.sa_data[2], 82 | (unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4], 83 | (unsigned char)ifr.ifr_hwaddr.sa_data[5]); 84 | dlog(LOG_DEBUG, 3, "%s hardware address: %s", iface->props.name, hwaddr); 85 | iface->props.max_ra_option_size -= 14; /* RFC 2464 */ 86 | break; 87 | #ifdef ARPHRD_FDDI 88 | case ARPHRD_FDDI: 89 | iface->sllao.if_hwaddr_len = 48; 90 | iface->sllao.if_prefix_len = 64; 91 | iface->props.max_ra_option_size -= 22; /* RFC 2109 */ 92 | break; 93 | #endif /* ARPHDR_FDDI */ 94 | #ifdef ARPHRD_ARCNET 95 | case ARPHRD_ARCNET: 96 | iface->sllao.if_hwaddr_len = 8; 97 | iface->sllao.if_prefix_len = -1; 98 | iface->sllao.if_maxmtu = -1; 99 | /* RFC1201: fragmentation handled at a lower layer. 100 | * native packet size is 256-512 bytes */ 101 | iface->props.max_ra_option_size -= 0; 102 | break; 103 | #endif /* ARPHDR_ARCNET */ 104 | case ARPHRD_6LOWPAN: 105 | #ifdef HAVE_NETLINK 106 | /* hwaddr length differs on some L2 type lets detect them */ 107 | iface->sllao.if_hwaddr_len = netlink_get_device_addr_len(iface); 108 | if (iface->sllao.if_hwaddr_len != -1) { 109 | iface->sllao.if_hwaddr_len *= 8; 110 | iface->sllao.if_prefix_len = 64; 111 | } else { 112 | iface->sllao.if_prefix_len = -1; 113 | } 114 | #else 115 | iface->sllao.if_hwaddr_len = -1; 116 | iface->sllao.if_prefix_len = -1; 117 | #endif 118 | /* RFC4944: fragmentation handled at a lower layer. 119 | * native packet size maxes at 127 bytes */ 120 | iface->props.max_ra_option_size -= 0; 121 | break; 122 | default: 123 | iface->sllao.if_hwaddr_len = -1; 124 | iface->sllao.if_prefix_len = -1; 125 | iface->sllao.if_maxmtu = -1; 126 | /* Assume fragmentation handled at a lower layer. */ 127 | iface->props.max_ra_option_size -= 0; 128 | break; 129 | } 130 | 131 | dlog(LOG_DEBUG, 3, "%s link layer token length: %d", iface->props.name, iface->sllao.if_hwaddr_len); 132 | 133 | dlog(LOG_DEBUG, 3, "%s prefix length: %d", iface->props.name, iface->sllao.if_prefix_len); 134 | 135 | if (iface->sllao.if_hwaddr_len != -1) { 136 | unsigned int if_hwaddr_len_bytes = (iface->sllao.if_hwaddr_len + 7) >> 3; 137 | 138 | if (if_hwaddr_len_bytes > sizeof(iface->sllao.if_hwaddr)) { 139 | flog(LOG_ERR, "%s address length too big: %d", iface->props.name, if_hwaddr_len_bytes); 140 | return -2; 141 | } 142 | memcpy(iface->sllao.if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes); 143 | 144 | char zero[sizeof(iface->props.if_addr)]; 145 | memset(zero, 0, sizeof(zero)); 146 | if (!memcmp(iface->sllao.if_hwaddr, zero, if_hwaddr_len_bytes)) 147 | flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!", iface->props.name); 148 | } 149 | 150 | struct AdvPrefix *prefix = iface->AdvPrefixList; 151 | while (prefix) { 152 | if ((iface->sllao.if_prefix_len != -1) && (iface->sllao.if_prefix_len != prefix->PrefixLen)) { 153 | flog(LOG_WARNING, "%s prefix length should be: %d", iface->props.name, iface->sllao.if_prefix_len); 154 | } 155 | 156 | prefix = prefix->next; 157 | } 158 | 159 | // Regardless of link-layer, every RA message will have an IPV6 header & RA header 160 | iface->props.max_ra_option_size -= sizeof(struct ip6_hdr); 161 | iface->props.max_ra_option_size -= sizeof(struct nd_router_advert); 162 | 163 | return 0; 164 | } 165 | 166 | int setup_allrouters_membership(int sock, struct Interface *iface) 167 | { 168 | struct ipv6_mreq mreq; 169 | 170 | memset(&mreq, 0, sizeof(mreq)); 171 | mreq.ipv6mr_interface = iface->props.if_index; 172 | 173 | /* ipv6-allrouters: ff02::2 */ 174 | mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000); 175 | mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2); 176 | 177 | if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 178 | /* linux-2.6.12-bk4 returns error with HUP signal but keep listening */ 179 | if (errno != EADDRINUSE) { 180 | flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->props.name); 181 | return -1; 182 | } 183 | } 184 | 185 | return 0; 186 | } 187 | 188 | int cleanup_allrouters_membership(int sock, struct Interface *iface) 189 | { 190 | struct ipv6_mreq mreq; 191 | 192 | memset(&mreq, 0, sizeof(mreq)); 193 | mreq.ipv6mr_interface = iface->props.if_index; 194 | 195 | /* ipv6-allrouters: ff02::2 */ 196 | mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000); 197 | mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2); 198 | 199 | setsockopt(sock, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 200 | 201 | return 0; 202 | } 203 | 204 | uint32_t get_interface_linkmtu(const char *iface) 205 | { 206 | int value; 207 | FILE *fp = NULL; 208 | char proc_path[sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ]; 209 | 210 | snprintf(proc_path, sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ, PROC_SYS_IP6_LINKMTU, iface); 211 | 212 | fp = fopen(proc_path, "r"); 213 | if (fp) { 214 | int rc = fscanf(fp, "%d", &value); 215 | if (rc != 1) { 216 | flog(LOG_ERR, "cannot read value from %s: %s", proc_path, strerror(errno)); 217 | exit(1); 218 | } 219 | fclose(fp); 220 | } else { 221 | flog(LOG_DEBUG, "Correct IPv6 MTU entry not found, " 222 | "perhaps the procfs is disabled, " 223 | "or the kernel interface has changed?"); 224 | value = 1280; /* RFC2460: section 5 */ 225 | } 226 | 227 | return value; 228 | } 229 | 230 | int set_interface_linkmtu(const char *iface, uint32_t mtu) { return privsep_interface_linkmtu(iface, mtu); } 231 | 232 | int set_interface_curhlim(const char *iface, uint8_t hlim) { return privsep_interface_curhlim(iface, hlim); } 233 | 234 | int set_interface_reachtime(const char *iface, uint32_t rtime) { return privsep_interface_reachtime(iface, rtime); } 235 | 236 | int set_interface_retranstimer(const char *iface, uint32_t rettimer) { return privsep_interface_retranstimer(iface, rettimer); } 237 | 238 | int check_ip6_iface_forwarding(const char *iface) 239 | { 240 | int value = -1; 241 | FILE *fp = NULL; 242 | char path[sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ]; 243 | 244 | snprintf(path, sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ, PROC_SYS_IP6_IFACE_FORWARDING, iface); 245 | 246 | fp = fopen(path, "r"); 247 | if (fp) { 248 | int rc = fscanf(fp, "%d", &value); 249 | if (rc != 1) { 250 | flog(LOG_ERR, "cannot read value from %s: %s", path, strerror(errno)); 251 | exit(1); 252 | } 253 | fclose(fp); 254 | } else { 255 | flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry for interface " 256 | "not found, perhaps the procfs is disabled, " 257 | "or the kernel interface has changed?"); 258 | value = -1; 259 | } 260 | 261 | return value; 262 | } 263 | 264 | int check_ip6_forwarding(void) 265 | { 266 | int value; 267 | FILE *fp = NULL; 268 | 269 | fp = fopen(PROC_SYS_IP6_FORWARDING, "r"); 270 | if (fp) { 271 | int rc = fscanf(fp, "%d", &value); 272 | if (rc != 1) { 273 | flog(LOG_ERR, "cannot read value from %s: %s", PROC_SYS_IP6_FORWARDING, strerror(errno)); 274 | exit(1); 275 | } 276 | fclose(fp); 277 | } else { 278 | flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, " 279 | "perhaps the procfs is disabled, " 280 | "or the kernel interface has changed?"); 281 | value = -1; 282 | } 283 | 284 | #ifdef HAVE_SYSCTL 285 | int forw_sysctl[] = {SYSCTL_IP6_FORWARDING}; 286 | size_t size = sizeof(value); 287 | if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl) / sizeof(forw_sysctl[0]), &value, &size, NULL, 0) < 0) { 288 | flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, " 289 | "perhaps the kernel interface has changed?"); 290 | return 0; /* this is of advisory value only */ 291 | } 292 | #endif 293 | 294 | /* Linux allows the forwarding value to be either 1 or 2. 295 | * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/ip-sysctl.txt?id=ae8abfa00efb8ec550f772cbd1e1854977d06212#n1078 296 | * 297 | * The value 2 indicates forwarding is enabled and that *AS* *WELL* router solicitations are being done. 298 | * 299 | * Which is sometimes used on routers performing RS on their WAN (ppp, etc.) links 300 | */ 301 | static int warned = 0; 302 | if (!warned && value != 1 && value != 2) { 303 | warned = 1; 304 | flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1 or 2", value); 305 | return -1; 306 | } 307 | 308 | return 0; 309 | } 310 | 311 | static char const *hwstr(unsigned short sa_family) 312 | { 313 | char const *rc = 0; 314 | 315 | switch (sa_family) { 316 | case ARPHRD_NETROM: 317 | rc = "ARPHRD_NETROM"; 318 | break; 319 | case ARPHRD_ETHER: 320 | rc = "ARPHRD_ETHER"; 321 | break; 322 | case ARPHRD_EETHER: 323 | rc = "ARPHRD_EETHER"; 324 | break; 325 | case ARPHRD_AX25: 326 | rc = "ARPHRD_AX25"; 327 | break; 328 | case ARPHRD_PRONET: 329 | rc = "ARPHRD_PRONET"; 330 | break; 331 | case ARPHRD_CHAOS: 332 | rc = "ARPHRD_CHAOS"; 333 | break; 334 | case ARPHRD_IEEE802: 335 | rc = "ARPHRD_IEEE802"; 336 | break; 337 | case ARPHRD_APPLETLK: 338 | rc = "ARPHRD_APPLETLK"; 339 | break; 340 | case ARPHRD_DLCI: 341 | rc = "ARPHRD_DLCI"; 342 | break; 343 | case ARPHRD_ATM: 344 | rc = "ARPHRD_ATM"; 345 | break; 346 | case ARPHRD_METRICOM: 347 | rc = "ARPHRD_METRICOM"; 348 | break; 349 | case ARPHRD_IEEE1394: 350 | rc = "ARPHRD_IEEE1394"; 351 | break; 352 | case ARPHRD_EUI64: 353 | rc = "ARPHRD_EUI64"; 354 | break; 355 | case ARPHRD_INFINIBAND: 356 | rc = "ARPHRD_INFINIBAND"; 357 | break; 358 | case ARPHRD_SLIP: 359 | rc = "ARPHRD_SLIP"; 360 | break; 361 | case ARPHRD_CSLIP: 362 | rc = "ARPHRD_CSLIP"; 363 | break; 364 | case ARPHRD_SLIP6: 365 | rc = "ARPHRD_SLIP6"; 366 | break; 367 | case ARPHRD_CSLIP6: 368 | rc = "ARPHRD_CSLIP6"; 369 | break; 370 | case ARPHRD_RSRVD: 371 | rc = "ARPHRD_RSRVD"; 372 | break; 373 | case ARPHRD_ADAPT: 374 | return "ARPHRD_ADAPT"; 375 | break; 376 | case ARPHRD_ROSE: 377 | rc = "ARPHRD_ROSE"; 378 | break; 379 | case ARPHRD_X25: 380 | rc = "ARPHRD_X25"; 381 | break; 382 | case ARPHRD_HWX25: 383 | rc = "ARPHRD_HWX25"; 384 | break; 385 | case ARPHRD_PPP: 386 | rc = "ARPHRD_PPP"; 387 | break; 388 | case ARPHRD_CISCO: 389 | rc = "ARPHRD_CISCO"; 390 | break; 391 | case ARPHRD_LAPB: 392 | rc = "ARPHRD_LAPB"; 393 | break; 394 | case ARPHRD_DDCMP: 395 | rc = "ARPHRD_DDCMP"; 396 | break; 397 | case ARPHRD_RAWHDLC: 398 | rc = "ARPHRD_RAWHDLC"; 399 | break; 400 | case ARPHRD_TUNNEL: 401 | rc = "ARPHRD_TUNNEL"; 402 | break; 403 | case ARPHRD_TUNNEL6: 404 | rc = "ARPHRD_TUNNEL6"; 405 | break; 406 | case ARPHRD_FRAD: 407 | rc = "ARPHRD_FRAD"; 408 | break; 409 | case ARPHRD_SKIP: 410 | rc = "ARPHRD_SKIP"; 411 | break; 412 | case ARPHRD_LOOPBACK: 413 | rc = "ARPHRD_LOOPBACK"; 414 | break; 415 | case ARPHRD_LOCALTLK: 416 | rc = "ARPHRD_LOCALTLK"; 417 | break; 418 | case ARPHRD_BIF: 419 | rc = "ARPHRD_BIF"; 420 | break; 421 | case ARPHRD_SIT: 422 | rc = "ARPHRD_SIT"; 423 | break; 424 | case ARPHRD_IPDDP: 425 | rc = "ARPHRD_IPDDP"; 426 | break; 427 | case ARPHRD_IPGRE: 428 | rc = "ARPHRD_IPGRE"; 429 | break; 430 | case ARPHRD_PIMREG: 431 | rc = "ARPHRD_PIMREG"; 432 | break; 433 | case ARPHRD_HIPPI: 434 | rc = "ARPHRD_HIPPI"; 435 | break; 436 | case ARPHRD_ASH: 437 | rc = "ARPHRD_ASH"; 438 | break; 439 | case ARPHRD_ECONET: 440 | rc = "ARPHRD_ECONET"; 441 | break; 442 | case ARPHRD_IRDA: 443 | rc = "ARPHRD_IRDA"; 444 | break; 445 | case ARPHRD_FCPP: 446 | rc = "ARPHRD_FCPP"; 447 | break; 448 | case ARPHRD_FCAL: 449 | rc = "ARPHRD_FCAL"; 450 | break; 451 | case ARPHRD_FCPL: 452 | rc = "ARPHRD_FCPL"; 453 | break; 454 | case ARPHRD_FCFABRIC: 455 | rc = "ARPHRD_FCFABRIC"; 456 | break; 457 | case ARPHRD_IEEE802_TR: 458 | rc = "ARPHRD_IEEE802_TR"; 459 | break; 460 | case ARPHRD_IEEE80211: 461 | rc = "ARPHRD_IEEE80211"; 462 | break; 463 | case ARPHRD_IEEE80211_PRISM: 464 | rc = "ARPHRD_IEEE80211_PRISM"; 465 | break; 466 | case ARPHRD_IEEE80211_RADIOTAP: 467 | rc = "ARPHRD_IEEE80211_RADIOTAP"; 468 | break; 469 | case ARPHRD_IEEE802154: 470 | rc = "ARPHRD_IEEE802154"; 471 | break; 472 | #if ARPHRD_IEEE802154_MONITOR 473 | case ARPHRD_IEEE802154_MONITOR: 474 | rc = "ARPHRD_IEEE802154_MONITOR"; 475 | break; 476 | #elif ARPHRD_IEEE802154_PHY 477 | case ARPHRD_IEEE802154_PHY: 478 | rc = "ARPHRD_IEEE802154_PHY"; 479 | break; 480 | #endif 481 | case ARPHRD_6LOWPAN: 482 | rc = "ARPHRD_6LOWPAN"; 483 | break; 484 | case ARPHRD_VOID: 485 | rc = "ARPHRD_VOID"; 486 | break; 487 | case ARPHRD_NONE: 488 | rc = "ARPHRD_NONE"; 489 | break; 490 | default: 491 | rc = "unknown"; 492 | break; 493 | } 494 | 495 | return rc; 496 | } 497 | -------------------------------------------------------------------------------- /interface.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Authors: 4 | * Lars Fenneberg 5 | * 6 | * This software is Copyright 1996,1997 by the above mentioned author(s), 7 | * All Rights Reserved. 8 | * 9 | * The license which is distributed with this software in the file COPYRIGHT 10 | * applies to this software. If your distribution is missing this file, you 11 | * may request it from . 12 | * 13 | */ 14 | 15 | #include "config.h" 16 | #include "defaults.h" 17 | #include "includes.h" 18 | #include "radvd.h" 19 | 20 | #define IFACE_SETUP_DELAY 1 21 | 22 | void iface_init_defaults(struct Interface *iface) 23 | { 24 | memset(iface, 0, sizeof(struct Interface)); 25 | 26 | iface->state_info.changed = 1; 27 | 28 | iface->IgnoreIfMissing = DFLT_IgnoreIfMissing; 29 | iface->AdvSendAdvert = DFLT_AdvSendAdv; 30 | iface->MaxRtrAdvInterval = DFLT_MaxRtrAdvInterval; 31 | iface->AdvSourceLLAddress = DFLT_AdvSourceLLAddress; 32 | iface->RemoveAdvOnExit = DFLT_RemoveAdvOnExit; 33 | iface->MinDelayBetweenRAs = DFLT_MinDelayBetweenRAs; 34 | iface->MinRtrAdvInterval = -1; 35 | iface->UnicastOnly = DFLT_UnicastOnly; 36 | iface->UnrestrictedUnicast = DFLT_UnrestrictedUnicast; 37 | iface->AdvRASolicitedUnicast = DFLT_AdvRASolicitedUnicast; 38 | 39 | iface->ra_header_info.AdvDefaultPreference = DFLT_AdvDefaultPreference; 40 | iface->ra_header_info.AdvDefaultLifetime = -1; 41 | iface->ra_header_info.AdvReachableTime = DFLT_AdvReachableTime; 42 | iface->ra_header_info.AdvRetransTimer = DFLT_AdvRetransTimer; 43 | iface->ra_header_info.AdvCurHopLimit = DFLT_AdvCurHopLimit; 44 | iface->ra_header_info.AdvHomeAgentFlag = DFLT_AdvHomeAgentFlag; 45 | 46 | iface->mipv6.AdvIntervalOpt = DFLT_AdvIntervalOpt; 47 | iface->mipv6.AdvHomeAgentInfo = DFLT_AdvHomeAgentInfo; 48 | iface->mipv6.HomeAgentPreference = DFLT_HomeAgentPreference; 49 | iface->mipv6.AdvMobRtrSupportFlag = DFLT_AdvMobRtrSupportFlag; 50 | iface->mipv6.HomeAgentLifetime = -1; 51 | 52 | iface->AdvLinkMTU = DFLT_AdvLinkMTU; 53 | iface->AdvRAMTU = DFLT_AdvRAMTU; 54 | } 55 | 56 | void touch_iface(struct Interface *iface) 57 | { 58 | iface->state_info.changed = 1; 59 | iface->state_info.ready = 0; 60 | iface->state_info.racount = 0; 61 | reschedule_iface(iface, 0); 62 | } 63 | 64 | int setup_iface(int sock, struct Interface *iface) 65 | { 66 | iface->state_info.changed = 0; 67 | iface->state_info.ready = 0; 68 | 69 | /* The device index must be setup first so we can search it later */ 70 | if (update_device_index(iface) < 0) { 71 | return -1; 72 | } 73 | 74 | /* Check IFF_UP, IFF_RUNNING and IFF_MULTICAST */ 75 | if (check_device(sock, iface) < 0) { 76 | return -2; 77 | } 78 | 79 | /* Set iface->max_mtu and iface hardware address */ 80 | if (update_device_info(sock, iface) < 0) { 81 | return -3; 82 | } 83 | 84 | /* Make sure the settings in the config file for this interface are ok (this depends 85 | * on iface->max_mtu already being set). */ 86 | if (check_iface(iface) < 0) { 87 | return -4; 88 | } 89 | 90 | /* Save the first link local address seen on the specified interface to 91 | * iface->props.if_addr and keep a list off all addrs in iface->props.if_addrs */ 92 | if (setup_iface_addrs(iface) < 0) { 93 | return -5; 94 | } 95 | 96 | /* Check if we a usable RA source address */ 97 | if (iface->props.if_addr_rasrc == NULL) { 98 | dlog(LOG_DEBUG, 5, "no configured AdvRASrcAddress present, skipping send"); 99 | return -6; 100 | } 101 | 102 | /* join the allrouters multicast group so we get the solicitations */ 103 | if (setup_allrouters_membership(sock, iface) < 0) { 104 | return -7; 105 | } 106 | 107 | iface->state_info.ready = 1; 108 | 109 | dlog(LOG_DEBUG, 4, "%s is ready", iface->props.name); 110 | 111 | return 0; 112 | } 113 | 114 | int cleanup_iface(int sock, struct Interface *iface) 115 | { 116 | /* leave the allrouters multicast group */ 117 | cleanup_allrouters_membership(sock, iface); 118 | return 0; 119 | } 120 | void prefix_init_defaults(struct AdvPrefix *prefix) 121 | { 122 | memset(prefix, 0, sizeof(struct AdvPrefix)); 123 | 124 | prefix->AdvOnLinkFlag = DFLT_AdvOnLinkFlag; 125 | prefix->AdvAutonomousFlag = DFLT_AdvAutonomousFlag; 126 | prefix->AdvRouterAddr = DFLT_AdvRouterAddr; 127 | prefix->AdvValidLifetime = DFLT_AdvValidLifetime; 128 | prefix->AdvPreferredLifetime = DFLT_AdvPreferredLifetime; 129 | prefix->DeprecatePrefixFlag = DFLT_DeprecatePrefixFlag; 130 | prefix->DecrementLifetimesFlag = DFLT_DecrementLifetimesFlag; 131 | 132 | prefix->curr_validlft = prefix->AdvValidLifetime; 133 | prefix->curr_preferredlft = prefix->AdvPreferredLifetime; 134 | } 135 | 136 | void nat64prefix_init_defaults(struct NAT64Prefix *prefix, struct Interface *iface) 137 | { 138 | memset(prefix, 0, sizeof(struct NAT64Prefix)); 139 | 140 | prefix->AdvValidLifetime = min(DFLT_NAT64MaxValidLifetime, 3*(iface->MaxRtrAdvInterval)); 141 | 142 | prefix->curr_validlft = prefix->AdvValidLifetime; 143 | } 144 | 145 | void route_init_defaults(struct AdvRoute *route, struct Interface *iface) 146 | { 147 | memset(route, 0, sizeof(struct AdvRoute)); 148 | 149 | route->AdvRouteLifetime = DFLT_AdvRouteLifetime(iface); 150 | route->AdvRoutePreference = DFLT_AdvRoutePreference; 151 | route->RemoveRouteFlag = DFLT_RemoveRouteFlag; 152 | } 153 | 154 | void rdnss_init_defaults(struct AdvRDNSS *rdnss, struct Interface *iface) 155 | { 156 | memset(rdnss, 0, sizeof(struct AdvRDNSS)); 157 | 158 | rdnss->AdvRDNSSLifetime = DFLT_AdvRDNSSLifetime(iface); 159 | rdnss->AdvRDNSSNumber = 0; 160 | rdnss->FlushRDNSSFlag = DFLT_FlushRDNSSFlag; 161 | } 162 | 163 | void dnssl_init_defaults(struct AdvDNSSL *dnssl, struct Interface *iface) 164 | { 165 | memset(dnssl, 0, sizeof(struct AdvDNSSL)); 166 | 167 | dnssl->AdvDNSSLLifetime = DFLT_AdvDNSSLLifetime(iface); 168 | dnssl->FlushDNSSLFlag = DFLT_FlushDNSSLFlag; 169 | } 170 | 171 | int check_iface(struct Interface *iface) 172 | { 173 | int res = 0; 174 | int MIPv6 = 0; 175 | struct in6_addr zeroaddr; 176 | memset(&zeroaddr, 0, sizeof(zeroaddr)); 177 | 178 | /* Check if we use Mobile IPv6 extensions */ 179 | if (iface->ra_header_info.AdvHomeAgentFlag || iface->mipv6.AdvHomeAgentInfo || iface->mipv6.AdvIntervalOpt) { 180 | MIPv6 = 1; 181 | flog(LOG_INFO, "using Mobile IPv6 extensions"); 182 | } 183 | 184 | /* Check forwarding on interface */ 185 | if (check_ip6_iface_forwarding(iface->props.name) < 1) { 186 | flog(LOG_WARNING, "IPv6 forwarding on interface seems to be disabled, but continuing anyway"); 187 | } 188 | 189 | struct AdvPrefix *prefix = iface->AdvPrefixList; 190 | while (!MIPv6 && prefix) { 191 | if (prefix->AdvRouterAddr) { 192 | MIPv6 = 1; 193 | } 194 | prefix = prefix->next; 195 | } 196 | 197 | if (iface->MinRtrAdvInterval < 0) 198 | iface->MinRtrAdvInterval = DFLT_MinRtrAdvInterval(iface); 199 | 200 | if ((iface->MinRtrAdvInterval < (MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : MIN_MinRtrAdvInterval)) || 201 | (iface->MinRtrAdvInterval > MAX_MinRtrAdvInterval(iface))) { 202 | flog(LOG_ERR, 203 | "MinRtrAdvInterval for %s (%.2f) must be at least %.2f but no more than 3/4 of MaxRtrAdvInterval (%.2f)", 204 | iface->props.name, iface->MinRtrAdvInterval, 205 | MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : (int)MIN_MinRtrAdvInterval, MAX_MinRtrAdvInterval(iface)); 206 | res = -1; 207 | } 208 | 209 | if ((iface->MaxRtrAdvInterval < (MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : MIN_MaxRtrAdvInterval)) || 210 | (iface->MaxRtrAdvInterval > MAX_MaxRtrAdvInterval)) { 211 | flog(LOG_ERR, "MaxRtrAdvInterval for %s (%.2f) must be between %.2f and %d", iface->props.name, 212 | iface->MaxRtrAdvInterval, MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : (int)MIN_MaxRtrAdvInterval, 213 | MAX_MaxRtrAdvInterval); 214 | res = -1; 215 | } 216 | 217 | if (iface->MinDelayBetweenRAs < (MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS)) { 218 | flog(LOG_ERR, "MinDelayBetweenRAs for %s (%.2f) must be at least %.2f", iface->props.name, 219 | iface->MinDelayBetweenRAs, MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS); 220 | res = -1; 221 | } 222 | 223 | if ((iface->AdvLinkMTU != 0) && ((iface->AdvLinkMTU < MIN_AdvLinkMTU) || 224 | (iface->sllao.if_maxmtu != -1 && (iface->AdvLinkMTU > iface->sllao.if_maxmtu)))) { 225 | flog(LOG_ERR, "AdvLinkMTU for %s (%u) must be zero or between %u and %u", iface->props.name, iface->AdvLinkMTU, 226 | MIN_AdvLinkMTU, iface->sllao.if_maxmtu); 227 | res = -1; 228 | } 229 | 230 | if (iface->ra_header_info.AdvReachableTime > MAX_AdvReachableTime) { 231 | flog(LOG_ERR, "AdvReachableTime for %s (%u) must not be greater than %u", iface->props.name, 232 | iface->ra_header_info.AdvReachableTime, MAX_AdvReachableTime); 233 | res = -1; 234 | } 235 | 236 | if (iface->ra_header_info.AdvDefaultLifetime < 0) 237 | iface->ra_header_info.AdvDefaultLifetime = DFLT_AdvDefaultLifetime(iface); 238 | 239 | if ((iface->ra_header_info.AdvDefaultLifetime != 0) && 240 | ((iface->ra_header_info.AdvDefaultLifetime > MAX_AdvDefaultLifetime) || 241 | (iface->ra_header_info.AdvDefaultLifetime < MIN_AdvDefaultLifetime(iface)))) { 242 | flog(LOG_ERR, "AdvDefaultLifetime for %s (%u) must be zero or between %u and %u", iface->props.name, 243 | iface->ra_header_info.AdvDefaultLifetime, (int)MIN_AdvDefaultLifetime(iface), MAX_AdvDefaultLifetime); 244 | res = -1; 245 | } 246 | 247 | /* Mobile IPv6 ext */ 248 | if (iface->mipv6.HomeAgentLifetime < 0) 249 | iface->mipv6.HomeAgentLifetime = DFLT_HomeAgentLifetime(iface); 250 | 251 | /* Mobile IPv6 ext */ 252 | if (iface->mipv6.AdvHomeAgentInfo) { 253 | if ((iface->mipv6.HomeAgentLifetime > MAX_HomeAgentLifetime) || 254 | (iface->mipv6.HomeAgentLifetime < MIN_HomeAgentLifetime)) { 255 | flog(LOG_ERR, "HomeAgentLifetime for %s (%u) must be between %u and %u", iface->props.name, 256 | iface->mipv6.HomeAgentLifetime, MIN_HomeAgentLifetime, MAX_HomeAgentLifetime); 257 | res = -1; 258 | } 259 | } 260 | 261 | /* Mobile IPv6 ext */ 262 | if (iface->mipv6.AdvHomeAgentInfo && !(iface->ra_header_info.AdvHomeAgentFlag)) { 263 | flog(LOG_ERR, "AdvHomeAgentFlag for %s must be set with HomeAgentInfo", iface->props.name); 264 | res = -1; 265 | } 266 | if (iface->mipv6.AdvMobRtrSupportFlag && !(iface->mipv6.AdvHomeAgentInfo)) { 267 | flog(LOG_ERR, "AdvHomeAgentInfo for %s must be set with AdvMobRtrSupportFlag", iface->props.name); 268 | res = -1; 269 | } 270 | 271 | /* XXX: need this? prefix = iface->AdvPrefixList; */ 272 | 273 | while (prefix) { 274 | if (prefix->PrefixLen > MAX_PrefixLen) { 275 | flog(LOG_ERR, "invalid prefix length (%u) for %s", prefix->PrefixLen, iface->props.name); 276 | res = -1; 277 | } 278 | 279 | if (prefix->AdvPreferredLifetime > prefix->AdvValidLifetime) { 280 | flog(LOG_ERR, "AdvValidLifetime for %s (%u) must be " 281 | "greater than or equal to AdvPreferredLifetime for", 282 | iface->props.name, prefix->AdvValidLifetime); 283 | res = -1; 284 | } 285 | 286 | prefix = prefix->next; 287 | } 288 | 289 | struct AdvRoute *route = iface->AdvRouteList; 290 | while (route) { 291 | if (route->PrefixLen > MAX_PrefixLen) { 292 | flog(LOG_ERR, "invalid route prefix length (%u) for %s", route->PrefixLen, iface->props.name); 293 | res = -1; 294 | } 295 | 296 | /* For the default route 0::/0, we need to explicitly check the 297 | * lifetime against the AdvDefaultLifetime value. 298 | * 299 | * If exactly one of the two has a zero value, then nodes processing 300 | * the RA may have a flap in their default route. 301 | * 302 | * AdvDefaultLifetime == 0 && route.AdvRouteLifetime > 0: 303 | * - default route is deleted and then re-added. 304 | * AdvDefaultLifetime > 0 && route.AdvRouteLifetime == 0: 305 | * - default route is added and then deleted. 306 | */ 307 | if (IN6_IS_ADDR_UNSPECIFIED(&(route->Prefix))) { 308 | int route_zerolife = (route->AdvRouteLifetime == 0); 309 | int defaultroute_zerolife = (iface->ra_header_info.AdvDefaultLifetime == 0); 310 | if (route_zerolife ^ defaultroute_zerolife) { 311 | flog( 312 | LOG_ERR, 313 | "route 0::/0 lifetime (%u) conflicts with AdvDefaultLifetime (%u), default routes will flap!", 314 | route->AdvRouteLifetime, iface->ra_header_info.AdvDefaultLifetime); 315 | // res = -1; // In some future version, abort on this configuration error. 316 | } 317 | } 318 | 319 | route = route->next; 320 | } 321 | 322 | return res; 323 | } 324 | 325 | struct Interface *find_iface_by_index(struct Interface *iface, int index) 326 | { 327 | for (; iface; iface = iface->next) { 328 | if (iface->props.if_index == index) { 329 | return iface; 330 | } 331 | } 332 | 333 | return 0; 334 | } 335 | 336 | struct Interface *find_iface_by_name(struct Interface *iface, const char *name) 337 | { 338 | if (!name) { 339 | return 0; 340 | } 341 | 342 | for (; iface; iface = iface->next) { 343 | if (strcmp(iface->props.name, name) == 0) { 344 | return iface; 345 | } 346 | } 347 | 348 | return 0; 349 | } 350 | 351 | struct Interface *find_iface_by_time(struct Interface *iface) 352 | { 353 | if (!iface) { 354 | return 0; 355 | } 356 | 357 | int timeout = next_time_msec(iface); 358 | struct Interface *next = iface; 359 | 360 | for (iface = iface->next; iface; iface = iface->next) { 361 | int t = next_time_msec(iface); 362 | if (timeout > t) { 363 | timeout = t; 364 | next = iface; 365 | } 366 | } 367 | 368 | return next; 369 | } 370 | 371 | void reschedule_iface(struct Interface *iface, double next) 372 | { 373 | #ifdef HAVE_NETLINK 374 | if (!iface->state_info.changed && !iface->state_info.ready) { 375 | next = 10 * iface->MaxRtrAdvInterval; 376 | } else if (next == 0) { 377 | next = IFACE_SETUP_DELAY; 378 | } else 379 | #endif 380 | if (iface->state_info.racount < MAX_INITIAL_RTR_ADVERTISEMENTS) { 381 | next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval); 382 | } 383 | 384 | dlog(LOG_DEBUG, 5, "%s next scheduled RA in %g second(s)", iface->props.name, next); 385 | 386 | iface->times.next_multicast = next_timespec(next); 387 | } 388 | 389 | void for_each_iface(struct Interface *ifaces, void (*foo)(struct Interface *, void *), void *data) 390 | { 391 | for (; ifaces; ifaces = ifaces->next) { 392 | foo(ifaces, data); 393 | } 394 | } 395 | 396 | static void free_iface_list(struct Interface *iface) 397 | { 398 | while (iface) { 399 | struct Interface *next_iface = iface->next; 400 | 401 | dlog(LOG_DEBUG, 4, "freeing interface %s", iface->props.name); 402 | 403 | struct AdvPrefix *prefix = iface->AdvPrefixList; 404 | while (prefix) { 405 | struct AdvPrefix *next_prefix = prefix->next; 406 | 407 | free(prefix); 408 | prefix = next_prefix; 409 | } 410 | 411 | struct AdvRoute *route = iface->AdvRouteList; 412 | while (route) { 413 | struct AdvRoute *next_route = route->next; 414 | 415 | free(route); 416 | route = next_route; 417 | } 418 | 419 | struct AdvRDNSS *rdnss = iface->AdvRDNSSList; 420 | while (rdnss) { 421 | struct AdvRDNSS *next_rdnss = rdnss->next; 422 | 423 | free(rdnss->AdvRDNSSAddr); 424 | free(rdnss); 425 | rdnss = next_rdnss; 426 | } 427 | 428 | struct AdvDNSSL *dnssl = iface->AdvDNSSLList; 429 | while (dnssl) { 430 | struct AdvDNSSL *next_dnssl = dnssl->next; 431 | 432 | for (int i = 0; i < dnssl->AdvDNSSLNumber; i++) 433 | free(dnssl->AdvDNSSLSuffixes[i]); 434 | free(dnssl->AdvDNSSLSuffixes); 435 | free(dnssl); 436 | 437 | dnssl = next_dnssl; 438 | } 439 | 440 | struct AutogenIgnorePrefix *ignore_prefixes = iface->IgnorePrefixList; 441 | while (ignore_prefixes) { 442 | struct AutogenIgnorePrefix *next_ignore_prefix = ignore_prefixes->next; 443 | 444 | free(ignore_prefixes); 445 | ignore_prefixes = next_ignore_prefix; 446 | } 447 | 448 | struct Clients *clients = iface->ClientList; 449 | while (clients) { 450 | struct Clients *next_client = clients->next; 451 | 452 | free(clients); 453 | clients = next_client; 454 | } 455 | 456 | free(iface->props.if_addrs); 457 | 458 | free(iface->AdvCaptivePortalAPI); 459 | 460 | free(iface); 461 | iface = next_iface; 462 | } 463 | } 464 | 465 | void free_ifaces(struct Interface *ifaces) 466 | { 467 | dlog(LOG_DEBUG, 3, "Freeing Interfaces"); 468 | 469 | free_iface_list(ifaces); 470 | } 471 | --------------------------------------------------------------------------------