12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697modulePfx=structtypet={on_link:bool;autonomous:bool;valid_lifetime:intoption;preferred_lifetime:intoption;prefix:Ipaddr.V6.Prefix.t}letppppft=Fmt.pfppf"{ @[<hov>on_link=@ %b;@ autonomous=@ %b;@ valid_lifetime=@ %a;@ \
preferred_lifetime=@ %a;@ prefix=@ %a;@] }"t.on_linkt.autonomousFmt.(Dump.optionint)t.valid_lifetimeFmt.(Dump.optionint)t.preferred_lifetimeIpaddr.V6.Prefix.ppt.prefixendmodulePrefix=structtypet={expire_at:intoption}letweight(_t:t)=1endmodulePrefixes=Lru.F.Make(Ipaddr.V6.Prefix)(Prefix)typet=Prefixes.tletmakecapacity=Prefixes.emptycapacity(* NOTE(dinosaure): From RFC 4861, 5.3:
When removing an entry from the Prefix List, there is no need to
purge any entries from the Destination or Neighbor Caches.
*)let_1s=1_000_000_000let_2h=2*60*60(* 2 hours in seconds *)(* RFC 4862 Section 5.5.3: Rules for updating valid lifetime to prevent DoS
attacks where an attacker sends RAs with very short lifetimes.
1. If advertised_lifetime > 2 hours, accept it
2. If advertised_lifetime > remaining_lifetime, accept it
3. If remaining_lifetime <= 2 hours, ignore (keep current)
4. Otherwise, set to 2 hours
NOTE(dinosaure):
- [expire_at] is in nanoseconds
- [advertised] is in seconds
- [rem] is in seconds *)letexpire_at~now~advertisedexisting=matchexistingwith|None->Some(now+(advertised*_1s))|Some{Prefix.expire_at=None}->Some(now+(advertised*_1s))|Some{Prefix.expire_at=Someexpire_at}->letrem=(expire_at-now)/_1sinifadvertised>_2hthenSome(now+(advertised*_1s))elseifadvertised>remthenSome(now+(advertised*_1s))elseifrem<=_2hthenSomeexpire_atelseSome(now+(_2h*_1s))letfn~nowtpfx=(* NOTE(dinosaure): RFC 4861, 6.2.5 — only prefixes with [on_link] set are
added to our list. Link-local prefixes are ignored. *)ifIpaddr.V6.Prefix.link=pfx.Pfx.prefix||notpfx.Pfx.on_linkthentelsematchpfx.Pfx.valid_lifetimewith|Some0->Prefixes.removepfx.Pfx.prefixt|Somelifetime->letexisting=Prefixes.findpfx.Pfx.prefixtinletexpire_at=expire_at~now~advertised:lifetimeexistinginlett=Prefixes.removepfx.Pfx.prefixtinPrefixes.addpfx.Pfx.prefix{Prefix.expire_at}t|None->lett=Prefixes.removepfx.Pfx.prefixtinPrefixes.addpfx.Pfx.prefix{Prefix.expire_at=None}tlettickt~nowpfxs=lett=List.fold_left(fn~now)tpfxsinletfnprefix({Prefix.expire_at}asvalue)t=matchexpire_atwith|Someexpire_atwhenexpire_at<now->t|_->Prefixes.addprefixvaluetinletcapacity=Prefixes.capacitytinlett=Prefixes.foldfn(Prefixes.emptycapacity)tinPrefixes.trimtexceptionYesletis_localtaddr=letfnprefix_=ifIpaddr.V6.Prefix.memaddrprefixthenraise_notraceYesinmatchPrefixes.iter_kfntwithexceptionYes->true|_->false