1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 1990 Mentat Inc.
  24  */
  25 
  26 #include <sys/ib/clients/rds/rds.h>
  27 #include <inet/proto_set.h>
  28 
  29 #define rds_max_buf 2097152
  30 opdes_t rds_opt_arr[] = {
  31 
  32 { SO_TYPE,      SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
  33 { SO_SNDBUF,    SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
  34 { SO_RCVBUF,    SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
  35 };
  36 
  37 /* ARGSUSED */
  38 int
  39 rds_opt_default(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
  40 {
  41         /* no default value processed by protocol specific code currently */
  42         return (-1);
  43 }
  44 
  45 /*
  46  * This routine retrieves the current status of socket options.
  47  * It returns the size of the option retrieved.
  48  */
  49 int
  50 rds_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
  51 {
  52         int     *i1 = (int *)(uintptr_t)ptr;
  53 
  54         switch (level) {
  55         case SOL_SOCKET:
  56                 switch (name) {
  57                 case SO_TYPE:
  58                         *i1 = SOCK_DGRAM;
  59                         break;  /* goto sizeof (int) option return */
  60 
  61                 case SO_SNDBUF:
  62                         *i1 = q->q_hiwat;
  63                         break;  /* goto sizeof (int) option return */
  64                 case SO_RCVBUF:
  65                         *i1 = RD(q)->q_hiwat;
  66                         break;  /* goto sizeof (int) option return */
  67                 default:
  68                         return (-1);
  69                 }
  70                 break;
  71         default:
  72                 return (-1);
  73         }
  74         return (sizeof (int));
  75 }
  76 
  77 /* This routine sets socket options. */
  78 /* ARGSUSED */
  79 int
  80 rds_opt_set(queue_t *q, uint_t optset_context, int level,
  81     int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
  82     uchar_t *outvalp, void *thisdg_attrs, cred_t *cr)
  83 {
  84         int     *i1 = (int *)(uintptr_t)invalp;
  85         boolean_t checkonly;
  86 
  87         switch (optset_context) {
  88         case SETFN_OPTCOM_CHECKONLY:
  89                 checkonly = B_TRUE;
  90                 /*
  91                  * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
  92                  * inlen != 0 implies value supplied and
  93                  *      we have to "pretend" to set it.
  94                  * inlen == 0 implies that there is no
  95                  *      value part in T_CHECK request and just validation
  96                  * done elsewhere should be enough, we just return here.
  97                  */
  98                 if (inlen == 0) {
  99                         *outlenp = 0;
 100                         return (0);
 101                 }
 102                 break;
 103         case SETFN_OPTCOM_NEGOTIATE:
 104                 checkonly = B_FALSE;
 105                 break;
 106         default:
 107                 /*
 108                  * We should never get here
 109                  */
 110                 *outlenp = 0;
 111                 return (EINVAL);
 112         }
 113 
 114         ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
 115             (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
 116 
 117         /*
 118          * For fixed length options, no sanity check
 119          * of passed in length is done. It is assumed *_optcom_req()
 120          * routines do the right thing.
 121          */
 122 
 123         switch (level) {
 124         case SOL_SOCKET:
 125                 switch (name) {
 126 
 127                 case SO_SNDBUF:
 128                         if (*i1 > rds_max_buf) {
 129                                 *outlenp = 0;
 130                                 return (ENOBUFS);
 131                         }
 132                         if (!checkonly) {
 133                                 q->q_hiwat = *i1;
 134                         }
 135                         break;
 136                 case SO_RCVBUF:
 137                         if (*i1 > rds_max_buf) {
 138                                 *outlenp = 0;
 139                                 return (ENOBUFS);
 140                         }
 141                         if (!checkonly) {
 142                                 RD(q)->q_hiwat = *i1;
 143                                 (void) proto_set_rx_hiwat(RD(q), NULL, *i1);
 144                         }
 145                         break;
 146                 default:
 147                         *outlenp = 0;
 148                         return (EINVAL);
 149                 }
 150                 break;
 151         default:
 152                 *outlenp = 0;
 153                 return (EINVAL);
 154         }
 155         /*
 156          * Common case of OK return with outval same as inval.
 157          */
 158         if (invalp != outvalp) {
 159                 /* don't trust bcopy for identical src/dst */
 160                 (void) bcopy(invalp, outvalp, inlen);
 161         }
 162         *outlenp = inlen;
 163         return (0);
 164 }
 165 
 166 uint_t rds_max_optsize; /* initialized when RDS driver is loaded */
 167 
 168 #define RDS_VALID_LEVELS_CNT    A_CNT(rds_valid_levels_arr)
 169 
 170 #define RDS_OPT_ARR_CNT         A_CNT(rds_opt_arr)
 171 
 172 
 173 optlevel_t rds_valid_levels_arr[] = {
 174         SOL_SOCKET,
 175 };
 176 
 177 /*
 178  * Initialize option database object for RDS
 179  *
 180  * This object represents database of options to search passed to
 181  * {sock,tpi}optcom_req() interface routine to take care of option
 182  * management and associated methods.
 183  */
 184 
 185 optdb_obj_t rds_opt_obj = {
 186         rds_opt_default,        /* RDS default value function pointer */
 187         rds_opt_get,            /* RDS get function pointer */
 188         rds_opt_set,            /* RDS set function pointer */
 189         RDS_OPT_ARR_CNT,        /* RDS option database count of entries */
 190         rds_opt_arr,            /* RDS option database */
 191         RDS_VALID_LEVELS_CNT,   /* RDS valid level count of entries */
 192         rds_valid_levels_arr    /* RDS valid level array */
 193 };