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