package mnet
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
An implementation of TCP (Transmission Control Protocol) in OCaml for Miou & Solo5
Install
dune-project
Dependency
Authors
Maintainers
Sources
mnet-0.0.4.tbz
sha256=154c3b18bbb019535a4f37d1fefa3c6131b636e53316a63b395d030b186f2a59
sha512=894d12ff3140b294de1129f76893b295e73093101edec137ba2b2106059081f94385e3e3420c48cde14df6aefbef333bec12f5772c1b85c7ecc73dcde9e5bb8c
doc/src/mnet.ethernet/ethernet.ml.html
Source file ethernet.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 188let src = Logs.Src.create "mnet.ethernet" module Log = (val Logs.src_log src : Logs.LOG) module Packet = struct type protocol = ARPv4 | IPv4 | IPv6 type t = { src: Macaddr.t; dst: Macaddr.t; protocol: protocol option } let guard err fn = if fn () then Ok () else Error err let protocol_of_int = function | 0x0806 -> Some ARPv4 | 0x0800 -> Some IPv4 | 0x86dd -> Some IPv6 | _ -> None let protocol_to_int = function | ARPv4 -> 0x0806 | IPv4 -> 0x0800 | IPv6 -> 0x86dd let decode bstr ~len = let ( let* ) = Result.bind in let* () = guard `Invalid_ethernet_packet @@ fun () -> len >= 14 in let dst = Macaddr.of_octets_exn (Bstr.sub_string bstr ~off:0 ~len:6) in let src = Macaddr.of_octets_exn (Bstr.sub_string bstr ~off:6 ~len:6) in let protocol = Bstr.get_uint16_be bstr 12 in let protocol = protocol_of_int protocol in let payload = Slice_bstr.make ~off:14 ~len:(len - 14) bstr in Ok ({ src; dst; protocol }, payload) let encode_into t ?(off = 0) bstr = match t.protocol with | None -> Fmt.invalid_arg "Ethernet.Packet.encode_into: you must specify a protocol" | Some protocol -> let protocol = protocol_to_int protocol in Bstr.blit_from_string (Macaddr.to_octets t.dst) ~src_off:0 bstr ~dst_off:(off + 0) ~len:6; Bstr.blit_from_string (Macaddr.to_octets t.src) ~src_off:0 bstr ~dst_off:(off + 6) ~len:6; Bstr.set_uint16_be bstr 12 protocol end type protocol = Packet.protocol = ARPv4 | IPv4 | IPv6 type t = { net: Mkernel.Net.t ; mutable handler: handler ; mtu: int ; mac: Macaddr.t ; bstr_ic: Bstr.t ; bstr_oc: Bstr.t ; extern: extern option ; cnt: int Atomic.t } and 'a packet = { src: Macaddr.t option ; dst: Macaddr.t ; protocol: Packet.protocol ; payload: 'a } and extern = External : 'net hypercalls -> extern [@@unboxed] and 'net hypercalls = { device: 'net ; swr: 'net -> ?off:int -> ?len:int -> Bstr.t -> unit ; srd: 'net -> ?off:int -> ?len:int -> Bstr.t -> int } and handler = Slice_bstr.t packet -> unit exception Packet_ignored let mac { mac; _ } = mac let mtu { mtu; _ } = mtu let macaddr { mac; _ } = mac let { mac; _ } = Logs.Tag.add Mnet_tags.mac mac tags let uninteresting_packet _ = raise_notrace Packet_ignored let write_directly_into t ?len:plus (packet : (Bstr.t -> int) packet) = let fn = packet.payload in let src = Option.value ~default:t.mac packet.src in let = tags t Logs.Tag.empty in let pkt = { Packet.src; dst= packet.dst; protocol= Some packet.protocol } in (* NOTE(dinosaure): clean-up our buffer. *) Packet.encode_into pkt ~off:0 t.bstr_oc; let bstr = Bstr.sub t.bstr_oc ~off:14 ~len:(Bstr.length t.bstr_oc - 14) in Bstr.memset bstr ~off:0 ~len:(Bstr.length bstr) '\000'; let plus' = fn bstr in Option.iter (fun plus -> assert (plus = plus')) plus; Log.debug (fun m -> m ~tags "write ethernet packet src:%a -> dst:%a (%d byte(s))" Macaddr.pp src Macaddr.pp packet.dst plus'); Log.debug (fun m -> m ~tags "@[<hov>%a@]" (Hxd_string.pp Hxd.default) (Bstr.sub_string t.bstr_oc ~off:0 ~len:(14 + plus'))); (* TODO(dinosaure): we must figure out about the impact of such branch. We also should compare when we directly use [Mkernel.net.write] or if we can wrap [read]/[write] into an [External] value (to simplify the API). *) match t.extern with | None -> (* TODO(dinosaure): use [Mkernel.Net.write_into]. *) Mkernel.Net.write_bigstring t.net ~off:0 ~len:(14 + plus') t.bstr_oc | Some (External { device; swr; _ }) -> swr device ~off:0 ~len:(14 + plus') t.bstr_oc let of_interest t dst = Macaddr.compare dst t.mac == 0 || Macaddr.is_unicast dst == false let handler t bstr ~len = if len >= 14 then let = tags t Logs.Tag.empty in match Packet.decode bstr ~len with | Error _ -> let str = Bstr.sub_string t.bstr_ic ~off:0 ~len in Log.err (fun m -> m ~tags "Invalid Ethernet packet"); Log.err (fun m -> m ~tags "@[<hov>%a@]" (Hxd_string.pp Hxd.default) str) | Ok ({ Packet.protocol= Some protocol; src; dst }, payload) -> begin try if of_interest t dst then t.handler { src= Some src; dst; protocol; payload } with | Packet_ignored -> () | exn -> Log.err (fun m -> m ~tags "Unexpected exception from the user's handler: %s" (Printexc.to_string exn)) end | Ok _ -> () let rec daemon t = let len = match t.extern with | None -> Mkernel.Net.read_bigstring t.net t.bstr_ic | Some (External { device; srd; _ }) -> srd device t.bstr_ic in handler t t.bstr_ic ~len; daemon t let write_directly_into t ?len ?src ~dst ~protocol fn = let pkt = { src; dst; protocol; payload= fn } in write_directly_into t ?len pkt let write_frame t str = let len = String.length str in match t.extern with | None -> Mkernel.Net.write_string t.net ~off:0 ~len str | Some (External { device; swr; _ }) -> Bstr.blit_from_string str ~src_off:0 t.bstr_oc ~dst_off:0 ~len; swr device ~off:0 ~len t.bstr_oc let guard err fn = if fn () then Ok () else Error err type daemon = unit Miou.t let create ?(mtu = 1500) ?(handler = uninteresting_packet) ?hypercalls:extern mac net = let ( let* ) = Result.bind in let* () = guard `MTU_too_small @@ fun () -> mtu > 14 in (* enough for Ethernet packets *) let bstr_ic = Bstr.create (14 + mtu) in let bstr_oc = Bstr.create (14 + mtu) in (* NOTE(dinosaure): the first [Bstr.sub] does a [malloc()], then any [Bstr.sub] are cheap. We should use [Slice] instead of [Bstr]. TODO! *) let bstr_ic = Bstr.sub bstr_ic ~off:0 ~len:(14 + mtu) in let bstr_oc = Bstr.sub bstr_oc ~off:0 ~len:(14 + mtu) in let cnt = Atomic.make 0 in let t = { net; handler; mtu; mac; bstr_ic; bstr_oc; extern; cnt } in let daemon = Miou.async @@ fun () -> daemon t in Ok (daemon, t) let set_handler t handler = Atomic.incr t.cnt; t.handler <- handler; let = tags t Logs.Tag.empty in if Atomic.get t.cnt > 1 then Log.warn (fun m -> m ~tags "Ethernet handler modified more than once") let extend_handler_with t handler = let prev = t.handler in let handler pkt = try prev pkt with Packet_ignored -> handler pkt in t.handler <- handler let kill = Miou.cancel
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>