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 }