The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
proto_dhcpv4_udp.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 2743f5a4b80d0ee71c48e3b0d41bdcb422f7d03f $
19 * @file proto_dhcpv4_udp.c
20 * @brief DHCPv4 handler for UDP.
21 *
22 * @copyright 2018 The FreeRADIUS server project.
23 * @copyright 2018 Alan DeKok (aland@deployingradius.com)
24 */
25#define LOG_PREFIX "proto_dhcpv4_udp"
26
27#include <netdb.h>
28#include <freeradius-devel/arp/arp.h>
29#include <freeradius-devel/server/protocol.h>
30#include <freeradius-devel/util/udp.h>
31#include <freeradius-devel/util/trie.h>
32#include <freeradius-devel/io/application.h>
33#include <freeradius-devel/io/listen.h>
34#include <freeradius-devel/io/schedule.h>
35#include <freeradius-devel/protocol/dhcpv4/freeradius.internal.h>
36#include "proto_dhcpv4.h"
37
39
40typedef struct {
41 char const *name; //!< socket name
42 int sockfd;
43
44 fr_io_address_t *connection; //!< for connected sockets.
45
46 fr_stats_t stats; //!< statistics for this socket
48
49typedef struct {
50 CONF_SECTION *cs; //!< our configuration
51
52 fr_ipaddr_t ipaddr; //!< IP address to listen on.
53
54 fr_ipaddr_t src_ipaddr; //!< IP address to source replies
55
56 char const *interface; //!< Interface to bind to.
57 char const *port_name; //!< Name of the port for getservent().
58
59 uint32_t recv_buff; //!< How big the kernel's receive buffer should be.
60
61 uint32_t max_packet_size; //!< for message ring buffer.
62 uint32_t max_attributes; //!< Limit maximum decodable attributes.
63
64 uint16_t port; //!< Port to listen on.
65 uint16_t client_port; //!< Client port to reply to.
66
67 bool broadcast; //!< whether we listen for broadcast packets
68
69 bool recv_buff_is_set; //!< Whether we were provided with a receive
70 //!< buffer value.
71 bool dynamic_clients; //!< whether we have dynamic clients
72
73 fr_client_list_t *clients; //!< local clients
74 fr_client_t *default_client; //!< default 0/0 client
75
76 fr_trie_t *trie; //!< for parsed networks
77 fr_ipaddr_t *allow; //!< allowed networks for dynamic clients
78 fr_ipaddr_t *deny; //!< denied networks for dynamic clients
80
81
88
89
93
95
96 { FR_CONF_OFFSET("interface", proto_dhcpv4_udp_t, interface) },
97 { FR_CONF_OFFSET("port_name", proto_dhcpv4_udp_t, port_name) },
98
99 { FR_CONF_OFFSET("port", proto_dhcpv4_udp_t, port) },
100 { FR_CONF_OFFSET("client_port", proto_dhcpv4_udp_t, client_port) },
101 { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, proto_dhcpv4_udp_t, recv_buff) },
102
103 { FR_CONF_OFFSET("broadcast", proto_dhcpv4_udp_t, broadcast) } ,
104
105 { FR_CONF_OFFSET("dynamic_clients", proto_dhcpv4_udp_t, dynamic_clients) } ,
106 { FR_CONF_POINTER("networks", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) networks_config },
107
108 { FR_CONF_OFFSET("max_packet_size", proto_dhcpv4_udp_t, max_packet_size), .dflt = "4096" } ,
109 { FR_CONF_OFFSET("max_attributes", proto_dhcpv4_udp_t, max_attributes), .dflt = STRINGIFY(DHCPV4_MAX_ATTRIBUTES) } ,
110
112};
113
114static fr_dict_t const *dict_dhcpv4;
115
118 { .out = &dict_dhcpv4, .proto = "dhcpv4" },
119 { NULL }
120};
121
124
127 { .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4},
128 { .out = &attr_dhcp_server_identifier, .name = "Server-Identifier", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4},
129 { NULL }
130};
131
132static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len,
133 size_t *leftover)
134{
135 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
136 fr_io_address_t *address, **address_p;
137
138 int flags;
139 ssize_t data_size;
140 size_t packet_len;
141 uint8_t message_type;
142 uint32_t xid, ipaddr;
143 dhcp_packet_t *packet;
144
145 *leftover = 0; /* always for UDP */
146
147 /*
148 * Where the addresses should go. This is a special case
149 * for proto_dhcpv4.
150 */
151 address_p = (fr_io_address_t **) packet_ctx;
152 address = *address_p;
153
154 /*
155 * Tell udp_recv if we're connected or not.
156 */
157 flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL);
158
159 data_size = udp_recv(thread->sockfd, flags, &address->socket, buffer, buffer_len, recv_time_p);
160 if (data_size < 0) {
161 RATE_LIMIT_GLOBAL(PERROR, "Read error (%zd)", data_size);
162 return data_size;
163 }
164
165 if (!data_size) {
166 RATE_LIMIT_GLOBAL(WARN, "Got no data - ignoring");
167 return 0;
168 }
169
170 /*
171 * @todo - make this take "&packet_len", as the DHCPv4
172 * packet may be smaller than the parent UDP packet.
173 */
174 if (!fr_dhcpv4_ok(buffer, data_size, &message_type, &xid)) {
175 RATE_LIMIT_GLOBAL(PWARN, "Invalid packet - ignoring");
176 return 0;
177 }
178
179 packet_len = data_size;
180
181 /*
182 * We've seen a server reply to this port, but the giaddr
183 * is *not* our address. Drop it.
184 */
185 packet = (dhcp_packet_t *) buffer;
186 memcpy(&ipaddr, &packet->giaddr, 4);
187 if ((packet->opcode == 2) && (ipaddr != address->socket.inet.dst_ipaddr.addr.v4.s_addr)) {
188 DEBUG2("Ignoring server reply which was not meant for us (was for %pV).",
189 fr_box_ipaddr(address->socket.inet.dst_ipaddr));
190 return 0;
191 }
192
193 /*
194 * proto_dhcpv4 sets the priority
195 */
196
197 /*
198 * Print out what we received.
199 */
200 DEBUG2("Received %s XID %08x length %d %s", dhcp_message_types[message_type], xid,
201 (int) packet_len, thread->name);
202
203 return packet_len;
204}
205
206
207static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
208 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
209{
211 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
212
213 fr_io_track_t *track = talloc_get_type_abort(packet_ctx, fr_io_track_t);
214 proto_dhcpv4_track_t *request = talloc_get_type_abort(track->packet, proto_dhcpv4_track_t);
215 fr_socket_t socket;
216
217 int flags;
218 ssize_t data_size;
219
220 /*
221 * @todo - share a stats interface with the parent? or
222 * put the stats in the listener, so that proto_dhcpv4
223 * can update them, too.. <sigh>
224 */
225 thread->stats.total_responses++;
226
227 flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL);
228
229 /*
230 * Swap src/dst IP/port
231 */
232 fr_socket_addr_swap(&socket, &track->address->socket);
233
234 /*
235 * Figure out which kind of packet we're sending.
236 */
237 if (!thread->connection) {
238 uint8_t const *code, *sid;
239 dhcp_packet_t *packet = (dhcp_packet_t *) buffer;
240#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
241 fr_ipaddr_t primary;
242#endif
243
244 /*
245 * This isn't available in the packet header.
246 */
247 code = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_message_type);
248 if (!code || (code[1] < 1) || (code[2] == 0) || (code[2] > FR_DHCP_LEASE_ACTIVE)) {
249 WARN("Silently discarding reply due to invalid or missing message type");
250 return 0;
251 }
252
253 /*
254 * Set the source IP we'll use for sending the packets.
255 *
256 * - if src_ipaddr is unicast, use that
257 * - else if socket wasn't bound to *, then use that
258 * - else if we have ifindex, get main IP from that interface and use that.
259 * - else for offer/ack, look at option 54, for Server Identification and use that
260 * - else leave source IP as whatever is already in "socket.inet.src_ipaddr".
261 */
262 if (!fr_ipaddr_is_inaddr_any(&inst->src_ipaddr)) {
263 socket.inet.src_ipaddr = inst->src_ipaddr;
264 } else if (!fr_ipaddr_is_inaddr_any(&inst->ipaddr)) {
265 socket.inet.src_ipaddr = inst->ipaddr;
266#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
267 } else if ((address->socket.inet.ifindex > 0) &&
268 (fr_ipaddr_from_ifindex(&primary, thread->sockfd, &socket.inet.dst_ipaddr.af,
269 &socket.inet.ifindex) == 0)) {
270 socket.inet.src_ipaddr = primary;
271#endif
272 } else if (((code[2] == FR_DHCP_OFFER) || (code[2] == FR_DHCP_ACK)) &&
273 ((sid = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_dhcp_server_identifier)) != NULL) &&
274 (sid[1] == 4)) {
275 memcpy(&socket.inet.src_ipaddr.addr.v4.s_addr, sid + 2, 4);
276 }
277
278 /*
279 * If we're forwarding the Discover or Request,
280 * then we need to figure out where to forward it
281 * to?
282 */
283 if ((code[2] == FR_DHCP_DISCOVER) || (code[2] == FR_DHCP_REQUEST)) {
284 WARN("Silently discarding client request, as we do not know where to send it");
285 return 0;
286 }
287
288 /*
289 * We have GIADDR in the packet, so send it
290 * there. The packet is FROM our IP address and
291 * port, TO the destination IP address, at the
292 * same (i.e. server) port.
293 *
294 * RFC 2131 page 23
295 *
296 * "If the 'giaddr' field in a DHCP message from
297 * a client is non-zero, the server sends any
298 * return messages to the 'DHCP server' port on
299 * the BOOTP relay agent whose address appears in
300 * 'giaddr'.
301 */
302 if (packet->giaddr != INADDR_ANY) {
303 DEBUG("Reply will be sent to giaddr.");
304 socket.inet.dst_ipaddr.addr.v4.s_addr = packet->giaddr;
305 socket.inet.dst_port = inst->port;
306 socket.inet.src_port = inst->port;
307
308 /*
309 * Increase the hop count for client
310 * packets sent to the next gateway.
311 */
312 if ((code[2] == FR_DHCP_DISCOVER) ||
313 (code[2] == FR_DHCP_REQUEST)) {
314 packet->opcode = 1; /* client message */
315 packet->hops = request->hops + 1;
316 } else {
317 packet->opcode = 2; /* server message */
318 }
319
320 goto send_reply;
321 }
322
323 packet->opcode = 2; /* server message */
324
325 /*
326 * If the client port is specified, use it.
327 *
328 * RFC 2131 page 23.
329 *
330 * "DHCP messages from a server to a client are sent
331 * to the 'DHCP client' port (68)"
332 */
333 if (inst->client_port) socket.inet.dst_port = inst->client_port;
334
335 /*
336 * NAKs are broadcast when there's no giaddr.
337 *
338 * RFC 2131 page 23.
339 *
340 * "In all cases, when 'giaddr' is zero, the server
341 * broadcasts any DHCPNAK messages to 0xffffffff."
342 */
343 if (code[2] == FR_DHCP_NAK) {
344 DEBUG("Reply will be broadcast due to NAK.");
345 socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
346 goto send_reply;
347 }
348
349 /*
350 * The original packet has CIADDR, so we unicast
351 * the reply there.
352 *
353 * RFC 2131 page 23.
354 *
355 * "If the 'giaddr' field is zero and the
356 * 'ciaddr' field is nonzero, then the server
357 * unicasts DHCPOFFER and DHCPACK messages to the
358 * address in 'ciaddr'."
359 */
360 if (request->ciaddr != INADDR_ANY) {
361 DEBUG("Reply will be unicast to CIADDR from original packet.");
362 memcpy(&socket.inet.dst_ipaddr.addr.v4.s_addr, &request->ciaddr, 4);
363 goto send_reply;
364 }
365
366 /*
367 * The original packet requested a broadcast
368 * reply, so we broadcast the reply.
369 *
370 * RFC 2131 page 23.
371 *
372 * "If 'giaddr' is zero and 'ciaddr' is zero, and
373 * the broadcast bit is set, then the server
374 * broadcasts DHCPOFFER and DHCPACK messages to
375 * 0xffffffff."
376 */
377 if (request->broadcast) {
378 DEBUG("Reply will be broadcast due to client request.");
379 socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
380 goto send_reply;
381 }
382
383 /*
384 * The original packet was unicast to us, such as
385 * via a relay. We have a unicast destination
386 * address, so we just use that.
387 *
388 * This extension isn't in the RFC, but we find it useful.
389 */
390 if ((packet->yiaddr == htonl(INADDR_ANY)) &&
391 (socket.inet.dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST))) {
392 DEBUG("Reply will be unicast to source IP from original packet.");
393 goto send_reply;
394 }
395
396 /*
397 * RFC 2131 page 23.
398 *
399 * "If the broadcast bit is not set and 'giaddr'
400 * is zero and 'ciaddr' is zero, then the server
401 * unicasts DHCPOFFER and DHCPACK messages to the
402 * client's hardware address and 'yiaddr'
403 * address."
404 */
405 switch (code[2]) {
406 /*
407 * OFFERs are sent to YIADDR if we
408 * received a unicast packet from YIADDR.
409 * Otherwise, they are unicast to YIADDR
410 * (if we can update ARP), otherwise they
411 * are broadcast.
412 */
413 case FR_DHCP_OFFER:
414 /*
415 * If the packet was unicast from the
416 * client, unicast it back without
417 * updating the ARP table. We presume
418 * that the ARP table has been updated by
419 * the OS, since we received a unicast
420 * packet.
421 *
422 * This check simply makes sure that we
423 * don't needlessly update the ARP table.
424 */
425 if (memcmp(&socket.inet.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4) == 0) {
426 DEBUG("Reply will be unicast to YIADDR.");
427
428#ifdef SIOCSARP
429 } else if (inst->broadcast && inst->interface) {
430 uint8_t macaddr[6];
431 uint8_t ipaddr[4];
432
433 memcpy(&ipaddr, &packet->yiaddr, 4);
434 memcpy(&macaddr, &packet->chaddr, 6);
435
436 /*
437 * Else the OFFER was broadcast.
438 * This socket is listening for
439 * broadcast packets on a
440 * particular interface. We're
441 * too lazy to write raw UDP
442 * packets, so we update our
443 * local ARP table and then
444 * unicast the reply.
445 */
446 if (fr_arp_entry_add(thread->sockfd, inst->interface, ipaddr, macaddr) == 0) {
447 DEBUG("Reply will be unicast to YIADDR, done ARP table updates.");
448 memcpy(&socket.inet.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4);
449 } else {
450 DEBUG("Failed adding ARP entry. Reply will be broadcast.");
451 socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
452 }
453
454#endif
455 } else {
456 DEBUG("Reply will be broadcast as we do not create raw UDP sockets.");
457 socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
458 }
459 break;
460
461 /*
462 * ACKs are unicast to YIADDR
463 */
464 case FR_DHCP_ACK:
465 DEBUG("Reply will be unicast to YIADDR.");
466 memcpy(&socket.inet.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4);
467 break;
468
469 default:
470 WARN("Silently discarding reply due to unimplemented message type %d", code[2]);
471 return 0;
472 }
473
475 DEBUG("Sending %s XID %08x from %pV:%d to %pV:%d", dhcp_message_types[code[2]], packet->xid,
476 fr_box_ipaddr(socket.inet.src_ipaddr), socket.inet.src_port,
477 fr_box_ipaddr(socket.inet.dst_ipaddr), socket.inet.dst_port);
478 }
479
480 /*
481 * proto_dhcpv4 takes care of suppressing do-not-respond, etc.
482 */
483 data_size = udp_send(&socket, flags, buffer, buffer_len);
484
485 /*
486 * This socket is dead. That's an error...
487 */
488 if (data_size <= 0) return data_size;
489
490 return data_size;
491}
492
493
495{
496 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
497
498 thread->connection = connection;
499 return 0;
500}
501
502
503static void mod_network_get(int *ipproto, bool *dynamic_clients, fr_trie_t const **trie, void *instance)
504{
505 proto_dhcpv4_udp_t *inst = talloc_get_type_abort(instance, proto_dhcpv4_udp_t);
506
507 *ipproto = IPPROTO_UDP;
508 *dynamic_clients = inst->dynamic_clients;
509 *trie = inst->trie;
510}
511
512
513/** Open a UDP listener for DHCPV4
514 *
515 */
516static int mod_open(fr_listen_t *li)
517{
519 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
520
521 int sockfd, rcode;
522 fr_ipaddr_t ipaddr = inst->ipaddr;
523 uint16_t port = inst->port;
524
525 li->fd = sockfd = fr_socket_server_udp(&inst->ipaddr, &port, inst->port_name, true);
526 if (sockfd < 0) {
527 PERROR("Failed opening UDP socket");
528 error:
529 return -1;
530 }
531
532 li->app_io_addr = fr_socket_addr_alloc_inet_src(li, IPPROTO_UDP, 0, &inst->ipaddr, port);
533
534 /*
535 * Set SO_REUSEPORT before bind, so that all packets can
536 * listen on the same destination IP address.
537 */
538 {
539 int on = 1;
540
541 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
542 ERROR("Failed to set socket 'reuseport': %s", fr_syserror(errno));
543 close(sockfd);
544 return -1;
545 }
546 }
547
548#ifdef SO_RCVBUF
549 if (inst->recv_buff_is_set) {
550 int opt;
551
552 opt = inst->recv_buff;
553 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
554 WARN("Failed setting 'recv_buf': %s", fr_syserror(errno));
555 }
556 }
557#endif
558
559 if (inst->broadcast) {
560 int on = 1;
561
562 if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
563 ERROR("Failed to set broadcast option: %s", fr_syserror(errno));
564 close(sockfd);
565 return -1;
566 }
567 }
568
569 rcode = fr_socket_bind(sockfd, inst->interface, &ipaddr, &port);
570 if (rcode < 0) {
571 close(sockfd);
572 PERROR("Failed binding socket");
573 goto error;
574 }
575
576 thread->sockfd = sockfd;
577
578 fr_assert((cf_parent(inst->cs) != NULL) && (cf_parent(cf_parent(inst->cs)) != NULL)); /* listen { ... } */
579
581 NULL, 0,
582 &inst->ipaddr, inst->port,
583 inst->interface);
584
585 return 0;
586}
587
588
589/** Set the file descriptor for this socket.
590 *
591 */
592static int mod_fd_set(fr_listen_t *li, int fd)
593{
595 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
596
597 thread->sockfd = fd;
598
600 &thread->connection->socket.inet.src_ipaddr,
601 thread->connection->socket.inet.src_port,
602 &inst->ipaddr, inst->port,
603 inst->interface);
604
605 return 0;
606}
607
608
609static void *mod_track_create(UNUSED void const *instance, UNUSED void *thread_instance, UNUSED fr_client_t *client,
610 fr_io_track_t *track, uint8_t const *packet, size_t packet_len)
611{
613 dhcp_packet_t const *dhcp = (dhcp_packet_t const *) packet;
614 uint8_t const *option;
615
616 option = fr_dhcpv4_packet_get_option(dhcp, packet_len, attr_message_type);
617 if (!option || (option[1] == 0)) {
618 DEBUG("No %s in the packet - ignoring", attr_message_type->name);
619 return NULL;
620 }
621
622 t = talloc_zero(track, proto_dhcpv4_track_t);
623 if (!t) return NULL;
624
625 memcpy(&t->xid, &dhcp->xid, sizeof(t->xid));
626
627 t->message_type = option[2];
628
629 /*
630 * Track most packets by chaddr. For lease queries, that
631 * field can be the client address being queried, not the
632 * address of the system which sent the packet. So
633 * instead for lease queries, we use giaddr, which MUST
634 * exist according to RFC 4388 Section 6.3
635 */
636 if (option[2] != FR_DHCP_LEASE_QUERY) {
637 if (dhcp->hlen == 6) memcpy(&t->chaddr, &dhcp->chaddr, 6);
638 }
639
640 t->broadcast = ((dhcp->flags & FR_FLAGS_VALUE_BROADCAST) != 0);
641 t->hops = dhcp->hops;
642 memcpy(&t->ciaddr, &dhcp->ciaddr, sizeof(t->ciaddr));
643 memcpy(&t->giaddr, &dhcp->giaddr, sizeof(t->giaddr));
644
645 return t;
646}
647
648static int mod_track_compare(UNUSED void const *instance, UNUSED void *thread_instance, UNUSED fr_client_t *client,
649 void const *one, void const *two)
650{
651 int ret;
652 proto_dhcpv4_track_t const *a = one;
653 proto_dhcpv4_track_t const *b = two;
654
655 /*
656 * The tree is ordered by XIDs, which are (hopefully)
657 * pseudo-randomly distributed.
658 */
659 ret = memcmp(&a->xid, &b->xid, sizeof(a->xid));
660 if (ret != 0) return ret;
661
662 /*
663 * Hardware addresses should also be randomly distributed.
664 */
665 ret = memcmp(&a->chaddr, &b->chaddr, sizeof(a->chaddr));
666 if (ret != 0) return ret;
667
668 /*
669 * Compare giaddr, but not ciaddr
670 */
671 ret = memcmp(&a->giaddr, &b->giaddr, sizeof(a->giaddr));
672 if (ret != 0) return ret;
673
674 return (a->message_type < b->message_type) - (a->message_type > b->message_type);
675}
676
677static char const *mod_name(fr_listen_t *li)
678{
679 proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t);
680
681 return thread->name;
682}
683
684
685static int mod_instantiate(module_inst_ctx_t const *mctx)
686{
687 proto_dhcpv4_udp_t *inst = talloc_get_type_abort(mctx->mi->data, proto_dhcpv4_udp_t);
688 CONF_SECTION *conf = mctx->mi->conf;
689 size_t num;
690 CONF_ITEM *ci;
691 CONF_SECTION *server_cs;
692 fr_client_t *client;
693
694 inst->cs = conf;
695
696 /*
697 * Complain if no "ipaddr" is set.
698 */
699 if (inst->ipaddr.af == AF_UNSPEC) {
700 cf_log_err(conf, "No 'ipaddr' was specified in the 'udp' section");
701 return -1;
702 }
703
704 if (inst->ipaddr.af != AF_INET) {
705 cf_log_err(conf, "DHCPv4 transport cannot use IPv6 for 'ipaddr'");
706 return -1;
707 }
708
709 /*
710 * If src_ipaddr is defined, it must be of the same address family as "ipaddr"
711 */
712 if ((inst->src_ipaddr.af != AF_UNSPEC) &&
713 (inst->src_ipaddr.af != inst->ipaddr.af)) {
714 cf_log_err(conf, "Both 'ipaddr' and 'src_ipaddr' must be from the same address family");
715 return -1;
716 }
717
718 /*
719 * Set src_ipaddr to INADDR_NONE if not otherwise specified
720 */
721 if (inst->src_ipaddr.af == AF_UNSPEC) {
722 memset(&inst->src_ipaddr, 0, sizeof(inst->src_ipaddr));
723 inst->src_ipaddr.af = AF_INET;
724 }
725
726 if (inst->recv_buff_is_set) {
727 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32);
728 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX);
729 }
730
731 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, MIN_PACKET_SIZE);
732 FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65536);
733
734 if (!inst->port) {
735 struct servent *s;
736
737 if (!inst->port_name) {
738 cf_log_err(conf, "No 'port' was specified in the 'udp' section");
739 return -1;
740 }
741
742 s = getservbyname(inst->port_name, "udp");
743 if (!s) {
744 cf_log_err(conf, "Unknown value for 'port_name = %s", inst->port_name);
745 return -1;
746 }
747
748 inst->port = ntohl(s->s_port);
749 }
750
751#ifdef SIOCSARP
752 /*
753 * If we're listening for broadcast requests, we MUST
754 */
755 if (inst->broadcast && !inst->interface) {
756 cf_log_warn(conf, "You SHOULD set 'interface' if you have set 'broadcast = yes'.");
757 cf_log_warn(conf, "All replies will be broadcast, as ARP updates require 'interface' to be set.");
758 }
759#endif
760
761 /*
762 * Parse and create the trie for dynamic clients, even if
763 * there's no dynamic clients.
764 */
765 num = talloc_array_length(inst->allow);
766 if (!num) {
767 if (inst->dynamic_clients) {
768 cf_log_err(conf, "The 'allow' subsection MUST contain at least one 'network' entry when 'dynamic_clients = true'.");
769 return -1;
770 }
771 } else {
772 inst->trie = fr_master_io_network(inst, inst->ipaddr.af, inst->allow, inst->deny);
773 if (!inst->trie) {
774 cf_log_perr(conf, "Failed creating list of networks");
775 return -1;
776 }
777 }
778
779 ci = cf_section_to_item(mctx->mi->parent->conf); /* listen { ... } */
780 fr_assert(ci != NULL);
781 ci = cf_parent(ci);
782 fr_assert(ci != NULL);
783
784 server_cs = cf_item_to_section(ci);
785
786 /*
787 * Look up local clients, if they exist.
788 *
789 * @todo - ensure that we only parse clients which are
790 * for IPPROTO_UDP, and don't require a "secret".
791 */
792 if (cf_section_find_next(server_cs, NULL, "client", CF_IDENT_ANY)) {
793 inst->clients = client_list_parse_section(server_cs, IPPROTO_UDP, false);
794 if (!inst->clients) {
795 cf_log_err(conf, "Failed creating local clients");
796 return -1;
797 }
798 }
799
800 /*
801 * Create a fake client.
802 */
803 client = inst->default_client = talloc_zero(inst, fr_client_t);
804 if (!inst->default_client) return 0;
805
806 client->ipaddr.af = AF_INET;
807 client->ipaddr.addr.v4.s_addr = htonl(INADDR_NONE);
808 client->src_ipaddr = client->ipaddr;
809
810 client->longname = client->shortname = client->secret = talloc_strdup(client, "default");
811 client->nas_type = talloc_strdup(client, "other");
812
813 return 0;
814}
815
817{
819
820 /*
821 * Prefer local clients.
822 */
823 if (inst->clients) {
824 fr_client_t *client;
825
826 client = client_find(inst->clients, ipaddr, ipproto);
827 if (client) return client;
828 }
829
830 return inst->default_client;
831}
832
834 .common = {
835 .magic = MODULE_MAGIC_INIT,
836 .name = "dhcpv4_udp",
838 .inst_size = sizeof(proto_dhcpv4_udp_t),
839 .thread_inst_size = sizeof(proto_dhcpv4_udp_thread_t),
840 .instantiate = mod_instantiate,
841 },
842 .default_message_size = 4096,
843 .track_duplicates = true,
844
845 .open = mod_open,
846 .read = mod_read,
847 .write = mod_write,
848 .fd_set = mod_fd_set,
849 .track_create = mod_track_create,
850 .track_compare = mod_track_compare,
851 .connection_set = mod_connection_set,
852 .network_get = mod_network_get,
853 .client_find = mod_client_find,
854 .get_name = mod_name,
855};
static int const char char buffer[256]
Definition acutest.h:576
char const * fr_app_io_socket_name(TALLOC_CTX *ctx, fr_app_io_t const *app_io, fr_ipaddr_t const *src_ipaddr, int src_port, fr_ipaddr_t const *dst_ipaddr, int dst_port, char const *interface)
Definition app_io.c:32
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
int fr_arp_entry_add(int fd, char const *interface, uint8_t ipaddr[static 4], uint8_t macaddr[static 6])
#define STRINGIFY(x)
Definition build.h:197
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:518
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition cf_parse.h:339
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:298
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:448
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:428
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_SECTION * cf_section_find_next(CONF_SECTION const *cs, CONF_SECTION const *prev, char const *name1, char const *name2)
Return the next matching section.
Definition cf_util.c:1048
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:296
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#define CF_IDENT_ANY
Definition cf_util.h:78
#define ERROR(fmt,...)
Definition dhcpclient.c:41
static int sockfd
Definition dhcpclient.c:56
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
#define MIN_PACKET_SIZE
Definition dhcpv4.h:88
uint32_t yiaddr
Definition dhcpv4.h:75
uint8_t const * fr_dhcpv4_packet_get_option(dhcp_packet_t const *packet, size_t packet_size, fr_dict_attr_t const *da)
Retrieve a DHCP option from a raw packet buffer.
Definition packet.c:37
#define INADDR_BROADCAST
Definition dhcpv4.h:101
uint32_t giaddr
Definition dhcpv4.h:77
@ FR_DHCP_REQUEST
Definition dhcpv4.h:47
@ FR_DHCP_OFFER
Definition dhcpv4.h:46
@ FR_DHCP_DISCOVER
Definition dhcpv4.h:45
@ FR_DHCP_LEASE_ACTIVE
Definition dhcpv4.h:57
@ FR_DHCP_LEASE_QUERY
Definition dhcpv4.h:54
@ FR_DHCP_NAK
Definition dhcpv4.h:50
@ FR_DHCP_ACK
Definition dhcpv4.h:49
uint8_t opcode
Definition dhcpv4.h:67
uint8_t hlen
Definition dhcpv4.h:69
uint8_t hops
Definition dhcpv4.h:70
uint16_t flags
Definition dhcpv4.h:73
uint32_t xid
Definition dhcpv4.h:71
uint32_t ciaddr
Definition dhcpv4.h:74
#define DHCPV4_MAX_ATTRIBUTES
Definition dhcpv4.h:91
uint8_t chaddr[DHCP_CHADDR_LEN]
Definition dhcpv4.h:78
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:273
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:286
Specifies an attribute which must be present for the module to function.
Definition dict.h:272
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:285
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
Definition inet.c:63
int af
Address family.
Definition inet.h:64
union fr_ipaddr_t::@135 addr
IPv4/6 prefix.
fr_socket_t socket
src/dst ip and port.
Definition base.h:336
fr_socket_t * app_io_addr
for tracking duplicate sockets
Definition listen.h:36
void const * app_io_instance
I/O path configuration context.
Definition listen.h:33
void * thread_instance
thread / socket context
Definition listen.h:34
int fd
file descriptor for this socket - set by open
Definition listen.h:28
fr_ipaddr_t ipaddr
IPv4/IPv6 address of the host.
Definition client.h:83
char const * secret
Secret PSK.
Definition client.h:90
fr_ipaddr_t src_ipaddr
IPv4/IPv6 address to send responses from (family must match ipaddr).
Definition client.h:84
char const * nas_type
Type of client (arbitrary).
Definition client.h:126
char const * longname
Client identifier.
Definition client.h:87
char const * shortname
Client nickname.
Definition client.h:88
Describes a host allowed to send packets to the server.
Definition client.h:80
#define PERROR(_fmt,...)
Definition log.h:228
#define PWARN(_fmt,...)
Definition log.h:227
#define RATE_LIMIT_GLOBAL(_log, _fmt,...)
Rate limit messages using a global limiting entry.
Definition log.h:641
ssize_t udp_recv(int sockfd, int flags, fr_socket_t *socket_out, void *data, size_t data_len, fr_time_t *when)
Read a UDP packet.
Definition udp.c:144
int udp_send(fr_socket_t const *sock, int flags, void *data, size_t data_len)
Send a packet via a UDP socket.
Definition udp.c:42
fr_trie_t * fr_master_io_network(TALLOC_CTX *ctx, int af, fr_ipaddr_t *allow, fr_ipaddr_t *deny)
Create a trie from arrays of allow / deny IP addresses.
Definition master.c:2912
fr_io_address_t const * address
of this packet.. shared between multiple packets
Definition master.h:54
uint8_t * packet
really a tracking structure, not a packet
Definition master.h:56
unsigned short uint16_t
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
fr_ethernet_t chaddr
fr_client_t * default_client
default 0/0 client
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
static fr_dict_attr_t const * attr_dhcp_server_identifier
fr_ipaddr_t * allow
allowed networks for dynamic clients
uint32_t max_attributes
Limit maximum decodable attributes.
bool dynamic_clients
whether we have dynamic clients
fr_dict_autoload_t proto_dhcpv4_udp_dict[]
fr_trie_t * trie
for parsed networks
uint16_t port
Port to listen on.
fr_ipaddr_t src_ipaddr
IP address to source replies.
fr_io_address_t * connection
for connected sockets.
CONF_SECTION * cs
our configuration
char const * port_name
Name of the port for getservent().
static fr_client_t * mod_client_find(fr_listen_t *li, fr_ipaddr_t const *ipaddr, int ipproto)
static int mod_open(fr_listen_t *li)
Open a UDP listener for DHCPV4.
static fr_dict_t const * dict_dhcpv4
static const conf_parser_t udp_listen_config[]
static void mod_network_get(int *ipproto, bool *dynamic_clients, fr_trie_t const **trie, void *instance)
static void * mod_track_create(UNUSED void const *instance, UNUSED void *thread_instance, UNUSED fr_client_t *client, fr_io_track_t *track, uint8_t const *packet, size_t packet_len)
fr_ipaddr_t ipaddr
IP address to listen on.
static const conf_parser_t networks_config[]
fr_app_io_t proto_dhcpv4_udp
bool recv_buff_is_set
Whether we were provided with a receive buffer value.
fr_ipaddr_t * deny
denied networks for dynamic clients
fr_stats_t stats
statistics for this socket
fr_dict_attr_autoload_t proto_dhcpv4_udp_dict_attr[]
uint16_t client_port
Client port to reply to.
bool broadcast
whether we listen for broadcast packets
uint32_t recv_buff
How big the kernel's receive buffer should be.
static int mod_track_compare(UNUSED void const *instance, UNUSED void *thread_instance, UNUSED fr_client_t *client, void const *one, void const *two)
static int mod_connection_set(fr_listen_t *li, fr_io_address_t *connection)
static int mod_fd_set(fr_listen_t *li, int fd)
Set the file descriptor for this socket.
static char const * mod_name(fr_listen_t *li)
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
static int mod_instantiate(module_inst_ctx_t const *mctx)
fr_client_list_t * clients
local clients
uint32_t max_packet_size
for message ring buffer.
char const * name
socket name
char const * interface
Interface to bind to.
static fr_dict_attr_t const * attr_message_type
char const * dhcp_message_types[]
Definition base.c:126
bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
Check received DHCP request is valid and build fr_packet_t structure if it is.
Definition base.c:240
#define fr_assert(_expr)
Definition rad_assert.h:38
static int ipproto
static uint16_t client_port
Definition radclient.c:86
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
static void send_reply(int sockfd, fr_channel_data_t *reply)
static rs_t * conf
Definition radsniff.c:53
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:330
void * data
Module's instance data.
Definition module.h:272
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:338
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:198
uint64_t total_responses
Definition stats.h:38
int fr_socket_server_udp(fr_ipaddr_t const *src_ipaddr, uint16_t *src_port, char const *port_name, bool async)
Open an IPv4/IPv6 unconnected UDP socket.
Definition socket.c:865
int fr_socket_bind(int sockfd, char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port)
Bind a UDP/TCP v4/v6 socket to a given ipaddr src port, and interface.
Definition socket.c:227
fr_client_t * client_find(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr, int proto)
Definition client.c:373
fr_client_list_t * client_list_parse_section(CONF_SECTION *section, int proto, TLS_UNUSED bool tls_required)
Definition client.c:469
Group of clients.
Definition client.c:50
eap_aka_sim_process_conf_t * inst
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define talloc_get_type_abort_const
Definition talloc.h:287
"server local" time.
Definition time.h:69
#define UDP_FLAGS_CONNECTED
Definition udp.h:38
close(uq->fd)
static fr_socket_t * fr_socket_addr_alloc_inet_src(TALLOC_CTX *ctx, int proto, int ifindex, fr_ipaddr_t const *ipaddr, int port)
A variant of fr_socket_addr_init_inet_src will also allocates a fr_socket_t.
Definition socket.h:244
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition socket.h:78
static void fr_socket_addr_swap(fr_socket_t *dst, fr_socket_t const *src)
Swap src/dst information of a fr_socket_t.
Definition socket.h:121
Holds information necessary for binding or connecting to a socket.
Definition socket.h:63
#define fr_box_ipaddr(_val)
Definition value.h:313