1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2012 David Hoeppner. All rights reserved.
  14  */
  15 
  16 #include <sys/types.h> 
  17 #include <sys/stream.h> 
  18 #include <sys/debug.h> 
  19 #include <sys/cmn_err.h>
  20 #include <inet/dccp_impl.h>
  21 #include <inet/dccp_stack.h>
  22 
  23 /*
  24  * This file contains functions to parse and process DCCP options.
  25  */
  26 
  27 
  28 /*
  29  * Parse the options in a DCCP header.
  30  */
  31 int
  32 dccp_parse_options(dccp_t *dccp, dccpha_t *dccpha)
  33 {
  34         uchar_t         *end;
  35         uchar_t         *up;
  36         uint8_t         dccp_type;
  37         uint32_t        option_value;
  38         uint8_t         option_type;
  39         uint8_t         option_length;
  40         int             len;
  41         int             i;
  42         uchar_t         *value;
  43         boolean_t       mandatory = B_FALSE;
  44         int             error;
  45 
  46         cmn_err(CE_NOTE, "dccp_features.c: dccp_parse_options");
  47 
  48         dccp_type = dccpha->dha_type;
  49 
  50         up = (uchar_t *)dccpha;
  51         end = up + DCCP_HDR_LENGTH(dccpha);
  52         up += 20;
  53 
  54         while (up != end) {
  55                 option_length = 0;
  56                 option_type = *up++;
  57 
  58                 if (option_type > 31) {
  59                         if (up == end) {
  60                                 goto length_error;
  61                         }
  62 
  63                         option_length = *up++;
  64                         if (option_length < 2) {
  65                                 goto length_error;
  66                         }
  67 
  68                         option_length -= 2;
  69                         value = up;
  70 
  71                         up += option_length;
  72 
  73                         /* Ignore options with greater length then header */
  74                         if (up > end) {
  75                                 goto length_error;
  76                         }
  77                 }
  78 
  79                 switch (option_type) {
  80                 case DCCP_OPTION_PADDING:
  81                         cmn_err(CE_NOTE, "PADDING");
  82                         break;
  83                 case DCCP_OPTION_MANDATORY:
  84                         cmn_err(CE_NOTE, "MANDATORY");
  85                         if (mandatory)
  86                                 goto option_error;
  87 
  88                         if (dccp_type != DCCP_PKT_DATA)
  89                                 mandatory = B_TRUE;
  90                         break;
  91                 case DCCP_OPTION_SLOW_RECEIVER:
  92                         cmn_err(CE_NOTE, "SLOW RECEIVER");
  93                         break;
  94                 case DCCP_OPTION_CHANGE_L:
  95                 case DCCP_OPTION_CONFIRM_L:
  96                 case DCCP_OPTION_CHANGE_R:
  97                 case DCCP_OPTION_CONFIRM_R:
  98                         if (dccp_type == DCCP_PKT_DATA)
  99                                 break;
 100 
 101                         if (option_length == 0)
 102                                 goto option_error;
 103 
 104                         dccp_parse_feature(dccp, option_type, option_length,
 105                             value, mandatory);
 106                         break;
 107                 case DCCP_OPTION_INIT_COOKIE:
 108                         cmn_err(CE_NOTE, "INIT COOKIE");
 109                         break;
 110                 case DCCP_OPTION_NDP_COUNT:
 111                         cmn_err(CE_NOTE, "NDP COUNT");
 112                         if (option_length > 6)
 113                                 goto option_error;
 114                         break;
 115                 case DCCP_OPTION_ACK_VECTOR_1:
 116                         cmn_err(CE_NOTE, "ACK VECTOR 1");
 117                         break;
 118                 case DCCP_OPTION_ACK_VECTOR_2:
 119                         cmn_err(CE_NOTE, "ACK VECTOR 2");
 120                         break;
 121                 case DCCP_OPTION_DATA_DROPPED:
 122                         cmn_err(CE_NOTE, "DATA DROPPED");
 123                         break;
 124                 case DCCP_OPTION_TIMESTAMP:
 125                         cmn_err(CE_NOTE, "TIMESTAMP");
 126                         if (option_length != 4)
 127                                 goto option_error;
 128 
 129                         /* XXX read unaligned big endian */
 130                         option_value = ((uint8_t)value[0] << 31);
 131                         if (option_value) {
 132                                 cmn_err(CE_NOTE, "Zero timestamp");
 133                                 break;
 134                         }
 135 
 136                         dccp->dccp_timestamp_echo = ntohs(option_value);
 137                         dccp->dccp_timestamp = gethrtime();
 138                         break;
 139                 case DCCP_OPTION_TIMESTAMP_ECHO:
 140                         cmn_err(CE_NOTE, "TIMESTAMP ECHO");
 141                         if (option_length != 4 &&
 142                             option_length != 6 &&
 143                             option_length != 8) {
 144                                 goto option_error;
 145                         }
 146 
 147                         break;
 148                 case DCCP_OPTION_ELAPSED_TIME:
 149                         cmn_err(CE_NOTE, "ELAPSES TIME");
 150                         switch (option_length) {
 151                         case 2:
 152                                 break;
 153                         case 4:
 154                                 break;
 155                         default:
 156                                 goto option_error;
 157                         }
 158                         break;
 159                 case DCCP_OPTION_DATA_CHECKSUM:
 160                         cmn_err(CE_NOTE, "DATA CHECKSUM");
 161                         break;
 162 
 163                 default:
 164                         cmn_err(CE_NOTE, "DEFAULT");
 165                         break;
 166                 }
 167 
 168                 if (option_type != DCCP_OPTION_MANDATORY) {
 169                         mandatory = B_FALSE;
 170                 }
 171         }
 172 
 173         if (mandatory)
 174                 goto option_error;
 175 
 176 length_error:
 177         return (0);
 178 
 179 option_error:
 180         error = DCCP_RESET_OPTION_ERROR;
 181 
 182         cmn_err(CE_NOTE, "setting error code");
 183 
 184         dccp->dccp_reset_code = error;
 185         dccp->dccp_reset_data[0] = option_type;
 186         dccp->dccp_reset_data[1] = option_length > 0 ? value[0] : 0;
 187         dccp->dccp_reset_data[2] = option_length > 1 ? value[1] : 0;
 188 
 189         return (-1);
 190 }
 191 
 192 void
 193 dccp_process_options(dccp_t *dccp, dccpha_t *dccpha)
 194 {
 195         cmn_err(CE_NOTE, "dccp_features.c: dccp_process_features");
 196 
 197         dccp_parse_options(dccp, dccpha);
 198 }
 199 
 200 int
 201 dccp_generate_options(dccp_t *dccp, void **opt, size_t *opt_len)
 202 {
 203         dccp_feature_t  *feature = NULL;
 204         uint8_t         buf[1024];      /* XXX */
 205         uint8_t         option_type;
 206         uint_t          len = 0;
 207         uint_t          total_len;
 208         void            *options;
 209         int             rest;
 210 
 211         cmn_err(CE_NOTE, "dccp_features.c: dccp_generate_options");
 212 
 213         for (feature = list_head(&dccp->dccp_features); feature;
 214             feature = list_next(&dccp->dccp_features, feature)) {
 215                 if (feature->df_option == DCCP_OPTION_CHANGE_L) {
 216                         option_type = DCCP_OPTION_CONFIRM_R;
 217                 } else {
 218                         option_type = DCCP_OPTION_CONFIRM_L;
 219                 }
 220 /*
 221                 if (feature->df_mandatory == B_TRUE) {
 222                         buf[len] = DCCP_OPTION_MANDATORY;
 223                         len++;
 224                 }
 225 */
 226                 if (feature->df_type == DCCP_FEATURE_CCID) {
 227                         cmn_err(CE_NOTE, "FOUND DCCP_FEATURE_CCID");
 228 
 229                         buf[len] = option_type;
 230                         len++;
 231                         buf[len] = 4;
 232                         len++;
 233                         buf[len] = DCCP_FEATURE_CCID;
 234                         len++;
 235                         buf[len] = 2;
 236                         len++;
 237                 }
 238 
 239                 if (feature->df_type == DCCP_FEATURE_ALLOW_SHORT_SEQNOS) {
 240                         buf[len] = option_type;
 241                         len++;
 242                         buf[len] = 4;
 243                         len++;
 244                         buf[len] = feature->df_type;
 245                         len++;
 246                         buf[len] = 0;
 247                         len++;
 248                 }
 249 
 250                 if (feature->df_type == DCCP_FEATURE_ECN_INCAPABLE) {
 251                         buf[len] = option_type;
 252                         len++;
 253                         buf[len] = 4;
 254                         len++;
 255                         buf[len] = feature->df_type;
 256                         len++;
 257                         buf[len] = 1;
 258                         len++;
 259                 }
 260         }
 261 
 262         if (dccp->dccp_timestamp_echo != 0) {
 263                 uint32_t        elapsed;
 264                 int             elapsed_length;
 265 
 266                 buf[len] = DCCP_OPTION_TIMESTAMP_ECHO;
 267                 len++;
 268 
 269                 elapsed = gethrtime() - dccp->dccp_timestamp;
 270 
 271                 dccp->dccp_timestamp_echo = 0;
 272         }
 273 
 274         total_len = ((len + (4 - 1)) / 4) * 4;
 275         options = kmem_zalloc(total_len, KM_SLEEP);
 276         if (options == NULL) {
 277                 cmn_err(CE_NOTE, "kmem_zalloc failed");
 278                 return (ENOMEM);
 279         }
 280         memcpy(options, buf, len);
 281 
 282         *opt = options;
 283         *opt_len = len;
 284 
 285         return (0);
 286 }