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 const struct ppp_peer_option_info *option_info =
148 ppp_peer_option_info_get(parse_data->options_info,
149 parse_data->num_options_info,
150 code);
151 int ret;
152
153 ret = option_info->parse(fsm, pkt, parse_data->user_data);
154 if (ret == -EINVAL) {
155 parse_data->nack_count++;
156 ret = option_info->nack(fsm, parse_data->ret_pkt,
157 parse_data->user_data);
158 }
159
160 return ret;
161 }
162
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)163 int ppp_config_info_req(struct ppp_fsm *fsm,
164 struct net_pkt *pkt,
165 uint16_t length,
166 struct net_pkt *ret_pkt,
167 enum ppp_protocol_type protocol,
168 const struct ppp_peer_option_info *options_info,
169 size_t num_options_info,
170 void *user_data)
171 {
172 struct ppp_parse_option_conf_req_data parse_data = {
173 .fsm = fsm,
174 .ret_pkt = ret_pkt,
175 .protocol = protocol,
176 .options_info = options_info,
177 .num_options_info = num_options_info,
178 .user_data = user_data,
179 };
180 struct net_pkt_cursor cursor;
181 int ret;
182
183 net_pkt_cursor_backup(pkt, &cursor);
184
185 ret = ppp_parse_options(fsm, pkt, length,
186 ppp_parse_option_conf_req_unsupported,
187 &parse_data);
188 if (ret < 0) {
189 return -EINVAL;
190 }
191
192 if (parse_data.rej_count) {
193 return PPP_CONFIGURE_REJ;
194 }
195
196 net_pkt_cursor_restore(pkt, &cursor);
197
198 ret = ppp_parse_options(fsm, pkt, length,
199 ppp_parse_option_conf_req_supported,
200 &parse_data);
201 if (ret < 0) {
202 return -EINVAL;
203 }
204
205 if (parse_data.nack_count) {
206 return PPP_CONFIGURE_NACK;
207 }
208
209 net_pkt_cursor_restore(pkt, &cursor);
210 net_pkt_copy(ret_pkt, pkt, length);
211
212 return PPP_CONFIGURE_ACK;
213 }
214
ppp_my_options_add(struct ppp_fsm * fsm,size_t packet_len)215 struct net_pkt *ppp_my_options_add(struct ppp_fsm *fsm, size_t packet_len)
216 {
217 struct ppp_context *ctx = ppp_fsm_ctx(fsm);
218 const struct ppp_my_option_info *info;
219 struct ppp_my_option_data *data;
220 struct net_pkt *pkt;
221 size_t i;
222 int err;
223
224 pkt = net_pkt_alloc_with_buffer(ppp_fsm_iface(fsm), packet_len,
225 AF_UNSPEC, 0, PPP_BUF_ALLOC_TIMEOUT);
226 if (!pkt) {
227 return NULL;
228 }
229
230 for (i = 0; i < fsm->my_options.count; i++) {
231 info = &fsm->my_options.info[i];
232 data = &fsm->my_options.data[i];
233
234 if (data->flags & PPP_MY_OPTION_REJECTED) {
235 continue;
236 }
237
238 err = net_pkt_write_u8(pkt, info->code);
239 if (err) {
240 goto unref_pkt;
241 }
242
243 err = info->conf_req_add(ctx, pkt);
244 if (err) {
245 goto unref_pkt;
246 }
247 }
248
249 return pkt;
250
251 unref_pkt:
252 net_pkt_unref(pkt);
253
254 return NULL;
255 }
256
257 typedef int (*ppp_my_option_handle_t)(struct ppp_context *ctx,
258 struct net_pkt *pkt, uint8_t len,
259 const struct ppp_my_option_info *info,
260 struct ppp_my_option_data *data);
261
262 struct ppp_my_option_handle_data {
263 struct ppp_fsm *fsm;
264 ppp_my_option_handle_t handle;
265 };
266
ppp_my_option_get(struct ppp_fsm * fsm,uint8_t code,const struct ppp_my_option_info ** info,struct ppp_my_option_data ** data)267 static int ppp_my_option_get(struct ppp_fsm *fsm, uint8_t code,
268 const struct ppp_my_option_info **info,
269 struct ppp_my_option_data **data)
270 {
271 int i;
272
273 for (i = 0; i < fsm->my_options.count; i++) {
274 if (fsm->my_options.info[i].code == code) {
275 *info = &fsm->my_options.info[i];
276 *data = &fsm->my_options.data[i];
277 return 0;
278 }
279 }
280
281 return -ENOENT;
282 }
283
ppp_my_option_parse(struct net_pkt * pkt,uint8_t code,uint8_t len,void * user_data)284 static int ppp_my_option_parse(struct net_pkt *pkt, uint8_t code,
285 uint8_t len, void *user_data)
286 {
287 struct ppp_my_option_handle_data *d = user_data;
288 struct ppp_context *ctx = ppp_fsm_ctx(d->fsm);
289 const struct ppp_my_option_info *info;
290 struct ppp_my_option_data *data;
291 int ret;
292
293 ret = ppp_my_option_get(d->fsm, code, &info, &data);
294 if (ret < 0) {
295 return 0;
296 }
297
298 return d->handle(ctx, pkt, len, info, data);
299 }
300
ppp_my_options_parse(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,ppp_my_option_handle_t handle)301 static int ppp_my_options_parse(struct ppp_fsm *fsm,
302 struct net_pkt *pkt,
303 uint16_t length,
304 ppp_my_option_handle_t handle)
305 {
306 struct ppp_my_option_handle_data parse_data = {
307 .fsm = fsm,
308 .handle = handle,
309 };
310
311 return ppp_parse_options(fsm, pkt, length, ppp_my_option_parse,
312 &parse_data);
313 }
314
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)315 static int ppp_my_option_parse_conf_ack(struct ppp_context *ctx,
316 struct net_pkt *pkt, uint8_t len,
317 const struct ppp_my_option_info *info,
318 struct ppp_my_option_data *data)
319 {
320 data->flags |= PPP_MY_OPTION_ACKED;
321
322 if (info->conf_ack_handle) {
323 return info->conf_ack_handle(ctx, pkt, len);
324 }
325
326 return 0;
327 }
328
ppp_my_options_parse_conf_ack(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)329 int ppp_my_options_parse_conf_ack(struct ppp_fsm *fsm,
330 struct net_pkt *pkt,
331 uint16_t length)
332 {
333 return ppp_my_options_parse(fsm, pkt, length,
334 ppp_my_option_parse_conf_ack);
335 }
336
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)337 static int ppp_my_option_parse_conf_nak(struct ppp_context *ctx,
338 struct net_pkt *pkt, uint8_t len,
339 const struct ppp_my_option_info *info,
340 struct ppp_my_option_data *data)
341 {
342 return info->conf_nak_handle(ctx, pkt, len);
343 }
344
ppp_my_options_parse_conf_nak(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)345 int ppp_my_options_parse_conf_nak(struct ppp_fsm *fsm,
346 struct net_pkt *pkt,
347 uint16_t length)
348 {
349 return ppp_my_options_parse(fsm, pkt, length,
350 ppp_my_option_parse_conf_nak);
351 }
352
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)353 static int ppp_my_option_parse_conf_rej(struct ppp_context *ctx,
354 struct net_pkt *pkt, uint8_t len,
355 const struct ppp_my_option_info *info,
356 struct ppp_my_option_data *data)
357 {
358 data->flags |= PPP_MY_OPTION_REJECTED;
359
360 return 0;
361 }
362
ppp_my_options_parse_conf_rej(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)363 int ppp_my_options_parse_conf_rej(struct ppp_fsm *fsm,
364 struct net_pkt *pkt,
365 uint16_t length)
366 {
367 return ppp_my_options_parse(fsm, pkt, length,
368 ppp_my_option_parse_conf_rej);
369 }
370
ppp_my_option_flags(struct ppp_fsm * fsm,uint8_t code)371 uint32_t ppp_my_option_flags(struct ppp_fsm *fsm, uint8_t code)
372 {
373 const struct ppp_my_option_info *info;
374 struct ppp_my_option_data *data;
375 int ret;
376
377 ret = ppp_my_option_get(fsm, code, &info, &data);
378 if (ret) {
379 return false;
380 }
381
382 return data->flags;
383 }
384