fabb5f4c6e8a8e846da7cde4cdc42e463dcd0fe4
[project/uqmi.git] / commands-wds.c
1 /*
2 * uqmi -- tiny QMI support implementation
3 *
4 * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
20 */
21
22 #include <stdlib.h>
23 #include <arpa/inet.h>
24
25 #include "qmi-message.h"
26
27 static struct qmi_wds_start_network_request wds_sn_req = {
28 QMI_INIT(authentication_preference,
29 QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP),
30 };
31 static struct qmi_wds_stop_network_request wds_stn_req;
32
33 #define cmd_wds_set_auth_cb no_cb
34 static enum qmi_cmd_result
35 cmd_wds_set_auth_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
36 {
37 static const struct {
38 const char *name;
39 QmiWdsAuthentication auth;
40 } modes[] = {
41 { "pap", QMI_WDS_AUTHENTICATION_PAP },
42 { "chap", QMI_WDS_AUTHENTICATION_CHAP },
43 { "both", QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP },
44 { "none", QMI_WDS_AUTHENTICATION_NONE },
45 };
46 int i;
47
48 for (i = 0; i < ARRAY_SIZE(modes); i++) {
49 if (strcasecmp(modes[i].name, arg) != 0)
50 continue;
51
52 qmi_set(&wds_sn_req, authentication_preference, modes[i].auth);
53 return QMI_CMD_DONE;
54 }
55
56 uqmi_add_error("Invalid auth mode (valid: pap, chap, both, none)");
57 return QMI_CMD_EXIT;
58 }
59
60 #define cmd_wds_set_username_cb no_cb
61 static enum qmi_cmd_result
62 cmd_wds_set_username_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
63 {
64 qmi_set_ptr(&wds_sn_req, username, arg);
65 return QMI_CMD_DONE;
66 }
67
68 #define cmd_wds_set_password_cb no_cb
69 static enum qmi_cmd_result
70 cmd_wds_set_password_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
71 {
72 qmi_set_ptr(&wds_sn_req, password, arg);
73 return QMI_CMD_DONE;
74 }
75
76 #define cmd_wds_set_autoconnect_cb no_cb
77 static enum qmi_cmd_result
78 cmd_wds_set_autoconnect_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
79 {
80 qmi_set(&wds_sn_req, enable_autoconnect, true);
81 qmi_set(&wds_stn_req, disable_autoconnect, true);
82 return QMI_CMD_DONE;
83 }
84
85 static void
86 cmd_wds_start_network_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
87 {
88 struct qmi_wds_start_network_response res;
89
90 qmi_parse_wds_start_network_response(msg, &res);
91 if (res.set.packet_data_handle)
92 blobmsg_add_u32(&status, NULL, res.data.packet_data_handle);
93 }
94
95 static enum qmi_cmd_result
96 cmd_wds_start_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
97 {
98 qmi_set_ptr(&wds_sn_req, apn, arg);
99 qmi_set_wds_start_network_request(msg, &wds_sn_req);
100 return QMI_CMD_REQUEST;
101 }
102
103 #define cmd_wds_stop_network_cb no_cb
104 static enum qmi_cmd_result
105 cmd_wds_stop_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
106 {
107 uint32_t pdh = strtoul(arg, NULL, 0);
108
109 qmi_set(&wds_stn_req, packet_data_handle, pdh);
110 qmi_set_wds_stop_network_request(msg, &wds_stn_req);
111 return QMI_CMD_REQUEST;
112 }
113
114 static void
115 cmd_wds_get_packet_service_status_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
116 {
117 struct qmi_wds_get_packet_service_status_response res;
118 const char *data_status[] = {
119 [QMI_WDS_CONNECTION_STATUS_UNKNOWN] = "unknown",
120 [QMI_WDS_CONNECTION_STATUS_DISCONNECTED] = "disconnected",
121 [QMI_WDS_CONNECTION_STATUS_CONNECTED] = "connected",
122 [QMI_WDS_CONNECTION_STATUS_SUSPENDED] = "suspended",
123 [QMI_WDS_CONNECTION_STATUS_AUTHENTICATING] = "authenticating",
124 };
125 int s = 0;
126
127 qmi_parse_wds_get_packet_service_status_response(msg, &res);
128 if (res.set.connection_status &&
129 res.data.connection_status < ARRAY_SIZE(data_status))
130 s = res.data.connection_status;
131
132 blobmsg_add_string(&status, NULL, data_status[s]);
133 }
134
135 static enum qmi_cmd_result
136 cmd_wds_get_packet_service_status_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
137 {
138 qmi_set_wds_get_packet_service_status_request(msg);
139 return QMI_CMD_REQUEST;
140 }
141
142 #define cmd_wds_set_autoconnect_setting_cb no_cb
143 static enum qmi_cmd_result
144 cmd_wds_set_autoconnect_setting_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
145 {
146 struct qmi_wds_set_autoconnect_setting_request ac_req;
147 const char *modes[] = {
148 [QMI_WDS_AUTOCONNECT_DISABLED] = "disabled",
149 [QMI_WDS_AUTOCONNECT_ENABLED] = "enabled",
150 [QMI_WDS_AUTOCONNECT_PAUSED] = "paused",
151 };
152 int i;
153
154 for (i = 0; i < ARRAY_SIZE(modes); i++) {
155 if (strcasecmp(modes[i], arg) != 0)
156 continue;
157
158 qmi_set(&ac_req, setting, i);
159 qmi_set_wds_set_autoconnect_setting_request(msg, &ac_req);
160 return QMI_CMD_DONE;
161 }
162
163 uqmi_add_error("Invalid value (valid: disabled, enabled, paused)");
164 return QMI_CMD_EXIT;
165 }
166
167 #define cmd_wds_reset_cb no_cb
168 static enum qmi_cmd_result
169 cmd_wds_reset_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
170 {
171 qmi_set_wds_reset_request(msg);
172 return QMI_CMD_REQUEST;
173 }
174
175 #define cmd_wds_set_ip_family_cb no_cb
176 static enum qmi_cmd_result
177 cmd_wds_set_ip_family_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
178 {
179 struct qmi_wds_set_ip_family_request ipf_req;
180 const struct ip_modes {
181 const char *name;
182 const QmiWdsIpFamily mode;
183 } modes[] = {
184 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
185 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
186 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
187 };
188 int i;
189
190 for (i = 0; i < ARRAY_SIZE(modes); i++) {
191 if (strcasecmp(modes[i].name, arg) != 0)
192 continue;
193
194 qmi_set(&ipf_req, preference, modes[i].mode);
195 qmi_set_wds_set_ip_family_request(msg, &ipf_req);
196 return QMI_CMD_REQUEST;
197 }
198
199 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
200 return QMI_CMD_EXIT;
201 }
202
203 static void wds_to_ipv4(const char *name, const uint32_t addr)
204 {
205 struct in_addr ip_addr;
206 char buf[INET_ADDRSTRLEN];
207
208 ip_addr.s_addr = htonl(addr);
209 blobmsg_add_string(&status, name, inet_ntop(AF_INET, &ip_addr, buf, sizeof(buf)));
210 }
211
212 static void wds_to_ipv6(const char *name, const uint16_t *addr)
213 {
214 int i;
215 struct in6_addr ip_addr;
216 char buf[INET6_ADDRSTRLEN];
217
218 for (i = 0; i < ARRAY_SIZE(ip_addr.s6_addr16); i++) {
219 ip_addr.s6_addr16[i] = htons(addr[i]);
220 }
221
222 blobmsg_add_string(&status, name, inet_ntop(AF_INET6, &ip_addr, buf, sizeof(buf)));
223 }
224
225 static void
226 cmd_wds_get_current_settings_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
227 {
228 void *v4, *v6, *d, *t;
229 struct qmi_wds_get_current_settings_response res;
230 const char *pdptypes[] = {
231 [QMI_WDS_PDP_TYPE_IPV4] = "ipv4",
232 [QMI_WDS_PDP_TYPE_PPP] = "ppp",
233 [QMI_WDS_PDP_TYPE_IPV6] = "ipv6",
234 [QMI_WDS_PDP_TYPE_IPV4_OR_IPV6] = "ipv4-or-ipv6",
235 };
236 const struct ip_modes {
237 const char *name;
238 const QmiWdsIpFamily mode;
239 } modes[] = {
240 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
241 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
242 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
243 };
244 int i;
245
246 qmi_parse_wds_get_current_settings_response(msg, &res);
247
248 t = blobmsg_open_table(&status, NULL);
249
250 if (res.set.pdp_type && res.data.pdp_type < ARRAY_SIZE(pdptypes))
251 blobmsg_add_string(&status, "pdp-type", pdptypes[res.data.pdp_type]);
252
253 if (res.set.ip_family) {
254 for (i = 0; i < ARRAY_SIZE(modes); i++) {
255 if (modes[i].mode != res.data.ip_family)
256 continue;
257 blobmsg_add_string(&status, "ip-family", modes[i].name);
258 break;
259 }
260 }
261
262 if (res.set.mtu)
263 blobmsg_add_u32(&status, "mtu", res.data.mtu);
264
265 /* IPV4 */
266 v4 = blobmsg_open_table(&status, "ipv4");
267
268 if (res.set.ipv4_address)
269 wds_to_ipv4("ip", res.data.ipv4_address);
270 if (res.set.primary_ipv4_dns_address)
271 wds_to_ipv4("dns1", res.data.primary_ipv4_dns_address);
272 if (res.set.secondary_ipv4_dns_address)
273 wds_to_ipv4("dns2", res.data.secondary_ipv4_dns_address);
274 if (res.set.ipv4_gateway_address)
275 wds_to_ipv4("gateway", res.data.ipv4_gateway_address);
276 if (res.set.ipv4_gateway_subnet_mask)
277 wds_to_ipv4("subnet", res.data.ipv4_gateway_subnet_mask);
278 blobmsg_close_table(&status, v4);
279
280 /* IPV6 */
281 v6 = blobmsg_open_table(&status, "ipv6");
282
283 if (res.set.ipv6_address) {
284 wds_to_ipv6("ip", res.data.ipv6_address.address);
285 blobmsg_add_u32(&status, "ip-prefix-length", res.data.ipv6_address.prefix_length);
286 }
287 if (res.set.ipv6_gateway_address) {
288 wds_to_ipv6("gateway", res.data.ipv6_gateway_address.address);
289 blobmsg_add_u32(&status, "gw-prefix-length", res.data.ipv6_gateway_address.prefix_length);
290 }
291 if (res.set.ipv6_primary_dns_address)
292 wds_to_ipv6("dns1", res.data.ipv6_primary_dns_address);
293 if (res.set.ipv6_secondary_dns_address)
294 wds_to_ipv6("dns2", res.data.ipv6_secondary_dns_address);
295
296 blobmsg_close_table(&status, v6);
297
298 d = blobmsg_open_table(&status, "domain-names");
299 for (i = 0; i < res.data.domain_name_list_n; i++) {
300 blobmsg_add_string(&status, NULL, res.data.domain_name_list[i]);
301 }
302 blobmsg_close_table(&status, d);
303
304 blobmsg_close_table(&status, t);
305 }
306
307 static enum qmi_cmd_result
308 cmd_wds_get_current_settings_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
309 {
310 struct qmi_wds_get_current_settings_request gcs_req;
311 memset(&gcs_req, '\0', sizeof(struct qmi_wds_get_current_settings_request));
312 qmi_set(&gcs_req, requested_settings,
313 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_PDP_TYPE |
314 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS |
315 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GRANTED_QOS |
316 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS |
317 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO |
318 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_MTU |
319 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST |
320 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY);
321 qmi_set_wds_get_current_settings_request(msg, &gcs_req);
322 return QMI_CMD_REQUEST;
323 }