1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2016 by the Massachusetts Institute of Technology.
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
26 #include "udppktinfo.h"
28 #include <netinet/in.h>
29 #include <sys/socket.h>
31 #if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO)
32 #define HAVE_IP_PKTINFO
35 #if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO)
36 #define HAVE_IPV6_PKTINFO
39 #if defined(HAVE_IP_PKTINFO) || defined(IP_SENDSRCADDR) || \
40 defined(HAVE_IPV6_PKTINFO)
41 #define HAVE_PKTINFO_SUPPORT
44 /* Use RFC 3542 API below, but fall back from IPV6_RECVPKTINFO to IPV6_PKTINFO
45 * for RFC 2292 implementations. */
46 #if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
47 #define IPV6_RECVPKTINFO IPV6_PKTINFO
50 /* Parallel, though not standardized. */
51 #if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO)
52 #define IP_RECVPKTINFO IP_PKTINFO
53 #endif /* IP_RECVPKTINFO */
55 #if defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \
56 defined(HAVE_PKTINFO_SUPPORT)
58 #ifdef HAVE_STRUCT_IN6_PKTINFO
59 struct in6_pktinfo pi6;
61 #ifdef HAVE_STRUCT_IN_PKTINFO
62 struct in_pktinfo pi4;
69 #endif /* HAVE_IPV6_PKTINFO && HAVE_STRUCT_CMSGHDR && HAVE_PKTINFO_SUPPORT */
71 #ifdef HAVE_IP_PKTINFO
73 #define set_ipv4_pktinfo set_ipv4_recvpktinfo
74 static inline krb5_error_code
75 set_ipv4_recvpktinfo(int sock)
78 return setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &sockopt,
82 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */
84 #define set_ipv4_pktinfo set_ipv4_recvdstaddr
85 static inline krb5_error_code
86 set_ipv4_recvdstaddr(int sock)
89 return setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt,
93 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
94 #define set_ipv4_pktinfo(s) EINVAL
95 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
97 #ifdef HAVE_IPV6_PKTINFO
99 #define set_ipv6_pktinfo set_ipv6_recvpktinfo
100 static inline krb5_error_code
101 set_ipv6_recvpktinfo(int sock)
104 return setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt,
108 #else /* HAVE_IPV6_PKTINFO */
109 #define set_ipv6_pktinfo(s) EINVAL
110 #endif /* HAVE_IPV6_PKTINFO */
113 * Set pktinfo option on a socket. Takes a socket and the socket address family
116 * Returns 0 on success, EINVAL if pktinfo is not supported for the address
120 set_pktinfo(int sock, int family)
124 return set_ipv4_pktinfo(sock);
126 return set_ipv6_pktinfo(sock);
132 #if defined(HAVE_PKTINFO_SUPPORT) && defined(CMSG_SPACE)
135 * Check if a socket is bound to a wildcard address.
136 * Returns 1 if it is, 0 if it's bound to a specific address, or -1 on error
137 * with errno set to the error.
140 is_socket_bound_to_wildcard(int sock)
142 struct sockaddr_storage bound_addr;
143 socklen_t bound_addr_len = sizeof(bound_addr);
145 if (getsockname(sock, ss2sa(&bound_addr), &bound_addr_len) < 0)
148 switch (ss2sa(&bound_addr)->sa_family) {
150 return ss2sin(&bound_addr)->sin_addr.s_addr == INADDR_ANY;
152 return IN6_IS_ADDR_UNSPECIFIED(&ss2sin6(&bound_addr)->sin6_addr);
159 #ifdef HAVE_IP_PKTINFO
161 static inline struct in_pktinfo *
162 cmsg2pktinfo(struct cmsghdr *cmsgptr)
164 return (struct in_pktinfo *)(void *)CMSG_DATA(cmsgptr);
167 #define check_cmsg_v4_pktinfo check_cmsg_ip_pktinfo
169 check_cmsg_ip_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to,
170 socklen_t *tolen, aux_addressing_info *auxaddr)
172 struct in_pktinfo *pktinfo;
174 if (cmsgptr->cmsg_level == IPPROTO_IP &&
175 cmsgptr->cmsg_type == IP_PKTINFO &&
176 *tolen >= sizeof(struct sockaddr_in)) {
178 memset(to, 0, sizeof(struct sockaddr_in));
179 pktinfo = cmsg2pktinfo(cmsgptr);
180 sa2sin(to)->sin_addr = pktinfo->ipi_addr;
181 sa2sin(to)->sin_family = AF_INET;
182 *tolen = sizeof(struct sockaddr_in);
188 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */
190 static inline struct in_addr *
191 cmsg2sin(struct cmsghdr *cmsgptr)
193 return (struct in_addr *)(void *)CMSG_DATA(cmsgptr);
196 #define check_cmsg_v4_pktinfo check_cmsg_ip_recvdstaddr
198 check_cmsg_ip_recvdstaddr(struct cmsghdr *cmsgptr, struct sockaddr *to,
199 socklen_t *tolen, aux_addressing_info * auxaddr)
201 if (cmsgptr->cmsg_level == IPPROTO_IP &&
202 cmsgptr->cmsg_type == IP_RECVDSTADDR &&
203 *tolen >= sizeof(struct sockaddr_in)) {
204 struct in_addr *sin_addr;
206 memset(to, 0, sizeof(struct sockaddr_in));
207 sin_addr = cmsg2sin(cmsgptr);
208 sa2sin(to)->sin_addr = *sin_addr;
209 sa2sin(to)->sin_family = AF_INET;
210 *tolen = sizeof(struct sockaddr_in);
216 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
217 #define check_cmsg_v4_pktinfo(c, t, l, a) 0
218 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
220 #ifdef HAVE_IPV6_PKTINFO
222 static inline struct in6_pktinfo *
223 cmsg2pktinfo6(struct cmsghdr *cmsgptr)
225 return (struct in6_pktinfo *)(void *)CMSG_DATA(cmsgptr);
228 #define check_cmsg_v6_pktinfo check_cmsg_ipv6_pktinfo
230 check_cmsg_ipv6_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to,
231 socklen_t *tolen, aux_addressing_info *auxaddr)
233 struct in6_pktinfo *pktinfo;
235 if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
236 cmsgptr->cmsg_type == IPV6_PKTINFO &&
237 *tolen >= sizeof(struct sockaddr_in6)) {
239 memset(to, 0, sizeof(struct sockaddr_in6));
240 pktinfo = cmsg2pktinfo6(cmsgptr);
241 sa2sin6(to)->sin6_addr = pktinfo->ipi6_addr;
242 sa2sin6(to)->sin6_family = AF_INET6;
243 *tolen = sizeof(struct sockaddr_in6);
244 auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex;
249 #else /* HAVE_IPV6_PKTINFO */
250 #define check_cmsg_v6_pktinfo(c, t, l, a) 0
251 #endif /* HAVE_IPV6_PKTINFO */
254 check_cmsg_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to,
255 socklen_t *tolen, aux_addressing_info *auxaddr)
257 return check_cmsg_v4_pktinfo(cmsgptr, to, tolen, auxaddr) ||
258 check_cmsg_v6_pktinfo(cmsgptr, to, tolen, auxaddr);
262 * Receive a message from a socket.
266 * buf - The buffer to store the message in.
269 * from - Set to the address that sent the message
271 * to - Set to the address that the message was sent to if possible.
272 * May not be set in certain cases such as if pktinfo support is
273 * missing. May be NULL.
275 * auxaddr - Miscellaneous address information.
277 * Returns 0 on success, otherwise an error code.
280 recv_from_to(int sock, void *buf, size_t len, int flags,
281 struct sockaddr *from, socklen_t * fromlen,
282 struct sockaddr *to, socklen_t * tolen,
283 aux_addressing_info *auxaddr)
288 char cmsg[CMSG_SPACE(sizeof(union pktinfo))];
289 struct cmsghdr *cmsgptr;
292 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */
293 r = is_socket_bound_to_wildcard(sock);
297 if (!to || !tolen || !r)
298 return recvfrom(sock, buf, len, flags, from, fromlen);
300 /* Clobber with something recognizeable in case we can't extract the
301 * address but try to use it anyways. */
302 memset(to, 0x40, *tolen);
306 memset(&msg, 0, sizeof(msg));
308 msg.msg_namelen = *fromlen;
311 msg.msg_control = cmsg;
312 msg.msg_controllen = sizeof(cmsg);
314 r = recvmsg(sock, &msg, flags);
317 *fromlen = msg.msg_namelen;
320 * On Darwin (and presumably all *BSD with KAME stacks), CMSG_FIRSTHDR
321 * doesn't check for a non-zero controllen. RFC 3542 recommends making
322 * this check, even though the (new) spec for CMSG_FIRSTHDR says it's
323 * supposed to do the check.
325 if (msg.msg_controllen) {
326 cmsgptr = CMSG_FIRSTHDR(&msg);
328 if (check_cmsg_pktinfo(cmsgptr, to, tolen, auxaddr))
330 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr);
333 /* No info about destination addr was available. */
338 #ifdef HAVE_IP_PKTINFO
340 #define set_msg_from_ipv4 set_msg_from_ip_pktinfo
341 static krb5_error_code
342 set_msg_from_ip_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr,
343 struct sockaddr *from, socklen_t fromlen,
344 aux_addressing_info *auxaddr)
346 struct in_pktinfo *p = cmsg2pktinfo(cmsgptr);
347 const struct sockaddr_in *from4 = sa2sin(from);
349 if (fromlen != sizeof(struct sockaddr_in))
351 cmsgptr->cmsg_level = IPPROTO_IP;
352 cmsgptr->cmsg_type = IP_PKTINFO;
353 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
354 p->ipi_spec_dst = from4->sin_addr;
356 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
360 #elif defined(IP_SENDSRCADDR) /* HAVE_IP_PKTINFO */
362 #define set_msg_from_ipv4 set_msg_from_ip_sendsrcaddr
363 static krb5_error_code
364 set_msg_from_ip_sendsrcaddr(struct msghdr *msg, struct cmsghdr *cmsgptr,
365 struct sockaddr *from, socklen_t fromlen,
366 aux_addressing_info *auxaddr)
368 struct in_addr *sin_addr = cmsg2sin(cmsgptr);
369 const struct sockaddr_in *from4 = sa2sin(from);
370 if (fromlen != sizeof(struct sockaddr_in))
372 cmsgptr->cmsg_level = IPPROTO_IP;
373 cmsgptr->cmsg_type = IP_SENDSRCADDR;
374 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
375 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
376 *sin_addr = from4->sin_addr;
380 #else /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */
381 #define set_msg_from_ipv4(m, c, f, l, a) EINVAL
382 #endif /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */
384 #ifdef HAVE_IPV6_PKTINFO
386 #define set_msg_from_ipv6 set_msg_from_ipv6_pktinfo
387 static krb5_error_code
388 set_msg_from_ipv6_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr,
389 struct sockaddr *from, socklen_t fromlen,
390 aux_addressing_info *auxaddr)
392 struct in6_pktinfo *p = cmsg2pktinfo6(cmsgptr);
393 const struct sockaddr_in6 *from6 = sa2sin6(from);
395 if (fromlen != sizeof(struct sockaddr_in6))
397 cmsgptr->cmsg_level = IPPROTO_IPV6;
398 cmsgptr->cmsg_type = IPV6_PKTINFO;
399 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
401 p->ipi6_addr = from6->sin6_addr;
403 * Because of the possibility of asymmetric routing, we
404 * normally don't want to specify an interface. However,
405 * Mac OS X doesn't like sending from a link-local address
406 * (which can come up in testing at least, if you wind up
407 * with a "foo.local" name) unless we do specify the
410 if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr))
411 p->ipi6_ifindex = auxaddr->ipv6_ifindex;
412 /* otherwise, already zero */
414 msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
418 #else /* HAVE_IPV6_PKTINFO */
419 #define set_msg_from_ipv6(m, c, f, l, a) EINVAL
420 #endif /* HAVE_IPV6_PKTINFO */
422 static krb5_error_code
423 set_msg_from(int family, struct msghdr *msg, struct cmsghdr *cmsgptr,
424 struct sockaddr *from, socklen_t fromlen,
425 aux_addressing_info *auxaddr)
429 return set_msg_from_ipv4(msg, cmsgptr, from, fromlen, auxaddr);
431 return set_msg_from_ipv6(msg, cmsgptr, from, fromlen, auxaddr);
438 * Send a message to an address.
442 * buf - The message to send.
445 * to - The address to send the message to.
447 * from - The address to attempt to send the message from. May be NULL.
449 * auxaddr - Miscellaneous address information.
451 * Returns 0 on success, otherwise an error code.
454 send_to_from(int sock, void *buf, size_t len, int flags,
455 const struct sockaddr *to, socklen_t tolen, struct sockaddr *from,
456 socklen_t fromlen, aux_addressing_info *auxaddr)
461 struct cmsghdr *cmsgptr;
462 char cbuf[CMSG_SPACE(sizeof(union pktinfo))];
464 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */
465 r = is_socket_bound_to_wildcard(sock);
469 if (from == NULL || fromlen == 0 || from->sa_family != to->sa_family || !r)
475 if (iov.iov_len != len)
477 memset(cbuf, 0, sizeof(cbuf));
478 memset(&msg, 0, sizeof(msg));
479 msg.msg_name = (void *)to;
480 msg.msg_namelen = tolen;
483 msg.msg_control = cbuf;
484 /* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL on
486 msg.msg_controllen = sizeof(cbuf);
487 cmsgptr = CMSG_FIRSTHDR(&msg);
488 msg.msg_controllen = 0;
490 if (set_msg_from(from->sa_family, &msg, cmsgptr, from, fromlen, auxaddr))
492 return sendmsg(sock, &msg, flags);
495 return sendto(sock, buf, len, flags, to, tolen);
498 #else /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */
501 recv_from_to(int sock, void *buf, size_t len, int flags,
502 struct sockaddr *from, socklen_t *fromlen,
503 struct sockaddr *to, socklen_t *tolen,
504 aux_addressing_info *auxaddr)
507 /* Clobber with something recognizeable in case we try to use the
509 memset(to, 0x40, *tolen);
513 return recvfrom(sock, buf, len, flags, from, fromlen);
517 send_to_from(int sock, void *buf, size_t len, int flags,
518 const struct sockaddr *to, socklen_t tolen,
519 struct sockaddr *from, socklen_t fromlen,
520 aux_addressing_info *auxaddr)
522 return sendto(sock, buf, len, flags, to, tolen);
525 #endif /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */