blob: 6e3ef484e4a1f4419bdfce8807ba7ec14f922425 [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
Denys Vlasenko24915112011-08-28 05:31:49 +020016socket_want_pktinfo(int fd UNUSED_PARAM)
Denis Vlasenko992e05b2007-04-03 01:13:04 +000017{
18#ifdef IP_PKTINFO
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +020019 setsockopt_1(fd, IPPROTO_IP, IP_PKTINFO);
Denis Vlasenko992e05b2007-04-03 01:13:04 +000020#endif
Denis Vlasenko64a15122007-04-04 10:16:15 +000021#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +020022 setsockopt_1(fd, IPPROTO_IPV6, IPV6_PKTINFO);
Denis Vlasenko992e05b2007-04-03 01:13:04 +000023#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);
Denys Vlasenko7cef4812016-09-15 13:20:51 +020073 /*
74 * Users report that to->sa_family can be AF_INET6 too,
75 * if "to" was acquired by recv_from_to(). IOW: recv_from_to()
76 * was seen showing IPv6 "from" even when the destination
77 * of received packet (our local address) was IPv4.
78 */
79 if (/* to->sa_family == AF_INET && */ from->sa_family == AF_INET) {
Denis Vlasenko992e05b2007-04-03 01:13:04 +000080 struct in_pktinfo *pktptr;
81 cmsgptr->cmsg_level = IPPROTO_IP;
82 cmsgptr->cmsg_type = IP_PKTINFO;
83 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
84 pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +010085 /*pktptr->ipi_ifindex = 0; -- already done by memset(u...) */
86 /* In general, CMSG_DATA() can be unaligned, but in this case
87 * we know for sure it is sufficiently aligned:
88 * CMSG_FIRSTHDR simply returns &u above,
89 * and CMSG_DATA returns &u + size_t + int + int.
90 * Thus direct assignment is ok:
91 */
Denis Vlasenko992e05b2007-04-03 01:13:04 +000092 pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
93 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +010094# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denys Vlasenko7cef4812016-09-15 13:20:51 +020095 else if (/* to->sa_family == AF_INET6 && */ from->sa_family == AF_INET6) {
Denis Vlasenko992e05b2007-04-03 01:13:04 +000096 struct in6_pktinfo *pktptr;
97 cmsgptr->cmsg_level = IPPROTO_IPV6;
Denis Vlasenko64a15122007-04-04 10:16:15 +000098 cmsgptr->cmsg_type = IPV6_PKTINFO;
Denis Vlasenko992e05b2007-04-03 01:13:04 +000099 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
100 pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100101 /* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000102 pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
103 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100104# endif
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +0000105 msg.msg_controllen = cmsgptr->cmsg_len;
106
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000107 return sendmsg(fd, &msg, flags);
108#endif
109}
110
Denis Vlasenko64a15122007-04-04 10:16:15 +0000111/* NB: this will never set port# in 'to'!
112 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
Denis Vlasenko79468792007-04-04 11:02:55 +0000113 * Typical usage is to preinit 'to' with "default" value
Denis Vlasenko64a15122007-04-04 10:16:15 +0000114 * before calling recv_from_to(). */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000115ssize_t FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000116recv_from_to(int fd, void *buf, size_t len, int flags,
117 struct sockaddr *from, struct sockaddr *to,
118 socklen_t sa_size)
119{
120#ifndef IP_PKTINFO
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100121 (void)to; /* suppress "unused to" warning */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000122 return recvfrom(fd, buf, len, flags, from, &sa_size);
123#else
124 /* man recvmsg and man cmsg is needed to make sense of code below */
125 struct iovec iov[1];
126 union {
127 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100128# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000129 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100130# endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000131 } u;
132 struct cmsghdr *cmsgptr;
133 struct msghdr msg;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000134 ssize_t recv_length;
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000135
136 iov[0].iov_base = buf;
137 iov[0].iov_len = len;
138
139 memset(&msg, 0, sizeof(msg));
140 msg.msg_name = (struct sockaddr *)from;
141 msg.msg_namelen = sa_size;
142 msg.msg_iov = iov;
143 msg.msg_iovlen = 1;
144 msg.msg_control = &u;
145 msg.msg_controllen = sizeof(u);
146
147 recv_length = recvmsg(fd, &msg, flags);
148 if (recv_length < 0)
149 return recv_length;
150
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100151# define to4 ((struct sockaddr_in*)to)
152# define to6 ((struct sockaddr_in6*)to)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000153 /* Here we try to retrieve destination IP and memorize it */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000154 for (cmsgptr = CMSG_FIRSTHDR(&msg);
155 cmsgptr != NULL;
156 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
157 ) {
158 if (cmsgptr->cmsg_level == IPPROTO_IP
159 && cmsgptr->cmsg_type == IP_PKTINFO
160 ) {
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100161 const int IPI_ADDR_OFF = offsetof(struct in_pktinfo, ipi_addr);
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000162 to->sa_family = AF_INET;
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100163 /*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100164 /*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100165 memcpy(&to4->sin_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI_ADDR_OFF, sizeof(to4->sin_addr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100166 /*to4->sin_port = 123; - this data is not supplied by kernel */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000167 break;
168 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100169# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000170 if (cmsgptr->cmsg_level == IPPROTO_IPV6
Denis Vlasenko64a15122007-04-04 10:16:15 +0000171 && cmsgptr->cmsg_type == IPV6_PKTINFO
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000172 ) {
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100173 const int IPI6_ADDR_OFF = offsetof(struct in6_pktinfo, ipi6_addr);
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000174 to->sa_family = AF_INET6;
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100175 /*# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100176 /*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */
Denys Vlasenko12ca0802010-02-04 18:41:18 +0100177 memcpy(&to6->sin6_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI6_ADDR_OFF, sizeof(to6->sin6_addr));
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100178 /*to6->sin6_port = 123; */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000179 break;
180 }
Denys Vlasenko1361aa22009-11-26 16:52:25 +0100181# endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000182 }
183 return recv_length;
184#endif
185}