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