I'm creating a L3-Switch that modifies packets by redirecting some of them to local app. My goal is to send them further to the same MAC as before.

Short "why": zero-conf device to connect with to any ethernet network, portable, does proxying.

Switch is organized as ethernet bridge (br-lan) between eth0 and eth1. It is assumed by default that gateway for br-lan clients lies through eth0.

Question: Let's say that packet comes from eth1 on the way to eth0 and gets redirected to local app. After that app has output and destination IP of the original packet has changed. L3 tries to route packet to new destination, but it doesn't have any default gateways (And it shouldn't, because it's switch!). Assuming I know the MAC address of default gateway, how to I force packet to go out through eth0 to specific MAC address?

Technically I'm not trying to do anything "illegal" in terms of network. I want to kick the packet out of eth0 and all I'm "missing" is destination MAC, but I can retrieve it from the original packet. I know for sure that destination IP isn't local, therefore it would be sent to default gateway anyway using it's MAC address. So it's a question of implementation.

I was trying to modify destination MAC at bridge -t NAT OUTPUT by doing this:

ebtables -t nat -A OUTPUT -p ipv4 --ip-proto tcp --ip-src -j dnat --to-dst 04:61:e7:d2:e2:09

But that didn't help. (Assuming 04:61:e7:d2:e2:09 is default gateway MAC and is one of the clients just to test this theory)

Actual implementation is on OpenWRT, so available packages might be limited.

How did I get to that problem:

More information on the local app: it's ss-redir from here, binds to => https://github.com/shadowsocks/shadowsocks-libev

Added use cases to the [Device]:

Expectation: We have 3 PC-clients connected to a regular switch. After bringing [Device] and connecting it to regular switch and reconnecting PC-clients to [Device], PC-clients gain [Result] without configuring the device.


1)From the "outside"(other network nodes except 3 ours and everything else) it should look like every user keeps his IP/MAC pair so admin would be happy. DHCP is static-configured in the office, so IP/MAC pair won't probably change, but admin can change any of that. And device should handle any changes without reconfiguring manually. No new IP/MAC should appear in the network(being not admin-registered).

2)From the "outside" every PC-client should be accessible for all protocols in the network, whatever they are (RDP, NetBIOS for naming resolution, file sharing, or whatever local admin decides to do).

3)They should have internet access via default gateway as always, except proxying tcp via SS for particular destination ipset (which is always through the very same gateway)

Under assumption that these use cases require device not having any IP/MAC knowledge of the existing network from the start(because office users won't config anything by themselves), I'm trying to make "proxying bridge" that works like a switch, intercepting packets and sends them out to eth0(WAN) after local app redirection. The problem is the after redirection packet needs to be sent on its way. I'm investigating "auto-reconfig on the fly idea" with a MAC-snat/dnat, but stuck with the problem that packet won't go to eth0 after being generated locally even if I can specify Default Gateway MAC-addr in ebtables as destination.

  • This looks very much like an XY-Problem. If the X is you want is "transparent proxying", then the Y you have chosen as "have an application act on layer 5, but keep layer 2 information" is extremely difficult to do. There are other options, like having the app work on layer 2 with packet sockets, or using network namespaces, but these depend on the exact X, which you didn't tell us.
    – dirkt
    Commented May 26, 2018 at 6:44
  • You are correct, X is "transparent proxying" switch. The only missing word here is "ShadowSocks". I added "the app in question" to the post, and I really didn't consider other ways except Y because I can't force ss-redir work on L2, it's too much effort. If I need a workaround for this, doesn't matter what local app I'm using. Anyway, thanks for response.
    – clockware
    Commented May 26, 2018 at 10:21
  • Is there any particular reason why you have to work on level 2, and can't work on level 3 with NAT as described e.g. here? If yes, can you edit the question and explain the circumstances in detail?
    – dirkt
    Commented May 26, 2018 at 14:06
  • 1)NAT is too easy, I already made it. Now I explore possibilities to upgrade to L2. 2)Doesn't solve original task. Original task is ability to have several clients on the device and using other IPs or MACs except that are already assigned to local devices is prohibited. So let's say I have an angry Admin that knows all IPs and MACs by heart, but still our office wants proxying. Basically: the switch.
    – clockware
    Commented May 26, 2018 at 19:18
  • 3)Connectivity from devices to other machines in network shouldn't be lost because of NAT. All device clients should accept RDP connections by their original IP and 3389 port.
    – clockware
    Commented May 26, 2018 at 19:28

2 Answers 2


Let me guess some "original task X". These probably won't match your original task X, but they may give you an idea what kind of information is needed.

1) The device is a normal off-the-shelf home router, with four LAN ports behind an external switch at eth0, and a single WAN port at eth1. The WAN port is connected to your local networks gateway. All hosts that should use the transparent proxy are connected to the LAN ports, possibly with additional switches. No other hosts or connected to the LAN ports. The other socks-endpoint is on the WAN side.

In that case forget about level 2, use the iptables rules as described in man ss-redir, adapted to only NAT on eth0. Routing will send the proxied SOCKS-request out of eth1. NAT will take care that any answers are send back on eth0, to the device with the correct IP. ARP will make sure the MAC for this IP is used.

2) Variation of the above: Some hosts connected to the LAN ports should use the proxy, some shouldn't. In that case, look closer at the hardware, it has probably a switch-chip you can configure with sw-conf. Reconfigure it so that two LAN ports are connected to eth0, and the other two are connected to eth2. Then proceed as above. Connect hosts to the correct LAN ports as appropriate.

3) The device is used as a simple bridge in a more complicated network configuration, where the other SOCKS endpoint is on the same internal LAN segment as the hosts that should use the proxy. In that case, I need a description of the network.

Etc., pp.


If I read your use case correctly, your main problem is that you must bridge the WAN port (eth0) to the LAN ports (eth1), because the DHCP server is behind the WAN port, and it should manage the static assignment of the IPs via DHCP correctly.

In that case, I'd configure the bridge as a "brouter": everything gets bridged, except packets arriving on LAN (eth1) that should be proxied (TCP and in the desired destination IP range). These packets will emerge from the br-lan internal port of the bridge instead. There, layer 3 processing can handle them as described above. In particular, the connection tracker can track them by IP and port, and can send answers from the socks proxy back to the correct device.

No MAC NAT needed, no MAC-based connection tracking needed that cooperates with L5, etc.

I would have liked to try this out with a few network namespaces, so I could give the whole recipe, but unfortunately I won't have time for this today or in the next days.

  • I added use cases to the post(Sorry, I'm too deep into context to bring it back into life-form). NAT solution wouldn't work with use cases 1, 2 along with "without reconfiguring". Thanks for the sw-conf hint, may be I'll make use of it.
    – clockware
    Commented May 27, 2018 at 6:36
  • I tried BROUTING, but I gave up. I wasn't getting more results than with iptables. If I understood correctly BROUTER does this: for some packets that are BROUTING DROPped it pretends that there is no eth0/eth1 bridge. So you could do eth0/eth1 config yourself. It never says about MAC-destination restore in ebtables mans/docs, so the packet still needs to be routed. Also I treat local app packets as "special" because at L5 they lose --set-mark's assigned to them. I doubt that kernel keeps track of them after passing app, but may be I'm wrong and I just don't know how it works.
    – clockware
    Commented May 27, 2018 at 15:12
  • No, BROUTING does this: When the DROP target activates for a BROUTE rule, it removes the packet from the bridge processing, and instead sends the packet to the higher levels for processing, as if the packet would have arrived inbound on a network interface. So instead of the packets that should be proxied leaving towards the gateway, they are now available for processing. You don't have to do any "MAC destination restore", because processing happens on level 3 - it's like the host sent the packet directly to the router to have it proxied.
    – dirkt
    Commented May 28, 2018 at 7:48
  • The conntrack module is responsible for the L3 connection tracking. If I find the time, I'll try to implement this and show you the concrete rules, but I won't have time for the next few days, probably.
    – dirkt
    Commented May 28, 2018 at 7:49
  • But I don't have a problem with "sending to the higher levels for processing". iptables does this successfully with net.bridge.bridge-nf-call-iptables=1 config. The problem is to send packet AFTER local app generation while you don't know what default gateway is, because device is a switch. So I still don't understand what you're saying - who is going to attach destination MAC to my packets? brouter? It doesn't feel like it, he's going to ARP destination IP and fail. And that's exactly what Y problem is about.
    – clockware
    Commented May 29, 2018 at 14:42

I found dirty solution.

If we forget the X context and dive deeper into Y problem then we discover the following.

The idea is that before redirecting packet to local app I can log it and then parse. Process of traversing the packet from eth1 to eth0 to default gateway looks like this:


But after passing local app it falls into this state:

((!)CLIENT1_SRC_IP, (!)CLIENT1_SRC_MAC, SS_SERVER) and DF_GW_MAC is about to be ARPed(and fail, because there is no SS_SERVER neighbor as well as default gateway itself, so packet goes nowhere).

So I decided to take advantage of the fact that SS_SERVER is a const IP address. DF_GW_MAC from the log can be parsed and then used to add static arp route for SS_SERVER IP.

Couple of details for OpenWRT users:

To get ip neigh working:

opkg update

opkg install ip-full

And then:

ip neigh add dev eth0 lladdr 11:22:33:44:55:66

ip route add default dev eth0

So the Y problem is solved here. X is not solved completely because above where I specified (!) it is not true because local app does MASQUARADE for you using interface IP, so you have to SNAT it back to original source IP, which is also dirty. I don't see any other option for now.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .