123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116moduleRedirect=structtypet={target:Ipaddr.V6.t;destination:Ipaddr.V6.t}letppppft=Fmt.pfppf"{ @[<hov>target=@ %a;@ destination=@ %a;@] }"Ipaddr.V6.ppt.targetIpaddr.V6.ppt.destinationendmoduleUnreachable=structtypet={code:int;destination:Ipaddr.V6.t}letppppf{code;destination}=Fmt.pfppf"Destination %a unreachable (%d)"Ipaddr.V6.ppdestinationcodeendmodulePTB=structtypet={mtu:int;destination:Ipaddr.V6.t}letppppft=Fmt.pfppf"Packet too big (mtu:%d, addr:%a)"t.mtuIpaddr.V6.ppt.destinationendtypeerror=[`Packet_too_big|`Destination_unreachableofint]moduleDst=structtypet={pmtu:int;next_hop:Ipaddr.V6.t;errored:erroroption}letweight(_t:t)=1endmoduleDsts=Lru.F.Make(Ipaddr.V6)(Dst)typet={cache:Dsts.t;lmtu:int}letmake~lmtucapacity={cache=Dsts.emptycapacity;lmtu}letnext_hopaddrt=matchDsts.findaddrt.cachewith|Some{pmtu;next_hop;errored=None}->Ok(next_hop,pmtu,{twithcache=Dsts.promoteaddrt.cache})|Some{errored=Someerr;_}->Error(err:>[`Not_found|error])|None->Error`Not_found(* NOTE(dinosaure): by default, we use the Link-MTU for any [addr]. *)letaddt?mtu:(pmtu=t.lmtu)addrnext_hop=letvalue={Dst.pmtu;next_hop;errored=None}inletcache=Dsts.addaddrvaluet.cachein{twithcache=Dsts.trimcache}letclean_old_routersrouterst=letcapacity=Dsts.capacityt.cacheinletfnaddr({Dst.next_hop;_}asvalue)t=ifList.memnext_hoproutersthentelseDsts.addaddrvaluetinletcache=Dsts.fold_kfn(Dsts.emptycapacity)t.cachein{twithcache}lettickt~now:_=function|`Redirect(_src,r)->beginmatchDsts.findr.Redirect.destinationt.cachewith|Some{Dst.pmtu;_}->letnext_hop=r.Redirect.targetinleterrored=Noneinletvalue={Dst.pmtu;next_hop;errored}inletcache=Dsts.addr.Redirect.destinationvaluet.cachein{twithcache=Dsts.trimcache}|None->letnext_hop=r.Redirect.targetinleterrored=Noneinletvalue={Dst.pmtu=t.lmtu;next_hop;errored}inletcache=Dsts.addr.Redirect.destinationvaluet.cachein{twithcache=Dsts.trimcache}end|`Destination_unreachableu->(* RFC 4443: Mark the destination as errored in the cache.
This prevents further attempts to send to this destination
until the entry expires or is cleared. *)beginmatchDsts.findu.Unreachable.destinationt.cachewith|Some{Dst.pmtu;next_hop;_}->leterrored=Some(`Destination_unreachableu.Unreachable.code)inletvalue={Dst.pmtu;next_hop;errored}inletcache=Dsts.addu.Unreachable.destinationvaluet.cachein{twithcache=Dsts.trimcache}|None->(* No cached entry, create one with the error *)leterrored=Some(`Destination_unreachableu.Unreachable.code)inletvalue={Dst.pmtu=t.lmtu;next_hop=u.Unreachable.destination;errored}inletcache=Dsts.addu.Unreachable.destinationvaluet.cachein{twithcache=Dsts.trimcache}end|`Packet_too_bigptb->begin(* RFC 8201: Path MTU Discovery for IPv6
Update the PMTU for the destination. The new PMTU should be
at least 1280 (minimum IPv6 MTU) and at most the current PMTU.
Mark the destination as errored so that [next_hop] returns
[`Packet_too_big] to the caller, allowing TCP to adjust. *)letnew_pmtu=Int.max1280ptb.PTB.mtuinleterrored=Some`Packet_too_biginmatchDsts.findptb.PTB.destinationt.cachewith|Some{Dst.pmtu;next_hop;_}->letpmtu=Int.minpmtunew_pmtuinletvalue={Dst.pmtu;next_hop;errored}inletcache=Dsts.addptb.PTB.destinationvaluet.cachein{twithcache=Dsts.trimcache}|None->letvalue={Dst.pmtu=new_pmtu;next_hop=ptb.PTB.destination;errored}inletcache=Dsts.addptb.PTB.destinationvaluet.cachein{twithcache=Dsts.trimcache}end|_->t