Annotation of embedaddon/strongswan/src/libcharon/plugins/dhcp/dhcp_socket.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Copyright (C) 2012-2018 Tobias Brunner
                      3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * Copyright (C) 2010 Martin Willi
                      6:  * Copyright (C) 2010 revosec AG
                      7:  *
                      8:  * This program is free software; you can redistribute it and/or modify it
                      9:  * under the terms of the GNU General Public License as published by the
                     10:  * Free Software Foundation; either version 2 of the License, or (at your
                     11:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful, but
                     14:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     15:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     16:  * for more details.
                     17:  */
                     18: 
                     19: #include "dhcp_socket.h"
                     20: 
                     21: #include <unistd.h>
                     22: #include <errno.h>
                     23: #include <string.h>
                     24: #include <netinet/in.h>
                     25: #include <netinet/ip.h>
                     26: #include <netinet/udp.h>
                     27: #include <linux/if_arp.h>
                     28: #include <linux/if_ether.h>
                     29: #include <linux/filter.h>
                     30: 
                     31: #include <collections/linked_list.h>
                     32: #include <utils/identification.h>
                     33: #include <threading/mutex.h>
                     34: #include <threading/condvar.h>
                     35: #include <threading/thread.h>
                     36: 
                     37: #include <daemon.h>
                     38: #include <processing/jobs/callback_job.h>
                     39: 
                     40: #define DHCP_SERVER_PORT 67
                     41: #define DHCP_CLIENT_PORT 68
                     42: #define DHCP_TRIES 5
                     43: 
                     44: typedef struct private_dhcp_socket_t private_dhcp_socket_t;
                     45: 
                     46: /**
                     47:  * Private data of an dhcp_socket_t object.
                     48:  */
                     49: struct private_dhcp_socket_t {
                     50: 
                     51:        /**
                     52:         * Public dhcp_socket_t interface.
                     53:         */
                     54:        dhcp_socket_t public;
                     55: 
                     56:        /**
                     57:         * Random number generator
                     58:         */
                     59:        rng_t *rng;
                     60: 
                     61:        /**
                     62:         * List of transactions in DISCOVER
                     63:         */
                     64:        linked_list_t *discover;
                     65: 
                     66:        /**
                     67:         * List of transactions in REQUEST
                     68:         */
                     69:        linked_list_t *request;
                     70: 
                     71:        /**
                     72:         * List of successfully completed transactions
                     73:         */
                     74:        linked_list_t *completed;
                     75: 
                     76:        /**
                     77:         * Lock for transactions
                     78:         */
                     79:        mutex_t *mutex;
                     80: 
                     81:        /**
                     82:         * Condvar to wait for transaction completion
                     83:         */
                     84:        condvar_t *condvar;
                     85: 
                     86:        /**
                     87:         * Threads waiting in condvar
                     88:         */
                     89:        int waiting;
                     90: 
                     91:        /**
                     92:         * DHCP send socket
                     93:         */
                     94:        int send;
                     95: 
                     96:        /**
                     97:         * DHCP receive socket
                     98:         */
                     99:        int receive;
                    100: 
                    101:        /**
                    102:         * Do we use per-identity or random leases (and MAC addresses)
                    103:         */
                    104:        bool identity_lease;
                    105: 
                    106:        /**
                    107:         * DHCP server address, or broadcast
                    108:         */
                    109:        host_t *dst;
                    110: 
                    111:        /**
                    112:         * Force configured destination address
                    113:         */
                    114:        bool force_dst;
                    115: };
                    116: 
                    117: /**
                    118:  * DHCP opcode (or BOOTP actually)
                    119:  */
                    120: typedef enum {
                    121:        BOOTREQUEST = 1,
                    122:        BOOTREPLY = 2,
                    123: } dhcp_opcode_t;
                    124: 
                    125: /**
                    126:  * Some DHCP options used
                    127:  */
                    128: typedef enum {
                    129:        DHCP_DNS_SERVER = 6,
                    130:        DHCP_HOST_NAME = 12,
                    131:        DHCP_NBNS_SERVER = 44,
                    132:        DHCP_REQUESTED_IP = 50,
                    133:        DHCP_MESSAGE_TYPE = 53,
                    134:        DHCP_SERVER_ID = 54,
                    135:        DHCP_PARAM_REQ_LIST = 55,
                    136:        DHCP_CLIENT_ID = 61,
                    137:        DHCP_OPTEND = 255,
                    138: } dhcp_option_type_t;
                    139: 
                    140: /**
                    141:  * DHCP messages types in the DHCP_MESSAGE_TYPE option
                    142:  */
                    143: typedef enum {
                    144:        DHCP_DISCOVER = 1,
                    145:        DHCP_OFFER = 2,
                    146:        DHCP_REQUEST = 3,
                    147:        DHCP_DECLINE = 4,
                    148:        DHCP_ACK = 5,
                    149:        DHCP_NAK = 6,
                    150:        DHCP_RELEASE = 7,
                    151:        DHCP_INFORM = 8,
                    152: } dhcp_message_type_t;
                    153: /**
                    154:  * DHCP option encoding, a TLV
                    155:  */
                    156: typedef struct __attribute__((packed)) {
                    157:        uint8_t type;
                    158:        uint8_t len;
                    159:        char data[];
                    160: } dhcp_option_t;
                    161: 
                    162: /**
                    163:  * DHCP message format, with a minimum size options buffer
                    164:  */
                    165: typedef struct __attribute__((packed)) {
                    166:        uint8_t opcode;
                    167:        uint8_t hw_type;
                    168:        uint8_t hw_addr_len;
                    169:        uint8_t hop_count;
                    170:        uint32_t transaction_id;
                    171:        uint16_t number_of_seconds;
                    172:        uint16_t flags;
                    173:        uint32_t client_address;
                    174:        uint32_t your_address;
                    175:        uint32_t server_address;
                    176:        uint32_t gateway_address;
                    177:        char client_hw_addr[6];
                    178:        char client_hw_padding[10];
                    179:        char server_hostname[64];
                    180:        char boot_filename[128];
                    181:        uint32_t magic_cookie;
                    182:        u_char options[308];
                    183: } dhcp_t;
                    184: 
                    185: /**
                    186:  * Check if the given address equals the broadcast address
                    187:  */
                    188: static inline bool is_broadcast(host_t *host)
                    189: {
                    190:        chunk_t broadcast = chunk_from_chars(0xFF,0xFF,0xFF,0xFF);
                    191: 
                    192:        return chunk_equals(broadcast, host->get_address(host));
                    193: }
                    194: 
                    195: /**
                    196:  * Prepare a DHCP message for a given transaction
                    197:  */
                    198: static int prepare_dhcp(private_dhcp_socket_t *this,
                    199:                                                dhcp_transaction_t *transaction,
                    200:                                                dhcp_message_type_t type, dhcp_t *dhcp)
                    201: {
                    202:        chunk_t chunk;
                    203:        identification_t *identity;
                    204:        dhcp_option_t *option;
                    205:        int optlen = 0, remaining;
                    206:        host_t *src;
                    207:        uint32_t id;
                    208: 
                    209:        memset(dhcp, 0, sizeof(*dhcp));
                    210:        dhcp->opcode = BOOTREQUEST;
                    211:        dhcp->hw_type = ARPHRD_ETHER;
                    212:        dhcp->hw_addr_len = 6;
                    213:        dhcp->transaction_id = transaction->get_id(transaction);
                    214:        if (is_broadcast(this->dst))
                    215:        {
                    216:                /* Set broadcast flag to get broadcasted replies, as we actually
                    217:                 * do not own the MAC we request an address for. */
                    218:                dhcp->flags = htons(0x8000);
                    219:                /* TODO: send with 0.0.0.0 source address */
                    220:        }
                    221:        else
                    222:        {
                    223:                /* act as relay agent */
                    224:                src = charon->kernel->get_source_addr(charon->kernel, this->dst, NULL);
                    225:                if (src)
                    226:                {
                    227:                        memcpy(&dhcp->gateway_address, src->get_address(src).ptr,
                    228:                                   sizeof(dhcp->gateway_address));
                    229:                        src->destroy(src);
                    230:                }
                    231:        }
                    232: 
                    233:        identity = transaction->get_identity(transaction);
                    234:        chunk = identity->get_encoding(identity);
                    235:        /* magic bytes, a locally administered unicast MAC */
                    236:        dhcp->client_hw_addr[0] = 0x7A;
                    237:        dhcp->client_hw_addr[1] = 0xA7;
                    238:        /* with ID specific postfix */
                    239:        if (this->identity_lease)
                    240:        {
                    241:                id = htonl(chunk_hash_static(chunk));
                    242:        }
                    243:        else
                    244:        {
                    245:                id = transaction->get_id(transaction);
                    246:        }
                    247:        memcpy(&dhcp->client_hw_addr[2], &id, sizeof(id));
                    248: 
                    249:        dhcp->magic_cookie = htonl(0x63825363);
                    250: 
                    251:        option = (dhcp_option_t*)&dhcp->options[optlen];
                    252:        option->type = DHCP_MESSAGE_TYPE;
                    253:        option->len = 1;
                    254:        option->data[0] = type;
                    255:        optlen += sizeof(dhcp_option_t) + option->len;
                    256: 
                    257:        /* the REQUEST message has the most static overhead in the 'options' field
                    258:         * with 17 bytes */
                    259:        remaining = sizeof(dhcp->options) - optlen - 17;
                    260: 
                    261:        if (identity->get_type(identity) == ID_FQDN)
                    262:        {
                    263:                option = (dhcp_option_t*)&dhcp->options[optlen];
                    264:                option->type = DHCP_HOST_NAME;
                    265:                option->len = min(min(chunk.len, remaining-sizeof(dhcp_option_t)), 255);
                    266:                memcpy(option->data, chunk.ptr, option->len);
                    267:                optlen += sizeof(dhcp_option_t) + option->len;
                    268:                remaining -= sizeof(dhcp_option_t) + option->len;
                    269:        }
                    270: 
                    271:        if (this->identity_lease &&
                    272:                remaining >= sizeof(dhcp_option_t) + 2)
                    273:        {
                    274:                option = (dhcp_option_t*)&dhcp->options[optlen];
                    275:                option->type = DHCP_CLIENT_ID;
                    276:                option->len = min(min(chunk.len, remaining-sizeof(dhcp_option_t)), 255);
                    277:                memcpy(option->data, chunk.ptr, option->len);
                    278:                optlen += sizeof(dhcp_option_t) + option->len;
                    279:        }
                    280:        return optlen;
                    281: }
                    282: 
                    283: /**
                    284:  * Send a DHCP message with given options length
                    285:  */
                    286: static bool send_dhcp(private_dhcp_socket_t *this,
                    287:                                          dhcp_transaction_t *transaction, dhcp_t *dhcp, int optlen)
                    288: {
                    289:        host_t *dst;
                    290:        ssize_t len;
                    291: 
                    292:        dst = transaction->get_server(transaction);
                    293:        if (!dst || this->force_dst)
                    294:        {
                    295:                dst = this->dst;
                    296:        }
                    297:        len = offsetof(dhcp_t, magic_cookie) + optlen + 4;
                    298:        return sendto(this->send, dhcp, len, 0, dst->get_sockaddr(dst),
                    299:                                  *dst->get_sockaddr_len(dst)) == len;
                    300: }
                    301: 
                    302: /**
                    303:  * Send DHCP discover using a given transaction
                    304:  */
                    305: static bool discover(private_dhcp_socket_t *this,
                    306:                                         dhcp_transaction_t *transaction)
                    307: {
                    308:        dhcp_option_t *option;
                    309:        dhcp_t dhcp;
                    310:        int optlen;
                    311: 
                    312:        optlen = prepare_dhcp(this, transaction, DHCP_DISCOVER, &dhcp);
                    313: 
                    314:        DBG1(DBG_CFG, "sending DHCP DISCOVER to %H", this->dst);
                    315: 
                    316:        option = (dhcp_option_t*)&dhcp.options[optlen];
                    317:        option->type = DHCP_PARAM_REQ_LIST;
                    318:        option->len = 2;
                    319:        option->data[0] = DHCP_DNS_SERVER;
                    320:        option->data[1] = DHCP_NBNS_SERVER;
                    321:        optlen += sizeof(dhcp_option_t) + option->len;
                    322: 
                    323:        dhcp.options[optlen++] = DHCP_OPTEND;
                    324: 
                    325:        if (!send_dhcp(this, transaction, &dhcp, optlen))
                    326:        {
                    327:                DBG1(DBG_CFG, "sending DHCP DISCOVER failed: %s", strerror(errno));
                    328:                return FALSE;
                    329:        }
                    330:        return TRUE;
                    331: }
                    332: 
                    333: /**
                    334:  * Send DHCP request using a given transaction
                    335:  */
                    336: static bool request(private_dhcp_socket_t *this,
                    337:                                        dhcp_transaction_t *transaction)
                    338: {
                    339:        dhcp_option_t *option;
                    340:        dhcp_t dhcp;
                    341:        host_t *offer, *server;
                    342:        chunk_t chunk;
                    343:        int optlen;
                    344: 
                    345:        optlen = prepare_dhcp(this, transaction, DHCP_REQUEST, &dhcp);
                    346: 
                    347:        offer = transaction->get_address(transaction);
                    348:        server = transaction->get_server(transaction);
                    349:        if (!offer || !server)
                    350:        {
                    351:                return FALSE;
                    352:        }
                    353:        DBG1(DBG_CFG, "sending DHCP REQUEST for %H to %H", offer, server);
                    354: 
                    355:        option = (dhcp_option_t*)&dhcp.options[optlen];
                    356:        option->type = DHCP_REQUESTED_IP;
                    357:        option->len = 4;
                    358:        chunk = offer->get_address(offer);
                    359:        memcpy(option->data, chunk.ptr, min(chunk.len, option->len));
                    360:        optlen += sizeof(dhcp_option_t) + option->len;
                    361: 
                    362:        option = (dhcp_option_t*)&dhcp.options[optlen];
                    363:        option->type = DHCP_SERVER_ID;
                    364:        option->len = 4;
                    365:        chunk = server->get_address(server);
                    366:        memcpy(option->data, chunk.ptr, min(chunk.len, option->len));
                    367:        optlen += sizeof(dhcp_option_t) + option->len;
                    368: 
                    369:        option = (dhcp_option_t*)&dhcp.options[optlen];
                    370:        option->type = DHCP_PARAM_REQ_LIST;
                    371:        option->len = 2;
                    372:        option->data[0] = DHCP_DNS_SERVER;
                    373:        option->data[1] = DHCP_NBNS_SERVER;
                    374:        optlen += sizeof(dhcp_option_t) + option->len;
                    375: 
                    376:        dhcp.options[optlen++] = DHCP_OPTEND;
                    377: 
                    378:        if (!send_dhcp(this, transaction, &dhcp, optlen))
                    379:        {
                    380:                DBG1(DBG_CFG, "sending DHCP REQUEST failed: %s", strerror(errno));
                    381:                return FALSE;
                    382:        }
                    383:        return TRUE;
                    384: }
                    385: 
                    386: METHOD(dhcp_socket_t, enroll, dhcp_transaction_t*,
                    387:        private_dhcp_socket_t *this, identification_t *identity)
                    388: {
                    389:        dhcp_transaction_t *transaction;
                    390:        uint32_t id;
                    391:        int try;
                    392: 
                    393:        if (!this->rng->get_bytes(this->rng, sizeof(id), (uint8_t*)&id))
                    394:        {
                    395:                DBG1(DBG_CFG, "DHCP DISCOVER failed, no transaction ID");
                    396:                return NULL;
                    397:        }
                    398:        transaction = dhcp_transaction_create(id, identity);
                    399: 
                    400:        this->mutex->lock(this->mutex);
                    401:        this->discover->insert_last(this->discover, transaction);
                    402:        try = 1;
                    403:        while (try <= DHCP_TRIES && discover(this, transaction))
                    404:        {
                    405:                if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000 * try) &&
                    406:                        this->request->find_first(this->request, NULL, (void**)&transaction))
                    407:                {
                    408:                        break;
                    409:                }
                    410:                try++;
                    411:        }
                    412:        if (this->discover->remove(this->discover, transaction, NULL))
                    413:        {       /* no OFFER received */
                    414:                this->mutex->unlock(this->mutex);
                    415:                transaction->destroy(transaction);
                    416:                DBG1(DBG_CFG, "DHCP DISCOVER timed out");
                    417:                return NULL;
                    418:        }
                    419: 
                    420:        try = 1;
                    421:        while (try <= DHCP_TRIES && request(this, transaction))
                    422:        {
                    423:                if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000 * try) &&
                    424:                        this->completed->remove(this->completed, transaction, NULL))
                    425:                {
                    426:                        break;
                    427:                }
                    428:                try++;
                    429:        }
                    430:        if (this->request->remove(this->request, transaction, NULL))
                    431:        {       /* no ACK received */
                    432:                this->mutex->unlock(this->mutex);
                    433:                transaction->destroy(transaction);
                    434:                DBG1(DBG_CFG, "DHCP REQUEST timed out");
                    435:                return NULL;
                    436:        }
                    437:        this->mutex->unlock(this->mutex);
                    438: 
                    439:        return transaction;
                    440: }
                    441: 
                    442: METHOD(dhcp_socket_t, release, void,
                    443:        private_dhcp_socket_t *this, dhcp_transaction_t *transaction)
                    444: {
                    445:        dhcp_option_t *option;
                    446:        dhcp_t dhcp;
                    447:        host_t *release, *server;
                    448:        chunk_t chunk;
                    449:        int optlen;
                    450: 
                    451:        optlen = prepare_dhcp(this, transaction, DHCP_RELEASE, &dhcp);
                    452: 
                    453:        release = transaction->get_address(transaction);
                    454:        server = transaction->get_server(transaction);
                    455:        if (!release || !server)
                    456:        {
                    457:                return;
                    458:        }
                    459:        DBG1(DBG_CFG, "sending DHCP RELEASE for %H to %H", release, server);
                    460: 
                    461:        chunk = release->get_address(release);
                    462:        memcpy((char*)&dhcp.client_address, chunk.ptr,
                    463:                   min(chunk.len, sizeof(dhcp.client_address)));
                    464: 
                    465:        option = (dhcp_option_t*)&dhcp.options[optlen];
                    466:        option->type = DHCP_SERVER_ID;
                    467:        option->len = 4;
                    468:        chunk = server->get_address(server);
                    469:        memcpy(option->data, chunk.ptr, min(chunk.len, option->len));
                    470:        optlen += sizeof(dhcp_option_t) + option->len;
                    471: 
                    472:        dhcp.options[optlen++] = DHCP_OPTEND;
                    473: 
                    474:        if (!send_dhcp(this, transaction, &dhcp, optlen))
                    475:        {
                    476:                DBG1(DBG_CFG, "sending DHCP RELEASE failed: %s", strerror(errno));
                    477:        }
                    478: }
                    479: 
                    480: /**
                    481:  * Handle a DHCP OFFER
                    482:  */
                    483: static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
                    484: {
                    485:        dhcp_transaction_t *transaction = NULL;
                    486:        enumerator_t *enumerator;
                    487:        host_t *offer, *server = NULL;
                    488: 
                    489:        offer = host_create_from_chunk(AF_INET,
                    490:                                        chunk_from_thing(dhcp->your_address), 0);
                    491: 
                    492:        if (offer->is_anyaddr(offer))
                    493:        {
                    494:                server = host_create_from_chunk(AF_INET,
                    495:                                        chunk_from_thing(dhcp->server_address), 0);
                    496:                DBG1(DBG_CFG, "ignoring DHCP OFFER %+H from %H", offer, server);
                    497:                server->destroy(server);
                    498:                offer->destroy(offer);
                    499:                return;
                    500:        }
                    501: 
                    502:        this->mutex->lock(this->mutex);
                    503:        enumerator = this->discover->create_enumerator(this->discover);
                    504:        while (enumerator->enumerate(enumerator, &transaction))
                    505:        {
                    506:                if (transaction->get_id(transaction) == dhcp->transaction_id)
                    507:                {
                    508:                        this->discover->remove_at(this->discover, enumerator);
                    509:                        this->request->insert_last(this->request, transaction);
                    510:                        break;
                    511:                }
                    512:        }
                    513:        enumerator->destroy(enumerator);
                    514: 
                    515:        if (transaction)
                    516:        {
                    517:                int optsize, optpos = 0, pos;
                    518:                dhcp_option_t *option;
                    519: 
                    520:                while (optlen > sizeof(dhcp_option_t))
                    521:                {
                    522:                        option = (dhcp_option_t*)&dhcp->options[optpos];
                    523:                        optsize = sizeof(dhcp_option_t) + option->len;
                    524:                        if (option->type == DHCP_OPTEND || optlen < optsize)
                    525:                        {
                    526:                                break;
                    527:                        }
                    528:                        if (option->type == DHCP_DNS_SERVER ||
                    529:                                option->type == DHCP_NBNS_SERVER)
                    530:                        {
                    531:                                for (pos = 0; pos + 4 <= option->len; pos += 4)
                    532:                                {
                    533:                                        transaction->add_attribute(transaction, option->type ==
                    534:                                                DHCP_DNS_SERVER ? INTERNAL_IP4_DNS : INTERNAL_IP4_NBNS,
                    535:                                                chunk_create((char*)&option->data[pos], 4));
                    536:                                }
                    537:                        }
                    538:                        if (!server && option->type == DHCP_SERVER_ID && option->len == 4)
                    539:                        {
                    540:                                server = host_create_from_chunk(AF_INET,
                    541:                                                        chunk_create(option->data, 4), DHCP_SERVER_PORT);
                    542:                        }
                    543:                        optlen -= optsize;
                    544:                        optpos += optsize;
                    545:                }
                    546:                if (!server)
                    547:                {
                    548:                        server = host_create_from_chunk(AF_INET,
                    549:                                chunk_from_thing(dhcp->server_address), DHCP_SERVER_PORT);
                    550:                }
                    551:                DBG1(DBG_CFG, "received DHCP OFFER %H from %H", offer, server);
                    552:                transaction->set_address(transaction, offer->clone(offer));
                    553:                transaction->set_server(transaction, server);
                    554:        }
                    555:        this->mutex->unlock(this->mutex);
                    556:        this->condvar->broadcast(this->condvar);
                    557:        offer->destroy(offer);
                    558: }
                    559: 
                    560: /**
                    561:  * Handle a DHCP ACK
                    562:  */
                    563: static void handle_ack(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
                    564: {
                    565:        dhcp_transaction_t *transaction;
                    566:        enumerator_t *enumerator;
                    567:        host_t *offer;
                    568: 
                    569:        offer = host_create_from_chunk(AF_INET,
                    570:                                                chunk_from_thing(dhcp->your_address), 0);
                    571: 
                    572:        this->mutex->lock(this->mutex);
                    573:        enumerator = this->request->create_enumerator(this->request);
                    574:        while (enumerator->enumerate(enumerator, &transaction))
                    575:        {
                    576:                if (transaction->get_id(transaction) == dhcp->transaction_id)
                    577:                {
                    578:                        DBG1(DBG_CFG, "received DHCP ACK for %H", offer);
                    579:                        this->request->remove_at(this->request, enumerator);
                    580:                        this->completed->insert_last(this->completed, transaction);
                    581:                        break;
                    582:                }
                    583:        }
                    584:        enumerator->destroy(enumerator);
                    585:        this->mutex->unlock(this->mutex);
                    586:        this->condvar->broadcast(this->condvar);
                    587:        offer->destroy(offer);
                    588: }
                    589: 
                    590: /**
                    591:  * Receive DHCP responses
                    592:  */
                    593: static bool receive_dhcp(private_dhcp_socket_t *this, int fd,
                    594:                                                 watcher_event_t event)
                    595: {
                    596:        struct sockaddr_ll addr;
                    597:        socklen_t addr_len = sizeof(addr);
                    598:        struct __attribute__((packed)) {
                    599:                struct iphdr ip;
                    600:                struct udphdr udp;
                    601:                dhcp_t dhcp;
                    602:        } packet;
                    603:        int optlen, origoptlen, optsize, optpos = 0;
                    604:        ssize_t len;
                    605:        dhcp_option_t *option;
                    606: 
                    607:        len = recvfrom(fd, &packet, sizeof(packet), MSG_DONTWAIT,
                    608:                                        (struct sockaddr*)&addr, &addr_len);
                    609: 
                    610:        if (len >= sizeof(struct iphdr) + sizeof(struct udphdr) +
                    611:                offsetof(dhcp_t, options))
                    612:        {
                    613:                origoptlen = optlen = len - sizeof(struct iphdr) +
                    614:                                         sizeof(struct udphdr) + offsetof(dhcp_t, options);
                    615:                while (optlen > sizeof(dhcp_option_t))
                    616:                {
                    617:                        option = (dhcp_option_t*)&packet.dhcp.options[optpos];
                    618:                        optsize = sizeof(dhcp_option_t) + option->len;
                    619:                        if (option->type == DHCP_OPTEND || optlen < optsize)
                    620:                        {
                    621:                                break;
                    622:                        }
                    623:                        if (option->type == DHCP_MESSAGE_TYPE && option->len == 1)
                    624:                        {
                    625:                                switch (option->data[0])
                    626:                                {
                    627:                                        case DHCP_OFFER:
                    628:                                                handle_offer(this, &packet.dhcp, origoptlen);
                    629:                                                break;
                    630:                                        case DHCP_ACK:
                    631:                                                handle_ack(this, &packet.dhcp, origoptlen);
                    632:                                        default:
                    633:                                                break;
                    634:                                }
                    635:                                break;
                    636:                        }
                    637:                        optlen -= optsize;
                    638:                        optpos += optsize;
                    639:                }
                    640:        }
                    641:        return TRUE;
                    642: }
                    643: 
                    644: METHOD(dhcp_socket_t, destroy, void,
                    645:        private_dhcp_socket_t *this)
                    646: {
                    647:        while (this->waiting)
                    648:        {
                    649:                this->condvar->signal(this->condvar);
                    650:        }
                    651:        if (this->send > 0)
                    652:        {
                    653:                close(this->send);
                    654:        }
                    655:        if (this->receive > 0)
                    656:        {
                    657:                lib->watcher->remove(lib->watcher, this->receive);
                    658:                close(this->receive);
                    659:        }
                    660:        this->mutex->destroy(this->mutex);
                    661:        this->condvar->destroy(this->condvar);
                    662:        this->discover->destroy_offset(this->discover,
                    663:                                                                offsetof(dhcp_transaction_t, destroy));
                    664:        this->request->destroy_offset(this->request,
                    665:                                                                offsetof(dhcp_transaction_t, destroy));
                    666:        this->completed->destroy_offset(this->completed,
                    667:                                                                offsetof(dhcp_transaction_t, destroy));
                    668:        DESTROY_IF(this->rng);
                    669:        DESTROY_IF(this->dst);
                    670:        free(this);
                    671: }
                    672: 
                    673: /**
                    674:  * Bind a socket to a particular interface name
                    675:  */
                    676: static bool bind_to_device(int fd, char *iface)
                    677: {
1.1.1.2 ! misho     678:        struct ifreq ifreq = {};
1.1       misho     679: 
                    680:        if (strlen(iface) > sizeof(ifreq.ifr_name))
                    681:        {
                    682:                DBG1(DBG_CFG, "name for DHCP interface too long: '%s'", iface);
                    683:                return FALSE;
                    684:        }
                    685:        memcpy(ifreq.ifr_name, iface, sizeof(ifreq.ifr_name));
                    686:        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)))
                    687:        {
                    688:                DBG1(DBG_CFG, "binding DHCP socket to '%s' failed: %s",
                    689:                         iface, strerror(errno));
                    690:                return FALSE;
                    691:        }
                    692:        return TRUE;
                    693: }
                    694: 
                    695: /**
                    696:  * See header
                    697:  */
                    698: dhcp_socket_t *dhcp_socket_create()
                    699: {
                    700:        private_dhcp_socket_t *this;
                    701:        struct sockaddr_in src = {
                    702:                .sin_family = AF_INET,
                    703:                .sin_port = htons(DHCP_CLIENT_PORT),
                    704:                .sin_addr = {
                    705:                        .s_addr = INADDR_ANY,
                    706:                },
                    707:        };
                    708:        char *iface;
                    709:        int on = 1, rcvbuf = 0;
                    710:        struct sock_filter dhcp_filter_code[] = {
                    711:                BPF_STMT(BPF_LD+BPF_B+BPF_ABS,
                    712:                                 offsetof(struct iphdr, protocol)),
                    713:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 16),
                    714:                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, sizeof(struct iphdr) +
                    715:                                 offsetof(struct udphdr, source)),
                    716:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 0, 14),
                    717:                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, sizeof(struct iphdr) +
                    718:                                 offsetof(struct udphdr, dest)),
                    719:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_CLIENT_PORT, 2, 0),
                    720:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 1, 0),
                    721:                BPF_JUMP(BPF_JMP+BPF_JA, 10, 0, 0),
                    722:                BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
                    723:                                 sizeof(struct udphdr) + offsetof(dhcp_t, opcode)),
                    724:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BOOTREPLY, 0, 8),
                    725:                BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
                    726:                                 sizeof(struct udphdr) + offsetof(dhcp_t, hw_type)),
                    727:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPHRD_ETHER, 0, 6),
                    728:                BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
                    729:                                 sizeof(struct udphdr) + offsetof(dhcp_t, hw_addr_len)),
                    730:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 4),
                    731:                BPF_STMT(BPF_LD+BPF_W+BPF_ABS, sizeof(struct iphdr) +
                    732:                                 sizeof(struct udphdr) + offsetof(dhcp_t, magic_cookie)),
                    733:                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x63825363, 0, 2),
                    734:                BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
                    735:                BPF_STMT(BPF_RET+BPF_A, 0),
                    736:                BPF_STMT(BPF_RET+BPF_K, 0),
                    737:        };
                    738:        struct sock_fprog dhcp_filter = {
                    739:                sizeof(dhcp_filter_code) / sizeof(struct sock_filter),
                    740:                dhcp_filter_code,
                    741:        };
                    742: 
                    743:        INIT(this,
                    744:                .public = {
                    745:                        .enroll = _enroll,
                    746:                        .release = _release,
                    747:                        .destroy = _destroy,
                    748:                },
                    749:                .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
                    750:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
                    751:                .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
                    752:                .discover = linked_list_create(),
                    753:                .request = linked_list_create(),
                    754:                .completed = linked_list_create(),
                    755:        );
                    756: 
                    757:        if (!this->rng)
                    758:        {
                    759:                DBG1(DBG_CFG, "unable to create RNG");
                    760:                destroy(this);
                    761:                return NULL;
                    762:        }
                    763:        this->identity_lease = lib->settings->get_bool(lib->settings,
                    764:                                                                "%s.plugins.dhcp.identity_lease", FALSE,
                    765:                                                                lib->ns);
                    766:        this->force_dst = lib->settings->get_str(lib->settings,
                    767:                                                                "%s.plugins.dhcp.force_server_address", FALSE,
                    768:                                                                lib->ns);
                    769:        this->dst = host_create_from_string(lib->settings->get_str(lib->settings,
                    770:                                                                "%s.plugins.dhcp.server", "255.255.255.255",
                    771:                                                                lib->ns), DHCP_SERVER_PORT);
                    772:        iface = lib->settings->get_str(lib->settings, "%s.plugins.dhcp.interface",
                    773:                                                                   NULL, lib->ns);
                    774:        if (!this->dst)
                    775:        {
                    776:                DBG1(DBG_CFG, "configured DHCP server address invalid");
                    777:                destroy(this);
                    778:                return NULL;
                    779:        }
                    780: 
                    781:        this->send = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
                    782:        if (this->send == -1)
                    783:        {
                    784:                DBG1(DBG_CFG, "unable to create DHCP send socket: %s", strerror(errno));
                    785:                destroy(this);
                    786:                return NULL;
                    787:        }
                    788:        if (setsockopt(this->send, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
                    789:        {
                    790:                DBG1(DBG_CFG, "unable to reuse DHCP socket address: %s", strerror(errno));
                    791:                destroy(this);
                    792:                return NULL;
                    793:        }
                    794:        if (setsockopt(this->send, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1)
                    795:        {
                    796:                DBG1(DBG_CFG, "unable to broadcast on DHCP socket: %s", strerror(errno));
                    797:                destroy(this);
                    798:                return NULL;
                    799:        }
                    800:        /* we won't read any data from this socket, so reduce the buffer to save
                    801:         * some memory (there is some minimum, still try 0, though).
                    802:         * note that we might steal some packets from other processes if e.g. a DHCP
                    803:         * client (or server) is running on the same host, but by reducing the
                    804:         * buffer size the impact should be minimized */
                    805:        if (setsockopt(this->send, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
                    806:                                   sizeof(rcvbuf)) == -1)
                    807:        {
                    808:                DBG1(DBG_CFG, "unable to reduce receive buffer on DHCP send socket: %s",
                    809:                         strerror(errno));
                    810:                destroy(this);
                    811:                return NULL;
                    812:        }
                    813:        if (!is_broadcast(this->dst) &&
                    814:                lib->settings->get_bool(lib->settings,
                    815:                                                                "%s.plugins.dhcp.use_server_port", FALSE,
                    816:                                                                lib->ns))
                    817:        {
                    818:                /* when setting giaddr (which we do when we don't broadcast), the server
                    819:                 * should respond to the server port on that IP, according to RFC 2131,
                    820:                 * section 4.1.  while we do receive such messages via raw socket, the
                    821:                 * kernel will respond with an ICMP port unreachable if there is no
                    822:                 * socket bound to that port, which might be problematic with certain
                    823:                 * DHCP servers.  instead of opening an additional socket, that we don't
                    824:                 * actually use, we can also just send our requests from port 67.
                    825:                 * we don't do this by default, as it might cause conflicts with DHCP
                    826:                 * servers running on the same host */
                    827:                src.sin_port = htons(DHCP_SERVER_PORT);
                    828:        }
                    829:        if (bind(this->send, (struct sockaddr*)&src, sizeof(src)) == -1)
                    830:        {
                    831:                DBG1(DBG_CFG, "unable to bind DHCP send socket: %s", strerror(errno));
                    832:                destroy(this);
                    833:                return NULL;
                    834:        }
                    835: 
                    836:        this->receive = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
                    837:        if (this->receive == -1)
                    838:        {
                    839:                DBG1(DBG_NET, "opening DHCP receive socket failed: %s", strerror(errno));
                    840:                destroy(this);
                    841:                return NULL;
                    842:        }
                    843:        if (setsockopt(this->receive, SOL_SOCKET, SO_ATTACH_FILTER,
                    844:                                   &dhcp_filter, sizeof(dhcp_filter)) < 0)
                    845:        {
                    846:                DBG1(DBG_CFG, "installing DHCP socket filter failed: %s",
                    847:                         strerror(errno));
                    848:                destroy(this);
                    849:                return NULL;
                    850:        }
                    851:        if (iface)
                    852:        {
                    853:                if (!bind_to_device(this->send, iface) ||
                    854:                        !bind_to_device(this->receive, iface))
                    855:                {
                    856:                        destroy(this);
                    857:                        return NULL;
                    858:                }
                    859:        }
                    860: 
                    861:        lib->watcher->add(lib->watcher, this->receive, WATCHER_READ,
                    862:                                          (watcher_cb_t)receive_dhcp, this);
                    863: 
                    864:        return &this->public;
                    865: }

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