Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / net / tcp / oncrpc.c
1 /*
2  * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
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 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <byteswap.h>
27 #include <ipxe/socket.h>
28 #include <ipxe/tcpip.h>
29 #include <ipxe/in.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/dhcp.h>
32 #include <ipxe/xfer.h>
33 #include <ipxe/open.h>
34 #include <ipxe/uri.h>
35 #include <ipxe/features.h>
36 #include <ipxe/oncrpc.h>
37 #include <ipxe/oncrpc_iob.h>
38 #include <ipxe/init.h>
39 #include <ipxe/settings.h>
40 #include <ipxe/version.h>
41
42 /** @file
43  *
44  * SUN ONC RPC protocol
45  *
46  */
47
48 /** Set most significant bit to 1. */
49 #define SET_LAST_FRAME( x ) ( (x) | 1 << 31 )
50 #define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) )
51
52 #define ONCRPC_CALL     0
53 #define ONCRPC_REPLY    1
54
55 /** AUTH NONE authentication flavor */
56 struct oncrpc_cred oncrpc_auth_none = {
57         .flavor = ONCRPC_AUTH_NONE,
58         .length = 0
59 };
60
61 const struct setting uid_setting __setting ( SETTING_AUTH, uid ) = {
62         .name        = "uid",
63         .description = "User ID",
64         .tag         = DHCP_EB_UID,
65         .type        = &setting_type_uint32
66 };
67
68 const struct setting gid_setting __setting ( SETTING_AUTH, gid ) = {
69         .name        = "gid",
70         .description = "Group ID",
71         .tag         = DHCP_EB_GID,
72         .type        = &setting_type_uint32
73 };
74
75 /**
76  * Initialize an ONC RPC AUTH SYS credential structure
77  *
78  * @v auth_sys          The structure to initialize
79  *
80  * The hostname field is filled with the value of the hostname setting, if the
81  * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used
82  * instead.
83  */
84 int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) {
85         if ( ! auth_sys )
86                 return -EINVAL;
87
88         fetch_string_setting_copy ( NULL, &hostname_setting,
89                                     &auth_sys->hostname );
90         if ( ! auth_sys->hostname )
91                 if ( ! ( auth_sys->hostname = strdup ( product_short_name ) ) )
92                         return -ENOMEM;
93
94         auth_sys->uid         = fetch_uintz_setting ( NULL, &uid_setting );
95         auth_sys->gid         = fetch_uintz_setting ( NULL, &uid_setting );
96         auth_sys->aux_gid_len = 0;
97         auth_sys->stamp       = 0;
98
99         auth_sys->credential.flavor = ONCRPC_AUTH_SYS;
100         auth_sys->credential.length = 16 +
101                                       oncrpc_strlen ( auth_sys->hostname );
102
103         return 0;
104 }
105
106 /**
107  * Prepare an ONC RPC session structure to be used by the ONC RPC layer
108  *
109  * @v session           ONC RPC session
110  * @v credential        Credential structure pointer
111  * @v verifier          Verifier structure pointer
112  * @v prog_name         ONC RPC program number
113  * @v prog_vers         ONC RPC program version number
114  */
115 void oncrpc_init_session ( struct oncrpc_session *session,
116                            struct oncrpc_cred *credential,
117                            struct oncrpc_cred *verifier, uint32_t prog_name,
118                            uint32_t prog_vers ) {
119         if ( ! session )
120                 return;
121
122         session->rpc_id     = rand();
123         session->credential = credential;
124         session->verifier   = verifier;
125         session->prog_name  = prog_name;
126         session->prog_vers  = prog_vers;
127 }
128
129 int oncrpc_call ( struct interface *intf, struct oncrpc_session *session,
130                   uint32_t proc_name, const struct oncrpc_field fields[] ) {
131         int              rc;
132         size_t           frame_size;
133         struct io_buffer *io_buf;
134
135         if ( ! session )
136                 return -EINVAL;
137
138         struct oncrpc_field header[] = {
139                 ONCRPC_FIELD ( int32, 0 ),
140                 ONCRPC_FIELD ( int32, ++session->rpc_id ),
141                 ONCRPC_FIELD ( int32, ONCRPC_CALL ),
142                 ONCRPC_FIELD ( int32, ONCRPC_VERS ),
143                 ONCRPC_FIELD ( int32, session->prog_name ),
144                 ONCRPC_FIELD ( int32, session->prog_vers ),
145                 ONCRPC_FIELD ( int32, proc_name ),
146                 ONCRPC_FIELD ( cred, session->credential ),
147                 ONCRPC_FIELD ( cred, session->verifier ),
148                 ONCRPC_FIELD_END,
149         };
150
151         frame_size  = oncrpc_compute_size ( header );
152         frame_size += oncrpc_compute_size ( fields );
153
154         io_buf = alloc_iob ( frame_size );
155         if ( ! io_buf )
156                 return -ENOBUFS;
157
158         header[0].value.int32 = SET_LAST_FRAME ( frame_size -
159                                                  sizeof ( uint32_t ) );
160
161         oncrpc_iob_add_fields ( io_buf, header );
162         oncrpc_iob_add_fields ( io_buf, fields );
163
164         rc = xfer_deliver_iob ( intf, io_buf );
165         if ( rc != 0 )
166                 free_iob ( io_buf );
167
168         return rc;
169 }
170
171 size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) {
172
173         size_t i;
174         size_t size = 0;
175
176         for ( i = 0; fields[i].type != oncrpc_none; i++ ) {
177                 switch ( fields[i].type ) {
178                 case oncrpc_int32:
179                         size += sizeof ( uint32_t );
180                         break;
181
182                 case oncrpc_int64:
183                         size += sizeof ( uint64_t );
184                         break;
185
186                 case oncrpc_str:
187                         size += oncrpc_strlen ( fields[i].value.str );
188                         break;
189
190                 case oncrpc_array:
191                         size += oncrpc_align ( fields[i].value.array.length );
192                         size += sizeof ( uint32_t );
193                         break;
194
195                 case oncrpc_intarray:
196                         size += sizeof ( uint32_t ) *
197                                 fields[i].value.intarray.length;
198                         size += sizeof ( uint32_t );
199                         break;
200
201                 case oncrpc_cred:
202                         size += fields[i].value.cred->length;
203                         size += 2 * sizeof ( uint32_t );
204                         break;
205
206                 default:
207                         return size;
208                 }
209         }
210
211         return size;
212 }
213
214 /**
215  * Parse an I/O buffer to extract a ONC RPC REPLY
216  * @v session           ONC RPC session
217  * @v reply             Reply structure where data will be saved
218  * @v io_buf            I/O buffer
219  */
220 int oncrpc_get_reply ( struct oncrpc_session *session __unused,
221                        struct oncrpc_reply *reply, struct io_buffer *io_buf ) {
222         if ( ! reply || ! io_buf )
223                 return -EINVAL;
224
225         reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) );
226         reply->rpc_id     = oncrpc_iob_get_int ( io_buf );
227
228         /* iPXE has no support for handling ONC RPC call */
229         if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY )
230                 return -ENOSYS;
231
232         reply->reply_state = oncrpc_iob_get_int ( io_buf );
233
234         if ( reply->reply_state == 0 )
235         {
236                 /* verifier.flavor */
237                 oncrpc_iob_get_int ( io_buf );
238                 /* verifier.length */
239                 iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf ));
240
241                 /* We don't use the verifier in iPXE, let it be an empty
242                    verifier. */
243                 reply->verifier = &oncrpc_auth_none;
244         }
245
246         reply->accept_state = oncrpc_iob_get_int ( io_buf );
247         reply->data         = io_buf;
248
249         return 0;
250 }