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_pkt.h>
12 
13 #include <zephyr/net/ppp.h>
14 
15 #include "net_private.h"
16 
17 #include "ppp_internal.h"
18 
19 #define ALLOC_TIMEOUT K_MSEC(100)
20 
ppp_parse_options(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,int (* parse)(struct net_pkt * pkt,uint8_t code,uint8_t len,void * user_data),void * user_data)21 int ppp_parse_options(struct ppp_fsm *fsm, struct net_pkt *pkt,
22 		      uint16_t length,
23 		      int (*parse)(struct net_pkt *pkt, uint8_t code,
24 				   uint8_t len, void *user_data),
25 		      void *user_data)
26 {
27 	int remaining = length, pkt_remaining;
28 	uint8_t opt_type, opt_len, opt_val_len;
29 	int ret;
30 	struct net_pkt_cursor cursor;
31 
32 	pkt_remaining = net_pkt_remaining_data(pkt);
33 	if (remaining != pkt_remaining) {
34 		NET_DBG("Expecting %d but pkt data length is %d bytes",
35 			remaining, pkt_remaining);
36 		return -EMSGSIZE;
37 	}
38 
39 	while (remaining > 0) {
40 		ret = net_pkt_read_u8(pkt, &opt_type);
41 		if (ret < 0) {
42 			NET_DBG("Cannot read %s (%d) (remaining len %d)",
43 				"opt_type", ret, pkt_remaining);
44 			return -EBADMSG;
45 		}
46 
47 		ret = net_pkt_read_u8(pkt, &opt_len);
48 		if (ret < 0) {
49 			NET_DBG("Cannot read %s (%d) (remaining len %d)",
50 				"opt_len", ret, remaining);
51 			return -EBADMSG;
52 		}
53 
54 		opt_val_len = opt_len - sizeof(opt_type) - sizeof(opt_len);
55 
56 		net_pkt_cursor_backup(pkt, &cursor);
57 
58 		NET_DBG("[%s/%p] option %s (%d) len %d", fsm->name, fsm,
59 			ppp_option2str(fsm->protocol, opt_type), opt_type,
60 			opt_len);
61 
62 		ret = parse(pkt, opt_type, opt_val_len, user_data);
63 		if (ret < 0) {
64 			return ret;
65 		}
66 
67 		net_pkt_cursor_restore(pkt, &cursor);
68 
69 		net_pkt_skip(pkt, opt_val_len);
70 		remaining -= opt_len;
71 	}
72 
73 	if (remaining < 0) {
74 		return -EBADMSG;
75 	}
76 
77 	return 0;
78 }
79 
80 static const struct ppp_peer_option_info *
ppp_peer_option_info_get(const struct ppp_peer_option_info * options,size_t num_options,uint8_t code)81 ppp_peer_option_info_get(const struct ppp_peer_option_info *options,
82 			 size_t num_options,
83 			 uint8_t code)
84 {
85 	size_t i;
86 
87 	for (i = 0; i < num_options; i++) {
88 		if (options[i].code == code) {
89 			return &options[i];
90 		}
91 	}
92 
93 	return NULL;
94 }
95 
96 struct ppp_parse_option_conf_req_data {
97 	struct ppp_fsm *fsm;
98 	struct net_pkt *ret_pkt;
99 	enum ppp_protocol_type protocol;
100 	const struct ppp_peer_option_info *options_info;
101 	size_t num_options_info;
102 	void *user_data;
103 
104 	int nack_count;
105 	int rej_count;
106 };
107 
ppp_parse_option_conf_req_unsupported(struct net_pkt * pkt,uint8_t code,uint8_t len,void * user_data)108 static int ppp_parse_option_conf_req_unsupported(struct net_pkt *pkt,
109 						 uint8_t code, uint8_t len,
110 						 void *user_data)
111 {
112 	struct ppp_parse_option_conf_req_data *parse_data = user_data;
113 	struct ppp_fsm *fsm = parse_data->fsm;
114 	struct net_pkt *ret_pkt = parse_data->ret_pkt;
115 	const struct ppp_peer_option_info *option_info =
116 		ppp_peer_option_info_get(parse_data->options_info,
117 					 parse_data->num_options_info,
118 					 code);
119 
120 	NET_DBG("[%s/%p] %s option %s (%d) len %d",
121 		fsm->name, fsm, "Check",
122 		ppp_option2str(parse_data->protocol, code),
123 		code, len);
124 
125 	if (option_info) {
126 		return 0;
127 	}
128 
129 	parse_data->rej_count++;
130 
131 	net_pkt_write_u8(ret_pkt, code);
132 	net_pkt_write_u8(ret_pkt, len + sizeof(code) + sizeof(len));
133 
134 	if (len > 0) {
135 		net_pkt_copy(ret_pkt, pkt, len);
136 	}
137 
138 	return 0;
139 }
140 
ppp_parse_option_conf_req_supported(struct net_pkt * pkt,uint8_t code,uint8_t len,void * user_data)141 static int ppp_parse_option_conf_req_supported(struct net_pkt *pkt,
142 					       uint8_t code, uint8_t len,
143 					       void *user_data)
144 {
145 	struct ppp_parse_option_conf_req_data *parse_data = user_data;
146 	struct ppp_fsm *fsm = parse_data->fsm;
147 	struct net_pkt *ret_pkt = parse_data->ret_pkt;
148 	struct net_pkt_cursor cursor;
149 	const struct ppp_peer_option_info *option_info =
150 		ppp_peer_option_info_get(parse_data->options_info,
151 					 parse_data->num_options_info,
152 					 code);
153 	int ret;
154 
155 	net_pkt_cursor_backup(pkt, &cursor);
156 	ret = option_info->parse(fsm, pkt, parse_data->user_data);
157 	if (ret == -ENOTSUP) {
158 		net_pkt_cursor_restore(pkt, &cursor);
159 		parse_data->rej_count++;
160 		if (parse_data->nack_count != 0) {
161 			/* Remove any NACKed data, if we need to reject something first */
162 			net_pkt_update_length(ret_pkt, 0);
163 			net_pkt_cursor_init(ret_pkt);
164 			parse_data->nack_count = 0;
165 		}
166 		net_pkt_write_u8(ret_pkt, code);
167 		net_pkt_write_u8(ret_pkt, len + sizeof(code) + sizeof(len));
168 		if (len > 0) {
169 			net_pkt_copy(ret_pkt, pkt, len);
170 		}
171 		return 0;
172 	} else if (ret == -EINVAL) {
173 		if (parse_data->rej_count != 0) {
174 			/* If we have already rejected some options, we
175 			 * cannot NACK anything in the same packet.
176 			 */
177 			return 0;
178 		}
179 		parse_data->nack_count++;
180 		ret = option_info->nack(fsm, parse_data->ret_pkt,
181 					parse_data->user_data);
182 	}
183 
184 	return ret;
185 }
186 
ppp_config_info_req(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,struct net_pkt * ret_pkt,enum ppp_protocol_type protocol,const struct ppp_peer_option_info * options_info,size_t num_options_info,void * user_data)187 int ppp_config_info_req(struct ppp_fsm *fsm,
188 			struct net_pkt *pkt,
189 			uint16_t length,
190 			struct net_pkt *ret_pkt,
191 			enum ppp_protocol_type protocol,
192 			const struct ppp_peer_option_info *options_info,
193 			size_t num_options_info,
194 			void *user_data)
195 {
196 	struct ppp_parse_option_conf_req_data parse_data = {
197 		.fsm = fsm,
198 		.ret_pkt = ret_pkt,
199 		.protocol = protocol,
200 		.options_info = options_info,
201 		.num_options_info = num_options_info,
202 		.user_data = user_data,
203 	};
204 	struct net_pkt_cursor cursor;
205 	int ret;
206 
207 	net_pkt_cursor_backup(pkt, &cursor);
208 
209 	ret = ppp_parse_options(fsm, pkt, length,
210 				ppp_parse_option_conf_req_unsupported,
211 				&parse_data);
212 	if (ret < 0) {
213 		return -EINVAL;
214 	}
215 
216 	if (parse_data.rej_count) {
217 		return PPP_CONFIGURE_REJ;
218 	}
219 
220 	net_pkt_cursor_restore(pkt, &cursor);
221 
222 	ret = ppp_parse_options(fsm, pkt, length,
223 				ppp_parse_option_conf_req_supported,
224 				&parse_data);
225 	if (ret < 0) {
226 		return -EINVAL;
227 	}
228 
229 	if (parse_data.rej_count) {
230 		return PPP_CONFIGURE_REJ;
231 	}
232 
233 	if (parse_data.nack_count) {
234 		return PPP_CONFIGURE_NACK;
235 	}
236 
237 	net_pkt_cursor_restore(pkt, &cursor);
238 	net_pkt_copy(ret_pkt, pkt, length);
239 
240 	return PPP_CONFIGURE_ACK;
241 }
242 
ppp_my_options_add(struct ppp_fsm * fsm,size_t packet_len)243 struct net_pkt *ppp_my_options_add(struct ppp_fsm *fsm, size_t packet_len)
244 {
245 	struct ppp_context *ctx = ppp_fsm_ctx(fsm);
246 	const struct ppp_my_option_info *info;
247 	struct ppp_my_option_data *data;
248 	struct net_pkt *pkt;
249 	size_t i;
250 	int err;
251 
252 	pkt = net_pkt_alloc_with_buffer(ppp_fsm_iface(fsm), packet_len,
253 					AF_UNSPEC, 0, PPP_BUF_ALLOC_TIMEOUT);
254 	if (!pkt) {
255 		return NULL;
256 	}
257 
258 	for (i = 0; i < fsm->my_options.count; i++) {
259 		info = &fsm->my_options.info[i];
260 		data = &fsm->my_options.data[i];
261 
262 		if (data->flags & PPP_MY_OPTION_REJECTED) {
263 			continue;
264 		}
265 
266 		err = net_pkt_write_u8(pkt, info->code);
267 		if (err) {
268 			goto unref_pkt;
269 		}
270 
271 		err = info->conf_req_add(ctx, pkt);
272 		if (err) {
273 			goto unref_pkt;
274 		}
275 	}
276 
277 	return pkt;
278 
279 unref_pkt:
280 	net_pkt_unref(pkt);
281 
282 	return NULL;
283 }
284 
285 typedef int (*ppp_my_option_handle_t)(struct ppp_context *ctx,
286 				      struct net_pkt *pkt, uint8_t len,
287 				      const struct ppp_my_option_info *info,
288 				      struct ppp_my_option_data *data);
289 
290 struct ppp_my_option_handle_data {
291 	struct ppp_fsm *fsm;
292 	ppp_my_option_handle_t handle;
293 };
294 
ppp_my_option_get(struct ppp_fsm * fsm,uint8_t code,const struct ppp_my_option_info ** info,struct ppp_my_option_data ** data)295 static int ppp_my_option_get(struct ppp_fsm *fsm, uint8_t code,
296 			     const struct ppp_my_option_info **info,
297 			     struct ppp_my_option_data **data)
298 {
299 	int i;
300 
301 	for (i = 0; i < fsm->my_options.count; i++) {
302 		if (fsm->my_options.info[i].code == code) {
303 			*info = &fsm->my_options.info[i];
304 			*data = &fsm->my_options.data[i];
305 			return 0;
306 		}
307 	}
308 
309 	return -ENOENT;
310 }
311 
ppp_my_option_parse(struct net_pkt * pkt,uint8_t code,uint8_t len,void * user_data)312 static int ppp_my_option_parse(struct net_pkt *pkt, uint8_t code,
313 			       uint8_t len, void *user_data)
314 {
315 	struct ppp_my_option_handle_data *d = user_data;
316 	struct ppp_context *ctx = ppp_fsm_ctx(d->fsm);
317 	const struct ppp_my_option_info *info;
318 	struct ppp_my_option_data *data;
319 	int ret;
320 
321 	ret = ppp_my_option_get(d->fsm, code, &info, &data);
322 	if (ret < 0) {
323 		return 0;
324 	}
325 
326 	return d->handle(ctx, pkt, len, info, data);
327 }
328 
ppp_my_options_parse(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,ppp_my_option_handle_t handle)329 static int ppp_my_options_parse(struct ppp_fsm *fsm,
330 				struct net_pkt *pkt,
331 				uint16_t length,
332 				ppp_my_option_handle_t handle)
333 {
334 	struct ppp_my_option_handle_data parse_data = {
335 		.fsm = fsm,
336 		.handle = handle,
337 	};
338 
339 	return ppp_parse_options(fsm, pkt, length, ppp_my_option_parse,
340 				 &parse_data);
341 }
342 
ppp_my_option_parse_conf_ack(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t len,const struct ppp_my_option_info * info,struct ppp_my_option_data * data)343 static int ppp_my_option_parse_conf_ack(struct ppp_context *ctx,
344 					struct net_pkt *pkt, uint8_t len,
345 					const struct ppp_my_option_info *info,
346 					struct ppp_my_option_data *data)
347 {
348 	data->flags |= PPP_MY_OPTION_ACKED;
349 
350 	if (info->conf_ack_handle) {
351 		return info->conf_ack_handle(ctx, pkt, len);
352 	}
353 
354 	return 0;
355 }
356 
ppp_my_options_parse_conf_ack(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)357 int ppp_my_options_parse_conf_ack(struct ppp_fsm *fsm,
358 				  struct net_pkt *pkt,
359 				  uint16_t length)
360 {
361 	return ppp_my_options_parse(fsm, pkt, length,
362 				    ppp_my_option_parse_conf_ack);
363 }
364 
ppp_my_option_parse_conf_nak(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t len,const struct ppp_my_option_info * info,struct ppp_my_option_data * data)365 static int ppp_my_option_parse_conf_nak(struct ppp_context *ctx,
366 					struct net_pkt *pkt, uint8_t len,
367 					const struct ppp_my_option_info *info,
368 					struct ppp_my_option_data *data)
369 {
370 	return info->conf_nak_handle(ctx, pkt, len);
371 }
372 
ppp_my_options_parse_conf_nak(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)373 int ppp_my_options_parse_conf_nak(struct ppp_fsm *fsm,
374 				  struct net_pkt *pkt,
375 				  uint16_t length)
376 {
377 	return ppp_my_options_parse(fsm, pkt, length,
378 				    ppp_my_option_parse_conf_nak);
379 }
380 
ppp_my_option_parse_conf_rej(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t len,const struct ppp_my_option_info * info,struct ppp_my_option_data * data)381 static int ppp_my_option_parse_conf_rej(struct ppp_context *ctx,
382 					struct net_pkt *pkt, uint8_t len,
383 					const struct ppp_my_option_info *info,
384 					struct ppp_my_option_data *data)
385 {
386 	data->flags |= PPP_MY_OPTION_REJECTED;
387 
388 	return 0;
389 }
390 
ppp_my_options_parse_conf_rej(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)391 int ppp_my_options_parse_conf_rej(struct ppp_fsm *fsm,
392 				  struct net_pkt *pkt,
393 				  uint16_t length)
394 {
395 	return ppp_my_options_parse(fsm, pkt, length,
396 				    ppp_my_option_parse_conf_rej);
397 }
398 
ppp_my_option_flags(struct ppp_fsm * fsm,uint8_t code)399 uint32_t ppp_my_option_flags(struct ppp_fsm *fsm, uint8_t code)
400 {
401 	const struct ppp_my_option_info *info;
402 	struct ppp_my_option_data *data;
403 	int ret;
404 
405 	ret = ppp_my_option_get(fsm, code, &info, &data);
406 	if (ret) {
407 		return false;
408 	}
409 
410 	return data->flags;
411 }
412