{ config, lib, pkgs, ... }: with lib; let pppInterface = config.networking.pppInterface; in { options = { networking.pppInterface = mkOption { type = types.str; default = "ppp"; }; }; config = { networking.vlans = { telekom = { id = 7; interface = "eno2"; }; }; services.pppd = { enable = true; peers.telekom.config = '' nodefaultroute ifname ${pppInterface} lcp-echo-adaptive lcp-echo-failure 10 lcp-echo-interval 1 maxfail 0 mtu 1492 mru 1492 plugin pppoe.so user congstar password congstar nic-telekom debug +ipv6 ''; }; systemd.services."pppd-telekom" = { stopIfChanged = true; serviceConfig = { Type = lib.mkForce "notify"; ExecStart = lib.mkForce "${getBin config.services.pppd.package}/sbin/pppd call telekom up_sdnotify nolog"; PIDFile = "/run/pppd/${pppInterface}.pid"; }; restartTriggers = with config; [ environment.etc."ppp/ip-pre-up".source environment.etc."ppp/ip-up".source environment.etc."ppp/ip-down".source ]; }; environment.etc = { "ppp/ip-pre-up".source = pkgs.resholve.writeScript "ip-pre-up" { interpreter = pkgs.runtimeShell; inputs = [ pkgs.iproute2 pkgs.ethtool ]; execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" "cannot:${lib.getExe' pkgs.iproute2 "tc"}" ]; } '' ethtool -K telekom tso off gso off gro off ip link del "ifb4${pppInterface}" || true ip link add name "ifb4${pppInterface}" type ifb ip link set "ifb4${pppInterface}" up tc qdisc del dev "ifb4${pppInterface}" root || true tc qdisc del dev "${pppInterface}" ingress || true tc qdisc del dev "${pppInterface}" root || true tc qdisc add dev "${pppInterface}" handle ffff: ingress tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}" tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth ${toString (builtins.floor (177968 * 0.95))}kbit tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth ${toString (builtins.floor (41216 * 0.95))}kbit ''; "ppp/ip-up".source = pkgs.resholve.writeScript "ip-up" { interpreter = pkgs.runtimeShell; inputs = [ pkgs.iproute2 ]; execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; } '' ip route add default via "$5" dev "${pppInterface}" metric 512 ''; "ppp/ip-down".source = pkgs.resholve.writeScript "ip-down" { interpreter = pkgs.runtimeShell; inputs = [ pkgs.iproute2 ]; execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; } '' ip link del "ifb4${pppInterface}" ''; }; systemd.network.networks.${pppInterface} = { matchConfig = { Name = pppInterface; }; dns = [ "::1" "127.0.0.1" ]; domains = [ "~." ]; networkConfig = { LinkLocalAddressing = "no"; DNSSEC = true; }; }; services.ndppd = { enable = true; proxies = { ${pppInterface} = { router = true; rules = { lan = { method = "iface"; interface = "lan"; network = "::/0"; }; }; }; }; }; boot.kernelModules = [ "ifb" ]; boot.kernel.sysctl = { "net.ipv6.conf.all.forwarding" = true; "net.ipv6.conf.default.forwarding" = true; "net.ipv4.conf.all.forwarding" = true; "net.ipv4.conf.default.forwarding" = true; "net.core.rmem_max" = 4194304; "net.core.wmem_max" = 4194304; }; systemd.services."pppd-telekom" = { bindsTo = [ "sys-subsystem-net-devices-telekom.device" ]; after = [ "sys-subsystem-net-devices-telekom.device" ]; }; networking.interfaces.${pppInterface}.useDHCP = true; networking.dhcpcd = { enable = true; persistent = false; setHostname = false; wait = "ipv6"; IPv6rs = false; extraConfig = '' duid vendorclassid ipv6only require dhcp_server_identifier reboot 0 interface ${pppInterface} nooption domain_name_servers, domain_name, domain_search, ntp_servers nohook hostname, resolv.conf option rapid_commit ipv6rs ia_pd 1 lan/0/64/0 ''; }; systemd.services.dhcpcd = { wantedBy = [ "multi-user.target" "network-online.target" "pppd-telekom.service" ]; bindsTo = [ "pppd-telekom.service" ]; after = [ "pppd-telekom.service" ]; wants = [ "network.target" ]; before = [ "network-online.target" ]; serviceConfig = { ExecStartPre = [ (pkgs.resholve.writeScript "wait-${pppInterface}-ip" { interpreter = pkgs.runtimeShell; inputs = with pkgs; [ iproute2 coreutils ]; execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; } '' i=0 while [[ -z "$(ip -6 addr show dev ${pppInterface} scope link)" ]]; do sleep 0.1 i=$((i + 1)) if [[ "$i" -ge 10 ]]; then exit 1 fi done '') ]; RestartSec = "5"; }; }; systemd.services.ndppd = { wantedBy = [ "dhcpcd.service" ]; bindsTo = [ "dhcpcd.service" ]; after = [ "dhcpcd.service" ]; serviceConfig = { Restart = "always"; RestartSec = "5"; }; }; systemd.services.radvd = { wantedBy = [ "dhcpcd.service" "multi-user.target" ]; bindsTo = [ "dhcpcd.service" ]; after = [ "dhcpcd.service" "network.target" ]; serviceConfig = { Restart = "always"; RestartSec = "5"; DynamicUser = true; AmbientCapabilities = ["CAP_NET_ADMIN" "CAP_NET_RAW"]; CapabilityBoundingSet = ["CAP_NET_ADMIN" "CAP_NET_RAW"]; RuntimeDirectory = "radvd"; PIDFile = "$RUNTIME_DIRECTORY/radvd.pid"; ExecStart = pkgs.writers.writePython3 "radvd-genconfig" { libraries = with pkgs.python3Packages; [ jinja2 ]; doCheck = false; } '' import os from tempfile import NamedTemporaryFile import subprocess import json import jinja2 from pathlib import Path from ipaddress import IPv6Network def network_address(value, prefixlen): return IPv6Network(value + "/" + str(prefixlen), strict=False).network_address with subprocess.Popen(["${lib.getExe' pkgs.iproute2 "ip"}", "-j", "addr", "show", "dev", "lan"], stdout=subprocess.PIPE) as proc: addresses = json.load(proc.stdout) global_addresses = [ addr for addr in addresses[0]["addr_info"] if addr["family"] == "inet6" and addr["scope"] == "global" ] if not global_addresses: sys.exit(1) with NamedTemporaryFile(mode='w', dir=os.environ["RUNTIME_DIRECTORY"], prefix="radvd.", suffix=".conf", delete=False) as fh: config_file = fh.name env = jinja2.Environment( loader = jinja2.FileSystemLoader("${pkgs.writeTextDir "radvd.conf.jinja2" '' interface lan { IgnoreIfMissing off; AdvSendAdvert on; MaxRtrAdvInterval 240; {% for addr in addrs %} prefix {{addr["local"] | network_address(addr["prefixlen"])}}/{{addr["prefixlen"]}} { AdvValidLifetime 86400; AdvPreferredLifetime 300; DeprecatePrefix on; }; route {{addr["local"] | network_address(56)}}/56 { AdvRouteLifetime 300; RemoveRoute on; }; RDNSS {{addr["local"]}} { AdvRDNSSLifetime 300; }; {%- endfor %} DNSSL yggdrasil {}; }; ''}"), autoescape = False, ) env.filters["network_address"] = network_address env.get_template("radvd.conf.jinja2").stream({ "addrs": global_addresses, }).dump(fh) os.execv("${lib.getExe pkgs.radvd}", ["radvd", "-n", "-p", str(Path(os.environ["RUNTIME_DIRECTORY"]) / "radvd.pid"), "-d", "1", "-C", config_file]) ''; }; }; systemd.services.unbound = { wantedBy = [ "dhcpcd.service" ]; bindsTo = [ "dhcpcd.service" ]; after = [ "dhcpcd.service" ]; serviceConfig = { Restart = lib.mkForce "always"; }; }; }; }