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