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 #include "ipv6.h"
17 
18 #include "ppp_internal.h"
19 
ipv6cp_handle(struct ppp_context * ctx,struct net_if * iface,struct net_pkt * pkt)20 static enum net_verdict ipv6cp_handle(struct ppp_context *ctx,
21 				    struct net_if *iface,
22 				    struct net_pkt *pkt)
23 {
24 	return ppp_fsm_input(&ctx->ipv6cp.fsm, PPP_IPV6CP, pkt);
25 }
26 
27 /* Length is (10): code + id + interface identifier length */
28 #define INTERFACE_IDENTIFIER_OPTION_LEN (1 + 1 + 8)
29 
ipv6cp_add_iid(struct ppp_context * ctx,struct net_pkt * pkt)30 static int ipv6cp_add_iid(struct ppp_context *ctx, struct net_pkt *pkt)
31 {
32 	uint8_t *iid = ctx->ipv6cp.my_options.iid;
33 	size_t iid_len = sizeof(ctx->ipv6cp.my_options.iid);
34 	struct net_linkaddr *linkaddr;
35 
36 	linkaddr = net_if_get_link_addr(ctx->iface);
37 	if (linkaddr->len == 8) {
38 		memcpy(iid, linkaddr->addr, iid_len);
39 	} else {
40 		NET_ASSERT(linkaddr->len >= 6);
41 		memcpy(iid, linkaddr->addr, 3);
42 		iid[3] = 0xff;
43 		iid[4] = 0xfe;
44 		memcpy(iid + 5, linkaddr->addr + 3, 3);
45 	}
46 
47 	net_pkt_write_u8(pkt, INTERFACE_IDENTIFIER_OPTION_LEN);
48 	return net_pkt_write(pkt, iid, iid_len);
49 }
50 
ipv6cp_ack_iid(struct ppp_context * ctx,struct net_pkt * pkt,uint8_t oplen)51 static int ipv6cp_ack_iid(struct ppp_context *ctx, struct net_pkt *pkt,
52 			  uint8_t oplen)
53 {
54 	uint8_t *req_iid = ctx->ipv6cp.my_options.iid;
55 	uint8_t ack_iid[PPP_INTERFACE_IDENTIFIER_LEN];
56 	int ret;
57 
58 	if (oplen != sizeof(ack_iid)) {
59 		return -EINVAL;
60 	}
61 
62 	ret = net_pkt_read(pkt, ack_iid, sizeof(ack_iid));
63 	if (ret) {
64 		return ret;
65 	}
66 
67 	if (memcmp(ack_iid, req_iid, sizeof(ack_iid)) != 0) {
68 		return -EINVAL;
69 	}
70 
71 	return 0;
72 }
73 
74 static const struct ppp_my_option_info ipv6cp_my_options[] = {
75 	PPP_MY_OPTION(IPV6CP_OPTION_INTERFACE_IDENTIFIER,
76 		      ipv6cp_add_iid, ipv6cp_ack_iid, NULL),
77 };
78 
79 BUILD_ASSERT(ARRAY_SIZE(ipv6cp_my_options) == IPV6CP_NUM_MY_OPTIONS);
80 
ipv6cp_config_info_add(struct ppp_fsm * fsm)81 static struct net_pkt *ipv6cp_config_info_add(struct ppp_fsm *fsm)
82 {
83 	return ppp_my_options_add(fsm, INTERFACE_IDENTIFIER_OPTION_LEN);
84 }
85 
86 struct ipv6cp_peer_option_data {
87 	bool iface_id_present;
88 	uint8_t iface_id[PPP_INTERFACE_IDENTIFIER_LEN];
89 };
90 
ipv6cp_interface_identifier_parse(struct ppp_fsm * fsm,struct net_pkt * pkt,void * user_data)91 static int ipv6cp_interface_identifier_parse(struct ppp_fsm *fsm,
92 					     struct net_pkt *pkt,
93 					     void *user_data)
94 {
95 	struct ipv6cp_peer_option_data *data = user_data;
96 	int ret;
97 
98 	ret = net_pkt_read(pkt, data->iface_id, sizeof(data->iface_id));
99 	if (ret < 0) {
100 		/* Should not happen, is the pkt corrupt? */
101 		return -EMSGSIZE;
102 	}
103 
104 	if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
105 		uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
106 
107 		net_sprint_ll_addr_buf(data->iface_id, sizeof(data->iface_id),
108 				       iid_str, sizeof(iid_str));
109 
110 		NET_DBG("[%s/%p] Received %siid %s",
111 			fsm->name, fsm, "peer ", iid_str);
112 	}
113 
114 	data->iface_id_present = true;
115 
116 	return 0;
117 }
118 
119 static const struct ppp_peer_option_info ipv6cp_peer_options[] = {
120 	PPP_PEER_OPTION(IPV6CP_OPTION_INTERFACE_IDENTIFIER,
121 			ipv6cp_interface_identifier_parse, NULL),
122 };
123 
ipv6cp_config_info_req(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length,struct net_pkt * ret_pkt)124 static int ipv6cp_config_info_req(struct ppp_fsm *fsm,
125 				  struct net_pkt *pkt,
126 				  uint16_t length,
127 				  struct net_pkt *ret_pkt)
128 {
129 	struct ppp_context *ctx =
130 		CONTAINER_OF(fsm, struct ppp_context, ipv6cp.fsm);
131 	struct ipv6cp_peer_option_data data = {
132 		.iface_id_present = false,
133 	};
134 	int ret;
135 
136 	ret = ppp_config_info_req(fsm, pkt, length, ret_pkt, PPP_IPV6CP,
137 				  ipv6cp_peer_options,
138 				  ARRAY_SIZE(ipv6cp_peer_options),
139 				  &data);
140 	if (ret != PPP_CONFIGURE_ACK) {
141 		/* There are some issues with configuration still */
142 		return ret;
143 	}
144 
145 	if (!data.iface_id_present) {
146 		/* Interface id option was not present */
147 		return -EINVAL;
148 	}
149 
150 	memcpy(ctx->ipv6cp.peer_options.iid, data.iface_id,
151 	       sizeof(data.iface_id));
152 
153 	return PPP_CONFIGURE_ACK;
154 }
155 
ipv6cp_config_info_ack(struct ppp_fsm * fsm,struct net_pkt * pkt,uint16_t length)156 static int ipv6cp_config_info_ack(struct ppp_fsm *fsm,
157 				  struct net_pkt *pkt,
158 				  uint16_t length)
159 {
160 	int ret;
161 
162 	ret = ppp_my_options_parse_conf_ack(fsm, pkt, length);
163 	if (ret) {
164 		return -EINVAL;
165 	}
166 
167 	if (!ppp_my_option_is_acked(fsm, IPV6CP_OPTION_INTERFACE_IDENTIFIER)) {
168 		NET_ERR("IID was not acked");
169 		return -EINVAL;
170 	}
171 
172 	if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
173 		struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
174 						       ipv6cp.fsm);
175 		uint8_t *iid = ctx->ipv6cp.my_options.iid;
176 		size_t iid_len = sizeof(ctx->ipv6cp.my_options.iid);
177 		uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
178 
179 		net_sprint_ll_addr_buf(iid, iid_len,
180 				       iid_str, sizeof(iid_str));
181 
182 		NET_DBG("[%s/%p] Received %siid %s",
183 			fsm->name, fsm, "", iid_str);
184 	}
185 
186 	return 0;
187 }
188 
ipv6cp_lower_down(struct ppp_context * ctx)189 static void ipv6cp_lower_down(struct ppp_context *ctx)
190 {
191 	ppp_fsm_lower_down(&ctx->ipv6cp.fsm);
192 }
193 
ipv6cp_lower_up(struct ppp_context * ctx)194 static void ipv6cp_lower_up(struct ppp_context *ctx)
195 {
196 	ppp_fsm_lower_up(&ctx->ipv6cp.fsm);
197 }
198 
ipv6cp_open(struct ppp_context * ctx)199 static void ipv6cp_open(struct ppp_context *ctx)
200 {
201 	ppp_fsm_open(&ctx->ipv6cp.fsm);
202 }
203 
ipv6cp_close(struct ppp_context * ctx,const uint8_t * reason)204 static void ipv6cp_close(struct ppp_context *ctx, const uint8_t *reason)
205 {
206 	ppp_fsm_close(&ctx->ipv6cp.fsm, reason);
207 }
208 
setup_iid_address(uint8_t * iid,struct in6_addr * addr)209 static void setup_iid_address(uint8_t *iid, struct in6_addr *addr)
210 {
211 	addr->s6_addr[0] = 0xfe;
212 	addr->s6_addr[1] = 0x80;
213 	UNALIGNED_PUT(0, &addr->s6_addr16[1]);
214 	UNALIGNED_PUT(0, &addr->s6_addr32[1]);
215 	memcpy(&addr->s6_addr[8], iid, PPP_INTERFACE_IDENTIFIER_LEN);
216 
217 	/* TODO: should we toggle local/global bit */
218 	/* addr->s6_addr[8] ^= 0x02; */
219 }
220 
add_iid_address(struct net_if * iface,uint8_t * iid)221 static void add_iid_address(struct net_if *iface, uint8_t *iid)
222 {
223 	struct net_if_addr *ifaddr;
224 	struct in6_addr addr;
225 
226 	setup_iid_address(iid, &addr);
227 
228 	ifaddr = net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, 0);
229 	if (!ifaddr) {
230 		NET_ERR("Cannot add %s address to interface %p",
231 			net_sprint_ipv6_addr(&addr), iface);
232 	} else {
233 		/* As DAD is disabled, we need to mark the address
234 		 * as a preferred one.
235 		 */
236 		ifaddr->addr_state = NET_ADDR_PREFERRED;
237 	}
238 }
239 
ipv6cp_up(struct ppp_fsm * fsm)240 static void ipv6cp_up(struct ppp_fsm *fsm)
241 {
242 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
243 					       ipv6cp.fsm);
244 	struct net_nbr *nbr;
245 	struct in6_addr peer_addr;
246 	struct net_linkaddr peer_lladdr;
247 
248 	if (ctx->is_ipv6cp_up) {
249 		return;
250 	}
251 
252 	ppp_network_up(ctx, PPP_IPV6);
253 
254 	ctx->is_ipv6cp_up = true;
255 
256 	NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm,
257 		ppp_state_str(fsm->state), fsm->state);
258 
259 	add_iid_address(ctx->iface, ctx->ipv6cp.my_options.iid);
260 
261 	/* Add peer to neighbor table */
262 	setup_iid_address(ctx->ipv6cp.peer_options.iid, &peer_addr);
263 
264 	peer_lladdr.addr = ctx->ipv6cp.peer_options.iid;
265 	peer_lladdr.len = sizeof(ctx->ipv6cp.peer_options.iid);
266 
267 	/* TODO: What should be the type? */
268 	peer_lladdr.type = NET_LINK_DUMMY;
269 
270 	nbr = net_ipv6_nbr_add(ctx->iface, &peer_addr, &peer_lladdr,
271 			       false, NET_IPV6_NBR_STATE_STATIC);
272 	if (!nbr) {
273 		NET_ERR("[%s/%p] Cannot add peer %s to nbr table",
274 			fsm->name, fsm,
275 			net_sprint_addr(AF_INET6, (const void *)&peer_addr));
276 	} else {
277 		if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
278 			uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
279 			char dst[INET6_ADDRSTRLEN];
280 			char *addr_str;
281 
282 			net_sprint_ll_addr_buf(peer_lladdr.addr,
283 					       peer_lladdr.len,
284 					       iid_str, sizeof(iid_str));
285 
286 			addr_str = net_addr_ntop(AF_INET6, &peer_addr, dst,
287 						 sizeof(dst));
288 
289 			NET_DBG("[%s/%p] Peer %s [%s] %s nbr cache",
290 				fsm->name, fsm, addr_str,
291 				iid_str, "added to");
292 		}
293 	}
294 }
295 
ipv6cp_down(struct ppp_fsm * fsm)296 static void ipv6cp_down(struct ppp_fsm *fsm)
297 {
298 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
299 					       ipv6cp.fsm);
300 	struct net_linkaddr peer_lladdr;
301 	struct in6_addr my_addr;
302 	struct in6_addr peer_addr;
303 	int ret;
304 
305 	if (!ctx->is_ipv6cp_up) {
306 		return;
307 	}
308 
309 	ctx->is_ipv6cp_up = false;
310 
311 	ppp_network_down(ctx, PPP_IPV6);
312 
313 	/* Remove my address */
314 	setup_iid_address(ctx->ipv6cp.my_options.iid, &my_addr);
315 	net_if_ipv6_addr_rm(ctx->iface, &my_addr);
316 
317 	/* Remove peer from neighbor table */
318 	setup_iid_address(ctx->ipv6cp.peer_options.iid, &peer_addr);
319 
320 	peer_lladdr.addr = ctx->ipv6cp.peer_options.iid;
321 	peer_lladdr.len = sizeof(ctx->ipv6cp.peer_options.iid);
322 
323 	/* TODO: What should be the type? */
324 	peer_lladdr.type = NET_LINK_DUMMY;
325 
326 	ret = net_ipv6_nbr_rm(ctx->iface, &peer_addr);
327 	if (!ret) {
328 		NET_ERR("[%s/%p] Cannot rm peer %s from nbr table",
329 			fsm->name, fsm,
330 			net_sprint_addr(AF_INET6, (const void *)&peer_addr));
331 	} else {
332 		if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
333 			uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
334 			char dst[INET6_ADDRSTRLEN];
335 			char *addr_str;
336 
337 			net_sprint_ll_addr_buf(ctx->ipv6cp.peer_options.iid,
338 					sizeof(ctx->ipv6cp.peer_options.iid),
339 					iid_str, sizeof(iid_str));
340 
341 			addr_str = net_addr_ntop(AF_INET6, &peer_addr, dst,
342 						 sizeof(dst));
343 
344 			NET_DBG("[%s/%p] Peer %s [%s] %s nbr cache",
345 				fsm->name, fsm, addr_str,
346 				iid_str, "removed from");
347 		}
348 	}
349 }
350 
ipv6cp_finished(struct ppp_fsm * fsm)351 static void ipv6cp_finished(struct ppp_fsm *fsm)
352 {
353 	struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
354 					       ipv6cp.fsm);
355 
356 	if (!ctx->is_ipv6cp_open) {
357 		return;
358 	}
359 
360 	ctx->is_ipv6cp_open = false;
361 
362 	ppp_network_done(ctx, PPP_IPV6);
363 }
364 
ipv6cp_proto_reject(struct ppp_fsm * fsm)365 static void ipv6cp_proto_reject(struct ppp_fsm *fsm)
366 {
367 	ppp_fsm_lower_down(fsm);
368 }
369 
ipv6cp_init(struct ppp_context * ctx)370 static void ipv6cp_init(struct ppp_context *ctx)
371 {
372 	NET_DBG("proto %s (0x%04x) fsm %p", ppp_proto2str(PPP_IPV6CP),
373 		PPP_IPV6CP, &ctx->ipv6cp.fsm);
374 
375 	memset(&ctx->ipv6cp.fsm, 0, sizeof(ctx->ipv6cp.fsm));
376 
377 	ppp_fsm_init(&ctx->ipv6cp.fsm, PPP_IPV6CP);
378 
379 	ppp_fsm_name_set(&ctx->ipv6cp.fsm, ppp_proto2str(PPP_IPV6CP));
380 
381 	ctx->ipv6cp.fsm.my_options.info = ipv6cp_my_options;
382 	ctx->ipv6cp.fsm.my_options.data = ctx->ipv6cp.my_options_data;
383 	ctx->ipv6cp.fsm.my_options.count = ARRAY_SIZE(ipv6cp_my_options);
384 
385 	ctx->ipv6cp.fsm.cb.up = ipv6cp_up;
386 	ctx->ipv6cp.fsm.cb.down = ipv6cp_down;
387 	ctx->ipv6cp.fsm.cb.finished = ipv6cp_finished;
388 	ctx->ipv6cp.fsm.cb.proto_reject = ipv6cp_proto_reject;
389 	ctx->ipv6cp.fsm.cb.config_info_ack = ipv6cp_config_info_ack;
390 	ctx->ipv6cp.fsm.cb.config_info_rej = ppp_my_options_parse_conf_rej;
391 	ctx->ipv6cp.fsm.cb.config_info_add = ipv6cp_config_info_add;
392 	ctx->ipv6cp.fsm.cb.config_info_req = ipv6cp_config_info_req;
393 }
394 
395 PPP_PROTOCOL_REGISTER(IPV6CP, PPP_IPV6CP,
396 		      ipv6cp_init, ipv6cp_handle,
397 		      ipv6cp_lower_up, ipv6cp_lower_down,
398 		      ipv6cp_open, ipv6cp_close);
399