Changes in uspace/srv/net/inetsrv/pdu.c [a1a101d:5a324d99] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/net/inetsrv/pdu.c
ra1a101d r5a324d99 44 44 #include <mem.h> 45 45 #include <stdlib.h> 46 47 46 #include "inetsrv.h" 48 47 #include "inet_std.h" 49 48 #include "pdu.h" 50 51 static FIBRIL_MUTEX_INITIALIZE(ip_ident_lock);52 static uint16_t ip_ident = 0;53 49 54 50 /** One's complement addition. … … 88 84 } 89 85 90 /** Encode I nternetPDU.86 /** Encode IPv4 PDU. 91 87 * 92 88 * Encode internet packet into PDU (serialized form). Will encode a … … 96 92 * be set in the header, otherwise the offset will equal @a packet->size. 97 93 * 98 * @param packet Packet to encode 99 * @param offs Offset into packet payload (in bytes) 100 * @param mtu MTU (Maximum Transmission Unit) in bytes 101 * @param rdata Place to store pointer to allocated data buffer 102 * @param rsize Place to store size of allocated data buffer 103 * @param roffs Place to store offset of remaning data 104 */ 105 int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu, 106 void **rdata, size_t *rsize, size_t *roffs) 107 { 108 void *data; 109 size_t size; 110 ip_header_t *hdr; 111 size_t hdr_size; 112 size_t data_offs; 113 uint16_t chksum; 114 uint16_t ident; 115 uint16_t flags_foff; 116 uint16_t foff; 117 size_t fragoff_limit; 118 size_t xfer_size; 119 size_t spc_avail; 120 size_t rem_offs; 121 94 * @param packet Packet to encode 95 * @param src Source address 96 * @param dest Destination address 97 * @param offs Offset into packet payload (in bytes) 98 * @param mtu MTU (Maximum Transmission Unit) in bytes 99 * @param rdata Place to store pointer to allocated data buffer 100 * @param rsize Place to store size of allocated data buffer 101 * @param roffs Place to store offset of remaning data 102 * 103 */ 104 int inet_pdu_encode(inet_packet_t *packet, addr32_t src, addr32_t dest, 105 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs) 106 { 122 107 /* Upper bound for fragment offset field */ 123 fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);124 108 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l); 109 125 110 /* Verify that total size of datagram is within reasonable bounds */ 126 111 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit) 127 112 return ELIMIT; 128 129 hdr_size = sizeof(ip_header_t); 130 data_offs = ROUND_UP(hdr_size, 4); 131 113 114 size_t hdr_size = sizeof(ip_header_t); 115 if (hdr_size >= mtu) 116 return EINVAL; 117 118 assert(hdr_size % 4 == 0); 132 119 assert(offs % FRAG_OFFS_UNIT == 0); 133 120 assert(offs / FRAG_OFFS_UNIT < fragoff_limit); 134 121 135 122 /* Value for the fragment offset field */ 136 foff = offs / FRAG_OFFS_UNIT; 137 138 if (hdr_size >= mtu) 139 return EINVAL; 140 123 uint16_t foff = offs / FRAG_OFFS_UNIT; 124 141 125 /* Amount of space in the PDU available for payload */ 142 s pc_avail = mtu - hdr_size;126 size_t spc_avail = mtu - hdr_size; 143 127 spc_avail -= (spc_avail % FRAG_OFFS_UNIT); 144 128 145 129 /* Amount of data (payload) to transfer */ 146 xfer_size = min(packet->size - offs, spc_avail);147 130 size_t xfer_size = min(packet->size - offs, spc_avail); 131 148 132 /* Total PDU size */ 149 size = hdr_size + xfer_size;150 133 size_t size = hdr_size + xfer_size; 134 151 135 /* Offset of remaining payload */ 152 rem_offs = offs + xfer_size;153 136 size_t rem_offs = offs + xfer_size; 137 154 138 /* Flags */ 155 flags_foff =139 uint16_t flags_foff = 156 140 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) + 157 141 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) + 158 142 (foff << FF_FRAGOFF_l); 159 160 data = calloc(size, 1);143 144 void *data = calloc(size, 1); 161 145 if (data == NULL) 162 146 return ENOMEM; 163 164 /* Allocate identifier */ 165 fibril_mutex_lock(&ip_ident_lock); 166 ident = ++ip_ident; 167 fibril_mutex_unlock(&ip_ident_lock); 168 147 169 148 /* Encode header fields */ 170 hdr = (ip_header_t *)data; 171 hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t)); 149 ip_header_t *hdr = (ip_header_t *) data; 150 151 hdr->ver_ihl = 152 (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t)); 172 153 hdr->tos = packet->tos; 173 154 hdr->tot_len = host2uint16_t_be(size); 174 hdr->id = host2uint16_t_be( ident);155 hdr->id = host2uint16_t_be(packet->ident); 175 156 hdr->flags_foff = host2uint16_t_be(flags_foff); 176 157 hdr->ttl = packet->ttl; 177 158 hdr->proto = packet->proto; 178 159 hdr->chksum = 0; 179 hdr->src_addr = host2uint32_t_be( packet->src.ipv4);180 hdr->dest_addr = host2uint32_t_be( packet->dest.ipv4);181 160 hdr->src_addr = host2uint32_t_be(src); 161 hdr->dest_addr = host2uint32_t_be(dest); 162 182 163 /* Compute checksum */ 183 chksum = inet_checksum_calc(INET_CHECKSUM_INIT, (void *)hdr, hdr_size); 164 uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT, 165 (void *) hdr, hdr_size); 184 166 hdr->chksum = host2uint16_t_be(chksum); 185 167 186 168 /* Copy payload */ 187 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);188 169 memcpy((uint8_t *) data + hdr_size, packet->data + offs, xfer_size); 170 189 171 *rdata = data; 190 172 *rsize = size; 191 173 *roffs = rem_offs; 192 174 193 175 return EOK; 194 176 } 195 177 178 /** Encode IPv6 PDU. 179 * 180 * Encode internet packet into PDU (serialized form). Will encode a 181 * fragment of the payload starting at offset @a offs. The resulting 182 * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset 183 * of remaining payload. If some data is remaining, the MF flag will 184 * be set in the header, otherwise the offset will equal @a packet->size. 185 * 186 * @param packet Packet to encode 187 * @param src Source address 188 * @param dest Destination address 189 * @param offs Offset into packet payload (in bytes) 190 * @param mtu MTU (Maximum Transmission Unit) in bytes 191 * @param rdata Place to store pointer to allocated data buffer 192 * @param rsize Place to store size of allocated data buffer 193 * @param roffs Place to store offset of remaning data 194 * 195 */ 196 int inet_pdu_encode6(inet_packet_t *packet, addr128_t src, addr128_t dest, 197 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs) 198 { 199 /* IPv6 mandates a minimal MTU of 1280 bytes */ 200 if (mtu < 1280) 201 return ELIMIT; 202 203 /* Upper bound for fragment offset field */ 204 size_t fragoff_limit = 1 << (OF_FRAGOFF_h - OF_FRAGOFF_l); 205 206 /* Verify that total size of datagram is within reasonable bounds */ 207 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit) 208 return ELIMIT; 209 210 /* Determine whether we need the Fragment extension header */ 211 bool fragment; 212 if (offs == 0) 213 fragment = (packet->size + sizeof(ip6_header_t) > mtu); 214 else 215 fragment = true; 216 217 size_t hdr_size; 218 if (fragment) 219 hdr_size = sizeof(ip6_header_t) + sizeof(ip6_header_fragment_t); 220 else 221 hdr_size = sizeof(ip6_header_t); 222 223 if (hdr_size >= mtu) 224 return EINVAL; 225 226 assert(sizeof(ip6_header_t) % 8 == 0); 227 assert(hdr_size % 8 == 0); 228 assert(offs % FRAG_OFFS_UNIT == 0); 229 assert(offs / FRAG_OFFS_UNIT < fragoff_limit); 230 231 /* Value for the fragment offset field */ 232 uint16_t foff = offs / FRAG_OFFS_UNIT; 233 234 /* Amount of space in the PDU available for payload */ 235 size_t spc_avail = mtu - hdr_size; 236 spc_avail -= (spc_avail % FRAG_OFFS_UNIT); 237 238 /* Amount of data (payload) to transfer */ 239 size_t xfer_size = min(packet->size - offs, spc_avail); 240 241 /* Total PDU size */ 242 size_t size = hdr_size + xfer_size; 243 244 /* Offset of remaining payload */ 245 size_t rem_offs = offs + xfer_size; 246 247 /* Flags */ 248 uint16_t offsmf = 249 (rem_offs < packet->size ? BIT_V(uint16_t, OF_FLAG_M) : 0) + 250 (foff << OF_FRAGOFF_l); 251 252 void *data = calloc(size, 1); 253 if (data == NULL) 254 return ENOMEM; 255 256 /* Encode header fields */ 257 ip6_header_t *hdr6 = (ip6_header_t *) data; 258 259 hdr6->ver_tc = (6 << (VI_VERSION_l)); 260 memset(hdr6->tc_fl, 0, 3); 261 hdr6->hop_limit = packet->ttl; 262 263 host2addr128_t_be(src, hdr6->src_addr); 264 host2addr128_t_be(dest, hdr6->dest_addr); 265 266 /* Optionally encode Fragment extension header fields */ 267 if (fragment) { 268 assert(offsmf != 0); 269 270 hdr6->payload_len = host2uint16_t_be(packet->size + 271 sizeof(ip6_header_fragment_t)); 272 hdr6->next = IP6_NEXT_FRAGMENT; 273 274 ip6_header_fragment_t *hdr6f = (ip6_header_fragment_t *) 275 (hdr6 + 1); 276 277 hdr6f->next = packet->proto; 278 hdr6f->reserved = 0; 279 hdr6f->offsmf = host2uint16_t_be(offsmf); 280 hdr6f->id = host2uint32_t_be(packet->ident); 281 } else { 282 assert(offsmf == 0); 283 284 hdr6->payload_len = host2uint16_t_be(packet->size); 285 hdr6->next = packet->proto; 286 } 287 288 /* Copy payload */ 289 memcpy((uint8_t *) data + hdr_size, packet->data + offs, xfer_size); 290 291 *rdata = data; 292 *rsize = size; 293 *roffs = rem_offs; 294 295 return EOK; 296 } 297 298 /** Decode IPv4 datagram 299 * 300 * @param data Serialized IPv4 datagram 301 * @param size Length of serialized IPv4 datagram 302 * @param packet IP datagram structure to be filled 303 * 304 * @return EOK on success 305 * @return EINVAL if the datagram is invalid or damaged 306 * @return ENOMEM if not enough memory 307 * 308 */ 196 309 int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet) 197 310 { 198 ip_header_t *hdr;199 size_t tot_len;200 size_t data_offs;201 uint8_t version;202 uint16_t ident;203 uint16_t flags_foff;204 uint16_t foff;205 206 311 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()"); 207 312 208 313 if (size < sizeof(ip_header_t)) { 209 314 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size); 210 315 return EINVAL; 211 316 } 212 213 hdr = (ip_header_t *)data;214 215 version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, VI_VERSION_l,216 hdr->ver_ihl);317 318 ip_header_t *hdr = (ip_header_t *) data; 319 320 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, 321 VI_VERSION_l, hdr->ver_ihl); 217 322 if (version != 4) { 218 323 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 4", version); 219 324 return EINVAL; 220 325 } 221 222 tot_len = uint16_t_be2host(hdr->tot_len);326 327 size_t tot_len = uint16_t_be2host(hdr->tot_len); 223 328 if (tot_len < sizeof(ip_header_t)) { 224 329 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length too small (%zu)", tot_len); 225 330 return EINVAL; 226 331 } 227 332 228 333 if (tot_len > size) { 229 334 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu", 230 231 return EINVAL; 232 } 233 234 ident = uint16_t_be2host(hdr->id);235 flags_foff = uint16_t_be2host(hdr->flags_foff);236 foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,335 tot_len, size); 336 return EINVAL; 337 } 338 339 uint16_t ident = uint16_t_be2host(hdr->id); 340 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff); 341 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l, 237 342 flags_foff); 238 343 /* XXX Checksum */ 239 240 packet->src.ipv4 = uint32_t_be2host(hdr->src_addr);241 packet->dest.ipv4 = uint32_t_be2host(hdr->dest_addr);344 345 inet_addr_set(uint32_t_be2host(hdr->src_addr), &packet->src); 346 inet_addr_set(uint32_t_be2host(hdr->dest_addr), &packet->dest); 242 347 packet->tos = hdr->tos; 243 348 packet->proto = hdr->proto; 244 349 packet->ttl = hdr->ttl; 245 350 packet->ident = ident; 246 351 247 352 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0; 248 353 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0; 249 354 packet->offs = foff * FRAG_OFFS_UNIT; 250 355 251 356 /* XXX IP options */ 252 data_offs = sizeof(uint32_t) * BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h,253 VI_IHL_l, hdr->ver_ihl);254 357 size_t data_offs = sizeof(uint32_t) * 358 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl); 359 255 360 packet->size = tot_len - data_offs; 256 361 packet->data = calloc(packet->size, 1); … … 259 364 return ENOMEM; 260 365 } 261 262 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);263 366 367 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size); 368 264 369 return EOK; 265 370 } 266 371 372 /** Decode IPv6 datagram 373 * 374 * @param data Serialized IPv6 datagram 375 * @param size Length of serialized IPv6 datagram 376 * @param packet IP datagram structure to be filled 377 * 378 * @return EOK on success 379 * @return EINVAL if the datagram is invalid or damaged 380 * @return ENOMEM if not enough memory 381 * 382 */ 383 int inet_pdu_decode6(void *data, size_t size, inet_packet_t *packet) 384 { 385 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode6()"); 386 387 if (size < sizeof(ip6_header_t)) { 388 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size); 389 return EINVAL; 390 } 391 392 ip6_header_t *hdr6 = (ip6_header_t *) data; 393 394 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, 395 VI_VERSION_l, hdr6->ver_tc); 396 if (version != 6) { 397 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 6", version); 398 return EINVAL; 399 } 400 401 size_t payload_len = uint16_t_be2host(hdr6->payload_len); 402 if (payload_len + sizeof(ip6_header_t) > size) { 403 log_msg(LOG_DEFAULT, LVL_DEBUG, "Payload Length = %zu > PDU size = %zu", 404 payload_len + sizeof(ip6_header_t), size); 405 return EINVAL; 406 } 407 408 uint32_t ident; 409 uint16_t offsmf; 410 uint16_t foff; 411 uint16_t next; 412 size_t data_offs = sizeof(ip6_header_t); 413 414 /* Fragment extension header */ 415 if (hdr6->next == IP6_NEXT_FRAGMENT) { 416 ip6_header_fragment_t *hdr6f = (ip6_header_fragment_t *) 417 (hdr6 + 1); 418 419 ident = uint32_t_be2host(hdr6f->id); 420 offsmf = uint16_t_be2host(hdr6f->offsmf); 421 foff = BIT_RANGE_EXTRACT(uint16_t, OF_FRAGOFF_h, OF_FRAGOFF_l, 422 offsmf); 423 next = hdr6f->next; 424 data_offs += sizeof(ip6_header_fragment_t); 425 payload_len -= sizeof(ip6_header_fragment_t); 426 } else { 427 ident = 0; 428 offsmf = 0; 429 foff = 0; 430 next = hdr6->next; 431 } 432 433 addr128_t src; 434 addr128_t dest; 435 436 addr128_t_be2host(hdr6->src_addr, src); 437 inet_addr_set6(src, &packet->src); 438 439 addr128_t_be2host(hdr6->dest_addr, dest); 440 inet_addr_set6(dest, &packet->dest); 441 442 packet->tos = 0; 443 packet->proto = next; 444 packet->ttl = hdr6->hop_limit; 445 packet->ident = ident; 446 447 packet->df = 1; 448 packet->mf = (offsmf & BIT_V(uint16_t, OF_FLAG_M)) != 0; 449 packet->offs = foff * FRAG_OFFS_UNIT; 450 451 packet->size = payload_len; 452 packet->data = calloc(packet->size, 1); 453 if (packet->data == NULL) { 454 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory."); 455 return ENOMEM; 456 } 457 458 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size); 459 460 return EOK; 461 } 462 463 /** Encode NDP packet 464 * 465 * @param ndp NDP packet structure to be serialized 466 * @param dgram IPv6 datagram structure to be filled 467 * 468 * @return EOK on success 469 * 470 */ 471 int ndp_pdu_encode(ndp_packet_t *ndp, inet_dgram_t *dgram) 472 { 473 inet_addr_set6(ndp->sender_proto_addr, &dgram->src); 474 inet_addr_set6(ndp->target_proto_addr, &dgram->dest); 475 dgram->tos = 0; 476 dgram->size = sizeof(icmpv6_message_t) + sizeof(ndp_message_t); 477 478 dgram->data = calloc(1, dgram->size); 479 if (dgram->data == NULL) 480 return ENOMEM; 481 482 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data; 483 484 icmpv6->type = ndp->opcode; 485 icmpv6->code = 0; 486 memset(icmpv6->un.ndp.reserved, 0, 3); 487 488 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1); 489 490 if (ndp->opcode == ICMPV6_NEIGHBOUR_SOLICITATION) { 491 host2addr128_t_be(ndp->solicited_ip, message->target_address); 492 message->option = 1; 493 icmpv6->un.ndp.flags = 0; 494 } else { 495 host2addr128_t_be(ndp->sender_proto_addr, message->target_address); 496 message->option = 2; 497 icmpv6->un.ndp.flags = NDP_FLAG_OVERRIDE | NDP_FLAG_SOLICITED; 498 } 499 500 message->length = 1; 501 addr48(ndp->sender_hw_addr, message->mac); 502 503 icmpv6_phdr_t phdr; 504 505 host2addr128_t_be(ndp->sender_proto_addr, phdr.src_addr); 506 host2addr128_t_be(ndp->target_proto_addr, phdr.dest_addr); 507 phdr.length = host2uint32_t_be(dgram->size); 508 memset(phdr.zeroes, 0, 3); 509 phdr.next = IP_PROTO_ICMPV6; 510 511 uint16_t cs_phdr = 512 inet_checksum_calc(INET_CHECKSUM_INIT, &phdr, 513 sizeof(icmpv6_phdr_t)); 514 515 uint16_t cs_all = inet_checksum_calc(cs_phdr, dgram->data, 516 dgram->size); 517 518 icmpv6->checksum = host2uint16_t_be(cs_all); 519 520 return EOK; 521 } 522 523 /** Decode NDP packet 524 * 525 * @param dgram Incoming IPv6 datagram encapsulating NDP packet 526 * @param ndp NDP packet structure to be filled 527 * 528 * @return EOK on success 529 * @return EINVAL if the Datagram is invalid 530 * 531 */ 532 int ndp_pdu_decode(inet_dgram_t *dgram, ndp_packet_t *ndp) 533 { 534 ip_ver_t src_ver = inet_addr_get(&dgram->src, NULL, 535 &ndp->sender_proto_addr); 536 if (src_ver != ip_v6) 537 return EINVAL; 538 539 if (dgram->size < sizeof(icmpv6_message_t) + sizeof(ndp_message_t)) 540 return EINVAL; 541 542 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data; 543 544 ndp->opcode = icmpv6->type; 545 546 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1); 547 548 addr128_t_be2host(message->target_address, ndp->target_proto_addr); 549 addr48(message->mac, ndp->sender_hw_addr); 550 551 return EOK; 552 } 553 267 554 /** @} 268 555 */
Note:
See TracChangeset
for help on using the changeset viewer.