Annotation of embedaddon/strongswan/src/libipsec/ip_packet.c, revision 1.1

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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>