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_DECLARE(net_l2_ppp, CONFIG_NET_L2_PPP_LOG_LEVEL);
9 
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_l2.h>
12 #include <zephyr/net/net_if.h>
13 #include <zephyr/net/net_pkt.h>
14 #include <zephyr/net/net_mgmt.h>
15 
16 #include <zephyr/net/ppp.h>
17 
18 #include "net_private.h"
19 
20 #include "ppp_stats.h"
21 #include "ppp_internal.h"
22 
lcp_handle_ext(struct ppp_fsm * fsm,enum ppp_packet_type code,uint8_t id,struct net_pkt * pkt)23 static enum net_verdict lcp_handle_ext(struct ppp_fsm *fsm,
24 				       enum ppp_packet_type code, uint8_t id,
25 				       struct net_pkt *pkt)
26 {
27 	enum net_verdict verdict = NET_DROP;
28 
29 	switch (code) {
30 	case PPP_PROTOCOL_REJ:
31 		NET_DBG("PPP Protocol-Rej");
32 		return ppp_fsm_recv_protocol_rej(fsm, id, pkt);
33 
34 	case PPP_ECHO_REQ:
35 		NET_DBG("PPP Echo-Req");
36 		return ppp_fsm_recv_echo_req(fsm, id, pkt);
37 
38 	case PPP_ECHO_REPLY:
39 		NET_DBG("PPP Echo-Reply");
40 		return ppp_fsm_recv_echo_reply(fsm, id, pkt);
41 
42 	case PPP_DISCARD_REQ:
43 		NET_DBG("PPP Discard-Req");
44 		return ppp_fsm_recv_discard_req(fsm, id, pkt);
45 
46 	default:
47 		break;
48 	}
49 
50 	return verdict;
51 }
52 
lcp_handle(struct ppp_context * ctx,struct net_if * iface,struct net_pkt * pkt)53 static enum net_verdict lcp_handle(struct ppp_context *ctx,
54 				   struct net_if *iface,
55 				   struct net_pkt *pkt)
56 {
57 	return ppp_fsm_input(&ctx->lcp.fsm, PPP_LCP, pkt);
58 }
59 
60 struct lcp_option_data {
61 	bool auth_proto_present;
62 	uint16_t auth_proto;
63 };
64 
65 static const enum ppp_protocol_type lcp_supported_auth_protos[] = {
66 #if defined(CONFIG_NET_L2_PPP_PAP)
67 	PPP_PAP,
68 #endif
69 };
70 
lcp_auth_proto_parse(struct ppp_fsm * fsm,struct net_pkt * pkt,void * user_data)71 static int lcp_auth_proto_parse(struct ppp_fsm *fsm, struct net_pkt *pkt,
72 				void *user_data)
73 {
74 	struct lcp_option_data *data = user_data;
75 	int ret;
76 	int i;
77 
78 	ret = net_pkt_read_be16(pkt, &data->auth_proto);
79 	if (ret < 0) {
80 		/* Should not happen, is the pkt corrupt? */
81 		return -EMSGSIZE;
82 	}
83 
84 	NET_DBG("[LCP] Received auth protocol %x (%s)",
85 		(unsigned int) data->auth_proto,
86 		ppp_proto2str(data->auth_proto));
87 
88 	for (i = 0; i < ARRAY_SIZE(lcp_supported_auth_protos); i++) {
89 		if (data->auth_proto == lcp_supported_auth_protos[i]) {
90 			data->auth_proto_present = true;
91 			return 0;
92 		}
93 	}
94 
95 	return -EINVAL;
96 }
97 
lcp_auth_proto_nack(struct ppp_fsm * fsm,struct net_pkt * ret_pkt,void * user_data)98 static int lcp_auth_proto_nack(struct ppp_fsm *fsm, struct net_pkt *ret_pkt,
99 			       void *user_data)
100 {
101 	(void)net_pkt_write_u8(ret_pkt, LCP_OPTION_AUTH_PROTO);
102 	(void)net_pkt_write_u8(ret_pkt, 4);
103 	return net_pkt_write_be16(ret_pkt, PPP_PAP);
104 }
105 
106 static const struct ppp_peer_option_info lcp_peer_options[] = {
107 	PPP_PEER_OPTION(LCP_OPTION_AUTH_PROTO, lcp_auth_proto_parse,
108 			lcp_auth_proto_nack),
109 };
110 
lcp_config_info_req(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,struct net_pkt * ret_pkt)111 static int lcp_config_info_req(struct ppp_fsm *fsm,
112 			       struct net_pkt *pkt,
113 			       uint16_t length,
114 			       struct net_pkt *ret_pkt)
115 {
116 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
117 					       lcp.fsm);
118 	struct lcp_option_data data = {
119 		.auth_proto_present = false,
120 	};
121 	int ret;
122 
123 	ret = ppp_config_info_req(fsm, pkt, length, ret_pkt, PPP_LCP,
124 				  lcp_peer_options,
125 				  ARRAY_SIZE(lcp_peer_options),
126 				  &data);
127 	if (ret != PPP_CONFIGURE_ACK) {
128 		/* There are some issues with configuration still */
129 		return ret;
130 	}
131 
132 	ctx->lcp.peer_options.auth_proto = data.auth_proto;
133 
134 	if (data.auth_proto_present) {
135 		NET_DBG("Authentication protocol negotiated: %x (%s)",
136 			(unsigned int) data.auth_proto,
137 			ppp_proto2str(data.auth_proto));
138 	}
139 
140 	return PPP_CONFIGURE_ACK;
141 }
142 
lcp_lower_down(struct ppp_context * ctx)143 static void lcp_lower_down(struct ppp_context *ctx)
144 {
145 	ppp_fsm_lower_down(&ctx->lcp.fsm);
146 }
147 
lcp_lower_up(struct ppp_context * ctx)148 static void lcp_lower_up(struct ppp_context *ctx)
149 {
150 #if defined(CONFIG_NET_L2_PPP_OPTION_MRU)
151 	/* Get currently set MTU */
152 	ctx->lcp.my_options.mru = net_if_get_mtu(ctx->iface);
153 #endif
154 
155 	ppp_fsm_lower_up(&ctx->lcp.fsm);
156 }
157 
lcp_open(struct ppp_context * ctx)158 static void lcp_open(struct ppp_context *ctx)
159 {
160 	ppp_fsm_open(&ctx->lcp.fsm);
161 }
162 
lcp_close(struct ppp_context * ctx,const uint8_t * reason)163 static void lcp_close(struct ppp_context *ctx, const uint8_t *reason)
164 {
165 	if (ctx->phase != PPP_DEAD) {
166 		ppp_change_phase(ctx, PPP_TERMINATE);
167 	}
168 
169 	ppp_fsm_close(&ctx->lcp.fsm, reason);
170 }
171 
lcp_down(struct ppp_fsm * fsm)172 static void lcp_down(struct ppp_fsm *fsm)
173 {
174 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
175 					       lcp.fsm);
176 
177 	memset(&ctx->lcp.peer_options.auth_proto, 0,
178 	       sizeof(ctx->lcp.peer_options.auth_proto));
179 
180 	ppp_link_down(ctx);
181 
182 	if (net_if_is_carrier_ok(ctx->iface) && ctx->is_enabled) {
183 		ppp_change_phase(ctx, PPP_ESTABLISH);
184 	}
185 }
186 
lcp_up(struct ppp_fsm * fsm)187 static void lcp_up(struct ppp_fsm *fsm)
188 {
189 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
190 					       lcp.fsm);
191 
192 	/* TODO: Set MRU/MTU of the network interface here */
193 
194 	ppp_link_established(ctx, fsm);
195 }
196 
lcp_starting(struct ppp_fsm * fsm)197 static void lcp_starting(struct ppp_fsm *fsm)
198 {
199 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
200 					       lcp.fsm);
201 
202 	ppp_link_needed(ctx);
203 }
204 
lcp_finished(struct ppp_fsm * fsm)205 static void lcp_finished(struct ppp_fsm *fsm)
206 {
207 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
208 					       lcp.fsm);
209 
210 	ppp_link_terminated(ctx);
211 }
212 
213 #if defined(CONFIG_NET_L2_PPP_OPTION_MRU)
214 
215 #define MRU_OPTION_LEN 4
216 
lcp_add_mru(struct ppp_context * ctx,struct net_pkt * pkt)217 static int lcp_add_mru(struct ppp_context *ctx, struct net_pkt *pkt)
218 {
219 	net_pkt_write_u8(pkt, MRU_OPTION_LEN);
220 	return net_pkt_write_be16(pkt, ctx->lcp.my_options.mru);
221 }
222 
lcp_ack_mru(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t oplen)223 static int lcp_ack_mru(struct ppp_context *ctx, struct net_pkt *pkt,
224 		       uint8_t oplen)
225 {
226 	int ret;
227 	uint16_t mru;
228 
229 	/* Handle ACK : */
230 	if (oplen != sizeof(mru)) {
231 		return -EINVAL;
232 	}
233 
234 	ret = net_pkt_read(pkt, &mru, sizeof(mru));
235 	if (ret) {
236 		return ret;
237 	}
238 	if (mru != ctx->lcp.my_options.mru) {
239 		/* Didn't acked our MRU: */
240 		return -EINVAL;
241 	}
242 
243 	return 0;
244 }
245 
lcp_nak_mru(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t oplen)246 static int lcp_nak_mru(struct ppp_context *ctx, struct net_pkt *pkt,
247 		       uint8_t oplen)
248 {
249 	int ret;
250 	uint16_t mru;
251 
252 	/* Handle NAK: accept only smaller/equal than ours */
253 	if (oplen != sizeof(mru)) {
254 		return -EINVAL;
255 	}
256 
257 	ret = net_pkt_read(pkt, &mru, sizeof(mru));
258 	if (ret) {
259 		return ret;
260 	}
261 
262 	if (mru <= ctx->lcp.my_options.mru) {
263 		/* OK, reset the MRU also in our side: */
264 		ctx->lcp.my_options.mru = mru;
265 	} else {
266 		return -EINVAL;
267 	}
268 
269 	return 0;
270 }
271 #endif
272 
273 #define ASYNC_MAP_OPTION_LEN 6
274 
lcp_add_async_map(struct ppp_context * ctx,struct net_pkt * pkt)275 static int lcp_add_async_map(struct ppp_context *ctx, struct net_pkt *pkt)
276 {
277 	net_pkt_write_u8(pkt, ASYNC_MAP_OPTION_LEN);
278 	return net_pkt_write_be32(pkt, ctx->lcp.my_options.async_map);
279 }
280 
lcp_ack_async_map(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t oplen)281 static int lcp_ack_async_map(struct ppp_context *ctx, struct net_pkt *pkt,
282 			     uint8_t oplen)
283 {
284 	int ret;
285 	uint32_t async_map;
286 
287 	/* Handle ACK : */
288 	if (oplen != sizeof(async_map)) {
289 		return -EINVAL;
290 	}
291 
292 	ret = net_pkt_read(pkt, &async_map, sizeof(async_map));
293 	if (ret) {
294 		return ret;
295 	}
296 	if (async_map != ctx->lcp.my_options.async_map) {
297 		/* Didn't acked our ASYNC_MAP: */
298 		return -EINVAL;
299 	}
300 
301 	return 0;
302 }
303 
lcp_nak_async_map(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t oplen)304 static int lcp_nak_async_map(struct ppp_context *ctx, struct net_pkt *pkt,
305 			     uint8_t oplen)
306 {
307 	int ret;
308 	uint16_t async_map;
309 
310 	/* Handle NAK: accept only equal to ours */
311 	if (oplen != sizeof(async_map)) {
312 		return -EINVAL;
313 	}
314 
315 	ret = net_pkt_read(pkt, &async_map, sizeof(async_map));
316 	if (ret) {
317 		return ret;
318 	}
319 
320 	if (async_map != ctx->lcp.my_options.async_map) {
321 		return -EINVAL;
322 	}
323 
324 	return 0;
325 }
326 
327 
328 static const struct ppp_my_option_info lcp_my_options[] = {
329 #if defined(CONFIG_NET_L2_PPP_OPTION_MRU)
330 	PPP_MY_OPTION(LCP_OPTION_MRU, lcp_add_mru, lcp_ack_mru, lcp_nak_mru),
331 #endif
332 	PPP_MY_OPTION(LCP_OPTION_ASYNC_CTRL_CHAR_MAP, lcp_add_async_map,
333 			lcp_ack_async_map, lcp_nak_async_map),
334 };
335 BUILD_ASSERT(ARRAY_SIZE(lcp_my_options) == LCP_NUM_MY_OPTIONS);
336 
lcp_config_info_add(struct ppp_fsm * fsm)337 static struct net_pkt *lcp_config_info_add(struct ppp_fsm *fsm)
338 {
339 #if defined(CONFIG_NET_L2_PPP_OPTION_MRU)
340 	return ppp_my_options_add(fsm, MRU_OPTION_LEN + ASYNC_MAP_OPTION_LEN);
341 #else
342 	return ppp_my_options_add(fsm, ASYNC_MAP_OPTION_LEN);
343 #endif
344 }
345 
lcp_config_info_nack(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,bool rejected)346 static int lcp_config_info_nack(struct ppp_fsm *fsm, struct net_pkt *pkt,
347 				uint16_t length, bool rejected)
348 {
349 	struct ppp_context *ctx =
350 		CONTAINER_OF(fsm, struct ppp_context, lcp.fsm);
351 	int ret;
352 
353 	ret = ppp_my_options_parse_conf_nak(fsm, pkt, length);
354 	if (ret) {
355 		return ret;
356 	}
357 
358 	if (!ctx->lcp.my_options.mru) {
359 		return -EINVAL;
360 	}
361 
362 	return 0;
363 }
364 
lcp_init(struct ppp_context * ctx)365 static void lcp_init(struct ppp_context *ctx)
366 {
367 	NET_DBG("proto %s (0x%04x) fsm %p", ppp_proto2str(PPP_LCP), PPP_LCP,
368 		&ctx->lcp.fsm);
369 
370 	memset(&ctx->lcp.fsm, 0, sizeof(ctx->lcp.fsm));
371 
372 	ppp_fsm_init(&ctx->lcp.fsm, PPP_LCP);
373 
374 	ppp_fsm_name_set(&ctx->lcp.fsm, ppp_proto2str(PPP_LCP));
375 
376 	ctx->lcp.my_options.mru = net_if_get_mtu(ctx->iface);
377 	ctx->lcp.my_options.async_map = 0xffffffff;
378 
379 	ctx->lcp.fsm.my_options.info = lcp_my_options;
380 	ctx->lcp.fsm.my_options.data = ctx->lcp.my_options_data;
381 	ctx->lcp.fsm.my_options.count = ARRAY_SIZE(lcp_my_options);
382 
383 	ctx->lcp.fsm.cb.config_info_add = lcp_config_info_add;
384 	ctx->lcp.fsm.cb.config_info_req = lcp_config_info_req;
385 	ctx->lcp.fsm.cb.config_info_nack = lcp_config_info_nack;
386 	ctx->lcp.fsm.cb.config_info_rej = ppp_my_options_parse_conf_rej;
387 
388 	ctx->lcp.fsm.cb.up = lcp_up;
389 	ctx->lcp.fsm.cb.down = lcp_down;
390 	ctx->lcp.fsm.cb.starting = lcp_starting;
391 	ctx->lcp.fsm.cb.finished = lcp_finished;
392 	if (IS_ENABLED(CONFIG_NET_L2_PPP_AUTH_SUPPORT)) {
393 		ctx->lcp.fsm.cb.config_info_req = lcp_config_info_req;
394 	}
395 	ctx->lcp.fsm.cb.proto_extension = lcp_handle_ext;
396 }
397 
398 PPP_PROTOCOL_REGISTER(LCP, PPP_LCP,
399 		      lcp_init, lcp_handle,
400 		      lcp_lower_up, lcp_lower_down,
401 		      lcp_open, lcp_close);
402