Return to ip_packet.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libipsec |
1.1 ! misho 1: /* ! 2: * Copyright (C) 2012-2014 Tobias Brunner ! 3: * HSR Hochschule fuer Technik Rapperswil ! 4: * ! 5: * This program is free software; you can redistribute it and/or modify it ! 6: * under the terms of the GNU General Public License as published by the ! 7: * Free Software Foundation; either version 2 of the License, or (at your ! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. ! 9: * ! 10: * This program is distributed in the hope that it will be useful, but ! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ! 13: * for more details. ! 14: */ ! 15: ! 16: ! 17: #include "ip_packet.h" ! 18: ! 19: #include <library.h> ! 20: #include <utils/debug.h> ! 21: ! 22: #include <sys/types.h> ! 23: ! 24: #ifndef WIN32 ! 25: #include <netinet/in.h> ! 26: #include <netinet/ip.h> ! 27: #ifdef HAVE_NETINET_IP6_H ! 28: #include <netinet/ip6.h> ! 29: #endif ! 30: #else ! 31: struct ip { ! 32: #if BYTE_ORDER == LITTLE_ENDIAN ! 33: uint8_t ip_hl: 4; ! 34: uint8_t ip_v: 4; ! 35: #elif BYTE_ORDER == BIG_ENDIAN ! 36: uint8_t ip_v: 4; ! 37: uint8_t ip_hl: 4; ! 38: #endif ! 39: uint8_t ip_tos; ! 40: uint16_t ip_len; ! 41: uint16_t ip_id; ! 42: uint16_t ip_off; ! 43: uint8_t ip_ttl; ! 44: uint8_t ip_p; ! 45: uint16_t ip_sum; ! 46: struct in_addr ip_src, ip_dst; ! 47: } __attribute__((packed)); ! 48: struct ip6_hdr { ! 49: uint32_t ip6_flow; /* 4 bit version, 8 bit TC, 20 bit flow label */ ! 50: uint16_t ip6_plen; ! 51: uint8_t ip6_nxt; ! 52: uint8_t ip6_hlim; ! 53: struct in6_addr ip6_src, ip6_dst; ! 54: } __attribute__((packed)); ! 55: struct ip6_ext { ! 56: uint8_t ip6e_nxt; ! 57: uint8_t ip6e_len; ! 58: } __attribute__((packed)); ! 59: #define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */ ! 60: #endif ! 61: ! 62: #ifndef IP_OFFMASK ! 63: #define IP_OFFMASK 0x1fff ! 64: #endif ! 65: ! 66: /** ! 67: * TCP header, defined here because platforms disagree regarding member names ! 68: * and unfortunately Android does not define a variant with BSD names. ! 69: */ ! 70: struct tcphdr { ! 71: uint16_t source; ! 72: uint16_t dest; ! 73: uint32_t seq; ! 74: uint32_t ack_seq; ! 75: uint16_t flags; ! 76: uint16_t window; ! 77: uint16_t check; ! 78: uint16_t urg_ptr; ! 79: } __attribute__((packed)); ! 80: ! 81: /** ! 82: * UDP header, similar to the TCP header the system headers disagree on member ! 83: * names. Linux uses a union and on Android we could define __FAVOR_BSD to get ! 84: * the BSD member names, but this is simpler and more consistent with the above. ! 85: */ ! 86: struct udphdr { ! 87: uint16_t source; ! 88: uint16_t dest; ! 89: uint16_t len; ! 90: uint16_t check; ! 91: } __attribute__((packed)); ! 92: ! 93: typedef struct private_ip_packet_t private_ip_packet_t; ! 94: ! 95: /** ! 96: * Private additions to ip_packet_t. ! 97: */ ! 98: struct private_ip_packet_t { ! 99: ! 100: /** ! 101: * Public members ! 102: */ ! 103: ip_packet_t public; ! 104: ! 105: /** ! 106: * Source address ! 107: */ ! 108: host_t *src; ! 109: ! 110: /** ! 111: * Destination address ! 112: */ ! 113: host_t *dst; ! 114: ! 115: /** ! 116: * IP packet ! 117: */ ! 118: chunk_t packet; ! 119: ! 120: /** ! 121: * IP payload (points into packet) ! 122: */ ! 123: chunk_t payload; ! 124: ! 125: /** ! 126: * IP version ! 127: */ ! 128: uint8_t version; ! 129: ! 130: /** ! 131: * Protocol|Next Header field ! 132: */ ! 133: uint8_t next_header; ! 134: ! 135: }; ! 136: ! 137: METHOD(ip_packet_t, get_version, uint8_t, ! 138: private_ip_packet_t *this) ! 139: { ! 140: return this->version; ! 141: } ! 142: ! 143: METHOD(ip_packet_t, get_source, host_t*, ! 144: private_ip_packet_t *this) ! 145: { ! 146: return this->src; ! 147: } ! 148: ! 149: METHOD(ip_packet_t, get_destination, host_t*, ! 150: private_ip_packet_t *this) ! 151: { ! 152: return this->dst; ! 153: } ! 154: ! 155: METHOD(ip_packet_t, get_encoding, chunk_t, ! 156: private_ip_packet_t *this) ! 157: { ! 158: return this->packet; ! 159: } ! 160: ! 161: METHOD(ip_packet_t, get_payload, chunk_t, ! 162: private_ip_packet_t *this) ! 163: { ! 164: return this->payload; ! 165: } ! 166: ! 167: METHOD(ip_packet_t, get_next_header, uint8_t, ! 168: private_ip_packet_t *this) ! 169: { ! 170: return this->next_header; ! 171: } ! 172: ! 173: METHOD(ip_packet_t, clone_, ip_packet_t*, ! 174: private_ip_packet_t *this) ! 175: { ! 176: return ip_packet_create(chunk_clone(this->packet)); ! 177: } ! 178: ! 179: METHOD(ip_packet_t, destroy, void, ! 180: private_ip_packet_t *this) ! 181: { ! 182: this->src->destroy(this->src); ! 183: this->dst->destroy(this->dst); ! 184: chunk_free(&this->packet); ! 185: free(this); ! 186: } ! 187: ! 188: /** ! 189: * Parse transport protocol header ! 190: */ ! 191: static bool parse_transport_header(chunk_t packet, uint8_t proto, ! 192: uint16_t *sport, uint16_t *dport) ! 193: { ! 194: switch (proto) ! 195: { ! 196: case IPPROTO_UDP: ! 197: { ! 198: struct udphdr *udp; ! 199: ! 200: if (packet.len < sizeof(*udp)) ! 201: { ! 202: DBG1(DBG_ESP, "UDP packet too short"); ! 203: return FALSE; ! 204: } ! 205: udp = (struct udphdr*)packet.ptr; ! 206: *sport = ntohs(udp->source); ! 207: *dport = ntohs(udp->dest); ! 208: break; ! 209: } ! 210: case IPPROTO_TCP: ! 211: { ! 212: struct tcphdr *tcp; ! 213: ! 214: if (packet.len < sizeof(*tcp)) ! 215: { ! 216: DBG1(DBG_ESP, "TCP packet too short"); ! 217: return FALSE; ! 218: } ! 219: tcp = (struct tcphdr*)packet.ptr; ! 220: *sport = ntohs(tcp->source); ! 221: *dport = ntohs(tcp->dest); ! 222: break; ! 223: } ! 224: default: ! 225: break; ! 226: } ! 227: return TRUE; ! 228: } ! 229: ! 230: #ifdef HAVE_NETINET_IP6_H ! 231: /** ! 232: * Skip to the actual payload and parse the transport header. ! 233: */ ! 234: static bool parse_transport_header_v6(struct ip6_hdr *ip, chunk_t packet, ! 235: chunk_t *payload, uint8_t *proto, ! 236: uint16_t *sport, uint16_t *dport) ! 237: { ! 238: struct ip6_ext *ext; ! 239: bool fragment = FALSE; ! 240: ! 241: *proto = ip->ip6_nxt; ! 242: *payload = chunk_skip(packet, 40); ! 243: while (payload->len >= sizeof(struct ip6_ext)) ! 244: { ! 245: switch (*proto) ! 246: { ! 247: case 44: /* Fragment Header */ ! 248: fragment = TRUE; ! 249: /* skip the header */ ! 250: case 0: /* Hop-by-Hop Options Header */ ! 251: case 43: /* Routing Header */ ! 252: case 60: /* Destination Options Header */ ! 253: case 135: /* Mobility Header */ ! 254: case 139: /* HIP */ ! 255: case 140: /* Shim6 */ ! 256: /* simply skip over these headers for now */ ! 257: ext = (struct ip6_ext*)payload->ptr; ! 258: *proto = ext->ip6e_nxt; ! 259: *payload = chunk_skip(*payload, 8 * (ext->ip6e_len + 1)); ! 260: continue; ! 261: default: ! 262: /* assume anything else is an upper layer protocol but only ! 263: * attempt to parse the transport header for non-fragmented ! 264: * packets as there is no guarantee that initial fragments ! 265: * contain the transport header, depending on the number and ! 266: * type of extension headers */ ! 267: if (!fragment && ! 268: !parse_transport_header(*payload, *proto, sport, dport)) ! 269: { ! 270: return FALSE; ! 271: } ! 272: break; ! 273: } ! 274: break; ! 275: } ! 276: return TRUE; ! 277: } ! 278: #endif /* HAVE_NETINET_IP6_H */ ! 279: ! 280: /** ! 281: * Described in header. ! 282: */ ! 283: ip_packet_t *ip_packet_create(chunk_t packet) ! 284: { ! 285: private_ip_packet_t *this; ! 286: uint8_t version, next_header; ! 287: uint16_t sport = 0, dport = 0; ! 288: host_t *src, *dst; ! 289: chunk_t payload; ! 290: ! 291: if (packet.len < 1) ! 292: { ! 293: DBG1(DBG_ESP, "IP packet too short"); ! 294: goto failed; ! 295: } ! 296: ! 297: version = (packet.ptr[0] & 0xf0) >> 4; ! 298: ! 299: switch (version) ! 300: { ! 301: case 4: ! 302: { ! 303: struct ip *ip; ! 304: ! 305: if (packet.len < sizeof(struct ip)) ! 306: { ! 307: DBG1(DBG_ESP, "IPv4 packet too short"); ! 308: goto failed; ! 309: } ! 310: ip = (struct ip*)packet.ptr; ! 311: /* remove any RFC 4303 TFC extra padding */ ! 312: packet.len = min(packet.len, untoh16(&ip->ip_len)); ! 313: payload = chunk_skip(packet, ip->ip_hl * 4); ! 314: if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && ! 315: !parse_transport_header(payload, ip->ip_p, &sport, &dport)) ! 316: { ! 317: goto failed; ! 318: } ! 319: src = host_create_from_chunk(AF_INET, ! 320: chunk_from_thing(ip->ip_src), sport); ! 321: dst = host_create_from_chunk(AF_INET, ! 322: chunk_from_thing(ip->ip_dst), dport); ! 323: next_header = ip->ip_p; ! 324: break; ! 325: } ! 326: #ifdef HAVE_NETINET_IP6_H ! 327: case 6: ! 328: { ! 329: struct ip6_hdr *ip; ! 330: ! 331: if (packet.len < sizeof(*ip)) ! 332: { ! 333: DBG1(DBG_ESP, "IPv6 packet too short"); ! 334: goto failed; ! 335: } ! 336: ip = (struct ip6_hdr*)packet.ptr; ! 337: /* remove any RFC 4303 TFC extra padding */ ! 338: packet.len = min(packet.len, 40 + untoh16((void*)&ip->ip6_plen)); ! 339: if (!parse_transport_header_v6(ip, packet, &payload, &next_header, ! 340: &sport, &dport)) ! 341: { ! 342: goto failed; ! 343: } ! 344: src = host_create_from_chunk(AF_INET6, ! 345: chunk_from_thing(ip->ip6_src), sport); ! 346: dst = host_create_from_chunk(AF_INET6, ! 347: chunk_from_thing(ip->ip6_dst), dport); ! 348: break; ! 349: } ! 350: #endif /* HAVE_NETINET_IP6_H */ ! 351: default: ! 352: DBG1(DBG_ESP, "unsupported IP version"); ! 353: goto failed; ! 354: } ! 355: ! 356: INIT(this, ! 357: .public = { ! 358: .get_version = _get_version, ! 359: .get_source = _get_source, ! 360: .get_destination = _get_destination, ! 361: .get_next_header = _get_next_header, ! 362: .get_encoding = _get_encoding, ! 363: .get_payload = _get_payload, ! 364: .clone = _clone_, ! 365: .destroy = _destroy, ! 366: }, ! 367: .src = src, ! 368: .dst = dst, ! 369: .packet = packet, ! 370: .payload = payload, ! 371: .version = version, ! 372: .next_header = next_header, ! 373: ); ! 374: return &this->public; ! 375: ! 376: failed: ! 377: chunk_free(&packet); ! 378: return NULL; ! 379: } ! 380: ! 381: /** ! 382: * Calculate the checksum for the pseudo IP header ! 383: */ ! 384: static uint16_t pseudo_header_checksum(host_t *src, host_t *dst, ! 385: uint8_t proto, chunk_t payload) ! 386: { ! 387: switch (src->get_family(src)) ! 388: { ! 389: case AF_INET: ! 390: { ! 391: struct __attribute__((packed)) { ! 392: uint32_t src; ! 393: uint32_t dst; ! 394: u_char zero; ! 395: u_char proto; ! 396: uint16_t len; ! 397: } pseudo = { ! 398: .proto = proto, ! 399: .len = htons(payload.len), ! 400: }; ! 401: memcpy(&pseudo.src, src->get_address(src).ptr, ! 402: sizeof(pseudo.src)); ! 403: memcpy(&pseudo.dst, dst->get_address(dst).ptr, ! 404: sizeof(pseudo.dst)); ! 405: return chunk_internet_checksum(chunk_from_thing(pseudo)); ! 406: } ! 407: case AF_INET6: ! 408: { ! 409: struct __attribute__((packed)) { ! 410: u_char src[16]; ! 411: u_char dst[16]; ! 412: uint32_t len; ! 413: u_char zero[3]; ! 414: u_char next_header; ! 415: } pseudo = { ! 416: .next_header = proto, ! 417: .len = htons(payload.len), ! 418: }; ! 419: memcpy(&pseudo.src, src->get_address(src).ptr, ! 420: sizeof(pseudo.src)); ! 421: memcpy(&pseudo.dst, dst->get_address(dst).ptr, ! 422: sizeof(pseudo.dst)); ! 423: return chunk_internet_checksum(chunk_from_thing(pseudo)); ! 424: } ! 425: } ! 426: return 0xffff; ! 427: } ! 428: ! 429: /** ! 430: * Apply transport ports and calculate header checksums ! 431: */ ! 432: static void fix_transport_header(host_t *src, host_t *dst, uint8_t proto, ! 433: chunk_t payload) ! 434: { ! 435: uint16_t sum = 0, sport, dport; ! 436: ! 437: sport = src->get_port(src); ! 438: dport = dst->get_port(dst); ! 439: ! 440: switch (proto) ! 441: { ! 442: case IPPROTO_UDP: ! 443: { ! 444: struct udphdr *udp; ! 445: ! 446: if (payload.len < sizeof(*udp)) ! 447: { ! 448: return; ! 449: } ! 450: udp = (struct udphdr*)payload.ptr; ! 451: if (sport != 0) ! 452: { ! 453: udp->source = htons(sport); ! 454: } ! 455: if (dport != 0) ! 456: { ! 457: udp->dest = htons(dport); ! 458: } ! 459: udp->check = 0; ! 460: sum = pseudo_header_checksum(src, dst, proto, payload); ! 461: udp->check = chunk_internet_checksum_inc(payload, sum); ! 462: break; ! 463: } ! 464: case IPPROTO_TCP: ! 465: { ! 466: struct tcphdr *tcp; ! 467: ! 468: if (payload.len < sizeof(*tcp)) ! 469: { ! 470: return; ! 471: } ! 472: tcp = (struct tcphdr*)payload.ptr; ! 473: if (sport != 0) ! 474: { ! 475: tcp->source = htons(sport); ! 476: } ! 477: if (dport != 0) ! 478: { ! 479: tcp->dest = htons(dport); ! 480: } ! 481: tcp->check = 0; ! 482: sum = pseudo_header_checksum(src, dst, proto, payload); ! 483: tcp->check = chunk_internet_checksum_inc(payload, sum); ! 484: break; ! 485: } ! 486: default: ! 487: break; ! 488: } ! 489: } ! 490: ! 491: /** ! 492: * Described in header. ! 493: */ ! 494: ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst, ! 495: uint8_t next_header, chunk_t data) ! 496: { ! 497: chunk_t packet; ! 498: int family; ! 499: ! 500: family = src->get_family(src); ! 501: if (family != dst->get_family(dst)) ! 502: { ! 503: DBG1(DBG_ESP, "address family does not match"); ! 504: return NULL; ! 505: } ! 506: ! 507: switch (family) ! 508: { ! 509: case AF_INET: ! 510: { ! 511: struct ip ip = { ! 512: .ip_v = 4, ! 513: .ip_hl = 5, ! 514: .ip_len = htons(20 + data.len), ! 515: .ip_ttl = 0x80, ! 516: .ip_p = next_header, ! 517: }; ! 518: memcpy(&ip.ip_src, src->get_address(src).ptr, sizeof(ip.ip_src)); ! 519: memcpy(&ip.ip_dst, dst->get_address(dst).ptr, sizeof(ip.ip_dst)); ! 520: ip.ip_sum = chunk_internet_checksum(chunk_from_thing(ip)); ! 521: ! 522: packet = chunk_cat("cc", chunk_from_thing(ip), data); ! 523: fix_transport_header(src, dst, next_header, chunk_skip(packet, 20)); ! 524: return ip_packet_create(packet); ! 525: } ! 526: #ifdef HAVE_NETINET_IP6_H ! 527: case AF_INET6: ! 528: { ! 529: struct ip6_hdr ip = { ! 530: .ip6_flow = htonl(6 << 28), ! 531: .ip6_plen = htons(data.len), ! 532: .ip6_nxt = next_header, ! 533: .ip6_hlim = 0x80, ! 534: }; ! 535: memcpy(&ip.ip6_src, src->get_address(src).ptr, sizeof(ip.ip6_src)); ! 536: memcpy(&ip.ip6_dst, dst->get_address(dst).ptr, sizeof(ip.ip6_dst)); ! 537: ! 538: packet = chunk_cat("cc", chunk_from_thing(ip), data); ! 539: fix_transport_header(src, dst, next_header, chunk_skip(packet, 40)); ! 540: return ip_packet_create(packet); ! 541: } ! 542: #endif /* HAVE_NETINET_IP6_H */ ! 543: default: ! 544: DBG1(DBG_ESP, "unsupported address family"); ! 545: return NULL; ! 546: } ! 547: } ! 548: ! 549: /** ! 550: * Described in header. ! 551: */ ! 552: ip_packet_t *ip_packet_create_udp_from_data(host_t *src, host_t *dst, ! 553: chunk_t data) ! 554: { ! 555: struct udphdr udp = { ! 556: .len = htons(8 + data.len), ! 557: .check = 0, ! 558: }; ! 559: ip_packet_t *packet; ! 560: ! 561: data = chunk_cat("cc", chunk_from_thing(udp), data); ! 562: packet = ip_packet_create_from_data(src, dst, IPPROTO_UDP, data); ! 563: chunk_free(&data); ! 564: return packet; ! 565: }