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