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.cc b/patchpanel/ndproxy.cc
index 4f7b051..996502e 100644
--- a/patchpanel/ndproxy.cc
+++ b/patchpanel/ndproxy.cc
@@ -277,6 +277,35 @@
}
}
+ // On receiving RA from router, generate an address for each guest-facing
+ // interface, and sent it to DeviceManager so it can be assigned. This address
+ // will be used when directly communicating with guest OS through IPv6.
+ if (icmp6->icmp6_type == ND_ROUTER_ADVERT &&
+ IsRouterInterface(dst_addr.sll_ifindex) &&
+ !router_discovery_handler_.is_null()) {
+ const nd_opt_prefix_info* prefix_info =
+ GetPrefixInfoOption(in_frame_buffer_, len);
+ if (prefix_info != nullptr && prefix_info->nd_opt_pi_prefix_len <= 64) {
+ // Generate an EUI-64 address from virtual interface MAC. A prefix
+ // larger that /64 is required.
+ for (int target_if : if_map_ra_[dst_addr.sll_ifindex]) {
+ MacAddress local_mac;
+ if (!GetLocalMac(target_if, &local_mac))
+ continue;
+ in6_addr eui64_ip;
+ GenerateEUI64Address(&eui64_ip, prefix_info->nd_opt_pi_prefix,
+ local_mac);
+ char eui64_addr_str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &eui64_ip, eui64_addr_str, INET6_ADDRSTRLEN);
+ char target_ifname[IFNAMSIZ];
+ if_indextoname(target_if, target_ifname);
+ router_discovery_handler_.Run(std::string(target_ifname),
+ std::string(eui64_addr_str));
+ }
+ }
+ }
+
+ // Translate the NDP frame and send it through proxy interface
auto map_entry = MapForType(icmp6->icmp6_type)->find(dst_addr.sll_ifindex);
if (map_entry == MapForType(icmp6->icmp6_type)->end())
return;
@@ -336,6 +365,23 @@
}
}
+const nd_opt_prefix_info* NDProxy::GetPrefixInfoOption(const uint8_t* in_frame,
+ ssize_t frame_len) {
+ for (const uint8_t* ptr =
+ in_frame + ETH_HLEN + sizeof(ip6_hdr) + sizeof(nd_router_advert);
+ ptr + offsetof(nd_opt_hdr, nd_opt_len) < in_frame + frame_len &&
+ (reinterpret_cast<const nd_opt_hdr*>(ptr))->nd_opt_len > 0;
+ ptr += (reinterpret_cast<const nd_opt_hdr*>(ptr))->nd_opt_len
+ << 3) /* nd_opt_len is in 8 bytes */ {
+ const nd_opt_hdr* opt = reinterpret_cast<const nd_opt_hdr*>(ptr);
+ if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
+ opt->nd_opt_len << 3 == sizeof(nd_opt_prefix_info)) {
+ return reinterpret_cast<const nd_opt_prefix_info*>(opt);
+ }
+ }
+ return nullptr;
+}
+
bool NDProxy::GetLocalMac(int if_id, MacAddress* mac_addr) {
ifreq ifr = {
.ifr_ifindex = if_id,
@@ -458,6 +504,12 @@
guest_discovery_handler_ = handler;
}
+void NDProxy::RegisterOnRouterDiscoveryHandler(
+ const base::Callback<void(const std::string&, const std::string&)>&
+ handler) {
+ router_discovery_handler_ = handler;
+}
+
NDProxy::interface_mapping* NDProxy::MapForType(uint8_t type) {
switch (type) {
case ND_ROUTER_SOLICIT:
@@ -556,6 +608,24 @@
return if_map_rs_.find(ifindex) != if_map_rs_.end();
}
+bool NDProxy::IsRouterInterface(int ifindex) {
+ return if_map_ra_.find(ifindex) != if_map_ra_.end();
+}
+
+std::vector<std::string> NDProxy::GetGuestInterfaces(
+ const std::string& ifname_physical) {
+ std::vector<std::string> result;
+ int ifid_physical = if_nametoindex(ifname_physical.c_str());
+ if (ifid_physical == 0)
+ return result;
+ for (int ifid_guest : if_map_ra_[ifid_physical]) {
+ char ifname[IFNAMSIZ];
+ if_indextoname(ifid_guest, ifname);
+ result.push_back(ifname);
+ }
+ return result;
+}
+
NDProxyDaemon::NDProxyDaemon(base::ScopedFD control_fd)
: msg_dispatcher_(
std::make_unique<MessageDispatcher>(std::move(control_fd))) {}
@@ -586,6 +656,8 @@
}
proxy_.RegisterOnGuestIpDiscoveryHandler(base::Bind(
&NDProxyDaemon::OnGuestIpDiscovery, weak_factory_.GetWeakPtr()));
+ proxy_.RegisterOnRouterDiscoveryHandler(base::Bind(
+ &NDProxyDaemon::OnRouterDiscovery, weak_factory_.GetWeakPtr()));
// Initialize data fd
fd_ = NDProxy::PreparePacketSocket();
@@ -618,8 +690,22 @@
if (msg.has_teardown()) {
if (msg.has_br_ifname()) {
proxy_.RemoveInterfacePair(dev_ifname, msg.br_ifname());
+ if (guest_if_addrs_.find(msg.br_ifname()) != guest_if_addrs_.end()) {
+ SendMessage(NDProxyMessage::DEL_ADDR, msg.br_ifname(),
+ guest_if_addrs_[msg.br_ifname()]);
+ guest_if_addrs_.erase(msg.br_ifname());
+ }
+
} else {
+ auto guest_ifs = proxy_.GetGuestInterfaces(dev_ifname);
proxy_.RemoveInterface(dev_ifname);
+ for (const auto& guest_if : guest_ifs) {
+ if (guest_if_addrs_.find(guest_if) != guest_if_addrs_.end()) {
+ SendMessage(NDProxyMessage::DEL_ADDR, guest_if,
+ guest_if_addrs_[guest_if]);
+ guest_if_addrs_.erase(guest_if);
+ }
+ }
}
} else if (msg.has_br_ifname()) {
proxy_.AddInterfacePair(dev_ifname, msg.br_ifname());
@@ -628,14 +714,32 @@
void NDProxyDaemon::OnGuestIpDiscovery(const std::string& ifname,
const std::string& ip6addr) {
- // Send information back to DeviceManager
+ SendMessage(NDProxyMessage::ADD_ROUTE, ifname, ip6addr);
+}
+
+void NDProxyDaemon::OnRouterDiscovery(const std::string& ifname,
+ const std::string& ip6addr) {
+ std::string current_addr = guest_if_addrs_[ifname];
+ if (current_addr == ip6addr)
+ return;
+ if (!current_addr.empty()) {
+ SendMessage(NDProxyMessage::DEL_ADDR, ifname, current_addr);
+ }
+ SendMessage(NDProxyMessage::ADD_ADDR, ifname, ip6addr);
+ guest_if_addrs_[ifname] = ip6addr;
+}
+
+void NDProxyDaemon::SendMessage(NDProxyMessage::NDProxyEventType type,
+ const std::string& ifname,
+ const std::string& ip6addr) {
if (!msg_dispatcher_)
return;
- DeviceMessage msg;
- msg.set_dev_ifname(ifname);
- msg.set_guest_ip6addr(ip6addr);
+ NDProxyMessage msg;
+ msg.set_type(type);
+ msg.set_ifname(ifname);
+ msg.set_ip6addr(ip6addr);
IpHelperMessage ipm;
- *ipm.mutable_device_message() = msg;
+ *ipm.mutable_ndproxy_message() = msg;
msg_dispatcher_->SendMessage(ipm);
}