1 /* vi: set sw=4 ts=4: */
5 * Copyright (C) 2007 Denys Vlasenko
7 * Licensed under GPLv2, see file LICENSE in this source tree.
11 #if defined(IPV6_PKTINFO) && !defined(IPV6_RECVPKTINFO)
12 # define IPV6_RECVPKTINFO IPV6_PKTINFO
16 * This asks kernel to let us know dst addr/port of incoming packets
17 * We don't check for errors here. Not supported == won't be used
20 socket_want_pktinfo(int fd UNUSED_PARAM
)
23 setsockopt_1(fd
, IPPROTO_IP
, IP_PKTINFO
);
25 #if ENABLE_FEATURE_IPV6 && defined(IPV6_RECVPKTINFO)
26 setsockopt_1(fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
);
32 send_to_from(int fd
, void *buf
, size_t len
, int flags
,
33 const struct sockaddr
*to
,
34 const struct sockaddr
*from
,
38 (void)from
; /* suppress "unused from" warning */
39 return sendto(fd
, buf
, len
, flags
, to
, tolen
);
44 char cmsg
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
45 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
46 char cmsg6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
49 struct cmsghdr
* cmsgptr
;
51 if (from
->sa_family
!= AF_INET
52 # if ENABLE_FEATURE_IPV6
53 && from
->sa_family
!= AF_INET6
56 /* ANY local address */
57 return sendto(fd
, buf
, len
, flags
, to
, tolen
);
60 /* man recvmsg and man cmsg is needed to make sense of code below */
62 iov
[0].iov_base
= buf
;
65 memset(&u
, 0, sizeof(u
));
67 memset(&msg
, 0, sizeof(msg
));
68 msg
.msg_name
= (void *)(struct sockaddr
*)to
; /* or compiler will annoy us */
69 msg
.msg_namelen
= tolen
;
73 msg
.msg_controllen
= sizeof(u
);
74 msg
.msg_flags
= flags
;
76 cmsgptr
= CMSG_FIRSTHDR(&msg
);
78 * Users report that to->sa_family can be AF_INET6 too,
79 * if "to" was acquired by recv_from_to(). IOW: recv_from_to()
80 * was seen showing IPv6 "from" even when the destination
81 * of received packet (our local address) was IPv4.
83 if (/* to->sa_family == AF_INET && */ from
->sa_family
== AF_INET
) {
84 struct in_pktinfo
*pktptr
;
85 cmsgptr
->cmsg_level
= IPPROTO_IP
;
86 cmsgptr
->cmsg_type
= IP_PKTINFO
;
87 cmsgptr
->cmsg_len
= CMSG_LEN(sizeof(struct in_pktinfo
));
88 pktptr
= (struct in_pktinfo
*)(CMSG_DATA(cmsgptr
));
89 /*pktptr->ipi_ifindex = 0; -- already done by memset(u...) */
90 /* In general, CMSG_DATA() can be unaligned, but in this case
91 * we know for sure it is sufficiently aligned:
92 * CMSG_FIRSTHDR simply returns &u above,
93 * and CMSG_DATA returns &u + size_t + int + int.
94 * Thus direct assignment is ok:
96 pktptr
->ipi_spec_dst
= ((struct sockaddr_in
*)from
)->sin_addr
;
98 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
99 else if (/* to->sa_family == AF_INET6 && */ from
->sa_family
== AF_INET6
) {
100 struct in6_pktinfo
*pktptr
;
101 cmsgptr
->cmsg_level
= IPPROTO_IPV6
;
102 cmsgptr
->cmsg_type
= IPV6_PKTINFO
;
103 cmsgptr
->cmsg_len
= CMSG_LEN(sizeof(struct in6_pktinfo
));
104 pktptr
= (struct in6_pktinfo
*)(CMSG_DATA(cmsgptr
));
105 /* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */
106 pktptr
->ipi6_addr
= ((struct sockaddr_in6
*)from
)->sin6_addr
;
109 msg
.msg_controllen
= cmsgptr
->cmsg_len
;
111 return sendmsg(fd
, &msg
, flags
);
115 /* NB: this will never set port# in 'to'!
116 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
117 * Typical usage is to preinit 'to' with "default" value
118 * before calling recv_from_to(). */
120 recv_from_to(int fd
, void *buf
, size_t len
, int flags
,
121 struct sockaddr
*from
, struct sockaddr
*to
,
125 (void)to
; /* suppress "unused to" warning */
126 return recvfrom(fd
, buf
, len
, flags
, from
, &sa_size
);
128 /* man recvmsg and man cmsg is needed to make sense of code below */
131 char cmsg
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
132 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
133 char cmsg6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
136 struct cmsghdr
*cmsgptr
;
140 iov
[0].iov_base
= buf
;
141 iov
[0].iov_len
= len
;
143 memset(&msg
, 0, sizeof(msg
));
144 msg
.msg_name
= (struct sockaddr
*)from
;
145 msg
.msg_namelen
= sa_size
;
148 msg
.msg_control
= &u
;
149 msg
.msg_controllen
= sizeof(u
);
151 recv_length
= recvmsg(fd
, &msg
, flags
);
155 # define to4 ((struct sockaddr_in*)to)
156 # define to6 ((struct sockaddr_in6*)to)
157 /* Here we try to retrieve destination IP and memorize it */
158 for (cmsgptr
= CMSG_FIRSTHDR(&msg
);
160 cmsgptr
= CMSG_NXTHDR(&msg
, cmsgptr
)
162 if (cmsgptr
->cmsg_level
== IPPROTO_IP
163 && cmsgptr
->cmsg_type
== IP_PKTINFO
165 const int IPI_ADDR_OFF
= offsetof(struct in_pktinfo
, ipi_addr
);
166 to
->sa_family
= AF_INET
;
167 /*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
168 /*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */
169 memcpy(&to4
->sin_addr
, (char*)(CMSG_DATA(cmsgptr
)) + IPI_ADDR_OFF
, sizeof(to4
->sin_addr
));
170 /*to4->sin_port = 123; - this data is not supplied by kernel */
173 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
174 if (cmsgptr
->cmsg_level
== IPPROTO_IPV6
175 && cmsgptr
->cmsg_type
== IPV6_PKTINFO
177 const int IPI6_ADDR_OFF
= offsetof(struct in6_pktinfo
, ipi6_addr
);
178 to
->sa_family
= AF_INET6
;
179 /*# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
180 /*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */
181 memcpy(&to6
->sin6_addr
, (char*)(CMSG_DATA(cmsgptr
)) + IPI6_ADDR_OFF
, sizeof(to6
->sin6_addr
));
182 /*to6->sin6_port = 123; */