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