1 /*
2  * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #include "esp_netif.h"
9 
10 #include "lwip/dns.h"
11 #include "netif/ppp/pppapi.h"
12 #include "netif/ppp/pppos.h"
13 #include "esp_log.h"
14 #include "esp_netif_net_stack.h"
15 #include "esp_event.h"
16 #include "esp_netif_ppp.h"
17 #include "esp_netif_lwip_internal.h"
18 #include <string.h>
19 #include "lwip/ip6_addr.h"
20 
21 ESP_EVENT_DEFINE_BASE(NETIF_PPP_STATUS);
22 
23 static const char *TAG = "esp-netif_lwip-ppp";
24 
25 /**
26  * @brief internal lwip_ppp context struct extends the netif related data
27  *        used to hold PPP netif related parameters
28  */
29 typedef struct lwip_peer2peer_ctx {
30     netif_related_data_t base;      // Generic portion of netif-related data
31     // PPP specific fields follow
32     bool ppp_phase_event_enabled;
33     bool ppp_error_event_enabled;
34     ppp_pcb *ppp;
35 } lwip_peer2peer_ctx_t;
36 
37 #if PPP_SUPPORT && PPP_AUTH_SUPPORT
38 typedef struct {
39     struct tcpip_api_call_data call;
40     ppp_pcb *ppp;
41     u8_t authtype;
42     const char *user;
43     const char *passwd;
44 } set_auth_msg_t;
45 
pppapi_do_ppp_set_auth(struct tcpip_api_call_data * m)46 static err_t pppapi_do_ppp_set_auth(struct tcpip_api_call_data *m)
47 {
48     set_auth_msg_t *msg = (set_auth_msg_t *)m;
49     ppp_set_auth(msg->ppp, msg->authtype, msg->user, msg->passwd);
50     return ERR_OK;
51 }
52 
pppapi_set_auth(ppp_pcb * pcb,u8_t authtype,const char * user,const char * passwd)53 static void pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd)
54 {
55     set_auth_msg_t msg = { .ppp = pcb, .authtype = authtype, .user = user, .passwd = passwd};
56     tcpip_api_call(pppapi_do_ppp_set_auth, &msg.call);
57 }
58 #endif // PPP_SUPPORT && PPP_AUTH_SUPPORT
59 
60 /**
61  * @brief lwip callback from PPP client used here to produce PPP error related events,
62  * as well as some IP events
63  */
on_ppp_status_changed(ppp_pcb * pcb,int err_code,void * ctx)64 static void on_ppp_status_changed(ppp_pcb *pcb, int err_code, void *ctx)
65 {
66     esp_netif_t *netif = ctx;
67     ip_event_got_ip_t evt = {
68             .esp_netif = netif,
69     };
70     esp_err_t err;
71     struct lwip_peer2peer_ctx *obj =  (struct lwip_peer2peer_ctx*)netif->related_data;
72     assert(obj->base.netif_type == PPP_LWIP_NETIF);
73     switch (err_code) {
74         case PPPERR_NONE:
75             ESP_LOGI(TAG, "Connected");
76             break;
77         case PPPERR_PARAM:
78             ESP_LOGE(TAG, "Invalid parameter");
79             break;
80         case PPPERR_OPEN:
81             ESP_LOGE(TAG, "Unable to open PPP session");
82             break;
83         case PPPERR_DEVICE:
84             ESP_LOGE(TAG, "Invalid I/O device for PPP");
85             break;
86         case PPPERR_ALLOC:
87             ESP_LOGE(TAG, "Unable to allocate resources");
88             break;
89         case PPPERR_USER: /* User interrupt */
90             ESP_LOGI(TAG, "User interrupt");
91             break;
92         case PPPERR_CONNECT: /* Connection lost */
93             ESP_LOGI(TAG, "Connection lost");
94             err = esp_event_post(IP_EVENT, netif->lost_ip_event, &evt, sizeof(evt), 0);
95 
96             if (ESP_OK != err) {
97                 ESP_LOGE(TAG, "esp_event_post failed with code %d", err);
98             }
99             return;
100 
101         case PPPERR_AUTHFAIL:
102             ESP_LOGE(TAG, "Failed authentication challenge");
103             break;
104         case PPPERR_PROTOCOL:
105             ESP_LOGE(TAG, "Failed to meet protocol");
106             break;
107         case PPPERR_PEERDEAD:
108             ESP_LOGE(TAG, "Connection timeout");
109             break;
110         case PPPERR_IDLETIMEOUT:
111             ESP_LOGE(TAG, "Idle Timeout");
112             break;
113         case PPPERR_CONNECTTIME:
114             ESP_LOGE(TAG, "Max connect time reached");
115             break;
116         case PPPERR_LOOPBACK:
117             ESP_LOGE(TAG, "Loopback detected");
118             break;
119         default:
120             ESP_LOGE(TAG, "Unknown error code %d", err_code);
121             break;
122     }
123     if (obj->ppp_error_event_enabled) {
124         err = esp_event_post(NETIF_PPP_STATUS, err_code, &netif, sizeof(netif), 0);
125         if (err != ESP_OK) {
126             ESP_LOGE(TAG, "esp_event_post failed with code %d", err);
127         }
128 
129     }
130 }
131 
132 #if PPP_NOTIFY_PHASE
133 /**
134  * @brief Notify phase callback which is called on each PPP internal state change
135  *
136  * @param pcb PPP control block
137  * @param phase Phase ID
138  * @param ctx Context of callback
139  */
on_ppp_notify_phase(ppp_pcb * pcb,u8_t phase,void * ctx)140 static void on_ppp_notify_phase(ppp_pcb *pcb, u8_t phase, void *ctx)
141 {
142     switch (phase) {
143         case PPP_PHASE_DEAD:
144             ESP_LOGD(TAG, "Phase Dead");
145             break;
146         case PPP_PHASE_INITIALIZE:
147             ESP_LOGD(TAG, "Phase Start");
148             break;
149         case PPP_PHASE_ESTABLISH:
150             ESP_LOGD(TAG, "Phase Establish");
151             break;
152         case PPP_PHASE_AUTHENTICATE:
153             ESP_LOGD(TAG, "Phase Authenticate");
154             break;
155         case PPP_PHASE_NETWORK:
156             ESP_LOGD(TAG, "Phase Network");
157             break;
158         case PPP_PHASE_RUNNING:
159             ESP_LOGD(TAG, "Phase Running");
160             break;
161         case PPP_PHASE_TERMINATE:
162             ESP_LOGD(TAG, "Phase Terminate");
163             break;
164         case PPP_PHASE_DISCONNECT:
165             ESP_LOGD(TAG, "Phase Disconnect");
166             break;
167         default:
168             ESP_LOGW(TAG, "Phase Unknown: %d", phase);
169             break;
170     }
171     esp_netif_t *netif = ctx;
172     lwip_peer2peer_ctx_t *obj = (lwip_peer2peer_ctx_t *)netif->related_data;
173     assert(obj->base.netif_type == PPP_LWIP_NETIF);
174     if (obj && obj->ppp_phase_event_enabled) {
175         esp_err_t err = esp_event_post(NETIF_PPP_STATUS, NETIF_PP_PHASE_OFFSET + phase, &netif, sizeof(netif), 0);
176         if (err != ESP_OK) {
177             ESP_LOGE(TAG, "esp_event_post failed with code %d", err);
178         }
179     }
180 }
181 #endif // PPP_NOTIFY_PHASE
182 
183 /**
184  * @brief PPP low level output callback used to transmit data using standard esp-netif interafce
185  *
186  * @param pcb PPP control block
187  * @param data Buffer to write to serial port
188  * @param len Length of the data buffer
189  * @param ctx Context of callback
190  *
191  * @return uint32_t Length of data successfully sent
192  */
pppos_low_level_output(ppp_pcb * pcb,uint8_t * data,uint32_t len,void * netif)193 static uint32_t pppos_low_level_output(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *netif)
194 {
195     esp_err_t ret = esp_netif_transmit(netif, data, len);
196     if (ret == ESP_OK) {
197         return len;
198     }
199     return 0;
200 }
201 
esp_netif_ppp_set_auth(esp_netif_t * netif,esp_netif_auth_type_t authtype,const char * user,const char * passwd)202 esp_err_t esp_netif_ppp_set_auth(esp_netif_t *netif, esp_netif_auth_type_t authtype, const char *user, const char *passwd)
203 {
204     if (!ESP_NETIF_IS_POINT2POINT_TYPE(netif, PPP_LWIP_NETIF)) {
205         return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
206     }
207 #if PPP_AUTH_SUPPORT
208     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif->related_data;
209     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
210     pppapi_set_auth(ppp_ctx->ppp, authtype, user, passwd);
211     return ESP_OK;
212 #else
213     ESP_LOGE(TAG, "%s failed: No authorisation enabled in menuconfig", __func__);
214     return ESP_ERR_ESP_NETIF_IF_NOT_READY;
215 #endif
216 }
217 
esp_netif_ppp_set_default_netif(netif_related_data_t * netif_related)218 void esp_netif_ppp_set_default_netif(netif_related_data_t *netif_related)
219 {
220     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif_related;
221     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
222 
223     ppp_set_default(ppp_ctx->ppp);
224 }
225 
esp_netif_new_ppp(esp_netif_t * esp_netif,const esp_netif_netstack_config_t * esp_netif_stack_config)226 netif_related_data_t * esp_netif_new_ppp(esp_netif_t *esp_netif, const esp_netif_netstack_config_t *esp_netif_stack_config)
227 {
228     struct netif * netif_impl = esp_netif->lwip_netif;
229     struct lwip_peer2peer_ctx * ppp_obj = calloc(1, sizeof(struct lwip_peer2peer_ctx));
230     if (ppp_obj == NULL) {
231         ESP_LOGE(TAG, "%s: cannot allocate lwip_ppp_ctx", __func__);
232         return NULL;
233     }
234     // Setup the generic esp-netif fields
235     ppp_obj->base.is_point2point = true;
236     ppp_obj->base.netif_type = PPP_LWIP_NETIF;
237 
238     ppp_obj->ppp = pppapi_pppos_create(netif_impl, pppos_low_level_output, on_ppp_status_changed, esp_netif);
239     ESP_LOGD(TAG, "%s: PPP connection created: %p", __func__, ppp_obj->ppp);
240     if (!ppp_obj->ppp) {
241         ESP_LOGE(TAG, "%s: lwIP PPP connection cannot be created", __func__);
242     }
243 
244     // Set the related data here, since the phase callback could be triggered before this function exits
245     esp_netif->related_data = (netif_related_data_t *)ppp_obj;
246 #if PPP_NOTIFY_PHASE
247     ppp_set_notify_phase_callback(ppp_obj->ppp, on_ppp_notify_phase);
248 #endif
249     ppp_set_usepeerdns(ppp_obj->ppp, 1);
250 
251     return (netif_related_data_t *)ppp_obj;
252 }
253 
esp_netif_start_ppp(esp_netif_t * esp_netif)254 esp_err_t esp_netif_start_ppp(esp_netif_t *esp_netif)
255 {
256     netif_related_data_t *netif_related = esp_netif->related_data;
257     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif_related;
258     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
259 
260     ESP_LOGD(TAG, "%s: Starting PPP connection: %p", __func__, ppp_ctx->ppp);
261     esp_err_t err = pppapi_connect(ppp_ctx->ppp, 0);
262     if (err != ESP_OK) {
263         ESP_LOGE(TAG, "%s: PPP connection cannot be started", __func__);
264         if (ppp_ctx->ppp_error_event_enabled) {
265             esp_event_post(NETIF_PPP_STATUS, NETIF_PPP_CONNECT_FAILED, esp_netif, sizeof(esp_netif), 0);
266         }
267         return ESP_FAIL;
268     }
269     return ESP_OK;
270 }
271 
esp_netif_lwip_ppp_input(void * ppp_ctx,void * buffer,size_t len,void * eb)272 esp_netif_recv_ret_t esp_netif_lwip_ppp_input(void *ppp_ctx, void *buffer, size_t len, void *eb)
273 {
274     struct lwip_peer2peer_ctx * obj = ppp_ctx;
275     err_t ret = pppos_input_tcpip(obj->ppp, buffer, len);
276     if (ret != ERR_OK) {
277         ESP_LOGE(TAG, "pppos_input_tcpip failed with %d", ret);
278         return ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_FAIL);
279     }
280     return ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_OK);
281 }
282 
esp_netif_stop_ppp(netif_related_data_t * netif_related)283 esp_err_t esp_netif_stop_ppp(netif_related_data_t *netif_related)
284 {
285     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif_related;
286     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
287     ESP_LOGD(TAG, "%s: Stopped PPP connection: %p", __func__, ppp_ctx->ppp);
288     err_t ret = pppapi_close(ppp_ctx->ppp, 0);
289     if (ret != ERR_OK) {
290         ESP_LOGE(TAG, "pppapi_close failed with %d", ret);
291         return ESP_FAIL;
292     }
293     return ESP_OK;
294 }
295 
esp_netif_destroy_ppp(netif_related_data_t * netif_related)296 void esp_netif_destroy_ppp(netif_related_data_t *netif_related)
297 {
298     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif_related;
299     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
300 
301     pppapi_free(ppp_ctx->ppp);
302     free(netif_related);
303 }
304 
esp_netif_ppp_set_params(esp_netif_t * netif,const esp_netif_ppp_config_t * config)305 esp_err_t esp_netif_ppp_set_params(esp_netif_t *netif, const esp_netif_ppp_config_t *config)
306 {
307     if (netif == NULL || netif->related_data == NULL || config == NULL ||
308         ((struct lwip_peer2peer_ctx *)netif->related_data)->base.netif_type != PPP_LWIP_NETIF) {
309         return ESP_ERR_INVALID_ARG;
310     }
311     struct lwip_peer2peer_ctx *obj =  (struct lwip_peer2peer_ctx *)netif->related_data;
312     obj->ppp_phase_event_enabled = config->ppp_phase_event_enabled;
313     obj->ppp_error_event_enabled = config->ppp_error_event_enabled;
314     return ESP_OK;
315 }
316 
esp_netif_ppp_get_params(esp_netif_t * netif,esp_netif_ppp_config_t * config)317 esp_err_t esp_netif_ppp_get_params(esp_netif_t *netif, esp_netif_ppp_config_t *config)
318 {
319     if (netif == NULL || netif->related_data == NULL || config == NULL ||
320         ((struct lwip_peer2peer_ctx *)netif->related_data)->base.netif_type != PPP_LWIP_NETIF) {
321         return ESP_ERR_INVALID_ARG;
322     }
323     struct lwip_peer2peer_ctx *obj =  (struct lwip_peer2peer_ctx *)netif->related_data;
324     config->ppp_phase_event_enabled = obj->ppp_phase_event_enabled;
325     config->ppp_error_event_enabled = obj->ppp_error_event_enabled;
326     return ESP_OK;
327 }
328