7665e85f9bff53912e8122964d22168bc823ad72
[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  *  Helper functions to get/set addresses of UDP packets
17  *  based on recvfromto by Miquel van Smoorenburg
18  *
19  * recvfromto   Like recvfrom, but also stores the destination
20  *              IP address. Useful on multihomed hosts.
21  *
22  *              Should work on Linux and BSD.
23  *
24  *              Copyright (C) 2002 Miquel van Smoorenburg.
25  *
26  *              This program is free software; you can redistribute it and/or
27  *              modify it under the terms of the GNU Lesser General Public
28  *              License as published by the Free Software Foundation; either
29  *              version 2 of the License, or (at your option) any later version.
30  *      Copyright (C) 2007 Alan DeKok <aland@deployingradius.com>
31  *
32  * sendfromto   added 18/08/2003, Jan Berkel <jan@sitadelle.com>
33  *              Works on Linux and FreeBSD (5.x)
34  *
35  * Version: $Id$
36  */
37
38 #include <freeradius-devel/ident.h>
39 RCSID("$Id$")
40
41 #include <freeradius-devel/udpfromto.h>
42
43 #ifdef WITH_UDPFROMTO
44
45 #ifdef HAVE_SYS_UIO_H
46 #include <sys/uio.h>
47 #endif
48
49 #include <fcntl.h>
50
51 /*
52  *      More portability idiocy
53  *      Mac OSX Lion doesn't define SOL_IP.  But IPPROTO_IP works.
54  */
55 #ifndef SOL_IP
56 #define SOL_IP IPPROTO_IP
57 #endif
58
59 /*
60  * glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and
61  * change IPV6_PKTINFO This is only supported in Linux kernel >=
62  * 2.6.14
63  *
64  * This is only an approximation because the kernel version that libc
65  * was compiled against could be older or newer than the one being
66  * run.  But this should not be a problem -- we just keep using the
67  * old kernel interface.
68  */
69 #ifdef __linux__
70 #  if defined IPV6_RECVPKTINFO
71 #    include <linux/version.h>
72 #    if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
73 #      if defined IPV6_2292PKTINFO
74 #        undef IPV6_RECVPKTINFO
75 #        undef IPV6_PKTINFO
76 #        define IPV6_RECVPKTINFO IPV6_2292PKTINFO
77 #        define IPV6_PKTINFO IPV6_2292PKTINFO
78 #      endif
79 #    endif
80 #  endif
81 #endif
82
83 /*
84  *      Linux requires IPV6_RECVPKTINFO for the setsockopt() call,
85  *      but sendmsg() and recvmsg() require IPV6_PKTINFO. <sigh>
86  *
87  *      We want all *other* (i.e. sane) systems to use IPV6_PKTINFO
88  *      for all three calls.
89  */
90 #ifdef IPV6_PKTINFO
91 #ifdef __linux__
92 #define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
93 #else
94 #define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
95 #endif
96 #endif
97
98 int udpfromto_init(int s)
99 {
100         int proto, flag, opt = 1;
101         struct sockaddr_storage si;
102         socklen_t si_len = sizeof(si);
103
104         errno = ENOSYS;
105
106         proto = -1;
107
108         if (getsockname(s, (struct sockaddr *) &si, &si_len) < 0) {
109                 return -1;
110         }
111
112         if (si.ss_family == AF_INET) {
113 #ifdef HAVE_IP_PKTINFO
114                 /*
115                  *      Linux
116                  */
117                 proto = SOL_IP;
118                 flag = IP_PKTINFO;
119 #endif
120
121 #ifdef IP_RECVDSTADDR
122                 /*
123                  *      Set the IP_RECVDSTADDR option (BSD).  Note:
124                  *      IP_RECVDSTADDR == IP_SENDSRCADDR
125                  */
126                 proto = IPPROTO_IP;
127                 flag = IP_RECVDSTADDR;
128 #endif
129
130 #ifdef AF_INET6
131         } else if (si.ss_family == AF_INET6) {
132 #ifdef IPV6_PKTINFO
133                 /*
134                  *      This should actually be standard IPv6
135                  */
136                 proto = IPPROTO_IPV6;
137
138                 /*
139                  *      Work around Linux-specific hackery.
140                  */
141                 flag = FR_IPV6_RECVPKTINFO;
142 #endif
143 #endif
144         } else {
145                 /*
146                  *      Unknown AF.
147                  */
148                 return -1;
149         }
150
151         /*
152          *      Unsupported.  Don't worry about it.
153          */
154         if (proto < 0) return 0;
155
156         return setsockopt(s, proto, flag, &opt, sizeof(opt));
157 }
158
159 int recvfromto(int s, void *buf, size_t len, int flags,
160                struct sockaddr *from, socklen_t *fromlen,
161                struct sockaddr *to, socklen_t *tolen)
162 {
163         struct msghdr msgh;
164         struct cmsghdr *cmsg;
165         struct iovec iov;
166         char cbuf[256];
167         int err;
168         struct sockaddr_storage si;
169         socklen_t si_len = sizeof(si);
170
171 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined (IPV6_PKTINFO)
172         /*
173          *      If the recvmsg() flags aren't defined, fall back to
174          *      using recvfrom().
175          */
176         to = NULL:
177 #endif
178
179         /*
180          *      Catch the case where the caller passes invalid arguments.
181          */
182         if (!to || !tolen) return recvfrom(s, buf, len, flags, from, fromlen);
183
184         /*
185          *      recvmsg doesn't provide sin_port so we have to
186          *      retrieve it using getsockname().
187          */
188         if (getsockname(s, (struct sockaddr *)&si, &si_len) < 0) {
189                 return -1;
190         }
191
192         /*
193          *      Initialize the 'to' address.  It may be INADDR_ANY here,
194          *      with a more specific address given by recvmsg(), below.
195          */
196         if (si.ss_family == AF_INET) {
197 #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)
198                 return recvfrom(s, buf, len, flags, from, fromlen);
199 #else
200                 struct sockaddr_in *dst = (struct sockaddr_in *) to;
201                 struct sockaddr_in *src = (struct sockaddr_in *) &si;
202
203                 if (*tolen < sizeof(*dst)) {
204                         errno = EINVAL;
205                         return -1;
206                 }
207                 *tolen = sizeof(*dst);
208                 *dst = *src;
209 #endif
210         }
211
212 #ifdef AF_INET6
213         else if (si.ss_family == AF_INET6) {
214 #if !defined(IPV6_PKTINFO)
215                 return recvfrom(s, buf, len, flags, from, fromlen);
216 #else
217                 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to;
218                 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si;
219
220                 if (*tolen < sizeof(*dst)) {
221                         errno = EINVAL;
222                         return -1;
223                 }
224                 *tolen = sizeof(*dst);
225                 *dst = *src;
226 #endif
227         }
228 #endif
229         /*
230          *      Unknown address family.
231          */
232         else {
233                 errno = EINVAL;
234                 return -1;
235         }
236
237         /* Set up iov and msgh structures. */
238         memset(&msgh, 0, sizeof(struct msghdr));
239         iov.iov_base = buf;
240         iov.iov_len  = len;
241         msgh.msg_control = cbuf;
242         msgh.msg_controllen = sizeof(cbuf);
243         msgh.msg_name = from;
244         msgh.msg_namelen = fromlen ? *fromlen : 0;
245         msgh.msg_iov  = &iov;
246         msgh.msg_iovlen = 1;
247         msgh.msg_flags = 0;
248
249         /* Receive one packet. */
250         if ((err = recvmsg(s, &msgh, flags)) < 0) {
251                 return err;
252         }
253
254         if (fromlen) *fromlen = msgh.msg_namelen;
255
256         /* Process auxiliary received data in msgh */
257         for (cmsg = CMSG_FIRSTHDR(&msgh);
258              cmsg != NULL;
259              cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
260
261 #ifdef IP_PKTINFO
262                 if ((cmsg->cmsg_level == SOL_IP) &&
263                     (cmsg->cmsg_type == IP_PKTINFO)) {
264                         struct in_pktinfo *i =
265                                 (struct in_pktinfo *) CMSG_DATA(cmsg);
266                         ((struct sockaddr_in *)to)->sin_addr = i->ipi_addr;
267                         *tolen = sizeof(struct sockaddr_in);
268                         break;
269                 }
270 #endif
271
272 #ifdef IP_RECVDSTADDR
273                 if ((cmsg->cmsg_level == IPPROTO_IP) &&
274                     (cmsg->cmsg_type == IP_RECVDSTADDR)) {
275                         struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
276                         ((struct sockaddr_in *)to)->sin_addr = *i;
277                         *tolen = sizeof(struct sockaddr_in);
278                         break;
279                 }
280 #endif
281
282 #ifdef IPV6_PKTINFO
283                 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
284                     (cmsg->cmsg_type == IPV6_PKTINFO)) {
285                         struct in6_pktinfo *i =
286                                 (struct in6_pktinfo *) CMSG_DATA(cmsg);
287                         ((struct sockaddr_in6 *)to)->sin6_addr = i->ipi6_addr;
288                         *tolen = sizeof(struct sockaddr_in6);
289                         break;
290                 }
291 #endif
292         }
293
294         return err;
295 }
296
297 int sendfromto(int s, void *buf, size_t len, int flags,
298                struct sockaddr *from, socklen_t fromlen,
299                struct sockaddr *to, socklen_t tolen)
300 {
301         struct msghdr msgh;
302         struct cmsghdr *cmsg;
303         struct iovec iov;
304         char cbuf[256];
305
306 #ifdef __FreeBSD__
307         /*
308          *      FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
309          *      and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
310          *      with a socket which is bound to something other than
311          *      INADDR_ANY
312          */
313         struct sockaddr bound;
314         socklen_t bound_len = sizeof(bound);
315
316         if (getsockname(s, &bound, &bound_len) < 0) {
317                 return -1;
318         }
319
320         switch (bound.sa_family) {
321         case AF_INET:
322                 if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
323                         from = NULL;
324                 }
325                 break;
326
327         case AF_INET6:
328                 if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr)) {
329                         from = NULL;
330                 }
331                 break;
332         }
333 #else
334 #  if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
335         /*
336          *      If the sendmsg() flags aren't defined, fall back to
337          *      using sendto().
338          */
339         from = NULL;
340 #  endif
341 #endif
342
343         /*
344          *      Catch the case where the caller passes invalid arguments.
345          */
346         if (!from || (fromlen == 0) || (from->sa_family == AF_UNSPEC)) {
347                 return sendto(s, buf, len, flags, to, tolen);
348         }
349
350         /* Set up iov and msgh structures. */
351         memset(&msgh, 0, sizeof(struct msghdr));
352         iov.iov_base = buf;
353         iov.iov_len = len;
354         msgh.msg_iov = &iov;
355         msgh.msg_iovlen = 1;
356         msgh.msg_name = to;
357         msgh.msg_namelen = tolen;
358
359         if (from->sa_family == AF_INET) {
360 #if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR)
361                 return sendto(s, buf, len, flags, to, tolen);
362 #else
363                 struct sockaddr_in *s4 = (struct sockaddr_in *) from;
364
365 #ifdef IP_PKTINFO
366                 struct in_pktinfo *pkt;
367
368                 msgh.msg_control = cbuf;
369                 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
370
371                 cmsg = CMSG_FIRSTHDR(&msgh);
372                 cmsg->cmsg_level = SOL_IP;
373                 cmsg->cmsg_type = IP_PKTINFO;
374                 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
375
376                 pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
377                 memset(pkt, 0, sizeof(*pkt));
378                 pkt->ipi_spec_dst = s4->sin_addr;
379 #endif
380
381 #ifdef IP_SENDSRCADDR
382                 struct in_addr *in;
383
384                 msgh.msg_control = cbuf;
385                 msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
386
387                 cmsg = CMSG_FIRSTHDR(&msgh);
388                 cmsg->cmsg_level = IPPROTO_IP;
389                 cmsg->cmsg_type = IP_SENDSRCADDR;
390                 cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
391
392                 in = (struct in_addr *) CMSG_DATA(cmsg);
393                 *in = s4->sin_addr;
394 #endif
395 #endif  /* IP_PKTINFO or IP_SENDSRCADDR */
396         }
397
398 #ifdef AF_INET6
399         else if (from->sa_family == AF_INET6) {
400 #if !defined(IPV6_PKTINFO)
401                 return sendto(s, buf, len, flags, to, tolen);
402 #else
403                 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) from;
404
405                 struct in6_pktinfo *pkt;
406
407                 msgh.msg_control = cbuf;
408                 msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
409
410                 cmsg = CMSG_FIRSTHDR(&msgh);
411                 cmsg->cmsg_level = IPPROTO_IPV6;
412                 cmsg->cmsg_type = IPV6_PKTINFO;
413                 cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
414
415                 pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
416                 memset(pkt, 0, sizeof(*pkt));
417                 pkt->ipi6_addr = s6->sin6_addr;
418 #endif  /* IPV6_PKTINFO */
419         }
420 #endif
421
422         /*
423          *      Unknown address family.
424          */
425         else {
426                 errno = EINVAL;
427                 return -1;
428         }
429
430         return sendmsg(s, &msgh, flags);
431 }
432
433
434 #ifdef TESTING
435 /*
436  *      Small test program to test recvfromto/sendfromto
437  *
438  *      use a virtual IP address as first argument to test
439  *
440  *      reply packet should originate from virtual IP and not
441  *      from the default interface the alias is bound to
442  */
443
444 #include <stdio.h>
445 #include <stdlib.h>
446 #include <arpa/inet.h>
447 #include <sys/types.h>
448 #include <sys/wait.h>
449
450 #define DEF_PORT 20000          /* default port to listen on */
451 #define DESTIP "127.0.0.1"      /* send packet to localhost per default */
452 #define TESTSTRING "foo"        /* what to send */
453 #define TESTLEN 4                       /* 4 bytes */
454
455 int main(int argc, char **argv)
456 {
457         struct sockaddr_in from, to, in;
458         char buf[TESTLEN];
459         char *destip = DESTIP;
460         int port = DEF_PORT;
461         int n, server_socket, client_socket, fl, tl, pid;
462
463         if (argc > 1) destip = argv[1];
464         if (argc > 2) port = atoi(argv[2]);
465
466         in.sin_family = AF_INET;
467         in.sin_addr.s_addr = INADDR_ANY;
468         in.sin_port = htons(port);
469         fl = tl = sizeof(struct sockaddr_in);
470         memset(&from, 0, sizeof(from));
471         memset(&to,   0, sizeof(to));
472
473         switch(pid = fork()) {
474                 case -1:
475                         perror("fork");
476                         return 0;
477                 case 0:
478                         /* child */
479                         usleep(100000);
480                         goto client;
481         }
482
483         /* parent: server */
484         server_socket = socket(PF_INET, SOCK_DGRAM, 0);
485         if (udpfromto_init(server_socket) != 0) {
486                 perror("udpfromto_init\n");
487                 waitpid(pid, NULL, WNOHANG);
488                 return 0;
489         }
490
491         if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
492                 perror("server: bind");
493                 waitpid(pid, NULL, WNOHANG);
494                 return 0;
495         }
496
497         printf("server: waiting for packets on INADDR_ANY:%d\n", port);
498         if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
499             (struct sockaddr *)&from, &fl,
500             (struct sockaddr *)&to, &tl)) < 0) {
501                 perror("server: recvfromto");
502                 waitpid(pid, NULL, WNOHANG);
503                 return 0;
504         }
505
506         printf("server: received a packet of %d bytes [%s] ", n, buf);
507         printf("(src ip:port %s:%d ",
508                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
509         printf(" dst ip:port %s:%d)\n",
510                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
511
512         printf("server: replying from address packet was received on to source address\n");
513
514         if ((n = sendfromto(server_socket, buf, n, 0,
515                 (struct sockaddr *)&to, tl,
516                 (struct sockaddr *)&from, fl)) < 0) {
517                 perror("server: sendfromto");
518         }
519
520         waitpid(pid, NULL, 0);
521         return 0;
522
523 client:
524         close(server_socket);
525         client_socket = socket(PF_INET, SOCK_DGRAM, 0);
526         if (udpfromto_init(client_socket) != 0) {
527                 perror("udpfromto_init");
528                 _exit(0);
529         }
530         /* bind client on different port */
531         in.sin_port = htons(port+1);
532         if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
533                 perror("client: bind");
534                 _exit(0);
535         }
536
537         in.sin_port = htons(port);
538         in.sin_addr.s_addr = inet_addr(destip);
539
540         printf("client: sending packet to %s:%d\n", destip, port);
541         if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
542                         (struct sockaddr *)&in, sizeof(in)) < 0) {
543                 perror("client: sendto");
544                 _exit(0);
545         }
546
547         printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
548
549         if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
550             (struct sockaddr *)&from, &fl,
551             (struct sockaddr *)&to, &tl)) < 0) {
552                 perror("client: recvfromto");
553                 _exit(0);
554         }
555
556         printf("client: received a packet of %d bytes [%s] ", n, buf);
557         printf("(src ip:port %s:%d",
558                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
559         printf(" dst ip:port %s:%d)\n",
560                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
561
562         _exit(0);
563 }
564
565 #endif /* TESTING */
566 #endif /* WITH_UDPFROMTO */