1 /* 2 * Copyright (c) 1989 Jan-Simon Pendry 3 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * from: @(#)rpc_fwd.c 8.1 (Berkeley) 6/6/93 35 * $Id: rpc_fwd.c,v 1.7 2003/06/02 23:36:51 millert Exp $ 36 */ 37 38 /* 39 * RPC packet forwarding 40 */ 41 42 #include "am.h" 43 #include <sys/ioctl.h> 44 #ifndef F_SETFL 45 #include <fcntl.h> 46 #endif /* F_SETFL */ 47 #ifndef FNDELAY 48 #include <sys/file.h> 49 #endif /* FNDELAY */ 50 51 /* 52 * Note that the ID field in the external packet is only 53 * ever treated as a 32 bit opaque data object, so there 54 * is no need to convert to and from network byte ordering. 55 */ 56 57 /* 58 * Each pending reply has an rpc_forward structure 59 * associated with it. These have a 15 second lifespan. 60 * If a new structure is required, then an expired 61 * one will be re-allocated if available, otherwise a fresh 62 * one is allocated. Whenever a reply is received the 63 * structure is discarded. 64 */ 65 typedef struct rpc_forward rpc_forward; 66 struct rpc_forward { 67 qelem rf_q; /* Linked list */ 68 time_t rf_ttl; /* Time to live */ 69 u_int rf_xid; /* Packet id */ 70 u_int rf_oldid; /* Original packet id */ 71 fwd_fun rf_fwd; /* Forwarding function */ 72 void * rf_ptr; 73 struct sockaddr_in rf_sin; 74 }; 75 76 /* 77 * Head of list of pending replies 78 */ 79 extern qelem rpc_head; 80 qelem rpc_head = { &rpc_head, &rpc_head }; 81 82 static u_int xid; 83 #define XID_ALLOC() (xid++) 84 85 #define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ 86 87 int fwd_sock; 88 89 /* 90 * Allocate a rely structure 91 */ 92 static rpc_forward * 93 fwd_alloc() 94 { 95 time_t now = clocktime(); 96 rpc_forward *p = 0, *p2; 97 98 #ifdef DEBUG 99 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ 100 #endif /* DEBUG */ 101 /* 102 * First search for an existing expired one. 103 */ 104 ITER(p2, rpc_forward, &rpc_head) { 105 if (p2->rf_ttl <= now) { 106 p = p2; 107 break; 108 } 109 } 110 111 /* 112 * If one couldn't be found then allocate 113 * a new structure and link it at the 114 * head of the list. 115 */ 116 if (p) { 117 /* 118 * Call forwarding function to say that 119 * this message was junked. 120 */ 121 #ifdef DEBUG 122 dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); 123 #endif /* DEBUG */ 124 if (p->rf_fwd) 125 (*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); 126 rem_que(&p->rf_q); 127 } else { 128 p = ALLOC(rpc_forward); 129 } 130 ins_que(&p->rf_q, &rpc_head); 131 132 /* 133 * Set the time to live field 134 * Timeout in 43 seconds 135 */ 136 p->rf_ttl = now + 43; 137 138 #ifdef DEBUG 139 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ 140 #endif /* DEBUG */ 141 return p; 142 } 143 144 /* 145 * Free an allocated reply structure. 146 * First unlink it from the list, then 147 * discard it. 148 */ 149 static void 150 fwd_free(rpc_forward *p) 151 { 152 #ifdef DEBUG 153 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ 154 #endif /* DEBUG */ 155 rem_que(&p->rf_q); 156 #ifdef DEBUG 157 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ 158 #endif /* DEBUG */ 159 free((void *)p); 160 } 161 162 /* 163 * Initialise the RPC forwarder 164 */ 165 int fwd_init() 166 { 167 int on = 1; 168 169 /* 170 * Create ping socket 171 */ 172 fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); 173 if (fwd_sock < 0) { 174 plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m"); 175 return errno; 176 } 177 178 /* 179 * Some things we talk to require a priv port - so make one here 180 */ 181 if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0) 182 plog(XLOG_ERROR, "can't bind privileged port"); 183 184 if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 && 185 ioctl(fwd_sock, FIONBIO, &on) < 0) { 186 plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); 187 return errno; 188 } 189 190 return 0; 191 } 192 193 /* 194 * Locate a packet in the forwarding list 195 */ 196 static rpc_forward * 197 fwd_locate(u_int id) 198 { 199 rpc_forward *p; 200 201 ITER(p, rpc_forward, &rpc_head) { 202 if (p->rf_xid == id) 203 return p; 204 } 205 206 return 0; 207 } 208 209 /* 210 * This is called to forward a packet to another 211 * RPC server. The message id is changed and noted 212 * so that when a reply appears we can tie it up 213 * correctly. Just matching the reply's source address 214 * would not work because it might come from a 215 * different address. 216 */ 217 int 218 fwd_packet(int type_id, void *pkt, int len, struct sockaddr_in *fwdto, 219 struct sockaddr_in *replyto, void *i, fwd_fun cb) 220 { 221 rpc_forward *p; 222 u_int *pkt_int; 223 int error; 224 225 if ((int)amd_state >= (int)Finishing) 226 return ENOENT; 227 228 /* 229 * See if the type_id is fully specified. 230 * If so, then discard any old entries 231 * for this id. 232 * Otherwise make sure the type_id is 233 * fully qualified by allocating an id here. 234 */ 235 #ifdef DEBUG 236 switch (type_id & RPC_XID_MASK) { 237 case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break; 238 case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break; 239 case RPC_XID_NFSPING: dlog("Sending NFS ping"); break; 240 default: dlog("UNKNOWN RPC XID"); break; 241 } 242 #endif /* DEBUG */ 243 244 if (type_id & ~RPC_XID_MASK) { 245 #ifdef DEBUG 246 /*dlog("Fully qualified rpc type provided");*/ 247 #endif /* DEBUG */ 248 p = fwd_locate(type_id); 249 if (p) { 250 #ifdef DEBUG 251 dlog("Discarding earlier rpc fwd handle"); 252 #endif /* DEBUG */ 253 fwd_free(p); 254 } 255 } else { 256 #ifdef DEBUG 257 dlog("Allocating a new xid..."); 258 #endif /* DEBUG */ 259 type_id = MK_RPC_XID(type_id, XID_ALLOC()); 260 } 261 262 p = fwd_alloc(); 263 if (!p) 264 return ENOBUFS; 265 266 error = 0; 267 268 pkt_int = (u_int *) pkt; 269 270 /* 271 * Get the original packet id 272 */ 273 p->rf_oldid = *pkt_int; 274 275 /* 276 * Replace with newly allocated id 277 */ 278 p->rf_xid = *pkt_int = type_id; 279 280 /* 281 * The sendto may fail if, for example, the route 282 * to a remote host is lost because an intermediate 283 * gateway has gone down. Important to fill in the 284 * rest of "p" otherwise nasty things happen later... 285 */ 286 #ifdef DEBUG 287 { char dq[20]; 288 289 dlog("Sending packet id %#x to %s.%d", p->rf_xid, 290 inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr), 291 ntohs(fwdto->sin_port)); 292 } 293 #endif /* DEBUG */ 294 if (sendto(fwd_sock, (char *) pkt, len, 0, 295 (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) 296 error = errno; 297 298 /* 299 * Save callback function and return address 300 */ 301 p->rf_fwd = cb; 302 if (replyto) 303 p->rf_sin = *replyto; 304 else 305 bzero((void *)&p->rf_sin, sizeof(p->rf_sin)); 306 p->rf_ptr = i; 307 308 return error; 309 } 310 311 /* 312 * Called when some data arrives on the forwarding socket 313 */ 314 void 315 fwd_reply() 316 { 317 int len; 318 #ifdef DYNAMIC_BUFFERS 319 void *pkt; 320 #else 321 u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1]; 322 #endif /* DYNAMIC_BUFFERS */ 323 u_int *pkt_int; 324 int rc; 325 rpc_forward *p; 326 struct sockaddr_in src_addr; 327 socklen_t src_addr_len; 328 329 /* 330 * Determine the length of the packet 331 */ 332 #ifdef DYNAMIC_BUFFERS 333 if (ioctl(fwd_sock, FIONREAD, &len) < 0) { 334 plog(XLOG_ERROR, "Error reading packet size: %m"); 335 return; 336 } 337 338 /* 339 * Allocate a buffer 340 */ 341 pkt = (void *)malloc((unsigned) len); 342 if (!pkt) { 343 plog(XLOG_ERROR, "Out of buffers in fwd_reply"); 344 return; 345 } 346 #else 347 len = MAX_PACKET_SIZE; 348 #endif /* DYNAMIC_BUFFERS */ 349 350 /* 351 * Read the packet and check for validity 352 */ 353 again: 354 src_addr_len = sizeof(src_addr); 355 rc = recvfrom(fwd_sock, (char *) pkt, len, 0, 356 (struct sockaddr *) &src_addr, &src_addr_len); 357 if (rc < 0 || src_addr_len != sizeof(src_addr) || 358 src_addr.sin_family != AF_INET) { 359 if (rc < 0 && errno == EINTR) 360 goto again; 361 plog(XLOG_ERROR, "Error reading RPC reply: %m"); 362 goto out; 363 } 364 365 #ifdef DYNAMIC_BUFFERS 366 if (rc != len) { 367 plog(XLOG_ERROR, "Short read in fwd_reply"); 368 goto out; 369 } 370 #endif /* DYNAMIC_BUFFERS */ 371 372 /* 373 * Do no more work if finishing soon 374 */ 375 if ((int)amd_state >= (int)Finishing) 376 goto out; 377 378 /* 379 * Find packet reference 380 */ 381 pkt_int = (u_int *) pkt; 382 383 #ifdef DEBUG 384 switch (*pkt_int & RPC_XID_MASK) { 385 case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break; 386 case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break; 387 case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break; 388 default: dlog("UNKNOWN RPC XID"); break; 389 } 390 #endif /* DEBUG */ 391 392 p = fwd_locate(*pkt_int); 393 if (!p) { 394 #ifdef DEBUG 395 dlog("Can't forward reply id %#x", *pkt_int); 396 #endif /* DEBUG */ 397 goto out; 398 } 399 400 if (p->rf_fwd) { 401 /* 402 * Put the original message id back 403 * into the packet. 404 */ 405 *pkt_int = p->rf_oldid; 406 407 /* 408 * Call forwarding function 409 */ 410 (*p->rf_fwd)((void *)pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); 411 } 412 413 /* 414 * Free forwarding info 415 */ 416 fwd_free(p); 417 418 out:; 419 #ifdef DYNAMIC_BUFFERS 420 /* 421 * Free the packet 422 */ 423 free((void *)pkt); 424 #endif /* DYNAMIC_BUFFERS */ 425 } 426