The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
udpfromto.c
Go to the documentation of this file.
1 /*
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This library 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 GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with this library; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /** API for sending and receiving packets on unconnected UDP sockets
18  *
19  * Like recvfrom, but also stores the destination IP address. Useful on multihomed hosts.
20  *
21  * @file src/lib/util/udpfromto.c
22  *
23  * @copyright 2007 Alan DeKok (aland@deployingradius.com)
24  * @copyright 2002 Miquel van Smoorenburg
25  */
26 RCSID("$Id: 5a8e92fb8fb6a3855f0d1dc30d321799b92ea81e $")
27 
28 #include <freeradius-devel/util/udpfromto.h>
29 
30 #ifdef HAVE_SYS_UIO_H
31 # include <sys/uio.h>
32 #endif
33 
34 #include <fcntl.h>
35 
36 /*
37  * More portability idiocy
38  * Mac OSX Lion doesn't define SOL_IP. But IPPROTO_IP works.
39  */
40 #ifndef SOL_IP
41 # define SOL_IP IPPROTO_IP
42 #endif
43 
44 /*
45  * glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and
46  * change IPV6_PKTINFO This is only supported in Linux kernel >=
47  * 2.6.14
48  *
49  * This is only an approximation because the kernel version that libc
50  * was compiled against could be older or newer than the one being
51  * run. But this should not be a problem -- we just keep using the
52  * old kernel interface.
53  */
54 #ifdef __linux__
55 # ifdef IPV6_RECVPKTINFO
56 # include <linux/version.h>
57 # if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
58 # ifdef IPV6_2292PKTINFO
59 # undef IPV6_RECVPKTINFO
60 # undef IPV6_PKTINFO
61 # define IPV6_RECVPKTINFO IPV6_2292PKTINFO
62 # define IPV6_PKTINFO IPV6_2292PKTINFO
63 # endif
64 # endif
65 /* Fall back to the legacy socket option if IPV6_RECVPKTINFO isn't defined */
66 # elif defined(IPV6_2292PKTINFO)
67 # define IPV6_RECVPKTINFO IPV6_2292PKTINFO
68 # endif
69 #else
70 
71 /*
72  * For everything that's not Linux we assume RFC 3542 compliance
73  * - setsockopt() takes IPV6_RECVPKTINFO
74  * - cmsg_type is IPV6_PKTINFO (in sendmsg, recvmsg)
75  *
76  * If we don't have IPV6_RECVPKTINFO defined but do have IPV6_PKTINFO
77  * defined, chances are the API is RFC2292 compliant and we need to use
78  * IPV6_PKTINFO for both.
79  */
80 # if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
81 # define IPV6_RECVPKTINFO IPV6_PKTINFO
82 
83 /*
84  * Ensure IPV6_RECVPKTINFO is not defined somehow if we have we
85  * don't have IPV6_PKTINFO.
86  */
87 # elif !defined(IPV6_PKTINFO)
88 # undef IPV6_RECVPKTINFO
89 # endif
90 #endif
91 
92 int udpfromto_init(int s, int af)
93 {
94  int proto = 0, flag = 0, opt = 1;
95 
96  errno = ENOSYS;
97 
98  if (af == AF_UNSPEC) {
99  struct sockaddr_storage si;
100  socklen_t si_len = sizeof(si);
101 
102  /*
103  * Static analyzer doesn't see that getsockname initialises
104  * the memory passed to it.
105  */
106 #ifdef STATIC_ANALYZER
107  memset(&si, 0, sizeof(si));
108 #endif
109 
110  if (getsockname(s, (struct sockaddr *) &si, &si_len) < 0) {
111  return -1;
112  }
113 
114  af = si.ss_family;
115  }
116 
117  if (af == AF_INET) {
118 #ifdef HAVE_IP_PKTINFO
119  /*
120  * Linux
121  */
122  proto = SOL_IP;
123  flag = IP_PKTINFO;
124 #else
125 # ifdef IP_RECVDSTADDR
126 
127  /*
128  * Set the IP_RECVDSTADDR option (BSD). Note:
129  * IP_RECVDSTADDR == IP_SENDSRCADDR
130  */
131  proto = IPPROTO_IP;
132  flag = IP_RECVDSTADDR;
133 # else
134  return -1;
135 # endif
136 #endif
137 
138 #if defined(AF_INET6) && defined(IPV6_PKTINFO)
139  } else if (af == AF_INET6) {
140  /*
141  * This should actually be standard IPv6
142  */
143  proto = IPPROTO_IPV6;
144 
145  /*
146  * Work around Linux-specific hackery.
147  */
148  flag = IPV6_RECVPKTINFO;
149  } else {
150 #endif
151 
152  /*
153  * Unknown AF. Return an error if possible.
154  */
155 # ifdef EPROTONOSUPPORT
156  errno = EPROTONOSUPPORT;
157 # endif
158  return -1;
159  }
160 
161  return setsockopt(s, proto, flag, &opt, sizeof(opt));
162 }
163 
164 /** Read a packet from a file descriptor, retrieving additional header information
165  *
166  * Abstracts away the complexity of using the complexity of using recvmsg().
167  *
168  * In addition to reading data from the file descriptor, the src and dst addresses
169  * and the receiving interface index are retrieved. This enables us to send
170  * replies using the correct IP interface, in the case where the server is multihomed.
171  * This is not normally possible on unconnected datagram sockets.
172  *
173  * @param[in] fd The file descriptor to read from.
174  * @param[out] buf Where to write the received datagram data.
175  * @param[in] len of buf.
176  * @param[in] flags passed unmolested to recvmsg.
177  * @param[out] ifindex The interface which received the datagram (may be NULL).
178  * Will only be populated if to is not NULL.
179  * @param[out] from Where to write the source address.
180  * @param[in] from_len Length of the structure pointed to by from.
181  * @param[out] to Where to write the destination address. If NULL recvmsg()
182  * will be used instead.
183  * @param[in] to_len Length of the structure pointed to by to.
184  * @param[out] when the packet was received (may be NULL). If SO_TIMESTAMP is
185  * not available or SO_TIMESTAMP Was not set on the socket,
186  * then another method will be used instead to get the time.
187  * @return
188  * - 0 on success.
189  * - -1 on failure.
190  */
191 int recvfromto(int fd, void *buf, size_t len, int flags,
192  int *ifindex,
193  struct sockaddr *from, socklen_t *from_len,
194  struct sockaddr *to, socklen_t *to_len,
195  fr_time_t *when)
196 {
197  struct msghdr msgh;
198  struct cmsghdr *cmsg;
199  struct iovec iov;
200  char cbuf[256];
201  int ret;
202  struct sockaddr_storage si;
203  socklen_t si_len = sizeof(si);
204 
205 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined(IPV6_PKTINFO)
206  /*
207  * If the recvmsg() flags aren't defined, fall back to
208  * using recvfrom().
209  */
210  to = NULL:
211 #endif
212 
213  /*
214  * Catch the case where the caller passes invalid arguments.
215  */
216  if (!to || !to_len) {
217  if (when) *when = fr_time();
218  return recvfrom(fd, buf, len, flags, from, from_len);
219  }
220 
221  /*
222  * Static analyzer doesn't see that getsockname initialises
223  * the memory passed to it.
224  */
225 #ifdef STATIC_ANALYZER
226  memset(&si, 0, sizeof(si));
227 #endif
228 
229  /*
230  * recvmsg doesn't provide sin_port so we have to
231  * retrieve it using getsockname().
232  */
233  if (getsockname(fd, (struct sockaddr *)&si, &si_len) < 0) {
234  return -1;
235  }
236 
237  /*
238  * Initialize the 'to' address. It may be INADDR_ANY here,
239  * with a more specific address given by recvmsg(), below.
240  */
241  if (si.ss_family == AF_INET) {
242 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)
243  return recvfrom(fd, buf, len, flags, from, from_len);
244 #else
245  struct sockaddr_in *dst = (struct sockaddr_in *) to;
246  struct sockaddr_in *src = (struct sockaddr_in *) &si; //-V641
247 
248  if (*to_len < sizeof(*dst)) {
249  errno = EINVAL;
250  return -1;
251  }
252  *to_len = sizeof(*dst);
253  *dst = *src;
254 #endif
255  }
256 
257 #ifdef AF_INET6
258  else if (si.ss_family == AF_INET6) {
259 #if !defined(IPV6_PKTINFO)
260  return recvfrom(fd, buf, len, flags, from, from_len);
261 #else
262  struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to;
263  struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si; //-V641
264 
265  if (*to_len < sizeof(*dst)) {
266  errno = EINVAL;
267  return -1;
268  }
269  *to_len = sizeof(*dst);
270  *dst = *src;
271 #endif
272  }
273 #endif
274  /*
275  * Unknown address family.
276  */
277  else {
278  errno = EINVAL;
279  return -1;
280  }
281 
282  /* Set up iov and msgh structures. */
283  memset(&cbuf, 0, sizeof(cbuf));
284  memset(&msgh, 0, sizeof(struct msghdr));
285  iov.iov_base = buf;
286  iov.iov_len = len;
287  msgh.msg_control = cbuf;
288  msgh.msg_controllen = sizeof(cbuf);
289  msgh.msg_name = from;
290  msgh.msg_namelen = from_len ? *from_len : 0;
291  msgh.msg_iov = &iov;
292  msgh.msg_iovlen = 1;
293  msgh.msg_flags = 0;
294 
295  /* Receive one packet. */
296  ret = recvmsg(fd, &msgh, flags);
297  if (ret < 0) return ret;
298 
299  if (from_len) *from_len = msgh.msg_namelen;
300 
301  if (ifindex) *ifindex = 0;
302  if (when) *when = fr_time_wrap(0);
303 
304 /*
305  * Needed for emscripten, seems to be an issue in CMSG_NXTHDR
306  */
307 DIAG_OFF(sign-compare)
308  /* Process auxiliary received data in msgh */
309  for (cmsg = CMSG_FIRSTHDR(&msgh);
310  cmsg != NULL;
311  cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
312 DIAG_ON(sign-compare)
313 
314 #ifdef IP_PKTINFO
315  if ((cmsg->cmsg_level == SOL_IP) &&
316  (cmsg->cmsg_type == IP_PKTINFO)) {
317  struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
318 
319  ((struct sockaddr_in *)to)->sin_addr = i->ipi_addr;
320  *to_len = sizeof(struct sockaddr_in);
321 
322  if (ifindex) *ifindex = i->ipi_ifindex;
323 
324  break;
325  }
326 #endif
327 
328 #ifdef IP_RECVDSTADDR
329  if ((cmsg->cmsg_level == IPPROTO_IP) &&
330  (cmsg->cmsg_type == IP_RECVDSTADDR)) {
331  struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
332 
333  ((struct sockaddr_in *)to)->sin_addr = *i;
334 
335  *to_len = sizeof(struct sockaddr_in);
336 
337  break;
338  }
339 #endif
340 
341 #ifdef IPV6_PKTINFO
342  if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
343  (cmsg->cmsg_type == IPV6_PKTINFO)) {
344  struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
345 
346  ((struct sockaddr_in6 *)to)->sin6_addr = i->ipi6_addr;
347  *to_len = sizeof(struct sockaddr_in6);
348 
349  if (ifindex) *ifindex = i->ipi6_ifindex;
350 
351  break;
352  }
353 #endif
354 
355 #ifdef SO_TIMESTAMP
356  if (when && (cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == SO_TIMESTAMP)) {
357  *when = fr_time_from_timeval((struct timeval *)CMSG_DATA(cmsg));
358  }
359 #endif
360 
361 #ifdef SO_TIMESTAMPNS
362  if (when && (cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == SO_TIMESTAMPNS)) {
363  *when = fr_time_from_timespec((struct timespec *)CMSG_DATA(cmsg));
364  }
365 #endif
366  }
367 
368  if (when && fr_time_eq(*when, fr_time_wrap(0))) *when = fr_time();
369 
370  return ret;
371 }
372 
373 /** Send packet via a file descriptor, setting the src address and outbound interface
374  *
375  * Abstracts away the complexity of using the complexity of using sendmsg().
376  *
377  * @param[in] fd The file descriptor to write to.
378  * @param[in] buf Where to read datagram data from.
379  * @param[in] len of datagram data.
380  * @param[in] flags passed unmolested to sendmsg.
381  * @param[in] ifindex The interface on which to send the datagram.
382  * If automatic interface selection is desired, value should be 0.
383  * @param[in] from The source address.
384  * @param[in] from_len Length of the structure pointed to by from.
385  * @param[in] to The destination address.
386  * @param[in] to_len Length of the structure pointed to by to.
387  * @return
388  * - 0 on success.
389  * - -1 on failure.
390  */
391 int sendfromto(int fd, void *buf, size_t len, int flags,
392  int ifindex,
393  struct sockaddr *from, socklen_t from_len,
394  struct sockaddr *to, socklen_t to_len)
395 {
396  struct msghdr msgh;
397  struct iovec iov;
398  char cbuf[256];
399 
400  /*
401  * Unknown address family, die.
402  */
403  if (from && (from->sa_family != AF_INET) && (from->sa_family != AF_INET6)) {
404  errno = EINVAL;
405  return -1;
406  }
407 
408 #ifdef __FreeBSD__
409  /*
410  * FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
411  * and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
412  * with a socket which is bound to something other than
413  * INADDR_ANY
414  */
415  {
416  struct sockaddr bound;
417  socklen_t bound_len = sizeof(bound);
418 
419  if (getsockname(fd, &bound, &bound_len) < 0) {
420  return -1;
421  }
422 
423  switch (bound.sa_family) {
424  case AF_INET:
425  if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
426  from = NULL;
427  }
428  break;
429 
430  case AF_INET6:
431  if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr)) {
432  from = NULL;
433  }
434  break;
435  }
436  }
437 #endif /* !__FreeBSD__ */
438 
439  /*
440  * If the sendmsg() flags aren't defined, fall back to
441  * using sendto(). These flags are defined on FreeBSD,
442  * but laying it out this way simplifies the look of the
443  * code.
444  */
445 # if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR)
446  if (from && from->sa_family == AF_INET) from = NULL;
447 # endif
448 
449 # if !defined(IPV6_PKTINFO)
450  if (from && from->sa_family == AF_INET6) from = NULL;
451 # endif
452 
453  /*
454  * No "from" or "from" is 0.0.0.0 or ::/0, and there's no
455  * interface binding, just use regular sendto.
456  */
457  if (!from || (from_len == 0) ||
458  ((ifindex == 0) &&
459  ((from->sa_family == AF_INET &&
460  (((struct sockaddr_in *) from)->sin_addr.s_addr == INADDR_ANY)) ||
461  (from->sa_family == AF_INET6 &&
462  IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) from)->sin6_addr))))) {
463  return sendto(fd, buf, len, flags, to, to_len);
464  }
465 
466  /* Set up control buffer iov and msgh structures. */
467  memset(&cbuf, 0, sizeof(cbuf));
468  memset(&msgh, 0, sizeof(msgh));
469  memset(&iov, 0, sizeof(iov));
470  iov.iov_base = buf;
471  iov.iov_len = len;
472 
473  msgh.msg_iov = &iov;
474  msgh.msg_iovlen = 1;
475  msgh.msg_name = to;
476  msgh.msg_namelen = to_len;
477 
478 # if defined(IP_PKTINFO) || defined(IP_SENDSRCADDR)
479  if (from->sa_family == AF_INET) {
480  struct sockaddr_in *s4 = (struct sockaddr_in *) from;
481 
482 # ifdef IP_PKTINFO
483  struct cmsghdr *cmsg;
484  struct in_pktinfo *pkt;
485 
486  msgh.msg_control = cbuf;
487  msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
488 
489  cmsg = CMSG_FIRSTHDR(&msgh);
490  cmsg->cmsg_level = SOL_IP;
491  cmsg->cmsg_type = IP_PKTINFO;
492  cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
493 
494  pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
495  memset(pkt, 0, sizeof(*pkt));
496  pkt->ipi_spec_dst = s4->sin_addr;
497  pkt->ipi_ifindex = ifindex;
498 
499 # elif defined(IP_SENDSRCADDR)
500  struct cmsghdr *cmsg;
501  struct in_addr *in;
502 
503  msgh.msg_control = cbuf;
504  msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
505 
506  cmsg = CMSG_FIRSTHDR(&msgh);
507  cmsg->cmsg_level = IPPROTO_IP;
508  cmsg->cmsg_type = IP_SENDSRCADDR;
509  cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
510 
511  in = (struct in_addr *) CMSG_DATA(cmsg);
512  *in = s4->sin_addr;
513 # endif
514  }
515 #endif
516 
517 # if defined(IPV6_PKTINFO)
518  if (from->sa_family == AF_INET6) {
519  struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) from;
520 
521  struct cmsghdr *cmsg;
522  struct in6_pktinfo *pkt;
523 
524  msgh.msg_control = cbuf;
525  msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
526 
527  cmsg = CMSG_FIRSTHDR(&msgh);
528  cmsg->cmsg_level = IPPROTO_IPV6;
529  cmsg->cmsg_type = IPV6_PKTINFO;
530  cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
531 
532  pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
533  memset(pkt, 0, sizeof(*pkt));
534  pkt->ipi6_addr = s6->sin6_addr;
535  pkt->ipi6_ifindex = ifindex;
536  }
537 # endif /* IPV6_PKTINFO */
538 
539  return sendmsg(fd, &msgh, flags);
540 }
541 
542 
543 #ifdef TESTING
544 /*
545  * Small test program to test recvfromto/sendfromto
546  *
547  * use a virtual IP address as first argument to test
548  *
549  * reply packet should originate from virtual IP and not
550  * from the default interface the alias is bound to
551  */
552 # include <sys/wait.h>
553 
554 # define DEF_PORT 20000 /* default port to listen on */
555 # define DESTIP "127.0.0.1" /* send packet to localhost per default */
556 # define TESTSTRING "foo" /* what to send */
557 # define TESTLEN 4 /* 4 bytes */
558 
559 int main(int argc, char **argv)
560 {
561  struct sockaddr_in from, to, in;
562  char buf[TESTLEN];
563  char *destip = DESTIP;
564  uint16_t port = DEF_PORT;
565  int n, server_socket, client_socket, fl, tl, pid;
566  int ifindex;
567  fr_time_t when;
568 
569  if (argc > 1) destip = argv[1];
570  if (argc > 2) port = atoi(argv[2]);
571 
572  in.sin_family = AF_INET;
573  in.sin_addr.s_addr = INADDR_ANY;
574  in.sin_port = htons(port);
575  fl = tl = sizeof(struct sockaddr_in);
576  memset(&from, 0, sizeof(from));
577  memset(&to, 0, sizeof(to));
578 
579  switch (pid = fork()) {
580  case -1:
581  perror("fork");
582  return 0;
583  case 0:
584  /* child */
585  usleep(100000);
586  goto client;
587  }
588 
589  /* parent: server */
590  server_socket = socket(PF_INET, SOCK_DGRAM, 0);
591  if (udpfromto_init(server_socket, AF_INET) != 0) {
592  perror("udpfromto_init\n");
593  waitpid(pid, NULL, WNOHANG);
594  return 0;
595  }
596 
597  if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
598  perror("server: bind");
599  waitpid(pid, NULL, WNOHANG);
600  return 0;
601  }
602 
603  printf("server: waiting for packets on INADDR_ANY:%d\n", port);
604  if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
605  (struct sockaddr *)&from, &fl,
606  (struct sockaddr *)&to, &tl, &ifindex, &when)) < 0) {
607  perror("server: recvfromto");
608  waitpid(pid, NULL, WNOHANG);
609  return 0;
610  }
611 
612  printf("server: received a packet of %d bytes [%s] ", n, buf);
613  printf("(src ip:port %s:%d ",
614  inet_ntoa(from.sin_addr), ntohs(from.sin_port));
615  printf(" dst ip:port %s:%d) via if %i\n",
616  inet_ntoa(to.sin_addr), ntohs(to.sin_port), ifindex);
617 
618  printf("server: replying from address packet was received on to source address\n");
619 
620  if ((n = sendfromto(server_socket, buf, n, 0,
621  0,
622  (struct sockaddr *)&to, tl,
623  (struct sockaddr *)&from, fl)) < 0) {
624  perror("server: sendfromto");
625  }
626 
627  waitpid(pid, NULL, 0);
628  return 0;
629 
630 client:
631  close(server_socket);
632  client_socket = socket(PF_INET, SOCK_DGRAM, 0);
633  fr_assert_fatal_msg(udpfromto_init(client_socket, AF_INET) != 0, "udpfromto_init - %s", fr_syserror(errno));
634 
635  /* bind client on different port */
636  in.sin_port = htons(port+1);
637  fr_assert_fatal_msg(bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0,
638  "client: bind - %s", fr_syserror(errno));
639 
640  in.sin_port = htons(port);
641  in.sin_addr.s_addr = inet_addr(destip);
642 
643  printf("client: sending packet to %s:%d\n", destip, port);
644  fr_assert_fatal_msg(sendto(client_socket, TESTSTRING, TESTLEN, 0, (struct sockaddr *)&in, sizeof(in)) < 0,
645  "client: sendto");
646 
647  printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
648 
649  fr_assert_fatal_msg((n = recvfromto(client_socket, buf, sizeof(buf), 0,
650  (struct sockaddr *)&from, &fl,
651  (struct sockaddr *)&to, &tl, &ifindex, NULL)) < 0,
652  "client: recvfromto - %s", fr_syserror(errno));
653 
654  printf("client: received a packet of %d bytes [%s] ", n, buf);
655  printf("(src ip:port %s:%d",
656  inet_ntoa(from.sin_addr), ntohs(from.sin_port));
657  printf(" dst ip:port %s:%d) via if %i\n",
658  inet_ntoa(to.sin_addr), ntohs(to.sin_port), ifindex);
659 
660  return EXIT_SUCCESS;
661 }
662 
663 #endif /* TESTING */
int n
Definition: acutest.h:577
#define RCSID(id)
Definition: build.h:444
#define DIAG_ON(_x)
Definition: build.h:419
#define DIAG_OFF(_x)
Definition: build.h:418
int main(int argc, char **argv)
Definition: dhcpclient.c:521
static fr_slen_t in
Definition: dict.h:645
waitpid(reap->pid_ev->pid, &status, 0)
static int client_socket(char const *server)
Definition: radmin.c:182
unsigned short uint16_t
Definition: merged_model.c:31
static char const * proto(int id, int porttype)
Definition: radwho.c:85
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
static fr_time_t fr_time_from_timeval(struct timeval const *when_tv)
Convert a timeval (wallclock time) to a fr_time_t (internal time)
Definition: time.h:894
#define fr_time_wrap(_time)
Definition: time.h:145
#define fr_time_eq(_a, _b)
Definition: time.h:241
static fr_time_t fr_time_from_timespec(struct timespec const *when_ts)
Convert a timespec (wallclock time) to a fr_time_t (internal time)
Definition: time.h:876
"server local" time.
Definition: time.h:69
close(uq->fd)
int sendfromto(int fd, void *buf, size_t len, int flags, int ifindex, struct sockaddr *from, socklen_t from_len, struct sockaddr *to, socklen_t to_len)
Send packet via a file descriptor, setting the src address and outbound interface.
Definition: udpfromto.c:391
#define SOL_IP
Definition: udpfromto.c:41
int udpfromto_init(int s, int af)
Definition: udpfromto.c:92
int recvfromto(int fd, void *buf, size_t len, int flags, int *ifindex, struct sockaddr *from, socklen_t *from_len, struct sockaddr *to, socklen_t *to_len, fr_time_t *when)
Read a packet from a file descriptor, retrieving additional header information.
Definition: udpfromto.c:191