blob: fe12ac3ce668c70a102f5cd586a80a214d3557c7 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package validation
import (
"errors"
"fmt"
"net"
"syscall"
)
import (
"golang.org/x/sys/unix"
"istio.io/pkg/log"
)
import (
"github.com/apache/dubbo-go-pixiu/tools/istio-iptables/pkg/constants"
)
// Recover the original address from redirect socket. Supposed to work for tcp over ipv4 and ipv6.
func GetOriginalDestination(conn net.Conn) (daddr net.IP, dport uint16, err error) {
// obtain os fd from Conn
tcp, ok := conn.(*net.TCPConn)
if !ok {
err = errors.New("socket is not tcp")
return
}
file, err := tcp.File()
if err != nil {
return
}
defer file.Close()
fd := file.Fd()
// Detect underlying ip is v4 or v6
ip := conn.RemoteAddr().(*net.TCPAddr).IP
isIpv4 := false
if ip.To4() != nil {
isIpv4 = true
} else if ip.To16() != nil {
isIpv4 = false
} else {
err = fmt.Errorf("neither ipv6 nor ipv4 original addr: %s", ip)
return
}
// golang doesn't provide a struct sockaddr_storage
// IPv6MTUInfo is chosen because
// 1. it is no smaller than sockaddr_storage,
// 2. it is provide the port field value
var addr *unix.IPv6MTUInfo
if isIpv4 {
addr, err = unix.GetsockoptIPv6MTUInfo(
int(fd),
unix.IPPROTO_IP,
constants.SoOriginalDst)
if err != nil {
log.Errorf("Error ipv4 getsockopt: %v", err)
return
}
// See struct sockaddr_in
daddr = net.IPv4(
addr.Addr.Addr[0], addr.Addr.Addr[1], addr.Addr.Addr[2], addr.Addr.Addr[3])
} else {
addr, err = unix.GetsockoptIPv6MTUInfo(
int(fd), unix.IPPROTO_IPV6,
constants.SoOriginalDst)
if err != nil {
log.Errorf("Error to ipv6 getsockopt: %v", err)
return
}
// See struct sockaddr_in6
daddr = addr.Addr.Addr[:]
}
// See sockaddr_in6 and sockaddr_in
dport = ntohs(addr.Addr.Port)
log.Infof("Local addr %s", conn.LocalAddr())
log.Infof("Original addr %s: %d", ip, dport)
return
}
// Setup reuse address to run the validation server more robustly
func reuseAddr(network, address string, conn syscall.RawConn) error {
return conn.Control(func(descriptor uintptr) {
err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
log.Errorf("Fail to set fd %d SO_REUSEADDR with error %v", descriptor, err)
}
err = unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
log.Errorf("Fail to set fd %d SO_REUSEPORT with error %v", descriptor, err)
}
})
}