1 /******************************************************************************
2  * Copyright (c) 2013 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12 
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <sys/socket.h>
18 #include "ethernet.h"
19 #include "ipv6.h"
20 #include "icmpv6.h"
21 #include "ndp.h"
22 #include "dhcpv6.h"
23 
24 static int ra_received = 0;
25 
26 /**
27  * NET:
28  * @param  fd           socket fd
29  */
30 void
send_router_solicitation(int fd)31 send_router_solicitation (int fd)
32 {
33 	ip6_addr_t dest_addr;
34 	uint8_t *ether_packet;
35 	struct packeth headers;
36 
37 	ether_packet = malloc(ETH_MTU_SIZE);
38 	if (!ether_packet) {
39 		fprintf(stderr, "send_router_solicitation: Out of memory\n");
40 		return;
41 	}
42 
43 	headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
44 	headers.icmp6h = (struct icmp6hdr *) (ether_packet +
45 			  sizeof(struct ethhdr) +
46 			  sizeof(struct ip6hdr));
47 
48 	/* Destination is "All routers multicast address" (link-local) */
49 	dest_addr.part.prefix       = 0xff02000000000000ULL;
50 	dest_addr.part.interface_id = 2;
51 
52 	/* Fill IPv6 header */
53 	fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
54 		     ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation),
55 		     0x3a, //ICMPV6
56 		     get_ipv6_address(), &dest_addr);
57 
58 	/* Fill ICMPv6 message */
59 	headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION;
60 	headers.icmp6h->code = 0;
61 	headers.icmp6h->icmp6body.router_solicit.lladdr.type    = 1;
62 	headers.icmp6h->icmp6body.router_solicit.lladdr.length  = 1;
63 	memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac),
64 		get_mac_address(), 6);
65 
66 	send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) +
67 		   ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation));
68 
69 	free(ether_packet);
70 }
71 
72 /**
73  * NET: Process prefix option in Router Advertisements
74  *
75  * @param  ip6_packet	pointer to an IPv6 packet
76  */
77 static void
handle_prefixoption(uint8_t * option)78 handle_prefixoption (uint8_t *option)
79 {
80 	ip6_addr_t prefix;
81 	struct ip6addr_list_entry *new_address;
82 	struct option_prefix *prefix_option;
83 	struct prefix_info *prfx_info;
84 
85 	prefix_option = (struct option_prefix *) option;
86 	memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH);
87 
88 	/* Link-local adresses in RAs are nonsense */
89 	if (ip6_is_linklocal(&prefix))
90 		return;
91 
92 	if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime)
93 		return;
94 
95 	/* Add address created from prefix to IPv6 address list */
96 	new_address = ip6_prefix2addr (prefix);
97 	if (!new_address)
98 		return;
99 
100 	/* Process only prefixes we don't already have an adress from */
101 	if (!unknown_prefix (&new_address->addr)) {
102 		return;
103 	}
104 
105 	/* Fill struct prefix_info from data in RA and store it in new_address */
106 	prfx_info = ip6_create_prefix_info();
107 	if (!prfx_info)
108 		return;
109 	memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct prefix_info));
110 
111 	/* Add prefix received in RA to list of known prefixes */
112 	ip6addr_add (new_address);
113 }
114 
115 /**
116  * NET: Process source link layer addresses in Router Advertisements
117  *
118  * @param  ip6_packet	pointer to an IPv6 packet
119  */
120 static void
handle_source_lladdr(struct option_ll_address * option,struct router * rtr)121 handle_source_lladdr ( struct option_ll_address *option, struct router *rtr)
122 {
123 	memcpy (&(rtr->mac), &(option->mac), 6);
124 }
125 
126 /**
127  * NET: Process ICMPv6 options in Router Advertisements
128  *
129  * @param  ip6_packet	pointer to an IPv6 packet
130  */
131 static void
process_ra_options(uint8_t * option,int32_t option_length,struct router * r)132 process_ra_options (uint8_t *option, int32_t option_length, struct router *r)
133 {
134 	while (option_length > 0) {
135 		switch (*option) {
136 			case ND_OPTION_SOURCE_LL_ADDR:
137 				handle_source_lladdr ((struct option_ll_address *) option, r);
138 				break;
139 			case ND_OPTION_PREFIX_INFO:
140 				handle_prefixoption(option);
141 				break;
142 			default:
143 				break;
144 		}
145 		//option+1 is the length field. length is in units of 8 bytes
146 		option_length = option_length - (*(option+1) * 8);
147 		option = option + (*(option+1) * 8);
148 	}
149 
150 	return;
151 }
152 
153 /**
154  * NET: Process Router Advertisements
155  *
156  * @param  ip6_packet	pointer to an IPv6 packet
157  */
158 static void
handle_ra(struct icmp6hdr * icmp6h,uint8_t * ip6_packet)159 handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet)
160 {
161 	uint8_t  *first_option;
162 	int32_t option_length;
163 	struct ip6hdr *ip6h;
164 	struct router_advertisement *ra;
165 	struct router *rtr;
166 	uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0};
167 
168 	ip6h = (struct ip6hdr *) ip6_packet;
169 	ra = (struct router_advertisement *) &icmp6h->icmp6body.ra;
170 
171 	rtr = find_router(ip6h->src);
172 	if (!rtr) {
173 		rtr = router_create (rtr_mac, ip6h->src);
174 		router_add (rtr);
175 	}
176 
177 	/* store info from router advertisement in router struct */
178 	rtr->lifetime = ra->router_lifetime;
179 	rtr->reachable_time = ra->reachable_time;
180 	rtr->retrans_timer = ra->retrans_timer;
181 
182 	/* save flags concerning address (auto-) configuration */
183 	ip6_state.managed_mode = ra->flags.managed;
184 	ip6_state.other_config = ra->flags.other;
185 
186 	/* Process ICMPv6 options in Router Advertisement */
187 	first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12;
188 	option_length =  (uint8_t *) icmp6h + ip6h->pl - first_option;
189 	process_ra_options( (uint8_t *) first_option, option_length, rtr);
190 
191 	ra_received = 1;
192 }
193 
is_ra_received(void)194 int is_ra_received(void)
195 {
196 	return ra_received;
197 }
198 
199 /**
200  * NET:
201  *
202  * @param  fd         socket fd
203  * @param  ip6_addr_t *dest_ip6
204  */
205 void
send_neighbour_solicitation(int fd,ip6_addr_t * dest_ip6)206 send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6)
207 {
208 	ip6_addr_t snma;
209 	uint8_t *ether_packet;
210 	struct  packeth headers;
211 
212 	ether_packet = malloc(ETH_MTU_SIZE);
213 	if (!ether_packet) {
214 		fprintf(stderr, "send_neighbour_solicitation: Out of memory\n");
215 		return;
216 	}
217 
218 	memset(ether_packet, 0, ETH_MTU_SIZE);
219 	headers.ethh   = (struct ethhdr *) ether_packet;
220 	headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
221 	headers.icmp6h = (struct icmp6hdr *) (ether_packet +
222 			  sizeof(struct ethhdr) +
223 			  sizeof(struct ip6hdr));
224 
225 	/* Fill IPv6 header */
226 	snma.part.prefix       = IPV6_SOLIC_NODE_PREFIX;
227 	snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID;
228 	snma.addr[13]          = dest_ip6->addr[13];
229 	snma.addr[14]          = dest_ip6->addr[14];
230 	snma.addr[15]          = dest_ip6->addr[15];
231 	fill_ip6hdr((uint8_t *) headers.ip6h,
232 		    ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation),
233 		    0x3a, //ICMPv6
234 		    get_ipv6_address(), &snma);
235 
236 	/* Fill ICMPv6 message */
237 	headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION;
238 	headers.icmp6h->code = 0;
239 	memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target),
240 		dest_ip6, IPV6_ADDR_LENGTH );
241 	headers.icmp6h->icmp6body.nghb_solicit.lladdr.type    = 1;
242 	headers.icmp6h->icmp6body.nghb_solicit.lladdr.length  = 1;
243 	memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac),
244 		get_mac_address(), 6);
245 
246 	send_ip (fd, ether_packet + sizeof(struct ethhdr),
247 		   sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
248 		   sizeof(struct neighbour_solicitation));
249 
250 	free(ether_packet);
251 }
252 
253 /**
254  * NET:
255  *
256  * @param  fd           socket fd
257  * @param  ip6_packet	pointer to an IPv6 packet
258  * @param  icmp6hdr	pointer to the icmp6 header in ip6_packet
259  * @param  na_flags	Neighbour advertisment flags
260  */
261 static void
send_neighbour_advertisement(int fd,struct neighbor * target)262 send_neighbour_advertisement (int fd, struct neighbor *target)
263 {
264 	struct na_flags na_adv_flags;
265 	uint8_t *ether_packet;
266 	struct  packeth headers;
267 
268 	ether_packet = malloc(ETH_MTU_SIZE);
269 	if (!ether_packet) {
270 		fprintf(stderr, "send_neighbour_advertisement: Out of memory\n");
271 		return;
272 	}
273 
274 	headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
275 	headers.icmp6h = (struct icmp6hdr *) (ether_packet +
276 			  sizeof(struct ethhdr) +
277 			  sizeof(struct ip6hdr));
278 
279 	/* Fill IPv6 header */
280 	fill_ip6hdr(ether_packet + sizeof(struct ethhdr),
281 		    ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement),
282 		    0x3a, //ICMPv6
283 		    get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr));
284 
285 	/* Fill ICMPv6 message */
286 	memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
287 		&(target->ip.addr), IPV6_ADDR_LENGTH );
288 	headers.icmp6h->icmp6body.nghb_adv.lladdr.type    = 1;
289 	headers.icmp6h->icmp6body.nghb_adv.lladdr.length  = 1;
290 	memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
291 		get_mac_address(), 6);
292 
293 	na_adv_flags.is_router = 0;
294 	na_adv_flags.na_is_solicited = 1;
295 	na_adv_flags.override = 1;
296 
297 	headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
298 	headers.icmp6h->code = 0;
299 	headers.icmp6h->icmp6body.nghb_adv.router    = na_adv_flags.is_router;
300 
301 	headers.icmp6h->icmp6body.nghb_adv.solicited = na_adv_flags.na_is_solicited;
302 	headers.icmp6h->icmp6body.nghb_adv.override  = na_adv_flags.override;
303 	headers.icmp6h->icmp6body.nghb_adv.lladdr.type	    = 2;
304 	headers.icmp6h->icmp6body.nghb_adv.lladdr.length    = 1;
305 
306 	memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0,
307 		IPV6_ADDR_LENGTH );
308 
309 	if( na_adv_flags.na_is_solicited ) {
310 		memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
311 			get_ipv6_address(), IPV6_ADDR_LENGTH);
312 	}
313 
314 	memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
315 		get_mac_address(), 6);
316 
317 	send_ip (fd, ether_packet + sizeof(struct ethhdr),
318 		   sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
319 		   sizeof(struct neighbour_advertisement));
320 
321 	free(ether_packet);
322 }
323 
324 /**
325  * NET:
326  *
327  * @param  fd           socket fd
328  * @param  ip6_packet	pointer to an IPv6 packet
329  */
330 static int8_t
handle_na(int fd,uint8_t * packet)331 handle_na (int fd, uint8_t *packet)
332 {
333 	struct neighbor *n = NULL;
334 	struct packeth headers;
335 	ip6_addr_t ip;
336 
337 	headers.ethh = (struct ethhdr *) packet;
338 	headers.ip6h = (struct ip6hdr *) (packet + sizeof(struct ethhdr));
339 	headers.icmp6h = (struct icmp6hdr *) (packet +
340 					      sizeof(struct ethhdr) +
341 					      sizeof(struct ip6hdr));
342 
343 	memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH);
344 
345 	n = find_neighbor(ip);
346 
347 	if (!n) {
348 		n= (struct neighbor *)
349 			neighbor_create( packet, &headers );
350 		if (!n)
351 			return 0;
352 		if (!neighbor_add(n))
353 			return 0;
354 	} else {
355 		memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6);
356 		n->status = NB_REACHABLE;
357 		if (n->eth_len > 0) {
358 			struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame);
359 			memcpy(ethh->dest_mac, &(n->mac), 6);
360 			send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr));
361 			n->eth_len = 0;
362 		}
363 	}
364 
365 	return 1;
366 }
367 
368 /**
369  * NET: Handles ICMPv6 messages
370  *
371  * @param  fd           socket fd
372  * @param  ip6_packet	pointer to an IPv6 packet
373  * @param  packetsize	size of ipv6_packet
374  */
375 int8_t
handle_icmpv6(int fd,struct ethhdr * etherhdr,uint8_t * ip6_packet)376 handle_icmpv6 (int fd, struct ethhdr *etherhdr,
377 	      uint8_t  *ip6_packet)
378 {
379 
380 	struct icmp6hdr *received_icmp6 = NULL;
381 	struct ip6hdr *received_ip6	= NULL;
382 	struct neighbor target;
383 
384 	received_ip6 =   (struct ip6hdr *) ip6_packet;
385 	received_icmp6 = (struct icmp6hdr *) (ip6_packet +
386 			  sizeof(struct ip6hdr));
387 	memcpy( &(target.ip.addr), &(received_ip6->src),
388 		IPV6_ADDR_LENGTH );
389 	memcpy( &(target.mac), etherhdr->src_mac, 6);
390 
391 	/* process ICMPv6 types */
392 	switch(received_icmp6->type) {
393 		case ICMPV6_NEIGHBOUR_SOLICITATION:
394 			send_neighbour_advertisement(fd, &target);
395 			break;
396 		case ICMPV6_NEIGHBOUR_ADVERTISEMENT:
397 			handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr));
398 			break;
399 		case ICMPV6_ROUTER_ADVERTISEMENT:
400 			handle_ra(received_icmp6, (uint8_t *) received_ip6);
401 			break;
402 		default:
403 			return -1;
404 	}
405 
406 	return 1;
407 }
408