GRE write failure in the log, and the first instinct is to reset the user's password. That instinct is almost always wrong. These messages describe the data path, not the credential check: the client may have authenticated successfully and then stalled at the stage where it registers on the network. Two different messages point at two different faults, and neither is solved by a new password.

Two messages, two different faults

A read failure means the GRE packets never reach the daemon, or are dropped on the way in. The control connection may look perfectly healthy while nothing crosses the data path. Authentication is the one thing that demonstrably worked.

A write failure usually means the session negotiated something the kernel or the client cannot use. The control channel completed, and the options agreed during that exchange do not match what the endpoints can actually do. That is a configuration disagreement, not an authentication event.

Read which of the two appears before you change anything. They send you to different layers, and treating them as one symptom is why the same server gets patched twice for unrelated reasons. The log line is the only free diagnostic you get.

Protocol 47 is not port 47

PPTP uses TCP 1723 to set up the call and GRE to carry the payload. GRE is IP protocol 47, not a port number, so a firewall rule that opens port 47 permits nothing useful.

A router in the path needs a PPTP passthrough feature or an explicit GRE allowance. Without one, the TCP handshake completes and the tunnel never carries a packet, which produces exactly the read failure above. Port 47 rules in a firewall are the most common wasted afternoon in this protocol.

Watch both directions at once on the server rather than guessing which side drops traffic: tcpdump -ni any port 1723 or proto gre.

If 1723 shows call setup while GRE stays silent, the block is upstream of the host. If GRE arrives and the daemon still fails, the problem is local and the options are the next place to look.

Allow GRE where the host actually forwards

Inserting a forward rule for GRE is a reasonable test on a host that forwards tunnel traffic, and it is not a policy. A test rule that accepts everything of one protocol is a diagnostic, not a design. Leave it in place long enough to learn something, then replace it with a scoped rule.

Add an input rule for packets addressed to the server itself, because the forward chain does not see those. Then watch the rule counters before and after a connection attempt, which tells you whether the packets arrive at all.

Persist the rule through the distribution's firewall service instead of re-running it after every reboot. On an nftables host, match ip protocol gre in both the input and forward chains.

Confirm the ppp_generic module is loaded as well, and check whether connection tracking is dropping the return direction. Both produce failures that look like a firewall problem and are not. A missing kernel module looks exactly like a blocked protocol until you check.

Match the options to the client you actually have

The other report's fix lives in /etc/ppp/pptpd-options, where the recipe refuses PAP, CHAP and MSCHAP, comments out the matching require lines and keeps stateful MPPE. That recipe was written for one client mix, and copying it blindly breaks the common case. Read the refuse lines as a statement about which clients the author had, not as a recommendation.

A Windows client normally needs MS-CHAPv2 with MPPE-128. Keep those requirements and remove only the options the client genuinely cannot negotiate.

# keep encryption; drop only what this client cannot negotiate
refuse-pap
refuse-chap
refuse-mschap
mppe-stateful
require-mschap-v2
require-mppe-128

Disabling encryption to make a connection succeed is a downgrade, not a repair. Test such a variant on a lab link, then restore encryption before the service faces real clients. The connection that works insecurely is worse than the one that fails honestly.

Read the client's error number

The client's numeric code narrows the stage faster than the server log does. Read it before you read anything else. 691 means the credentials were rejected, 721 means the server never answered, and 619 means the connection dropped. Two minutes with the number saves an hour with the configuration.

Compare that code with the server log before editing anything, and retest with the same client after each change. Otherwise a second fault hides behind the first and the fix looks ineffective.

When the login succeeds and no traffic moves, GRE remains the suspect. That is the moment to return to the capture rather than to the credential store.

Nuŋlɔɖi siwo ku ɖe eŋu