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