├── .github └── workflows │ └── go.yml ├── README.md ├── assets ├── 1.jpg ├── 2.jpg └── 3.png └── main.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.20' 23 | 24 | - name: Build 25 | run: go build -v ./... 26 | 27 | - name: Test 28 | run: go test -v ./... 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## portreuse reuseport 端口复用 2 | 3 | ## Usage 4 | 5 | ``` 6 | go run main.go -listenport 8080 -remoteaddr 127.0.0.1:22 -sourceaddr 112.65.12.104 -listenaddr 172.19.0.2 7 | ``` 8 | 9 | ``` 10 | -listenport 想要复用的本地端口,该端口不能监听在0.0.0.0,最好监听在127上 11 | -remoteaddr 把流量转发到的目的IP和端口 12 | -sourceaddr 远程IP,只有该IP过来的才会走端口复用 13 | -listenaddr 想要复用的本地IP,建议把该值设置为本地内网IP 14 | ``` 15 | 上面的命令含义为:当来源IP为112.65.12.104时,会把访问8080端口的流量转发到本地的22端口上。 16 | 17 | ## 实操 18 | 19 | 1、目标服务127监听着8080 tomcat服务: 20 | 21 | ![](assets/1.jpg) 22 | 23 | 2、开启端口复用,监听内网网卡: 24 | 25 | ![](assets/2.jpg) 26 | 27 | 28 | 3、外部访问8080端口,成功访问到另外一台机器的ssh: 29 | 30 | ![](assets/3.png) 31 | 32 | -------------------------------------------------------------------------------- /assets/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jumbo-WJB/portreuse/5af6fb9e3d431189d076af4468cea564107ba2a4/assets/1.jpg -------------------------------------------------------------------------------- /assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jumbo-WJB/portreuse/5af6fb9e3d431189d076af4468cea564107ba2a4/assets/2.jpg -------------------------------------------------------------------------------- /assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jumbo-WJB/portreuse/5af6fb9e3d431189d076af4468cea564107ba2a4/assets/3.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "os" 9 | "syscall" 10 | "strings" 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | var lc = net.ListenConfig{ 15 | Control: func(network, address string, c syscall.RawConn) error { 16 | var opErr error 17 | if err := c.Control(func(fd uintptr) { 18 | opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) 19 | opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 20 | }); err != nil { 21 | return err 22 | } 23 | return opErr 24 | }, 25 | } 26 | 27 | // // 定义常量 28 | // const ( 29 | // localPort = "8080" // 本地监听端口 30 | // remoteAddr = "43.128.47.230:22" // 远程转发地址 31 | // sourceAddr = "112.65.12.104" // 来源地址 32 | // ) 33 | 34 | // 定义错误处理函数 35 | func handleError(err error) { 36 | if err != nil { 37 | fmt.Println(err) 38 | os.Exit(1) 39 | } 40 | } 41 | 42 | // 定义转发函数 43 | func forward(src, dst net.Conn) { 44 | defer src.Close() 45 | defer dst.Close() 46 | buf := make([]byte, 1024) 47 | for { 48 | n, err := src.Read(buf) 49 | if err != nil { 50 | break 51 | } 52 | n, err = dst.Write(buf[:n]) 53 | if err != nil { 54 | break 55 | } 56 | } 57 | } 58 | 59 | func main() { 60 | pid := os.Getpid() 61 | // 定义命令行参数 62 | var localPort string 63 | var remoteAddr string 64 | var sourceAddr string 65 | var listenAddr string 66 | 67 | // 解析命令行参数 68 | flag.StringVar(&localPort, "listenport", "80", "local port to listen") 69 | flag.StringVar(&remoteAddr, "remoteaddr", "2.2.2.2:22", "remote address to forward") 70 | flag.StringVar(&sourceAddr, "sourceaddr", "1.1.1.1", "source address to filter") 71 | flag.StringVar(&listenAddr, "listenaddr", "172.19.0.2", "local address to listen") 72 | flag.Parse() 73 | l, err := lc.Listen(context.Background(), "tcp", listenAddr+":"+localPort) 74 | if err != nil { 75 | panic(err) 76 | } 77 | fmt.Printf("TCP Server with PID: %d is running \n", pid) 78 | 79 | for { 80 | // 接受客户端连接 81 | conn, err := l.Accept() 82 | if err != nil { 83 | break 84 | } 85 | fmt.Println("Accepted connection from", conn.RemoteAddr()) 86 | 87 | // 判断来源地址是否为1.1.1.1,如果是则转发流量,否则关闭连接 88 | if strings.HasPrefix(conn.RemoteAddr().String(), sourceAddr) { 89 | fmt.Println("Forwarding traffic to", remoteAddr) 90 | // 创建远程连接套接字 91 | rconn, err := net.Dial("tcp", remoteAddr) 92 | handleError(err) 93 | 94 | // 启动两个协程,分别转发两个方向的流量 95 | go forward(conn, rconn) 96 | go forward(rconn, conn) 97 | } else { 98 | fmt.Println("Closing connection from", conn.RemoteAddr()) 99 | conn.Close() 100 | } 101 | } 102 | } 103 | 104 | 105 | --------------------------------------------------------------------------------