Someone forwards 1723, the client reaches the server, authentication begins, and then everything stops without an error. The natural conclusion is a wrong password, so the password gets retyped three times. The forward was fine and the credentials were fine. What broke is a second protocol that the forward never covered, and a kernel default that quietly stopped helping with it.

The forward was never the problem

A port-forward rule describes TCP or UDP traffic. It cannot describe a payload that switches to a different IP protocol partway through a session, and PPTP does exactly that.

That is why the connection looks healthy right up to the moment it hangs. The part that a port rule can govern succeeded, and the part it cannot govern is the part that failed.

TCP for control, GRE for data

The call is negotiated over TCP 1723. Once it is up, the payload moves into GRE, IP protocol 47, which carries no port numbers at all. A rule that keys on ports has nothing to match.

What GRE does carry is a call identifier in its header, and a translator has to rewrite that field consistently in both directions or the two halves of the session stop agreeing with each other. One direction rewritten and the other not produces a session that authenticates and then discards every data packet. That is exactly the shape of the symptom: success, then silence.

Bare GRE without PPTP needs no such rewriting, which is why a plain GRE tunnel can work through a router that fails for a PPTP client on the same path.

Four modules and a sysctl that turned them off

Two modules handle the framing, nf_conntrack_proto_gre and nf_nat_proto_gre. Two more handle the call identifier, nf_conntrack_pptp and nf_nat_pptp. Loading the last of those pulls the rest in through dependencies, so a module listing should show all four.

Older instructions use an ip_ prefix from the 2.6 era. Those commands can appear to run while loading nothing, so check the loaded list rather than the exit status.

Having them loaded is not enough any more. Since Linux 4.7 the net.netfilter.nf_conntrack_helper sysctl defaults to 0, which disables automatic helper assignment for new flows. A router can have every module present and still translate nothing. Check the effective value before you check the module list again.

Either turn automatic assignment back on globally, or attach the helper to the flows that need it and leave everything else alone:

iptables -t raw -A PREROUTING -p tcp --dport 1723 -j CT --helper pptp

The second option is the one to prefer. It scopes the behaviour to PPTP traffic instead of re-enabling automatic assignment across the router.

Opening a protocol that has no ports

A firewall rule that permits 1723 says nothing about protocol 47. On firewalld, allow the protocol on the zone facing the client. On plain iptables, accept protocol gre in the input chain.

Then capture on both sides. Packets leaving the client with no replies arriving means the return path is filtered, and that is a different fault from a rejected credential.

After it connects, the size limit

GRE inserts a header, and PPP inside it adds more, so a working link reports an MTU well under 1500. Small responses load and large ones stall, which looks like a second, unrelated problem appearing the moment the first one is solved.

Read the MTU on the client's PPP adapter and reconcile it with the router's own calculation instead of lowering numbers until something works. Full-size packets leaving with nothing coming back point at that limit rather than at translation.

Repairing the path gets the user working, and it does not make PPTP a good choice. MS-CHAPv2 and RC4 are long deprecated, so treat the module load as a temporary fix and plan a move to a maintained protocol.

Artitgels cun il medem tema