mirage / mirage-tcpip

TCP/IP networking stack in pure OCaml, using the Mirage platform libraries. Includes IPv4/6, ICMP, and UDP/TCP support.
https://mirage.io
ISC License
341 stars 87 forks source link

Multicast Listening on Unikernel? #362

Open gabrik opened 6 years ago

gabrik commented 6 years ago

Hi all, I'm not sure this is an issue. There is the possibility to listen on some multicast address when using mirage-tcpip inside an unikernel? I see some ~group parameter in dhcp_ipv4_stack but I'm not sure is used for multicast groups. I'm trying to modify the simple TCP example to be able to listen also to UDP multicast.

config.ml:

open Mirage

let main = foreign ~packages: [package "duration"; package "fmt"] "Unikernel.Main"  (stackv4 @-> console @-> job)
let stack = dhcp_ipv4_stack ~group:"239.0.0.5" default_network
let () =
  register "services" [ main $ stack $ default_console]

unikernel.ml:

open Lwt.Infix

module Main (S: Mirage_types_lwt.STACKV4) (C: Mirage_types_lwt.CONSOLE) = struct
  let report_and_close flow pp e message =
    let ip, port = S.TCPV4.dst flow in
    Logs.warn
      (fun m -> m "closing connection from %a:%d due to error %a while %s"
          Ipaddr.V4.pp_hum ip port pp e message);
    S.TCPV4.close flow

  let task duration marker =
    ignore @@ OS.Time.sleep_ns (Duration.of_sec duration);
    Lwt.return @@ marker ^ marker

  let listener console udp port ~src ~dst ~src_port buf =
    let s = Format.asprintf "Data from %s to %s" (Ipaddr.V4.to_string src) (Ipaddr.V4.to_string dst) in
    ignore @@ C.log console s;
    let src' = (Ipaddr.V4 dst), port in
    let dst' = (Ipaddr.V4 src), src_port in
    S.UDPV4.write ~src_port:port ~dst:src ~dst_port:src_port udp buf >>= function
          | Ok () -> Lwt.return_unit
          | Error e -> Lwt.return_unit

  let rec echo flow =
    S.TCPV4.read flow >>= function
    | Error e -> report_and_close flow S.TCPV4.pp_error e "reading in Echo"
    | Ok `Eof -> report_and_close flow Fmt.string "end of file" "reading in Echo"
    | Ok (`Data buf) ->

      Lwt.choose[(task 1 "+");(task 2 "=")] >>= (
        fun x ->
          let b = Cstruct.of_string x
          in
          S.TCPV4.write flow b >>= function
          | Ok () -> echo flow
          | Error e -> report_and_close flow S.TCPV4.pp_write_error e "writing in Echo")

  let start s c =

    S.listen_udpv4 s ~port:7 (listener c (S.udpv4 s) 7);
    S.listen_tcpv4 s ~port:7 echo;
    S.listen s

end

These is a way to do that? I need a new direct stack with UDP?

hannesm commented 4 years ago

Dear @gabrik, thanks for your report. The "group" argument of dhcp_ipv4_stack is just a name (used for command-line / boot arguments), and has no relation with multicast.

The mirage-tcpip stack does not have appropriate multicast support. From the Static_ipv4 module on the receiving side:

      let of_interest ip =
        Ipaddr.V4.(compare ip (Prefix.address t.cidr) = 0
                   || compare ip broadcast = 0
                   || compare ip (Prefix.broadcast t.cidr) = 0)
      in
      if not (of_interest packet.dst) then begin
        Log.debug (fun m -> m "dropping IP fragment not for us or broadcast %a"
                      Ipv4_packet.pp packet);
        Lwt.return_unit

This means that all packets which are not broadcast or unicast to the configured IP address are dropped.

If you're interested in implementing multicast into tcpip, please let us know and we can help where which changes would be necessary.