diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 7cc9d7dea..53033501e 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -280,6 +280,26 @@ static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, return hapd->driver->status(hapd->drv_priv, buf, buflen); } +static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd, + be32 ipaddr, int prefixlen, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_add_ip_neigh == NULL) + return -1; + return hapd->driver->br_add_ip_neigh(hapd->drv_priv, ipaddr, prefixlen, + addr); +} + +static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd, + be32 ipaddr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_delete_ip_neigh == NULL) + return -1; + return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, ipaddr); +} + static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, int vendor_id, int subcmd, const u8 *data, size_t data_len, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 369c8d2d5..78db6a11e 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2600,6 +2600,25 @@ struct wpa_driver_ops { int (*set_qos_map)(void *priv, const u8 *qos_map_set, u8 qos_map_set_len); + /** + * br_add_ip_neigh - Add a neigh to the bridge ip neigh table + * @priv: Private driver interface data + * @ipaddr: IPv4 address for the neigh entry + * @prefixlen: IPv4 address netmask prefix length + * @addr: Corresponding MAC address + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_add_ip_neigh)(void *priv, be32 ipaddr, int prefixlen, + const u8 *addr); + + /** + * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table + * @priv: Private driver interface data + * @ipaddr: IPv4 address for the neigh entry + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_delete_ip_neigh)(void *priv, be32 ipaddr); + /** * set_wowlan - Set wake-on-wireless triggers * @priv: Private driver interface data diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b8d886b2c..4ffb313c3 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -8835,6 +8835,145 @@ nla_put_failure: #endif /* CONFIG_MESH */ +static int wpa_driver_br_add_ip_neigh(void *priv, be32 ipaddr, + int prefixlen, const u8 *addr) +{ +#ifdef CONFIG_LIBNL3_ROUTE + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr = NULL; + struct nl_addr *nl_lladdr = NULL; + int res; + + if (ipaddr == 0 || prefixlen == 0 || !addr) + return -EINVAL; + + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set before adding an ip neigh to it"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + rn = rtnl_neigh_alloc(); + if (rn == NULL) + return -ENOMEM; + + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(AF_INET, &ipaddr, sizeof(ipaddr)); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + nl_addr_set_prefixlen(nl_ipaddr, prefixlen); + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } + + /* set the corresponding lladdr for neigh */ + nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN); + if (nl_lladdr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed"); + res = -ENOMEM; + goto errout; + } + rtnl_neigh_set_lladdr(rn, nl_lladdr); + + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + rtnl_neigh_set_state(rn, NUD_PERMANENT); + + res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Adding bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_lladdr) + nl_addr_put(nl_lladdr); + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ +} + + +static int wpa_driver_br_delete_ip_neigh(void *priv, be32 ipaddr) +{ +#ifdef CONFIG_LIBNL3_ROUTE + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr; + int res; + + if (ipaddr == 0) + return -EINVAL; + + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set to delete an ip neigh"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + rn = rtnl_neigh_alloc(); + if (rn == NULL) + return -ENOMEM; + + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(AF_INET, &ipaddr, sizeof(ipaddr)); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } + + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + + res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Deleting bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -8933,4 +9072,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .join_mesh = wpa_driver_nl80211_join_mesh, .leave_mesh = wpa_driver_nl80211_leave_mesh, #endif /* CONFIG_MESH */ + .br_add_ip_neigh = wpa_driver_br_add_ip_neigh, + .br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh, };