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 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2012 David Hoeppner.  All rights reserved.
  29  */
  30 
  31 /*
  32  * This file contains functions related to getting and setting options
  33  * thought the getsockopt and setsockopt socket functions.
  34  */
  35 
  36 #include <sys/types.h>
  37 #include <sys/stream.h>
  38 #define _SUN_TPI_VERSION 2
  39 #include <sys/tihdr.h>
  40 #include <sys/xti_xtiopt.h>
  41 #include <sys/xti_inet.h>
  42 #include <sys/policy.h>
  43 
  44 #include <inet/common.h>
  45 #include <inet/dccp_impl.h>
  46 #include <inet/ip.h>
  47 #include <inet/optcom.h>
  48 #include <netinet/ip.h>
  49 
  50 #include <sys/cmn_err.h>
  51 
  52 static int dccp_opt_default(queue_t *, int, int, uchar_t *);
  53 
  54 /*
  55  * Supported options.
  56  */
  57 opdes_t dccp_opt_arr[] = {
  58 { SO_DEBUG, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
  59 }; 
  60 
  61 /*
  62  * Supported levels.
  63  */
  64 optlevel_t dccp_valid_levels_arr[] = {
  65         SOL_SOCKET,
  66 };
  67 
  68 #define DCCP_OPT_ARR_CNT        A_CNT(dccp_opt_arr)
  69 #define DCCP_VALID_LEVELS_CNT   A_CNT(dccp_valid_levels_arr)
  70 
  71 uint_t dccp_max_optsize;
  72 
  73 /*
  74  * Options database object.
  75  */
  76 optdb_obj_t dccp_opt_obj = {
  77         dccp_opt_default,
  78         dccp_tpi_opt_get,
  79         dccp_tpi_opt_set,
  80         DCCP_OPT_ARR_CNT,
  81         dccp_opt_arr,
  82         DCCP_VALID_LEVELS_CNT,
  83         dccp_valid_levels_arr,
  84 };
  85 
  86 /*
  87  * Default value for certain options.
  88  */
  89 int
  90 dccp_opt_default(queue_t *q, int level, int name, uchar_t *ptr)
  91 {
  92         dccp_stack_t    *dccps = Q_TO_DCCP(q)->dccp_dccps;
  93         int32_t         *il = (int32_t *)ptr;
  94 
  95         return (sizeof (int));
  96 }
  97 
  98 int
  99 dccp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr)
 100 {
 101         dccp_t          *dccp = connp->conn_dccp;
 102         conn_opt_arg_t  coas;
 103         int             *i1 = (int *)ptr;
 104         int             retval;
 105 
 106         coas.coa_connp = connp;
 107         coas.coa_ixa = connp->conn_ixa;
 108         coas.coa_ipp = &connp->conn_xmit_ipp;
 109         coas.coa_ancillary = B_FALSE;
 110         coas.coa_changed = 0;
 111 
 112         switch (level) {
 113         case SOL_SOCKET:
 114                 break;
 115         case IPPROTO_DCCP:
 116                 switch (name) {
 117                 case DCCP_NOTIFY_THRESHOLD:
 118                         *i1 = dccp->dccp_first_timer_threshold;
 119                         return (sizeof (int));
 120                 case DCCP_ABORT_THRESHOLD:
 121                         *i1 = dccp->dccp_second_timer_threshold;
 122                         return (sizeof (int));
 123                 case DCCP_CONN_NOTIFY_THRESHOLD:
 124                         *i1 = dccp->dccp_first_ctimer_threshold;
 125                         return (sizeof (int));
 126                 case DCCP_CONN_ABORT_THRESHOLD:
 127                         *i1 = dccp->dccp_second_ctimer_threshold;
 128                         return (sizeof (int));
 129                 case DCCP_KEEPALIVE_THRESHOLD:
 130                         *i1 = dccp->dccp_ka_interval;
 131                         return (sizeof (int));
 132                 case DCCP_KEEPINTVL:
 133                         *i1 = dccp->dccp_ka_rinterval / 1000;
 134                         return (sizeof (int));
 135                 }
 136                 break;
 137         case IPPROTO_IP:
 138                 break;
 139         case IPPROTO_IPV6:
 140                 break;
 141         }
 142 
 143         mutex_enter(&connp->conn_lock);
 144         retval = conn_opt_get(&coas, level, name, ptr);
 145         mutex_exit(&connp->conn_lock);
 146 
 147         return (retval);
 148 }
 149 
 150 /* ARGSUSED */
 151 int
 152 dccp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
 153     uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
 154     void *thisdg_attrs, cred_t *cr)
 155 {
 156         dccp_t          *dccp = connp->conn_dccp;
 157         dccp_stack_t    *dccps = dccp->dccp_dccps;
 158         conn_opt_arg_t  coas;
 159         uint32_t        val = *((uint32_t *)invalp);
 160         int             *i1 = (int *)invalp;
 161         boolean_t       onoff = (*i1 == 0) ? B_FALSE : B_TRUE;
 162         boolean_t       checkonly = B_FALSE;
 163         int             error;
 164 
 165         coas.coa_connp = connp;
 166         coas.coa_ixa = connp->conn_ixa;
 167         coas.coa_ipp = &connp->conn_xmit_ipp;
 168         coas.coa_ancillary = B_FALSE;
 169         coas.coa_changed = 0;
 170 
 171         switch (optset_context) {
 172         case SETFN_OPTCOM_CHECKONLY:
 173                 checkonly = B_TRUE;
 174                 if (inlen = 0) {
 175                         *outlenp = 0;
 176                         return (0);
 177                 }
 178                 break;
 179         case SETFN_OPTCOM_NEGOTIATE:
 180                 break;
 181         case SETFN_UD_NEGOTIATE:
 182         case SETFN_CONN_NEGOTIATE:
 183                 break;
 184         default:
 185                 /*
 186                  * We should never get here.
 187                  */
 188                 *outlenp = 0;
 189                 return (EINVAL);
 190         }
 191 
 192         ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
 193             (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
 194 
 195         /*
 196          * No ancillary data should be sent down.
 197          */
 198         ASSERT(thisdg_attrs == NULL);
 199 
 200         switch (level) {
 201         case SOL_SOCKET:
 202                 switch (name) {
 203                 case SO_KEEPALIVE:
 204                         if (checkonly) {
 205                                 /* Check only case */
 206                                 break;
 207                         }
 208 
 209                         if (!onoff) {
 210                                 if (connp->conn_keepalive) {
 211                                         if (dccp->dccp_ka_tid != 0) {
 212                                                 (void) DCCP_TIMER_CANCEL(dccp,
 213                                                     dccp->dccp_ka_tid);
 214                                                 dccp->dccp_ka_tid = 0;
 215                                         }
 216                                         connp->conn_keepalive = 0;
 217                                 }
 218                                 break;
 219                         }
 220 
 221                         if (!connp->conn_keepalive) {
 222                                 /* Crank up the keepalive timer */
 223                                 dccp->dccp_ka_last_intrvl = 0;
 224                                 dccp->dccp_ka_tid = DCCP_TIMER(dccp,
 225                                     dccp_keepalive_timer, dccp->dccp_ka_interval);
 226                                 connp->conn_keepalive = 1;
 227                         }
 228                         break;
 229                 }
 230                 break;
 231         case IPPROTO_DCCP:
 232                 switch (name) {
 233                 case DCCP_NOTIFY_THRESHOLD:
 234                         if (!checkonly)
 235                                 dccp->dccp_first_timer_threshold = *i1;
 236                         break;
 237                 case DCCP_ABORT_THRESHOLD:
 238                         if (!checkonly)
 239                                 dccp->dccp_second_timer_threshold = *i1;
 240                 case DCCP_CONN_NOTIFY_THRESHOLD:
 241                         if (!checkonly)
 242                                 dccp->dccp_first_ctimer_threshold = *i1;
 243                         break;
 244                 case DCCP_CONN_ABORT_THRESHOLD:
 245                         if (!checkonly)
 246                                 dccp->dccp_second_ctimer_threshold = *i1;
 247                         break;
 248                 case DCCP_KEEPIDLE:
 249                         *i1 *= 1000;
 250                         /* FALLTHRU */
 251                 case DCCP_KEEPALIVE_THRESHOLD:
 252                         if (checkonly)
 253                                 break;
 254 
 255                         if (*i1 < dccps->dccps_keepalive_interval_low ||
 256                             *i1 > dccps->dccps_keepalive_interval_high) {
 257                                 *outlenp = 0;
 258                                 return (EINVAL);
 259                         }
 260 
 261                         if (*i1 != dccp->dccp_ka_interval) {
 262                                 dccp->dccp_ka_interval = *i1;
 263 
 264                                 if (dccp->dccp_ka_tid != 0) {
 265                                         ASSERT(connp->conn_keepalive);
 266                                         (void) DCCP_TIMER_CANCEL(dccp,
 267                                             dccp->dccp_ka_tid);
 268                                         dccp->dccp_ka_last_intrvl = 0;
 269                                         dccp->dccp_ka_tid = DCCP_TIMER(dccp,
 270                                             dccp_keepalive_timer,
 271                                             dccp->dccp_ka_interval);
 272                                 }
 273                         }
 274                         break;
 275                 }
 276                 break;
 277         case IPPROTO_IP:
 278                 break;
 279         case IPPROTO_IPV6:
 280                 break;
 281         }
 282 
 283         error = conn_opt_set(&coas, level, name, inlen, invalp,
 284             B_FALSE, cr);
 285         if (error !=0) {
 286                 *outlenp = 0;
 287                 return (error);
 288         }
 289 
 290         /*
 291          * Common case of OK return with outval same as inval.
 292          */
 293         if (invalp != outvalp) {
 294                 (void) bcopy(invalp, outvalp, inlen);
 295         }
 296         *outlenp = inlen;
 297 
 298         if (coas.coa_changed && COA_HEADER_CHANGED) {
 299                 /* If we are connected we rebuilt the headers */
 300                 if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) &&
 301                     !IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
 302                         error = dccp_build_hdrs(dccp);
 303                         if (error != 0) {
 304                                 return (error);
 305                         }
 306                 }
 307         }
 308 
 309         if (coas.coa_changed & COA_ROUTE_CHANGED) {
 310                 in6_addr_t      nexthop;
 311 
 312                 ip_attr_nexthop(&connp->conn_xmit_ipp, connp->conn_ixa,
 313                     &connp->conn_faddr_v6, &nexthop);
 314 
 315                 if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) &&
 316                     !IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
 317                         (void) ip_attr_connect(connp, connp->conn_ixa,
 318                             &connp->conn_laddr_v6, &connp->conn_faddr_v6,
 319                             &nexthop, connp->conn_fport, NULL, NULL,
 320                             IPDF_VERIFY_DST);
 321                 }
 322         }
 323 
 324         /* XXX */
 325 
 326         return (0);
 327 }