1 /*
2 * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: wlan_hdd_subnet_detect.c
22 *
23 * WLAN Host Device Driver subnet detect API implementation
24 */
25
26 #include <linux/version.h>
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <net/cfg80211.h>
30 #include <ani_global.h>
31 #include "sme_api.h"
32 #include "osif_sync.h"
33 #include "wlan_hdd_main.h"
34 #include "wlan_hdd_subnet_detect.h"
35 #include <qca_vendor.h>
36 #include "wlan_dp_ucfg_api.h"
37 #include "wlan_hdd_object_manager.h"
38
39 /*
40 * define short names for the global vendor params
41 * used by __wlan_hdd_cfg80211_set_gateway_params()
42 */
43 #define PARAM_MAC_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_GW_MAC_ADDR
44 #define PARAM_IPV4_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV4_ADDR
45 #define PARAM_IPV6_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV6_ADDR
46
47 const struct nla_policy subnet_detect_policy[
48 QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX + 1] = {
49 [QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_GW_MAC_ADDR] =
50 VENDOR_NLA_POLICY_MAC_ADDR,
51 [QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV4_ADDR] =
52 VENDOR_NLA_POLICY_IPV4_ADDR,
53 [QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV6_ADDR] =
54 VENDOR_NLA_POLICY_IPV6_ADDR,
55 };
56
57 /**
58 * __wlan_hdd_cfg80211_set_gateway_params() - set gateway params
59 * @wiphy: Pointer to wireless phy
60 * @wdev: Pointer to wireless device
61 * @data: Pointer to data
62 * @data_len: Data length
63 *
64 * Return: 0 on success, negative errno on failure
65 */
__wlan_hdd_cfg80211_set_gateway_params(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)66 static int __wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy,
67 struct wireless_dev *wdev,
68 const void *data,
69 int data_len)
70 {
71 struct net_device *dev = wdev->netdev;
72 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
73 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
74 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX + 1];
75 struct gateway_update_req_param req = { 0 };
76 int ret;
77 QDF_STATUS status;
78 bool subnet_detection_enabled;
79 struct wlan_objmgr_vdev *vdev;
80
81 hdd_enter_dev(dev);
82
83 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
84 hdd_err("Command not allowed in FTM mode");
85 return -EPERM;
86 }
87
88 ret = wlan_hdd_validate_context(hdd_ctx);
89 if (0 != ret)
90 return ret;
91
92 /* user may have disabled the feature in INI */
93 ucfg_mlme_is_subnet_detection_enabled(hdd_ctx->psoc,
94 &subnet_detection_enabled);
95 if (!subnet_detection_enabled) {
96 hdd_debug("LFR Subnet Detection disabled in INI");
97 return -ENOTSUPP;
98 }
99
100 /* The gateway parameters are only valid in the STA persona
101 * and only in the connected state.
102 */
103 if (QDF_STA_MODE != adapter->device_mode) {
104 hdd_debug("Received GW param update for non-STA mode adapter");
105 return -ENOTSUPP;
106 }
107
108 if (!hdd_cm_is_vdev_associated(adapter->deflink)) {
109 hdd_debug("Received GW param update in disconnected state!");
110 return -ENOTSUPP;
111 }
112
113 /* Extract NL parameters
114 * mac_addr: 6 bytes
115 * ipv4 addr: 4 bytes
116 * ipv6 addr: 16 bytes
117 */
118 if (wlan_cfg80211_nla_parse(tb,
119 QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX,
120 data, data_len, subnet_detect_policy)) {
121 hdd_err("Invalid ATTR list");
122 return -EINVAL;
123 }
124
125 if (!tb[PARAM_MAC_ADDR]) {
126 hdd_err("request mac addr failed");
127 return -EINVAL;
128 }
129 nla_memcpy(req.gw_mac_addr.bytes, tb[PARAM_MAC_ADDR],
130 QDF_MAC_ADDR_SIZE);
131
132 /* req ipv4_addr_type and ipv6_addr_type are initially false due
133 * to zeroing the struct
134 */
135 if (tb[PARAM_IPV4_ADDR]) {
136 nla_memcpy(req.ipv4_addr, tb[PARAM_IPV4_ADDR],
137 QDF_IPV4_ADDR_SIZE);
138 req.ipv4_addr_type = true;
139 }
140
141 if (tb[PARAM_IPV6_ADDR]) {
142 nla_memcpy(&req.ipv6_addr, tb[PARAM_IPV6_ADDR],
143 QDF_IPV6_ADDR_SIZE);
144 req.ipv6_addr_type = true;
145 }
146
147 if (!req.ipv4_addr_type && !req.ipv6_addr_type) {
148 hdd_err("invalid ipv4 or ipv6 gateway address");
149 return -EINVAL;
150 }
151
152 req.max_retries = 3;
153 req.timeout = 100; /* in milliseconds */
154 req.vdev_id = adapter->deflink->vdev_id;
155
156 hdd_debug("Configuring gateway for session %d", req.vdev_id);
157 hdd_debug("mac:"QDF_MAC_ADDR_FMT", ipv4:%pI4 (type %d), ipv6:%pI6c (type %d)",
158 QDF_MAC_ADDR_REF(req.gw_mac_addr.bytes),
159 req.ipv4_addr, req.ipv4_addr_type,
160 req.ipv6_addr, req.ipv6_addr_type);
161
162 vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_DP_ID);
163 if (vdev) {
164 ucfg_dp_nud_set_gateway_addr(vdev, req.gw_mac_addr);
165 hdd_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
166 }
167
168 status = sme_gateway_param_update(hdd_ctx->mac_handle, &req);
169 if (!QDF_IS_STATUS_SUCCESS(status)) {
170 hdd_err("sme_gateway_param_update failed(err=%d)", status);
171 ret = -EINVAL;
172 }
173
174 hdd_exit();
175 return ret;
176 }
177
178 /**
179 * wlan_hdd_cfg80211_set_gateway_params() - set gateway parameters
180 * @wiphy: wiphy structure pointer
181 * @wdev: Wireless device structure pointer
182 * @data: Pointer to the data received
183 * @data_len: Length of @data
184 *
185 * The API is invoked by the user space to set the gateway parameters
186 * such as mac address and the IP address which is used for detecting
187 * the IP subnet change
188 *
189 * Return: 0 on success; errno on failure
190 */
wlan_hdd_cfg80211_set_gateway_params(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)191 int wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy,
192 struct wireless_dev *wdev, const void *data, int data_len)
193 {
194 int errno;
195 struct osif_vdev_sync *vdev_sync;
196
197 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
198 if (errno)
199 return errno;
200
201 errno = __wlan_hdd_cfg80211_set_gateway_params(wiphy, wdev,
202 data, data_len);
203
204 osif_vdev_sync_op_stop(vdev_sync);
205
206 return errno;
207 }
208 #undef PARAM_MAC_ADDR
209 #undef PARAM_IPV4_ADDR
210 #undef PARAM_IPV6_ADDR
211