7 #include <gpxe/if_ether.h>
8 #include <gpxe/iobuf.h>
10 #include <gpxe/icmp6.h>
11 #include <gpxe/tcpip.h>
12 #include <gpxe/netdevice.h>
14 #include <gpxe/ethernet.h>
16 struct tcpip_protocol icmp6_protocol;
19 * Send neighbour solicitation packet
21 * @v netdev Network device
22 * @v src Source address
23 * @v dest Destination address
25 * This function prepares a neighbour solicitation packet and sends it to the
28 int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused,
29 struct in6_addr *dest ) {
31 struct sockaddr_in6 sin6;
32 struct sockaddr_tcpip st;
34 struct ll_protocol *ll_protocol = netdev->ll_protocol;
35 struct neighbour_solicit *nsolicit;
36 struct ll_option *llopt;
37 struct io_buffer *iobuf = alloc_iob ( sizeof ( struct ll_option ) +
38 sizeof ( *nsolicit ) + MIN_IOB_LEN );
39 iob_reserve ( iobuf, MAX_HDR_LEN );
40 nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) );
41 llopt = iob_put ( iobuf, sizeof ( *llopt ) );
43 /* Fill up the headers */
44 memset ( nsolicit, 0, sizeof ( *nsolicit ) );
45 nsolicit->type = ICMP6_NSOLICIT;
47 nsolicit->target = *dest;
49 /* Fill in the link-layer address. FIXME: ll_option assumes 6 bytes. */
51 llopt->length = ( 2 + ll_protocol->ll_addr_len ) / 8;
52 memcpy ( llopt->address, netdev->ll_addr, netdev->ll_protocol->ll_addr_len );
54 /* Partial checksum */
56 nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) + sizeof ( *llopt ) );
58 /* Solicited multicast address - FF02::1 (all stations on local network) */
59 memset(&st_dest.sin6, 0, sizeof(st_dest.sin6));
60 st_dest.sin6.sin_family = AF_INET6;
61 st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
62 st_dest.sin6.sin6_addr.in6_u.u6_addr8[1] = 0x2;
63 st_dest.sin6.sin6_addr.in6_u.u6_addr8[15] = 0x1;
65 /* Send packet over IP6 */
66 return ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
67 netdev, &nsolicit->csum );
71 * Send neighbour advertisement packet
73 * @v netdev Network device
74 * @v src Source address
75 * @v dest Destination address
77 * This function prepares a neighbour advertisement packet and sends it to the
80 int icmp6_send_advert ( struct net_device *netdev, struct in6_addr *src,
81 struct in6_addr *dest ) {
83 struct sockaddr_in6 sin6;
84 struct sockaddr_tcpip st;
86 struct ll_protocol *ll_protocol = netdev->ll_protocol;
87 struct neighbour_advert *nadvert;
88 struct ll_option *llopt;
89 struct io_buffer *iobuf = alloc_iob ( sizeof ( struct ll_option ) +
90 sizeof ( *nadvert ) + MIN_IOB_LEN );
91 iob_reserve ( iobuf, MAX_HDR_LEN );
92 nadvert = iob_put ( iobuf, sizeof ( *nadvert ) );
93 llopt = iob_put ( iobuf, sizeof ( *llopt ) );
95 /* Fill up the headers */
96 memset ( nadvert, 0, sizeof ( *nadvert ) );
97 nadvert->type = ICMP6_NADVERT;
99 nadvert->target = *src;
100 nadvert->flags = ICMP6_FLAGS_SOLICITED | ICMP6_FLAGS_OVERRIDE;
102 /* Fill in the link-layer address. FIXME: ll_option assumes 6 bytes. */
104 llopt->length = ( 2 + ll_protocol->ll_addr_len ) / 8;
105 memcpy ( llopt->address, netdev->ll_addr, netdev->ll_protocol->ll_addr_len );
107 /* Partial checksum */
109 nadvert->csum = tcpip_chksum ( nadvert, sizeof ( *nadvert ) + sizeof ( *llopt ) );
111 /* Target network address. */
112 st_dest.sin6.sin_family = AF_INET6;
113 st_dest.sin6.sin6_addr = *dest;
115 /* Send packet over IP6 */
116 return ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
117 NULL, &nadvert->csum );
121 * Process ICMP6 Echo Request
123 * @v iobuf I/O buffer containing the original ICMPv6 packet.
124 * @v st_src Address of the source station.
125 * @v st_dest Address of the destination station.
127 int icmp6_handle_echo ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
128 struct sockaddr_tcpip *st_dest,
129 struct icmp6_net_protocol *net_protocol __unused ) {
130 struct icmp6_header *icmp6hdr = iobuf->data;
131 size_t len = iob_len ( iobuf );
134 /* Change type to response and recalculate checksum */
135 icmp6hdr->type = ICMP6_ECHO_RESPONSE;
137 icmp6hdr->csum = tcpip_chksum ( icmp6hdr, len );
139 /* Transmit the response */
140 if ( ( rc = ipv6_tx ( iob_disown ( iobuf ), &icmp6_protocol, st_dest,
141 st_src, NULL, &icmp6hdr->csum ) ) != 0 ) {
142 DBG ( "ICMP could not transmit ping response: %s\n",
151 * Identify ICMP6 network layer protocol
153 * @v net_proto Network-layer protocol, in network-endian order
154 * @ret arp_net_protocol ARP protocol, or NULL
157 static struct icmp6_net_protocol * icmp6_find_protocol ( uint16_t net_proto ) {
158 struct icmp6_net_protocol *icmp6_net_protocol;
160 for_each_table_entry ( icmp6_net_protocol, ICMP6_NET_PROTOCOLS ) {
161 if ( icmp6_net_protocol->net_protocol->net_proto == net_proto ) {
162 return icmp6_net_protocol;
169 * Process ICMP6 headers
171 * @v iobuf I/O buffer
172 * @v st_src Source address
173 * @v st_dest Destination address
175 int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
176 struct sockaddr_tcpip *st_dest, struct net_device *netdev,
177 uint16_t pshdr_csum ) {
178 struct icmp6_header *icmp6hdr = iobuf->data;
179 struct icmp6_net_protocol *icmp6_net_protocol;
180 size_t len = iob_len ( iobuf );
185 if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) {
186 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
191 /* Verify checksum */
192 csum = tcpip_continue_chksum ( pshdr_csum, icmp6hdr, len );
194 DBG ( "ICMPv6 checksum incorrect (is %04x, should be 0000)\n",
196 DBG_HD ( icmp6hdr, len );
201 /* Get the net protocol for this packet. */
202 icmp6_net_protocol = icmp6_find_protocol ( htons ( ETH_P_IPV6 ) );
203 if ( ! icmp6_net_protocol ) {
208 DBG ( "ICMPv6: packet with type %d and code %x\n", icmp6hdr->type, icmp6hdr->code);
210 /* Process the ICMP header */
211 switch ( icmp6hdr->type ) {
212 case ICMP6_ROUTER_ADVERT:
213 return ndp_process_radvert ( iobuf, st_src, st_dest, netdev, icmp6_net_protocol );
215 return ndp_process_nsolicit ( iobuf, st_src, st_dest, netdev, icmp6_net_protocol );
217 return ndp_process_nadvert ( iobuf, st_src, st_dest, icmp6_net_protocol );
218 case ICMP6_ECHO_REQUEST:
219 return icmp6_handle_echo ( iobuf, st_src, st_dest, icmp6_net_protocol );
230 void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
232 struct sockaddr_in6 server;
233 memcpy ( &server, server_p, sizeof ( server ) );
234 struct io_buffer *rxiobuf = alloc_iob ( 500 );
235 iob_reserve ( rxiobuf, MAX_HDR_LEN );
236 struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) );
239 nadvert->flags = ICMP6_FLAGS_SOLICITED;
240 nadvert->csum = 0xffff;
241 nadvert->target = server.sin6_addr;
242 nadvert->opt_type = 2;
243 nadvert->opt_len = 1;
244 memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
245 struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) );
246 ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
247 ip6hdr->hop_limit = 255;
248 ip6hdr->nxt_hdr = 58;
249 ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
250 ip6hdr->src = server.sin6_addr;
251 ip6hdr->dest = server.sin6_addr;
252 hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) );
253 net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr );
257 /** ICMP6 protocol (needed for ipv6_tx) */
258 struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
260 .rx = NULL, /* icmp6_rx if tcpip passes netdev in future */
261 .tcpip_proto = IP_ICMP6, // 58