The following patches were meant as a preparation for supporting traceroute from remote to internal peers. Unfortunately we ran into two showstoppers during this work. 1: We cannot read IPv4 ttl from the dual-stack socket we are using as listener. setsockopt(IP_RECVTTL) is simply not supported by the kernel for IPv6 socket, even in dual-stack mode. It is not acceptable from a memory consumption viewpoint to create two listener sockets, one for IPv4 and one for IPv6, for each bound port. 2: There is no way we can create and send ICMP response messages to be sent out from the outgoing socket unless we can access it with privileges, which we cannot in PASST. Because of this, these patches are posted mostly as a documentation of the work done, maybe to be applied some time in the future if new conditions permit. Jon Maloy (2): make ttl parametrized udp: copy incoming packet TTL from socket to tap checksum.c | 7 +++-- checksum.h | 2 +- ip.h | 11 ++++--- tap.c | 16 +++++----- tap.h | 6 ++-- tcp.c | 5 ++-- udp.c | 79 +++++++++++++++++++++++++++++++++++++------------- udp_flow.c | 18 ++++++++++++ udp_internal.h | 4 +-- udp_vu.c | 5 ++-- util.c | 5 ++++ 11 files changed, 112 insertions(+), 46 deletions(-) -- 2.48.1
Currently, IPv4 TTL and IPv6 hop_limit for packets sent out over the tap interface always are given the value 255. Even the checksum algorithm is hard coded to assume this value. In some cases we may want the TTL of packets arriving at the incoming socket to be propagated to the internal peer via the tap interface, so we need to make the value parametrized when we set the ip header. We also need to update the checksum algorithms to take this change into account. We do that in this commit, at the same time changing the default TTL/hop_limit from 255 to 64. Signed-off-by: Jon Maloy <jmaloy(a)redhat.com> --- checksum.c | 7 +++++-- checksum.h | 2 +- ip.h | 11 +++++------ tap.c | 16 +++++++++------- tap.h | 6 ++---- tcp.c | 5 +++-- udp.c | 18 +++++++++++------- udp_internal.h | 4 ++-- udp_vu.c | 6 ++++-- 9 files changed, 42 insertions(+), 33 deletions(-) diff --git a/checksum.c b/checksum.c index 0894eca..2e19722 100644 --- a/checksum.c +++ b/checksum.c @@ -124,11 +124,14 @@ static uint16_t csum_fold(uint32_t sum) * * Return: 16-bit folded sum of the IPv4 header */ -uint16_t csum_ip4_header(uint16_t l3len, uint8_t protocol, +uint16_t csum_ip4_header(uint16_t l3len, uint8_t protocol, uint8_t ttl, struct in_addr saddr, struct in_addr daddr) { - uint32_t sum = L2_BUF_IP4_PSUM(protocol); + uint32_t sum; + sum = (uint32_t)htons_constant(0x4500); + sum += (uint32_t)htons_constant(IP_DF); + sum += htons((ttl << 8) | protocol); sum += htons(l3len); sum += (saddr.s_addr >> 16) & 0xffff; sum += saddr.s_addr & 0xffff; diff --git a/checksum.h b/checksum.h index 683a09b..a9ebf96 100644 --- a/checksum.h +++ b/checksum.h @@ -12,7 +12,7 @@ struct icmp6hdr; struct iov_tail; uint16_t csum_unaligned(const void *buf, size_t len, uint32_t init); -uint16_t csum_ip4_header(uint16_t l3len, uint8_t protocol, +uint16_t csum_ip4_header(uint16_t l3len, uint8_t protocol, uint8_t ttl, struct in_addr saddr, struct in_addr daddr); uint32_t proto_ipv4_header_psum(uint16_t l4len, uint8_t protocol, struct in_addr saddr, struct in_addr daddr); diff --git a/ip.h b/ip.h index 471c57e..b67a7b7 100644 --- a/ip.h +++ b/ip.h @@ -9,6 +9,9 @@ #include <netinet/ip.h> #include <netinet/ip6.h> +#define DEFAULT_TTL 64 +#define NTP_HOP_LIMIT 255 + #define IN4_IS_ADDR_UNSPECIFIED(a) \ (((struct in_addr *)(a))->s_addr == htonl_constant(INADDR_ANY)) #define IN4_IS_ADDR_BROADCAST(a) \ @@ -37,15 +40,11 @@ .tot_len = 0, \ .id = 0, \ .frag_off = htons(IP_DF), \ - .ttl = 0xff, \ + .ttl = DEFAULT_TTL, \ .protocol = (proto), \ .saddr = 0, \ .daddr = 0, \ } -#define L2_BUF_IP4_PSUM(proto) ((uint32_t)htons_constant(0x4500) + \ - (uint32_t)htons_constant(IP_DF) + \ - (uint32_t)htons(0xff00 | (proto))) - #define IN6_IS_PREFIX_LINKLOCAL(a, len) \ ((len) >= 10 && IN6_IS_ADDR_LINKLOCAL(a)) @@ -57,7 +56,7 @@ .flow_lbl = { 0 }, \ .payload_len = 0, \ .nexthdr = (proto), \ - .hop_limit = 255, \ + .hop_limit = DEFAULT_TTL, \ .saddr = IN6ADDR_ANY_INIT, \ .daddr = IN6ADDR_ANY_INIT, \ } diff --git a/tap.c b/tap.c index d630f6d..8a66ca3 100644 --- a/tap.c +++ b/tap.c @@ -205,11 +205,11 @@ void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src, ip4h->tot_len = htons(l3len); ip4h->id = 0; ip4h->frag_off = htons(IP_DF); - ip4h->ttl = 255; + ip4h->ttl = DEFAULT_TTL; ip4h->protocol = proto; ip4h->saddr = src.s_addr; ip4h->daddr = dst.s_addr; - ip4h->check = csum_ip4_header(l3len, proto, src, dst); + ip4h->check = csum_ip4_header(l3len, proto, DEFAULT_TTL, src, dst); return (char *)ip4h + sizeof(*ip4h); } @@ -302,13 +302,14 @@ void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst, */ void *tap_push_ip6h(struct ipv6hdr *ip6h, const struct in6_addr *src, const struct in6_addr *dst, - size_t l4len, uint8_t proto, uint32_t flow) + size_t l4len, uint8_t proto, uint8_t hop_limit, + uint32_t flow) { ip6h->payload_len = htons(l4len); ip6h->priority = 0; ip6h->version = 6; ip6h->nexthdr = proto; - ip6h->hop_limit = 255; + ip6h->hop_limit = hop_limit; ip6h->saddr = *src; ip6h->daddr = *dst; ip6_set_flow_lbl(ip6h, flow); @@ -366,8 +367,8 @@ void tap_udp6_send(const struct ctx *c, size_t l4len = dlen + sizeof(struct udphdr); char buf[USHRT_MAX]; struct ipv6hdr *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6); - struct udphdr *uh = tap_push_ip6h(ip6h, src, dst, - l4len, IPPROTO_UDP, flow); + struct udphdr *uh = tap_push_ip6h(ip6h, src, dst, l4len, + IPPROTO_UDP, DEFAULT_TTL, flow); char *data = tap_push_uh6(uh, src, sport, dst, dport, in, dlen); memcpy(data, in, dlen); @@ -389,7 +390,8 @@ void tap_icmp6_send(const struct ctx *c, char buf[USHRT_MAX]; struct ipv6hdr *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6); struct icmp6hdr *icmp6h = tap_push_ip6h(ip6h, src, dst, l4len, - IPPROTO_ICMPV6, 0); + IPPROTO_ICMPV6, NTP_HOP_LIMIT, + 0); memcpy(icmp6h, in, l4len); csum_icmp6(icmp6h, src, dst, icmp6h + 1, l4len - sizeof(*icmp6h)); diff --git a/tap.h b/tap.h index 6fe3d15..d166226 100644 --- a/tap.h +++ b/tap.h @@ -85,7 +85,8 @@ void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src, void *tap_push_ip6h(struct ipv6hdr *ip6h, const struct in6_addr *src, const struct in6_addr *dst, - size_t l4len, uint8_t proto, uint32_t flow); + size_t l4len, uint8_t proto, + uint8_t hop_limit, uint32_t flow); void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport, struct in_addr dst, in_port_t dport, const void *in, size_t dlen); @@ -93,9 +94,6 @@ void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst, const void *in, size_t l4len); const struct in6_addr *tap_ip6_daddr(const struct ctx *c, const struct in6_addr *src); -void *tap_push_ip6h(struct ipv6hdr *ip6h, - const struct in6_addr *src, const struct in6_addr *dst, - size_t l4len, uint8_t proto, uint32_t flow); void tap_udp6_send(const struct ctx *c, const struct in6_addr *src, in_port_t sport, const struct in6_addr *dst, in_port_t dport, diff --git a/tcp.c b/tcp.c index 0ac298a..6948c5e 100644 --- a/tcp.c +++ b/tcp.c @@ -946,7 +946,7 @@ void tcp_fill_headers(const struct tcp_tap_conn *conn, ip4h->check = *ip4_check; else ip4h->check = csum_ip4_header(l3len, IPPROTO_TCP, - *src4, *dst4); + ip4h->ttl, *src4, *dst4); if (!no_tcp_csum) { psum = proto_ipv4_header_psum(l4len, IPPROTO_TCP, @@ -1908,7 +1908,8 @@ static void tcp_rst_no_conn(const struct ctx *c, int af, const struct in6_addr *rst_dst = saddr; rsth = tap_push_ip6h(ip6h, rst_src, rst_dst, - sizeof(*rsth), IPPROTO_TCP, flow_lbl); + sizeof(*rsth), IPPROTO_TCP, + DEFAULT_TTL, flow_lbl); psum = proto_ipv6_header_psum(sizeof(*rsth), IPPROTO_TCP, rst_src, rst_dst); } diff --git a/udp.c b/udp.c index f5a5cd1..7cc050c 100644 --- a/udp.c +++ b/udp.c @@ -272,7 +272,7 @@ static void udp_iov_init(const struct ctx *c) */ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, const struct flowside *toside, size_t dlen, - bool no_udp_csum) + uint8_t ttl, bool no_udp_csum) { const struct in_addr *src = inany_v4(&toside->oaddr); const struct in_addr *dst = inany_v4(&toside->eaddr); @@ -282,9 +282,10 @@ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, ASSERT(src && dst); ip4h->tot_len = htons(l3len); + ip4h->ttl = ttl; ip4h->daddr = dst->s_addr; ip4h->saddr = src->s_addr; - ip4h->check = csum_ip4_header(l3len, IPPROTO_UDP, *src, *dst); + ip4h->check = csum_ip4_header(l3len, IPPROTO_UDP, ttl, *src, *dst); bp->uh.source = htons(toside->oport); bp->uh.dest = htons(toside->eport); @@ -316,7 +317,7 @@ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, */ size_t udp_update_hdr6(struct ipv6hdr *ip6h, struct udp_payload_t *bp, const struct flowside *toside, size_t dlen, - bool no_udp_csum) + uint8_t hop_limit, bool no_udp_csum) { uint16_t l4len = dlen + sizeof(bp->uh); @@ -325,7 +326,7 @@ size_t udp_update_hdr6(struct ipv6hdr *ip6h, struct udp_payload_t *bp, ip6h->saddr = toside->oaddr.a6; ip6h->version = 6; ip6h->nexthdr = IPPROTO_UDP; - ip6h->hop_limit = 255; + ip6h->hop_limit = hop_limit; bp->uh.source = htons(toside->oport); bp->uh.dest = htons(toside->eport); @@ -366,14 +367,16 @@ static void udp_tap_prepare(const struct mmsghdr *mmh, if (!inany_v4(&toside->eaddr) || !inany_v4(&toside->oaddr)) { l4len = udp_update_hdr6(&bm->ip6h, bp, toside, - mmh[idx].msg_len, no_udp_csum); + mmh[idx].msg_len, + DEFAULT_TTL, no_udp_csum); tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip6h) + sizeof(udp6_eth_hdr)); (*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp6_eth_hdr); (*tap_iov)[UDP_IOV_IP] = IOV_OF_LVALUE(bm->ip6h); } else { l4len = udp_update_hdr4(&bm->ip4h, bp, toside, - mmh[idx].msg_len, no_udp_csum); + mmh[idx].msg_len, + DEFAULT_TTL, no_udp_csum); tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip4h) + sizeof(udp4_eth_hdr)); (*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp4_eth_hdr); @@ -463,7 +466,8 @@ static void udp_send_tap_icmp6(const struct ctx *c, msg.icmp6h.icmp6_dataun.icmp6_un_data32[0] = htonl(ee->ee_info); /* Reconstruct the original headers as returned in the ICMP message */ - tap_push_ip6h(&msg.ip6h, eaddr, oaddr, l4len, IPPROTO_UDP, flow); + tap_push_ip6h(&msg.ip6h, eaddr, oaddr, l4len, + IPPROTO_UDP, DEFAULT_TTL, flow); tap_push_uh6(&msg.uh, eaddr, eport, oaddr, oport, in, dlen); memcpy(&msg.data, in, dlen); diff --git a/udp_internal.h b/udp_internal.h index 96d11cf..2a73779 100644 --- a/udp_internal.h +++ b/udp_internal.h @@ -24,10 +24,10 @@ struct udp_payload_t { size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, const struct flowside *toside, size_t dlen, - bool no_udp_csum); + uint8_t ttl, bool no_udp_csum); size_t udp_update_hdr6(struct ipv6hdr *ip6h, struct udp_payload_t *bp, const struct flowside *toside, size_t dlen, - bool no_udp_csum); + uint8_t hop_limit, bool no_udp_csum); void udp_sock_fwd(const struct ctx *c, int s, uint8_t frompif, in_port_t port, const struct timespec *now); diff --git a/udp_vu.c b/udp_vu.c index 1f89509..ef2257c 100644 --- a/udp_vu.c +++ b/udp_vu.c @@ -152,7 +152,8 @@ static size_t udp_vu_prepare(const struct ctx *c, *iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_UDP); - l4len = udp_update_hdr4(iph, bp, toside, dlen, true); + l4len = udp_update_hdr4(iph, bp, toside, dlen, + DEFAULT_TTL, true); } else { struct ipv6hdr *ip6h = vu_ip(iov_vu[0].iov_base); struct udp_payload_t *bp = vu_payloadv6(iov_vu[0].iov_base); @@ -161,7 +162,8 @@ static size_t udp_vu_prepare(const struct ctx *c, *ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_UDP); - l4len = udp_update_hdr6(ip6h, bp, toside, dlen, true); + l4len = udp_update_hdr6(ip6h, bp, toside, dlen, + DEFAULT_TTL, true); } return l4len; -- 2.48.1
We read the TTL/hop_limit from UDP packets arriving at outbound sockets and convey to the value to the packets delivered to the internal peers via the tap interface. A prerequisite for this to work is that we eliminate the dual-stack listener socket and create separate IPv4 and IPv6 sockets for each bound UDP port. The extra memory required for that approach seems to be a showstopper, so this patch is posted mostly as a documentation of the work done, maybe to be applied some time in the future if new conditions permit. Signed-off-by: Jon Maloy <jmaloy(a)redhat.com> --- udp.c | 69 ++++++++++++++++++++++++++++++++++++++++-------------- udp_flow.c | 18 ++++++++++++++ udp_vu.c | 3 +-- util.c | 5 ++++ 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/udp.c b/udp.c index 7cc050c..31b9203 100644 --- a/udp.c +++ b/udp.c @@ -181,7 +181,12 @@ enum udp_iov_idx { UDP_NUM_IOVS, }; +struct udp_cmsg { + char buf[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct in6_pktinfo))]; +}; + /* IOVs and msghdr arrays for receiving datagrams from sockets */ +static struct udp_cmsg udp_cmsg_recv [UDP_MAX_FRAMES]; static struct iovec udp_iov_recv [UDP_MAX_FRAMES]; static struct mmsghdr udp_mh_recv [UDP_MAX_FRAMES]; @@ -194,6 +199,40 @@ static struct mmsghdr udp_mh_splice [UDP_MAX_FRAMES]; /* IOVs for L2 frames */ static struct iovec udp_l2_iov [UDP_MAX_FRAMES][UDP_NUM_IOVS]; +static uint8_t udp4_read_ttl(const struct msghdr *mhdr) +{ + struct msghdr *mh = (struct msghdr *)mhdr; + struct cmsghdr *cm; + int ttl = 0; + + for (cm = CMSG_FIRSTHDR(mh); cm != NULL; cm = CMSG_NXTHDR(mh, cm)) { + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_TTL) { + memcpy(&ttl, CMSG_DATA(cm), sizeof(ttl)); + ttl = *(int *) CMSG_DATA(cm); + break; + } + } + + return ttl; +} + +static uint8_t udp6_read_hop_limit(const struct msghdr *mhdr) +{ + struct msghdr *mh = (struct msghdr *)mhdr; + struct cmsghdr *cm; + int hop_limit = 0; + + for (cm = CMSG_FIRSTHDR(mh); cm != NULL; cm = CMSG_NXTHDR(mh, cm)) { + if (cm->cmsg_level == SOL_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT) { + memcpy(&hop_limit, CMSG_DATA(cm), sizeof(hop_limit)); + break; + } + } + + return hop_limit; +} + /** * udp_portmap_clear() - Clear UDP port map before configuration */ @@ -230,6 +269,7 @@ static void udp_iov_init_one(const struct ctx *c, size_t i) struct udp_meta_t *meta = &udp_meta[i]; struct iovec *siov = &udp_iov_recv[i]; struct iovec *tiov = udp_l2_iov[i]; + struct udp_cmsg *ucmsg = &udp_cmsg_recv[i]; *meta = (struct udp_meta_t) { .ip4h = L2_BUF_IP4_INIT(IPPROTO_UDP), @@ -243,6 +283,9 @@ static void udp_iov_init_one(const struct ctx *c, size_t i) mh->msg_iov = siov; mh->msg_iovlen = 1; + + mh->msg_control = ucmsg; + mh->msg_controllen = sizeof(*ucmsg); } /** @@ -271,8 +314,8 @@ static void udp_iov_init(const struct ctx *c) * Return: size of IPv4 payload (UDP header + data) */ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, - const struct flowside *toside, size_t dlen, - uint8_t ttl, bool no_udp_csum) + const struct flowside *toside, + size_t dlen, uint8_t ttl, bool no_udp_csum) { const struct in_addr *src = inany_v4(&toside->oaddr); const struct in_addr *dst = inany_v4(&toside->eaddr); @@ -285,6 +328,7 @@ size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp, ip4h->ttl = ttl; ip4h->daddr = dst->s_addr; ip4h->saddr = src->s_addr; + ip4h->ttl = ttl; ip4h->check = csum_ip4_header(l3len, IPPROTO_UDP, ttl, *src, *dst); bp->uh.source = htons(toside->oport); @@ -366,17 +410,20 @@ static void udp_tap_prepare(const struct mmsghdr *mmh, size_t l4len; if (!inany_v4(&toside->eaddr) || !inany_v4(&toside->oaddr)) { + uint8_t hop_limit = udp6_read_hop_limit(&mmh[idx].msg_hdr); + l4len = udp_update_hdr6(&bm->ip6h, bp, toside, mmh[idx].msg_len, - DEFAULT_TTL, no_udp_csum); + hop_limit, no_udp_csum); tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip6h) + sizeof(udp6_eth_hdr)); (*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp6_eth_hdr); (*tap_iov)[UDP_IOV_IP] = IOV_OF_LVALUE(bm->ip6h); } else { + uint8_t ttl = udp4_read_ttl(&mmh[idx].msg_hdr); + l4len = udp_update_hdr4(&bm->ip4h, bp, toside, - mmh[idx].msg_len, - DEFAULT_TTL, no_udp_csum); + mmh[idx].msg_len, ttl, no_udp_csum); tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip4h) + sizeof(udp4_eth_hdr)); (*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp4_eth_hdr); @@ -1094,18 +1141,6 @@ int udp_sock_init(const struct ctx *c, int ns, const union inany_addr *addr, ASSERT(!c->no_udp); - if (!addr && c->ifi4 && c->ifi6 && !ns) { - int s; - - /* Attempt to get a dual stack socket */ - s = pif_sock_l4(c, EPOLL_TYPE_UDP_LISTEN, PIF_HOST, - NULL, ifname, port, uref.u32); - udp_splice_init[V4][port] = s < 0 ? -1 : s; - udp_splice_init[V6][port] = s < 0 ? -1 : s; - if (IN_INTERVAL(0, FD_REF_MAX, s)) - return 0; - } - if ((!addr || inany_v4(addr)) && c->ifi4) { if (!ns) { r4 = pif_sock_l4(c, EPOLL_TYPE_UDP_LISTEN, PIF_HOST, diff --git a/udp_flow.c b/udp_flow.c index fea1cf3..8af0c1f 100644 --- a/udp_flow.c +++ b/udp_flow.c @@ -73,6 +73,8 @@ static int udp_flow_sock(const struct ctx *c, { const struct flowside *side = &uflow->f.side[sidei]; uint8_t pif = uflow->f.pif[sidei]; + int enable = 1; + union { flow_sidx_t sidx; uint32_t data; @@ -91,6 +93,22 @@ static int udp_flow_sock(const struct ctx *c, return rc; } + if (pif == PIF_HOST) { + if (inany_v4(&side->oaddr)) { + if (setsockopt(s, IPPROTO_IP, IP_RECVTTL, + &enable, sizeof(enable)) < 0) { + perror("setsockopt IP_RECVTTL"); + exit(1); + } + } else { + if (setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, + &enable, sizeof(enable)) < 0) { + perror("setsockopt IPV6_RECVHOPLIMIT"); + exit(1); + } + } + } + /* It's possible, if unlikely, that we could receive some packets in * between the bind() and connect() which may or may not be for this * flow. Being UDP we could just discard them, but it's not ideal. diff --git a/udp_vu.c b/udp_vu.c index ef2257c..9871024 100644 --- a/udp_vu.c +++ b/udp_vu.c @@ -152,8 +152,7 @@ static size_t udp_vu_prepare(const struct ctx *c, *iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_UDP); - l4len = udp_update_hdr4(iph, bp, toside, dlen, - DEFAULT_TTL, true); + l4len = udp_update_hdr4(iph, bp, toside, dlen, 255, true); } else { struct ipv6hdr *ip6h = vu_ip(iov_vu[0].iov_base); struct udp_payload_t *bp = vu_payloadv6(iov_vu[0].iov_base); diff --git a/util.c b/util.c index 62a6003..7c06029 100644 --- a/util.c +++ b/util.c @@ -111,13 +111,18 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type, if (proto == IPPROTO_UDP) { int pktinfo = af == AF_INET ? IP_PKTINFO : IPV6_RECVPKTINFO; int recverr = af == AF_INET ? IP_RECVERR : IPV6_RECVERR; + int ttlopt = af == AF_INET ? IP_RECVTTL : IPV6_RECVHOPLIMIT; int level = af == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + int ttlevel = af == AF_INET ? IPPROTO_IP : SOL_IPV6; if (setsockopt(fd, level, recverr, &y, sizeof(y))) die_perror("Failed to set RECVERR on socket %i", fd); if (setsockopt(fd, level, pktinfo, &y, sizeof(y))) die_perror("Failed to set PKTINFO on socket %i", fd); + + if (setsockopt(fd, ttlevel, ttlopt, &y, sizeof(y))) + die_perror("Failed to set RECVTTL on socket %i", fd); } if (ifname && *ifname) { -- 2.48.1