Redirect traffic going to a dst-port to another port

Hi all,

I’m new to firewall on mikrotik. I would like to redirect traffic destined to a specific port, to another port.

Explanation: I have a webserver accepting connections on port 80. On the same machine, I have a docker bound to port 3000. I would like to kind of rewrite the destination port, from 80 to 3000. That way, if I type “www.example.com” I get the webserver homepage, but if I type “www2.example.com”, The connection would be sent to the port 3000.

I have tried many things:

  1. Use Firewall/NAT on dst-nat to port 3000. It works but it can’t be filtered using the “content” field. Empty, it matches any packet and the result is what I want. But I would like to filter the content with: “Host: www2.example.com
  2. Maybe packet content isn’t accessible before pre-routing. So I tried to mangle them, and it works! I can add a connection-mark then a packet-mark filtered by the “content” field. Great. But now I can’t route them on the est-port needed. Apparently, there is strictly no action on any chain being able to do what I want with a marked-packet.
  3. There are 2 routers between me and the servers. I thought about marking the packets in the previous router and dst-nat accordingly on the second. But marks don’t leave the router using them.
  4. I thought about a way to send back the marked packet to the beginning of its flow, then I could dst-nat it, but I don’t find how.

Any idea on how to achieve that, only using the mikrotik firewall?

Short answer: Impossible.

Long answer: Trouble with your approach is that for dstnat you need to start redirecting from very first packet of connection, but it’s just SYN packet without any useful info. The “Host: www2.example.com” only comes later and then it’s too late to do anything.

Solution you’re looking for is called reverse proxy and RouterOS does not have proper support for that. Something can be done (https://wiki.mikrotik.com/wiki/Multiple_Web_Servers), but it’s more a hack than proper solution. Plus I’m not sure if it can do anything about different ports. On the other hand, with second router on the way, you could use proxy on first one to send requests to some different “virtual” addresses and second router could forward those to different ports on real server. But again, it’s definitely not proper or nice solution.

Thank you for your answer.

So really, there is strictly no way to send back a packet to the beginning of the flow? Like for instance, redirect the packet to the same router (or localhost) without rewriting the dst-port?

Too bad… but I’ll dig into web-proxies.

PS: You’re saying that when dst-nat, packets containing http payload only comes later. But if you forget about marking, is it possible to access the content of a packet at dst-nat?

Having a web server, isn’t it easier to set up virtual hosts on it and proxy www2.example.com to the destination you want by the web server?
Even a page that just redirects to port 3000 would do.
Then you only need to forward port 3000 to server:3000 on your router using simple port forward. Your client will connect directly to 3000 because of the web server redirect.

Thank you for your answer.

Of course it’s easy. I could have done it the first time, but I’m looking for a way to achieve it using only firewall. If it’s impossible, I have workarounds, but still, no answer to my question. I’m trying to understand how it works with different twisted ideas :wink:

I think it would be a nice feature to implement on MT, the ability to still have a change to dst-nat or change dst-port of a marked packet.

I’m all for twisted ideas, but there’s nothing you can do here. When you look at TCP communication using packet sniffer, you’ll see that it begins like this:

client->server: SYN (= hey, I’d like to connect to this port)
server->client: SYN, ACK (= sure, go ahead)
client->server:

Something must answer to client’s initial SYN, otherwise nothing can happen. The trouble is, when you get that packet, there’s nothing in it about requested hostname, you can’t forward it to right server, because you don’t know which one it’s going to be. Your idea with “re-sending” packets (if I understand it correctly) won’t help you either, because it’s not just one packet, you need several and they’re part of bidirectional conversation. So you need to establish connection between client and router first, but once you do that, you can’t simply send it somewhere else later.

Well, perhaps in theory, with some fake packets sent to real server (basically repeating what client sent to router), … I’m not sure, but even if it worked, it would be too fragile, because there would be no guarantee that server replies exactly as router already did. And then what?

That’s why there are reverse proxies. You connect to them, they undertand HTTP, so they take the request from client and send it to real server. And if that fails, they return correct error message to client.

A nice feature would be proper support for reverse proxy scenario in RouterOS, I’m sure that many users would like that. The basic framework is already there (as the linked wiki page shows), it just needs some polishing, i.e. proper definition of backends, SSL support, and that’s pretty much it. Until then, use reverse proxy on your server, as suggested by docmarius. Either use your current webserver if it supports it or install another.

Thank you for your clarifications.

It wasn’t really “re-sending” them. Actually, what I thought about, was: mark-connection/packet, then send back the packets to the beginning of its flow, according to the packet flow diagram. Like for instance, routing it back to 127.0.0.1 (which would be the router itself) which would THEN be able to dst-nat the marked packets (as it has already been marked). Too much twisted apparently.

Just to be sure. There as many packets, firsts are SYN/ACK, but the rest? One of them (I sniffed them) will contain a “Host:” string and will match, if it’s not the first, the second, … Then it will be the n-th packet, why isn’t it possible to dst-nat/redirect/re-route/you-name-it THIS packet or THIS and SUBSEQUENT only?

Thank you anyway for your answer. I have already configured my web server (which is just a Synology NAS) with a php “header(‘Location: …’);” inside :wink:

How to better explain it… There are multiple packets and they form the connection. It’s a dialog between client and server. More complete simplified example of http request is:

1 - client: Hello server, I want to connect to port 80.
2 - server: Ok, you’re welcome, go for it.
3 - client: Thanks, you’re so great!
4 - client: So gimme /picture.jpg from http://www.example.net.
5 - server: Ok, here it goes:
6 - server:

Each line is one packet. You’ll see the same in e.g. Wireshark, just in a little less descriptive form.

You want to send this connection to right backend server. But you won’t learn which one it is until packet #4 arrives. And it won’t arrive before you reply (#2) to original starting packet (#1).

Packets pass through firewall one by one. You could send one to go through packet flow diagram again from beginning, but it would be only the one you have right now. And it won’t help you. If you do it for #1, it’s useless. If you do it for #4, it’s useless too, because you no longer have previous ones.

Port forwarding does not work with individual packets, it works with connections. It has to, because it would not work otherwise. Connection is everything from packet #1. If you waited for packet #4 and then sent it (and all following) to backend, it would refuse it, because it also requires initial conversation about which port to use. It would go like this:

4 - client: So gimme /picture.jpg from http://www.example.net.
5 - backend: What? I don’t know you, go away.

There would have to be some kind of buffer where you’d store all previous packets from client. And when you’d learn about proper destination, you’d have to pretend to be client and try to establish connection with backend. And if it went well, you’d stick further packets to this connection. But it would be to risky. What if the faked connection went like this:

1 - client(faked): Hello server, I want to connect to port 80.
2 - backend: Nah, I don’t feel like it.

What you’d say to real client?

But even if backend accepted the connection, there are other things in packets that could cause trouble (sequence numbers, …). So you’d most likely have to “fix” all following packets in both directions, so that neither server nor client would realize you’re interfering. That would be just too expensive. Even the initial buffering would be bad. Router needs to pass packets as fast as possible, it can’t hold tons of them in memory for later shenanigans. :wink:

Well well… Put that way… I didn’t know that a connection won’t be established if there isn’t the initial connection, thanks for that.