package charrua-client
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
DHCP client implementation
Install
dune-project
Dependency
Authors
Maintainers
Sources
charrua-3.2.1.tbz
sha256=23feba77dd924883d33605a24b442ebe6ce873e673bc634c2c7b8b82843c3e05
sha512=055a944fa60c02f383ffce231de77b31bdf67ee60cf98523b6d3e547ec3cba069af8b3175293ce3990a6884e30fb82f180bd16ca29517b819705fa2e69587b00
doc/src/charrua-client.lwt/dhcp_client_lwt.ml.html
Source file dhcp_client_lwt.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222let src = Logs.Src.create "dhcp_client_lwt" module Log = (val Logs.src_log src : Logs.LOG) module Make (Net : Mirage_net.S) = struct open Lwt.Infix type lease = Dhcp_wire.pkt type t = { lease : lease Lwt_mvar.t; net : Net.t; mutable listen : Cstruct.t -> unit Lwt.t; stop : (unit, Net.error) result Lwt.t; listener_condition : unit Lwt_condition.t; } let lease_mvar t = t.lease let connect ?(renew = true) ?xid ?options ?requests net = (* listener needs to occasionally check to see whether the state has advanced, * and if not, start a new attempt at a lease transaction *) let sleep_interval = Duration.of_sec 4 in let header_size = Ethernet.Packet.sizeof_ethernet in let size = Net.mtu net + header_size in let xid = match xid with | None -> Randomconv.int32 Mirage_crypto_rng.generate | Some xid -> xid in let (client, dhcpdiscover) = Dhcp_client.create ?options ?requests xid (Net.mac net) in let c = ref client in let cond = Lwt_condition.create () in let rec do_renew lease = let renewal = Dhcp_wire.find_renewal_t1 lease.Dhcp_wire.options |> Option.value ~default:1800l |> Int32.unsigned_to_int |> Option.value ~default:Int.max_int in let t2 (* rebinding *) = Dhcp_wire.find_rebinding_t2 lease.Dhcp_wire.options |> Option.value ~default:75600l (* 21h = (7/8)*24h *) |> Int32.unsigned_to_int |> Option.value ~default:Int.max_int in let expiry = Dhcp_wire.find_ip_lease_time lease.Dhcp_wire.options |> Option.value ~default:86400l (* 24h *) |> Int32.unsigned_to_int |> Option.value ~default:Int.max_int in let t2 = Mirage_sleep.ns @@ Duration.of_sec t2 in let expiry = Mirage_sleep.ns @@ Duration.of_sec expiry in let new_lease = Lwt_condition.wait cond >|= fun lease -> `Lease lease in Mirage_sleep.ns @@ Duration.of_sec renewal >>= fun () -> let rec send_renewal () = match Dhcp_client.renew !c with | `Noop -> Lwt.return `Can't_renew | `Response (updated_c, pkt) -> c := updated_c; Log.debug (fun f -> f "attempted to renew lease: %a" Dhcp_client.pp updated_c); Net.write net ~size (Dhcp_wire.pkt_into_buf pkt) >>= function | Error e -> Lwt.return (`Failed_to_write e) | Ok () -> Mirage_sleep.ns sleep_interval >>= send_renewal in Lwt.pick [ new_lease; send_renewal (); (t2 >|= fun () -> `T2_rebinding); (expiry >|= fun () -> `Expired); ] >>= function | `Lease lease -> do_renew lease | `Can't_renew -> Log.debug (fun f -> f "Can't renew this lease; won't try"); Lwt.return_unit | `T2_rebinding -> do_rebind expiry | `Expired -> Log.warn (fun f -> f "Lease expired before we could renew"); failwith "DHCP lease expired" | `Failed_to_write e -> Log.err (fun f -> f "Failed to write lease renewal request: %a" Net.pp_error e); Lwt.return_unit and do_rebind expiry = let new_lease = Lwt_condition.wait cond >|= fun lease -> `Lease lease in let rec send_rebind () = match Dhcp_client.rebind !c with | `Noop -> Lwt.return `Can't_renew | `Response (updated_c, pkt) -> c := updated_c; Log.debug (fun f -> f "attempted to rebind lease: %a" Dhcp_client.pp updated_c); Net.write net ~size (Dhcp_wire.pkt_into_buf pkt) >>= function | Error e -> Lwt.return (`Failed_to_write e) | Ok () -> Mirage_sleep.ns sleep_interval >>= send_rebind in Lwt.pick [ new_lease; send_rebind (); (expiry >|= fun () -> `Expired); ] >>= function | `Lease lease -> do_renew lease | `Can't_renew -> Log.debug (fun f -> f "Can't renew this lease; won't try"); Lwt.return_unit | `Expired -> Log.warn (fun f -> f "Lease expired before we could renew"); failwith "DHCP lease expired" | `Failed_to_write e -> Log.err (fun f -> f "Failed to write lease renewal request: %a" Net.pp_error e); Lwt.return_unit in let rec get_lease cond dhcpdiscover = Log.debug (fun f -> f "Sending DHCPDISCOVER..."); Net.write net ~size (Dhcp_wire.pkt_into_buf dhcpdiscover) >>= function | Error e -> Log.err (fun f -> f "Failed to write initial lease discovery request: %a" Net.pp_error e); Lwt.return_unit | Ok () -> Lwt.pick [ (Lwt_condition.wait cond >|= fun lease -> `Lease lease); (Mirage_sleep.ns sleep_interval >|= fun () -> `Timeout); ] >>= function | `Lease lease -> if renew then do_renew lease else Lwt.return_unit | `Timeout -> let xid = Randomconv.int32 Mirage_crypto_rng.generate in let (client, dhcpdiscover) = Dhcp_client.create ?requests xid (Net.mac net) in c := client; Log.info (fun f -> f "Timeout expired without a usable lease! Starting over..."); Log.debug (fun f -> f "New lease attempt: %a" Dhcp_client.pp !c); get_lease cond dhcpdiscover in let listen t cond = Net.listen t.net ~header_size (fun buf -> match Dhcp_client.input !c buf with | `Noop -> Lwt.return_unit | `Not_dhcp -> t.listen buf | `Response (s, action) -> begin Net.write net ~size (Dhcp_wire.pkt_into_buf action) >>= function | Error e -> Log.err (fun f -> f "Failed to write lease transaction response: %a" Net.pp_error e); Lwt.return_unit | Ok () -> Log.debug (fun f -> f "State advanced! Now %a" Dhcp_client.pp s); c := s; Lwt.return_unit end | `New_lease (s, l) -> let open Dhcp_wire in (* a lease is obtained! Note it, and replace the current listener *) Log.info (fun f -> f "Lease obtained! IP: %a, routers: %a" Ipaddr.V4.pp l.yiaddr (Fmt.list Ipaddr.V4.pp) (collect_routers l.options)); Lwt_mvar.put t.lease l >>= fun () -> c := s; Lwt_condition.broadcast cond l; Lwt.return_unit ) in let lease_wrapper t stop_waker = Lwt.all [ (listen t cond >|= fun r -> Lwt.wakeup_later stop_waker r); (get_lease cond dhcpdiscover); ] >|= fun _units -> () in let lease = Lwt_mvar.create_empty () in let stop, stop_waker = Lwt.task () in let t = { lease; net; listen = Fun.const Lwt.return_unit; stop; listener_condition = Lwt_condition.create () } in Lwt.async (fun () -> lease_wrapper t stop_waker); Lwt.return t let connect_no_dhcp net = let lease = Lwt_mvar.create_empty () in let stop, stop_waker = Lwt.task () in let t = { lease; net; listen = Fun.const Lwt.return_unit; stop ; listener_condition = Lwt_condition.create ()} in let task = Lwt_condition.wait t.listener_condition >>= fun () -> let listen frame = t.listen frame in Net.listen t.net ~header_size:Ethernet.Packet.sizeof_ethernet listen >|= fun r -> Lwt.wakeup_later stop_waker r in Lwt.async (fun () -> task); Lwt.return t let listen' t fn = t.listen <- fn; Lwt_condition.broadcast t.listener_condition (); (* Callers of listen don't expect cancelling to cancel all other calls on listen. So we return a [_ Lwt.t] that can't cancel [t.stop]. *) Lwt.protected t.stop let listen t ~header_size fn = (* can this ever not be ethernet?! *) assert (header_size = Ethernet.Packet.sizeof_ethernet); listen' t fn type error = Net.error let pp_error = Net.pp_error let disconnect t = Net.disconnect t.net let write t = Net.write t.net let mac t = Net.mac t.net let mtu t = Net.mtu t.net let get_stats_counters t = Net.get_stats_counters t.net let reset_stats_counters t = Net.reset_stats_counters t.net end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>