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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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)
38 static const char rcsid[] = "$Id$";
40 #include <sys/types.h>
46 #include <netinet/in.h>
52 #include "udpfromto.h"
54 /* Remove this when autoconf can detect this. */
55 #if defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)
56 # define HAVE_IP_RECVDSTADDR
59 #if defined(IP_SENDSRCADDR) && !defined(HAVE_IP_SENDSRCADDR)
60 # define HAVE_IP_SENDSRCADDR
63 int udpfromto_init(int s)
65 int err = -1, opt = 1;
67 #ifdef HAVE_IP_PKTINFO
68 /* Set the IP_PKTINFO option (Linux). */
69 err = setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
72 #ifdef HAVE_IP_RECVDSTADDR
74 * Set the IP_RECVDSTADDR option (BSD).
75 * Note: IP_RECVDSTADDR == IP_SENDSRCADDR
77 err = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
82 int recvfromto(int s, void *buf, size_t len, int flags,
83 struct sockaddr *from, socklen_t *fromlen,
84 struct sockaddr *to, socklen_t *tolen)
86 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR)
94 * If from or to are set, they must be big enough
95 * to store a struct sockaddr_in.
97 if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
98 (to && (!tolen || *tolen < sizeof(struct sockaddr_in)))) {
104 * IP_PKTINFO / IP_RECVDSTADDR don't provide sin_port so we have to
105 * retrieve it using getsockname().
108 struct sockaddr_in si;
109 socklen_t l = sizeof(si);
111 ((struct sockaddr_in *)to)->sin_family = AF_INET;
112 ((struct sockaddr_in *)to)->sin_port = 0;
114 if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
115 ((struct sockaddr_in *)to)->sin_port = si.sin_port;
116 ((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
118 if (tolen) *tolen = sizeof(struct sockaddr_in);
121 /* Set up iov and msgh structures. */
122 memset(&msgh, 0, sizeof(struct msghdr));
125 msgh.msg_control = cbuf;
126 msgh.msg_controllen = sizeof(cbuf);
127 msgh.msg_name = from;
128 msgh.msg_namelen = fromlen ? *fromlen : 0;
133 /* Receive one packet. */
134 if ((err = recvmsg(s, &msgh, flags)) < 0) {
137 if (fromlen) *fromlen = msgh.msg_namelen;
139 /* Process auxiliary received data in msgh */
140 for (cmsg = CMSG_FIRSTHDR(&msgh);
142 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
144 # ifdef HAVE_IP_PKTINFO
145 if (cmsg->cmsg_level == SOL_IP
146 && cmsg->cmsg_type == IP_PKTINFO) {
147 struct in_pktinfo *i =
148 (struct in_pktinfo *)CMSG_DATA(cmsg);
150 ((struct sockaddr_in *)to)->sin_addr =
152 if (tolen) *tolen = sizeof(struct sockaddr_in);
158 # ifdef HAVE_IP_RECVDSTADDR
159 if (cmsg->cmsg_level == IPPROTO_IP
160 && cmsg->cmsg_type == IP_RECVDSTADDR) {
161 struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
163 ((struct sockaddr_in *)to)->sin_addr = *i;
164 if (tolen) *tolen = sizeof(struct sockaddr_in);
172 /* fallback: call recvfrom */
173 return recvfrom(s, buf, len, flags, from, fromlen);
174 #endif /* defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) */
177 int sendfromto(int s, void *buf, size_t len, int flags,
178 struct sockaddr *from, socklen_t fromlen,
179 struct sockaddr *to, socklen_t tolen)
181 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_SENDSRCADDR)
183 struct cmsghdr *cmsg;
185 # ifdef HAVE_IP_PKTINFO
186 char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
187 struct in_pktinfo pktinfo, *pktinfo_ptr;
188 memset(&pktinfo, 0, sizeof(struct in_pktinfo));
191 # ifdef HAVE_IP_SENDSRCADDR
192 char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))];
195 /* Set up iov and msgh structures. */
196 memset(&msgh, 0, sizeof(struct msghdr));
201 msgh.msg_control = cmsgbuf;
202 msgh.msg_controllen = sizeof(cmsgbuf);
204 msgh.msg_namelen = tolen;
206 cmsg = CMSG_FIRSTHDR(&msgh);
208 # ifdef HAVE_IP_PKTINFO
209 cmsg->cmsg_level = SOL_IP;
210 cmsg->cmsg_type = IP_PKTINFO;
211 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
212 pktinfo.ipi_spec_dst = ((struct sockaddr_in *)from)->sin_addr;
213 pktinfo_ptr = (struct in_pktinfo *)CMSG_DATA(cmsg);
214 memcpy(pktinfo_ptr, &pktinfo, sizeof(struct in_pktinfo));
216 # ifdef HAVE_IP_SENDSRCADDR
217 cmsg->cmsg_level = IPPROTO_IP;
218 cmsg->cmsg_type = IP_SENDSRCADDR;
219 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
220 memcpy((struct in_addr *)CMSG_DATA(cmsg),
221 &((struct sockaddr_in *)from)->sin_addr, sizeof(struct in_addr));
224 return sendmsg(s, &msgh, flags);
226 /* fallback: call sendto() */
227 return sendto(s, buf, len, flags, to, tolen);
228 #endif /* defined(HAVE_IP_PKTINFO) || defined (HAVE_IP_SENDSRCADDR) */
234 * Small test program to test recvfromto/sendfromto
236 * use a virtual IP address as first argument to test
238 * reply packet should originate from virtual IP and not
239 * from the default interface the alias is bound to
244 #include <arpa/inet.h>
245 #include <sys/types.h>
246 #include <sys/wait.h>
248 #define DEF_PORT 20000 /* default port to listen on */
249 #define DESTIP "127.0.0.1" /* send packet to localhost per default */
250 #define TESTSTRING "foo" /* what to send */
251 #define TESTLEN 4 /* 4 bytes */
253 int main(int argc, char **argv)
255 struct sockaddr_in from, to, in;
257 char *destip = DESTIP;
259 int n, server_socket, client_socket, fl, tl, pid;
261 if (argc > 1) destip = argv[1];
262 if (argc > 2) port = atoi(argv[2]);
264 in.sin_family = AF_INET;
265 in.sin_addr.s_addr = INADDR_ANY;
266 in.sin_port = htons(port);
267 fl = tl = sizeof(struct sockaddr_in);
268 memset(&from, 0, sizeof(from));
269 memset(&to, 0, sizeof(to));
271 switch(pid = fork()) {
282 server_socket = socket(PF_INET, SOCK_DGRAM, 0);
283 if (udpfromto_init(server_socket) != 0) {
284 perror("udpfromto_init\n");
285 waitpid(pid, NULL, WNOHANG);
289 if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
290 perror("server: bind");
291 waitpid(pid, NULL, WNOHANG);
295 printf("server: waiting for packets on INADDR_ANY:%d\n", port);
296 if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
297 (struct sockaddr *)&from, &fl,
298 (struct sockaddr *)&to, &tl)) < 0) {
299 perror("server: recvfromto");
300 waitpid(pid, NULL, WNOHANG);
304 printf("server: received a packet of %d bytes [%s] ", n, buf);
305 printf("(src ip:port %s:%d ",
306 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
307 printf(" dst ip:port %s:%d)\n",
308 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
310 printf("server: replying from address packet was received on to source address\n");
312 if ((n = sendfromto(server_socket, buf, n, 0,
313 (struct sockaddr *)&to, tl,
314 (struct sockaddr *)&from, fl)) < 0) {
315 perror("server: sendfromto");
318 waitpid(pid, NULL, 0);
322 close(server_socket);
323 client_socket = socket(PF_INET, SOCK_DGRAM, 0);
324 if (udpfromto_init(client_socket) != 0) {
325 perror("udpfromto_init");
328 /* bind client on different port */
329 in.sin_port = htons(port+1);
330 if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
331 perror("client: bind");
335 in.sin_port = htons(port);
336 in.sin_addr.s_addr = inet_addr(destip);
338 printf("client: sending packet to %s:%d\n", destip, port);
339 if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
340 (struct sockaddr *)&in, sizeof(in)) < 0) {
341 perror("client: sendto");
345 printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
347 if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
348 (struct sockaddr *)&from, &fl,
349 (struct sockaddr *)&to, &tl)) < 0) {
350 perror("client: recvfromto");
354 printf("client: received a packet of %d bytes [%s] ", n, buf);
355 printf("(src ip:port %s:%d",
356 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
357 printf(" dst ip:port %s:%d)\n",
358 inet_ntoa(to.sin_addr), ntohs(to.sin_port));