26c9f594963879d9eace3e5dc5dd029b7a6eafa0
[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_apn_cb no_cb
34 static enum qmi_cmd_result
35 cmd_wds_set_apn_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
36 {
37         qmi_set_ptr(&wds_sn_req, apn, arg);
38         return QMI_CMD_DONE;
39 }
40
41 #define cmd_wds_set_auth_cb no_cb
42 static enum qmi_cmd_result
43 cmd_wds_set_auth_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
44 {
45         static const struct {
46                 const char *name;
47                 QmiWdsAuthentication auth;
48         } modes[] = {
49                 { "pap", QMI_WDS_AUTHENTICATION_PAP },
50                 { "chap", QMI_WDS_AUTHENTICATION_CHAP },
51                 { "both", QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP },
52                 { "none", QMI_WDS_AUTHENTICATION_NONE },
53         };
54         int i;
55
56         for (i = 0; i < ARRAY_SIZE(modes); i++) {
57                 if (strcasecmp(modes[i].name, arg) != 0)
58                         continue;
59
60                 qmi_set(&wds_sn_req, authentication_preference, modes[i].auth);
61                 return QMI_CMD_DONE;
62         }
63
64         uqmi_add_error("Invalid auth mode (valid: pap, chap, both, none)");
65         return QMI_CMD_EXIT;
66 }
67
68 #define cmd_wds_set_username_cb no_cb
69 static enum qmi_cmd_result
70 cmd_wds_set_username_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
71 {
72         qmi_set_ptr(&wds_sn_req, username, arg);
73         return QMI_CMD_DONE;
74 }
75
76 #define cmd_wds_set_password_cb no_cb
77 static enum qmi_cmd_result
78 cmd_wds_set_password_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
79 {
80         qmi_set_ptr(&wds_sn_req, password, arg);
81         return QMI_CMD_DONE;
82 }
83
84 #define cmd_wds_set_autoconnect_cb no_cb
85 static enum qmi_cmd_result
86 cmd_wds_set_autoconnect_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
87 {
88         qmi_set(&wds_sn_req, enable_autoconnect, true);
89         qmi_set(&wds_stn_req, disable_autoconnect, true);
90         return QMI_CMD_DONE;
91 }
92
93 #define cmd_wds_set_ip_family_pref_cb no_cb
94 static enum qmi_cmd_result
95 cmd_wds_set_ip_family_pref_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
96 {
97         static const struct {
98                 const char *name;
99                 const QmiWdsIpFamily mode;
100         } modes[] = {
101                 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
102                 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
103                 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
104         };
105         int i;
106
107         for (i = 0; i < ARRAY_SIZE(modes); i++) {
108                 if (strcasecmp(modes[i].name, arg) != 0)
109                         continue;
110
111                 qmi_set(&wds_sn_req, ip_family_preference, modes[i].mode);
112                 return QMI_CMD_DONE;
113         }
114
115         uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
116         return QMI_CMD_EXIT;
117 }
118
119 static void
120 cmd_wds_start_network_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
121 {
122         struct qmi_wds_start_network_response res;
123
124         qmi_parse_wds_start_network_response(msg, &res);
125         if (res.set.packet_data_handle)
126                 blobmsg_add_u32(&status, NULL, res.data.packet_data_handle);
127 }
128
129 static enum qmi_cmd_result
130 cmd_wds_start_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
131 {
132         qmi_set_wds_start_network_request(msg, &wds_sn_req);
133         return QMI_CMD_REQUEST;
134 }
135
136 #define cmd_wds_stop_network_cb no_cb
137 static enum qmi_cmd_result
138 cmd_wds_stop_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
139 {
140         uint32_t pdh = strtoul(arg, NULL, 0);
141
142         qmi_set(&wds_stn_req, packet_data_handle, pdh);
143         qmi_set_wds_stop_network_request(msg, &wds_stn_req);
144         return QMI_CMD_REQUEST;
145 }
146
147 static void
148 cmd_wds_get_packet_service_status_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
149 {
150         struct qmi_wds_get_packet_service_status_response res;
151         const char *data_status[] = {
152                 [QMI_WDS_CONNECTION_STATUS_UNKNOWN] = "unknown",
153                 [QMI_WDS_CONNECTION_STATUS_DISCONNECTED] = "disconnected",
154                 [QMI_WDS_CONNECTION_STATUS_CONNECTED] = "connected",
155                 [QMI_WDS_CONNECTION_STATUS_SUSPENDED] = "suspended",
156                 [QMI_WDS_CONNECTION_STATUS_AUTHENTICATING] = "authenticating",
157         };
158         int s = 0;
159
160         qmi_parse_wds_get_packet_service_status_response(msg, &res);
161         if (res.set.connection_status &&
162             res.data.connection_status < ARRAY_SIZE(data_status))
163                 s = res.data.connection_status;
164
165         blobmsg_add_string(&status, NULL, data_status[s]);
166 }
167
168 static enum qmi_cmd_result
169 cmd_wds_get_packet_service_status_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
170 {
171         qmi_set_wds_get_packet_service_status_request(msg);
172         return QMI_CMD_REQUEST;
173 }
174
175 #define cmd_wds_set_autoconnect_setting_cb no_cb
176 static enum qmi_cmd_result
177 cmd_wds_set_autoconnect_setting_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
178 {
179         struct qmi_wds_set_autoconnect_setting_request ac_req;
180         const char *modes[] = {
181                 [QMI_WDS_AUTOCONNECT_DISABLED] = "disabled",
182                 [QMI_WDS_AUTOCONNECT_ENABLED] = "enabled",
183                 [QMI_WDS_AUTOCONNECT_PAUSED] = "paused",
184         };
185         int i;
186
187         for (i = 0; i < ARRAY_SIZE(modes); i++) {
188                 if (strcasecmp(modes[i], arg) != 0)
189                         continue;
190
191                 qmi_set(&ac_req, setting, i);
192                 qmi_set_wds_set_autoconnect_setting_request(msg, &ac_req);
193                 return QMI_CMD_DONE;
194         }
195
196         uqmi_add_error("Invalid value (valid: disabled, enabled, paused)");
197         return QMI_CMD_EXIT;
198 }
199
200 #define cmd_wds_reset_cb no_cb
201 static enum qmi_cmd_result
202 cmd_wds_reset_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
203 {
204         qmi_set_wds_reset_request(msg);
205         return QMI_CMD_REQUEST;
206 }
207
208 #define cmd_wds_set_ip_family_cb no_cb
209 static enum qmi_cmd_result
210 cmd_wds_set_ip_family_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
211 {
212         struct qmi_wds_set_ip_family_request ipf_req;
213         const struct ip_modes {
214                 const char *name;
215                 const QmiWdsIpFamily mode;
216         } modes[] = {
217                 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
218                 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
219                 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
220         };
221         int i;
222
223         for (i = 0; i < ARRAY_SIZE(modes); i++) {
224                 if (strcasecmp(modes[i].name, arg) != 0)
225                         continue;
226
227                 qmi_set(&ipf_req, preference, modes[i].mode);
228                 qmi_set_wds_set_ip_family_request(msg, &ipf_req);
229                 return QMI_CMD_REQUEST;
230         }
231
232         uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
233         return QMI_CMD_EXIT;
234 }
235
236 static void wds_to_ipv4(const char *name, const uint32_t addr)
237 {
238         struct in_addr ip_addr;
239         char buf[INET_ADDRSTRLEN];
240
241         ip_addr.s_addr = htonl(addr);
242         blobmsg_add_string(&status, name, inet_ntop(AF_INET, &ip_addr, buf, sizeof(buf)));
243 }
244
245 static void wds_to_ipv6(const char *name, const uint16_t *addr)
246 {
247         char buf[INET6_ADDRSTRLEN];
248         uint16_t ip_addr[8];
249         int i;
250
251         for (i = 0; i < ARRAY_SIZE(ip_addr); i++)
252                 ip_addr[i] = htons(addr[i]);
253
254         blobmsg_add_string(&status, name, inet_ntop(AF_INET6, &ip_addr, buf, sizeof(buf)));
255 }
256
257 static void
258 cmd_wds_get_current_settings_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
259 {
260         void *v4, *v6, *d, *t;
261         struct qmi_wds_get_current_settings_response res;
262         const char *pdptypes[] = {
263                 [QMI_WDS_PDP_TYPE_IPV4] = "ipv4",
264                 [QMI_WDS_PDP_TYPE_PPP] = "ppp",
265                 [QMI_WDS_PDP_TYPE_IPV6] = "ipv6",
266                 [QMI_WDS_PDP_TYPE_IPV4_OR_IPV6] = "ipv4-or-ipv6",
267         };
268         const struct ip_modes {
269                 const char *name;
270                 const QmiWdsIpFamily mode;
271         } modes[] = {
272                 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
273                 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
274                 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
275         };
276         int i;
277
278         qmi_parse_wds_get_current_settings_response(msg, &res);
279
280         t = blobmsg_open_table(&status, NULL);
281
282         if (res.set.pdp_type && (int) res.data.pdp_type < ARRAY_SIZE(pdptypes))
283                 blobmsg_add_string(&status, "pdp-type", pdptypes[res.data.pdp_type]);
284
285         if (res.set.ip_family) {
286                 for (i = 0; i < ARRAY_SIZE(modes); i++) {
287                         if (modes[i].mode != res.data.ip_family)
288                                 continue;
289                         blobmsg_add_string(&status, "ip-family", modes[i].name);
290                         break;
291                 }
292         }
293
294         if (res.set.mtu)
295                 blobmsg_add_u32(&status, "mtu", res.data.mtu);
296
297         /* IPV4 */
298         v4 = blobmsg_open_table(&status, "ipv4");
299
300         if (res.set.ipv4_address)
301                 wds_to_ipv4("ip", res.data.ipv4_address);
302         if (res.set.primary_ipv4_dns_address)
303                 wds_to_ipv4("dns1", res.data.primary_ipv4_dns_address);
304         if (res.set.secondary_ipv4_dns_address)
305                 wds_to_ipv4("dns2", res.data.secondary_ipv4_dns_address);
306         if (res.set.ipv4_gateway_address)
307                 wds_to_ipv4("gateway", res.data.ipv4_gateway_address);
308         if (res.set.ipv4_gateway_subnet_mask)
309                 wds_to_ipv4("subnet", res.data.ipv4_gateway_subnet_mask);
310         blobmsg_close_table(&status, v4);
311
312         /* IPV6 */
313         v6 = blobmsg_open_table(&status, "ipv6");
314
315         if (res.set.ipv6_address) {
316                 wds_to_ipv6("ip", res.data.ipv6_address.address);
317                 blobmsg_add_u32(&status, "ip-prefix-length", res.data.ipv6_address.prefix_length);
318         }
319         if (res.set.ipv6_gateway_address) {
320                 wds_to_ipv6("gateway", res.data.ipv6_gateway_address.address);
321                 blobmsg_add_u32(&status, "gw-prefix-length", res.data.ipv6_gateway_address.prefix_length);
322         }
323         if (res.set.ipv6_primary_dns_address)
324                 wds_to_ipv6("dns1", res.data.ipv6_primary_dns_address);
325         if (res.set.ipv6_secondary_dns_address)
326                 wds_to_ipv6("dns2", res.data.ipv6_secondary_dns_address);
327
328         blobmsg_close_table(&status, v6);
329
330         d = blobmsg_open_table(&status, "domain-names");
331         for (i = 0; i < res.data.domain_name_list_n; i++) {
332                 blobmsg_add_string(&status, NULL, res.data.domain_name_list[i]);
333         }
334         blobmsg_close_table(&status, d);
335
336         blobmsg_close_table(&status, t);
337 }
338
339 static enum qmi_cmd_result
340 cmd_wds_get_current_settings_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
341 {
342         struct qmi_wds_get_current_settings_request gcs_req;
343         memset(&gcs_req, '\0', sizeof(struct qmi_wds_get_current_settings_request));
344         qmi_set(&gcs_req, requested_settings,
345                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_PDP_TYPE |
346                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS |
347                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GRANTED_QOS |
348                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS |
349                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO |
350                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_MTU |
351                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST |
352                 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY);
353         qmi_set_wds_get_current_settings_request(msg, &gcs_req);
354         return QMI_CMD_REQUEST;
355 }