blob: b31f28416b6031cd91b372603bd870b14ac8c6a5 [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 *
7 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
8 */
Denis Vlasenko992e05b2007-04-03 01:13:04 +00009
Denis Vlasenko29fe7262007-04-05 20:26:28 +000010#include "libbb.h"
Denis Vlasenko992e05b2007-04-03 01:13:04 +000011
12/*
13 * This asks kernel to let us know dst addr/port of incoming packets
14 * We don't check for errors here. Not supported == won't be used
15 */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000016void FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +000017socket_want_pktinfo(int fd)
18{
19#ifdef IP_PKTINFO
20 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
21#endif
Denis Vlasenko64a15122007-04-04 10:16:15 +000022#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +000023 setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int));
24#endif
25}
26
27
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000028ssize_t FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +000029send_to_from(int fd, void *buf, size_t len, int flags,
Denis Vlasenko081eb712008-03-17 09:02:21 +000030 const struct sockaddr *to,
31 const struct sockaddr *from,
Denis Vlasenko992e05b2007-04-03 01:13:04 +000032 socklen_t tolen)
33{
34#ifndef IP_PKTINFO
35 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))];
Denis Vlasenko64a15122007-04-04 10:16:15 +000041#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000042 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Denis Vlasenko64a15122007-04-04 10:16:15 +000043#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
48#if ENABLE_FEATURE_IPV6
49 && from->sa_family != AF_INET6
50#endif
51 ) {
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));
79 /* pktptr->ipi_ifindex = 0; -- already done by memset(cbuf...) */
80 pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
81 }
Denis Vlasenko64a15122007-04-04 10:16:15 +000082#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +000083 else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
84 struct in6_pktinfo *pktptr;
85 cmsgptr->cmsg_level = IPPROTO_IPV6;
Denis Vlasenko64a15122007-04-04 10:16:15 +000086 cmsgptr->cmsg_type = IPV6_PKTINFO;
Denis Vlasenko992e05b2007-04-03 01:13:04 +000087 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
88 pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr));
89 /* pktptr->ipi6_ifindex = 0; -- already done by memset(cbuf...) */
90 pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
91 }
92#endif
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +000093 msg.msg_controllen = cmsgptr->cmsg_len;
94
Denis Vlasenko992e05b2007-04-03 01:13:04 +000095 return sendmsg(fd, &msg, flags);
96#endif
97}
98
Denis Vlasenko64a15122007-04-04 10:16:15 +000099/* NB: this will never set port# in 'to'!
100 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
Denis Vlasenko79468792007-04-04 11:02:55 +0000101 * Typical usage is to preinit 'to' with "default" value
Denis Vlasenko64a15122007-04-04 10:16:15 +0000102 * before calling recv_from_to(). */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000103ssize_t FAST_FUNC
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000104recv_from_to(int fd, void *buf, size_t len, int flags,
105 struct sockaddr *from, struct sockaddr *to,
106 socklen_t sa_size)
107{
108#ifndef IP_PKTINFO
109 return recvfrom(fd, buf, len, flags, from, &sa_size);
110#else
111 /* man recvmsg and man cmsg is needed to make sense of code below */
112 struct iovec iov[1];
113 union {
114 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +0000115#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000116 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +0000117#endif
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000118 } u;
119 struct cmsghdr *cmsgptr;
120 struct msghdr msg;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000121 ssize_t recv_length;
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000122
123 iov[0].iov_base = buf;
124 iov[0].iov_len = len;
125
126 memset(&msg, 0, sizeof(msg));
127 msg.msg_name = (struct sockaddr *)from;
128 msg.msg_namelen = sa_size;
129 msg.msg_iov = iov;
130 msg.msg_iovlen = 1;
131 msg.msg_control = &u;
132 msg.msg_controllen = sizeof(u);
133
134 recv_length = recvmsg(fd, &msg, flags);
135 if (recv_length < 0)
136 return recv_length;
137
138 /* Here we try to retrieve destination IP and memorize it */
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000139 for (cmsgptr = CMSG_FIRSTHDR(&msg);
140 cmsgptr != NULL;
141 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
142 ) {
143 if (cmsgptr->cmsg_level == IPPROTO_IP
144 && cmsgptr->cmsg_type == IP_PKTINFO
145 ) {
146#define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )
147 to->sa_family = AF_INET;
148 ((struct sockaddr_in*)to)->sin_addr = pktinfo(cmsgptr)->ipi_addr;
149 /* ((struct sockaddr_in*)to)->sin_port = 123; */
150#undef pktinfo
151 break;
152 }
Denis Vlasenko64a15122007-04-04 10:16:15 +0000153#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000154 if (cmsgptr->cmsg_level == IPPROTO_IPV6
Denis Vlasenko64a15122007-04-04 10:16:15 +0000155 && cmsgptr->cmsg_type == IPV6_PKTINFO
Denis Vlasenko992e05b2007-04-03 01:13:04 +0000156 ) {
157#define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )
158 to->sa_family = AF_INET6;
159 ((struct sockaddr_in6*)to)->sin6_addr = pktinfo(cmsgptr)->ipi6_addr;
160 /* ((struct sockaddr_in6*)to)->sin6_port = 123; */
161#undef pktinfo
162 break;
163 }
164#endif
165 }
166 return recv_length;
167#endif
168}