Browse Source

[ipv6] Replace IPv6 stack

Replace the existing partially-implemented IPv6 stack with a fresh
implementation.

This implementation is not yet complete.  The IPv6 transmit and
receive datapaths are functional (including fragment reassembly and
parsing of arbitrary extension headers).  NDP neighbour solicitations
and advertisements are supported.  ICMPv6 echo is supported.

At present, only link-local addresses may be used, and there is no way
to specify an IPv6 address as part of a URI (either directly or via
a DNS lookup).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
f7f3087cc5

+ 3
- 0
src/config/config.c View File

101
 #ifdef NET_PROTO_IPV4
101
 #ifdef NET_PROTO_IPV4
102
 REQUIRE_OBJECT ( ipv4 );
102
 REQUIRE_OBJECT ( ipv4 );
103
 #endif
103
 #endif
104
+#ifdef NET_PROTO_IPV6
105
+REQUIRE_OBJECT ( ipv6 );
106
+#endif
104
 
107
 
105
 /*
108
 /*
106
  * Drag in all requested PXE support
109
  * Drag in all requested PXE support

+ 3
- 0
src/config/config_route.c View File

22
 #ifdef NET_PROTO_IPV4
22
 #ifdef NET_PROTO_IPV4
23
 REQUIRE_OBJECT ( route_ipv4 );
23
 REQUIRE_OBJECT ( route_ipv4 );
24
 #endif
24
 #endif
25
+#ifdef NET_PROTO_IPV6
26
+REQUIRE_OBJECT ( route_ipv6 );
27
+#endif

+ 1
- 0
src/config/general.h View File

40
  */
40
  */
41
 
41
 
42
 #define	NET_PROTO_IPV4		/* IPv4 protocol */
42
 #define	NET_PROTO_IPV4		/* IPv4 protocol */
43
+#undef	NET_PROTO_IPV6		/* IPv6 protocol */
43
 #undef	NET_PROTO_FCOE		/* Fibre Channel over Ethernet protocol */
44
 #undef	NET_PROTO_FCOE		/* Fibre Channel over Ethernet protocol */
44
 
45
 
45
 /*
46
 /*

+ 0
- 59
src/include/ipxe/icmp6.h View File

1
-#ifndef _IPXE_ICMP6_H
2
-#define _IPXE_ICMP6_H
3
-
4
-/** @file
5
- *
6
- * ICMP6 protocol
7
- *
8
- */
9
-
10
-FILE_LICENCE ( GPL2_OR_LATER );
11
-
12
-#include <ipxe/ip6.h>
13
-#include <ipxe/ndp.h>
14
-
15
-#define ICMP6_NSOLICIT 135
16
-#define ICMP6_NADVERT 136
17
-
18
-extern struct tcpip_protocol icmp6_protocol __tcpip_protocol;
19
-
20
-struct icmp6_header {
21
-	uint8_t type;
22
-	uint8_t code;
23
-	uint16_t csum;
24
-	/* Message body */
25
-};
26
-
27
-struct neighbour_solicit {
28
-	uint8_t type;
29
-	uint8_t code;
30
-	uint16_t csum;
31
-	uint32_t reserved;
32
-	struct in6_addr target;
33
-	/* "Compulsory" options */
34
-	uint8_t opt_type;
35
-	uint8_t opt_len;
36
-  /* FIXME:  hack alert */
37
-	uint8_t opt_ll_addr[6];
38
-};
39
-
40
-struct neighbour_advert {
41
-	uint8_t type;
42
-	uint8_t code;
43
-	uint16_t csum;
44
-	uint8_t flags;
45
-	uint8_t reserved;
46
-	struct in6_addr target;
47
-	uint8_t opt_type;
48
-	uint8_t opt_len;
49
-  /* FIXME:  hack alert */
50
-	uint8_t opt_ll_addr[6];
51
-};
52
-
53
-#define ICMP6_FLAGS_ROUTER 0x80
54
-#define ICMP6_FLAGS_SOLICITED 0x40
55
-#define ICMP6_FLAGS_OVERRIDE 0x20
56
-
57
-int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
58
-
59
-#endif /* _IPXE_ICMP6_H */

+ 78
- 0
src/include/ipxe/icmpv6.h View File

1
+#ifndef _IPXE_ICMP6_H
2
+#define _IPXE_ICMP6_H
3
+
4
+/** @file
5
+ *
6
+ * ICMPv6 protocol
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/tables.h>
14
+#include <ipxe/iobuf.h>
15
+#include <ipxe/netdevice.h>
16
+
17
+/** An ICMPv6 header */
18
+struct icmpv6_header {
19
+	/** Type */
20
+	uint8_t type;
21
+	/** Code */
22
+	uint8_t code;
23
+	/** Checksum */
24
+	uint16_t chksum;
25
+} __attribute__ (( packed ));
26
+
27
+/** An ICMPv6 echo request/reply */
28
+struct icmpv6_echo {
29
+	/** ICMPv6 header */
30
+	struct icmpv6_header icmp;
31
+	/** Identifier */
32
+	uint16_t ident;
33
+	/** Sequence number */
34
+	uint16_t sequence;
35
+	/** Data */
36
+	uint8_t data[0];
37
+} __attribute__ (( packed ));
38
+
39
+/** An ICMPv6 handler */
40
+struct icmpv6_handler {
41
+	/** Type */
42
+	unsigned int type;
43
+	/** Process received packet
44
+	 *
45
+	 * @v iobuf		I/O buffer
46
+	 * @v netdev		Network device
47
+	 * @v sin6_src		Source socket address
48
+	 * @v sin6_dest		Destination socket address
49
+	 * @ret rc		Return status code
50
+	 *
51
+	 * This function takes ownership of the I/O buffer.
52
+	 */
53
+	int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev,
54
+		       struct sockaddr_in6 *sin6_src,
55
+		       struct sockaddr_in6 *sin6_dest );
56
+};
57
+
58
+/** ICMPv6 handler table */
59
+#define ICMPV6_HANDLERS __table ( struct icmpv6_handler, "icmpv6_handlers" )
60
+
61
+/** Declare an ICMPv6 handler */
62
+#define __icmpv6_handler __table_entry ( ICMPV6_HANDLERS, 01 )
63
+
64
+/** ICMPv6 echo request */
65
+#define ICMPV6_ECHO_REQUEST 128
66
+
67
+/** ICMPv6 echo reply */
68
+#define ICMPV6_ECHO_REPLY 129
69
+
70
+/** ICMPv6 neighbour solicitation */
71
+#define ICMPV6_NDP_NEIGHBOUR_SOLICITATION 135
72
+
73
+/** ICMPv6 neighbour advertisement */
74
+#define ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT 136
75
+
76
+extern struct tcpip_protocol icmpv6_protocol __tcpip_protocol;
77
+
78
+#endif /* _IPXE_ICMP6_H */

+ 17
- 14
src/include/ipxe/in.h View File

50
 #define s6_addr32       in6_u.u6_addr32
50
 #define s6_addr32       in6_u.u6_addr32
51
 };
51
 };
52
 
52
 
53
+#define IN6_IS_ADDR_MULTICAST( addr )					\
54
+	( *( ( const uint8_t * ) (addr) ) == 0xff )
55
+
56
+#define IN6_IS_ADDR_LINKLOCAL( addr )					\
57
+	( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) ==	\
58
+	  htonl ( 0xfe80 ) )
59
+
53
 /**
60
 /**
54
  * IPv4 socket address
61
  * IPv4 socket address
55
  */
62
  */
90
 	uint16_t sin6_flags;
97
 	uint16_t sin6_flags;
91
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
98
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
92
 	uint16_t sin6_port;
99
 	uint16_t sin6_port;
93
-        uint32_t        sin6_flowinfo;  /* Flow number */
94
-        struct in6_addr sin6_addr;      /* 128-bit destination address */
95
-        uint32_t        sin6_scope_id;  /* Scope ID */
100
+	/** Scope ID
101
+	 *
102
+	 * For link-local addresses, this is the network device index.
103
+	 */
104
+        uint16_t sin6_scope_id;
105
+	/** IPv6 address */
106
+        struct in6_addr sin6_addr;
96
 	/** Padding
107
 	/** Padding
97
 	 *
108
 	 *
98
 	 * This ensures that a struct @c sockaddr_in6 is large
109
 	 * This ensures that a struct @c sockaddr_in6 is large
103
 		  ( sizeof ( sa_family_t ) /* sin6_family */ +
114
 		  ( sizeof ( sa_family_t ) /* sin6_family */ +
104
 		    sizeof ( uint16_t ) /* sin6_flags */ +
115
 		    sizeof ( uint16_t ) /* sin6_flags */ +
105
 		    sizeof ( uint16_t ) /* sin6_port */ +
116
 		    sizeof ( uint16_t ) /* sin6_port */ +
106
-		    sizeof ( uint32_t ) /* sin6_flowinfo */ +
107
-		    sizeof ( struct in6_addr ) /* sin6_addr */ +
108
-		    sizeof ( uint32_t ) /* sin6_scope_id */ ) ];
117
+		    sizeof ( uint16_t ) /* sin6_scope_id */ +
118
+		    sizeof ( struct in6_addr ) /* sin6_addr */ ) ];
109
 } __attribute__ (( may_alias ));
119
 } __attribute__ (( may_alias ));
110
 
120
 
111
 extern int inet_aton ( const char *cp, struct in_addr *inp );
121
 extern int inet_aton ( const char *cp, struct in_addr *inp );
112
 extern char * inet_ntoa ( struct in_addr in );
122
 extern char * inet_ntoa ( struct in_addr in );
113
-
114
-/* Adding the following for IP6 support
115
- *
116
-
117
-extern int inet6_aton ( const char *cp, struct in6_addr *inp );
118
-extern char * inet6_ntoa ( struct in_addr in );
119
-
120
- */
123
+extern char * inet6_ntoa ( const struct in6_addr *in6 );
121
 
124
 
122
 #endif	/* _IPXE_IN_H */
125
 #endif	/* _IPXE_IN_H */

+ 0
- 80
src/include/ipxe/ip6.h View File

1
-#ifndef _IPXE_IP6_H
2
-#define _IPXE_IP6_H
3
-
4
-/** @file
5
- *
6
- * IP6 protocol
7
- *
8
- */
9
-
10
-FILE_LICENCE ( GPL2_OR_LATER );
11
-
12
-#include <stdint.h>
13
-#include <ipxe/in.h>
14
-#include <ipxe/netdevice.h>
15
-#include <ipxe/tcpip.h>
16
-
17
-/* IP6 constants */
18
-
19
-#define IP6_VERSION	0x6
20
-#define IP6_HOP_LIMIT	255
21
-
22
-/**
23
- * I/O buffer contents
24
- * This is duplicated in tcp.h and here. Ideally it should go into iobuf.h
25
- */
26
-#define MAX_HDR_LEN	100
27
-#define MAX_IOB_LEN	1500
28
-#define MIN_IOB_LEN	MAX_HDR_LEN + 100 /* To account for padding by LL */
29
-
30
-#define IP6_EQUAL( in6_addr1, in6_addr2 ) \
31
-        ( memcmp ( ( char* ) &( in6_addr1 ), ( char* ) &( in6_addr2 ),\
32
-	sizeof ( struct in6_addr ) ) == 0 )
33
-
34
-#define IS_UNSPECIFIED( addr ) \
35
-	( ( (addr).in6_u.u6_addr32[0] == 0x00000000 ) && \
36
-	( (addr).in6_u.u6_addr32[1] == 0x00000000 ) && \
37
-	( (addr).in6_u.u6_addr32[2] == 0x00000000 ) && \
38
-	( (addr).in6_u.u6_addr32[3] == 0x00000000 ) )
39
-/* IP6 header */
40
-struct ip6_header {
41
-	uint32_t 	ver_traffic_class_flow_label;
42
-	uint16_t 	payload_len;
43
-	uint8_t 	nxt_hdr;
44
-	uint8_t 	hop_limit;
45
-	struct in6_addr src;
46
-	struct in6_addr dest;
47
-};
48
-
49
-/* IP6 pseudo header */
50
-struct ipv6_pseudo_header {
51
-	struct in6_addr src;
52
-	struct in6_addr dest;
53
-	uint8_t zero_padding;
54
-	uint8_t nxt_hdr;
55
-	uint16_t len;
56
-};
57
-
58
-/* Next header numbers */
59
-#define IP6_HOPBYHOP 		0x00
60
-#define IP6_ROUTING 		0x43
61
-#define IP6_FRAGMENT		0x44
62
-#define IP6_AUTHENTICATION	0x51
63
-#define IP6_DEST_OPTS		0x60
64
-#define IP6_ESP			0x50
65
-#define IP6_ICMP6		0x58
66
-#define IP6_NO_HEADER		0x59
67
-
68
-struct io_buffer;
69
-
70
-extern struct net_protocol ipv6_protocol __net_protocol;
71
-extern struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol;
72
-extern char * inet6_ntoa ( struct in6_addr in6 );
73
-
74
-extern int add_ipv6_address ( struct net_device *netdev,
75
-			      struct in6_addr prefix, int prefix_len,
76
-			      struct in6_addr address,
77
-			      struct in6_addr gateway );
78
-extern void del_ipv6_address ( struct net_device *netdev );
79
-
80
-#endif /* _IPXE_IP6_H */

+ 218
- 0
src/include/ipxe/ipv6.h View File

1
+#ifndef _IPXE_IPV6_H
2
+#define _IPXE_IPV6_H
3
+
4
+/** @file
5
+ *
6
+ * IPv6 protocol
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <string.h>
14
+#include <byteswap.h>
15
+#include <ipxe/in.h>
16
+#include <ipxe/list.h>
17
+#include <ipxe/netdevice.h>
18
+
19
+/** IPv6 version */
20
+#define IPV6_VER 0x60000000UL
21
+
22
+/** IPv6 version mask */
23
+#define IPV6_MASK_VER 0xf0000000UL
24
+
25
+/** IPv6 maximum hop limit */
26
+#define IPV6_HOP_LIMIT 0xff
27
+
28
+/** IPv6 header */
29
+struct ipv6_header {
30
+	/** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */
31
+	uint32_t ver_tc_label;
32
+	/** Payload length, including any extension headers */
33
+	uint16_t len;
34
+	/** Next header type */
35
+	uint8_t next_header;
36
+	/** Hop limit */
37
+	uint8_t hop_limit;
38
+	/** Source address */
39
+	struct in6_addr src;
40
+	/** Destination address */
41
+	struct in6_addr dest;
42
+} __attribute__ (( packed ));
43
+
44
+/** IPv6 extension header common fields */
45
+struct ipv6_extension_header_common {
46
+	/** Next header type */
47
+	uint8_t next_header;
48
+	/** Header extension length (excluding first 8 bytes) */
49
+	uint8_t len;
50
+} __attribute__ (( packed ));
51
+
52
+/** IPv6 type-length-value options */
53
+struct ipv6_option {
54
+	/** Type */
55
+	uint8_t type;
56
+	/** Length */
57
+	uint8_t len;
58
+	/** Value */
59
+	uint8_t value[0];
60
+} __attribute__ (( packed ));
61
+
62
+/** IPv6 option types */
63
+enum ipv6_option_type {
64
+	/** Pad1 */
65
+	IPV6_OPT_PAD1 = 0x00,
66
+	/** PadN */
67
+	IPV6_OPT_PADN = 0x01,
68
+};
69
+
70
+/** Test if IPv6 option can be safely ignored */
71
+#define IPV6_CAN_IGNORE_OPT( type ) ( ( (type) & 0xc0 ) == 0x00 )
72
+
73
+/** IPv6 option-based extension header */
74
+struct ipv6_options_header {
75
+	/** Extension header common fields */
76
+	struct ipv6_extension_header_common common;
77
+	/** Options */
78
+	struct ipv6_option options[0];
79
+} __attribute__ (( packed ));
80
+
81
+/** IPv6 routing header */
82
+struct ipv6_routing_header {
83
+	/** Extension header common fields */
84
+	struct ipv6_extension_header_common common;
85
+	/** Routing type */
86
+	uint8_t type;
87
+	/** Segments left */
88
+	uint8_t remaining;
89
+	/** Type-specific data */
90
+	uint8_t data[0];
91
+} __attribute__ (( packed ));
92
+
93
+/** IPv6 fragment header */
94
+struct ipv6_fragment_header {
95
+	/** Extension header common fields */
96
+	struct ipv6_extension_header_common common;
97
+	/** Fragment offset (13 bits), reserved, more fragments (1 bit) */
98
+	uint16_t offset_more;
99
+	/** Identification */
100
+	uint32_t ident;
101
+} __attribute__ (( packed ));
102
+
103
+/** Fragment offset mask */
104
+#define IPV6_MASK_OFFSET 0xfff8
105
+
106
+/** More fragments */
107
+#define IPV6_MASK_MOREFRAGS 0x0001
108
+
109
+/** IPv6 extension header */
110
+union ipv6_extension_header {
111
+	/** Extension header common fields */
112
+	struct ipv6_extension_header_common common;
113
+	/** Minimum size padding */
114
+	uint8_t pad[8];
115
+	/** Generic options header */
116
+	struct ipv6_options_header options;
117
+	/** Hop-by-hop options header */
118
+	struct ipv6_options_header hopbyhop;
119
+	/** Routing header */
120
+	struct ipv6_routing_header routing;
121
+	/** Fragment header */
122
+	struct ipv6_fragment_header fragment;
123
+	/** Destination options header */
124
+	struct ipv6_options_header destination;
125
+};
126
+
127
+/** IPv6 header types */
128
+enum ipv6_header_type {
129
+	/** IPv6 hop-by-hop options header type */
130
+	IPV6_HOPBYHOP = 0,
131
+	/** IPv6 routing header type */
132
+	IPV6_ROUTING = 43,
133
+	/** IPv6 fragment header type */
134
+	IPV6_FRAGMENT = 44,
135
+	/** IPv6 no next header type */
136
+	IPV6_NO_HEADER = 59,
137
+	/** IPv6 destination options header type */
138
+	IPV6_DESTINATION = 60,
139
+};
140
+
141
+/** IPv6 pseudo-header */
142
+struct ipv6_pseudo_header {
143
+	/** Source address */
144
+	struct in6_addr src;
145
+	/** Destination address */
146
+	struct in6_addr dest;
147
+	/** Upper-layer packet length */
148
+	uint32_t len;
149
+	/** Zero padding */
150
+	uint8_t zero[3];
151
+	/** Next header */
152
+	uint8_t next_header;
153
+} __attribute__ (( packed ));
154
+
155
+/** An IPv6 address/routing table entry */
156
+struct ipv6_miniroute {
157
+	/** List of miniroutes */
158
+	struct list_head list;
159
+
160
+	/** Network device */
161
+	struct net_device *netdev;
162
+
163
+	/** IPv6 address */
164
+	struct in6_addr address;
165
+	/** Prefix length */
166
+	unsigned int prefix_len;
167
+	/** IPv6 prefix mask (derived from prefix length) */
168
+	struct in6_addr prefix_mask;
169
+	/** Router address is present */
170
+	int has_router;
171
+	/** Router address */
172
+	struct in6_addr router;
173
+};
174
+
175
+/**
176
+ * Construct link-local address (via EUI-64)
177
+ *
178
+ * @v addr		Address to construct
179
+ * @v netdev		Network device
180
+ * @ret prefix_len	Prefix length, or negative error
181
+ */
182
+static inline int ipv6_link_local ( struct in6_addr *addr,
183
+				    struct net_device *netdev ) {
184
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
185
+	const void *ll_addr = netdev->ll_addr;
186
+	int rc;
187
+
188
+	memset ( addr, 0, sizeof ( *addr ) );
189
+	addr->s6_addr16[0] = htons ( 0xfe80 );
190
+	if ( ( rc = ll_protocol->eui64 ( ll_addr, &addr->s6_addr[8] ) ) != 0 )
191
+		return rc;
192
+	addr->s6_addr[8] ^= 0x02;
193
+	return 64;
194
+}
195
+
196
+/**
197
+ * Construct solicited-node multicast address
198
+ *
199
+ * @v addr		Address to construct
200
+ * @v unicast		Unicast address
201
+ */
202
+static inline void ipv6_solicited_node ( struct in6_addr *addr,
203
+					 const struct in6_addr *unicast ) {
204
+
205
+	memset ( addr, 0, sizeof ( *addr ) );
206
+	addr->s6_addr16[0] = htons ( 0xff02 );
207
+	addr->s6_addr[11] = 1;
208
+	addr->s6_addr[12] = 0xff;
209
+	memcpy ( &addr->s6_addr[13], &unicast->s6_addr[13], 3 );
210
+}
211
+
212
+extern struct list_head ipv6_miniroutes;
213
+
214
+extern struct net_protocol ipv6_protocol __net_protocol;
215
+
216
+extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
217
+
218
+#endif /* _IPXE_IPV6_H */

+ 78
- 19
src/include/ipxe/ndp.h View File

1
+#ifndef _IPXE_NDP_H
2
+#define _IPXE_NDP_H
3
+
4
+/** @file
5
+ *
6
+ * Neighbour discovery protocol
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
1
 #include <stdint.h>
12
 #include <stdint.h>
2
-#include <byteswap.h>
3
-#include <string.h>
4
-#include <ipxe/icmp6.h>
5
-#include <ipxe/ip6.h>
6
 #include <ipxe/in.h>
13
 #include <ipxe/in.h>
7
-#include <ipxe/netdevice.h>
8
-#include <ipxe/iobuf.h>
9
-#include <ipxe/tcpip.h>
10
-
11
-#define NDP_STATE_INVALID 0
12
-#define NDP_STATE_INCOMPLETE 1
13
-#define NDP_STATE_REACHABLE 2
14
-#define NDP_STATE_DELAY 3
15
-#define NDP_STATE_PROBE 4
16
-#define NDP_STATE_STALE 5
17
-
18
-int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
19
-		  struct in6_addr *dest, void *dest_ll_addr );
20
-int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
21
-			 struct sockaddr_tcpip *st_dest );
14
+#include <ipxe/ipv6.h>
15
+#include <ipxe/icmpv6.h>
16
+#include <ipxe/neighbour.h>
17
+
18
+/** An NDP option */
19
+struct ndp_option {
20
+	/** Type */
21
+	uint8_t type;
22
+	/** Length (in blocks of 8 bytes) */
23
+	uint8_t blocks;
24
+	/** Value */
25
+	uint8_t value[0];
26
+} __attribute__ (( packed ));
27
+
28
+/** NDP option block size */
29
+#define NDP_OPTION_BLKSZ 8
30
+
31
+/** An NDP header */
32
+struct ndp_header {
33
+	/** ICMPv6 header */
34
+	struct icmpv6_header icmp;
35
+	/** Flags */
36
+	uint8_t flags;
37
+	/** Reserved */
38
+	uint8_t reserved[3];
39
+	/** Target address */
40
+	struct in6_addr target;
41
+	/** Options */
42
+	struct ndp_option option[0];
43
+} __attribute__ (( packed ));
44
+
45
+/** NDP router flag */
46
+#define NDP_ROUTER 0x80
47
+
48
+/** NDP solicited flag */
49
+#define NDP_SOLICITED 0x40
50
+
51
+/** NDP override flag */
52
+#define NDP_OVERRIDE 0x20
53
+
54
+/** NDP source link-layer address option */
55
+#define NDP_OPT_LL_SOURCE 1
56
+
57
+/** NDP target link-layer address option */
58
+#define NDP_OPT_LL_TARGET 2
59
+
60
+extern struct neighbour_discovery ndp_discovery;
61
+
62
+/**
63
+ * Transmit packet, determining link-layer address via NDP
64
+ *
65
+ * @v iobuf		I/O buffer
66
+ * @v netdev		Network device
67
+ * @v net_dest		Destination network-layer address
68
+ * @v net_source	Source network-layer address
69
+ * @v ll_source		Source link-layer address
70
+ * @ret rc		Return status code
71
+ */
72
+static inline int ndp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
73
+			   const void *net_dest, const void *net_source,
74
+			   const void *ll_source ) {
75
+
76
+	return neighbour_tx ( iobuf, netdev, &ipv6_protocol, net_dest,
77
+			      &ndp_discovery, net_source, ll_source );
78
+}
79
+
80
+#endif /* _IPXE_NDP_H */

+ 147
- 101
src/net/icmpv6.c View File

1
-#include <stdint.h>
1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
2
 #include <string.h>
22
 #include <string.h>
3
-#include <byteswap.h>
4
 #include <errno.h>
23
 #include <errno.h>
24
+#include <byteswap.h>
5
 #include <ipxe/in.h>
25
 #include <ipxe/in.h>
6
-#include <ipxe/ip6.h>
7
-#include <ipxe/if_ether.h>
8
 #include <ipxe/iobuf.h>
26
 #include <ipxe/iobuf.h>
9
-#include <ipxe/ndp.h>
10
-#include <ipxe/icmp6.h>
11
 #include <ipxe/tcpip.h>
27
 #include <ipxe/tcpip.h>
12
-#include <ipxe/netdevice.h>
28
+#include <ipxe/icmpv6.h>
29
+
30
+/** @file
31
+ *
32
+ * ICMPv6 protocol
33
+ *
34
+ */
13
 
35
 
14
 /**
36
 /**
15
- * Send neighbour solicitation packet
37
+ * Process received ICMPv6 echo request packet
16
  *
38
  *
17
- * @v netdev	Network device
18
- * @v src	Source address
19
- * @v dest	Destination address
39
+ * @v iobuf		I/O buffer
40
+ * @v netdev		Network device
41
+ * @v sin6_src		Source socket address
42
+ * @v sin6_dest		Destination socket address
43
+ * @ret rc		Return status code
44
+ */
45
+static int icmpv6_rx_echo ( struct io_buffer *iobuf,
46
+			    struct net_device *netdev,
47
+			    struct sockaddr_in6 *sin6_src,
48
+			    struct sockaddr_in6 *sin6_dest __unused ) {
49
+	struct sockaddr_tcpip *st_src =
50
+		( ( struct sockaddr_tcpip * ) sin6_src );
51
+	struct icmpv6_echo *echo = iobuf->data;
52
+	size_t len = iob_len ( iobuf );
53
+	int rc;
54
+
55
+	/* Sanity check */
56
+	if ( iob_len ( iobuf ) < sizeof ( *echo ) ) {
57
+		DBGC ( netdev, "ICMPv6 echo request too short at %zd bytes "
58
+		       "(min %zd bytes)\n", iob_len ( iobuf ),
59
+		       sizeof ( *echo ) );
60
+		rc = -EINVAL;
61
+		goto done;
62
+	}
63
+	DBGC ( netdev, "ICMPv6 echo request from %s (id %#04x seq %#04x)\n",
64
+	       inet6_ntoa ( &sin6_dest->sin6_addr ), ntohs ( echo->ident ),
65
+	       ntohs ( echo->sequence ) );
66
+
67
+	/* Convert echo request to echo reply and recalculate checksum */
68
+	echo->icmp.type = ICMPV6_ECHO_REPLY;
69
+	echo->icmp.chksum = 0;
70
+	echo->icmp.chksum = tcpip_chksum ( echo, len );
71
+
72
+	/* Transmit echo reply */
73
+	if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmpv6_protocol, NULL,
74
+			       st_src, netdev, &echo->icmp.chksum ) ) != 0 ) {
75
+		DBGC ( netdev, "ICMPv6 could not transmit reply: %s\n",
76
+		       strerror ( rc ) );
77
+		goto done;
78
+	}
79
+
80
+ done:
81
+	free_iob ( iobuf );
82
+	return rc;
83
+}
84
+
85
+/** ICMPv6 echo request handlers */
86
+struct icmpv6_handler icmpv6_echo_handler __icmpv6_handler = {
87
+	.type = ICMPV6_ECHO_REQUEST,
88
+	.rx = icmpv6_rx_echo,
89
+};
90
+
91
+/**
92
+ * Identify ICMPv6 handler
20
  *
93
  *
21
- * This function prepares a neighbour solicitation packet and sends it to the
22
- * network layer.
94
+ * @v type		ICMPv6 type
95
+ * @ret handler		ICMPv6 handler, or NULL if not found
23
  */
96
  */
24
-int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused,
25
-			 struct in6_addr *dest ) {
26
-	union {
27
-		struct sockaddr_in6 sin6;
28
-		struct sockaddr_tcpip st;
29
-	} st_dest;
30
-	struct ll_protocol *ll_protocol = netdev->ll_protocol;
31
-	struct neighbour_solicit *nsolicit;
32
-	struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN );
33
-	iob_reserve ( iobuf, MAX_HDR_LEN );
34
-	nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) );
35
-
36
-	/* Fill up the headers */
37
-	memset ( nsolicit, 0, sizeof ( *nsolicit ) );
38
-	nsolicit->type = ICMP6_NSOLICIT;
39
-	nsolicit->code = 0;
40
-	nsolicit->target = *dest;
41
-	nsolicit->opt_type = 1;
42
-	nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8;
43
-	memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr,
44
-				netdev->ll_protocol->ll_addr_len );
45
-	/* Partial checksum */
46
-	nsolicit->csum = 0;
47
-	nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) );
48
-
49
-	/* Solicited multicast address */
50
-	st_dest.sin6.sin6_family = AF_INET6;
51
-	st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
52
-	st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02;
53
-	st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000;
54
-	st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000;
55
-	st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000;
56
-	st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001;
57
-	st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3];
58
-	st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
59
-	
60
-	/* Send packet over IP6 */
61
-	return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
62
-			  NULL, &nsolicit->csum );
97
+static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) {
98
+	struct icmpv6_handler *handler;
99
+
100
+	for_each_table_entry ( handler, ICMPV6_HANDLERS ) {
101
+		if ( handler->type == type )
102
+			return handler;
103
+	}
104
+	return NULL;
63
 }
105
 }
64
 
106
 
65
 /**
107
 /**
66
- * Process ICMP6 headers
108
+ * Process a received packet
67
  *
109
  *
68
- * @v iobuf	I/O buffer
69
- * @v st_src	Source address
70
- * @v st_dest	Destination address
110
+ * @v iobuf		I/O buffer
111
+ * @v netdev		Network device
112
+ * @v st_src		Partially-filled source address
113
+ * @v st_dest		Partially-filled destination address
114
+ * @v pshdr_csum	Pseudo-header checksum
115
+ * @ret rc		Return status code
71
  */
116
  */
72
-static int icmp6_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, struct sockaddr_tcpip *st_src,
73
-		      struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) {
74
-	struct icmp6_header *icmp6hdr = iobuf->data;
117
+static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
118
+		       struct sockaddr_tcpip *st_src,
119
+		       struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
120
+	struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
121
+	struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
122
+	struct icmpv6_header *icmp = iobuf->data;
123
+	size_t len = iob_len ( iobuf );
124
+	struct icmpv6_handler *handler;
125
+	unsigned int csum;
126
+	int rc;
75
 
127
 
76
 	/* Sanity check */
128
 	/* Sanity check */
77
-	if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) {
78
-		DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
79
-		free_iob ( iobuf );
80
-		return -EINVAL;
129
+	if ( len < sizeof ( *icmp ) ) {
130
+		DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd "
131
+		       "bytes)\n", len, sizeof ( *icmp ) );
132
+		rc = -EINVAL;
133
+		goto done;
81
 	}
134
 	}
82
 
135
 
83
-	/* TODO: Verify checksum */
136
+	/* Verify checksum */
137
+	csum = tcpip_continue_chksum ( pshdr_csum, icmp, len );
138
+	if ( csum != 0 ) {
139
+		DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be "
140
+		       "0000)\n", csum );
141
+		DBGC_HDA ( netdev, 0, icmp, len );
142
+		rc = -EINVAL;
143
+		goto done;
144
+	}
84
 
145
 
85
-	/* Process the ICMP header */
86
-	switch ( icmp6hdr->type ) {
87
-	case ICMP6_NADVERT:
88
-		return ndp_process_advert ( iobuf, st_src, st_dest );
146
+	/* Identify handler */
147
+	handler = icmpv6_handler ( icmp->type );
148
+	if ( ! handler ) {
149
+		DBGC ( netdev, "ICMPv6 unrecognised type %d\n", icmp->type );
150
+		rc = -ENOTSUP;
151
+		goto done;
152
+	}
153
+
154
+	/* Pass to handler */
155
+	if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src,
156
+				  sin6_dest ) ) != 0 ) {
157
+		DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n",
158
+		       icmp->type, strerror ( rc ) );
159
+		goto done;
89
 	}
160
 	}
90
-	return -ENOSYS;
91
-}
92
 
161
 
93
-#if 0
94
-void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
95
-
96
-		struct sockaddr_in6 server;
97
-		memcpy ( &server, server_p, sizeof ( server ) );
98
-                struct io_buffer *rxiobuf = alloc_iob ( 500 );
99
-                iob_reserve ( rxiobuf, MAX_HDR_LEN );
100
-                struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) );
101
-                nadvert->type = 136;
102
-                nadvert->code = 0;
103
-                nadvert->flags = ICMP6_FLAGS_SOLICITED;
104
-		nadvert->csum = 0xffff;
105
-		nadvert->target = server.sin6_addr;
106
-                nadvert->opt_type = 2;
107
-                nadvert->opt_len = 1;
108
-                memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
109
-                struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) );
110
-                ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
111
-		ip6hdr->hop_limit = 255;
112
-		ip6hdr->nxt_hdr = 58;
113
-		ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
114
-                ip6hdr->src = server.sin6_addr;
115
-                ip6hdr->dest = server.sin6_addr;
116
-		hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) );
117
-                net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr );
162
+ done:
163
+	free_iob ( iobuf );
164
+	return rc;
118
 }
165
 }
119
-#endif
120
 
166
 
121
-/** ICMP6 protocol */
122
-struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
123
-	.name = "ICMP6",
124
-	.rx = icmp6_rx,
125
-	.tcpip_proto = IP_ICMP6, // 58
167
+/** ICMPv6 TCP/IP protocol */
168
+struct tcpip_protocol icmpv6_protocol __tcpip_protocol = {
169
+	.name = "ICMPv6",
170
+	.rx = icmpv6_rx,
171
+	.tcpip_proto = IP_ICMP6,
126
 };
172
 };

+ 1
- 1
src/net/ipv4.c View File

290
 			DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
290
 			DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
291
 			       "multicast %s: %s\n",
291
 			       "multicast %s: %s\n",
292
 			       inet_ntoa ( next_hop ), strerror ( rc ) );
292
 			       inet_ntoa ( next_hop ), strerror ( rc ) );
293
-			return rc;
293
+			goto err;
294
 		}
294
 		}
295
 		ll_dest = ll_dest_buf;
295
 		ll_dest = ll_dest_buf;
296
 	} else {
296
 	} else {

+ 630
- 249
src/net/ipv6.c
File diff suppressed because it is too large
View File


+ 319
- 129
src/net/ndp.c View File

1
-#include <stdint.h>
1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
2
 #include <string.h>
22
 #include <string.h>
3
-#include <byteswap.h>
4
 #include <errno.h>
23
 #include <errno.h>
5
-#include <ipxe/if_ether.h>
24
+#include <byteswap.h>
25
+#include <ipxe/in.h>
6
 #include <ipxe/iobuf.h>
26
 #include <ipxe/iobuf.h>
27
+#include <ipxe/tcpip.h>
28
+#include <ipxe/ipv6.h>
29
+#include <ipxe/icmpv6.h>
30
+#include <ipxe/neighbour.h>
7
 #include <ipxe/ndp.h>
31
 #include <ipxe/ndp.h>
8
-#include <ipxe/icmp6.h>
9
-#include <ipxe/ip6.h>
10
-#include <ipxe/netdevice.h>
11
 
32
 
12
 /** @file
33
 /** @file
13
  *
34
  *
14
- * Neighbour Discovery Protocol
35
+ * IPv6 neighbour discovery protocol
15
  *
36
  *
16
- * This file implements address resolution as specified by the neighbour
17
- * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
18
- * family.
19
  */
37
  */
20
 
38
 
21
-/* A neighbour entry */
22
-struct ndp_entry {
23
-	/** Target IP6 address */
24
-	struct in6_addr in6;
25
-	/** Link layer protocol */
26
-	struct ll_protocol *ll_protocol;
27
-	/** Link-layer address */
28
-	uint8_t ll_addr[MAX_LL_ADDR_LEN];
29
-	/** State of the neighbour entry */
30
-	int state;
31
-};
32
-
33
-/** Number of entries in the neighbour cache table */
34
-#define NUM_NDP_ENTRIES 4
35
-
36
-/** The neighbour cache table */
37
-static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
38
-#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
39
-
40
-static unsigned int next_new_ndp_entry = 0;
41
-
42
 /**
39
 /**
43
- * Find entry in the neighbour cache
40
+ * Transmit NDP neighbour solicitation/advertisement packet
44
  *
41
  *
45
- * @v in6	IP6 address
42
+ * @v netdev		Network device
43
+ * @v sin6_src		Source socket address
44
+ * @v sin6_dest		Destination socket address
45
+ * @v target		Neighbour target address
46
+ * @v icmp_type		ICMPv6 type
47
+ * @v flags		NDP flags
48
+ * @v option_type	NDP option type
49
+ * @ret rc		Return status code
46
  */
50
  */
47
-static struct ndp_entry *
48
-ndp_find_entry ( struct in6_addr *in6 ) {
49
-	struct ndp_entry *ndp;
50
-
51
-	for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
52
-		if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) && 
53
-		     ( ndp->state != NDP_STATE_INVALID ) ) {
54
-			return ndp;
55
-		}
51
+static int ndp_tx_neighbour ( struct net_device *netdev,
52
+			      struct sockaddr_in6 *sin6_src,
53
+			      struct sockaddr_in6 *sin6_dest,
54
+			      const struct in6_addr *target,
55
+			      unsigned int icmp_type,
56
+			      unsigned int flags,
57
+			      unsigned int option_type ) {
58
+	struct sockaddr_tcpip *st_src =
59
+		( ( struct sockaddr_tcpip * ) sin6_src );
60
+	struct sockaddr_tcpip *st_dest =
61
+		( ( struct sockaddr_tcpip * ) sin6_dest );
62
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
63
+	struct io_buffer *iobuf;
64
+	struct ndp_header *ndp;
65
+	size_t option_len;
66
+	size_t len;
67
+	int rc;
68
+
69
+	/* Allocate and populate buffer */
70
+	option_len = ( ( sizeof ( ndp->option[0] ) + ll_protocol->ll_addr_len +
71
+			 NDP_OPTION_BLKSZ - 1 ) &
72
+		       ~( NDP_OPTION_BLKSZ - 1 ) );
73
+	len = ( sizeof ( *ndp ) + option_len );
74
+	iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len );
75
+	if ( ! iobuf )
76
+		return -ENOMEM;
77
+	iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
78
+	ndp = iob_put ( iobuf, len );
79
+	memset ( ndp, 0, len );
80
+	ndp->icmp.type = icmp_type;
81
+	ndp->flags = flags;
82
+	memcpy ( &ndp->target, target, sizeof ( ndp->target ) );
83
+	ndp->option[0].type = option_type;
84
+	ndp->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ );
85
+	memcpy ( ndp->option[0].value, netdev->ll_addr,
86
+		 ll_protocol->ll_addr_len );
87
+	ndp->icmp.chksum = tcpip_chksum ( ndp, len );
88
+
89
+	/* Transmit packet */
90
+	if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
91
+			       netdev, &ndp->icmp.chksum ) ) != 0 ) {
92
+		DBGC ( netdev, "NDP could not transmit packet: %s\n",
93
+		       strerror ( rc ) );
94
+		return rc;
56
 	}
95
 	}
57
-	return NULL;
96
+
97
+	return 0;
58
 }
98
 }
59
 
99
 
60
 /**
100
 /**
61
- * Add NDP entry
62
- * 
63
- * @v netdev	Network device
64
- * @v in6	IP6 address
65
- * @v ll_addr	Link-layer address
66
- * @v state	State of the entry - one of the NDP_STATE_XXX values
101
+ * Transmit NDP neighbour discovery request
102
+ *
103
+ * @v netdev		Network device
104
+ * @v net_protocol	Network-layer protocol
105
+ * @v net_dest		Destination network-layer address
106
+ * @v net_source	Source network-layer address
107
+ * @ret rc		Return status code
67
  */
108
  */
68
-static void 
69
-add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
70
-		void *ll_addr, int state ) {
71
-	struct ndp_entry *ndp;
72
-	ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
73
-
74
-	/* Fill up entry */
75
-	ndp->ll_protocol = netdev->ll_protocol;
76
-	memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
77
-	if ( ll_addr ) {
78
-		memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
79
-	} else {
80
-		memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
81
-	}
82
-	ndp->state = state;
83
-	DBG ( "New neighbour cache entry: IP6 %s => %s %s\n",
84
-	      inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
85
-	      netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
109
+static int ndp_tx_request ( struct net_device *netdev,
110
+			    struct net_protocol *net_protocol __unused,
111
+			    const void *net_dest, const void *net_source ) {
112
+	struct sockaddr_in6 sin6_src;
113
+	struct sockaddr_in6 sin6_dest;
114
+
115
+	/* Construct source address */
116
+	memset ( &sin6_src, 0, sizeof ( sin6_src ) );
117
+	sin6_src.sin6_family = AF_INET6;
118
+	memcpy ( &sin6_src.sin6_addr, net_source,
119
+		 sizeof ( sin6_src.sin6_addr ) );
120
+	sin6_src.sin6_scope_id = htons ( netdev->index );
121
+
122
+	/* Construct multicast destination address */
123
+	memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
124
+	sin6_dest.sin6_family = AF_INET6;
125
+	sin6_dest.sin6_scope_id = htons ( netdev->index );
126
+	ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
127
+
128
+	/* Transmit neighbour discovery packet */
129
+	return ndp_tx_neighbour ( netdev, &sin6_src, &sin6_dest, net_dest,
130
+				  ICMPV6_NDP_NEIGHBOUR_SOLICITATION, 0,
131
+				  NDP_OPT_LL_SOURCE );
86
 }
132
 }
87
 
133
 
134
+/** NDP neighbour discovery protocol */
135
+struct neighbour_discovery ndp_discovery = {
136
+	.name = "NDP",
137
+	.tx_request = ndp_tx_request,
138
+};
139
+
88
 /**
140
 /**
89
- * Resolve the link-layer address
141
+ * Process NDP neighbour solicitation source link-layer address option
90
  *
142
  *
91
  * @v netdev		Network device
143
  * @v netdev		Network device
92
- * @v dest		Destination address
93
- * @v src		Source address
94
- * @ret dest_ll_addr	Destination link-layer address or NULL
95
- * @ret rc		Status
96
- *
97
- * This function looks up the neighbour cache for an entry corresponding to the
98
- * destination address. If it finds a valid entry, it fills up dest_ll_addr and
99
- * returns 0. Otherwise it sends a neighbour solicitation to the solicited
100
- * multicast address.
144
+ * @v sin6_src		Source socket address
145
+ * @v ndp		NDP packet
146
+ * @v ll_addr		Source link-layer address
147
+ * @v ll_addr_len	Source link-layer address length
148
+ * @ret rc		Return status code
101
  */
149
  */
102
-int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
103
-		  struct in6_addr *src, void *dest_ll_addr ) {
150
+static int ndp_rx_neighbour_solicitation ( struct net_device *netdev,
151
+					   struct sockaddr_in6 *sin6_src,
152
+					   struct ndp_header *ndp __unused,
153
+					   const void *ll_addr,
154
+					   size_t ll_addr_len ) {
104
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
155
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
105
-	struct ndp_entry *ndp;
106
 	int rc;
156
 	int rc;
107
 
157
 
108
-	ndp = ndp_find_entry ( dest );
109
-	/* Check if the entry is valid */
110
-	if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
111
-		DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
112
-		      inet6_ntoa ( *dest ), ll_protocol->name,
113
-		      ll_protocol->ntoa ( ndp->ll_addr ) );
114
-		memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
158
+	/* Silently ignore neighbour solicitations for addresses we do
159
+	 * not own.
160
+	 */
161
+	if ( ! ipv6_has_addr ( netdev, &ndp->target ) )
115
 		return 0;
162
 		return 0;
116
-	}
117
 
163
 
118
-	/* Check if the entry was already created */
119
-	if ( ndp ) {
120
-		DBG ( "Awaiting neighbour advertisement\n" );
121
-		/* For test */
122
-//		ndp->state = NDP_STATE_REACHABLE;
123
-//		memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
124
-//		assert ( ndp->ll_protocol->ll_addr_len == 6 );
125
-//		icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
126
-//		assert ( ndp->state == NDP_STATE_REACHABLE );
127
-		/* Take it out till here */
128
-		return -ENOENT;
164
+	/* Sanity check */
165
+	if ( ll_addr_len < ll_protocol->ll_addr_len ) {
166
+		DBGC ( netdev, "NDP neighbour solicitation link-layer address "
167
+		       "too short at %zd bytes (min %d bytes)\n",
168
+		       ll_addr_len, ll_protocol->ll_addr_len );
169
+		return -EINVAL;
129
 	}
170
 	}
130
-	DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
131
 
171
 
132
-	/* Add entry in the neighbour cache */
133
-	add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
172
+	/* Create or update neighbour cache entry */
173
+	if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
174
+				       &sin6_src->sin6_addr,
175
+				       ll_addr ) ) != 0 ) {
176
+		DBGC ( netdev, "NDP could not define %s => %s: %s\n",
177
+		       inet6_ntoa ( &sin6_src->sin6_addr ),
178
+		       ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
179
+		return rc;
180
+	}
134
 
181
 
135
-	/* Send neighbour solicitation */
136
-	if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
182
+	/* Send neighbour advertisement */
183
+	if ( ( rc = ndp_tx_neighbour ( netdev, NULL, sin6_src, &ndp->target,
184
+				       ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
185
+				       ( NDP_SOLICITED | NDP_OVERRIDE ),
186
+				       NDP_OPT_LL_TARGET ) ) != 0 ) {
137
 		return rc;
187
 		return rc;
138
 	}
188
 	}
139
-	return -ENOENT;
189
+
190
+	return 0;
140
 }
191
 }
141
 
192
 
142
 /**
193
 /**
143
- * Process neighbour advertisement
194
+ * Process NDP neighbour advertisement target link-layer address option
144
  *
195
  *
145
- * @v iobuf	I/O buffer
146
- * @v st_src	Source address
147
- * @v st_dest	Destination address 
196
+ * @v netdev		Network device
197
+ * @v sin6_src		Source socket address
198
+ * @v ndp		NDP packet
199
+ * @v ll_addr		Target link-layer address
200
+ * @v ll_addr_len	Target link-layer address length
201
+ * @ret rc		Return status code
148
  */
202
  */
149
-int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused,
150
-			   struct sockaddr_tcpip *st_dest __unused ) {
151
-	struct neighbour_advert *nadvert = iobuf->data;
152
-	struct ndp_entry *ndp;
203
+static int
204
+ndp_rx_neighbour_advertisement ( struct net_device *netdev,
205
+				 struct sockaddr_in6 *sin6_src __unused,
206
+				 struct ndp_header *ndp, const void *ll_addr,
207
+				 size_t ll_addr_len ) {
208
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
209
+	int rc;
153
 
210
 
154
 	/* Sanity check */
211
 	/* Sanity check */
155
-	if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) {
156
-		DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
212
+	if ( ll_addr_len < ll_protocol->ll_addr_len ) {
213
+		DBGC ( netdev, "NDP neighbour advertisement link-layer address "
214
+		       "too short at %zd bytes (min %d bytes)\n",
215
+		       ll_addr_len, ll_protocol->ll_addr_len );
157
 		return -EINVAL;
216
 		return -EINVAL;
158
 	}
217
 	}
159
 
218
 
160
-	assert ( nadvert->code == 0 );
161
-	assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
162
-	assert ( nadvert->opt_type == 2 );
219
+	/* Update neighbour cache entry, if any */
220
+	if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &ndp->target,
221
+				       ll_addr ) ) != 0 ) {
222
+		DBGC ( netdev, "NDP could not update %s => %s: %s\n",
223
+		       inet6_ntoa ( &ndp->target ),
224
+		       ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
225
+		return rc;
226
+	}
163
 
227
 
164
-	/* Update the neighbour cache, if entry is present */
165
-	ndp = ndp_find_entry ( &nadvert->target );
166
-	if ( ndp ) {
228
+	return 0;
229
+}
167
 
230
 
168
-	assert ( nadvert->opt_len ==
169
-			( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
231
+/** An NDP option handler */
232
+struct ndp_option_handler {
233
+	/** ICMPv6 type */
234
+	uint8_t icmp_type;
235
+	/** Option type */
236
+	uint8_t option_type;
237
+	/**
238
+	 * Handle received option
239
+	 *
240
+	 * @v netdev		Network device
241
+	 * @v sin6_src		Source socket address
242
+	 * @v ndp		NDP packet
243
+	 * @v value		Option value
244
+	 * @v len		Option length
245
+	 * @ret rc		Return status code
246
+	 */
247
+	int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
248
+		       struct ndp_header *ndp, const void *value, size_t len );
249
+};
170
 
250
 
171
-		if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
172
-			memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
173
-				 ndp->ll_protocol->ll_addr_len );
174
-			ndp->state = NDP_STATE_REACHABLE;
175
-			return 0;
251
+/** NDP option handlers */
252
+static struct ndp_option_handler ndp_option_handlers[] = {
253
+	{
254
+		.icmp_type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION,
255
+		.option_type = NDP_OPT_LL_SOURCE,
256
+		.rx = ndp_rx_neighbour_solicitation,
257
+	},
258
+	{
259
+		.icmp_type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
260
+		.option_type = NDP_OPT_LL_TARGET,
261
+		.rx = ndp_rx_neighbour_advertisement,
262
+	},
263
+};
264
+
265
+/**
266
+ * Process received NDP option
267
+ *
268
+ * @v netdev		Network device
269
+ * @v sin6_src		Source socket address
270
+ * @v ndp		NDP packet
271
+ * @v type		Option type
272
+ * @v value		Option value
273
+ * @v len		Option length
274
+ * @ret rc		Return status code
275
+ */
276
+static int ndp_rx_option ( struct net_device *netdev,
277
+			   struct sockaddr_in6 *sin6_src,
278
+			   struct ndp_header *ndp, unsigned int type,
279
+			   const void *value, size_t len ) {
280
+	struct ndp_option_handler *handler;
281
+	unsigned int i;
282
+
283
+	/* Locate a suitable option handler, if any */
284
+	for ( i = 0 ; i < ( sizeof ( ndp_option_handlers ) /
285
+			    sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
286
+		handler = &ndp_option_handlers[i];
287
+		if ( ( handler->icmp_type == ndp->icmp.type ) &&
288
+		     ( handler->option_type == type ) ) {
289
+			return handler->rx ( netdev, sin6_src, ndp,
290
+					     value, len );
176
 		}
291
 		}
177
 	}
292
 	}
178
-	DBG ( "Unsolicited advertisement (dropping packet)\n" );
293
+
294
+	/* Silently ignore unknown options as per RFC 4861 */
179
 	return 0;
295
 	return 0;
180
 }
296
 }
297
+
298
+/**
299
+ * Process received NDP packet
300
+ *
301
+ * @v iobuf		I/O buffer
302
+ * @v netdev		Network device
303
+ * @v sin6_src		Source socket address
304
+ * @v sin6_dest		Destination socket address
305
+ * @ret rc		Return status code
306
+ */
307
+static int ndp_rx ( struct io_buffer *iobuf,
308
+		    struct net_device *netdev,
309
+		    struct sockaddr_in6 *sin6_src,
310
+		    struct sockaddr_in6 *sin6_dest __unused ) {
311
+	struct ndp_header *ndp = iobuf->data;
312
+	struct ndp_option *option;
313
+	size_t remaining;
314
+	size_t option_len;
315
+	size_t option_value_len;
316
+	int rc;
317
+
318
+	/* Sanity check */
319
+	if ( iob_len ( iobuf ) < sizeof ( *ndp ) ) {
320
+		DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
321
+		       "bytes)\n", iob_len ( iobuf ), sizeof ( *ndp ) );
322
+		rc = -EINVAL;
323
+		goto done;
324
+	}
325
+
326
+	/* Search for option */
327
+	option = ndp->option;
328
+	remaining = ( iob_len ( iobuf ) - offsetof ( typeof ( *ndp ), option ));
329
+	while ( remaining ) {
330
+
331
+		/* Sanity check */
332
+		if ( ( remaining < sizeof ( *option ) ) ||
333
+		     ( option->blocks == 0 ) ||
334
+		     ( remaining < ( option->blocks * NDP_OPTION_BLKSZ ) ) ) {
335
+			DBGC ( netdev, "NDP bad option length:\n" );
336
+			DBGC_HDA ( netdev, 0, option, remaining );
337
+			rc = -EINVAL;
338
+			goto done;
339
+		}
340
+		option_len = ( option->blocks * NDP_OPTION_BLKSZ );
341
+		option_value_len = ( option_len - sizeof ( *option ) );
342
+
343
+		/* Handle option */
344
+		if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp,
345
+					    option->type, option->value,
346
+					    option_value_len ) ) != 0 ) {
347
+			goto done;
348
+		}
349
+
350
+		/* Move to next option */
351
+		option = ( ( ( void * ) option ) + option_len );
352
+		remaining -= option_len;
353
+	}
354
+
355
+ done:
356
+	free_iob ( iobuf );
357
+	return rc;
358
+}
359
+
360
+/** NDP ICMPv6 handlers */
361
+struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
362
+	{
363
+		.type = ICMPV6_NDP_NEIGHBOUR_SOLICITATION,
364
+		.rx = ndp_rx,
365
+	},
366
+	{
367
+		.type = ICMPV6_NDP_NEIGHBOUR_ADVERTISEMENT,
368
+		.rx = ndp_rx,
369
+	},
370
+};

+ 115
- 0
src/tests/ipv6_test.c View File

1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * IPv6 tests
25
+ *
26
+ */
27
+
28
+/* Forcibly enable assertions */
29
+#undef NDEBUG
30
+
31
+#include <stdint.h>
32
+#include <string.h>
33
+#include <byteswap.h>
34
+#include <ipxe/ipv6.h>
35
+#include <ipxe/test.h>
36
+
37
+/** Define inline IPv6 address */
38
+#define IPV6(...) { __VA_ARGS__ }
39
+
40
+/**
41
+ * Report an inet6_ntoa() test result
42
+ *
43
+ * @v addr		IPv6 address
44
+ * @v text		Expected textual representation
45
+ */
46
+#define inet6_ntoa_ok( addr, text ) do {				\
47
+	static const struct in6_addr in = {				\
48
+		.s6_addr = addr,					\
49
+	};								\
50
+	static const char expected[] = text;				\
51
+	char *actual;							\
52
+									\
53
+	actual = inet6_ntoa ( &in );					\
54
+	DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) "	\
55
+	      "= %s\n", ntohs ( in.s6_addr16[0] ),			\
56
+	      ntohs ( in.s6_addr16[1] ), ntohs ( in.s6_addr16[2] ),	\
57
+	      ntohs ( in.s6_addr16[3] ), ntohs ( in.s6_addr16[4] ),	\
58
+	      ntohs ( in.s6_addr16[5] ), ntohs ( in.s6_addr16[6] ),	\
59
+	      ntohs ( in.s6_addr16[7] ), actual );			\
60
+	ok ( strcmp ( actual, expected ) == 0 );			\
61
+	} while ( 0 )
62
+
63
+/**
64
+ * Perform IPv6 self-tests
65
+ *
66
+ */
67
+static void ipv6_test_exec ( void ) {
68
+
69
+	/* inet6_ntoa() tests */
70
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
71
+			       0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ),
72
+			"2001:ba8:0:1d4::6950:5845" );
73
+	/* No zeros */
74
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
75
+			       0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
76
+			"2001:db8:1:1:1:1:1:1" );
77
+	/* Run of zeros */
78
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
79
+			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
80
+			"2001:db8::1" );
81
+	/* No "::" for single zero */
82
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01,
83
+			       0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
84
+			"2001:db8:0:1:1:1:1:1" );
85
+	/* Use "::" for longest run of zeros */
86
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
87
+			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
88
+			"2001:0:0:1::1" );
89
+	/* Use "::" for leftmost equal-length run of zeros */
90
+	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
91
+			       0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
92
+			"2001:db8::1:0:0:1" );
93
+	/* Trailing run of zeros */
94
+	inet6_ntoa_ok ( IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95
+			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
96
+			"fe80::" );
97
+	/* Leading run of zeros */
98
+	inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99
+			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
100
+			"::1" );
101
+	/* All zeros */
102
+	inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103
+			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
104
+			"::" );
105
+	/* Maximum length */
106
+	inet6_ntoa_ok ( IPV6 ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
107
+			       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ),
108
+			"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" );
109
+}
110
+
111
+/** IPv6 self-test */
112
+struct self_test ipv6_test __self_test = {
113
+	.name = "ipv6",
114
+	.exec = ipv6_test_exec,
115
+};

+ 1
- 0
src/tests/tests.c View File

36
 REQUIRE_OBJECT ( settings_test );
36
 REQUIRE_OBJECT ( settings_test );
37
 REQUIRE_OBJECT ( time_test );
37
 REQUIRE_OBJECT ( time_test );
38
 REQUIRE_OBJECT ( tcpip_test );
38
 REQUIRE_OBJECT ( tcpip_test );
39
+REQUIRE_OBJECT ( ipv6_test );
39
 REQUIRE_OBJECT ( crc32_test );
40
 REQUIRE_OBJECT ( crc32_test );
40
 REQUIRE_OBJECT ( md5_test );
41
 REQUIRE_OBJECT ( md5_test );
41
 REQUIRE_OBJECT ( sha1_test );
42
 REQUIRE_OBJECT ( sha1_test );

+ 58
- 0
src/usr/route_ipv6.c View File

1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdio.h>
23
+#include <ipxe/netdevice.h>
24
+#include <ipxe/ipv6.h>
25
+#include <usr/route.h>
26
+
27
+/** @file
28
+ *
29
+ * IPv6 routing management
30
+ *
31
+ */
32
+
33
+/**
34
+ * Print IPv6 routing table
35
+ *
36
+ * @v netdev		Network device
37
+ */
38
+static void route_ipv6_print ( struct net_device *netdev ) {
39
+	struct ipv6_miniroute *miniroute;
40
+
41
+	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
42
+		if ( miniroute->netdev != netdev )
43
+			continue;
44
+		printf ( "%s: %s/%d", netdev->name,
45
+			 inet6_ntoa ( &miniroute->address ),
46
+			 miniroute->prefix_len );
47
+		if ( miniroute->has_router )
48
+			printf ( " gw %s", inet6_ntoa ( &miniroute->router ) );
49
+		if ( ! netdev_is_open ( miniroute->netdev ) )
50
+			printf ( " (inaccessible)" );
51
+		printf ( "\n" );
52
+	}
53
+}
54
+
55
+/** IPv6 routing family */
56
+struct routing_family ipv6_routing_family __routing_family ( ROUTING_IPV6 ) = {
57
+	.print = route_ipv6_print,
58
+};

Loading…
Cancel
Save