diff --git a/nat.nft b/nat.nft new file mode 100644 index 0000000..20c8619 --- /dev/null +++ b/nat.nft @@ -0,0 +1,16 @@ +#!/usr/bin/nft -f + +flush ruleset + +table ip nat { + chain prerouting { + type nat hook prerouting priority 0; policy accept; + } + + # for all packets to WAN, after routing, replace source address with primary IP of WAN interface + chain postrouting { + type nat hook postrouting priority 100; policy accept; + oifname "ppp0" masquerade + } +} + diff --git a/rotuer.nix b/rotuer.nix new file mode 100644 index 0000000..9c2fef8 --- /dev/null +++ b/rotuer.nix @@ -0,0 +1,215 @@ +# This is not part of Liminix per se. This is my "scratchpad" +# configuration for the device I'm testing with. +# +# Parts of it do do things that Liminix eventually needs to do, but +# don't look in here for solutions - just for identifying the +# problems. + + +{ config, pkgs, lib, ... } : +let + secrets = import ./rotuer-secrets.nix; + inherit (pkgs.liminix.networking) + address + bridge + dnsmasq + hostapd + interface + pppoe + route; + inherit (pkgs.liminix.services) oneshot longrun bundle target; + inherit (pkgs) + waitup + serviceFns + iptables; +in rec { + services.loopback = + let iface = interface { type = "loopback"; device = "lo";}; + in bundle { + name = "loopback"; + contents = [ + (address iface { family = "inet4"; address ="127.0.0.1"; prefixLength = 8;}) + (address iface { family = "inet6"; address ="::1"; prefixLength = 128;}) + ]; + }; + + boot = { + tftp = { + enable = true; + serverip = "10.0.0.1"; + ipaddr = "10.0.0.8"; + }; + }; + + imports = [ + ./modules/wlan.nix + ./modules/phram.nix + ]; + + kernel = { + config = { + PPP = "y"; + PPP_BSDCOMP = "y"; + PPP_DEFLATE = "y"; + PPP_ASYNC = "y"; + PPP_SYNC_TTY = "y"; + BRIDGE = "y"; + + NETFILTER_XT_MATCH_CONNTRACK = "y"; + + IP6_NF_IPTABLES= "y"; + IP_NF_IPTABLES= "y"; + IP_NF_NAT = "y"; + IP_NF_TARGET_MASQUERADE = "y"; + NETFILTER = "y"; + NETFILTER_ADVANCED = "y"; + NETFILTER_XTABLES = "y"; + + NFT_COMPAT = "y"; + NFT_CT = "y"; + NFT_LOG = "y"; + NFT_MASQ = "y"; + NFT_NAT = "y"; + NFT_REJECT = "y"; + NFT_REJECT_INET = "y"; + + NF_CONNTRACK = "y"; + NF_NAT = "y"; + NF_NAT_MASQUERADE = "y"; + NF_TABLES= "y"; + NF_TABLES_INET = "y"; + NF_TABLES_IPV4 = "y"; + NF_TABLES_IPV6 = "y"; + }; + }; + + services.lan = + let iface = interface { + type = "bridge"; + device = "lan"; + }; + in address iface { + family = "inet4"; address ="10.8.0.1"; prefixLength = 16; + }; + + services.wireless = interface { + type = "hardware"; + device = "wlan0"; + dependencies = [ config.services.wlan_module ]; + }; + + services.wired = interface { + type = "hardware"; + device = "eth0"; + primary = services.lan; + }; + + services.hostap = hostapd (services.wireless) { + params = { + ssid = "liminix"; + country_code = "GB"; + hw_mode="g"; + channel = "2"; + wmm_enabled = 1; + ieee80211n = 1; + inherit (secrets) wpa_passphrase; + auth_algs = 1; # 1=wpa2, 2=wep, 3=both + wpa = 2; # 1=wpa, 2=wpa2, 3=both + wpa_key_mgmt = "WPA-PSK"; + wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?) + rsn_pairwise = "CCMP"; # auth for wpa2 + }; + }; + + services.bridgewlan = + let waitup-wlan = longrun { + name = "waitup-wlan0"; + run = "${waitup}/bin/waitup wlan0 10"; + notification-fd = 10; + dependencies = [ services.wireless services.hostap ]; + }; + in oneshot { + name = "add-wlan-to-bridge"; + up = "ip link set dev ${services.wireless.device} master ${services.lan.device}"; + down = "ip link set dev ${services.wireless.device} nomaster"; + dependencies = [ waitup-wlan ]; + }; + + users.dnsmasq = { + uid = 51; gid= 51; gecos = "DNS/DHCP service user"; + dir = "/run/dnsmasq"; + shell = "/bin/false"; + }; + groups.dnsmasq = { + gid = 51; usernames = ["dnsmasq"]; + }; + groups.system.usernames = ["dnsmasq"]; + + services.dns = + dnsmasq { + resolvconf = services.resolvconf; + interface = services.lan; + ranges = ["10.8.0.10,10.8.0.240"]; + domain = "fake.liminix.org"; + }; + + services.wan = + let iface = interface { type = "hardware"; device = "eth1"; }; + in pppoe iface { + ppp-options = [ + "debug" "+ipv6" "noauth" + "name" secrets.l2tp.name + "password" secrets.l2tp.password + ]; + }; + + services.resolvconf = oneshot rec { + dependencies = [ services.wan ]; + name = "resolvconf"; + up = '' + . ${serviceFns} + ( cd `mkoutputs ${name}`; umask 0027 + echo "nameserver $(output ${services.wan} ns1)" > resolv.conf + echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf + ) + ''; + down = '' + rm -rf /run/service-state/${name}/ + ''; + }; + + services.defaultroute4 = route { + name = "defaultroute"; + via = "$(output ${services.wan} address)"; + target = "default"; + dependencies = [ services.wan ]; + }; + + services.packet_forwarding = + let filename = "/proc/sys/net/ipv4/conf/all/forwarding"; + in oneshot { + name = "let-the-ip-flow"; + up = '' + ${pkgs.nftables}/bin/nft -f ${./nat.nft} + echo 1 > ${filename} + ''; + down = "echo 0 > ${filename}"; + }; + + services.default = target { + name = "default"; + contents = with services; [ + loopback + wired + wireless + lan + hostap + defaultroute4 + packet_forwarding + dns + bridgewlan + resolvconf + ]; + }; + defaultProfile.packages = with pkgs; [ nftables strace tcpdump ] ; +}