1 /* vi: set sw=4 ts=4: */
5 * Copyright (C) 2007 Denys Vlasenko
7 * Licensed under GPLv2, see file LICENSE in this source tree.
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
16 socket_want_pktinfo(int fd UNUSED_PARAM
)
19 setsockopt_1(fd
, IPPROTO_IP
, IP_PKTINFO
);
21 #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
22 setsockopt_1(fd
, IPPROTO_IPV6
, IPV6_PKTINFO
);
28 send_to_from(int fd
, void *buf
, size_t len
, int flags
,
29 const struct sockaddr
*to
,
30 const struct sockaddr
*from
,
34 (void)from
; /* suppress "unused from" warning */
35 return sendto(fd
, buf
, len
, flags
, to
, tolen
);
40 char cmsg
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
41 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
42 char cmsg6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
45 struct cmsghdr
* cmsgptr
;
47 if (from
->sa_family
!= AF_INET
48 # if ENABLE_FEATURE_IPV6
49 && from
->sa_family
!= AF_INET6
52 /* ANY local address */
53 return sendto(fd
, buf
, len
, flags
, to
, tolen
);
56 /* man recvmsg and man cmsg is needed to make sense of code below */
58 iov
[0].iov_base
= buf
;
61 memset(&u
, 0, sizeof(u
));
63 memset(&msg
, 0, sizeof(msg
));
64 msg
.msg_name
= (void *)(struct sockaddr
*)to
; /* or compiler will annoy us */
65 msg
.msg_namelen
= tolen
;
69 msg
.msg_controllen
= sizeof(u
);
70 msg
.msg_flags
= flags
;
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(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:
86 pktptr
->ipi_spec_dst
= ((struct sockaddr_in
*)from
)->sin_addr
;
88 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
89 else if (to
->sa_family
== AF_INET6
&& from
->sa_family
== AF_INET6
) {
90 struct in6_pktinfo
*pktptr
;
91 cmsgptr
->cmsg_level
= IPPROTO_IPV6
;
92 cmsgptr
->cmsg_type
= IPV6_PKTINFO
;
93 cmsgptr
->cmsg_len
= CMSG_LEN(sizeof(struct in6_pktinfo
));
94 pktptr
= (struct in6_pktinfo
*)(CMSG_DATA(cmsgptr
));
95 /* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */
96 pktptr
->ipi6_addr
= ((struct sockaddr_in6
*)from
)->sin6_addr
;
99 msg
.msg_controllen
= cmsgptr
->cmsg_len
;
101 return sendmsg(fd
, &msg
, flags
);
105 /* NB: this will never set port# in 'to'!
106 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
107 * Typical usage is to preinit 'to' with "default" value
108 * before calling recv_from_to(). */
110 recv_from_to(int fd
, void *buf
, size_t len
, int flags
,
111 struct sockaddr
*from
, struct sockaddr
*to
,
115 (void)to
; /* suppress "unused to" warning */
116 return recvfrom(fd
, buf
, len
, flags
, from
, &sa_size
);
118 /* man recvmsg and man cmsg is needed to make sense of code below */
121 char cmsg
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
122 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
123 char cmsg6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
126 struct cmsghdr
*cmsgptr
;
130 iov
[0].iov_base
= buf
;
131 iov
[0].iov_len
= len
;
133 memset(&msg
, 0, sizeof(msg
));
134 msg
.msg_name
= (struct sockaddr
*)from
;
135 msg
.msg_namelen
= sa_size
;
138 msg
.msg_control
= &u
;
139 msg
.msg_controllen
= sizeof(u
);
141 recv_length
= recvmsg(fd
, &msg
, flags
);
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 */
148 for (cmsgptr
= CMSG_FIRSTHDR(&msg
);
150 cmsgptr
= CMSG_NXTHDR(&msg
, cmsgptr
)
152 if (cmsgptr
->cmsg_level
== IPPROTO_IP
153 && cmsgptr
->cmsg_type
== IP_PKTINFO
155 const int IPI_ADDR_OFF
= offsetof(struct in_pktinfo
, ipi_addr
);
156 to
->sa_family
= AF_INET
;
157 /*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
158 /*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */
159 memcpy(&to4
->sin_addr
, (char*)(CMSG_DATA(cmsgptr
)) + IPI_ADDR_OFF
, sizeof(to4
->sin_addr
));
160 /*to4->sin_port = 123; - this data is not supplied by kernel */
163 # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
164 if (cmsgptr
->cmsg_level
== IPPROTO_IPV6
165 && (cmsgptr
->cmsg_type
== IPV6_PKTINFO
166 #if defined(IPV6_2292PKTINFO) && defined(IPV6_RECVPKTINFO)
167 || cmsgptr
->cmsg_type
== IPV6_2292PKTINFO
170 const int IPI6_ADDR_OFF
= offsetof(struct in6_pktinfo
, ipi6_addr
);
171 to
->sa_family
= AF_INET6
;
172 /*# define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
173 /*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */
174 memcpy(&to6
->sin6_addr
, (char*)(CMSG_DATA(cmsgptr
)) + IPI6_ADDR_OFF
, sizeof(to6
->sin6_addr
));
175 /*to6->sin6_port = 123; */