Set errno appropriately if we're building without IPv6 support and an IPv6 socket...
[freeradius.git] / src / lib / udpfromto.c
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$
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$")
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 #endif
71
72 /*
73  *      Linux requires IPV6_RECVPKTINFO for the setsockopt() call,
74  *      but sendmsg() and recvmsg() require IPV6_PKTINFO. <sigh>
75  *
76  *      We want all *other* (i.e. sane) systems to use IPV6_PKTINFO
77  *      for all three calls.
78  */
79 #ifdef IPV6_PKTINFO
80 #  ifdef __linux__
81 #    ifdef IPV6_RECVPKTINFO
82 #      define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
83 /* Fallback to to using recvfrom */
84 #    else
85 #      undef IPV6_RECVPKTINFO
86 #      undef IPV6_PKTINFO
87 #    endif
88 #  else
89 #    define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
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 #ifdef AF_INET6
135         } else if (si.ss_family == AF_INET6) {
136 #  ifdef IPV6_PKTINFO
137                 /*
138                  *      This should actually be standard IPv6
139                  */
140                 proto = IPPROTO_IPV6;
141
142                 /*
143                  *      Work around Linux-specific hackery.
144                  */
145                 flag = FR_IPV6_RECVPKTINFO;
146 #else
147 #  ifdef EPROTONOSUPPORT
148                 errno = EPROTONOSUPPORT;
149 #  endif
150                 return -1;
151 #  endif
152 #endif
153         } else {
154                 /*
155                  *      Unknown AF.
156                  */
157                 return -1;
158         }
159
160         return setsockopt(s, proto, flag, &opt, sizeof(opt));
161 }
162
163 int recvfromto(int s, void *buf, size_t len, int flags,
164                struct sockaddr *from, socklen_t *fromlen,
165                struct sockaddr *to, socklen_t *tolen)
166 {
167         struct msghdr msgh;
168         struct cmsghdr *cmsg;
169         struct iovec iov;
170         char cbuf[256];
171         int err;
172         struct sockaddr_storage si;
173         socklen_t si_len = sizeof(si);
174
175 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined(IPV6_PKTINFO)
176         /*
177          *      If the recvmsg() flags aren't defined, fall back to
178          *      using recvfrom().
179          */
180         to = NULL:
181 #endif
182
183         /*
184          *      Catch the case where the caller passes invalid arguments.
185          */
186         if (!to || !tolen) return recvfrom(s, buf, len, flags, from, fromlen);
187
188         /*
189          *      Clang analyzer doesn't see that getsockname initialises
190          *      the memory passed to it.
191          */
192 #ifdef __clang_analyzer__
193         memset(&si, 0, sizeof(si));
194 #endif
195
196         /*
197          *      recvmsg doesn't provide sin_port so we have to
198          *      retrieve it using getsockname().
199          */
200         if (getsockname(s, (struct sockaddr *)&si, &si_len) < 0) {
201                 return -1;
202         }
203
204         /*
205          *      Initialize the 'to' address.  It may be INADDR_ANY here,
206          *      with a more specific address given by recvmsg(), below.
207          */
208         if (si.ss_family == AF_INET) {
209 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)
210                 return recvfrom(s, buf, len, flags, from, fromlen);
211 #else
212                 struct sockaddr_in *dst = (struct sockaddr_in *) to;
213                 struct sockaddr_in *src = (struct sockaddr_in *) &si;
214
215                 if (*tolen < sizeof(*dst)) {
216                         errno = EINVAL;
217                         return -1;
218                 }
219                 *tolen = sizeof(*dst);
220                 *dst = *src;
221 #endif
222         }
223
224 #ifdef AF_INET6
225         else if (si.ss_family == AF_INET6) {
226 #if !defined(IPV6_PKTINFO)
227                 return recvfrom(s, buf, len, flags, from, fromlen);
228 #else
229                 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to;
230                 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si;
231
232                 if (*tolen < sizeof(*dst)) {
233                         errno = EINVAL;
234                         return -1;
235                 }
236                 *tolen = sizeof(*dst);
237                 *dst = *src;
238 #endif
239         }
240 #endif
241         /*
242          *      Unknown address family.
243          */
244         else {
245                 errno = EINVAL;
246                 return -1;
247         }
248
249         /* Set up iov and msgh structures. */
250         memset(&cbuf, 0, sizeof(cbuf));
251         memset(&msgh, 0, sizeof(struct msghdr));
252         iov.iov_base = buf;
253         iov.iov_len  = len;
254         msgh.msg_control = cbuf;
255         msgh.msg_controllen = sizeof(cbuf);
256         msgh.msg_name = from;
257         msgh.msg_namelen = fromlen ? *fromlen : 0;
258         msgh.msg_iov  = &iov;
259         msgh.msg_iovlen = 1;
260         msgh.msg_flags = 0;
261
262         /* Receive one packet. */
263         if ((err = recvmsg(s, &msgh, flags)) < 0) {
264                 return err;
265         }
266
267         if (fromlen) *fromlen = msgh.msg_namelen;
268
269         /* Process auxiliary received data in msgh */
270         for (cmsg = CMSG_FIRSTHDR(&msgh);
271              cmsg != NULL;
272              cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
273
274 #ifdef IP_PKTINFO
275                 if ((cmsg->cmsg_level == SOL_IP) &&
276                     (cmsg->cmsg_type == IP_PKTINFO)) {
277                         struct in_pktinfo *i =
278                                 (struct in_pktinfo *) CMSG_DATA(cmsg);
279                         ((struct sockaddr_in *)to)->sin_addr = i->ipi_addr;
280                         *tolen = sizeof(struct sockaddr_in);
281                         break;
282                 }
283 #endif
284
285 #ifdef IP_RECVDSTADDR
286                 if ((cmsg->cmsg_level == IPPROTO_IP) &&
287                     (cmsg->cmsg_type == IP_RECVDSTADDR)) {
288                         struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
289                         ((struct sockaddr_in *)to)->sin_addr = *i;
290                         *tolen = sizeof(struct sockaddr_in);
291                         break;
292                 }
293 #endif
294
295 #ifdef IPV6_PKTINFO
296                 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
297                     (cmsg->cmsg_type == IPV6_PKTINFO)) {
298                         struct in6_pktinfo *i =
299                                 (struct in6_pktinfo *) CMSG_DATA(cmsg);
300                         ((struct sockaddr_in6 *)to)->sin6_addr = i->ipi6_addr;
301                         *tolen = sizeof(struct sockaddr_in6);
302                         break;
303                 }
304 #endif
305         }
306
307         return err;
308 }
309
310 int sendfromto(int s, void *buf, size_t len, int flags,
311                struct sockaddr *from, socklen_t fromlen,
312                struct sockaddr *to, socklen_t tolen)
313 {
314         struct msghdr msgh;
315         struct cmsghdr *cmsg;
316         struct iovec iov;
317         char cbuf[256];
318
319 #ifdef __FreeBSD__
320         /*
321          *      FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
322          *      and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
323          *      with a socket which is bound to something other than
324          *      INADDR_ANY
325          */
326         struct sockaddr bound;
327         socklen_t bound_len = sizeof(bound);
328
329         if (getsockname(s, &bound, &bound_len) < 0) {
330                 return -1;
331         }
332
333         switch (bound.sa_family) {
334         case AF_INET:
335                 if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
336                         from = NULL;
337                 }
338                 break;
339
340         case AF_INET6:
341                 if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr))) {
342                         from = NULL;
343                 }
344                 break;
345         }
346 #else
347 #  if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
348         /*
349          *      If the sendmsg() flags aren't defined, fall back to
350          *      using sendto().
351          */
352         from = NULL;
353 #  endif
354 #endif
355
356         /*
357          *      Catch the case where the caller passes invalid arguments.
358          */
359         if (!from || (fromlen == 0) || (from->sa_family == AF_UNSPEC)) {
360                 return sendto(s, buf, len, flags, to, tolen);
361         }
362
363         /* Set up control buffer iov and msgh structures. */
364         memset(&cbuf, 0, sizeof(cbuf));
365         memset(&msgh, 0, sizeof(msgh));
366         memset(&iov, 0, sizeof(iov));
367         iov.iov_base = buf;
368         iov.iov_len = len;
369         msgh.msg_iov = &iov;
370         msgh.msg_iovlen = 1;
371         msgh.msg_name = to;
372         msgh.msg_namelen = tolen;
373
374         if (from->sa_family == AF_INET) {
375 #if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR)
376                 return sendto(s, buf, len, flags, to, tolen);
377 #else
378                 struct sockaddr_in *s4 = (struct sockaddr_in *) from;
379
380 #  ifdef IP_PKTINFO
381                 struct in_pktinfo *pkt;
382
383                 msgh.msg_control = cbuf;
384                 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
385
386                 cmsg = CMSG_FIRSTHDR(&msgh);
387                 cmsg->cmsg_level = SOL_IP;
388                 cmsg->cmsg_type = IP_PKTINFO;
389                 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
390
391                 pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
392                 memset(pkt, 0, sizeof(*pkt));
393                 pkt->ipi_spec_dst = s4->sin_addr;
394 #  endif
395
396 #  ifdef IP_SENDSRCADDR
397                 struct in_addr *in;
398
399                 msgh.msg_control = cbuf;
400                 msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
401
402                 cmsg = CMSG_FIRSTHDR(&msgh);
403                 cmsg->cmsg_level = IPPROTO_IP;
404                 cmsg->cmsg_type = IP_SENDSRCADDR;
405                 cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
406
407                 in = (struct in_addr *) CMSG_DATA(cmsg);
408                 *in = s4->sin_addr;
409 #  endif
410 #endif  /* IP_PKTINFO or IP_SENDSRCADDR */
411         }
412
413 #ifdef AF_INET6
414         else if (from->sa_family == AF_INET6) {
415 #  if !defined(IPV6_PKTINFO)
416                 return sendto(s, buf, len, flags, to, tolen);
417 #  else
418                 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) from;
419
420                 struct in6_pktinfo *pkt;
421
422                 msgh.msg_control = cbuf;
423                 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
424
425                 cmsg = CMSG_FIRSTHDR(&msgh);
426                 cmsg->cmsg_level = IPPROTO_IPV6;
427                 cmsg->cmsg_type = IPV6_PKTINFO;
428                 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
429
430                 pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
431                 memset(pkt, 0, sizeof(*pkt));
432                 pkt->ipi6_addr = s6->sin6_addr;
433 #  endif        /* IPV6_PKTINFO */
434         }
435 #endif
436
437         /*
438          *      Unknown address family.
439          */
440         else {
441                 errno = EINVAL;
442                 return -1;
443         }
444
445         return sendmsg(s, &msgh, flags);
446 }
447
448
449 #ifdef TESTING
450 /*
451  *      Small test program to test recvfromto/sendfromto
452  *
453  *      use a virtual IP address as first argument to test
454  *
455  *      reply packet should originate from virtual IP and not
456  *      from the default interface the alias is bound to
457  */
458 #  include <sys/wait.h>
459
460 #  define DEF_PORT 20000                /* default port to listen on */
461 #  define DESTIP "127.0.0.1"    /* send packet to localhost per default */
462 #  define TESTSTRING "foo"      /* what to send */
463 #  define TESTLEN 4                     /* 4 bytes */
464
465 int main(int argc, char **argv)
466 {
467         struct sockaddr_in from, to, in;
468         char buf[TESTLEN];
469         char *destip = DESTIP;
470         uint16_t port = DEF_PORT;
471         int n, server_socket, client_socket, fl, tl, pid;
472
473         if (argc > 1) destip = argv[1];
474         if (argc > 2) port = atoi(argv[2]);
475
476         in.sin_family = AF_INET;
477         in.sin_addr.s_addr = INADDR_ANY;
478         in.sin_port = htons(port);
479         fl = tl = sizeof(struct sockaddr_in);
480         memset(&from, 0, sizeof(from));
481         memset(&to,   0, sizeof(to));
482
483         switch(pid = fork()) {
484                 case -1:
485                         perror("fork");
486                         return 0;
487                 case 0:
488                         /* child */
489                         usleep(100000);
490                         goto client;
491         }
492
493         /* parent: server */
494         server_socket = socket(PF_INET, SOCK_DGRAM, 0);
495         if (udpfromto_init(server_socket) != 0) {
496                 perror("udpfromto_init\n");
497                 waitpid(pid, NULL, WNOHANG);
498                 return 0;
499         }
500
501         if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
502                 perror("server: bind");
503                 waitpid(pid, NULL, WNOHANG);
504                 return 0;
505         }
506
507         printf("server: waiting for packets on INADDR_ANY:%d\n", port);
508         if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
509             (struct sockaddr *)&from, &fl,
510             (struct sockaddr *)&to, &tl)) < 0) {
511                 perror("server: recvfromto");
512                 waitpid(pid, NULL, WNOHANG);
513                 return 0;
514         }
515
516         printf("server: received a packet of %d bytes [%s] ", n, buf);
517         printf("(src ip:port %s:%d ",
518                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
519         printf(" dst ip:port %s:%d)\n",
520                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
521
522         printf("server: replying from address packet was received on to source address\n");
523
524         if ((n = sendfromto(server_socket, buf, n, 0,
525                 (struct sockaddr *)&to, tl,
526                 (struct sockaddr *)&from, fl)) < 0) {
527                 perror("server: sendfromto");
528         }
529
530         waitpid(pid, NULL, 0);
531         return 0;
532
533 client:
534         close(server_socket);
535         client_socket = socket(PF_INET, SOCK_DGRAM, 0);
536         if (udpfromto_init(client_socket) != 0) {
537                 perror("udpfromto_init");
538                 fr_exit_now(0);
539         }
540         /* bind client on different port */
541         in.sin_port = htons(port+1);
542         if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
543                 perror("client: bind");
544                 fr_exit_now(0);
545         }
546
547         in.sin_port = htons(port);
548         in.sin_addr.s_addr = inet_addr(destip);
549
550         printf("client: sending packet to %s:%d\n", destip, port);
551         if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
552                         (struct sockaddr *)&in, sizeof(in)) < 0) {
553                 perror("client: sendto");
554                 fr_exit_now(0);
555         }
556
557         printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
558
559         if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
560             (struct sockaddr *)&from, &fl,
561             (struct sockaddr *)&to, &tl)) < 0) {
562                 perror("client: recvfromto");
563                 fr_exit_now(0);
564         }
565
566         printf("client: received a packet of %d bytes [%s] ", n, buf);
567         printf("(src ip:port %s:%d",
568                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
569         printf(" dst ip:port %s:%d)\n",
570                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
571
572         fr_exit_now(0);
573 }
574
575 #endif /* TESTING */
576 #endif /* WITH_UDPFROMTO */