[Anima] GRASP DULL, IPv6 LL scope and multicast and BSD sockets API

Michael Richardson <mcr+ietf@sandelman.ca> Tue, 06 April 2021 16:49 UTC

Return-Path: <mcr+ietf@sandelman.ca>
X-Original-To: anima@ietfa.amsl.com
Delivered-To: anima@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id DAC243A2897 for <anima@ietfa.amsl.com>; Tue, 6 Apr 2021 09:49:56 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.899
X-Spam-Level:
X-Spam-Status: No, score=-1.899 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Z7eIRsfZbnjB for <anima@ietfa.amsl.com>; Tue, 6 Apr 2021 09:49:52 -0700 (PDT)
Received: from tuna.sandelman.ca (tuna.sandelman.ca [209.87.249.19]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 4E0753A2895 for <anima@ietf.org>; Tue, 6 Apr 2021 09:49:52 -0700 (PDT)
Received: from localhost (localhost [127.0.0.1]) by tuna.sandelman.ca (Postfix) with ESMTP id 332E638EC4; Tue, 6 Apr 2021 12:56:36 -0400 (EDT)
Received: from tuna.sandelman.ca ([127.0.0.1]) by localhost (localhost [127.0.0.1]) (amavisd-new, port 10024) with LMTP id AZ5k-H5Fd4To; Tue, 6 Apr 2021 12:56:35 -0400 (EDT)
Received: from sandelman.ca (obiwan.sandelman.ca [IPv6:2607:f0b0:f:2::247]) by tuna.sandelman.ca (Postfix) with ESMTP id 3574238EC2; Tue, 6 Apr 2021 12:56:35 -0400 (EDT)
Received: from localhost (localhost [IPv6:::1]) by sandelman.ca (Postfix) with ESMTP id 2B39C6A7; Tue, 6 Apr 2021 12:49:49 -0400 (EDT)
From: Michael Richardson <mcr+ietf@sandelman.ca>
To: Brian E Carpenter <brian.e.carpenter@gmail.com>, anima@ietf.org
X-Mailer: MH-E 8.6+git; nmh 1.7+dev; GNU Emacs 26.1
X-Face: $\n1pF)h^`}$H>Hk{L"x@)JS7<%Az}5RyS@k9X%29-lHB$Ti.V>2bi.~ehC0; <'$9xN5Ub# z!G,p`nR&p7Fz@^UXIn156S8.~^@MJ*mMsD7=QFeq%AL4m<nPbLgmtKK-5dC@#:k
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha512; protocol="application/pgp-signature"
Date: Tue, 06 Apr 2021 12:49:49 -0400
Message-ID: <7643.1617727789@localhost>
Archived-At: <https://mailarchive.ietf.org/arch/msg/anima/kPWcyY2DRmZvdA-27mYBM_nE8DU>
Subject: [Anima] GRASP DULL, IPv6 LL scope and multicast and BSD sockets API
X-BeenThere: anima@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Autonomic Networking Integrated Model and Approach <anima.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/anima>, <mailto:anima-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/anima/>
List-Post: <mailto:anima@ietf.org>
List-Help: <mailto:anima-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/anima>, <mailto:anima-request@ietf.org?subject=subscribe>
X-List-Received-Date: Tue, 06 Apr 2021 16:49:57 -0000

Brian, I think that I am making a mistake in how I am binding my IPv6
multicast sockets on which I expect to hear GRASP DULL messages.

1) I create a socket with an unspecified address, and the GRASP_PORT (7017):
        let rsin6 = SocketAddrV6::new(Ipv6Addr::UNSPECIFIED,
                                     grasp::GRASP_PORT as u16, 0, ifindex);

        let recv_try = UdpSocket::bind(rsin6).await;

     Note that I had originally expected that I should bind it to the IPv6-LL of
     the interface, but that meant that the socket does not match the
     multicast destinations.

     I mark SO_REUSEPORT, and SO_REUSEADDR on this socket.

2) I join the multicast address for that socket:
                let grasp_mcast = "FF02:0:0:0:0:0:0:13".parse::<Ipv6Addr>().unwrap();
                recv.join_multicast_v6(&grasp_mcast, ifindex).unwrap();

   Note that "ifindex" is the scope of the interface that I want to bind.

3) If that works, then, in order to send, I create a new socket, which I do
   not bind to multicast, but I do bind it to the interface:

                let ssin6 = SocketAddrV6::new(Ipv6Addr::UNSPECIFIED,
                                              0 as u16, 0, ifindex);
                let send = UdpSocket::bind(ssin6).await.unwrap();

   maybe I could use the above socket, but I think it is easier to have two
   sockets as it simplies threads.

I have been binding a multicast listening socket for each "physical" interface.
In testing with more than one interface I realized two things:

a) I hear myself on the same interface.  That is, M_FLOODs about address
   fe80::1234, are heard on the multicast socket bound to fe80::1234.
   Okay, I think, just filter those out since they come from "me"

b) Oops, I hear myself on the other interfaces.
   So, M_FLOODs about fe80::1234 on ifindex 2 are heard on ifindex 3,
   (call it "fe80::abcd").  They aren't "me", so I actually have to talk my
   list of interfaces and filter out all the "me".

   I realized that the originating sockaddr provided in recvfrom() also
   has an sin6_scope which is filled in, so the multicasts which loop back
   internally can be filtered out by listening on for things which
   are on the ifindex I wanted to listen to.
   But, I still have to filter for the list of "me", because it could be
   that fe80::1234 and fe80::abcd are actually two ports in the same L2 domain.

I actually ran into the last bit when looking at the IPsec policies that were
being created.  My IKEv2 daemon gets cross when you ask it to initiate to
a peer which it is convinced it also self.

I may have made some mistakes setting the IP TTL of my packets.
I think they are set to 1.
(The GRASP TTL was incorrectly set to that as well, which I fixed already.)

I have been creating a GraspDaemon thread per interface.

Since this is really just a Rust Async co-routine (a "green thread"), and not
an actual system thread, I feel that the simplicity of just having the
simplest of loops running outweighs the potential scaling issue of having
hundreds of these running.  The co-routine mechanism means that this all just
leads down to an event loop and a call to select(2)/poll(2)/epoll(2),
etc. all handled by tokio and the compiler and not me.

But now, I'm thinking that I should have just done a single Grasp(DULL)
daemon receive thread, listening on all interfaces.  I can't really remember
why I didn't do that.  Maybe because I thought I would need a multicast
socket per physical interface.

On the transmit side, I have multiple loops sending, but that is easily
merged into a single loop, and it would have the advantage that I could more
easily stagger DULL announcements across different interfaces.
I'm not actually sure how often M_FLOODs are supposed to be sent.
I scanned through ACP (section 6.4) and through GRASP-15, and I didn't see anything.

That is:
     loop {
          sleep(60s +- rand(10));
          send-M_FLOOD-on-next-interface;
     }

rather than:
     loop {
          sleep(60s);
          for if in interfaces {
              send-M_FLOOD-on(if)
          }
     }


--
Michael Richardson <mcr+IETF@sandelman.ca>   . o O ( IPv6 IøT consulting )
           Sandelman Software Works Inc, Ottawa and Worldwide