6 |
* |
* |
7 |
* Licensed under GPL version 2, see file LICENSE in this tarball for details. |
* Licensed under GPL version 2, see file LICENSE in this tarball for details. |
8 |
*/ |
*/ |
|
|
|
9 |
#include "libbb.h" |
#include "libbb.h" |
10 |
|
|
11 |
/* |
/* |
31 |
socklen_t tolen) |
socklen_t tolen) |
32 |
{ |
{ |
33 |
#ifndef IP_PKTINFO |
#ifndef IP_PKTINFO |
34 |
|
(void)from; /* suppress "unused from" warning */ |
35 |
return sendto(fd, buf, len, flags, to, tolen); |
return sendto(fd, buf, len, flags, to, tolen); |
36 |
#else |
#else |
37 |
struct iovec iov[1]; |
struct iovec iov[1]; |
38 |
struct msghdr msg; |
struct msghdr msg; |
39 |
union { |
union { |
40 |
char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
41 |
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
42 |
char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
43 |
#endif |
# endif |
44 |
} u; |
} u; |
45 |
struct cmsghdr* cmsgptr; |
struct cmsghdr* cmsgptr; |
46 |
|
|
47 |
if (from->sa_family != AF_INET |
if (from->sa_family != AF_INET |
48 |
#if ENABLE_FEATURE_IPV6 |
# if ENABLE_FEATURE_IPV6 |
49 |
&& from->sa_family != AF_INET6 |
&& from->sa_family != AF_INET6 |
50 |
#endif |
# endif |
51 |
) { |
) { |
52 |
/* ANY local address */ |
/* ANY local address */ |
53 |
return sendto(fd, buf, len, flags, to, tolen); |
return sendto(fd, buf, len, flags, to, tolen); |
76 |
cmsgptr->cmsg_type = IP_PKTINFO; |
cmsgptr->cmsg_type = IP_PKTINFO; |
77 |
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
78 |
pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr)); |
pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr)); |
79 |
/* pktptr->ipi_ifindex = 0; -- already done by memset(cbuf...) */ |
/*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 |
|
*/ |
86 |
pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; |
pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; |
87 |
} |
} |
88 |
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
89 |
else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { |
else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { |
90 |
struct in6_pktinfo *pktptr; |
struct in6_pktinfo *pktptr; |
91 |
cmsgptr->cmsg_level = IPPROTO_IPV6; |
cmsgptr->cmsg_level = IPPROTO_IPV6; |
92 |
cmsgptr->cmsg_type = IPV6_PKTINFO; |
cmsgptr->cmsg_type = IPV6_PKTINFO; |
93 |
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
94 |
pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr)); |
pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr)); |
95 |
/* pktptr->ipi6_ifindex = 0; -- already done by memset(cbuf...) */ |
/* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */ |
96 |
pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr; |
pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr; |
97 |
} |
} |
98 |
#endif |
# endif |
99 |
msg.msg_controllen = cmsgptr->cmsg_len; |
msg.msg_controllen = cmsgptr->cmsg_len; |
100 |
|
|
101 |
return sendmsg(fd, &msg, flags); |
return sendmsg(fd, &msg, flags); |
112 |
socklen_t sa_size) |
socklen_t sa_size) |
113 |
{ |
{ |
114 |
#ifndef IP_PKTINFO |
#ifndef IP_PKTINFO |
115 |
|
(void)to; /* suppress "unused to" warning */ |
116 |
return recvfrom(fd, buf, len, flags, from, &sa_size); |
return recvfrom(fd, buf, len, flags, from, &sa_size); |
117 |
#else |
#else |
118 |
/* man recvmsg and man cmsg is needed to make sense of code below */ |
/* man recvmsg and man cmsg is needed to make sense of code below */ |
119 |
struct iovec iov[1]; |
struct iovec iov[1]; |
120 |
union { |
union { |
121 |
char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
122 |
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
123 |
char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
124 |
#endif |
# endif |
125 |
} u; |
} u; |
126 |
struct cmsghdr *cmsgptr; |
struct cmsghdr *cmsgptr; |
127 |
struct msghdr msg; |
struct msghdr msg; |
142 |
if (recv_length < 0) |
if (recv_length < 0) |
143 |
return recv_length; |
return recv_length; |
144 |
|
|
145 |
|
# define to4 ((struct sockaddr_in*)to) |
146 |
|
# define to6 ((struct sockaddr_in6*)to) |
147 |
/* Here we try to retrieve destination IP and memorize it */ |
/* Here we try to retrieve destination IP and memorize it */ |
148 |
for (cmsgptr = CMSG_FIRSTHDR(&msg); |
for (cmsgptr = CMSG_FIRSTHDR(&msg); |
149 |
cmsgptr != NULL; |
cmsgptr != NULL; |
152 |
if (cmsgptr->cmsg_level == IPPROTO_IP |
if (cmsgptr->cmsg_level == IPPROTO_IP |
153 |
&& cmsgptr->cmsg_type == IP_PKTINFO |
&& cmsgptr->cmsg_type == IP_PKTINFO |
154 |
) { |
) { |
155 |
#define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) ) |
# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) ) |
156 |
to->sa_family = AF_INET; |
to->sa_family = AF_INET; |
157 |
((struct sockaddr_in*)to)->sin_addr = pktinfo(cmsgptr)->ipi_addr; |
/*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */ |
158 |
/* ((struct sockaddr_in*)to)->sin_port = 123; */ |
memcpy(&to4->sin_addr, &pktinfo(cmsgptr)->ipi_addr, sizeof(to4->sin_addr)); |
159 |
#undef pktinfo |
/*to4->sin_port = 123; - this data is not supplied by kernel */ |
160 |
|
# undef pktinfo |
161 |
break; |
break; |
162 |
} |
} |
163 |
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
164 |
if (cmsgptr->cmsg_level == IPPROTO_IPV6 |
if (cmsgptr->cmsg_level == IPPROTO_IPV6 |
165 |
&& cmsgptr->cmsg_type == IPV6_PKTINFO |
&& cmsgptr->cmsg_type == IPV6_PKTINFO |
166 |
) { |
) { |
167 |
#define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) ) |
# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) ) |
168 |
to->sa_family = AF_INET6; |
to->sa_family = AF_INET6; |
169 |
((struct sockaddr_in6*)to)->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; |
/*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */ |
170 |
/* ((struct sockaddr_in6*)to)->sin6_port = 123; */ |
memcpy(&to6->sin6_addr, &pktinfo(cmsgptr)->ipi6_addr, sizeof(to6->sin6_addr)); |
171 |
#undef pktinfo |
/*to6->sin6_port = 123; */ |
172 |
|
# undef pktinfo |
173 |
break; |
break; |
174 |
} |
} |
175 |
#endif |
# endif |
176 |
} |
} |
177 |
return recv_length; |
return recv_length; |
178 |
#endif |
#endif |