tuntap: add raw tcp tunnel

This commit is contained in:
ginuerzh 2020-01-06 13:05:20 +08:00
parent 479ad9aab7
commit c0c477493d
6 changed files with 93 additions and 13 deletions

View File

@ -31,13 +31,14 @@ gost - GO Simple Tunnel
* [负载均衡](https://docs.ginuerzh.xyz/gost/load-balancing/) * [负载均衡](https://docs.ginuerzh.xyz/gost/load-balancing/)
* [路由控制](https://docs.ginuerzh.xyz/gost/bypass/) * [路由控制](https://docs.ginuerzh.xyz/gost/bypass/)
* [DNS控制](https://docs.ginuerzh.xyz/gost/dns/) * [DNS控制](https://docs.ginuerzh.xyz/gost/dns/)
* [TUN/TAP设备](https://docs.ginuerzh.xyz/gost/tuntap/)
Wiki站点: <https://docs.ginuerzh.xyz/gost/> Wiki站点: <https://docs.ginuerzh.xyz/gost/>
Google讨论组: <https://groups.google.com/d/forum/go-gost>
Telegram讨论群: <https://t.me/gogost> Telegram讨论群: <https://t.me/gogost>
Google讨论组: <https://groups.google.com/d/forum/go-gost>
安装 安装
------ ------

View File

@ -28,13 +28,14 @@ Features
* [Load balancing](https://docs.ginuerzh.xyz/gost/en/load-balancing/) * [Load balancing](https://docs.ginuerzh.xyz/gost/en/load-balancing/)
* [Routing control](https://docs.ginuerzh.xyz/gost/en/bypass/) * [Routing control](https://docs.ginuerzh.xyz/gost/en/bypass/)
* [DNS control](https://docs.ginuerzh.xyz/gost/en/dns/) * [DNS control](https://docs.ginuerzh.xyz/gost/en/dns/)
* [TUN/TAP device](https://docs.ginuerzh.xyz/gost/en/tuntap/)
Wiki: <https://docs.ginuerzh.xyz/gost/en/> Wiki: <https://docs.ginuerzh.xyz/gost/en/>
Google group: <https://groups.google.com/d/forum/go-gost>
Telegram group: <https://t.me/gogost> Telegram group: <https://t.me/gogost>
Google group: <https://groups.google.com/d/forum/go-gost>
Installation Installation
------ ------

View File

@ -395,9 +395,17 @@ func (r *route) GenRouters() ([]router, error) {
case "ohttp": case "ohttp":
ln, err = gost.ObfsHTTPListener(node.Addr) ln, err = gost.ObfsHTTPListener(node.Addr)
case "tun": case "tun":
ln, err = gost.TunListener(node.Addr) ln, err = gost.TunListener(node.Addr,
gost.TunListenConfig{
TCP: node.GetBool("tcp"),
RemoteAddr: node.Remote,
})
case "tap": case "tap":
ln, err = gost.TapListener(node.Addr) ln, err = gost.TapListener(node.Addr,
gost.TapListenConfig{
TCP: node.GetBool("tcp"),
RemoteAddr: node.Remote,
})
default: default:
ln, err = gost.TCPListener(node.Addr) ln, err = gost.TCPListener(node.Addr)
} }

8
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect
github.com/cheekybits/genny v1.0.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect
github.com/coreos/go-iptables v0.4.5 // indirect
github.com/dchest/siphash v1.2.1 // indirect github.com/dchest/siphash v1.2.1 // indirect
github.com/docker/libcontainer v2.2.1+incompatible github.com/docker/libcontainer v2.2.1+incompatible
github.com/ginuerzh/gosocks4 v0.0.1 github.com/ginuerzh/gosocks4 v0.0.1
@ -17,6 +18,7 @@ require (
github.com/go-log/log v0.1.0 github.com/go-log/log v0.1.0
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/golang/mock v1.2.0 // indirect github.com/golang/mock v1.2.0 // indirect
github.com/google/gopacket v1.1.17 // indirect
github.com/gorilla/websocket v1.4.0 // indirect github.com/gorilla/websocket v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/klauspost/compress v1.4.1 github.com/klauspost/compress v1.4.1
@ -37,10 +39,10 @@ require (
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
github.com/tjfoc/gmsm v1.0.1 // indirect github.com/tjfoc/gmsm v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 github.com/xtaci/tcpraw v1.2.25
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e // indirect
gopkg.in/gorilla/websocket.v1 v1.4.0 gopkg.in/gorilla/websocket.v1 v1.4.0
gopkg.in/xtaci/kcp-go.v4 v4.3.2 gopkg.in/xtaci/kcp-go.v4 v4.3.2
gopkg.in/xtaci/smux.v1 v1.0.7 gopkg.in/xtaci/smux.v1 v1.0.7

13
go.sum
View File

@ -12,6 +12,8 @@ github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 h1:x17NvoJaphEzay
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/dchest/siphash v1.2.0 h1:YWOShuhvg0GqbQpMa60QlCGtEyf7O7HC1Jf0VjdQ60M= github.com/dchest/siphash v1.2.0 h1:YWOShuhvg0GqbQpMa60QlCGtEyf7O7HC1Jf0VjdQ60M=
github.com/dchest/siphash v1.2.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/dchest/siphash v1.2.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
@ -34,6 +36,8 @@ github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
@ -77,19 +81,28 @@ github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkB
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8= golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

View File

@ -14,6 +14,7 @@ import (
"github.com/shadowsocks/go-shadowsocks2/core" "github.com/shadowsocks/go-shadowsocks2/core"
"github.com/songgao/water" "github.com/songgao/water"
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
"github.com/xtaci/tcpraw"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
@ -290,14 +291,20 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A
return err return err
} }
type TunListenConfig struct {
TCP bool
RemoteAddr string
}
type tunListener struct { type tunListener struct {
addr net.Addr addr net.Addr
conns chan net.Conn conns chan net.Conn
closed chan struct{} closed chan struct{}
config TunListenConfig
} }
// TunListener creates a listener for tun tunnel. // TunListener creates a listener for tun tunnel.
func TunListener(addr string) (Listener, error) { func TunListener(addr string, cfg TunListenConfig) (Listener, error) {
laddr, err := net.ResolveUDPAddr("udp", addr) laddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -308,10 +315,22 @@ func TunListener(addr string) (Listener, error) {
addr: laddr, addr: laddr,
conns: make(chan net.Conn, threads), conns: make(chan net.Conn, threads),
closed: make(chan struct{}), closed: make(chan struct{}),
config: cfg,
} }
for i := 0; i < threads; i++ { for i := 0; i < threads; i++ {
conn, err := net.ListenUDP("udp", laddr) var conn net.Conn
if cfg.TCP {
var c *tcpraw.TCPConn
if cfg.RemoteAddr != "" {
c, err = tcpraw.Dial("tcp", cfg.RemoteAddr)
} else {
c, err = tcpraw.Listen("tcp", addr)
}
conn = &rawTCPConn{c}
} else {
conn, err = net.ListenUDP("udp", laddr)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -592,14 +611,20 @@ func (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.A
return err return err
} }
type TapListenConfig struct {
TCP bool
RemoteAddr string
}
type tapListener struct { type tapListener struct {
addr net.Addr addr net.Addr
conns chan net.Conn conns chan net.Conn
closed chan struct{} closed chan struct{}
config TapListenConfig
} }
// TapListener creates a listener for tap tunnel. // TapListener creates a listener for tap tunnel.
func TapListener(addr string) (Listener, error) { func TapListener(addr string, cfg TapListenConfig) (Listener, error) {
laddr, err := net.ResolveUDPAddr("udp", addr) laddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -610,10 +635,22 @@ func TapListener(addr string) (Listener, error) {
addr: laddr, addr: laddr,
conns: make(chan net.Conn, threads), conns: make(chan net.Conn, threads),
closed: make(chan struct{}), closed: make(chan struct{}),
config: cfg,
} }
for i := 0; i < threads; i++ { for i := 0; i < threads; i++ {
conn, err := net.ListenUDP("udp", laddr) var conn net.Conn
if cfg.TCP {
var c *tcpraw.TCPConn
if cfg.RemoteAddr != "" {
c, err = tcpraw.Dial("tcp", cfg.RemoteAddr)
} else {
c, err = tcpraw.Listen("tcp", addr)
}
conn = &rawTCPConn{c}
} else {
conn, err = net.ListenUDP("udp", laddr)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -684,6 +721,24 @@ func (c *tunTapConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
type rawTCPConn struct {
*tcpraw.TCPConn
}
func (c *rawTCPConn) Read(b []byte) (n int, err error) {
err = &net.OpError{Op: "read", Net: "rawtcp", Source: nil, Addr: nil, Err: errors.New("read not supported")}
return
}
func (c *rawTCPConn) Write(b []byte) (n int, err error) {
err = &net.OpError{Op: "write", Net: "rawtcp", Source: nil, Addr: nil, Err: errors.New("write not supported")}
return
}
func (c *rawTCPConn) RemoteAddr() net.Addr {
return &net.IPAddr{}
}
func IsIPv6Multicast(addr net.HardwareAddr) bool { func IsIPv6Multicast(addr net.HardwareAddr) bool {
return addr[0] == 0x33 && addr[1] == 0x33 return addr[0] == 0x33 && addr[1] == 0x33
} }