istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/istio-iptables/pkg/validation/vld_unix.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
    16  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
    17  
    18  package validation
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"net"
    24  	"syscall"
    25  
    26  	"golang.org/x/sys/unix"
    27  
    28  	"istio.io/istio/pkg/log"
    29  	"istio.io/istio/tools/istio-iptables/pkg/constants"
    30  )
    31  
    32  // Recover the original address from redirect socket. Supposed to work for tcp over ipv4 and ipv6.
    33  func GetOriginalDestination(conn net.Conn) (daddr net.IP, dport uint16, err error) {
    34  	// obtain os fd from Conn
    35  	tcp, ok := conn.(*net.TCPConn)
    36  	if !ok {
    37  		err = errors.New("socket is not tcp")
    38  		return
    39  	}
    40  	file, err := tcp.File()
    41  	if err != nil {
    42  		return
    43  	}
    44  	defer file.Close()
    45  	fd := file.Fd()
    46  
    47  	// Detect underlying ip is v4 or v6
    48  	ip := conn.RemoteAddr().(*net.TCPAddr).IP
    49  	isIpv4 := false
    50  	if ip.To4() != nil {
    51  		isIpv4 = true
    52  	} else if ip.To16() != nil {
    53  		isIpv4 = false
    54  	} else {
    55  		err = fmt.Errorf("neither ipv6 nor ipv4 original addr: %s", ip)
    56  		return
    57  	}
    58  
    59  	// golang doesn't provide a struct sockaddr_storage
    60  	// IPv6MTUInfo is chosen because
    61  	// 1. it is no smaller than sockaddr_storage,
    62  	// 2. it is provide the port field value
    63  	var addr *unix.IPv6MTUInfo
    64  	if isIpv4 {
    65  		addr, err = unix.GetsockoptIPv6MTUInfo(
    66  			int(fd),
    67  			unix.IPPROTO_IP,
    68  			constants.SoOriginalDst)
    69  		if err != nil {
    70  			log.Errorf("Error ipv4 getsockopt: %v", err)
    71  			return
    72  		}
    73  		// See struct sockaddr_in
    74  		daddr = net.IPv4(
    75  			addr.Addr.Addr[0], addr.Addr.Addr[1], addr.Addr.Addr[2], addr.Addr.Addr[3])
    76  	} else {
    77  		addr, err = unix.GetsockoptIPv6MTUInfo(
    78  			int(fd), unix.IPPROTO_IPV6,
    79  			constants.SoOriginalDst)
    80  		if err != nil {
    81  			log.Errorf("Error to ipv6 getsockopt: %v", err)
    82  			return
    83  		}
    84  		// See struct sockaddr_in6
    85  		daddr = addr.Addr.Addr[:]
    86  	}
    87  	// See sockaddr_in6 and sockaddr_in
    88  	dport = ntohs(addr.Addr.Port)
    89  
    90  	log.Infof("Local addr %s", conn.LocalAddr())
    91  	log.Infof("Original addr %s: %d", ip, dport)
    92  	return
    93  }
    94  
    95  // Setup reuse address to run the validation server more robustly
    96  func reuseAddr(network, address string, conn syscall.RawConn) error {
    97  	return conn.Control(func(descriptor uintptr) {
    98  		err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
    99  		if err != nil {
   100  			log.Errorf("Fail to set fd %d SO_REUSEADDR with error %v", descriptor, err)
   101  		}
   102  		err = unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
   103  		if err != nil {
   104  			log.Errorf("Fail to set fd %d SO_REUSEPORT with error %v", descriptor, err)
   105  		}
   106  	})
   107  }