1 #include <stdint.h> 2 #include <string.h> 3 #include <byteswap.h> 4 #include <errno.h> 5 #include <gpxe/in.h> 6 #include <gpxe/ip6.h> 7 #include <gpxe/if_ether.h> 8 #include <gpxe/iobuf.h> 9 #include <gpxe/ndp.h> 10 #include <gpxe/icmp6.h> 11 #include <gpxe/tcpip.h> 12 #include <gpxe/netdevice.h> 13 14 struct tcpip_protocol icmp6_protocol; 15 16 /** 17 * Send neighbour solicitation packet 18 * 19 * @v netdev Network device 20 * @v src Source address 21 * @v dest Destination address 22 * 23 * This function prepares a neighbour solicitation packet and sends it to the 24 * network layer. 25 */ 26 int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused, 27 struct in6_addr *dest ) { 28 union { 29 struct sockaddr_in6 sin6; 30 struct sockaddr_tcpip st; 31 } st_dest; 32 struct ll_protocol *ll_protocol = netdev->ll_protocol; 33 struct neighbour_solicit *nsolicit; 34 struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN ); 35 iob_reserve ( iobuf, MAX_HDR_LEN ); 36 nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) ); 37 38 /* Fill up the headers */ 39 memset ( nsolicit, 0, sizeof ( *nsolicit ) ); 40 nsolicit->type = ICMP6_NSOLICIT; 41 nsolicit->code = 0; 42 nsolicit->target = *dest; 43 nsolicit->opt_type = 1; 44 nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8; 45 memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr, 46 netdev->ll_protocol->ll_addr_len ); 47 /* Partial checksum */ 48 nsolicit->csum = 0; 49 nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) ); 50 51 /* Solicited multicast address */ 52 st_dest.sin6.sin_family = AF_INET6; 53 st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; 54 st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02; 55 st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000; 56 st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000; 57 st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000; 58 st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001; 59 st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3]; 60 st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff; 61 62 /* Send packet over IP6 */ 63 return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st, 64 NULL, &nsolicit->csum ); 65 } 66 67 /** 68 * Process ICMP6 headers 69 * 70 * @v iobuf I/O buffer 71 * @v st_src Source address 72 * @v st_dest Destination address 73 */ 74 static int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, 75 struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) { 76 struct icmp6_header *icmp6hdr = iobuf->data; 77 78 /* Sanity check */ 79 if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) { 80 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) ); 81 free_iob ( iobuf ); 82 return -EINVAL; 83 } 84 85 /* TODO: Verify checksum */ 86 87 /* Process the ICMP header */ 88 switch ( icmp6hdr->type ) { 89 case ICMP6_NADVERT: 90 return ndp_process_advert ( iobuf, st_src, st_dest ); 91 } 92 return -ENOSYS; 93 } 94 95 #if 0 96 void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) { 97 98 struct sockaddr_in6 server; 99 memcpy ( &server, server_p, sizeof ( server ) ); 100 struct io_buffer *rxiobuf = alloc_iob ( 500 ); 101 iob_reserve ( rxiobuf, MAX_HDR_LEN ); 102 struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) ); 103 nadvert->type = 136; 104 nadvert->code = 0; 105 nadvert->flags = ICMP6_FLAGS_SOLICITED; 106 nadvert->csum = 0xffff; 107 nadvert->target = server.sin6_addr; 108 nadvert->opt_type = 2; 109 nadvert->opt_len = 1; 110 memcpy ( nadvert->opt_ll_addr, ll_addr, 6 ); 111 struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) ); 112 ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 ); 113 ip6hdr->hop_limit = 255; 114 ip6hdr->nxt_hdr = 58; 115 ip6hdr->payload_len = htons ( sizeof ( *nadvert ) ); 116 ip6hdr->src = server.sin6_addr; 117 ip6hdr->dest = server.sin6_addr; 118 hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) ); 119 net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr ); 120 } 121 #endif 122 123 /** ICMP6 protocol */ 124 struct tcpip_protocol icmp6_protocol __tcpip_protocol = { 125 .name = "ICMP6", 126 .rx = icmp6_rx, 127 .tcpip_proto = IP_ICMP6, // 58 128 }; 129