code wiki / (root) / nx_router_ctl.nx

nx_router_ctl.nx source

↩ module page · 99 lines · 9869 B

1// nx_router_ctl.nx -- SOVEREIGN router control (operator: "you have api access, get it going, towards apis"; 2// roadmap NR-7). Drives the home GL.iNet GL-BE9300 (OpenWrt) via its uci config API over OUR OWN sovereign SSH 3// (nx_ssh_lib -- curve25519 KEX, chacha/aes, exec channel), NOT WSL bash. Verbs: 4// fwadd [port] [destip] -> idempotent DNAT redirect wan:port -> destip:port (TCP+UDP); opens the seeder to WAN 5// fwlist -> show our Nishi firewall redirects 6// wan -> WAN interface IP (are we actually reachable?) 7// Password from data/router.pw (0600; vault-migratable). Router LAN IP 192.168.8.1. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_ssh_lib.nx" 10const RC_MAGIC_6881: i64 = 6881 11const RC_MAGIC_8192: i64 = 8192 12 13const RC_PW: *u8 = "data/router.pw" as *u8 14const RC_ROUTER: i64 = (192 << 24) | (168 << 16) | (8 << 8) | 1 // 192.168.8.1 (GL.iNet LAN) 15 16func rc_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 17func rc_app(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { dst[o]=s[i]; o=o+1; i=i+1 } return o } 18func rc_appn(dst: *u8, o: i64, v: i64) -> i64 { if v==0 { dst[o]=48 as u8; return o+1 } var m: i64=v; let t: *u8=sys_mmap(28); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } while k>0 { k=k-1; dst[o]=t[k]; o=o+1 } return o } 19func rc_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 } 20 21// build one idempotent uci DNAT redirect section into buf at o; returns new o. 22func rc_redir(buf: *u8, o: i64, sect: *u8, proto: *u8, port: i64, destip: *u8) -> i64 { 23 o=rc_app(buf,o,"uci -q delete firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,";" as *u8) 24 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,"=redirect;" as *u8) 25 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".name=Nishi-" as *u8); o=rc_appn(buf,o,port); o=rc_app(buf,o,"-" as *u8); o=rc_app(buf,o,proto); o=rc_app(buf,o,";" as *u8) 26 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".src=wan;" as *u8) 27 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".proto=" as *u8); o=rc_app(buf,o,proto); o=rc_app(buf,o,";" as *u8) 28 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".src_dport=" as *u8); o=rc_appn(buf,o,port); o=rc_app(buf,o,";" as *u8) 29 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".dest=lan;" as *u8) 30 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".dest_ip=" as *u8); o=rc_app(buf,o,destip); o=rc_app(buf,o,";" as *u8) 31 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".dest_port=" as *u8); o=rc_appn(buf,o,port); o=rc_app(buf,o,";" as *u8) 32 o=rc_app(buf,o,"uci set firewall." as *u8); o=rc_app(buf,o,sect); o=rc_app(buf,o,".target=DNAT;" as *u8) 33 return o 34} 35 36func main(argc: i64, argv: *i64) -> i64 { 37 var verb: *u8 = "fwadd" as *u8 38 if argc>1 { verb = argv[1] as *u8 } 39 var port: i64 = RC_MAGIC_6881 40 if argc>2 { var v: i64=0; var i: i64=0; let a: *u8=argv[2] as *u8; while a[i]!=(0 as u8) { if a[i]>=(48 as u8) { if a[i]<=(57 as u8) { v=v*10+((a[i] as i64)-48) } } i=i+1 } if v>0 { port=v } } 41 var destip: *u8 = "192.168.8.227" as *u8 42 if argc>3 { destip = argv[3] as *u8 } 43 // raw string args (dhcpreserve <mac> <ip> [name]) 44 var a2: *u8 = "" as *u8; var a3: *u8 = "" as *u8; var a4: *u8 = "Nishi" as *u8 45 if argc>2 { a2 = argv[2] as *u8 } 46 if argc>3 { a3 = argv[3] as *u8 } 47 if argc>4 { a4 = argv[4] as *u8 } 48 49 ssh_puts("NX-ROUTER-CTL verb=" as *u8); ssh_puts(verb); ssh_puts(" (GL.iNet 192.168.8.1 via sovereign SSH+uci)\n" as *u8) 50 // read the router password (sovereign secret file) 51 let pwbox: *i64 = sys_mmap(16) as *i64 52 let pw: *u8 = sys_read_file(RC_PW, pwbox) 53 if (pw as i64) == 0 { ssh_puts(" cannot read data/router.pw\n" as *u8); sys_exit(3); return 3 } 54 var pwlen: i64 = pwbox[0] 55 while pwlen > 0 { if pw[pwlen-1] == 10 as u8 { pwlen = pwlen - 1 } else { if pw[pwlen-1] == 13 as u8 { pwlen = pwlen - 1 } else { break } } } 56 // sovereign SSH 57 let st: *SshState = sys_mmap(SSH_STATE_BYTES) as *SshState 58 if ssh_open_session(st, RC_ROUTER) != 0 { ssh_puts(" SSH session FAILED (router :22 unreachable / KEX mismatch vs dropbear)\n" as *u8); sys_exit(5); return 5 } 59 if ssh_userauth_password(st, "root" as *u8, 4, pw, pwlen) != 1 { ssh_puts(" SSH auth FAILED (wrong router password?)\n" as *u8); sys_close(st.fd); sys_exit(6); return 6 } 60 ssh_puts(" SSH+auth OK -> running uci\n" as *u8) 61 // compose the command per verb 62 // FLAT dispatch (verbs are mutually exclusive; only one sets cmd). Full network-control API surface. 63 let cmd: *u8 = sys_mmap(RC_MAGIC_8192); var o: i64=0; var handled: i64=0 64 // PORT-SCOPED section names (nishitcp<port>/nishiudp<port>) so multiple forwards coexist -- adding :25 65 // must NOT clobber the existing :6881 seeder forward. (Legacy 6881 lives in unsuffixed nishitcp/nishiudp; 66 // left intact for back-compat.) 67 let stcp: *u8 = sys_mmap(32); var sc1: i64 = rc_app(stcp, 0, "nishitcp" as *u8); sc1 = rc_appn(stcp, sc1, port); stcp[sc1] = 0 as u8 68 let sudp: *u8 = sys_mmap(32); var sc2: i64 = rc_app(sudp, 0, "nishiudp" as *u8); sc2 = rc_appn(sudp, sc2, port); sudp[sc2] = 0 as u8 69 if rc_streq(verb, "fwadd" as *u8) == 1 { 70 o = rc_redir(cmd, o, stcp, "tcp" as *u8, port, destip) 71 o = rc_redir(cmd, o, sudp, "udp" as *u8, port, destip) 72 o = rc_app(cmd, o, "uci commit firewall;/etc/init.d/firewall reload;echo NISHI_FWDONE;uci show firewall|grep -i nishi" as *u8); handled=1 } 73 if rc_streq(verb, "fwdel" as *u8) == 1 { 74 o = rc_app(cmd, o, "uci -q delete firewall." as *u8); o = rc_app(cmd, o, stcp); o = rc_app(cmd, o, ";uci -q delete firewall." as *u8); o = rc_app(cmd, o, sudp); o = rc_app(cmd, o, ";uci commit firewall;/etc/init.d/firewall reload;echo NISHI_FWDEL_DONE" as *u8); handled=1 } 75 if rc_streq(verb, "fwlist" as *u8) == 1 { 76 o = rc_app(cmd, o, "uci show firewall|grep -iE 'redirect|nishi';echo ---redirect-count---;uci show firewall|grep -c redirect" as *u8); handled=1 } 77 if rc_streq(verb, "wan" as *u8) == 1 { 78 o = rc_app(cmd, o, "ifstatus wan 2>/dev/null|grep -m3 address;echo ---;ubus call network.interface.wan status 2>/dev/null|grep -m2 address" as *u8); handled=1 } 79 if rc_streq(verb, "status" as *u8) == 1 { 80 o = rc_app(cmd, o, "ubus call system board 2>/dev/null;echo ---glversion---;cat /etc/glversion 2>/dev/null;echo ---uptime/load---;uptime;cat /proc/loadavg" as *u8); handled=1 } 81 if rc_streq(verb, "clients" as *u8) == 1 { 82 o = rc_app(cmd, o, "echo ===DHCP-LEASES:expiry_mac_ip_name===;cat /tmp/dhcp.leases 2>/dev/null;echo ===ARP-REACHABLE===;ip neigh 2>/dev/null|grep -i reachable" as *u8); handled=1 } 83 if rc_streq(verb, "dns" as *u8) == 1 { 84 o = rc_app(cmd, o, "echo ===WAN-DNS===;uci -q get network.wan.dns;echo ===dnsmasq===;uci show dhcp 2>/dev/null|grep -iE 'server|domain';echo ===resolv===;grep -i nameserver /tmp/resolv.conf.d/resolv.conf.auto 2>/dev/null" as *u8); handled=1 } 85 if rc_streq(verb, "dhcpreserve" as *u8) == 1 { 86 o = rc_app(cmd, o, "uci -q delete dhcp.nishihost;uci set dhcp.nishihost=host;uci set dhcp.nishihost.mac=" as *u8); o=rc_app(cmd,o,a2); o=rc_app(cmd,o,";uci set dhcp.nishihost.ip=" as *u8); o=rc_app(cmd,o,a3); o=rc_app(cmd,o,";uci set dhcp.nishihost.name=" as *u8); o=rc_app(cmd,o,a4); o=rc_app(cmd,o,";uci commit dhcp;/etc/init.d/dnsmasq restart;echo NISHI_DHCP_DONE;uci show dhcp|grep nishihost" as *u8); handled=1 } 87 if rc_streq(verb, "v6" as *u8) == 1 { 88 o = rc_app(cmd, o, "echo ===WAN6-STATUS===;ifstatus wan6 2>/dev/null|grep -iE 'up|address|mask|prefix';echo ===WAN6-PD-PREFIX===;ubus call network.interface.wan6 status 2>/dev/null|grep -iE 'address|mask|prefix';echo ===LAN6-CFG===;uci show network 2>/dev/null|grep -iE 'ip6assign|ip6class|dhcpv6|ipv6|ra_';echo ===DHCP-RA===;uci show dhcp 2>/dev/null|grep -iE 'ra=|ra_|dhcpv6|ndp|master';echo ===LAN6-GLOBAL===;ip -6 addr show br-lan 2>/dev/null|grep -i 'scope global'|head" as *u8); handled=1 } 89 if rc_streq(verb, "v6on" as *u8) == 1 { 90 o = rc_app(cmd, o, "uci set network.wan.ipv6='1';uci set network.wan6.proto='dhcpv6';uci set dhcp.lan.ra='server';uci set dhcp.lan.dhcpv6='server';uci set dhcp.lan.ra_slaac='1';uci commit network;uci commit dhcp;ifup wan6 2>/dev/null;/etc/init.d/odhcpd restart 2>/dev/null;sleep 12;echo ===WAN6-GLOBAL+PD===;ifstatus wan6 2>/dev/null|grep -iE 'address|prefix|mask';ip -6 addr 2>/dev/null|grep -i 'scope global'|head;echo ===V6-DEFAULT-ROUTE===;ip -6 route show default 2>/dev/null|head;echo V6ON_DONE" as *u8); handled=1 } 91 if rc_streq(verb, "v6off" as *u8) == 1 { 92 o = rc_app(cmd, o, "uci set network.wan.ipv6='0';uci set dhcp.lan.ra='disabled';uci set dhcp.lan.dhcpv6='disabled';uci commit network;uci commit dhcp;ifdown wan6 2>/dev/null;/etc/init.d/odhcpd restart 2>/dev/null;echo V6OFF_REVERTED" as *u8); handled=1 } 93 if rc_streq(verb, "dash" as *u8) == 1 { 94 o = rc_app(cmd, o, "echo ===STATUS===;ubus call system board 2>/dev/null|grep -E 'model|version|hostname';echo ===WAN-IP===;ifstatus wan 2>/dev/null|grep -m1 address;echo ===NISHI-FORWARDS===;uci show firewall|grep -i nishi;echo ===CLIENT-COUNT===;cat /tmp/dhcp.leases 2>/dev/null|wc -l" as *u8); handled=1 } 95 if handled==0 { o = rc_app(cmd, o, "echo verbs: fwadd[port destip] | fwdel | fwlist | wan | status | clients | dns | dhcpreserve mac ip name | dash" as *u8) } 96 ssh_exec(st, cmd, o) 97 sys_close(st.fd) 98 sys_exit(0); return 0 99}