Относительно вывода tcp46
из netstat
я не могу найти никакой документации, но нашел соответствующий источник.
С network_cmds-543 / netstat.proj / inet.c :
void
protopr(uint32_t proto, /* for sysctl version we pass proto # */
char *name, int af)
{
...
struct xinpcb_n *inp = NULL;
...
const char *vchar;
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0)
vchar = ((inp->inp_vflag & INP_IPV4) != 0)
? "46" : "6 ";
else
#endif
vchar = ((inp->inp_vflag & INP_IPV4) != 0)
? "4 " : " ";
xinpcb_n
определено в bsd / netinet / in_pcb.h .
* struct inpcb captures the network layer state for TCP, UDP and raw IPv6
* and IPv6 sockets.
В другом месте в этом файле inp_vflag
задокументировано как /* INP_IPV4 or INP_IPV6 */
. Они, в свою очередь, определены как:
#define INP_IPV4 0x1
#define INP_IPV6 0x2
Таким образом, в основном, когда в сокете установлены биты v4 и v6, в столбце протокола будет отображаться 46
.
Что касается Go, в сетевом пакете есть эта socket()
функция :
func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) {
...
if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil {
setDefaultSockopts()
имеет для каждой платформы определения, вот выдержка из варианта BSD :
func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
...
if supportsIPv4map() && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
Итак, разрешение обеих версий IP для сокета определяется логическим значением ipv6Only
. После еще нескольких копаний я нашел , где это решено и включает подробное объяснение логики:
// favoriteAddrFamily returns the appropriate address family for the
// given network, laddr, raddr and mode.
//
// If mode indicates "listen" and laddr is a wildcard, we assume that
// the user wants to make a passive-open connection with a wildcard
// address family, both AF_INET and AF_INET6, and a wildcard address
// like the following:
//
// - A listen for a wildcard communication domain, "tcp" or
// "udp", with a wildcard address: If the platform supports
// both IPv6 and IPv4-mapped IPv6 communication capabilities,
// or does not support IPv4, we use a dual stack, AF_INET6 and
// IPV6_V6ONLY=0, wildcard address listen. The dual stack
// wildcard address listen may fall back to an IPv6-only,
// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
// Otherwise we prefer an IPv4-only, AF_INET, wildcard address
// listen.
//
// - A listen for a wildcard communication domain, "tcp" or
// "udp", with an IPv4 wildcard address: same as above.
//
// - A listen for a wildcard communication domain, "tcp" or
// "udp", with an IPv6 wildcard address: same as above.
//
// - A listen for an IPv4 communication domain, "tcp4" or "udp4",
// with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
// wildcard address listen.
//
// - A listen for an IPv6 communication domain, "tcp6" or "udp6",
// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
// and IPV6_V6ONLY=1, wildcard address listen.
//
// Otherwise guess: If the addresses are IPv4 then returns AF_INET,
// or else returns AF_INET6. It also returns a boolean value what
// designates IPV6_V6ONLY option.
//
// Note that the latest DragonFly BSD and OpenBSD kernels allow
// neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level
// IPV6_V6ONLY socket option setting.