Annotation of embedaddon/strongswan/src/libcharon/plugins/dhcp/dhcp_socket.c, revision 1.1.1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>