I'm trying to write a program that receives DHCP discoveries (UDP) and forwards them on to a given IP address using a different source IP address depending on the content of a specific field (GIADDR) in the DHCP packet.
I could get working the receiving and sending bit but I'm having an issue with using as IP source address anything that is not a configured IP address on the local machine.
I believe that this can only be done using Raw sockets; is that true ?
Are there any examples out there on how to do that in Go ?
I've spent a couple of days looking around but could not find much.
Cheers,
Sal
There are a number of hurdles to jump with what you propose:
Security
In general, being able to set the source IP address for a packet could be a very dangerous thing security wise. Under linux, in order to forge your own raw DHCP packets with custom headers, you will need to run your application as root or from an application with the CAP_NET_RAW capability (see setcap).
Raw Sockets in Go
The standard net library does not provide raw socket capability because it is very specialized and the API may be subject to change as people begin to use it in anger.
The go.net subrepository provides an ipv4 and an ipv6 package, the former of which should suit your needs:
http://godoc.org/code.google.com/p/go.net/ipv4#NewRawConn
Header Spoofing
You will need to use ipv4.RawConn's ReadFrom method to read your source packet. You should then be able to use most of those fields, along with your GIADDR logic, to set up the headers for the WriteTo call. It will probably look something like:
for {
hdr, payload, _, err := conn.ReadFrom(buf)
if err != nil { ... }
hdr.ID = 0
hdr.Checksum = 0
hdr.Src = ...
hdr.Dst = ...
if err := conn.WriteTo(hdr, payload, nil); err != nil { ... }
}
Related
Firstly, here is a PRC server. Please notice one of the return type is chan:
func (c *Coordinator) FetchTask() (*chan string, error) {
// ...
return &reply, nil
}
Then the client makes a RPC call. Typically the caller will get a channel which type is *chan string.
call("Coordinator.FecthTask", &args, &reply)
Here is my question. If the server continuously write into the channel:
for i := 0; i < 100; i++ {
reply<- strconv.Itoa(i)
}
🎈🎈🎈 Can the client continuously read read from the channel?
for {
var s string = <-reply
}
I guess the client can't, cuz server and client are not in the same memory. They communicate via Internet. Therefore, even the variable reply is a pointer, it points different address in server and client.
I'am not sure about it. What do you think of it? Thanks a lot!!!!
🎈🎈 BTW, is there anyway to implement a REAL, stateful channel between server and client?
As you already mentioned, channels are in memory variables and it is not possible to use them in other apps or systems. In the other hand gRPC will pass and parse binary data which in this case again passing a channel pointer, will only returns the pointer address in server's memory. After client receiving that address it will try to point to that address in local machine's memory which will can be any sort of data unfortunately.
If you want to push a group of data (let's say an array of strings) you can use a Server streaming or Bidirectional streaming.
In the other hand if you want to accomplish some sort of stable and keep-alive connection you can consider websockets too.
I would like to initiate TCP connections between a process and other processes which are already listening from incoming TCP connections.
I would like to use the same source address (ip-port) for these connections.
I am programming in Go.
I tried to run this code, but the second DialTCP call fails.
func main() {
ourAddr, _ := net.ResolveTCPAddr("tcp", "localhost:51234")
otherAddr1, _ := net.ResolveTCPAddr("tcp", "localhost:51236")
otherAddr2, _ := net.ResolveTCPAddr("tcp", "localhost:51237")
conn1, err1 := net.DialTCP("tcp", ourAddr, otherAddr1)
if err1 != nil {
log.Fatal("err1: " + err1.Error())
}
defer conn1.Close()
conn2, err2 := net.DialTCP("tcp", ourAddr, otherAddr2) // <- this fails
if err2 != nil {
log.Fatal("err2: " + err2.Error())
}
defer conn2.Close()
}
The error message is: err2: dial tcp 127.0.0.1:51234->127.0.0.1:51237: bind: address already in use
(the problem should not be related to the destination processes since two instances of netcat are already listening on the destination addresses)
I also tried using other ports (in case they were still in use) and it failed the same way
Here is my go version:
go version go1.13.4 linux/amd64
It seems to me the problem is not related to TCP but to the way I am using the Go library.
What am I doing wrong, and how am I supposed to do?
The problem you are facing is a general one, not related to Go programming language.
To my knowedge, you should not be able to connect to two destinations from the same source IP address and Port. I am pretty sure this is backed up by the message you get.
err2: dial tcp 127.0.0.1:51234->127.0.0.1:51237: bind: address already in use
It is not complaining about your destination address. It is complaining about the source address 127.0.0.1:51234. Try to change your source address and see what happens.
Edit: It appears that there are socket options SO_REUSEADDR and SO_REUSEPORT that allow that but are non-standardized and appear to be used under verify specific load-intensive scenarios.
It would help to know why you want to achieve such a setup.
Addition: There is a stackoverflow question that is related to your problem: TCP: can two different sockets share a port?
I'm writing a DNS server in Go to learn how DNS works and how to write a real, potentially useful program in Go.
One of the reasons I chose Go was for its Go routines instead of threads.
Currently, my DNS server doesn't really do much, it sends the same response for every query it receives.
One thing that confuses me is that my DNS server, even with its Go routines and even though it's small and doesn't do much is 10x slower than BIND.
I ran a program called dnsblast to send lots of DNS queries at once and these are my results:
BIND
Sending 10,000 queries = 39,000 pps
My server
Sending 10,000 queries = 3,000 pps
Also, as I increase the number of packets I send per second, the server responds to less and less of the queries.
For example:
When sending 1,000 queries, the server responds to 100%, but when sending 10,000 queries the server responds to just 66%.
Is there anything to do with networking in Go that could be limiting the performance of my DNS server? Are there settings in Go I can configure?
Currently, the main program looks like this:
func main() {
serv, err := net.ListenPacket("udp", ":53")
if err != nil {
panic(err)
}
defer serv.Close()
for {
tmp := make([]byte, 512)
num_bytes, addr, _ := serv.ReadFrom(tmp)
go handleQuery(serv, bytes.NewBuffer(tmp[:num_bytes]), addr)
}
}
This seems to be a pretty standard way of creating a server in Go from what I've read online.
Listen for packets
Save packet data in a buffer
Process each packet using a separate Go routine.
Are there any best practices to improve my server's throughput or does the server look okay and it's just my partial DNS implementation is slow?
Thanks!
Unfortunately Go's UDP support is suboptimal. The implementation allocates memory. What helps a bit is to run the loop in parallel. Increased buffer sizes on OS level limit package loss.
I have been trying to solve these 2 problems, but without success.
I wonder if it's possible to remove specific packets from an interface with Gopacket or is it just for listening on the wire? For example when I send a UDP packet to a wrong port and then with Gopacket I correct it, it will send 2 packets, 1 to the wrong port and 1 to the correct one. Is there a way to discard/drop the wrong packet with Gopacket?
What I am trying to do, is to pick up all packets that are sent by a client over IP and then encapsulate each packet as a payload in another protocol X and send to the remote host which will receive on protocol X, get the payload and send it on its interface to reach the server over IP again. (IP (Client) -> Protocol X (Sniffer 1) -> Protocol X (Sniffer 2) -> IP (Server))
I have verified that the packet which Sniffer 1 picks up from the Client's interface is the same which arrives at Sniffer 2, but the problem is when Sniffer 2 injects it on the Server's interface. I can't see that packet with tcpdump or any other tool. The packet is injected with this command:
if handle, err := pcap.OpenLive("enp0s8", 1600, true, 100); err != nil {
panic(err)
} else {
err = handle.WritePacketData(packet.Data())
}
If the Protocol X part is avoided, then the server will receive messages from client, but with Protocol X it is not possible.
Thanks in advance!
According to the Documentation
Package pcap allows users of gopacket to read packets off the wire or
from pcap files.
To discard packages, you will need to be able to intercept them. Depending on how generic you want to solve this problem, you probably need to hook into the kernel. I recommend looking into iptables and netfilters.
I found some VPN that are written in go, maybe look into how they are built, as you want to do something similar (tunnelling of packets).
I've been scouring the internet and can't find much at all about posting forms in golang tests. This is my attempt at it. I get the error "dial tcp: too many colons in address ::1" though. If I change the address to "http://localhost:8080/" I get "dial tcp 127.0.0.1:8080: connection refused".
I've read that if you put the (IPv6) address in brackets, the brackets will fix the problem, but then I get the error unrecognized protocol.
var addr = "http://::1/"
h := handlers.GetHandler()
server := httptest.NewServer(h)
server.URL = addr
req, err := http.PostForm(addr+"login",
url.Values{"username": {"lemonparty"}, "password": {"bluewaffle"}})
if err != nil {
log.Fatal(err)
}
tl;dr the Listener in httptest.Server doesn't use the httptest.Server.URL as the url to listen on. It doesn't care what that value is. It listens on local hosts lowest open port number.
The URL property on httptest.Server is not really doing anything. Change it all you want, just don't send your requests there. Check out this example program https://play.golang.org/p/BsH38WLkrJ
Basically, if I change the servers URL then send the request to the value I set it to it doesn't work, but if I send it to the default value it does.
Also check out the source http://golang.org/src/net/http/httptest/server.go?s=415:1018#L65, as well as the certs at the bottom of the file; clearly hard coded for the lowest open port on local host. If you want to make request to another URL the Listener.