1 /*
2 * Copyright (c) 2019 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_l2_ppp, CONFIG_NET_L2_PPP_LOG_LEVEL);
9
10 #include <stdlib.h>
11 #include <zephyr/net/net_core.h>
12 #include <zephyr/net/net_l2.h>
13 #include <zephyr/net/net_if.h>
14 #include <zephyr/net/net_pkt.h>
15 #include <zephyr/net/net_mgmt.h>
16 #include <zephyr/net/ppp.h>
17 #include <zephyr/sys/iterable_sections.h>
18
19 #include "net_private.h"
20
21 #include "ppp_stats.h"
22 #include "ppp_internal.h"
23
24 static K_FIFO_DEFINE(tx_queue);
25
26 #if defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
27 /* Lowest priority cooperative thread */
28 #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NET_L2_PPP_THREAD_PRIO)
29 #else
30 #define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NET_L2_PPP_THREAD_PRIO)
31 #endif
32
33 static void tx_handler(void);
34
35 static K_THREAD_DEFINE(tx_handler_thread, CONFIG_NET_L2_PPP_TX_STACK_SIZE,
36 (k_thread_entry_t)tx_handler, NULL, NULL, NULL,
37 THREAD_PRIORITY, 0, 0);
38
39 static const struct ppp_protocol_handler *ppp_lcp;
40
ppp_update_rx_stats(struct net_if * iface,struct net_pkt * pkt,size_t length)41 static void ppp_update_rx_stats(struct net_if *iface,
42 struct net_pkt *pkt, size_t length)
43 {
44 #if defined(CONFIG_NET_STATISTICS_PPP)
45 ppp_stats_update_bytes_rx(iface, length);
46 ppp_stats_update_pkts_rx(iface);
47 #endif /* CONFIG_NET_STATISTICS_PPP */
48 }
49
ppp_update_tx_stats(struct net_if * iface,struct net_pkt * pkt,size_t length)50 static void ppp_update_tx_stats(struct net_if *iface,
51 struct net_pkt *pkt, size_t length)
52 {
53 #if defined(CONFIG_NET_STATISTICS_PPP)
54 ppp_stats_update_bytes_tx(iface, length);
55 ppp_stats_update_pkts_tx(iface);
56 #endif /* CONFIG_NET_STATISTICS_PPP */
57 }
58
59 #if defined(CONFIG_NET_TEST)
60 typedef enum net_verdict (*ppp_l2_callback_t)(struct net_if *iface,
61 struct net_pkt *pkt);
62
63 static ppp_l2_callback_t testing_cb;
64
ppp_l2_register_pkt_cb(ppp_l2_callback_t cb)65 void ppp_l2_register_pkt_cb(ppp_l2_callback_t cb)
66 {
67 testing_cb = cb;
68 }
69 #endif
70
process_ppp_msg(struct net_if * iface,struct net_pkt * pkt)71 static enum net_verdict process_ppp_msg(struct net_if *iface,
72 struct net_pkt *pkt)
73 {
74 struct ppp_context *ctx = net_if_l2_data(iface);
75 enum net_verdict verdict = NET_DROP;
76 uint16_t protocol;
77 int ret;
78
79 if (!ctx->is_ready_to_serve) {
80 goto quit;
81 }
82
83 ret = net_pkt_read_be16(pkt, &protocol);
84 if (ret < 0) {
85 goto quit;
86 }
87
88 if ((IS_ENABLED(CONFIG_NET_IPV4) && protocol == PPP_IP) ||
89 (IS_ENABLED(CONFIG_NET_IPV6) && protocol == PPP_IPV6)) {
90 /* Remove the protocol field so that IP packet processing
91 * continues properly in net_core.c:process_data()
92 */
93 (void)net_buf_pull_be16(pkt->buffer);
94 net_pkt_cursor_init(pkt);
95 return NET_CONTINUE;
96 }
97
98 STRUCT_SECTION_FOREACH(ppp_protocol_handler, proto) {
99 if (proto->protocol != protocol) {
100 continue;
101 }
102
103 return proto->handler(ctx, iface, pkt);
104 }
105
106 switch (protocol) {
107 case PPP_IP:
108 case PPP_IPV6:
109 case PPP_ECP:
110 case PPP_CCP:
111 case PPP_LCP:
112 case PPP_IPCP:
113 case PPP_IPV6CP:
114 ppp_send_proto_rej(iface, pkt, protocol);
115 break;
116 default:
117 break;
118 }
119
120 NET_DBG("%s protocol %s%s(0x%02x)",
121 ppp_proto2str(protocol) ? "Unhandled" : "Unknown",
122 ppp_proto2str(protocol),
123 ppp_proto2str(protocol) ? " " : "",
124 protocol);
125
126 quit:
127 return verdict;
128 }
129
ppp_recv(struct net_if * iface,struct net_pkt * pkt)130 static enum net_verdict ppp_recv(struct net_if *iface,
131 struct net_pkt *pkt)
132 {
133 enum net_verdict verdict;
134
135 #if defined(CONFIG_NET_TEST)
136 /* If we are running a PPP unit test, then feed the packet
137 * back to test app for verification.
138 */
139 if (testing_cb) {
140 return testing_cb(iface, pkt);
141 }
142 #endif
143
144 ppp_update_rx_stats(iface, pkt, net_pkt_get_len(pkt));
145
146 if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
147 net_pkt_hexdump(pkt, "recv L2");
148 }
149
150 verdict = process_ppp_msg(iface, pkt);
151
152 switch (verdict) {
153 case NET_OK:
154 net_pkt_unref(pkt);
155 break;
156 case NET_DROP:
157 ppp_stats_update_drop_rx(iface);
158 break;
159 case NET_CONTINUE:
160 break;
161 }
162
163 return verdict;
164 }
165
ppp_send(struct net_if * iface,struct net_pkt * pkt)166 static int ppp_send(struct net_if *iface, struct net_pkt *pkt)
167 {
168 const struct ppp_api *api = net_if_get_device(iface)->api;
169 struct ppp_context *ctx = net_if_l2_data(iface);
170 int ret;
171
172 if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
173 net_pkt_hexdump(pkt, "send L2");
174 }
175
176 /* If PPP is not yet ready, then just give error to caller as there
177 * is no way to send before the PPP handshake is finished.
178 */
179 if (ctx->phase != PPP_RUNNING && !net_pkt_is_ppp(pkt)) {
180 return -ENETDOWN;
181 }
182
183 ret = net_l2_send(api->send, net_if_get_device(iface), iface, pkt);
184 if (!ret) {
185 ret = net_pkt_get_len(pkt);
186 ppp_update_tx_stats(iface, pkt, ret);
187 net_pkt_unref(pkt);
188 }
189
190 return ret;
191 }
192
ppp_close(struct ppp_context * ctx)193 static void ppp_close(struct ppp_context *ctx)
194 {
195 if (ppp_lcp) {
196 ppp_lcp->close(ctx, "Shutdown");
197 } else {
198 ppp_change_phase(ctx, PPP_DEAD);
199 }
200 }
201
ppp_open(struct ppp_context * ctx)202 static void ppp_open(struct ppp_context *ctx)
203 {
204 ppp_change_phase(ctx, PPP_ESTABLISH);
205
206 if (ppp_lcp) {
207 NET_DBG("Starting LCP");
208 ppp_lcp->lower_up(ctx);
209 ppp_lcp->open(ctx);
210 }
211 }
212
ppp_flags(struct net_if * iface)213 static enum net_l2_flags ppp_flags(struct net_if *iface)
214 {
215 struct ppp_context *ctx = net_if_l2_data(iface);
216
217 return ctx->ppp_l2_flags;
218 }
219
ppp_enable(struct net_if * iface,bool state)220 static int ppp_enable(struct net_if *iface, bool state)
221 {
222 const struct ppp_api *ppp =
223 net_if_get_device(iface)->api;
224 struct ppp_context *ctx = net_if_l2_data(iface);
225
226 if (ctx->is_enabled == state) {
227 return 0;
228 }
229
230 ctx->is_enabled = state;
231
232 if (!state) {
233 if (ppp->stop) {
234 ppp->stop(net_if_get_device(iface));
235 }
236 } else {
237 if (ppp->start) {
238 ppp->start(net_if_get_device(iface));
239 }
240 }
241 return 0;
242 }
243
244 NET_L2_INIT(PPP_L2, ppp_recv, ppp_send, ppp_enable, ppp_flags);
245
246 #if defined(CONFIG_NET_SHELL)
get_ppp_context(int idx,struct ppp_context ** ctx,struct net_if ** iface)247 static int get_ppp_context(int idx, struct ppp_context **ctx,
248 struct net_if **iface)
249 {
250 *iface = net_if_get_by_index(idx);
251
252 if (!*iface) {
253 return -ENOENT;
254 }
255
256 if (net_if_l2(*iface) != &NET_L2_GET_NAME(PPP)) {
257 return -ENODEV;
258 }
259
260 *ctx = net_if_l2_data(*iface);
261
262 return 0;
263 }
264
echo_reply_handler(void * user_data,size_t user_data_len)265 static void echo_reply_handler(void *user_data, size_t user_data_len)
266 {
267 struct ppp_context *ctx = user_data;
268 uint32_t end_time = k_cycle_get_32();
269 uint32_t time_diff;
270
271 time_diff = end_time - ctx->shell.echo_req_data;
272 ctx->shell.echo_req_data =
273 k_cyc_to_ns_floor64(time_diff) / 1000;
274
275 k_sem_give(&ctx->shell.wait_echo_reply);
276 }
277
net_ppp_ping(int idx,int32_t timeout)278 int net_ppp_ping(int idx, int32_t timeout)
279 {
280 struct ppp_context *ctx;
281 struct net_if *iface;
282 int ret;
283
284 ret = get_ppp_context(idx, &ctx, &iface);
285 if (ret < 0) {
286 return ret;
287 }
288
289 ctx->shell.echo_req_data = k_cycle_get_32();
290 ctx->shell.echo_reply.cb = echo_reply_handler;
291 ctx->shell.echo_reply.user_data = ctx;
292 ctx->shell.echo_reply.user_data_len = sizeof(ctx);
293
294 ret = ppp_send_pkt(&ctx->lcp.fsm, iface, PPP_ECHO_REQ, 0,
295 UINT_TO_POINTER(ctx->shell.echo_req_data),
296 sizeof(ctx->shell.echo_req_data));
297 if (ret < 0) {
298 return ret;
299 }
300
301 ret = k_sem_take(&ctx->shell.wait_echo_reply, K_MSEC(timeout));
302
303 ctx->shell.echo_reply.cb = NULL;
304
305 if (ret < 0) {
306 return ret;
307 }
308
309 /* Returns amount of microseconds waited */
310 return ctx->shell.echo_req_data;
311 }
312
net_ppp_context_get(int idx)313 struct ppp_context *net_ppp_context_get(int idx)
314 {
315 struct ppp_context *ctx;
316 struct net_if *iface;
317 int ret;
318
319 if (idx == 0) {
320 iface = net_if_get_first_by_type(&NET_L2_GET_NAME(PPP));
321 if (!iface) {
322 return NULL;
323 }
324
325 return net_if_l2_data(iface);
326 }
327
328 ret = get_ppp_context(idx, &ctx, &iface);
329 if (ret < 0) {
330 return NULL;
331 }
332
333 return ctx;
334 }
335 #endif
336
ppp_lcp_get(void)337 const struct ppp_protocol_handler *ppp_lcp_get(void)
338 {
339 return ppp_lcp;
340 }
341
ppp_queue_pkt(struct net_pkt * pkt)342 void ppp_queue_pkt(struct net_pkt *pkt)
343 {
344 k_fifo_put(&tx_queue, pkt);
345 }
346
tx_handler(void)347 static void tx_handler(void)
348 {
349 struct net_pkt *pkt;
350 int ret;
351
352 NET_DBG("PPP TX started");
353
354 k_thread_name_set(NULL, "ppp_tx");
355
356 while (1) {
357 pkt = k_fifo_get(&tx_queue, K_FOREVER);
358 if (pkt == NULL) {
359 continue;
360 }
361
362 ret = net_send_data(pkt);
363 if (ret < 0) {
364 net_pkt_unref(pkt);
365 }
366 }
367 }
368
net_ppp_mgmt_evt_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)369 static void net_ppp_mgmt_evt_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
370 struct net_if *iface)
371 {
372 struct ppp_context *ctx;
373
374 if (net_if_l2(iface) != &NET_L2_GET_NAME(PPP)) {
375 return;
376 }
377
378 ctx = net_if_l2_data(iface);
379
380 if (net_if_is_carrier_ok(iface)) {
381 ppp_mgmt_raise_carrier_on_event(iface);
382 } else {
383 ppp_mgmt_raise_carrier_off_event(iface);
384 }
385
386 if (mgmt_event == NET_EVENT_IF_UP) {
387 ppp_open(ctx);
388 return;
389 }
390
391 if (mgmt_event == NET_EVENT_IF_DOWN) {
392 ppp_close(ctx);
393 return;
394 }
395 }
396
net_ppp_init(struct net_if * iface)397 void net_ppp_init(struct net_if *iface)
398 {
399 struct ppp_context *ctx = net_if_l2_data(iface);
400 uint8_t count = 0;
401
402 NET_DBG("Initializing PPP L2 %p for iface %p", ctx, iface);
403
404 memset(ctx, 0, sizeof(*ctx));
405
406 ctx->ppp_l2_flags = NET_L2_MULTICAST | NET_L2_POINT_TO_POINT;
407 ctx->iface = iface;
408
409 #if defined(CONFIG_NET_SHELL)
410 k_sem_init(&ctx->shell.wait_echo_reply, 0, K_SEM_MAX_LIMIT);
411 #endif
412
413 net_mgmt_init_event_callback(&ctx->mgmt_evt_cb, net_ppp_mgmt_evt_handler,
414 (NET_EVENT_IF_UP | NET_EVENT_IF_DOWN));
415
416 net_mgmt_add_event_callback(&ctx->mgmt_evt_cb);
417
418 STRUCT_SECTION_FOREACH(ppp_protocol_handler, proto) {
419 if (proto->protocol == PPP_LCP) {
420 ppp_lcp = proto;
421 }
422
423 proto->init(ctx);
424 count++;
425 }
426
427 if (count == 0) {
428 NET_ERR("There are no PPP protocols configured!");
429 return;
430 }
431
432 if (ppp_lcp == NULL) {
433 NET_ERR("No LCP found!");
434 return;
435 }
436
437 ctx->is_ready_to_serve = true;
438 }
439