1 /* vi: set sw=4 ts=4: */
5 * Copyright (C) 2007 Denys Vlasenko
7 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
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
17 socket_want_pktinfo(int fd
)
20 setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &const_int_1
, sizeof(int));
22 #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
23 setsockopt(fd
, IPPROTO_IPV6
, IPV6_PKTINFO
, &const_int_1
, sizeof(int));
29 send_to_from(int fd
, void *buf
, size_t len
, int flags
,
30 const struct sockaddr
*to
,
31 const struct sockaddr
*from
,
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(cbuf...) */
80 pktptr
->ipi_spec_dst
= ((struct sockaddr_in
*)from
)->sin_addr
;
82 #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
83 else if (to
->sa_family
== AF_INET6
&& from
->sa_family
== AF_INET6
) {
84 struct in6_pktinfo
*pktptr
;
85 cmsgptr
->cmsg_level
= IPPROTO_IPV6
;
86 cmsgptr
->cmsg_type
= IPV6_PKTINFO
;
87 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
;
93 msg
.msg_controllen
= cmsgptr
->cmsg_len
;
95 return sendmsg(fd
, &msg
, flags
);
99 /* NB: this will never set port# in 'to'!
100 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
101 * Typical usage is to preinit 'to' with "default" value
102 * before calling recv_from_to(). */
104 recv_from_to(int fd
, void *buf
, size_t len
, int flags
,
105 struct sockaddr
*from
, struct sockaddr
*to
,
109 return recvfrom(fd
, buf
, len
, flags
, from
, &sa_size
);
111 /* man recvmsg and man cmsg is needed to make sense of code below */
114 char cmsg
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
115 #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
116 char cmsg6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
119 struct cmsghdr
*cmsgptr
;
123 iov
[0].iov_base
= buf
;
124 iov
[0].iov_len
= len
;
126 memset(&msg
, 0, sizeof(msg
));
127 msg
.msg_name
= (struct sockaddr
*)from
;
128 msg
.msg_namelen
= sa_size
;
131 msg
.msg_control
= &u
;
132 msg
.msg_controllen
= sizeof(u
);
134 recv_length
= recvmsg(fd
, &msg
, flags
);
138 /* Here we try to retrieve destination IP and memorize it */
139 for (cmsgptr
= CMSG_FIRSTHDR(&msg
);
141 cmsgptr
= CMSG_NXTHDR(&msg
, cmsgptr
)
143 if (cmsgptr
->cmsg_level
== IPPROTO_IP
144 && cmsgptr
->cmsg_type
== IP_PKTINFO
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; */
153 #if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
154 if (cmsgptr
->cmsg_level
== IPPROTO_IPV6
155 && cmsgptr
->cmsg_type
== IPV6_PKTINFO
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; */