2 * Copyright (C) 2012-2018 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
5 * Copyright (C) 2010 Martin Willi
6 * Copyright (C) 2010 revosec AG
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>.
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
19 #include "dhcp_socket.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>
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>
38 #include <processing/jobs/callback_job.h>
40 #define DHCP_SERVER_PORT 67
41 #define DHCP_CLIENT_PORT 68
44 typedef struct private_dhcp_socket_t private_dhcp_socket_t
;
47 * Private data of an dhcp_socket_t object.
49 struct private_dhcp_socket_t
{
52 * Public dhcp_socket_t interface.
57 * Random number generator
62 * List of transactions in DISCOVER
64 linked_list_t
*discover
;
67 * List of transactions in REQUEST
69 linked_list_t
*request
;
72 * List of successfully completed transactions
74 linked_list_t
*completed
;
77 * Lock for transactions
82 * Condvar to wait for transaction completion
87 * Threads waiting in condvar
102 * Do we use per-identity or random leases (and MAC addresses)
107 * DHCP server address, or broadcast
112 * Force configured destination address
118 * DHCP opcode (or BOOTP actually)
126 * Some DHCP options used
131 DHCP_NBNS_SERVER
= 44,
132 DHCP_REQUESTED_IP
= 50,
133 DHCP_MESSAGE_TYPE
= 53,
135 DHCP_PARAM_REQ_LIST
= 55,
138 } dhcp_option_type_t
;
141 * DHCP messages types in the DHCP_MESSAGE_TYPE option
152 } dhcp_message_type_t
;
154 * DHCP option encoding, a TLV
156 typedef struct __attribute__((packed
)) {
163 * DHCP message format, with a minimum size options buffer
165 typedef struct __attribute__((packed
)) {
170 uint32_t transaction_id
;
171 uint16_t number_of_seconds
;
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
;
186 * Check if the given address equals the broadcast address
188 static inline bool is_broadcast(host_t
*host
)
190 chunk_t broadcast
= chunk_from_chars(0xFF,0xFF,0xFF,0xFF);
192 return chunk_equals(broadcast
, host
->get_address(host
));
196 * Prepare a DHCP message for a given transaction
198 static int prepare_dhcp(private_dhcp_socket_t
*this,
199 dhcp_transaction_t
*transaction
,
200 dhcp_message_type_t type
, dhcp_t
*dhcp
)
203 identification_t
*identity
;
204 dhcp_option_t
*option
;
205 int optlen
= 0, remaining
;
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
))
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 */
223 /* act as relay agent */
224 src
= charon
->kernel
->get_source_addr(charon
->kernel
, this->dst
, NULL
);
227 memcpy(&dhcp
->gateway_address
, src
->get_address(src
).ptr
,
228 sizeof(dhcp
->gateway_address
));
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
)
241 id
= htonl(chunk_hash_static(chunk
));
245 id
= transaction
->get_id(transaction
);
247 memcpy(&dhcp
->client_hw_addr
[2], &id
, sizeof(id
));
249 dhcp
->magic_cookie
= htonl(0x63825363);
251 option
= (dhcp_option_t
*)&dhcp
->options
[optlen
];
252 option
->type
= DHCP_MESSAGE_TYPE
;
254 option
->data
[0] = type
;
255 optlen
+= sizeof(dhcp_option_t
) + option
->len
;
257 /* the REQUEST message has the most static overhead in the 'options' field
259 remaining
= sizeof(dhcp
->options
) - optlen
- 17;
261 if (identity
->get_type(identity
) == ID_FQDN
)
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
;
271 if (this->identity_lease
&&
272 remaining
>= sizeof(dhcp_option_t
) + 2)
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
;
284 * Send a DHCP message with given options length
286 static bool send_dhcp(private_dhcp_socket_t
*this,
287 dhcp_transaction_t
*transaction
, dhcp_t
*dhcp
, int optlen
)
292 dst
= transaction
->get_server(transaction
);
293 if (!dst
|| this->force_dst
)
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
;
303 * Send DHCP discover using a given transaction
305 static bool discover(private_dhcp_socket_t
*this,
306 dhcp_transaction_t
*transaction
)
308 dhcp_option_t
*option
;
312 optlen
= prepare_dhcp(this, transaction
, DHCP_DISCOVER
, &dhcp
);
314 DBG1(DBG_CFG
, "sending DHCP DISCOVER to %H", this->dst
);
316 option
= (dhcp_option_t
*)&dhcp
.options
[optlen
];
317 option
->type
= DHCP_PARAM_REQ_LIST
;
319 option
->data
[0] = DHCP_DNS_SERVER
;
320 option
->data
[1] = DHCP_NBNS_SERVER
;
321 optlen
+= sizeof(dhcp_option_t
) + option
->len
;
323 dhcp
.options
[optlen
++] = DHCP_OPTEND
;
325 if (!send_dhcp(this, transaction
, &dhcp
, optlen
))
327 DBG1(DBG_CFG
, "sending DHCP DISCOVER failed: %s", strerror(errno
));
334 * Send DHCP request using a given transaction
336 static bool request(private_dhcp_socket_t
*this,
337 dhcp_transaction_t
*transaction
)
339 dhcp_option_t
*option
;
341 host_t
*offer
, *server
;
345 optlen
= prepare_dhcp(this, transaction
, DHCP_REQUEST
, &dhcp
);
347 offer
= transaction
->get_address(transaction
);
348 server
= transaction
->get_server(transaction
);
349 if (!offer
|| !server
)
353 DBG1(DBG_CFG
, "sending DHCP REQUEST for %H to %H", offer
, server
);
355 option
= (dhcp_option_t
*)&dhcp
.options
[optlen
];
356 option
->type
= DHCP_REQUESTED_IP
;
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
;
362 option
= (dhcp_option_t
*)&dhcp
.options
[optlen
];
363 option
->type
= DHCP_SERVER_ID
;
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
;
369 option
= (dhcp_option_t
*)&dhcp
.options
[optlen
];
370 option
->type
= DHCP_PARAM_REQ_LIST
;
372 option
->data
[0] = DHCP_DNS_SERVER
;
373 option
->data
[1] = DHCP_NBNS_SERVER
;
374 optlen
+= sizeof(dhcp_option_t
) + option
->len
;
376 dhcp
.options
[optlen
++] = DHCP_OPTEND
;
378 if (!send_dhcp(this, transaction
, &dhcp
, optlen
))
380 DBG1(DBG_CFG
, "sending DHCP REQUEST failed: %s", strerror(errno
));
386 METHOD(dhcp_socket_t
, enroll
, dhcp_transaction_t
*,
387 private_dhcp_socket_t
*this, identification_t
*identity
)
389 dhcp_transaction_t
*transaction
;
393 if (!this->rng
->get_bytes(this->rng
, sizeof(id
), (uint8_t*)&id
))
395 DBG1(DBG_CFG
, "DHCP DISCOVER failed, no transaction ID");
398 transaction
= dhcp_transaction_create(id
, identity
);
400 this->mutex
->lock(this->mutex
);
401 this->discover
->insert_last(this->discover
, transaction
);
403 while (try <= DHCP_TRIES
&& discover(this, transaction
))
405 if (!this->condvar
->timed_wait(this->condvar
, this->mutex
, 1000 * try) &&
406 this->request
->find_first(this->request
, NULL
, (void**)&transaction
))
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");
421 while (try <= DHCP_TRIES
&& request(this, transaction
))
423 if (!this->condvar
->timed_wait(this->condvar
, this->mutex
, 1000 * try) &&
424 this->completed
->remove(this->completed
, transaction
, NULL
))
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");
437 this->mutex
->unlock(this->mutex
);
442 METHOD(dhcp_socket_t
, release
, void,
443 private_dhcp_socket_t
*this, dhcp_transaction_t
*transaction
)
445 dhcp_option_t
*option
;
447 host_t
*release
, *server
;
451 optlen
= prepare_dhcp(this, transaction
, DHCP_RELEASE
, &dhcp
);
453 release
= transaction
->get_address(transaction
);
454 server
= transaction
->get_server(transaction
);
455 if (!release
|| !server
)
459 DBG1(DBG_CFG
, "sending DHCP RELEASE for %H to %H", release
, server
);
461 chunk
= release
->get_address(release
);
462 memcpy((char*)&dhcp
.client_address
, chunk
.ptr
,
463 min(chunk
.len
, sizeof(dhcp
.client_address
)));
465 option
= (dhcp_option_t
*)&dhcp
.options
[optlen
];
466 option
->type
= DHCP_SERVER_ID
;
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
;
472 dhcp
.options
[optlen
++] = DHCP_OPTEND
;
474 if (!send_dhcp(this, transaction
, &dhcp
, optlen
))
476 DBG1(DBG_CFG
, "sending DHCP RELEASE failed: %s", strerror(errno
));
481 * Handle a DHCP OFFER
483 static void handle_offer(private_dhcp_socket_t
*this, dhcp_t
*dhcp
, int optlen
)
485 dhcp_transaction_t
*transaction
= NULL
;
486 enumerator_t
*enumerator
;
487 host_t
*offer
, *server
= NULL
;
489 offer
= host_create_from_chunk(AF_INET
,
490 chunk_from_thing(dhcp
->your_address
), 0);
492 this->mutex
->lock(this->mutex
);
493 enumerator
= this->discover
->create_enumerator(this->discover
);
494 while (enumerator
->enumerate(enumerator
, &transaction
))
496 if (transaction
->get_id(transaction
) == dhcp
->transaction_id
)
498 this->discover
->remove_at(this->discover
, enumerator
);
499 this->request
->insert_last(this->request
, transaction
);
503 enumerator
->destroy(enumerator
);
507 int optsize
, optpos
= 0, pos
;
508 dhcp_option_t
*option
;
510 while (optlen
> sizeof(dhcp_option_t
))
512 option
= (dhcp_option_t
*)&dhcp
->options
[optpos
];
513 optsize
= sizeof(dhcp_option_t
) + option
->len
;
514 if (option
->type
== DHCP_OPTEND
|| optlen
< optsize
)
518 if (option
->type
== DHCP_DNS_SERVER
||
519 option
->type
== DHCP_NBNS_SERVER
)
521 for (pos
= 0; pos
+ 4 <= option
->len
; pos
+= 4)
523 transaction
->add_attribute(transaction
, option
->type
==
524 DHCP_DNS_SERVER ? INTERNAL_IP4_DNS
: INTERNAL_IP4_NBNS
,
525 chunk_create((char*)&option
->data
[pos
], 4));
528 if (!server
&& option
->type
== DHCP_SERVER_ID
&& option
->len
== 4)
530 server
= host_create_from_chunk(AF_INET
,
531 chunk_create(option
->data
, 4), DHCP_SERVER_PORT
);
538 server
= host_create_from_chunk(AF_INET
,
539 chunk_from_thing(dhcp
->server_address
), DHCP_SERVER_PORT
);
541 DBG1(DBG_CFG
, "received DHCP OFFER %H from %H", offer
, server
);
542 transaction
->set_address(transaction
, offer
->clone(offer
));
543 transaction
->set_server(transaction
, server
);
545 this->mutex
->unlock(this->mutex
);
546 this->condvar
->broadcast(this->condvar
);
547 offer
->destroy(offer
);
553 static void handle_ack(private_dhcp_socket_t
*this, dhcp_t
*dhcp
, int optlen
)
555 dhcp_transaction_t
*transaction
;
556 enumerator_t
*enumerator
;
559 offer
= host_create_from_chunk(AF_INET
,
560 chunk_from_thing(dhcp
->your_address
), 0);
562 this->mutex
->lock(this->mutex
);
563 enumerator
= this->request
->create_enumerator(this->request
);
564 while (enumerator
->enumerate(enumerator
, &transaction
))
566 if (transaction
->get_id(transaction
) == dhcp
->transaction_id
)
568 DBG1(DBG_CFG
, "received DHCP ACK for %H", offer
);
569 this->request
->remove_at(this->request
, enumerator
);
570 this->completed
->insert_last(this->completed
, transaction
);
574 enumerator
->destroy(enumerator
);
575 this->mutex
->unlock(this->mutex
);
576 this->condvar
->broadcast(this->condvar
);
577 offer
->destroy(offer
);
581 * Receive DHCP responses
583 static bool receive_dhcp(private_dhcp_socket_t
*this, int fd
,
584 watcher_event_t event
)
586 struct sockaddr_ll addr
;
587 socklen_t addr_len
= sizeof(addr
);
588 struct __attribute__((packed
)) {
593 int optlen
, origoptlen
, optsize
, optpos
= 0;
595 dhcp_option_t
*option
;
597 len
= recvfrom(fd
, &packet
, sizeof(packet
), MSG_DONTWAIT
,
598 (struct sockaddr
*)&addr
, &addr_len
);
600 if (len
>= sizeof(struct iphdr
) + sizeof(struct udphdr
) +
601 offsetof(dhcp_t
, options
))
603 origoptlen
= optlen
= len
- sizeof(struct iphdr
) +
604 sizeof(struct udphdr
) + offsetof(dhcp_t
, options
);
605 while (optlen
> sizeof(dhcp_option_t
))
607 option
= (dhcp_option_t
*)&packet
.dhcp
.options
[optpos
];
608 optsize
= sizeof(dhcp_option_t
) + option
->len
;
609 if (option
->type
== DHCP_OPTEND
|| optlen
< optsize
)
613 if (option
->type
== DHCP_MESSAGE_TYPE
&& option
->len
== 1)
615 switch (option
->data
[0])
618 handle_offer(this, &packet
.dhcp
, origoptlen
);
621 handle_ack(this, &packet
.dhcp
, origoptlen
);
634 METHOD(dhcp_socket_t
, destroy
, void,
635 private_dhcp_socket_t
*this)
637 while (this->waiting
)
639 this->condvar
->signal(this->condvar
);
645 if (this->receive
> 0)
647 lib
->watcher
->remove(lib
->watcher
, this->receive
);
648 close(this->receive
);
650 this->mutex
->destroy(this->mutex
);
651 this->condvar
->destroy(this->condvar
);
652 this->discover
->destroy_offset(this->discover
,
653 offsetof(dhcp_transaction_t
, destroy
));
654 this->request
->destroy_offset(this->request
,
655 offsetof(dhcp_transaction_t
, destroy
));
656 this->completed
->destroy_offset(this->completed
,
657 offsetof(dhcp_transaction_t
, destroy
));
658 DESTROY_IF(this->rng
);
659 DESTROY_IF(this->dst
);
664 * Bind a socket to a particular interface name
666 static bool bind_to_device(int fd
, char *iface
)
670 if (strlen(iface
) > sizeof(ifreq
.ifr_name
))
672 DBG1(DBG_CFG
, "name for DHCP interface too long: '%s'", iface
);
675 memcpy(ifreq
.ifr_name
, iface
, sizeof(ifreq
.ifr_name
));
676 if (setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, &ifreq
, sizeof(ifreq
)))
678 DBG1(DBG_CFG
, "binding DHCP socket to '%s' failed: %s",
679 iface
, strerror(errno
));
688 dhcp_socket_t
*dhcp_socket_create()
690 private_dhcp_socket_t
*this;
691 struct sockaddr_in src
= {
692 .sin_family
= AF_INET
,
693 .sin_port
= htons(DHCP_CLIENT_PORT
),
695 .s_addr
= INADDR_ANY
,
699 int on
= 1, rcvbuf
= 0;
700 struct sock_filter dhcp_filter_code
[] = {
701 BPF_STMT(BPF_LD
+BPF_B
+BPF_ABS
,
702 offsetof(struct iphdr
, protocol
)),
703 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, IPPROTO_UDP
, 0, 16),
704 BPF_STMT(BPF_LD
+BPF_H
+BPF_ABS
, sizeof(struct iphdr
) +
705 offsetof(struct udphdr
, source
)),
706 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, DHCP_SERVER_PORT
, 0, 14),
707 BPF_STMT(BPF_LD
+BPF_H
+BPF_ABS
, sizeof(struct iphdr
) +
708 offsetof(struct udphdr
, dest
)),
709 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, DHCP_CLIENT_PORT
, 2, 0),
710 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, DHCP_SERVER_PORT
, 1, 0),
711 BPF_JUMP(BPF_JMP
+BPF_JA
, 10, 0, 0),
712 BPF_STMT(BPF_LD
+BPF_B
+BPF_ABS
, sizeof(struct iphdr
) +
713 sizeof(struct udphdr
) + offsetof(dhcp_t
, opcode
)),
714 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, BOOTREPLY
, 0, 8),
715 BPF_STMT(BPF_LD
+BPF_B
+BPF_ABS
, sizeof(struct iphdr
) +
716 sizeof(struct udphdr
) + offsetof(dhcp_t
, hw_type
)),
717 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, ARPHRD_ETHER
, 0, 6),
718 BPF_STMT(BPF_LD
+BPF_B
+BPF_ABS
, sizeof(struct iphdr
) +
719 sizeof(struct udphdr
) + offsetof(dhcp_t
, hw_addr_len
)),
720 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, 6, 0, 4),
721 BPF_STMT(BPF_LD
+BPF_W
+BPF_ABS
, sizeof(struct iphdr
) +
722 sizeof(struct udphdr
) + offsetof(dhcp_t
, magic_cookie
)),
723 BPF_JUMP(BPF_JMP
+BPF_JEQ
+BPF_K
, 0x63825363, 0, 2),
724 BPF_STMT(BPF_LD
+BPF_W
+BPF_LEN
, 0),
725 BPF_STMT(BPF_RET
+BPF_A
, 0),
726 BPF_STMT(BPF_RET
+BPF_K
, 0),
728 struct sock_fprog dhcp_filter
= {
729 sizeof(dhcp_filter_code
) / sizeof(struct sock_filter
),
739 .rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
),
740 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
741 .condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
),
742 .discover
= linked_list_create(),
743 .request
= linked_list_create(),
744 .completed
= linked_list_create(),
749 DBG1(DBG_CFG
, "unable to create RNG");
753 this->identity_lease
= lib
->settings
->get_bool(lib
->settings
,
754 "%s.plugins.dhcp.identity_lease", FALSE
,
756 this->force_dst
= lib
->settings
->get_str(lib
->settings
,
757 "%s.plugins.dhcp.force_server_address", FALSE
,
759 this->dst
= host_create_from_string(lib
->settings
->get_str(lib
->settings
,
760 "%s.plugins.dhcp.server", "255.255.255.255",
761 lib
->ns
), DHCP_SERVER_PORT
);
762 iface
= lib
->settings
->get_str(lib
->settings
, "%s.plugins.dhcp.interface",
766 DBG1(DBG_CFG
, "configured DHCP server address invalid");
771 this->send
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
772 if (this->send
== -1)
774 DBG1(DBG_CFG
, "unable to create DHCP send socket: %s", strerror(errno
));
778 if (setsockopt(this->send
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1)
780 DBG1(DBG_CFG
, "unable to reuse DHCP socket address: %s", strerror(errno
));
784 if (setsockopt(this->send
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof(on
)) == -1)
786 DBG1(DBG_CFG
, "unable to broadcast on DHCP socket: %s", strerror(errno
));
790 /* we won't read any data from this socket, so reduce the buffer to save
791 * some memory (there is some minimum, still try 0, though).
792 * note that we might steal some packets from other processes if e.g. a DHCP
793 * client (or server) is running on the same host, but by reducing the
794 * buffer size the impact should be minimized */
795 if (setsockopt(this->send
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
,
796 sizeof(rcvbuf
)) == -1)
798 DBG1(DBG_CFG
, "unable to reduce receive buffer on DHCP send socket: %s",
803 if (!is_broadcast(this->dst
) &&
804 lib
->settings
->get_bool(lib
->settings
,
805 "%s.plugins.dhcp.use_server_port", FALSE
,
808 /* when setting giaddr (which we do when we don't broadcast), the server
809 * should respond to the server port on that IP, according to RFC 2131,
810 * section 4.1. while we do receive such messages via raw socket, the
811 * kernel will respond with an ICMP port unreachable if there is no
812 * socket bound to that port, which might be problematic with certain
813 * DHCP servers. instead of opening an additional socket, that we don't
814 * actually use, we can also just send our requests from port 67.
815 * we don't do this by default, as it might cause conflicts with DHCP
816 * servers running on the same host */
817 src
.sin_port
= htons(DHCP_SERVER_PORT
);
819 if (bind(this->send
, (struct sockaddr
*)&src
, sizeof(src
)) == -1)
821 DBG1(DBG_CFG
, "unable to bind DHCP send socket: %s", strerror(errno
));
826 this->receive
= socket(AF_PACKET
, SOCK_DGRAM
, htons(ETH_P_IP
));
827 if (this->receive
== -1)
829 DBG1(DBG_NET
, "opening DHCP receive socket failed: %s", strerror(errno
));
833 if (setsockopt(this->receive
, SOL_SOCKET
, SO_ATTACH_FILTER
,
834 &dhcp_filter
, sizeof(dhcp_filter
)) < 0)
836 DBG1(DBG_CFG
, "installing DHCP socket filter failed: %s",
843 if (!bind_to_device(this->send
, iface
) ||
844 !bind_to_device(this->receive
, iface
))
851 lib
->watcher
->add(lib
->watcher
, this->receive
, WATCHER_READ
,
852 (watcher_cb_t
)receive_dhcp
, this);
854 return &this->public;