The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/ipfilter/netinet/ip_rpcb_pxy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (C) 2002-2012 by Ryan Beasley <ryanb@goddamnbastard.org>
    3  *
    4  * See the IPFILTER.LICENCE file for details on licencing.
    5  */
    6 /*
    7  * Overview:
    8  *   This is an in-kernel application proxy for Sun's RPCBIND (nee portmap)
    9  *   protocol as defined in RFC1833.  It is far from complete, mostly
   10  *   lacking in less-likely corner cases, but it's definitely functional.
   11  *
   12  *   Invocation:
   13  *     rdr <int> <e_ip>/32 port <e_p> -> <i_ip> port <i_p> udp proxy rpcbu
   14  *
   15  *   If the host running IP Filter is the same as the RPC server, it's
   16  *   perfectly legal for both the internal and external addresses and ports
   17  *   to match.
   18  *
   19  *   When triggered by appropriate IP NAT rules, this proxy works by
   20  *   examining data contained in received packets.  Requests and replies are
   21  *   modified, NAT and state table entries created, etc., as necessary.
   22  */
   23 /*
   24  * TODO / NOTES
   25  *
   26  *   o Must implement locking to protect proxy session data.
   27  *   o Fragmentation isn't supported.
   28  *   o Only supports UDP.
   29  *   o Doesn't support multiple RPC records in a single request.
   30  *   o Errors should be more fine-grained.  (e.g., malloc failure vs.
   31  *     illegal RPCB request / reply)
   32  *   o Even with the limit on the total amount of recorded transactions,
   33  *     should there be a timeout on transaction removal?
   34  *   o There is a potential collision between cloning, wildcard NAT and
   35  *     state entries.  There should be an appr_getport routine for
   36  *     to avoid this.
   37  *   o The enclosed hack of STREAMS support is pretty sick and most likely
   38  *     broken.
   39  *
   40  *      $Id$
   41  */
   42 #define IPF_RPCB_PROXY
   43 
   44 /*
   45  * Function prototypes
   46  */
   47 void    ipf_p_rpcb_main_load(void);
   48 void    ipf_p_rpcb_main_unload(void);
   49 int     ipf_p_rpcb_new(void *, fr_info_t *, ap_session_t *, nat_t *);
   50 void    ipf_p_rpcb_del(ipf_main_softc_t *, ap_session_t *);
   51 int     ipf_p_rpcb_in(void *, fr_info_t *, ap_session_t *, nat_t *);
   52 int     ipf_p_rpcb_out(void *, fr_info_t *, ap_session_t *, nat_t *);
   53 
   54 static void     ipf_p_rpcb_flush(rpcb_session_t *);
   55 static int      ipf_p_rpcb_decodereq(fr_info_t *, nat_t *,
   56         rpcb_session_t *, rpc_msg_t *);
   57 static int      ipf_p_rpcb_skipauth(rpc_msg_t *, xdr_auth_t *, u_32_t **);
   58 static int      ipf_p_rpcb_insert(rpcb_session_t *, rpcb_xact_t *);
   59 static int      ipf_p_rpcb_xdrrpcb(rpc_msg_t *, u_32_t *, rpcb_args_t *);
   60 static int      ipf_p_rpcb_getuaddr(rpc_msg_t *, xdr_uaddr_t *,
   61         u_32_t **);
   62 static u_int    ipf_p_rpcb_atoi(char *);
   63 static int      ipf_p_rpcb_modreq(fr_info_t *, nat_t *, rpc_msg_t *,
   64         mb_t *, u_int);
   65 static int      ipf_p_rpcb_decoderep(fr_info_t *, nat_t *,
   66         rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **);
   67 static rpcb_xact_t *    ipf_p_rpcb_lookup(rpcb_session_t *, u_32_t);
   68 static void     ipf_p_rpcb_deref(rpcb_session_t *, rpcb_xact_t *);
   69 static int      ipf_p_rpcb_getproto(rpc_msg_t *, xdr_proto_t *,
   70         u_32_t **);
   71 static int      ipf_p_rpcb_getnat(fr_info_t *, nat_t *, u_int, u_int);
   72 static int      ipf_p_rpcb_modv3(fr_info_t *, nat_t *, rpc_msg_t *,
   73         mb_t *, u_int);
   74 static int      ipf_p_rpcb_modv4(fr_info_t *, nat_t *, rpc_msg_t *,
   75         mb_t *, u_int);
   76 static void     ipf_p_rpcb_fixlen(fr_info_t *, int);
   77 
   78 /*
   79  * Global variables
   80  */
   81 static  frentry_t       rpcbfr; /* Skeleton rule for reference by entities
   82                                    this proxy creates. */
   83 VNET_DEFINE_STATIC(int, rpcbcnt);
   84 #define V_rpcbcnt               VNET(rpcbcnt)
   85                                 /* Upper bound of allocated RPCB sessions. */
   86                                 /* XXX rpcbcnt still requires locking. */
   87 
   88 static  int     rpcb_proxy_init = 0;
   89 
   90 
   91 /*
   92  * Since rpc_msg contains only pointers, one should use this macro as a
   93  * handy way to get to the goods.  (In case you're wondering about the name,
   94  * this started as BYTEREF -> BREF -> B.)
   95  */
   96 #define B(r)    (u_32_t)ntohl(*(r))
   97 
   98 /*
   99  * Public subroutines
  100  */
  101 
  102 /* -------------------------------------------------------------------- */
  103 /* Function:    ipf_p_rpcb_main_load                                    */
  104 /* Returns:     void                                                    */
  105 /* Parameters:  (void)                                                  */
  106 /*                                                                      */
  107 /* Initialize the filter rule entry and session limiter.                */
  108 /* -------------------------------------------------------------------- */
  109 void
  110 ipf_p_rpcb_main_load(void)
  111 {
  112         V_rpcbcnt = 0;
  113 
  114         bzero((char *)&rpcbfr, sizeof(rpcbfr));
  115         rpcbfr.fr_ref = 1;
  116         rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE;
  117         MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock");
  118         rpcb_proxy_init = 1;
  119 }
  120 
  121 /* -------------------------------------------------------------------- */
  122 /* Function:    ipf_p_rpcb_main_unload                                  */
  123 /* Returns:     void                                                    */
  124 /* Parameters:  (void)                                                  */
  125 /*                                                                      */
  126 /* Destroy rpcbfr's mutex to avoid a lock leak.                         */
  127 /* -------------------------------------------------------------------- */
  128 void
  129 ipf_p_rpcb_main_unload(void)
  130 {
  131         if (rpcb_proxy_init == 1) {
  132                 MUTEX_DESTROY(&rpcbfr.fr_lock);
  133                 rpcb_proxy_init = 0;
  134         }
  135 }
  136 
  137 /* -------------------------------------------------------------------- */
  138 /* Function:    ipf_p_rpcb_new                                          */
  139 /* Returns:     int - -1 == failure, 0 == success                       */
  140 /* Parameters:  fin(I)  - pointer to packet information                 */
  141 /*              aps(I)  - pointer to proxy session structure            */
  142 /*              nat(I)  - pointer to NAT session structure              */
  143 /*                                                                      */
  144 /* Allocate resources for per-session proxy structures.                 */
  145 /* -------------------------------------------------------------------- */
  146 int
  147 ipf_p_rpcb_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
  148 {
  149         rpcb_session_t *rs;
  150 
  151         nat = nat;      /* LINT */
  152 
  153         if (fin->fin_v != 4)
  154                 return (-1);
  155 
  156         KMALLOC(rs, rpcb_session_t *);
  157         if (rs == NULL)
  158                 return (-1);
  159 
  160         bzero((char *)rs, sizeof(*rs));
  161         MUTEX_INIT(&rs->rs_rxlock, "ipf Sun RPCB proxy session lock");
  162 
  163         aps->aps_data = rs;
  164 
  165         return (0);
  166 }
  167 
  168 /* -------------------------------------------------------------------- */
  169 /* Function:    ipf_p_rpcb_del                                          */
  170 /* Returns:     void                                                    */
  171 /* Parameters:  aps(I)  - pointer to proxy session structure            */
  172 /*                                                                      */
  173 /* Free up a session's list of RPCB requests.                           */
  174 /* -------------------------------------------------------------------- */
  175 void
  176 ipf_p_rpcb_del(ipf_main_softc_t *softc, ap_session_t *aps)
  177 {
  178         rpcb_session_t *rs;
  179         rs = (rpcb_session_t *)aps->aps_data;
  180 
  181         MUTEX_ENTER(&rs->rs_rxlock);
  182         ipf_p_rpcb_flush(rs);
  183         MUTEX_EXIT(&rs->rs_rxlock);
  184         MUTEX_DESTROY(&rs->rs_rxlock);
  185 }
  186 
  187 /* -------------------------------------------------------------------- */
  188 /* Function:    ipf_p_rpcb_in                                           */
  189 /* Returns:     int - APR_ERR(1) == drop the packet,                    */
  190 /*                    APR_ERR(2) == kill the proxy session,             */
  191 /*                    else change in packet length (in bytes)           */
  192 /* Parameters:  fin(I)  - pointer to packet information                 */
  193 /*              ip(I)   - pointer to packet header                      */
  194 /*              aps(I)  - pointer to proxy session structure            */
  195 /*              nat(I)  - pointer to NAT session structure              */
  196 /*                                                                      */
  197 /* Given a presumed RPCB request, perform some minor tests and pass off */
  198 /* for decoding.  Also pass packet off for a rewrite if necessary.      */
  199 /* -------------------------------------------------------------------- */
  200 int
  201 ipf_p_rpcb_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
  202 {
  203         rpc_msg_t rpcmsg, *rm;
  204         rpcb_session_t *rs;
  205         u_int off, dlen;
  206         mb_t *m;
  207         int rv;
  208 
  209         /* Disallow fragmented or illegally short packets. */
  210         if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0)
  211                 return (APR_ERR(1));
  212 
  213         /* Perform basic variable initialization. */
  214         rs = (rpcb_session_t *)aps->aps_data;
  215 
  216         m = fin->fin_m;
  217         off = (char *)fin->fin_dp - (char *)fin->fin_ip;
  218         off += sizeof(udphdr_t) + fin->fin_ipoff;
  219         dlen = fin->fin_dlen - sizeof(udphdr_t);
  220 
  221         /* Disallow packets outside legal range for supported requests. */
  222         if ((dlen < RPCB_REQMIN) || (dlen > RPCB_REQMAX))
  223                 return (APR_ERR(1));
  224 
  225         /* Copy packet over to convenience buffer. */
  226         rm = &rpcmsg;
  227         bzero((char *)rm, sizeof(*rm));
  228         COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf);
  229         rm->rm_buflen = dlen;
  230 
  231         /* Send off to decode request. */
  232         rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm);
  233 
  234         switch(rv)
  235         {
  236         case -1:
  237                 return (APR_ERR(1));
  238                 /*NOTREACHED*/
  239                 break;
  240         case 0:
  241                 break;
  242         case 1:
  243                 rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off);
  244                 break;
  245         default:
  246                 /*CONSTANTCONDITION*/
  247                 IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv));
  248         }
  249 
  250         return (rv);
  251 }
  252 
  253 /* -------------------------------------------------------------------- */
  254 /* Function:    ipf_p_rpcb_out                                          */
  255 /* Returns:     int - APR_ERR(1) == drop the packet,                    */
  256 /*                    APR_ERR(2) == kill the proxy session,             */
  257 /*                    else change in packet length (in bytes)           */
  258 /* Parameters:  fin(I)  - pointer to packet information                 */
  259 /*              ip(I)   - pointer to packet header                      */
  260 /*              aps(I)  - pointer to proxy session structure            */
  261 /*              nat(I)  - pointer to NAT session structure              */
  262 /*                                                                      */
  263 /* Given a presumed RPCB reply, perform some minor tests and pass off   */
  264 /* for decoding.  If the message indicates a successful request with    */
  265 /* valid addressing information, create NAT and state structures to     */
  266 /* allow direct communication between RPC client and server.            */
  267 /* -------------------------------------------------------------------- */
  268 int
  269 ipf_p_rpcb_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
  270 {
  271         rpc_msg_t rpcmsg, *rm;
  272         rpcb_session_t *rs;
  273         rpcb_xact_t *rx;
  274         u_int off, dlen;
  275         int rv, diff;
  276         mb_t *m;
  277 
  278         /* Disallow fragmented or illegally short packets. */
  279         if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0)
  280                 return (APR_ERR(1));
  281 
  282         /* Perform basic variable initialization. */
  283         rs = (rpcb_session_t *)aps->aps_data;
  284         rx = NULL;
  285 
  286         m = fin->fin_m;
  287         off = (char *)fin->fin_dp - (char *)fin->fin_ip;
  288         off += sizeof(udphdr_t) + fin->fin_ipoff;
  289         dlen = fin->fin_dlen - sizeof(udphdr_t);
  290         diff = 0;
  291 
  292         /* Disallow packets outside legal range for supported requests. */
  293         if ((dlen < RPCB_REPMIN) || (dlen > RPCB_REPMAX))
  294                 return (APR_ERR(1));
  295 
  296         /* Copy packet over to convenience buffer. */
  297         rm = &rpcmsg;
  298         bzero((char *)rm, sizeof(*rm));
  299         COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf);
  300         rm->rm_buflen = dlen;
  301 
  302         rx = NULL;              /* XXX gcc */
  303 
  304         /* Send off to decode reply. */
  305         rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx);
  306 
  307         switch(rv)
  308         {
  309         case -1: /* Bad packet */
  310                 if (rx != NULL) {
  311                         MUTEX_ENTER(&rs->rs_rxlock);
  312                         ipf_p_rpcb_deref(rs, rx);
  313                         MUTEX_EXIT(&rs->rs_rxlock);
  314                 }
  315                 return (APR_ERR(1));
  316                 /*NOTREACHED*/
  317                 break;
  318         case  0: /* Negative reply / request rejected */
  319                 break;
  320         case  1: /* Positive reply */
  321                 /*
  322                  * With the IP address embedded in a GETADDR(LIST) reply,
  323                  * we'll need to rewrite the packet in the very possible
  324                  * event that the internal & external addresses aren't the
  325                  * same.  (i.e., this box is either a router or rpcbind
  326                  * only listens on loopback.)
  327                  */
  328                 if (nat->nat_odstaddr != nat->nat_ndstaddr) {
  329                         if (rx->rx_type == RPCB_RES_STRING)
  330                                 diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off);
  331                         else if (rx->rx_type == RPCB_RES_LIST)
  332                                 diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off);
  333                 }
  334                 break;
  335         default:
  336                 /*CONSTANTCONDITION*/
  337                 IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv));
  338         }
  339 
  340         if (rx != NULL) {
  341                 MUTEX_ENTER(&rs->rs_rxlock);
  342                 /* XXX Gross hack - I'm overloading the reference
  343                  * counter to deal with both threads and retransmitted
  344                  * requests.  One deref signals that this thread is
  345                  * finished with rx, and the other signals that we've
  346                  * processed its reply.
  347                  */
  348                 ipf_p_rpcb_deref(rs, rx);
  349                 ipf_p_rpcb_deref(rs, rx);
  350                 MUTEX_EXIT(&rs->rs_rxlock);
  351         }
  352 
  353         return (diff);
  354 }
  355 
  356 /*
  357  * Private support subroutines
  358  */
  359 
  360 /* -------------------------------------------------------------------- */
  361 /* Function:    ipf_p_rpcb_flush                                                */
  362 /* Returns:     void                                                    */
  363 /* Parameters:  rs(I)   - pointer to RPCB session structure             */
  364 /*                                                                      */
  365 /* Simply flushes the list of outstanding transactions, if any.         */
  366 /* -------------------------------------------------------------------- */
  367 static void
  368 ipf_p_rpcb_flush(rpcb_session_t *rs)
  369 {
  370         rpcb_xact_t *r1, *r2;
  371 
  372         r1 = rs->rs_rxlist;
  373         if (r1 == NULL)
  374                 return;
  375 
  376         while (r1 != NULL) {
  377                 r2 = r1;
  378                 r1 = r1->rx_next;
  379                 KFREE(r2);
  380         }
  381 }
  382 
  383 /* -------------------------------------------------------------------- */
  384 /* Function:    ipf_p_rpcb_decodereq                                    */
  385 /* Returns:     int - -1 == bad request or critical failure,            */
  386 /*                     0 == request successfully decoded,               */
  387 /*                     1 == request successfully decoded; requires      */
  388 /*                          address rewrite/modification                */
  389 /* Parameters:  fin(I)  - pointer to packet information                 */
  390 /*              nat(I)  - pointer to NAT session structure              */
  391 /*              rs(I)   - pointer to RPCB session structure             */
  392 /*              rm(I)   - pointer to RPC message structure              */
  393 /*                                                                      */
  394 /* Take a presumed RPCB request, decode it, and store the results in    */
  395 /* the transaction list.  If the internal target address needs to be    */
  396 /* modified, store its location in ptr.                                 */
  397 /* WARNING:  It's the responsibility of the caller to make sure there   */
  398 /* is enough room in rs_buf for the basic RPC message "preamble".       */
  399 /* -------------------------------------------------------------------- */
  400 static int
  401 ipf_p_rpcb_decodereq(fr_info_t *fin, nat_t *nat, rpcb_session_t *rs,
  402         rpc_msg_t *rm)
  403 {
  404         rpcb_args_t *ra;
  405         u_32_t xdr, *p;
  406         rpc_call_t *rc;
  407         rpcb_xact_t rx;
  408         int mod;
  409 
  410         p = (u_32_t *)rm->rm_msgbuf;
  411         mod = 0;
  412 
  413         bzero((char *)&rx, sizeof(rx));
  414         rc = &rm->rm_call;
  415 
  416         rm->rm_xid = p;
  417         rx.rx_xid = B(p++);     /* Record this message's XID. */
  418 
  419         /* Parse out and test the RPC header. */
  420         if ((B(p++) != RPCB_CALL) ||
  421             (B(p++) != RPCB_MSG_VERSION) ||
  422             (B(p++) != RPCB_PROG))
  423                 return (-1);
  424 
  425         /* Record the RPCB version and procedure. */
  426         rc->rc_vers = p++;
  427         rc->rc_proc = p++;
  428 
  429         /* Bypass RPC authentication stuff. */
  430         if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0)
  431                 return (-1);
  432         if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0)
  433                 return (-1);
  434 
  435         /* Compare RPCB version and procedure numbers. */
  436         switch(B(rc->rc_vers))
  437         {
  438         case 2:
  439                 /* This proxy only supports PMAP_GETPORT. */
  440                 if (B(rc->rc_proc) != RPCB_GETPORT)
  441                         return (-1);
  442 
  443                 /* Portmap requests contain four 4 byte parameters. */
  444                 if (RPCB_BUF_EQ(rm, p, 16) == 0)
  445                         return (-1);
  446 
  447                 p += 2; /* Skip requested program and version numbers. */
  448 
  449                 /* Sanity check the requested protocol. */
  450                 xdr = B(p);
  451                 if (!(xdr == IPPROTO_UDP || xdr == IPPROTO_TCP))
  452                         return (-1);
  453 
  454                 rx.rx_type = RPCB_RES_PMAP;
  455                 rx.rx_proto = xdr;
  456                 break;
  457         case 3:
  458         case 4:
  459                 /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */
  460                 switch(B(rc->rc_proc))
  461                 {
  462                 case RPCB_GETADDR:
  463                         rx.rx_type = RPCB_RES_STRING;
  464                         rx.rx_proto = (u_int)fin->fin_p;
  465                         break;
  466                 case RPCB_GETADDRLIST:
  467                         if (B(rc->rc_vers) != 4)
  468                                 return (-1);
  469                         rx.rx_type = RPCB_RES_LIST;
  470                         break;
  471                 default:
  472                         return (-1);
  473                 }
  474 
  475                 ra = &rc->rc_rpcbargs;
  476 
  477                 /* Decode the 'struct rpcb' request. */
  478                 if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0)
  479                         return (-1);
  480 
  481                 /* Are the target address & port valid? */
  482                 if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) ||
  483                     (ra->ra_maddr.xu_port != nat->nat_ndport))
  484                         return (-1);
  485 
  486                 /* Do we need to rewrite this packet? */
  487                 if ((nat->nat_ndstaddr != nat->nat_odstaddr) ||
  488                     (nat->nat_ndport != nat->nat_odport))
  489                         mod = 1;
  490                 break;
  491         default:
  492                 return (-1);
  493         }
  494 
  495         MUTEX_ENTER(&rs->rs_rxlock);
  496         if (ipf_p_rpcb_insert(rs, &rx) != 0) {
  497                 MUTEX_EXIT(&rs->rs_rxlock);
  498                 return (-1);
  499         }
  500         MUTEX_EXIT(&rs->rs_rxlock);
  501 
  502         return (mod);
  503 }
  504 
  505 /* -------------------------------------------------------------------- */
  506 /* Function:    ipf_p_rpcb_skipauth                                     */
  507 /* Returns:     int -- -1 == illegal auth parameters (lengths)          */
  508 /*                      0 == valid parameters, pointer advanced         */
  509 /* Parameters:  rm(I)   - pointer to RPC message structure              */
  510 /*              auth(I) - pointer to RPC auth structure                 */
  511 /*              buf(IO) - pointer to location within convenience buffer */
  512 /*                                                                      */
  513 /* Record auth data length & location of auth data, then advance past   */
  514 /* it.                                                                  */
  515 /* -------------------------------------------------------------------- */
  516 static int
  517 ipf_p_rpcb_skipauth(rpc_msg_t *rm, xdr_auth_t *auth, u_32_t **buf)
  518 {
  519         u_32_t *p, xdr;
  520 
  521         p = *buf;
  522 
  523         /* Make sure we have enough space for expected fixed auth parms. */
  524         if (RPCB_BUF_GEQ(rm, p, 8) == 0)
  525                 return (-1);
  526 
  527         p++; /* We don't care about auth_flavor. */
  528 
  529         auth->xa_string.xs_len = p;
  530         xdr = B(p++);           /* Length of auth_data */
  531 
  532         /* Test for absurdity / illegality of auth_data length. */
  533         if ((XDRALIGN(xdr) < xdr) || (RPCB_BUF_GEQ(rm, p, XDRALIGN(xdr)) == 0))
  534                 return (-1);
  535 
  536         auth->xa_string.xs_str = (char *)p;
  537 
  538         p += XDRALIGN(xdr);     /* Advance our location. */
  539 
  540         *buf = (u_32_t *)p;
  541 
  542         return (0);
  543 }
  544 
  545 /* -------------------------------------------------------------------- */
  546 /* Function:    ipf_p_rpcb_insert                                       */
  547 /* Returns:     int -- -1 == list insertion failed,                     */
  548 /*                      0 == item successfully added                    */
  549 /* Parameters:  rs(I)   - pointer to RPCB session structure             */
  550 /*              rx(I)   - pointer to RPCB transaction structure         */
  551 /* -------------------------------------------------------------------- */
  552 static int
  553 ipf_p_rpcb_insert(rpcb_session_t *rs, rpcb_xact_t *rx)
  554 {
  555         rpcb_xact_t *rxp;
  556 
  557         rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid);
  558         if (rxp != NULL) {
  559                 ++rxp->rx_ref;
  560                 return (0);
  561         }
  562 
  563         if (V_rpcbcnt == RPCB_MAXREQS)
  564                 return (-1);
  565 
  566         KMALLOC(rxp, rpcb_xact_t *);
  567         if (rxp == NULL)
  568                 return (-1);
  569 
  570         bcopy((char *)rx, (char *)rxp, sizeof(*rx));
  571 
  572         if (rs->rs_rxlist != NULL)
  573                 rs->rs_rxlist->rx_pnext = &rxp->rx_next;
  574 
  575         rxp->rx_pnext = &rs->rs_rxlist;
  576         rxp->rx_next = rs->rs_rxlist;
  577         rs->rs_rxlist = rxp;
  578 
  579         rxp->rx_ref = 1;
  580 
  581         ++V_rpcbcnt;
  582 
  583         return (0);
  584 }
  585 
  586 /* -------------------------------------------------------------------- */
  587 /* Function:    ipf_p_rpcb_xdrrpcb                                      */
  588 /* Returns:     int -- -1 == failure to properly decode the request     */
  589 /*                      0 == rpcb successfully decoded                  */
  590 /* Parameters:  rs(I)   - pointer to RPCB session structure             */
  591 /*              p(I)    - pointer to location within session buffer     */
  592 /*              rpcb(O) - pointer to rpcb (xdr type) structure          */
  593 /*                                                                      */
  594 /* Decode a XDR encoded rpcb structure and record its contents in rpcb  */
  595 /* within only the context of TCP/UDP over IP networks.                 */
  596 /* -------------------------------------------------------------------- */
  597 static int
  598 ipf_p_rpcb_xdrrpcb(rpc_msg_t *rm, u_32_t *p, rpcb_args_t *ra)
  599 {
  600         if (!RPCB_BUF_GEQ(rm, p, 20))
  601                 return (-1);
  602 
  603         /* Bypass target program & version. */
  604         p += 2;
  605 
  606         /* Decode r_netid.  Must be "tcp" or "udp". */
  607         if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0)
  608                 return (-1);
  609 
  610         /* Decode r_maddr. */
  611         if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0)
  612                 return (-1);
  613 
  614         /* Advance to r_owner and make sure it's empty. */
  615         if (!RPCB_BUF_EQ(rm, p, 4) || (B(p) != 0))
  616                 return (-1);
  617 
  618         return (0);
  619 }
  620 
  621 /* -------------------------------------------------------------------- */
  622 /* Function:    ipf_p_rpcb_getuaddr                                     */
  623 /* Returns:     int -- -1 == illegal string,                            */
  624 /*                      0 == string parsed; contents recorded           */
  625 /* Parameters:  rm(I)   - pointer to RPC message structure              */
  626 /*              xu(I)   - pointer to universal address structure        */
  627 /*              p(IO)   - pointer to location within message buffer     */
  628 /*                                                                      */
  629 /* Decode the IP address / port at p and record them in xu.             */
  630 /* -------------------------------------------------------------------- */
  631 static int
  632 ipf_p_rpcb_getuaddr(rpc_msg_t *rm, xdr_uaddr_t *xu, u_32_t **p)
  633 {
  634         char *c, *i, *b, *pp;
  635         u_int d, dd, l, t;
  636         char uastr[24];
  637 
  638         /* Test for string length. */
  639         if (!RPCB_BUF_GEQ(rm, *p, 4))
  640                 return (-1);
  641 
  642         xu->xu_xslen = (*p)++;
  643         xu->xu_xsstr = (char *)*p;
  644 
  645         /* Length check */
  646         l = B(xu->xu_xslen);
  647         if (l < 11 || l > 23 || !RPCB_BUF_GEQ(rm, *p, XDRALIGN(l)))
  648                 return (-1);
  649 
  650         /* Advance p */
  651         *(char **)p += XDRALIGN(l);
  652 
  653         /* Copy string to local buffer & terminate C style */
  654         bcopy(xu->xu_xsstr, uastr, l);
  655         uastr[l] = '\0';
  656 
  657         i = (char *)&xu->xu_ip;
  658         pp = (char *)&xu->xu_port;
  659 
  660         /*
  661          * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of
  662          * an IP address and [ef] are the bytes of a L4 port.
  663          */
  664         if (!(ISDIGIT(uastr[0]) && ISDIGIT(uastr[l-1])))
  665                 return (-1);
  666         b = uastr;
  667         for (c = &uastr[1], d = 0, dd = 0; c < &uastr[l-1]; c++) {
  668                 if (ISDIGIT(*c)) {
  669                         dd = 0;
  670                         continue;
  671                 }
  672                 if (*c == '.') {
  673                         if (dd != 0)
  674                                 return (-1);
  675 
  676                         /* Check for ASCII byte. */
  677                         *c = '\0';
  678                         t = ipf_p_rpcb_atoi(b);
  679                         if (t > 255)
  680                                 return (-1);
  681 
  682                         /* Aim b at beginning of the next byte. */
  683                         b = c + 1;
  684 
  685                         /* Switch off IP addr vs port parsing. */
  686                         if (d < 4)
  687                                 i[d++] = t & 0xff;
  688                         else
  689                                 pp[d++ - 4] = t & 0xff;
  690 
  691                         dd = 1;
  692                         continue;
  693                 }
  694                 return (-1);
  695         }
  696         if (d != 5) /* String must contain exactly 5 periods. */
  697                 return (-1);
  698 
  699         /* Handle the last byte (port low byte) */
  700         t = ipf_p_rpcb_atoi(b);
  701         if (t > 255)
  702                 return (-1);
  703         pp[d - 4] = t & 0xff;
  704 
  705         return (0);
  706 }
  707 
  708 /* -------------------------------------------------------------------- */
  709 /* Function:    ipf_p_rpcb_atoi (XXX should be generic for all proxies) */
  710 /* Returns:     int -- integer representation of supplied string        */
  711 /* Parameters:  ptr(I)  - input string                                  */
  712 /*                                                                      */
  713 /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c.                 */
  714 /* -------------------------------------------------------------------- */
  715 static u_int
  716 ipf_p_rpcb_atoi(char *ptr)
  717 {
  718         register char *s = ptr, c;
  719         register u_int i = 0;
  720 
  721         while (((c = *s++) != '\0') && ISDIGIT(c)) {
  722                 i *= 10;
  723                 i += c - '';
  724         }
  725         return (i);
  726 }
  727 
  728 /* -------------------------------------------------------------------- */
  729 /* Function:    ipf_p_rpcb_modreq                                       */
  730 /* Returns:     int -- change in datagram length                        */
  731 /*                      APR_ERR(2) - critical failure                   */
  732 /* Parameters:  fin(I)  - pointer to packet information                 */
  733 /*              nat(I)  - pointer to NAT session                        */
  734 /*              rm(I)   - pointer to RPC message structure              */
  735 /*              m(I)    - pointer to mbuf chain                         */
  736 /*              off(I)  - current offset within mbuf chain              */
  737 /*                                                                      */
  738 /* When external and internal addresses differ, we rewrite the former   */
  739 /* with the latter.  (This is exclusive to protocol versions 3 & 4).    */
  740 /* -------------------------------------------------------------------- */
  741 static int
  742 ipf_p_rpcb_modreq(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m,
  743         u_int off)
  744 {
  745         u_int len, xlen, pos, bogo;
  746         rpcb_args_t *ra;
  747         char uaddr[24];
  748         udphdr_t *udp;
  749         char *i, *p;
  750         int diff;
  751 
  752         ra = &rm->rm_call.rc_rpcbargs;
  753         i = (char *)&nat->nat_odstaddr;
  754         p = (char *)&nat->nat_odport;
  755 
  756         /* Form new string. */
  757         bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */
  758         (void) snprintf(uaddr, sizeof(uaddr),
  759                        "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff,
  760                        i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff);
  761         len = strlen(uaddr);
  762         xlen = XDRALIGN(len);
  763 
  764         /* Determine mbuf offset to start writing to. */
  765         pos = (char *)ra->ra_maddr.xu_xslen - rm->rm_msgbuf;
  766         off += pos;
  767 
  768         /* Write new string length. */
  769         bogo = htonl(len);
  770         COPYBACK(m, off, 4, (caddr_t)&bogo);
  771         off += 4;
  772 
  773         /* Write new string. */
  774         COPYBACK(m, off, xlen, uaddr);
  775         off += xlen;
  776 
  777         /* Write in zero r_owner. */
  778         bogo = 0;
  779         COPYBACK(m, off, 4, (caddr_t)&bogo);
  780 
  781         /* Determine difference in data lengths. */
  782         diff = xlen - XDRALIGN(B(ra->ra_maddr.xu_xslen));
  783 
  784         /*
  785          * If our new string has a different length, make necessary
  786          * adjustments.
  787          */
  788         if (diff != 0) {
  789                 udp = fin->fin_dp;
  790                 udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff);
  791                 fin->fin_plen += diff;
  792                 fin->fin_ip->ip_len = htons(fin->fin_plen);
  793                 fin->fin_dlen += diff;
  794                 /* XXX Storage lengths. */
  795         }
  796 
  797         return (diff);
  798 }
  799 
  800 /* -------------------------------------------------------------------- */
  801 /* Function:    ipf_p_rpcb_decoderep                                    */
  802 /* Returns:     int - -1 == bad request or critical failure,            */
  803 /*                     0 == valid, negative reply                       */
  804 /*                     1 == vaddlid, positive reply; needs no changes   */
  805 /* Parameters:  fin(I)  - pointer to packet information                 */
  806 /*              nat(I)  - pointer to NAT session structure              */
  807 /*              rs(I)   - pointer to RPCB session structure             */
  808 /*              rm(I)   - pointer to RPC message structure              */
  809 /*              rxp(O)  - pointer to RPCB transaction structure         */
  810 /*                                                                      */
  811 /* Take a presumed RPCB reply, extract the XID, search for the original */
  812 /* request information, and determine whether the request was accepted  */
  813 /* or rejected.  With a valid accepted reply, go ahead and create NAT   */
  814 /* and state entries, and finish up by rewriting the packet as          */
  815 /* required.                                                            */
  816 /*                                                                      */
  817 /* WARNING:  It's the responsibility of the caller to make sure there   */
  818 /* is enough room in rs_buf for the basic RPC message "preamble".       */
  819 /* -------------------------------------------------------------------- */
  820 static int
  821 ipf_p_rpcb_decoderep(fr_info_t *fin, nat_t *nat, rpcb_session_t *rs,
  822         rpc_msg_t *rm, rpcb_xact_t **rxp)
  823 {
  824         rpcb_listp_t *rl;
  825         rpcb_entry_t *re;
  826         rpcb_xact_t *rx;
  827         u_32_t xdr, *p;
  828         rpc_resp_t *rr;
  829         int rv, cnt;
  830 
  831         p = (u_32_t *)rm->rm_msgbuf;
  832 
  833         bzero((char *)&rx, sizeof(rx));
  834         rr = &rm->rm_resp;
  835 
  836         rm->rm_xid = p;
  837         xdr = B(p++);           /* Record this message's XID. */
  838 
  839         /* Lookup XID */
  840         MUTEX_ENTER(&rs->rs_rxlock);
  841         if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) {
  842                 MUTEX_EXIT(&rs->rs_rxlock);
  843                 return (-1);
  844         }
  845         ++rx->rx_ref;        /* per thread reference */
  846         MUTEX_EXIT(&rs->rs_rxlock);
  847 
  848         *rxp = rx;
  849 
  850         /* Test call vs reply */
  851         if (B(p++) != RPCB_REPLY)
  852                 return (-1);
  853 
  854         /* Test reply_stat */
  855         switch(B(p++))
  856         {
  857         case RPCB_MSG_DENIED:
  858                 return (0);
  859         case RPCB_MSG_ACCEPTED:
  860                 break;
  861         default:
  862                 return (-1);
  863         }
  864 
  865         /* Bypass RPC authentication stuff. */
  866         if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0)
  867                 return (-1);
  868 
  869         /* Test accept status */
  870         if (!RPCB_BUF_GEQ(rm, p, 4))
  871                 return (-1);
  872         if (B(p++) != 0)
  873                 return (0);
  874 
  875         /* Parse out the expected reply */
  876         switch(rx->rx_type)
  877         {
  878         case RPCB_RES_PMAP:
  879                 /* There must be only one 4 byte argument. */
  880                 if (!RPCB_BUF_EQ(rm, p, 4))
  881                         return (-1);
  882 
  883                 rr->rr_v2 = p;
  884                 xdr = B(rr->rr_v2);
  885 
  886                 /* Reply w/ a 0 port indicates service isn't registered */
  887                 if (xdr == 0)
  888                         return (0);
  889 
  890                 /* Is the value sane? */
  891                 if (xdr > 65535)
  892                         return (-1);
  893 
  894                 /* Create NAT & state table entries. */
  895                 if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0)
  896                         return (-1);
  897                 break;
  898         case RPCB_RES_STRING:
  899                 /* Expecting a XDR string; need 4 bytes for length */
  900                 if (!RPCB_BUF_GEQ(rm, p, 4))
  901                         return (-1);
  902 
  903                 rr->rr_v3.xu_str.xs_len = p++;
  904                 rr->rr_v3.xu_str.xs_str = (char *)p;
  905 
  906                 xdr = B(rr->rr_v3.xu_xslen);
  907 
  908                 /* A null string indicates an unregistered service */
  909                 if ((xdr == 0) && RPCB_BUF_EQ(rm, p, 0))
  910                         return (0);
  911 
  912                 /* Decode the target IP address / port. */
  913                 if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0)
  914                         return (-1);
  915 
  916                 /* Validate the IP address and port contained. */
  917                 if (nat->nat_odstaddr != rr->rr_v3.xu_ip)
  918                         return (-1);
  919 
  920                 /* Create NAT & state table entries. */
  921                 if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto,
  922                                      (u_int)rr->rr_v3.xu_port) != 0)
  923                         return (-1);
  924                 break;
  925         case RPCB_RES_LIST:
  926                 if (!RPCB_BUF_GEQ(rm, p, 4))
  927                         return (-1);
  928                 /* rpcb_entry_list_ptr */
  929                 switch(B(p))
  930                 {
  931                 case 0:
  932                         return (0);
  933                         /*NOTREACHED*/
  934                         break;
  935                 case 1:
  936                         break;
  937                 default:
  938                         return (-1);
  939                 }
  940                 rl = &rr->rr_v4;
  941                 rl->rl_list = p++;
  942                 cnt = 0;
  943 
  944                 for(;;) {
  945                         re = &rl->rl_entries[rl->rl_cnt];
  946                         if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0)
  947                                 return (-1);
  948                         if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0)
  949                                 return (-1);
  950                         /* re_semantics & re_pfamily length */
  951                         if (!RPCB_BUF_GEQ(rm, p, 12))
  952                                 return (-1);
  953                         p++; /* Skipping re_semantics. */
  954                         xdr = B(p++);
  955                         if ((xdr != 4) || strncmp((char *)p, "inet", 4))
  956                                 return (-1);
  957                         p++;
  958                         if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0)
  959                                 return (-1);
  960                         if (!RPCB_BUF_GEQ(rm, p, 4))
  961                                 return (-1);
  962                         re->re_more = p;
  963                         if (B(re->re_more) > 1) /* 0,1 only legal values */
  964                                 return (-1);
  965                         ++rl->rl_cnt;
  966                         ++cnt;
  967                         if (B(re->re_more) == 0)
  968                                 break;
  969                         /* Replies in  max out at 2; TCP and/or UDP */
  970                         if (cnt > 2)
  971                                 return (-1);
  972                         p++;
  973                 }
  974 
  975                 for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) {
  976                         re = &rl->rl_entries[rl->rl_cnt];
  977                         rv = ipf_p_rpcb_getnat(fin, nat,
  978                                               re->re_proto.xp_proto,
  979                                               (u_int)re->re_maddr.xu_port);
  980                         if (rv != 0)
  981                                 return (-1);
  982                 }
  983                 break;
  984         default:
  985                 /*CONSTANTCONDITION*/
  986                 IPF_PANIC(1, ("illegal rx_type %d", rx->rx_type));
  987         }
  988 
  989         return (1);
  990 }
  991 
  992 /* -------------------------------------------------------------------- */
  993 /* Function:    ipf_p_rpcb_lookup                                       */
  994 /* Returns:     rpcb_xact_t *   - NULL == no matching record,           */
  995 /*                                else pointer to relevant entry        */
  996 /* Parameters:  rs(I)   - pointer to RPCB session                       */
  997 /*              xid(I)  - XID to look for                               */
  998 /* -------------------------------------------------------------------- */
  999 static rpcb_xact_t *
 1000 ipf_p_rpcb_lookup(rpcb_session_t *rs, u_32_t xid)
 1001 {
 1002         rpcb_xact_t *rx;
 1003 
 1004         if (rs->rs_rxlist == NULL)
 1005                 return (NULL);
 1006 
 1007         for (rx = rs->rs_rxlist; rx != NULL; rx = rx->rx_next)
 1008                 if (rx->rx_xid == xid)
 1009                         break;
 1010 
 1011         return (rx);
 1012 }
 1013 
 1014 /* -------------------------------------------------------------------- */
 1015 /* Function:    ipf_p_rpcb_deref                                                */
 1016 /* Returns:     (void)                                                  */
 1017 /* Parameters:  rs(I)   - pointer to RPCB session                       */
 1018 /*              rx(I)   - pointer to RPC transaction struct to remove   */
 1019 /*              force(I) - indicates to delete entry regardless of      */
 1020 /*                         reference count                              */
 1021 /* Locking:     rs->rs_rxlock must be held write only                   */
 1022 /*                                                                      */
 1023 /* Free the RPCB transaction record rx from the chain of entries.       */
 1024 /* -------------------------------------------------------------------- */
 1025 static void
 1026 ipf_p_rpcb_deref(rpcb_session_t *rs, rpcb_xact_t *rx)
 1027 {
 1028         rs = rs;        /* LINT */
 1029 
 1030         if (rx == NULL)
 1031                 return;
 1032 
 1033         if (--rx->rx_ref != 0)
 1034                 return;
 1035 
 1036         if (rx->rx_next != NULL)
 1037                 rx->rx_next->rx_pnext = rx->rx_pnext;
 1038 
 1039         *rx->rx_pnext = rx->rx_next;
 1040 
 1041         KFREE(rx);
 1042 
 1043         --V_rpcbcnt;
 1044 }
 1045 
 1046 /* -------------------------------------------------------------------- */
 1047 /* Function:    ipf_p_rpcb_getproto                                     */
 1048 /* Returns:     int - -1 == illegal protocol/netid,                     */
 1049 /*                     0 == legal protocol/netid                        */
 1050 /* Parameters:  rm(I)   - pointer to RPC message structure              */
 1051 /*              xp(I)   - pointer to netid structure                    */
 1052 /*              p(IO)   - pointer to location within packet buffer      */
 1053 /*                                                                      */
 1054 /* Decode netid/proto stored at p and record its numeric value.         */
 1055 /* -------------------------------------------------------------------- */
 1056 static int
 1057 ipf_p_rpcb_getproto(rpc_msg_t *rm, xdr_proto_t *xp, u_32_t **p)
 1058 {
 1059         u_int len;
 1060 
 1061         /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */
 1062         if (!RPCB_BUF_GEQ(rm, p, 8))
 1063                 return (-1);
 1064 
 1065         xp->xp_xslen = (*p)++;
 1066         xp->xp_xsstr = (char *)*p;
 1067 
 1068         /* Test the string length. */
 1069         len = B(xp->xp_xslen);
 1070         if (len != 3)
 1071                 return (-1);
 1072 
 1073         /* Test the actual string & record the protocol accordingly. */
 1074         if (!strncmp((char *)xp->xp_xsstr, "tcp\0", 4))
 1075                 xp->xp_proto = IPPROTO_TCP;
 1076         else if (!strncmp((char *)xp->xp_xsstr, "udp\0", 4))
 1077                 xp->xp_proto = IPPROTO_UDP;
 1078         else {
 1079                 return (-1);
 1080         }
 1081 
 1082         /* Advance past the string. */
 1083         (*p)++;
 1084 
 1085         return (0);
 1086 }
 1087 
 1088 /* -------------------------------------------------------------------- */
 1089 /* Function:    ipf_p_rpcb_getnat                                       */
 1090 /* Returns:     int -- -1 == failed to create table entries,            */
 1091 /*                      0 == success                                    */
 1092 /* Parameters:  fin(I)  - pointer to packet information                 */
 1093 /*              nat(I)  - pointer to NAT table entry                    */
 1094 /*              proto(I) - transport protocol for new entries           */
 1095 /*              port(I) - new port to use w/ wildcard table entries     */
 1096 /*                                                                      */
 1097 /* Create state and NAT entries to handle an anticipated connection     */
 1098 /* attempt between RPC client and server.                               */
 1099 /* -------------------------------------------------------------------- */
 1100 static int
 1101 ipf_p_rpcb_getnat(fr_info_t *fin, nat_t *nat, u_int proto, u_int port)
 1102 {
 1103         ipf_main_softc_t *softc = fin->fin_main_soft;
 1104         ipnat_t *ipn, ipnat;
 1105         tcphdr_t tcp;
 1106         ipstate_t *is;
 1107         fr_info_t fi;
 1108         nat_t *natl;
 1109         int nflags;
 1110 
 1111         ipn = nat->nat_ptr;
 1112 
 1113         /* Generate dummy fr_info */
 1114         bcopy((char *)fin, (char *)&fi, sizeof(fi));
 1115         fi.fin_out = 0;
 1116         fi.fin_p = proto;
 1117         fi.fin_sport = 0;
 1118         fi.fin_dport = port & 0xffff;
 1119         fi.fin_flx |= FI_IGNORE;
 1120         fi.fin_saddr = nat->nat_osrcaddr;
 1121         fi.fin_daddr = nat->nat_odstaddr;
 1122 
 1123         bzero((char *)&tcp, sizeof(tcp));
 1124         tcp.th_dport = htons(port);
 1125 
 1126         if (proto == IPPROTO_TCP) {
 1127                 tcp.th_win = htons(8192);
 1128                 TCP_OFF_A(&tcp, sizeof(tcphdr_t) >> 2);
 1129                 fi.fin_dlen = sizeof(tcphdr_t);
 1130                 tcp.th_flags = TH_SYN;
 1131                 nflags = NAT_TCP;
 1132         } else {
 1133                 fi.fin_dlen = sizeof(udphdr_t);
 1134                 nflags = NAT_UDP;
 1135         }
 1136 
 1137         nflags |= SI_W_SPORT|NAT_SEARCH;
 1138         fi.fin_dp = &tcp;
 1139         fi.fin_plen = fi.fin_hlen + fi.fin_dlen;
 1140 
 1141         /*
 1142          * Search for existing NAT & state entries.  Pay close attention to
 1143          * mutexes / locks grabbed from lookup routines, as not doing so could
 1144          * lead to bad things.
 1145          *
 1146          * If successful, fr_stlookup returns with ipf_state locked.  We have
 1147          * no use for this lock, so simply unlock it if necessary.
 1148          */
 1149         is = ipf_state_lookup(&fi, &tcp, NULL);
 1150         if (is != NULL) {
 1151                 RWLOCK_EXIT(&softc->ipf_state);
 1152         }
 1153 
 1154         RWLOCK_EXIT(&softc->ipf_nat);
 1155 
 1156         WRITE_ENTER(&softc->ipf_nat);
 1157         natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst);
 1158 
 1159         if ((natl != NULL) && (is != NULL)) {
 1160                 MUTEX_DOWNGRADE(&softc->ipf_nat);
 1161                 return (0);
 1162         }
 1163 
 1164         /* Slightly modify the following structures for actual use in creating
 1165          * NAT and/or state entries.  We're primarily concerned with stripping
 1166          * flags that may be detrimental to the creation process or simply
 1167          * shouldn't be associated with a table entry.
 1168          */
 1169         fi.fin_fr = &rpcbfr;
 1170         fi.fin_flx &= ~FI_IGNORE;
 1171         nflags &= ~NAT_SEARCH;
 1172 
 1173         if (natl == NULL) {
 1174 #ifdef USE_MUTEXES
 1175                 ipf_nat_softc_t *softn = softc->ipf_nat_soft;
 1176 #endif
 1177 
 1178                 /* XXX Since we're just copying the original ipn contents
 1179                  * back, would we be better off just sending a pointer to
 1180                  * the 'temp' copy off to nat_new instead?
 1181                  */
 1182                 /* Generate template/bogus NAT rule. */
 1183                 bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat));
 1184                 ipn->in_flags = nflags & IPN_TCPUDP;
 1185                 ipn->in_apr = NULL;
 1186                 ipn->in_pr[0] = proto;
 1187                 ipn->in_pr[1] = proto;
 1188                 ipn->in_dpmin = fi.fin_dport;
 1189                 ipn->in_dpmax = fi.fin_dport;
 1190                 ipn->in_dpnext = fi.fin_dport;
 1191                 ipn->in_space = 1;
 1192                 ipn->in_ippip = 1;
 1193                 if (ipn->in_flags & IPN_FILTER) {
 1194                         ipn->in_scmp = 0;
 1195                         ipn->in_dcmp = 0;
 1196                 }
 1197                 ipn->in_plabel = -1;
 1198 
 1199                 /* Create NAT entry.  return NULL if this fails. */
 1200                 MUTEX_ENTER(&softn->ipf_nat_new);
 1201                 natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE,
 1202                                NAT_INBOUND);
 1203                 MUTEX_EXIT(&softn->ipf_nat_new);
 1204 
 1205                 bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat));
 1206 
 1207                 if (natl == NULL) {
 1208                         MUTEX_DOWNGRADE(&softc->ipf_nat);
 1209                         return (-1);
 1210                 }
 1211 
 1212                 natl->nat_ptr = ipn;
 1213                 fi.fin_saddr = natl->nat_nsrcaddr;
 1214                 fi.fin_daddr = natl->nat_ndstaddr;
 1215                 ipn->in_use++;
 1216                 (void) ipf_nat_proto(&fi, natl, nflags);
 1217                 MUTEX_ENTER(&natl->nat_lock);
 1218                 ipf_nat_update(&fi, natl);
 1219                 MUTEX_EXIT(&natl->nat_lock);
 1220         }
 1221         MUTEX_DOWNGRADE(&softc->ipf_nat);
 1222 
 1223         if (is == NULL) {
 1224                 /* Create state entry.  Return NULL if this fails. */
 1225                 fi.fin_flx |= FI_NATED;
 1226                 fi.fin_flx &= ~FI_STATE;
 1227                 nflags &= NAT_TCPUDP;
 1228                 nflags |= SI_W_SPORT|SI_CLONE;
 1229 
 1230                 if (ipf_state_add(softc, &fi, NULL, nflags) != 0) {
 1231                         /*
 1232                          * XXX nat_delete is private to ip_nat.c.  Should
 1233                          * check w/ Darren about this one.
 1234                          *
 1235                          * nat_delete(natl, NL_EXPIRE);
 1236                          */
 1237                         return (-1);
 1238                 }
 1239         }
 1240 
 1241         return (0);
 1242 }
 1243 
 1244 /* -------------------------------------------------------------------- */
 1245 /* Function:    ipf_p_rpcb_modv3                                                */
 1246 /* Returns:     int -- change in packet length                          */
 1247 /* Parameters:  fin(I)  - pointer to packet information                 */
 1248 /*              nat(I)  - pointer to NAT session                        */
 1249 /*              rm(I)   - pointer to RPC message structure              */
 1250 /*              m(I)    - pointer to mbuf chain                         */
 1251 /*              off(I)  - offset within mbuf chain                      */
 1252 /*                                                                      */
 1253 /* Write a new universal address string to this packet, adjusting       */
 1254 /* lengths as necessary.                                                */
 1255 /* -------------------------------------------------------------------- */
 1256 static int
 1257 ipf_p_rpcb_modv3(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m,
 1258         u_int off)
 1259 {
 1260         u_int len, xlen, pos, bogo;
 1261         rpc_resp_t *rr;
 1262         char uaddr[24];
 1263         char *i, *p;
 1264         int diff;
 1265 
 1266         rr = &rm->rm_resp;
 1267         i = (char *)&nat->nat_ndstaddr;
 1268         p = (char *)&rr->rr_v3.xu_port;
 1269 
 1270         /* Form new string. */
 1271         bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */
 1272         (void) snprintf(uaddr, sizeof(uaddr),
 1273                        "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff,
 1274                        i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff);
 1275         len = strlen(uaddr);
 1276         xlen = XDRALIGN(len);
 1277 
 1278         /* Determine mbuf offset to write to. */
 1279         pos = (char *)rr->rr_v3.xu_xslen - rm->rm_msgbuf;
 1280         off += pos;
 1281 
 1282         /* Write new string length. */
 1283         bogo = htonl(len);
 1284         COPYBACK(m, off, 4, (caddr_t)&bogo);
 1285         off += 4;
 1286 
 1287         /* Write new string. */
 1288         COPYBACK(m, off, xlen, uaddr);
 1289 
 1290         /* Determine difference in data lengths. */
 1291         diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen));
 1292 
 1293         /*
 1294          * If our new string has a different length, make necessary
 1295          * adjustments.
 1296          */
 1297         if (diff != 0)
 1298                 ipf_p_rpcb_fixlen(fin, diff);
 1299 
 1300         return (diff);
 1301 }
 1302 
 1303 /* -------------------------------------------------------------------- */
 1304 /* Function:    ipf_p_rpcb_modv4                                                */
 1305 /* Returns:     int -- change in packet length                          */
 1306 /* Parameters:  fin(I)  - pointer to packet information                 */
 1307 /*              nat(I)  - pointer to NAT session                        */
 1308 /*              rm(I)   - pointer to RPC message structure              */
 1309 /*              m(I)    - pointer to mbuf chain                         */
 1310 /*              off(I)  - offset within mbuf chain                      */
 1311 /*                                                                      */
 1312 /* Write new rpcb_entry list, adjusting lengths as necessary.           */
 1313 /* -------------------------------------------------------------------- */
 1314 static int
 1315 ipf_p_rpcb_modv4(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m,
 1316         u_int off)
 1317 {
 1318         u_int len, xlen, pos, bogo;
 1319         rpcb_listp_t *rl;
 1320         rpcb_entry_t *re;
 1321         rpc_resp_t *rr;
 1322         char uaddr[24];
 1323         int diff, cnt;
 1324         char *i, *p;
 1325 
 1326         diff = 0;
 1327         rr = &rm->rm_resp;
 1328         rl = &rr->rr_v4;
 1329 
 1330         i = (char *)&nat->nat_ndstaddr;
 1331 
 1332         /* Determine mbuf offset to write to. */
 1333         re = &rl->rl_entries[0];
 1334         pos = (char *)re->re_maddr.xu_xslen - rm->rm_msgbuf;
 1335         off += pos;
 1336 
 1337         for (cnt = 0; cnt < rl->rl_cnt; cnt++) {
 1338                 re = &rl->rl_entries[cnt];
 1339                 p = (char *)&re->re_maddr.xu_port;
 1340 
 1341                 /* Form new string. */
 1342                 bzero(uaddr, sizeof(uaddr)); /* Just in case we need
 1343                                                 padding. */
 1344                 (void) snprintf(uaddr, sizeof(uaddr),
 1345                                "%u.%u.%u.%u.%u.%u", i[0] & 0xff,
 1346                                i[1] & 0xff, i[2] & 0xff, i[3] & 0xff,
 1347                                p[0] & 0xff, p[1] & 0xff);
 1348                 len = strlen(uaddr);
 1349                 xlen = XDRALIGN(len);
 1350 
 1351                 /* Write new string length. */
 1352                 bogo = htonl(len);
 1353                 COPYBACK(m, off, 4, (caddr_t)&bogo);
 1354                 off += 4;
 1355 
 1356                 /* Write new string. */
 1357                 COPYBACK(m, off, xlen, uaddr);
 1358                 off += xlen;
 1359 
 1360                 /* Record any change in length. */
 1361                 diff += xlen - XDRALIGN(B(re->re_maddr.xu_xslen));
 1362 
 1363                 /* If the length changed, copy back the rest of this entry. */
 1364                 len = ((char *)re->re_more + 4) -
 1365                        (char *)re->re_netid.xp_xslen;
 1366                 if (diff != 0) {
 1367                         COPYBACK(m, off, len, (caddr_t)re->re_netid.xp_xslen);
 1368                 }
 1369                 off += len;
 1370         }
 1371 
 1372         /*
 1373          * If our new string has a different length, make necessary
 1374          * adjustments.
 1375          */
 1376         if (diff != 0)
 1377                 ipf_p_rpcb_fixlen(fin, diff);
 1378 
 1379         return (diff);
 1380 }
 1381 
 1382 
 1383 /* -------------------------------------------------------------------- */
 1384 /* Function:    ipf_p_rpcb_fixlen                                        */
 1385 /* Returns:     (void)                                                  */
 1386 /* Parameters:  fin(I)  - pointer to packet information                 */
 1387 /*              len(I)  - change in packet length                       */
 1388 /*                                                                      */
 1389 /* Adjust various packet related lengths held in structure and packet   */
 1390 /* header fields.                                                       */
 1391 /* -------------------------------------------------------------------- */
 1392 static void
 1393 ipf_p_rpcb_fixlen(fr_info_t *fin, int len)
 1394 {
 1395         udphdr_t *udp;
 1396 
 1397         udp = fin->fin_dp;
 1398         udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len);
 1399         fin->fin_plen += len;
 1400         fin->fin_ip->ip_len = htons(fin->fin_plen);
 1401         fin->fin_dlen += len;
 1402 }
 1403 
 1404 #undef B

Cache object: 3fc63571de11c743e9b22630b4cc16fc


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.