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