Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / net / icmpv6.c
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 <string.h>
23 #include <errno.h>
24 #include <byteswap.h>
25 #include <ipxe/in.h>
26 #include <ipxe/iobuf.h>
27 #include <ipxe/tcpip.h>
28 #include <ipxe/ping.h>
29 #include <ipxe/icmpv6.h>
30
31 /** @file
32  *
33  * ICMPv6 protocol
34  *
35  */
36
37 struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol;
38
39 /**
40  * Process received ICMPv6 echo request packet
41  *
42  * @v iobuf             I/O buffer
43  * @v netdev            Network device
44  * @v sin6_src          Source socket address
45  * @v sin6_dest         Destination socket address
46  * @ret rc              Return status code
47  */
48 static int icmpv6_rx_echo_request ( struct io_buffer *iobuf,
49                                     struct net_device *netdev __unused,
50                                     struct sockaddr_in6 *sin6_src,
51                                     struct sockaddr_in6 *sin6_dest __unused ) {
52         struct sockaddr_tcpip *st_src =
53                 ( ( struct sockaddr_tcpip * ) sin6_src );
54
55         return icmp_rx_echo_request ( iobuf, st_src, &icmpv6_echo_protocol );
56 }
57
58 /** ICMPv6 echo request handler */
59 struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler = {
60         .type = ICMPV6_ECHO_REQUEST,
61         .rx = icmpv6_rx_echo_request,
62 };
63
64 /**
65  * Process received ICMPv6 echo reply packet
66  *
67  * @v iobuf             I/O buffer
68  * @v netdev            Network device
69  * @v sin6_src          Source socket address
70  * @v sin6_dest         Destination socket address
71  * @ret rc              Return status code
72  */
73 static int icmpv6_rx_echo_reply ( struct io_buffer *iobuf,
74                                   struct net_device *netdev __unused,
75                                   struct sockaddr_in6 *sin6_src,
76                                   struct sockaddr_in6 *sin6_dest __unused ) {
77         struct sockaddr_tcpip *st_src =
78                 ( ( struct sockaddr_tcpip * ) sin6_src );
79
80         return icmp_rx_echo_reply ( iobuf, st_src );
81 }
82
83 /** ICMPv6 echo reply handler */
84 struct icmpv6_handler icmpv6_echo_reply_handler __icmpv6_handler = {
85         .type = ICMPV6_ECHO_REPLY,
86         .rx = icmpv6_rx_echo_reply,
87 };
88
89 /**
90  * Identify ICMPv6 handler
91  *
92  * @v type              ICMPv6 type
93  * @ret handler         ICMPv6 handler, or NULL if not found
94  */
95 static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) {
96         struct icmpv6_handler *handler;
97
98         for_each_table_entry ( handler, ICMPV6_HANDLERS ) {
99                 if ( handler->type == type )
100                         return handler;
101         }
102         return NULL;
103 }
104
105 /**
106  * Process a received packet
107  *
108  * @v iobuf             I/O buffer
109  * @v netdev            Network device
110  * @v st_src            Partially-filled source address
111  * @v st_dest           Partially-filled destination address
112  * @v pshdr_csum        Pseudo-header checksum
113  * @ret rc              Return status code
114  */
115 static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
116                        struct sockaddr_tcpip *st_src,
117                        struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
118         struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
119         struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
120         struct icmp_header *icmp = iobuf->data;
121         size_t len = iob_len ( iobuf );
122         struct icmpv6_handler *handler;
123         unsigned int csum;
124         int rc;
125
126         /* Sanity check */
127         if ( len < sizeof ( *icmp ) ) {
128                 DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd "
129                        "bytes)\n", len, sizeof ( *icmp ) );
130                 rc = -EINVAL;
131                 goto done;
132         }
133
134         /* Verify checksum */
135         csum = tcpip_continue_chksum ( pshdr_csum, icmp, len );
136         if ( csum != 0 ) {
137                 DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be "
138                        "0000)\n", csum );
139                 DBGC_HDA ( netdev, 0, icmp, len );
140                 rc = -EINVAL;
141                 goto done;
142         }
143
144         /* Identify handler */
145         handler = icmpv6_handler ( icmp->type );
146         if ( ! handler ) {
147                 DBGC ( netdev, "ICMPv6 unrecognised type %d\n", icmp->type );
148                 rc = -ENOTSUP;
149                 goto done;
150         }
151
152         /* Pass to handler */
153         if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src,
154                                   sin6_dest ) ) != 0 ) {
155                 DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n",
156                        icmp->type, strerror ( rc ) );
157                 goto done;
158         }
159
160  done:
161         free_iob ( iobuf );
162         return rc;
163 }
164
165 /** ICMPv6 TCP/IP protocol */
166 struct tcpip_protocol icmpv6_protocol __tcpip_protocol = {
167         .name = "ICMPv6",
168         .rx = icmpv6_rx,
169         .tcpip_proto = IP_ICMP6,
170 };
171
172 /** ICMPv6 echo protocol */
173 struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol = {
174         .family = AF_INET6,
175         .request = ICMPV6_ECHO_REQUEST,
176         .reply = ICMPV6_ECHO_REPLY,
177         .tcpip_protocol = &icmpv6_protocol,
178         .net_checksum = 1,
179 };