From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/socket.c | 60 +++++++++++++++++++++++++++++++++++++++- server/sock.c | 22 +++++++++++---- 2 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 0c53585443e..c4960852592 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -573,6 +573,15 @@ struct ip_hdr ULONG daddr; };
+struct ipv6_pseudo_header +{ + struct in6_addr src; + struct in6_addr dst; + UINT32 next_len; /* incapsulated packet length in network byte order */ + BYTE zero[3]; + BYTE next_header; +}; + struct icmp_hdr { BYTE type; @@ -625,6 +634,52 @@ static unsigned short chksum( BYTE *data, unsigned int count, unsigned int sum ) return check; }
+static ssize_t fixup_icmpv6_over_dgram( struct msghdr *hdr, void *buf, union unix_sockaddr *unix_addr, + HANDLE handle, ssize_t recv_len ) +{ + struct icmp_hdr *icmp_h = buf; + struct ipv6_pseudo_header ip_h; + unsigned int fixup_status; + struct in6_pktinfo *info; + struct cmsghdr *cmsg; + unsigned int sum; + + if (recv_len < sizeof(*icmp_h)) return recv_len; + + memset( &ip_h, 0, sizeof(ip_h) ); + ip_h.src = unix_addr->in6.sin6_addr; + for (cmsg = CMSG_FIRSTHDR( hdr ); cmsg; cmsg = CMSG_NXTHDR( hdr, cmsg )) + { + if (cmsg->cmsg_level != IPPROTO_IPV6) continue; +#ifdef IPV6_PKTINFO + if (cmsg->cmsg_type != IPV6_PKTINFO) continue; + info = (struct in6_pktinfo *)CMSG_DATA( cmsg ); + ip_h.dst = info->ipi6_addr; +#endif + break; + } + ip_h.next_len = htonl( recv_len ); + ip_h.next_header = IPPROTO_ICMPV6; + + SERVER_START_REQ( socket_get_icmp_id ) + { + req->handle = wine_server_obj_handle( handle ); + req->icmp_seq = icmp_h->un.echo.sequence; + if (!(fixup_status = wine_server_call( req ))) + icmp_h->un.echo.id = reply->icmp_id; + } + SERVER_END_REQ; + + if (fixup_status) WARN( "socket_get_icmp_id returned %#x.\n", fixup_status ); + else + { + sum = chksum_add( (BYTE *)&ip_h, sizeof(ip_h), 0 ); + icmp_h->checksum = 0; + icmp_h->checksum = chksum( (BYTE *)icmp_h, recv_len, sum ); + } + return recv_len; +} + static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *unix_addr, HANDLE handle, ssize_t recv_len, NTSTATUS *status ) { @@ -645,6 +700,9 @@ static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *u buf = hdr->msg_iov[0].iov_base; buf_len = hdr->msg_iov[0].iov_len;
+ if (unix_addr->addr.sa_family == AF_INET6) + return fixup_icmpv6_over_dgram( hdr, buf, unix_addr, handle, recv_len ); + if (recv_len + sizeof(ip_h) > buf_len) *status = STATUS_BUFFER_OVERFLOW;
@@ -836,7 +894,7 @@ static BOOL is_icmp_over_dgram( int fd ) int val;
len = sizeof(val); - if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || val != IPPROTO_ICMP) + if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || (val != IPPROTO_ICMP && val != IPPROTO_ICMPV6)) return FALSE;
len = sizeof(val); diff --git a/server/sock.c b/server/sock.c index 7ea708be8af..7785d3c7706 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1876,6 +1876,7 @@ static int get_unix_protocol( int family, int protocol ) switch (protocol) { case WS_IPPROTO_ICMP: return IPPROTO_ICMP; + case WS_IPPROTO_ICMPV6: return IPPROTO_ICMPV6; case WS_IPPROTO_IGMP: return IPPROTO_IGMP; case WS_IPPROTO_IP: return IPPROTO_IP; case WS_IPPROTO_IPV4: return IPPROTO_IPIP; @@ -1947,19 +1948,28 @@ static int init_socket( struct sock *sock, int family, int type, int protocol ) }
sockfd = socket( unix_family, unix_type, unix_protocol ); - #ifdef linux - if (sockfd == -1 && errno == EPERM && unix_family == AF_INET - && unix_type == SOCK_RAW && unix_protocol == IPPROTO_ICMP) + if (sockfd == -1 && errno == EPERM && unix_type == SOCK_RAW + && ((unix_family == AF_INET && unix_protocol == IPPROTO_ICMP) + || (unix_family == AF_INET6 && unix_protocol == IPPROTO_ICMPV6))) { sockfd = socket( unix_family, SOCK_DGRAM, unix_protocol ); if (sockfd != -1) { const int val = 1;
- setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) ); - setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) ); - setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) ); + if (unix_family == AF_INET6) + { +#ifdef IPV6_RECVPKTINFO + setsockopt( sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char *)&val, sizeof(val) ); +#endif + } + else + { + setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) ); + setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) ); + setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) ); + } } } #endif