How do I get the original destination IP of a redirected connection with pf on FreeBSD? - firewall

When I'm redirecting a connection with iptables on Linux with a -j REDIRECT rule, the program that receives the redirected connection can do getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &val, &len); to get the original, pre-redirection destination IP of the connection. How do I do the same thing when I'm redirecting a connection with pf on FreeBSD with an rdr rule?

Just solved this problem yesterday on MacOS, didn't test on FreeBSD, I think it's similar.
you need pfioc_natlook struct from net/pfvar.h.
General steps are:
initialize pfioc_natlook to pnl
fill pnl with client socket's ip & port(saddr,sxport), proxy server's bind ip & port(daddr, dxport)
open('/dev/pf'), get its fd
do ioctl(fd, C.DIOCNATLOOK, *pnl), then pnl's rdxport and rdaddr will be connection's original port & ip
Anyone still interested in this problem can check this gist: https://gist.github.com/gkoyuncu/f8aad43f66815dac7769
I did a mini POC program with golang(CGO wrapper), it works on MacOS: https://github.com/monsterxx03/pf_poc

Related

how to redirect TCP packages to a proxy without losing the original destination?

im trying to redirect tcp packages to my proxy but i lose the original ip destination in the process. because of that my proxy cant start a connection to
the original destination. my setup looks like this:
i have a normal tls linux server which i call C.
i have a man-in-the-middle proxy on my linux pc called B.
A is an android amulator in which a tls connection to C is started. A doesnt
use http or https. it just sends random data over tls.
i want A to connect to B and B to connect to C.
inside the android emulator i put in a iptables rule to redirect the tcp packages to my proxy server and it works. the only problem is that my
proxy doesnt know the destination ip of the original destination. do i need
to somehow add a header to the packages or how would i solve this problem?
The original target IP address is permanently lost if you redirect a packet to another machine since in this case the target IP of the packet needs to be replaced. There is no way to attach some information about the original IP address to the packet.
The situation is different if you redirect just to some proxy on the same machine. In this case the proxy could retrieve the original destination by calling getsockopt(fd, SOL_IP, SO_ORIGINAL_DST) (on Linux, other systems might differ) since it is still known by the OS kernel.
Thus you need to make sure that you do the redirect on the same system as the proxy resides. For this the proxy system must be in the routing path to receive the packet, for example by setting it as the default gateway.

How to bind socket to a non local interface? [duplicate]

With the following snapshot of C code, I understand that, the address that bind() call binds to listfd, is the logical address of the local machine where this server program is running. Subsequently, server listens on listfd socket of that same machine.
struct sockaddr_in serv_addr;
listfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(8000);
retval = bind(listfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listfd)
I learnt at coursera ---
that, bind() call also allow you to bind socket to remote address and port.
I would like to understand this point.
I mean,
listfd = socket(AF_INET, SOCK_STREAM, 0);
provides a file descriptor from the program process where this program is running(local machine).
My question:
If bind() call binds this local socket listfd to a remote address instead of INADDR_ANY, then which machine is actually listening? Because listfd is an entry from local process file descriptor table of that local machine where this program is running and this socket listfd is getting bind to remote machine IP address? How do i interpret that? How does this work under the hood?
You cannot bind() to a remote address, at least not in the AF_INET family. According to the man page of bind, you will get a EADDRNOTAVAIL error, saying that the address you wanted to bind to is not local.
Edit: bind() may work for remote addresses but it certainly does not in the AF_INET family. Please note that there is more than this. There are probably some families that do indeed support binding to remote addresses, probably some clustering protocols. Even if there are not, bind() may work on those theoretically in case some protocols emerge where this makes sense at all.
Edit2: As thuovila pointed out, there actually is a case where binding on remote addresses in AF_INET works. That is, setting the IP_TRANSPARENT socket option before binding. The man page of ip(7) tells us:
IP_TRANSPARENT (since Linux 2.6.24)
Setting this boolean option enables transparent proxying on
this socket. This socket option allows the calling
application to bind to a nonlocal IP address and operate both
as a client and a server with the foreign address as the local
endpoint. NOTE: this requires that routing be set up in a way
that packets going to the foreign address are routed through
the TProxy box (i.e., the system hosting the application that
employs the IP_TRANSPARENT socket option). Enabling this
socket option requires superuser privileges (the CAP_NET_ADMIN
capability).
TProxy redirection with the iptables TPROXY target also
requires that this option be set on the redirected socket.
So, with a lot of extra work, you can build a transparent proxy by tieing a local and a remote socket together with that socket option set (if I understand correctly).
I agree the Coursera material is probably wrong or at least misleading for introduction level learning about network programming.
However, I can not suppress a pedantic comment. It is possible (on Linux) to bind to a non-local IP address. Please see option IP_TRANSPARENT at http://man7.org/linux/man-pages/man7/ip.7.html. This probably has very little to do with the issue at hand, though. Also i appreciate the question is tagged bsd.
More on the point, bind() does not have anything to do with listening. It simply associates the fd with an address. It is the listen() call that enables "listening". It is possible to bind() a socket used for outgoing connections.

How to redirect all internet traffic to local proxy in the same workstation

I'm trying to find a way to redirect all the internet traffic to catch all the ip packets and work with them.
For example, with my webbrowser I try to connect to www.google.com then it generates a http request, with ip packet. I want to get that packet in my machine and then do something with it.
Is there any way to do it?
(I'm working with linux OS)
Thanks.
If you just want to capture network traffic on your own machine, try tcpdump. It dumps all IP packets to a file (with -w flag). See http://linux.die.net/man/8/tcpdump

Does accept function return error(-1) if TCP server goes out of network

Does the accept function returns error(-1) if the Ethernet interface it is attached goes out of network?if not how does the application(TCP server) will know that its interface is not active any more ??
I am using one thread for accepting the connection and not using any "select" statement for doing so.Directly calling accept() function but somehow it is not retuning error if I remove the IP address from Ethernet interface.
using C and working environment is linux.
Usually you don't bind your server socket to a specific IP address (you use INADDR_ANY). So even if you remove your ethernet IP address, you could still contact your server using the loopback interface. Or some other interface with an IP address.
If you want to make sure that your server is reachable from the net, checking the interface status does not get you much. It's just a single hop on a long path through the network. You'd need a testing client somewhere else to check the reachability of your server.

disable telnet on http port?

I might be asking wrong questions now but bear with me.
I have a Linux system with a daemon and a web front end to it. The daemon accepts socket request on a certain port and receive commands. Normally, such commands are issued by the web front end, which has it's secure login procedure. However, as HTTP allows anybody from telnet-ing onto that port and issue raw commands, I need a way of protecting the system from abuse.
I actually don't think there is a way to configure apache to no allow telnet because then the whole thing probably wouldn't work any more.
So is there any way to only allow socket created from local host?
ps. I know there is local version of socket but I'd like to avoid it - reason is that I've written an automated testing framework depending on remote connection.
Thanks,
Use iptables to restrict access to port 80 only to connections from localhost
iptables -I INPUT -j ACCEPT -p tcp --destination-port 80 -i lo
iptables -A INPUT -j DROP -p tcp --destination-port 80 -i eth0
Have your daemon bind() to the local host(127.0.0.1) only, and your web server make calls to 127.0.0.1. That way noone outside the box can make direct connections to it.
struct inaddr_in listen_addr = {0};
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(my_port);
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
Alternativly, add a firewall/iptables rule that blocks your daemon port from anything but localhost.
Your daemon could keep a list of allowed IPs, preferably in a config file, and immediately disconnect any connections that are not on the allowed list. Use the getpeername() function to get the connecting IP.
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
bzero(&sin, sizeof(sin));
if (getpeername(sock, (struct sockaddr *) &sin, &slen) != 0) {
/* getpeername failed */
close(sock);
return;
}
char * c = inet_ntoa(sin.sin_addr);
/* Now loop through your list of permitted addresses and compare to c */
Disclaimer: Code not compiled or tested, but should give you an idea of how to implement it.
If you don't want to alter your app, you could configure the firewall to restrict access to the appropriate port number, so that only that machine, and your test machine can connect to the port.

Resources