AP: Add support for IPv4 neighbor entry management to the BSS bridge
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>
Wed, 24 Sep 2014 06:02:04 +0000 (23:02 -0700)
committerJouni Malinen <j@w1.fi>
Mon, 27 Oct 2014 23:08:29 +0000 (01:08 +0200)
This allows adding/deleting an IPv4 neighbor entry to/from the bridge,
to which the BSS belongs. This commit adds the needed functionality in
driver_nl80211.c for the Linux bridge implementation. In theory, this
could be shared with multiple Linux driver interfaces, but for now, only
the main nl80211 interface is supported.

Signed-off-by: Kyeyoon Park <kyeyoonp@qca.qualcomm.com>
src/ap/ap_drv_ops.h
src/drivers/driver.h
src/drivers/driver_nl80211.c

index 7cc9d7d..5303350 100644 (file)
@@ -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,
index 369c8d2..78db6a1 100644 (file)
@@ -2601,6 +2601,25 @@ struct wpa_driver_ops {
                           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
         * @triggers: wowlan triggers
index b8d886b..4ffb313 100644 (file)
@@ -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,
 };