code wiki / (root) / nx_ice_agent.nx

nx_ice_agent.nx source

↩ module page · 144 lines · 5900 B

1// nx_ice_agent.nx -- SOVEREIGN ICE AGENT state machine (RFC 8445 6), NishiLang, composing nx_ice. 2// 3// The orchestration on top of the check-exchange primitives: an agent holds its LOCAL candidates and the 4// peer's REMOTE candidates, forms candidate PAIRS (same component), orders them by candidate-pair priority 5// (RFC 8445 6.1.2.3, via the overflow-safe comparator), schedules connectivity checks highest-priority 6// first, records each check's result, and NOMINATES the best valid pair as the selected path. Pure logic 7// over the primitives -- the actual check send/recv is nx_ice's ic_build_check / ic_verify_* over UDP; this 8// module decides WHICH pair to check next and WHICH becomes the path. Zero third-party code. 9// license_tier: INDEPENDENT_REDERIVE genealogy_id: international-research-sources/ietf/rfc_8445 10import "nx_syscalls.nx" 11import "nx_ice.nx" 12const AG_MAGIC_4096: i64 = 4096 13 14const AG_MAX_LOCAL: i64 = 8 15const AG_MAX_REMOTE: i64 = 8 16const AG_MAX_PAIRS: i64 = 64 17 18// agent header (i64 slots) 19const AG_NLOCAL: i64 = 0 20const AG_NREMOTE: i64 = 1 21const AG_NPAIRS: i64 = 2 22const AG_CONTROLLING: i64 = 3 23const AG_NOMINATED: i64 = 4 // pair index of the SELECTED pair, or -1 24// candidate + pair tables (i64 offsets); each candidate = 4 slots (ip, port, prio, component), 25// each pair = 4 slots (local_idx, remote_idx, state, spare) 26const AG_LOCAL0: i64 = 8 27const AG_REMOTE0: i64 = 40 // AG_LOCAL0 + AG_MAX_LOCAL*4 28const AG_PAIRS0: i64 = 72 // AG_REMOTE0 + AG_MAX_REMOTE*4 29 30// pair states 31const AG_ST_WAITING: i64 = 0 32const AG_ST_INPROGRESS: i64 = 1 33const AG_ST_SUCCEEDED: i64 = 2 34const AG_ST_FAILED: i64 = 3 35const AG_ST_SELECTED: i64 = 4 36 37func ag_new(controlling: i64) -> *i64 { 38 let a: *i64 = sys_mmap(AG_MAGIC_4096) as *i64 39 a[AG_NLOCAL]=0; a[AG_NREMOTE]=0; a[AG_NPAIRS]=0 40 a[AG_CONTROLLING]=controlling; a[AG_NOMINATED]=0-1 41 return a 42} 43func ag_add_local(a: *i64, ip: i64, port: i64, prio: i64, comp: i64) -> i64 { 44 let i: i64 = a[AG_NLOCAL] 45 if i >= AG_MAX_LOCAL { return 0-1 } 46 let b: i64 = AG_LOCAL0 + i*4 47 a[b]=ip; a[b+1]=port; a[b+2]=prio; a[b+3]=comp 48 a[AG_NLOCAL]=i+1 49 return i 50} 51func ag_add_remote(a: *i64, ip: i64, port: i64, prio: i64, comp: i64) -> i64 { 52 let i: i64 = a[AG_NREMOTE] 53 if i >= AG_MAX_REMOTE { return 0-1 } 54 let b: i64 = AG_REMOTE0 + i*4 55 a[b]=ip; a[b+1]=port; a[b+2]=prio; a[b+3]=comp 56 a[AG_NREMOTE]=i+1 57 return i 58} 59func ag_local_prio(a: *i64, i: i64) -> i64 { return a[AG_LOCAL0 + i*4 + 2] } 60func ag_local_comp(a: *i64, i: i64) -> i64 { return a[AG_LOCAL0 + i*4 + 3] } 61func ag_remote_prio(a: *i64, i: i64) -> i64 { return a[AG_REMOTE0 + i*4 + 2] } 62func ag_remote_comp(a: *i64, i: i64) -> i64 { return a[AG_REMOTE0 + i*4 + 3] } 63func ag_pair_local(a: *i64, p: i64) -> i64 { return a[AG_PAIRS0 + p*4] } 64func ag_pair_remote(a: *i64, p: i64) -> i64 { return a[AG_PAIRS0 + p*4 + 1] } 65func ag_pair_state(a: *i64, p: i64) -> i64 { return a[AG_PAIRS0 + p*4 + 2] } 66 67// (G,D) for a pair: controlling agent -> G=local prio, D=remote prio; controlled -> swapped (RFC 8445 6.1.2.3) 68func ag_pair_G(a: *i64, p: i64) -> i64 { if a[AG_CONTROLLING]==1 { return ag_local_prio(a, ag_pair_local(a,p)) } return ag_remote_prio(a, ag_pair_remote(a,p)) } 69func ag_pair_D(a: *i64, p: i64) -> i64 { if a[AG_CONTROLLING]==1 { return ag_remote_prio(a, ag_pair_remote(a,p)) } return ag_local_prio(a, ag_pair_local(a,p)) } 70 71// swap two pair rows 72func ag_swap(a: *i64, p: i64, q: i64) -> i64 { 73 var k: i64 = 0 74 while k < 4 { 75 let tmp: i64 = a[AG_PAIRS0 + p*4 + k] 76 a[AG_PAIRS0 + p*4 + k] = a[AG_PAIRS0 + q*4 + k] 77 a[AG_PAIRS0 + q*4 + k] = tmp 78 k = k + 1 79 } 80 return 0 81} 82// form pairs (matching component) then sort HIGHEST candidate-pair priority first (selection sort; n<=64). 83func ag_form_pairs(a: *i64) -> i64 { 84 var np: i64 = 0 85 var li: i64 = 0 86 while li < a[AG_NLOCAL] { 87 var ri: i64 = 0 88 while ri < a[AG_NREMOTE] { 89 if ag_local_comp(a, li) == ag_remote_comp(a, ri) { 90 if np < AG_MAX_PAIRS { 91 let b: i64 = AG_PAIRS0 + np*4 92 a[b]=li; a[b+1]=ri; a[b+2]=AG_ST_WAITING; a[b+3]=0 93 np = np + 1 94 } 95 } 96 ri = ri + 1 97 } 98 li = li + 1 99 } 100 a[AG_NPAIRS] = np 101 // selection sort by ic_pair_cmp descending 102 var i: i64 = 0 103 while i < np { 104 var best: i64 = i 105 var j: i64 = i + 1 106 while j < np { 107 if ic_pair_cmp(ag_pair_G(a,j), ag_pair_D(a,j), ag_pair_G(a,best), ag_pair_D(a,best)) > 0 { best = j } 108 j = j + 1 109 } 110 if best != i { ag_swap(a, i, best) } 111 i = i + 1 112 } 113 return np 114} 115// the next pair to check = the highest-priority pair still WAITING; marks it IN-PROGRESS. -1 if none left. 116func ag_next_check(a: *i64) -> i64 { 117 var i: i64 = 0 118 while i < a[AG_NPAIRS] { 119 if ag_pair_state(a, i) == AG_ST_WAITING { a[AG_PAIRS0 + i*4 + 2] = AG_ST_INPROGRESS; return i } 120 i = i + 1 121 } 122 return 0-1 123} 124// record a check result: success -> SUCCEEDED, else FAILED. 125func ag_result(a: *i64, p: i64, success: i64) -> i64 { 126 if p < 0 { return 0-1 } 127 if p >= a[AG_NPAIRS] { return 0-1 } 128 if success == 1 { a[AG_PAIRS0 + p*4 + 2] = AG_ST_SUCCEEDED } else { a[AG_PAIRS0 + p*4 + 2] = AG_ST_FAILED } 129 return 0 130} 131// NOMINATE: the highest-priority SUCCEEDED pair becomes the selected path (pairs are priority-ordered, so 132// the first succeeded one is the best). Marks it SELECTED, records AG_NOMINATED, returns its index or -1. 133func ag_nominate(a: *i64) -> i64 { 134 var i: i64 = 0 135 while i < a[AG_NPAIRS] { 136 if ag_pair_state(a, i) == AG_ST_SUCCEEDED { 137 a[AG_PAIRS0 + i*4 + 2] = AG_ST_SELECTED 138 a[AG_NOMINATED] = i 139 return i 140 } 141 i = i + 1 142 } 143 return 0-1 144}