diff --git a/src/ap.c b/src/ap.c index 77de596a..d6802fda 100644 --- a/src/ap.c +++ b/src/ap.c @@ -92,6 +92,10 @@ struct ap_state { struct l_dhcp_server *netconfig_dhcp; struct l_rtnl_address *netconfig_addr4; uint32_t rtnl_add_cmd; + uint32_t rtnl_get_gateway4_mac_cmd; + uint32_t rtnl_get_dns4_mac_cmd; + uint8_t netconfig_gateway4_mac[6]; + uint8_t netconfig_dns4_mac[6]; bool started : 1; bool gtk_set : 1; @@ -211,6 +215,16 @@ static void ap_reset(struct ap_state *ap) if (ap->rtnl_add_cmd) l_netlink_cancel(rtnl, ap->rtnl_add_cmd); + if (ap->rtnl_get_gateway4_mac_cmd) { + l_netlink_cancel(rtnl, ap->rtnl_get_gateway4_mac_cmd); + ap->rtnl_get_gateway4_mac_cmd = 0; + } + + if (ap->rtnl_get_dns4_mac_cmd) { + l_netlink_cancel(rtnl, ap->rtnl_get_dns4_mac_cmd); + ap->rtnl_get_dns4_mac_cmd = 0; + } + l_queue_destroy(ap->sta_states, ap_sta_free); if (ap->rates) @@ -1470,13 +1484,19 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta, if (lease_lifetime != 0xffffffff) ip_resp_info.ipv4_lifetime = lease_lifetime; - if (lease_gateway_str) + if (lease_gateway_str) { ip_resp_info.ipv4_gateway = IP4_FROM_STR(lease_gateway_str); + memcpy(ip_resp_info.ipv4_gateway_mac, + ap->netconfig_gateway4_mac, 6); + } - if (lease_dns_str_list && lease_dns_str_list[0]) + if (lease_dns_str_list && lease_dns_str_list[0]) { ip_resp_info.ipv4_dns = IP4_FROM_STR(lease_dns_str_list[0]); + memcpy(ip_resp_info.ipv4_dns_mac, + ap->netconfig_dns4_mac, 6); + } l_strv_free(lease_dns_str_list); sta->ip_alloc_sent = true; @@ -2561,6 +2581,104 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data) } } +static void ap_get_gateway4_mac_cb(int error, const uint8_t *hwaddr, + size_t hwaddr_len, void *user_data) +{ + struct ap_state *ap = user_data; + + ap->rtnl_get_gateway4_mac_cmd = 0; + + if (error) { + l_debug("Error: %s (%i)", strerror(-error), -error); + return; + } + + if (L_WARN_ON(unlikely(hwaddr_len != 6))) + return; + + l_debug("Resolved mac to " MAC, MAC_STR(hwaddr)); + memcpy(ap->netconfig_gateway4_mac, hwaddr, 6); +} + +static void ap_get_dns4_mac_cb(int error, const uint8_t *hwaddr, + size_t hwaddr_len, void *user_data) +{ + struct ap_state *ap = user_data; + + ap->rtnl_get_dns4_mac_cmd = 0; + + if (error) { + l_debug("Error: %s (%i)", strerror(-error), -error); + return; + } + + if (L_WARN_ON(unlikely(hwaddr_len != 6))) + return; + + l_debug("Resolved mac to " MAC, MAC_STR(hwaddr)); + memcpy(ap->netconfig_dns4_mac, hwaddr, 6); +} + +static void ap_query_macs(struct ap_state *ap, const char *addr_str, + uint8_t prefix_len, const char *gateway_str, + const char **dns_str_list) +{ + uint32_t local = IP4_FROM_STR(addr_str); + uint32_t gateway = 0; + uint32_t dns = 0; + uint32_t ifindex = netdev_get_ifindex(ap->netdev); + + /* + * For simplicity only check the ARP/NDP tables to see if we already + * have the MACs that we need. There doesn't seem to be an API to + * actually resolve addresses that are not in these tables other than + * by triggering IP traffic to those hosts, such as a ping. In a PC + * or mobile device scenario we're likely to have these MACs already, + * otherwise we give up as this is a pretty low-priority feature. + */ + + if (gateway_str) { + gateway = IP4_FROM_STR(gateway_str); + if (L_WARN_ON(unlikely(!gateway))) + return; + + if (gateway == local) + memcpy(ap->netconfig_gateway4_mac, + netdev_get_address(ap->netdev), 6); + else { + ap->rtnl_get_gateway4_mac_cmd = + l_rtnl_neighbor_get_hwaddr(rtnl, ifindex, + AF_INET, &gateway, + ap_get_gateway4_mac_cb, + ap, NULL); + if (!ap->rtnl_get_gateway4_mac_cmd) + l_debug("l_rtnl_neighbor_get_hwaddr() failed " + "for the gateway IP"); + } + } + + if (dns_str_list) { + dns = IP4_FROM_STR(dns_str_list[0]); + if (L_WARN_ON(unlikely(!dns))) + return; + + /* TODO: can also skip query if dns == gateway */ + if (dns == local) + memcpy(ap->netconfig_dns4_mac, + netdev_get_address(ap->netdev), 6); + else if (util_ip_subnet_match(prefix_len, &dns, &local)) { + ap->rtnl_get_dns4_mac_cmd = + l_rtnl_neighbor_get_hwaddr(rtnl, ifindex, + AF_INET, &dns, + ap_get_dns4_mac_cb, + ap, NULL); + if (!ap->rtnl_get_dns4_mac_cmd) + l_debug("l_rtnl_neighbor_get_hwaddr() failed " + "for the DNS IP"); + } + } +} + #define AP_DEFAULT_IPV4_PREFIX_LEN 28 static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list, @@ -2689,6 +2807,8 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list, ap->netconfig_set_addr4 = false; } + ap_query_macs(ap, addr_str_buf, prefix_len, gateway_str, dns_str_list); + cleanup: l_dhcp_server_destroy(dhcp); l_rtnl_address_free(new_addr);