Return to dhcp_socket.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / dhcp |
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: { 678: struct ifreq ifreq; 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: }