source: mainline/uspace/srv/net/tcp/pdu.c@ 02a09ed

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 02a09ed was 02a09ed, checked in by Martin Decky <martin@…>, 12 years ago

add basic infrastructure for IPv6 (inactive)
make inet_addr_t a universal address type

  • Property mode set to 100644
File size: 8.1 KB
RevLine 
[c5808b41]1/*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup tcp
30 * @{
31 */
32
33/**
[032bbe7]34 * @file TCP header encoding and decoding
[c5808b41]35 */
36
[6896409c]37#include <bitops.h>
[c5808b41]38#include <byteorder.h>
[eea65f4]39#include <errno.h>
40#include <mem.h>
41#include <stdlib.h>
[02a09ed]42#include <net/socket_codes.h>
[762b48a]43#include "pdu.h"
[c5808b41]44#include "segment.h"
[6896409c]45#include "seq_no.h"
[c5808b41]46#include "std.h"
47#include "tcp_type.h"
48
[eea65f4]49#define TCP_CHECKSUM_INIT 0xffff
50
[0ac2158]51/** One's complement addition.
52 *
53 * Result is a + b + carry.
54 */
55static uint16_t tcp_ocadd16(uint16_t a, uint16_t b)
56{
57 uint32_t s;
58
59 s = (uint32_t)a + (uint32_t)b;
60 return (s & 0xffff) + (s >> 16);
61}
62
[eea65f4]63static uint16_t tcp_checksum_calc(uint16_t ivalue, void *data, size_t size)
64{
65 uint16_t sum;
[0ac2158]66 uint16_t w;
[eea65f4]67 size_t words, i;
68 uint8_t *bdata;
69
70 sum = ~ivalue;
71 words = size / 2;
72 bdata = (uint8_t *)data;
73
74 for (i = 0; i < words; i++) {
[0ac2158]75 w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
76 sum = tcp_ocadd16(sum, w);
[eea65f4]77 }
78
79 if (size % 2 != 0) {
[0ac2158]80 w = ((uint16_t)bdata[2*words] << 8);
81 sum = tcp_ocadd16(sum, w);
[eea65f4]82 }
83
84 return ~sum;
85}
86
87static void tcp_header_decode_flags(uint16_t doff_flags, tcp_control_t *rctl)
[c5808b41]88{
[eea65f4]89 tcp_control_t ctl;
90
91 ctl = 0;
92
[6896409c]93 if ((doff_flags & BIT_V(uint16_t, DF_URG)) != 0)
[eea65f4]94 ctl |= 0 /* XXX */;
[6896409c]95 if ((doff_flags & BIT_V(uint16_t, DF_ACK)) != 0)
[eea65f4]96 ctl |= CTL_ACK;
[6896409c]97 if ((doff_flags & BIT_V(uint16_t, DF_PSH)) != 0)
[eea65f4]98 ctl |= 0 /* XXX */;
[6896409c]99 if ((doff_flags & BIT_V(uint16_t, DF_RST)) != 0)
[eea65f4]100 ctl |= CTL_RST;
[6896409c]101 if ((doff_flags & BIT_V(uint16_t, DF_SYN)) != 0)
[eea65f4]102 ctl |= CTL_SYN;
[6896409c]103 if ((doff_flags & BIT_V(uint16_t, DF_FIN)) != 0)
[eea65f4]104 ctl |= CTL_FIN;
105
106 *rctl = ctl;
107}
108
109static void tcp_header_encode_flags(tcp_control_t ctl, uint16_t doff_flags0,
110 uint16_t *rdoff_flags)
111{
112 uint16_t doff_flags;
113
114 doff_flags = doff_flags0;
115
116 if ((ctl & CTL_ACK) != 0)
[6896409c]117 doff_flags |= BIT_V(uint16_t, DF_ACK);
[eea65f4]118 if ((ctl & CTL_RST) != 0)
[6896409c]119 doff_flags |= BIT_V(uint16_t, DF_RST);
[eea65f4]120 if ((ctl & CTL_SYN) != 0)
[6896409c]121 doff_flags |= BIT_V(uint16_t, DF_SYN);
[eea65f4]122 if ((ctl & CTL_FIN) != 0)
[6896409c]123 doff_flags |= BIT_V(uint16_t, DF_FIN);
[eea65f4]124
125 *rdoff_flags = doff_flags;
126}
127
128static void tcp_header_setup(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_header_t *hdr)
129{
130 uint16_t doff_flags;
[5f9ecd3]131 uint16_t doff;
[eea65f4]132
133 hdr->src_port = host2uint16_t_be(sp->local.port);
134 hdr->dest_port = host2uint16_t_be(sp->foreign.port);
135 hdr->seq = host2uint32_t_be(seg->seq);
136 hdr->ack = host2uint32_t_be(seg->ack);
[5f9ecd3]137
138 doff = (sizeof(tcp_header_t) / sizeof(uint32_t)) << DF_DATA_OFFSET_l;
139 tcp_header_encode_flags(seg->ctrl, doff, &doff_flags);
140
[eea65f4]141 hdr->doff_flags = host2uint16_t_be(doff_flags);
142 hdr->window = host2uint16_t_be(seg->wnd);
[c5808b41]143 hdr->checksum = 0;
[eea65f4]144 hdr->urg_ptr = host2uint16_t_be(seg->up);
[c5808b41]145}
146
[02a09ed]147static uint16_t tcp_phdr_setup(tcp_pdu_t *pdu, tcp_phdr_t *phdr)
[c5808b41]148{
[02a09ed]149 addr32_t src_v4;
150 addr128_t src_v6;
151 uint16_t src_af = inet_addr_get(&pdu->src, &src_v4, &src_v6);
[a2e3ee6]152
[02a09ed]153 addr32_t dest_v4;
154 addr128_t dest_v6;
155 uint16_t dest_af = inet_addr_get(&pdu->dest, &dest_v4, &dest_v6);
[a2e3ee6]156
[02a09ed]157 assert(src_af == dest_af);
[a2e3ee6]158
[02a09ed]159 switch (src_af) {
160 case AF_INET:
161 phdr->src = host2uint32_t_be(src_v4);
162 phdr->dest = host2uint32_t_be(dest_v4);
163 phdr->zero = 0;
164 phdr->protocol = IP_PROTO_TCP;
165 phdr->tcp_length =
166 host2uint16_t_be(pdu->header_size + pdu->text_size);
167 break;
168 case AF_INET6:
169 // FIXME TODO
170 assert(false);
171 default:
172 assert(false);
173 }
174
175 return src_af;
[eea65f4]176}
177
178static void tcp_header_decode(tcp_header_t *hdr, tcp_segment_t *seg)
179{
180 tcp_header_decode_flags(uint16_t_be2host(hdr->doff_flags), &seg->ctrl);
181 seg->seq = uint32_t_be2host(hdr->seq);
182 seg->ack = uint32_t_be2host(hdr->ack);
183 seg->wnd = uint16_t_be2host(hdr->window);
184 seg->up = uint16_t_be2host(hdr->urg_ptr);
185}
186
187static int tcp_header_encode(tcp_sockpair_t *sp, tcp_segment_t *seg,
188 void **header, size_t *size)
189{
190 tcp_header_t *hdr;
191
192 hdr = calloc(1, sizeof(tcp_header_t));
193 if (hdr == NULL)
194 return ENOMEM;
195
196 tcp_header_setup(sp, seg, hdr);
197 *header = hdr;
198 *size = sizeof(tcp_header_t);
199
200 return EOK;
201}
202
203static tcp_pdu_t *tcp_pdu_new(void)
204{
205 return calloc(1, sizeof(tcp_pdu_t));
206}
207
[1812a0d]208/** Create PDU with the specified header and text data.
209 *
210 * Note that you still need to set addresses in the returned PDU.
211 *
212 * @param hdr Header data
213 * @param hdr_size Header size in bytes
214 * @param text Text data
215 * @param text_size Text size in bytes
216 * @return New PDU
217 */
218tcp_pdu_t *tcp_pdu_create(void *hdr, size_t hdr_size, void *text,
219 size_t text_size)
220{
221 tcp_pdu_t *pdu;
222
223 pdu = tcp_pdu_new();
224 if (pdu == NULL)
225 return NULL;
226
227 pdu->header = malloc(hdr_size);
228 pdu->text = malloc(text_size);
229 if (pdu->header == NULL || pdu->text == NULL)
230 goto error;
231
232 memcpy(pdu->header, hdr, hdr_size);
233 memcpy(pdu->text, text, text_size);
234
235 pdu->header_size = hdr_size;
236 pdu->text_size = text_size;
237
238 return pdu;
239
240error:
241 if (pdu->header != NULL)
242 free(pdu->header);
243 if (pdu->text != NULL)
244 free(pdu->text);
245
246 return NULL;
247}
248
[eea65f4]249void tcp_pdu_delete(tcp_pdu_t *pdu)
250{
[1812a0d]251 free(pdu->header);
[eea65f4]252 free(pdu->text);
253 free(pdu);
254}
255
256static uint16_t tcp_pdu_checksum_calc(tcp_pdu_t *pdu)
257{
258 uint16_t cs_phdr;
259 uint16_t cs_headers;
260 tcp_phdr_t phdr;
[02a09ed]261
262 uint16_t af = tcp_phdr_setup(pdu, &phdr);
263 switch (af) {
264 case AF_INET:
265 cs_phdr = tcp_checksum_calc(TCP_CHECKSUM_INIT, (void *) &phdr,
266 sizeof(tcp_phdr_t));
267 break;
268 case AF_INET6:
269 // FIXME TODO
270 assert(false);
271 default:
272 assert(false);
273 }
274
[eea65f4]275 cs_headers = tcp_checksum_calc(cs_phdr, pdu->header, pdu->header_size);
[02a09ed]276 return tcp_checksum_calc(cs_headers, pdu->text, pdu->text_size);
[eea65f4]277}
278
279static void tcp_pdu_set_checksum(tcp_pdu_t *pdu, uint16_t checksum)
280{
281 tcp_header_t *hdr;
282
283 hdr = (tcp_header_t *)pdu->header;
[0ac2158]284 hdr->checksum = host2uint16_t_be(checksum);
[eea65f4]285}
286
[66a272f8]287/** Decode incoming PDU */
[eea65f4]288int tcp_pdu_decode(tcp_pdu_t *pdu, tcp_sockpair_t *sp, tcp_segment_t **seg)
289{
290 tcp_segment_t *nseg;
291 tcp_header_t *hdr;
292
293 nseg = tcp_segment_make_data(0, pdu->text, pdu->text_size);
294 if (nseg == NULL)
295 return ENOMEM;
296
297 tcp_header_decode(pdu->header, nseg);
[6896409c]298 nseg->len += seq_no_control_len(nseg->ctrl);
[eea65f4]299
300 hdr = (tcp_header_t *)pdu->header;
301
302 sp->local.port = uint16_t_be2host(hdr->dest_port);
[02a09ed]303 sp->local.addr = pdu->dest;
[eea65f4]304 sp->foreign.port = uint16_t_be2host(hdr->src_port);
[02a09ed]305 sp->foreign.addr = pdu->src;
[eea65f4]306
307 *seg = nseg;
308 return EOK;
309}
310
[66a272f8]311/** Encode outgoing PDU */
[eea65f4]312int tcp_pdu_encode(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_pdu_t **pdu)
313{
314 tcp_pdu_t *npdu;
315 size_t text_size;
316 uint16_t checksum;
317
318 npdu = tcp_pdu_new();
319 if (npdu == NULL)
320 return ENOMEM;
321
[02a09ed]322 npdu->src = sp->local.addr;
323 npdu->dest = sp->foreign.addr;
[eea65f4]324 tcp_header_encode(sp, seg, &npdu->header, &npdu->header_size);
325
326 text_size = tcp_segment_text_size(seg);
327 npdu->text = calloc(1, text_size);
328 if (npdu->text == NULL)
329 return ENOMEM;
330
331 npdu->text_size = text_size;
332 memcpy(npdu->text, seg->data, text_size);
333
334 /* Checksum calculation */
335 checksum = tcp_pdu_checksum_calc(npdu);
336 tcp_pdu_set_checksum(npdu, checksum);
[c5808b41]337
[eea65f4]338 *pdu = npdu;
339 return EOK;
[c5808b41]340}
341
342/**
343 * @}
344 */
Note: See TracBrowser for help on using the repository browser.