go network traffic GRE encapsulation - go

I want to write an application using golang that will intercept any traffic from the NIC to an application and from the application to the nic running on the machine. For example, we are running an HTTP server.
This application will look into the packet, and if the packet has a GRE header from a specific IP, it will decapsulate the traffic and forward using the inner IP header. With traffic from the application to the NIC, I want to encapsulate the packet with a GRE header.
I have no idea where to start and need some advice.
Update my findings:
So I have the following:
package main
import (
"fmt"
"io"
"net"
)
func main() {
ln, err := net.Listen("tcp", "0.0.0.0:8000")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
fmt.Println("new client")
proxy, err := net.Dial("tcp", "10.10.1.1:80")
if err != nil {
panic(err)
}
fmt.Println("proxy connected")
go copyIO(conn, proxy)
go copyIO(proxy, conn)
}
func copyIO(src, dest net.Conn) {
defer src.Close()
defer dest.Close()
io.Copy(src, dest)
}
I believe that I have to have the logic in this go copyIO(proxy, conn) call.

Related

How to extract the connected local ip address using http.Client in Go?

My PC has multiple IP addresses(ex: 10.1.1.20, 192.168.123.30, ...).
Can I extract the connected local ip address when connecting to remote server using http.Client?
If this is not possible with http.Client, is there any other possible way?
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://www.google.com", nil)
if err != nil {
panic(err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// extract the local ip address???
// getsockname(?????)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Printf("StatusCode=%v\n", resp.StatusCode)
fmt.Printf("%v\n", string(data))
}
You can either:
loop through all network interfaces
or retrieve the preferred outbound ip address
But in both case, the fact that you are in the middle of using an http.Client and making a GET would not matter: you could get those IP addresses independently.
You can provide your own Transport implementation that extracts the outgoing local IP address right after establishing the TCP connection, e.g. like this:
client := &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
conn, err := net.Dial(network, addr)
if err == nil {
localAddr := conn.LocalAddr().(*net.TCPAddr)
fmt.Println("LOCAL IP:", localAddr.IP)
}
return conn, err
},
},
}

Why can't I start two servers in the same Go project?

I am a new Go developer and I am trying to build a single project that has both a gRPC server and a Gin HTTP server. This is what my code roughly looks like:
package main
import (
"log"
"net"
"github.com/gin-gonic/gin"
"google.golang.org/grpc"
)
func main() {
startGRPCServer()
startHTTPServer()
}
func startGRPCServer() {
listener, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("could not attach listener to port: %v", err)
}
s := grpc.NewServer()
if err := s.Serve(listener); err != nil {
log.Fatalf("could not start grpc server: %v", err)
}
}
func startHTTPServer() {
g := gin.Default()
if err := g.Run(":8000"); err != nil {
log.Fatalf("could not start http server: %v", err)
}
}
When I run the code above, only the gRPC server starts. The startHTTPServer() function doesn't even get invoked. When I switch the order of the calls (move startHTTPServer() above startGRPCServer()), the startGRPCServer() function never gets called. Why does this happen? How can I fix this so that both functions get called and both servers run?
The startGRPCServer function blocks on running the grpc server. Run the GRPC server in a goroutine so that startGRPCServer returns.
func startGRPCServer() {
listener, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("could not attach listener to port: %v", err)
}
s := grpc.NewServer()
go func() {
if err := s.Serve(listener); err != nil {
log.Fatalf("could not start grpc server: %v", err)
}
}()
}

Different behaviors of golang tcp socket on different OS

I want to simulate the http server with tcp socket written in Go. The program runs well on Linux or Mac, but has some problems on Windows. I have configured the built-in firewall to allow connections on the port. On Windows, when the browser requests the program, it shows me "This site can’t be reached. The connection was reset." Instead, it can response "Hello world" correctly on Linux or Mac. As follows.
// implement http server with tcp socket
package main
import (
"log"
"net"
"os"
)
var content = []byte(`HTTP/1.1 200 OK
Content-type: text/plain
Hello world!`)
func handleConn(conn net.Conn) {
conn.Write(content)
defer conn.Close()
}
func main() {
addr := "localhost:10000"
listener, err := net.Listen("tcp", addr)
checkErr(err)
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
continue
}
go handleConn(conn)
}
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
os.Exit(1)
}
}
I try to change some code to read all the bytes from the connection on Windows, after that, the program responses correctly.
func handleConn(conn net.Conn) {
var buf = make([]byte, 1024)
_, err := conn.Read(buf)
checkErr(err)
conn.Write(content)
defer conn.Close()
}
But I don't know why must I read all the bytes of connection on Windows.

Sending TCP packets to proxy

I've familiar with HTTP_PROXY and defining a DefaultTransport to proxy HTTP requests. But I can't find anything about how to do the same for TCP. Is this possible? Or do I have to rely on on the proxy itself to forward the packet?
This is possible but not with an HTTP Proxy. You want a SOCKS proxy. Check out the https://godoc.org/golang.org/x/net/proxy package that provides a SOCKS5 Dialer.
package main
import (
"fmt"
"os"
"golang.org/x/net/proxy"
)
var (
proxy_addr = "my.socks.proxy.local:8088"
remote_addr = "chat.freenode.net:6697"
)
func main() {
dialer, err := proxy.SOCKS5("tcp", proxy_addr, nil, proxy.Direct)
if err != nil {
fmt.Fprintln(os.Stderr, "proxy connection error:", err)
os.Exit(1)
}
conn, err := dialer.Dial("tcp", remote_addr)
if err != nil {
fmt.Fprintln(os.Stderr, "remote connection error:", err)
os.Exit(1)
}
defer conn.Close()
// communicate with remote addr here
}

Golang: Mixing Gin with an UDP server

I'm trying to use both a UDP server to listen continuously to datagrams and a http server, but the string "UDP server up and listening on port..." and command "server.Run()" are never reached.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net"
)
func handleUDPConnection(conn *net.UDPConn) {
buffer := make([]byte, 8096)
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("UDP client: ", addr)
fmt.Println("Received from UDP client: ", string(buffer[:n]))
}
}
func main() {
server := gin.Default()
host, port := "localhost", "41234"
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", host, port))
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
server.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
for {
handleUDPConnection(conn)
}
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
}
How can I make it work?
There is an infinite loop in your code.
for {
handleUDPConnection(conn)
}
This will repetedly call the handleUDPConnection function until the program exits without ever moving on to
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
Perhaps you want to deal with the connections in a go thread. This would be something more like this:
//define an exit variable
keepListening := true
//spawn a go routine (starts the function on another thread*)
go func() {
for keepListening {
handleUDPConnection(conn)
}
}()
//notify the user that the server is listening
fmt.Sprintf("UDP server up and listening on port %s\n", port)
//run the server (I assume this function call is blocking
server.Run()
//stop the go routine when the server is done running
keepListening = false
Hope this helps!
*a goroutine is not a thread. It can be useful/simple to think of it like that, but they are distinctly different. Here's an article explaining some of the differences and advantages.

Resources