source: bootcd/isolinux/syslinux-6.03/gpxe/src/net/ipv6.c @ 26ffad7

Last change on this file since 26ffad7 was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 9.7 KB
Line 
1#include <errno.h>
2#include <stdint.h>
3#include <string.h>
4#include <stdlib.h>
5#include <stdio.h>
6#include <byteswap.h>
7#include <gpxe/in.h>
8#include <gpxe/ip6.h>
9#include <gpxe/ndp.h>
10#include <gpxe/list.h>
11#include <gpxe/icmp6.h>
12#include <gpxe/tcpip.h>
13#include <gpxe/socket.h>
14#include <gpxe/iobuf.h>
15#include <gpxe/netdevice.h>
16#include <gpxe/if_ether.h>
17
18struct net_protocol ipv6_protocol;
19
20/* Unspecified IP6 address */
21static struct in6_addr ip6_none = {
22        .in6_u.u6_addr32 = { 0,0,0,0 }
23};
24
25/** An IPv6 routing table entry */
26struct ipv6_miniroute {
27        /* List of miniroutes */
28        struct list_head list;
29
30        /* Network device */
31        struct net_device *netdev;
32
33        /* Destination prefix */
34        struct in6_addr prefix;
35        /* Prefix length */
36        int prefix_len;
37        /* IPv6 address of interface */
38        struct in6_addr address;
39        /* Gateway address */
40        struct in6_addr gateway;
41};
42
43/** List of IPv6 miniroutes */
44static LIST_HEAD ( miniroutes );
45
46/**
47 * Add IPv6 minirouting table entry
48 *
49 * @v netdev            Network device
50 * @v prefix            Destination prefix
51 * @v address           Address of the interface
52 * @v gateway           Gateway address (or ::0 for no gateway)
53 * @ret miniroute       Routing table entry, or NULL
54 */
55static struct ipv6_miniroute * __malloc
56add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
57                     int prefix_len, struct in6_addr address,
58                     struct in6_addr gateway ) {
59        struct ipv6_miniroute *miniroute;
60       
61        miniroute = malloc ( sizeof ( *miniroute ) );
62        if ( miniroute ) {
63                /* Record routing information */
64                miniroute->netdev = netdev_get ( netdev );
65                miniroute->prefix = prefix;
66                miniroute->prefix_len = prefix_len;
67                miniroute->address = address;
68                miniroute->gateway = gateway;
69               
70                /* Add miniroute to list of miniroutes */
71                if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
72                        list_add_tail ( &miniroute->list, &miniroutes );
73                } else {
74                        list_add ( &miniroute->list, &miniroutes );
75                }
76        }
77
78        return miniroute;
79}
80
81/**
82 * Delete IPv6 minirouting table entry
83 *
84 * @v miniroute         Routing table entry
85 */
86static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
87        netdev_put ( miniroute->netdev );
88        list_del ( &miniroute->list );
89        free ( miniroute );
90}
91
92/**
93 * Add IPv6 interface
94 *
95 * @v netdev    Network device
96 * @v prefix    Destination prefix
97 * @v address   Address of the interface
98 * @v gateway   Gateway address (or ::0 for no gateway)
99 */
100int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
101                       int prefix_len, struct in6_addr address,
102                       struct in6_addr gateway ) {
103        struct ipv6_miniroute *miniroute;
104
105        /* Clear any existing address for this net device */
106        del_ipv6_address ( netdev );
107
108        /* Add new miniroute */
109        miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
110                                         gateway );
111        if ( ! miniroute )
112                return -ENOMEM;
113
114        return 0;
115}
116
117/**
118 * Remove IPv6 interface
119 *
120 * @v netdev    Network device
121 */
122void del_ipv6_address ( struct net_device *netdev ) {
123        struct ipv6_miniroute *miniroute;
124
125        list_for_each_entry ( miniroute, &miniroutes, list ) {
126                if ( miniroute->netdev == netdev ) {
127                        del_ipv6_miniroute ( miniroute );
128                        break;
129                }
130        }
131}
132
133/**
134 * Calculate TCPIP checksum
135 *
136 * @v iobuf     I/O buffer
137 * @v tcpip     TCP/IP protocol
138 *
139 * This function constructs the pseudo header and completes the checksum in the
140 * upper layer header.
141 */
142static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
143        struct ip6_header *ip6hdr = iobuf->data;
144        struct ipv6_pseudo_header pshdr;
145
146        /* Calculate pseudo header */
147        memset ( &pshdr, 0, sizeof ( pshdr ) );
148        pshdr.src = ip6hdr->src;
149        pshdr.dest = ip6hdr->dest;
150        pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
151        pshdr.nxt_hdr = ip6hdr->nxt_hdr;
152
153        /* Update checksum value */
154        return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
155}
156
157/**
158 * Dump IP6 header for debugging
159 *
160 * ip6hdr       IPv6 header
161 */
162void ipv6_dump ( struct ip6_header *ip6hdr ) {
163        DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
164              inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
165              ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
166}
167
168/**
169 * Transmit IP6 packet
170 *
171 * iobuf                I/O buffer
172 * tcpip        TCP/IP protocol
173 * st_dest      Destination socket address
174 *
175 * This function prepends the IPv6 headers to the payload an transmits it.
176 */
177static int ipv6_tx ( struct io_buffer *iobuf,
178                     struct tcpip_protocol *tcpip,
179                     struct sockaddr_tcpip *st_src __unused,
180                     struct sockaddr_tcpip *st_dest,
181                     struct net_device *netdev,
182                     uint16_t *trans_csum ) {
183        struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
184        struct in6_addr next_hop;
185        struct ipv6_miniroute *miniroute;
186        uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
187        const uint8_t *ll_dest = ll_dest_buf;
188        int rc;
189
190        /* Construct the IPv6 packet */
191        struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
192        memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
193        ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
194        ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
195        ip6hdr->nxt_hdr = tcpip->tcpip_proto;
196        ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
197
198        /* Determine the next hop address and interface
199         *
200         * TODO: Implement the routing table.
201         */
202        next_hop = dest->sin6_addr;
203        list_for_each_entry ( miniroute, &miniroutes, list ) {
204                if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
205                                        miniroute->prefix_len ) == 0 ) ||
206                     ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
207                        netdev = miniroute->netdev;
208                        ip6hdr->src = miniroute->address;
209                        if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
210                                next_hop = miniroute->gateway;
211                        }
212                        break;
213                }
214        }
215        /* No network interface identified */
216        if ( !netdev ) {
217                DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
218                rc = -ENETUNREACH;
219                goto err;
220        }
221
222        /* Complete the transport layer checksum */
223        if ( trans_csum )
224                *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
225
226        /* Print IPv6 header */
227        ipv6_dump ( ip6hdr );
228       
229        /* Resolve link layer address */
230        if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
231                ll_dest_buf[0] = 0x33;
232                ll_dest_buf[1] = 0x33;
233                ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
234                ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
235                ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
236                ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
237        } else {
238                /* Unicast address needs to be resolved by NDP */
239                if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
240                                          ll_dest_buf ) ) != 0 ) {
241                        DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
242                        goto err;
243                }
244        }
245
246        /* Transmit packet */
247        return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
248
249  err:
250        free_iob ( iobuf );
251        return rc;
252}
253
254/**
255 * Process next IP6 header
256 *
257 * @v iobuf     I/O buffer
258 * @v nxt_hdr   Next header number
259 * @v src       Source socket address
260 * @v dest      Destination socket address
261 *
262 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
263 */
264static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
265                struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
266        switch ( nxt_hdr ) {
267        case IP6_HOPBYHOP:
268        case IP6_ROUTING:
269        case IP6_FRAGMENT:
270        case IP6_AUTHENTICATION:
271        case IP6_DEST_OPTS:
272        case IP6_ESP:
273                DBG ( "Function not implemented for header %d\n", nxt_hdr );
274                return -ENOSYS;
275        case IP6_ICMP6:
276                break;
277        case IP6_NO_HEADER:
278                DBG ( "No next header\n" );
279                return 0;
280        }
281        /* Next header is not a IPv6 extension header */
282        return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
283}
284
285/**
286 * Process incoming IP6 packets
287 *
288 * @v iobuf             I/O buffer
289 * @v netdev            Network device
290 * @v ll_source         Link-layer source address
291 *
292 * This function processes a IPv6 packet
293 */
294static int ipv6_rx ( struct io_buffer *iobuf,
295                     __unused struct net_device *netdev,
296                     __unused const void *ll_source ) {
297
298        struct ip6_header *ip6hdr = iobuf->data;
299        union {
300                struct sockaddr_in6 sin6;
301                struct sockaddr_tcpip st;
302        } src, dest;
303
304        /* Sanity check */
305        if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
306                DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
307                goto drop;
308        }
309
310        /* TODO: Verify checksum */
311
312        /* Print IP6 header for debugging */
313        ipv6_dump ( ip6hdr );
314
315        /* Check header version */
316        if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
317                DBG ( "Invalid protocol version\n" );
318                goto drop;
319        }
320
321        /* Check the payload length */
322        if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
323                DBG ( "Inconsistent packet length (%d bytes)\n",
324                        ip6hdr->payload_len );
325                goto drop;
326        }
327
328        /* Ignore the traffic class and flow control values */
329
330        /* Construct socket address */
331        memset ( &src, 0, sizeof ( src ) );
332        src.sin6.sin_family = AF_INET6;
333        src.sin6.sin6_addr = ip6hdr->src;
334        memset ( &dest, 0, sizeof ( dest ) );
335        dest.sin6.sin_family = AF_INET6;
336        dest.sin6.sin6_addr = ip6hdr->dest;
337
338        /* Strip header */
339        iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
340                                                        sizeof ( *ip6hdr ) );
341        iob_pull ( iobuf, sizeof ( *ip6hdr ) );
342
343        /* Send it to the transport layer */
344        return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
345
346  drop:
347        DBG ( "Packet dropped\n" );
348        free_iob ( iobuf );
349        return -1;
350}
351
352/**
353 * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
354 */
355char * inet6_ntoa ( struct in6_addr in6 ) {
356        static char buf[40];
357        uint16_t *bytes = ( uint16_t* ) &in6;
358        sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
359                        bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
360        return buf;
361}
362
363static const char * ipv6_ntoa ( const void *net_addr ) {
364        return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
365}
366
367/** IPv6 protocol */
368struct net_protocol ipv6_protocol __net_protocol = {
369        .name = "IPv6",
370        .net_proto = htons ( ETH_P_IPV6 ),
371        .net_addr_len = sizeof ( struct in6_addr ),
372        .rx = ipv6_rx,
373        .ntoa = ipv6_ntoa,
374};
375
376/** IPv6 TCPIP net protocol */
377struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
378        .name = "IPv6",
379        .sa_family = AF_INET6,
380        .tx = ipv6_tx,
381};
Note: See TracBrowser for help on using the repository browser.