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.
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.
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
16 * Helper functions to get/set addresses of UDP packets
17 * based on recvfromto by Miquel van Smoorenburg
19 * recvfromto Like recvfrom, but also stores the destination
20 * IP address. Useful on multihomed hosts.
22 * Should work on Linux and BSD.
24 * Copyright (C) 2002 Miquel van Smoorenburg.
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.
31 * sendfromto added 18/08/2003, Jan Berkel <jan@sitadelle.com>
32 * Works on Linux and FreeBSD (5.x)
37 #include <freeradius-devel/ident.h>
40 #include <freeradius-devel/autoconf.h>
44 #include <sys/types.h>
50 #include <netinet/in.h>
56 #include <freeradius-devel/udpfromto.h>
58 int udpfromto_init(int s)
60 int err = -1, opt = 1;
62 #ifdef HAVE_IP_PKTINFO
63 /* Set the IP_PKTINFO option (Linux). */
64 err = setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
67 #ifdef HAVE_IP_RECVDSTADDR
69 * Set the IP_RECVDSTADDR option (BSD).
70 * Note: IP_RECVDSTADDR == IP_SENDSRCADDR
72 err = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
77 int recvfromto(int s, void *buf, size_t len, int flags,
78 struct sockaddr *from, socklen_t *fromlen,
79 struct sockaddr *to, socklen_t *tolen)
81 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR)
89 * If from or to are set, they must be big enough
90 * to store a struct sockaddr_in.
92 if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
93 (to && (!tolen || *tolen < sizeof(struct sockaddr_in)))) {
99 * IP_PKTINFO / IP_RECVDSTADDR don't provide sin_port so we have to
100 * retrieve it using getsockname().
103 struct sockaddr_in si;
104 socklen_t l = sizeof(si);
106 ((struct sockaddr_in *)to)->sin_family = AF_INET;
107 ((struct sockaddr_in *)to)->sin_port = 0;
109 if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
110 ((struct sockaddr_in *)to)->sin_port = si.sin_port;
111 ((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
113 if (tolen) *tolen = sizeof(struct sockaddr_in);
116 /* Set up iov and msgh structures. */
117 memset(&msgh, 0, sizeof(struct msghdr));
120 msgh.msg_control = cbuf;
121 msgh.msg_controllen = sizeof(cbuf);
122 msgh.msg_name = from;
123 msgh.msg_namelen = fromlen ? *fromlen : 0;
128 /* Receive one packet. */
129 if ((err = recvmsg(s, &msgh, flags)) < 0) {
132 if (fromlen) *fromlen = msgh.msg_namelen;
134 /* Process auxiliary received data in msgh */
135 for (cmsg = CMSG_FIRSTHDR(&msgh);
137 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
139 # ifdef HAVE_IP_PKTINFO
140 if (cmsg->cmsg_level == SOL_IP
141 && cmsg->cmsg_type == IP_PKTINFO) {
142 struct in_pktinfo *i =
143 (struct in_pktinfo *)CMSG_DATA(cmsg);
145 ((struct sockaddr_in *)to)->sin_addr =
147 if (tolen) *tolen = sizeof(struct sockaddr_in);
153 # ifdef HAVE_IP_RECVDSTADDR
154 if (cmsg->cmsg_level == IPPROTO_IP
155 && cmsg->cmsg_type == IP_RECVDSTADDR) {
156 struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
158 ((struct sockaddr_in *)to)->sin_addr = *i;
159 if (tolen) *tolen = sizeof(struct sockaddr_in);
167 /* fallback: call recvfrom */
168 return recvfrom(s, buf, len, flags, from, fromlen);
169 #endif /* defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) */
172 int sendfromto(int s, void *buf, size_t len, int flags,
173 struct sockaddr *from, socklen_t fromlen,
174 struct sockaddr *to, socklen_t tolen)
176 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_SENDSRCADDR)
178 struct cmsghdr *cmsg;
180 # ifdef HAVE_IP_PKTINFO
181 char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
182 struct in_pktinfo pktinfo, *pktinfo_ptr;
183 memset(&pktinfo, 0, sizeof(struct in_pktinfo));
185 struct sockaddr_in *s4;
187 # ifdef HAVE_IP_SENDSRCADDR
188 char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))];
191 s4 = (struct sockaddr_in *) from;
194 * Source is unset, empty, or unspecified,
195 * fall back to sendto().
197 if (!s4 || (fromlen == 0) || (s4->sin_family == AF_UNSPEC)) {
198 return sendto(s, buf, len, flags, to, tolen);
201 /* Set up iov and msgh structures. */
202 memset(&msgh, 0, sizeof(struct msghdr));
207 msgh.msg_control = cmsgbuf;
208 msgh.msg_controllen = sizeof(cmsgbuf);
210 msgh.msg_namelen = tolen;
212 cmsg = CMSG_FIRSTHDR(&msgh);
214 # ifdef HAVE_IP_PKTINFO
215 cmsg->cmsg_level = SOL_IP;
216 cmsg->cmsg_type = IP_PKTINFO;
217 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
218 pktinfo.ipi_spec_dst = ((struct sockaddr_in *)from)->sin_addr;
219 pktinfo_ptr = (struct in_pktinfo *)CMSG_DATA(cmsg);
220 memcpy(pktinfo_ptr, &pktinfo, sizeof(struct in_pktinfo));
222 # ifdef HAVE_IP_SENDSRCADDR
223 cmsg->cmsg_level = IPPROTO_IP;
224 cmsg->cmsg_type = IP_SENDSRCADDR;
225 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
226 memcpy((struct in_addr *)CMSG_DATA(cmsg),
227 &((struct sockaddr_in *)from)->sin_addr, sizeof(struct in_addr));
230 return sendmsg(s, &msgh, flags);
232 /* fallback: call sendto() */
233 return sendto(s, buf, len, flags, to, tolen);
234 #endif /* defined(HAVE_IP_PKTINFO) || defined (HAVE_IP_SENDSRCADDR) */
240 * Small test program to test recvfromto/sendfromto
242 * use a virtual IP address as first argument to test
244 * reply packet should originate from virtual IP and not
245 * from the default interface the alias is bound to
250 #include <arpa/inet.h>
251 #include <sys/types.h>
252 #include <sys/wait.h>
254 #define DEF_PORT 20000 /* default port to listen on */
255 #define DESTIP "127.0.0.1" /* send packet to localhost per default */
256 #define TESTSTRING "foo" /* what to send */
257 #define TESTLEN 4 /* 4 bytes */
259 int main(int argc, char **argv)
261 struct sockaddr_in from, to, in;
263 char *destip = DESTIP;
265 int n, server_socket, client_socket, fl, tl, pid;
267 if (argc > 1) destip = argv[1];
268 if (argc > 2) port = atoi(argv[2]);
270 in.sin_family = AF_INET;
271 in.sin_addr.s_addr = INADDR_ANY;
272 in.sin_port = htons(port);
273 fl = tl = sizeof(struct sockaddr_in);
274 memset(&from, 0, sizeof(from));
275 memset(&to, 0, sizeof(to));
277 switch(pid = fork()) {
288 server_socket = socket(PF_INET, SOCK_DGRAM, 0);
289 if (udpfromto_init(server_socket) != 0) {
290 perror("udpfromto_init\n");
291 waitpid(pid, NULL, WNOHANG);
295 if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
296 perror("server: bind");
297 waitpid(pid, NULL, WNOHANG);
301 printf("server: waiting for packets on INADDR_ANY:%d\n", port);
302 if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
303 (struct sockaddr *)&from, &fl,
304 (struct sockaddr *)&to, &tl)) < 0) {
305 perror("server: recvfromto");
306 waitpid(pid, NULL, WNOHANG);
310 printf("server: received a packet of %d bytes [%s] ", n, buf);
311 printf("(src ip:port %s:%d ",
312 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
313 printf(" dst ip:port %s:%d)\n",
314 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
316 printf("server: replying from address packet was received on to source address\n");
318 if ((n = sendfromto(server_socket, buf, n, 0,
319 (struct sockaddr *)&to, tl,
320 (struct sockaddr *)&from, fl)) < 0) {
321 perror("server: sendfromto");
324 waitpid(pid, NULL, 0);
328 close(server_socket);
329 client_socket = socket(PF_INET, SOCK_DGRAM, 0);
330 if (udpfromto_init(client_socket) != 0) {
331 perror("udpfromto_init");
334 /* bind client on different port */
335 in.sin_port = htons(port+1);
336 if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
337 perror("client: bind");
341 in.sin_port = htons(port);
342 in.sin_addr.s_addr = inet_addr(destip);
344 printf("client: sending packet to %s:%d\n", destip, port);
345 if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
346 (struct sockaddr *)&in, sizeof(in)) < 0) {
347 perror("client: sendto");
351 printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
353 if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
354 (struct sockaddr *)&from, &fl,
355 (struct sockaddr *)&to, &tl)) < 0) {
356 perror("client: recvfromto");
360 printf("client: received a packet of %d bytes [%s] ", n, buf);
361 printf("(src ip:port %s:%d",
362 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
363 printf(" dst ip:port %s:%d)\n",
364 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
370 #endif /* WITH_UDPFROMTO */