patchpanel: ndproxy: assign public v6 addr to guest-facing ifs

Currently there is no public IPv6 address assigned on the
guest-facing interfaces (arc bridges and taps) when device is on
IPv6 network. This is causing Linux choosing a wrong src address
on packets directly originated from host to guest and drop the
returning traffic.

This patch generates an EUI-64 address based on virtual interface
MAC address upon receiving an RA, and add it to the interface.

BUG=chromium:1069985
TEST=unit;fuzz
TEST=manual(deploy on octopus and verify pinging penguin from host)

Change-Id: Id3ae953df6b3c84411461294bbc8dbd236cef901
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2428652
Tested-by: Taoyu Li <taoyl@chromium.org>
Reviewed-by: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
Commit-Queue: Taoyu Li <taoyl@chromium.org>
diff --git a/patchpanel/ndproxy.h b/patchpanel/ndproxy.h
index 4a4427b..3857169 100644
--- a/patchpanel/ndproxy.h
+++ b/patchpanel/ndproxy.h
@@ -16,6 +16,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include <base/files/scoped_file.h>
 #include <base/macros.h>
@@ -47,6 +48,9 @@
                            const MacAddress& local_mac_addr,
                            uint8_t* out_frame);
 
+  static const nd_opt_prefix_info* GetPrefixInfoOption(const uint8_t* in_frame,
+                                                       ssize_t frame_len);
+
   // Given an extended |buffer|, find a proper frame buffer pointer so that
   // pt > buffer, and start of IP header (pt + ETH_H_LEN) is 4-bytes aligned.
   // In the worst case the size of usable buffer will be original size minus 3.
@@ -76,6 +80,11 @@
       const base::Callback<void(const std::string&, const std::string&)>&
           handler);
 
+  // Callback upon receiving prefix information from RA frame.
+  void RegisterOnRouterDiscoveryHandler(
+      const base::Callback<void(const std::string&, const std::string&)>&
+          handler);
+
   // To proxy between upstream interface and guest OS interface (eth0-arc_eth0)
   // Outbound RS, inbound RA, and bidirectional NS/NA will be proxied.
   bool AddInterfacePair(const std::string& ifname_physical,
@@ -88,6 +97,11 @@
   // Remove all proxy interface pair with ifindex.
   bool RemoveInterface(const std::string& ifname);
 
+  // Utility to get a list of guest interfaces names that are currently being
+  // proxied with a specific physical interface.
+  std::vector<std::string> GetGuestInterfaces(
+      const std::string& ifname_physical);
+
  private:
   // Data structure to store interface mapping for a certain kind of packet to
   // be proxied. For example, {1: {2}, 2: {1}} means that packet from interfaces
@@ -110,6 +124,7 @@
 
   interface_mapping* MapForType(uint8_t type);
   bool IsGuestInterface(int ifindex);
+  bool IsRouterInterface(int ifindex);
 
   // Socket used to communicate with kernel through ioctl. No real packet data
   // goes through this socket.
@@ -129,6 +144,8 @@
 
   base::Callback<void(const std::string&, const std::string&)>
       guest_discovery_handler_;
+  base::Callback<void(const std::string&, const std::string&)>
+      router_discovery_handler_;
 
   base::WeakPtrFactory<NDProxy> weak_factory_{this};
 
@@ -156,6 +173,16 @@
   void OnGuestIpDiscovery(const std::string& ifname,
                           const std::string& ip6addr);
 
+  // Callback from NDProxy core when receive prefix info from router
+  void OnRouterDiscovery(const std::string& ifname, const std::string& ip6addr);
+
+  void SendMessage(NDProxyMessage::NDProxyEventType type,
+                   const std::string& ifname,
+                   const std::string& ip6addr);
+
+  // Map from guest-facing ifname to eui address we assigned
+  std::map<std::string, std::string> guest_if_addrs_;
+
   // Utilize MessageDispatcher to watch control fd
   std::unique_ptr<MessageDispatcher> msg_dispatcher_;