From: Phil Mayers Date: Tue, 07 Feb 2006 19:55:13 +0000 Subject: mppe_decompress pessimism / CCP collapse Message-Id: <43E8FB21.90505@imperial.ac.uk> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: linux-ppp@vger.kernel.org All, I'm surprised not to find another report on this. I'm using ppp as part of a PPTP (Poptop) VPN. We're seeing problems with various crappy clients getting their realworld MTU, MRU and tunnel MTU wrong. Whilst investigating this, I found that ANY use of the "mru" option on an MPPE connection creates a "hole" at the top of the IP MTU which if any packets are sent with, locks the connection up. This works: lock #uncomment when testing: debug # /testing name pptpd asyncmap 0 -chap -mschap +mschap-v2 require-mppe lcp-echo-failure 30 lcp-echo-interval 5 ipcp-accept-local ipcp-accept-remote using channel 19 sent [LCP ConfReq id=0x1 ] rcvd [LCP ConfReq id=0x0 ] sent [LCP ConfRej id=0x0 ] rcvd [LCP ConfReq id=0x1 ] sent [LCP ConfAck id=0x1 ] sent [LCP ConfReq id=0x1 ] rcvd [LCP ConfAck id=0x1 ] sent [LCP EchoReq id=0x0 magic=0x4a337f21] sent [CHAP Challenge id=0xf5 , name = "pptpd"] rcvd [LCP code=0xc id=0x2 13 cc 71 31 4d 53 52 41 53 56 35 2e 31 30] sent [LCP CodeRej id=0x2 0c 02 00 12 13 cc 71 31 4d 53 52 41 53 56 35 2e 31 30] rcvd [LCP code=0xc id=0x3 13 cc 71 31 4d 53 52 41 53 2d 30 2d 58 50 2d 53 50 32 2d 56 4d 57 41 52 45] sent [LCP CodeRej id=0x3 0c 03 00 1d 13 cc 71 31 4d 53 52 41 53 2d 30 2d 58 50 2d 53 50 32 2d 56 4d 57 41 52 45] rcvd [LCP EchoRep id=0x0 magic=0x13cc7131] rcvd [CHAP Response id=0xf5 , name = "user"] sent [CHAP Success id=0xf5 "S=BYTES"] sent [CCP ConfReq id=0x1 ] rcvd [CCP ConfReq id=0x4 ] sent [CCP ConfNak id=0x4 ] rcvd [IPCP ConfReq id=0x5 ] sent [IPCP TermAck id=0x5] rcvd [CCP ConfNak id=0x1 ] sent [CCP ConfReq id=0x2 ] rcvd [CCP ConfReq id=0x6 ] sent [CCP ConfAck id=0x6 ] rcvd [CCP ConfAck id=0x2 ] sent [IPCP ConfReq id=0x1 ] rcvd [IPCP ConfRej id=0x1 ] sent [IPCP ConfReq id=0x2 ] rcvd [IPCP ConfAck id=0x2 ] rcvd [IPCP ConfReq id=0x7 ] sent [IPCP ConfNak id=0x7 ] rcvd [IPCP ConfReq id=0x8 ] sent [IPCP ConfAck id=0x8 ] Script /etc/ppp/ip-up started (pid 30930) Script /etc/ppp/ip-up finished (pid 30930), status = 0x0 I can then send packets with 1400 byte payloads and everything works, even though pppd has set the MTU to 1396 rcvd [LCP TermReq id=0x9 13 cc 71 31 00 3c cd 74 00 00 00 00] Script /etc/ppp/ip-down started (pid 30938) sent [LCP TermAck id=0x9] Script /etc/ppp/ip-down finished (pid 30938), status = 0x0 However, if I add: mru 1400 ...to the options.pptpd (doesn't matter what value I use, it happens with all of them), I get: using channel 20 sent [LCP ConfReq id=0x1 ] rcvd [LCP ConfReq id=0x0 ] sent [LCP ConfRej id=0x0 ] rcvd [LCP ConfAck id=0x1 ] rcvd [LCP ConfReq id=0x1 ] sent [LCP ConfAck id=0x1 ] sent [LCP EchoReq id=0x0 magic=0xb85c8941] sent [CHAP Challenge id=0xd1 , name = "pptpd"] rcvd [LCP code=0xc id=0x2 6c bd 0c ba 4d 53 52 41 53 56 35 2e 31 30] sent [LCP CodeRej id=0x2 0c 02 00 12 6c bd 0c ba 4d 53 52 41 53 56 35 2e 31 30] rcvd [LCP code=0xc id=0x3 6c bd 0c ba 4d 53 52 41 53 2d 30 2d 58 50 2d 53 50 32 2d 56 4d 57 41 52 45] sent [LCP CodeRej id=0x3 0c 03 00 1d 6c bd 0c ba 4d 53 52 41 53 2d 30 2d 58 50 2d 53 50 32 2d 56 4d 57 41 52 45] rcvd [LCP EchoRep id=0x0 magic=0x6cbd0cba] rcvd [CHAP Response id=0xd1 , name = "user"] sent [CHAP Success id=0xd1 "S=BYTES"] sent [CCP ConfReq id=0x1 ] rcvd [CCP ConfReq id=0x4 ] sent [CCP ConfNak id=0x4 ] rcvd [IPCP ConfReq id=0x5 ] sent [IPCP TermAck id=0x5] rcvd [CCP ConfNak id=0x1 ] sent [CCP ConfReq id=0x2 ] rcvd [CCP ConfReq id=0x6 ] sent [CCP ConfAck id=0x6 ] rcvd [CCP ConfAck id=0x2 ] sent [IPCP ConfReq id=0x1 ] rcvd [IPCP ConfRej id=0x1 ] sent [IPCP ConfReq id=0x2 ] rcvd [IPCP ConfAck id=0x2 ] rcvd [IPCP ConfReq id=0x7 ] sent [IPCP ConfNak id=0x7 ] rcvd [IPCP ConfReq id=0x8 ] sent [IPCP ConfAck id=0x8 ] Script /etc/ppp/ip-up started (pid 31165) Script /etc/ppp/ip-up finished (pid 31165), status = 0x0 Then, when I send a packet of 1400 bytes I get this: rcvd [Compressed data] 90 13 53 11 6b f8 d5 3d ... sent [CCP ResetReq id=0x3] decompress[0]: osize too small! (have: 1404 need: 1405) ...and the connection collapses: rcvd [Compressed data] 90 14 f6 94 c2 07 e5 db ... sent [CCP ResetReq id=0x3] rcvd [Compressed data] 90 15 7c 2d 5d 98 46 23 ... sent [CCP ResetReq id=0x3] rcvd [Compressed data] 90 16 67 90 1a 78 d7 1a ... rcvd [Compressed data] 90 17 65 60 78 20 2c c3 ... sent [CCP ResetReq id=0x3] rcvd [LCP TermReq id=0x9 "l\37777777675\014\37777777672\000<\37777777715t\000\000\000\000"] Script /etc/ppp/ip-down started (pid 31178) sent [LCP TermAck id=0x9] Script /etc/ppp/ip-down finished (pid 31178), status = 0x0 I think the problem may be pessimistic code in ppp_mppe.c or I guess possibly a bug in windows' PPP stack. Specifically, ppp_generic.c has ppp_decompress_frame, which does: len = ppp->rcomp->decompress(OTHERARGS, ppp->mru + PPP_HDRLEN); ...and ppp_mppe.c has: /* * Make sure we have enough room to decrypt the packet. * Note that for our test we only subtract 1 byte whereas in * mppe_compress() we added 2 bytes (+MPPE_OVHD); * this is to account for possible PFC. */ if (osize < isize - MPPE_OVHD - 1) { printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! " "(have: %d need: %d)\n", state->unit, osize, isize - MPPE_OVHD - 1); return DECOMP_ERROR; } osize = isize - MPPE_OVHD - 2; /* assume no PFC */ I think this "-1" is wrong. Surely it should be a -2, then further down when it says: /* * Do PFC decompression. * This would be nicer if we were given the actual sk_buff * instead of a char *. */ if ((obuf[0] & 0x01) != 0) { obuf[1] = obuf[0]; obuf[0] = 0; obuf++; osize++; } ...only THEN once you KNOW you've got a compressed packet add the extra byte and raise an error if you don't have enough space. Would this be correct and feasible? If so I'll have a go at a patch, but it's possible my limited understanding of PPP or MPPE means this would not help. I don't really understand the user-space code, but as far as I can tell, it pushes the MRU into the kernel via the ioctl() and that number ends up as the osize parameter. So, if you *don't* specify mru, the user-space code pushes DEFMRU, 1500 in and everything is fine. But if you *do* want to decrease the MRU, the value you specify gets pushed into the kernel *and* sent via LCP, and the far end obeys it. Hope I'm being clear - let me know if not.