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