blob: b8fb6755dc5455099dbb87db3d4ac98ab3197de8 [file] [log] [blame]
Denis Vlasenko29fe7262007-04-05 20:26:28 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
Denis Vlasenkod18f52b2008-03-02 12:53:15 +00005 * Copyright (C) 2007 Denys Vlasenko
Denis Vlasenko29fe7262007-04-05 20:26:28 +00006 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko29fe7262007-04-05 20:26:28 +00008 */
Denis Vlasenko29fe7262007-04-05 20:26:28 +00009#include "libbb.h"
Denis Vlasenko992e05b2007-04-03 01:13:04 +000010
11/*
12 * This asks kernel to let us know dst addr/port of incoming packets
13 * We don't check for errors here. Not supported == won't be used
14 */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000015void FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +000016socket_want_pktinfo(int fd)
17{
18#ifdef IP_PKTINFO
19 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
20#endif
Denis Vlasenko64a15122007-04-04 10:16:15 +000021#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +000022 setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int));
23#endif
24}
25
26
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000027ssize_t FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +000028send_to_from(int fd, void *buf, size_t len, int flags,
Denis Vlasenko081eb712008-03-17 09:02:21 +000029 const struct sockaddr *to,
30 const struct sockaddr *from,
Denis Vlasenko992e05b2007-04-03 01:13:04 +000031 socklen_t tolen)
32{
33#ifndef IP_PKTINFO
Denys Vlasenko1361aa22009-11-26 16:52:25 +010034 (void)from; /* suppress "unused from" warning */
Denis Vlasenko992e05b2007-04-03 01:13:04 +000035 return sendto(fd, buf, len, flags, to, tolen);
36#else
37 struct iovec iov[1];
38 struct msghdr msg;
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000039 union {
40 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +010041# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000042 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +010043# endif
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000044 } u;
Denis Vlasenko992e05b2007-04-03 01:13:04 +000045 struct cmsghdr* cmsgptr;
46
47 if (from->sa_family != AF_INET
Denys Vlasenko1361aa22009-11-26 16:52:25 +010048# if ENABLE_FEATURE_IPV6
Denis Vlasenko992e05b2007-04-03 01:13:04 +000049 && from->sa_family != AF_INET6
Denys Vlasenko1361aa22009-11-26 16:52:25 +010050# endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +000051 ) {
52 /* ANY local address */
53 return sendto(fd, buf, len, flags, to, tolen);
54 }
Denis Vlasenko4b924f32007-05-30 00:29:55 +000055
Denis Vlasenko992e05b2007-04-03 01:13:04 +000056 /* man recvmsg and man cmsg is needed to make sense of code below */
57
58 iov[0].iov_base = buf;
59 iov[0].iov_len = len;
60
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000061 memset(&u, 0, sizeof(u));
Denis Vlasenko992e05b2007-04-03 01:13:04 +000062
63 memset(&msg, 0, sizeof(msg));
64 msg.msg_name = (void *)(struct sockaddr *)to; /* or compiler will annoy us */
65 msg.msg_namelen = tolen;
66 msg.msg_iov = iov;
67 msg.msg_iovlen = 1;
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000068 msg.msg_control = &u;
69 msg.msg_controllen = sizeof(u);
Denis Vlasenko992e05b2007-04-03 01:13:04 +000070 msg.msg_flags = flags;
71
72 cmsgptr = CMSG_FIRSTHDR(&msg);
73 if (to->sa_family == AF_INET && from->sa_family == AF_INET) {
74 struct in_pktinfo *pktptr;
75 cmsgptr->cmsg_level = IPPROTO_IP;
76 cmsgptr->cmsg_type = IP_PKTINFO;
77 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
78 pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +010079 /*pktptr->ipi_ifindex = 0; -- already done by memset(u...) */
80 /* In general, CMSG_DATA() can be unaligned, but in this case
81 * we know for sure it is sufficiently aligned:
82 * CMSG_FIRSTHDR simply returns &u above,
83 * and CMSG_DATA returns &u + size_t + int + int.
84 * Thus direct assignment is ok:
85 */
Denis Vlasenko992e05b2007-04-03 01:13:04 +000086 pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
87 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +010088# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +000089 else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
90 struct in6_pktinfo *pktptr;
91 cmsgptr->cmsg_level = IPPROTO_IPV6;
Denis Vlasenko64a15122007-04-04 10:16:15 +000092 cmsgptr->cmsg_type = IPV6_PKTINFO;
Denis Vlasenko992e05b2007-04-03 01:13:04 +000093 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
94 pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +010095 /* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */
Denis Vlasenko992e05b2007-04-03 01:13:04 +000096 pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
97 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +010098# endif
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000099 msg.msg_controllen = cmsgptr->cmsg_len;
100
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000101 return sendmsg(fd, &msg, flags);
102#endif
103}
104
Denis Vlasenko64a15122007-04-04 10:16:15 +0000105/* NB: this will never set port# in 'to'!
106 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
Denis Vlasenko79468792007-04-04 11:02:55 +0000107 * Typical usage is to preinit 'to' with "default" value
Denis Vlasenko64a15122007-04-04 10:16:15 +0000108 * before calling recv_from_to(). */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000109ssize_t FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000110recv_from_to(int fd, void *buf, size_t len, int flags,
111 struct sockaddr *from, struct sockaddr *to,
112 socklen_t sa_size)
113{
114#ifndef IP_PKTINFO
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100115 (void)to; /* suppress "unused to" warning */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000116 return recvfrom(fd, buf, len, flags, from, &sa_size);
117#else
118 /* man recvmsg and man cmsg is needed to make sense of code below */
119 struct iovec iov[1];
120 union {
121 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100122# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000123 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100124# endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000125 } u;
126 struct cmsghdr *cmsgptr;
127 struct msghdr msg;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000128 ssize_t recv_length;
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000129
130 iov[0].iov_base = buf;
131 iov[0].iov_len = len;
132
133 memset(&msg, 0, sizeof(msg));
134 msg.msg_name = (struct sockaddr *)from;
135 msg.msg_namelen = sa_size;
136 msg.msg_iov = iov;
137 msg.msg_iovlen = 1;
138 msg.msg_control = &u;
139 msg.msg_controllen = sizeof(u);
140
141 recv_length = recvmsg(fd, &msg, flags);
142 if (recv_length < 0)
143 return recv_length;
144
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100145# define to4 ((struct sockaddr_in*)to)
146# define to6 ((struct sockaddr_in6*)to)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000147 /* Here we try to retrieve destination IP and memorize it */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000148 for (cmsgptr = CMSG_FIRSTHDR(&msg);
149 cmsgptr != NULL;
150 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
151 ) {
152 if (cmsgptr->cmsg_level == IPPROTO_IP
153 && cmsgptr->cmsg_type == IP_PKTINFO
154 ) {
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100155 const int IPI_ADDR_OFF = offsetof(struct in_pktinfo, ipi_addr);
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000156 to->sa_family = AF_INET;
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100157 /*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100158 /*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100159 memcpy(&to4->sin_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI_ADDR_OFF, sizeof(to4->sin_addr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100160 /*to4->sin_port = 123; - this data is not supplied by kernel */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000161 break;
162 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100163# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000164 if (cmsgptr->cmsg_level == IPPROTO_IPV6
Denis Vlasenko64a15122007-04-04 10:16:15 +0000165 && cmsgptr->cmsg_type == IPV6_PKTINFO
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000166 ) {
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100167 const int IPI6_ADDR_OFF = offsetof(struct in6_pktinfo, ipi6_addr);
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000168 to->sa_family = AF_INET6;
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100169 /*# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100170 /*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100171 memcpy(&to6->sin6_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI6_ADDR_OFF, sizeof(to6->sin6_addr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100172 /*to6->sin6_port = 123; */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000173 break;
174 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100175# endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000176 }
177 return recv_length;
178#endif
179}