1/* 2 * ip_nat_pptp.c - Version 1.11 3 * 4 * NAT support for PPTP (Point to Point Tunneling Protocol). 5 * PPTP is a a protocol for creating virtual private networks. 6 * It is a specification defined by Microsoft and some vendors 7 * working with Microsoft. PPTP is built on top of a modified 8 * version of the Internet Generic Routing Encapsulation Protocol. 9 * GRE is defined in RFC 1701 and RFC 1702. Documentation of 10 * PPTP can be found in RFC 2637 11 * 12 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org> 13 * 14 * Development of this code funded by Astaro AG (http://www.astaro.com/) 15 * 16 * TODO: - Support for multiple calls within one session 17 * (needs netfilter newnat code) 18 * - NAT to a unique tuple, not to TCP source port 19 * (needs netfilter tuple reservation) 20 * - Support other NAT scenarios than SNAT of PNS 21 * 22 */ 23 24#include <linux/config.h> 25#include <linux/module.h> 26#include <linux/ip.h> 27#include <linux/tcp.h> 28#include <net/tcp.h> 29#include <linux/netfilter_ipv4/ip_nat.h> 30#include <linux/netfilter_ipv4/ip_nat_rule.h> 31#include <linux/netfilter_ipv4/ip_nat_helper.h> 32#include <linux/netfilter_ipv4/ip_nat_pptp.h> 33#include <linux/netfilter_ipv4/ip_conntrack_helper.h> 34#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> 35#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> 36 37MODULE_LICENSE("GPL"); 38MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 39MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); 40 41 42#define DEBUGP(format, args...) 43 44static unsigned int 45pptp_nat_expected(struct sk_buff **pskb, 46 unsigned int hooknum, 47 struct ip_conntrack *ct, 48 struct ip_nat_info *info) 49{ 50 struct ip_conntrack *master = master_ct(ct); 51 struct ip_nat_multi_range mr; 52 struct ip_ct_pptp_master *ct_pptp_info; 53 struct ip_nat_pptp *nat_pptp_info; 54 u_int32_t newsrcip, newdstip, newcid; 55 int ret; 56 57 IP_NF_ASSERT(info); 58 IP_NF_ASSERT(master); 59 IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); 60 61 DEBUGP("we have a connection!\n"); 62 63 LOCK_BH(&ip_pptp_lock); 64 ct_pptp_info = &master->help.ct_pptp_info; 65 nat_pptp_info = &master->nat.help.nat_pptp_info; 66 67 /* need to alter GRE tuple because conntrack expectfn() used 'wrong' 68 * (unmanipulated) values */ 69 if (hooknum == NF_IP_PRE_ROUTING) { 70 DEBUGP("completing tuples with NAT info \n"); 71 /* we can do this, since we're unconfirmed */ 72 if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == 73 htonl(ct_pptp_info->pac_call_id)) { 74 /* assume PNS->PAC */ 75 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = 76 htonl(nat_pptp_info->pns_call_id); 77// ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.gre.key = 78// htonl(nat_pptp_info->pac_call_id); 79 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = 80 htonl(nat_pptp_info->pns_call_id); 81 } else { 82 /* assume PAC->PNS */ 83 DEBUGP("WRONG DIRECTION\n"); 84 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = 85 htonl(nat_pptp_info->pac_call_id); 86 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = 87 htonl(nat_pptp_info->pns_call_id); 88 } 89 } 90 91 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { 92 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; 93 newcid = htonl(master->nat.help.nat_pptp_info.pac_call_id); 94 95 mr.rangesize = 1; 96 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED; 97 mr.range[0].min_ip = mr.range[0].max_ip = newdstip; 98 mr.range[0].min = mr.range[0].max = 99 ((union ip_conntrack_manip_proto ) { newcid }); 100 DEBUGP("change dest ip to %u.%u.%u.%u\n", 101 NIPQUAD(newdstip)); 102 DEBUGP("change dest key to 0x%x\n", ntohl(newcid)); 103 ret = ip_nat_setup_info(ct, &mr, hooknum); 104 } else { 105 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 106 /* nat_multi_range is in network byte order, and GRE tuple 107 * is 32 bits, not 16 like callID */ 108 newcid = htonl(master->help.ct_pptp_info.pns_call_id); 109 110 mr.rangesize = 1; 111 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS 112 |IP_NAT_RANGE_PROTO_SPECIFIED; 113 mr.range[0].min_ip = mr.range[0].max_ip = newsrcip; 114 mr.range[0].min = mr.range[0].max = 115 ((union ip_conntrack_manip_proto ) { newcid }); 116 DEBUGP("change src ip to %u.%u.%u.%u\n", 117 NIPQUAD(newsrcip)); 118 DEBUGP("change 'src' key to 0x%x\n", ntohl(newcid)); 119 ret = ip_nat_setup_info(ct, &mr, hooknum); 120 } 121 122 UNLOCK_BH(&ip_pptp_lock); 123 124 return ret; 125 126} 127 128/* outbound packets == from PNS to PAC */ 129static inline unsigned int 130pptp_outbound_pkt(struct tcphdr *tcph, struct pptp_pkt_hdr *pptph, 131 size_t datalen, 132 struct ip_conntrack *ct, 133 enum ip_conntrack_info ctinfo, 134 struct ip_conntrack_expect *exp) 135 136{ 137 struct PptpControlHeader *ctlh; 138 union pptp_ctrl_union pptpReq; 139 struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; 140 struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; 141 142 u_int16_t msg, *cid = NULL, new_callid; 143 144 ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); 145 pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); 146 147 new_callid = htons(ct_pptp_info->pns_call_id); 148 149 switch (msg = ntohs(ctlh->messageType)) { 150 case PPTP_OUT_CALL_REQUEST: 151 cid = &pptpReq.ocreq->callID; 152 153 /* save original call ID in nat_info */ 154 nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; 155 156 new_callid = tcph->source; 157 /* save new call ID in ct info */ 158 ct_pptp_info->pns_call_id = ntohs(new_callid); 159 break; 160 case PPTP_IN_CALL_REPLY: 161 cid = &pptpReq.icreq->callID; 162 break; 163 case PPTP_CALL_CLEAR_REQUEST: 164 cid = &pptpReq.clrreq->callID; 165 break; 166 case PPTP_CALL_DISCONNECT_NOTIFY: 167 cid = &pptpReq.disc->callID; 168 break; 169 170 default: 171 DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, 172 (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); 173 /* fall through */ 174 175 case PPTP_SET_LINK_INFO: 176 /* only need to NAT in case PAC is behind NAT box */ 177 case PPTP_START_SESSION_REQUEST: 178 case PPTP_START_SESSION_REPLY: 179 case PPTP_STOP_SESSION_REQUEST: 180 case PPTP_STOP_SESSION_REPLY: 181 case PPTP_ECHO_REQUEST: 182 case PPTP_ECHO_REPLY: 183 /* no need to alter packet */ 184 return NF_ACCEPT; 185 } 186 187 IP_NF_ASSERT(cid); 188 189 DEBUGP("altering call id from 0x%04x to 0x%04x\n", 190 ntohs(*cid), ntohs(new_callid)); 191 /* mangle packet */ 192 tcph->check = ip_nat_cheat_check(*cid^0xFFFF, 193 new_callid, tcph->check); 194 *cid = new_callid; 195 196 return NF_ACCEPT; 197} 198 199/* inbound packets == from PAC to PNS */ 200static inline unsigned int 201pptp_inbound_pkt(struct tcphdr *tcph, struct pptp_pkt_hdr *pptph, 202 size_t datalen, 203 struct ip_conntrack *ct, 204 enum ip_conntrack_info ctinfo, 205 struct ip_conntrack_expect *oldexp) 206{ 207 struct PptpControlHeader *ctlh; 208 union pptp_ctrl_union pptpReq; 209 struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; 210 struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; 211 212 u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL; 213 u_int32_t old_dst_ip; 214 215 struct ip_conntrack_tuple t; 216 217 ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); 218 pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); 219 220 new_pcid = htons(nat_pptp_info->pns_call_id); 221 222 switch (msg = ntohs(ctlh->messageType)) { 223 case PPTP_OUT_CALL_REPLY: 224 pcid = &pptpReq.ocack->peersCallID; 225 cid = &pptpReq.ocack->callID; 226 if (!oldexp) { 227 DEBUGP("outcall but no expectation\n"); 228 break; 229 } 230 old_dst_ip = oldexp->tuple.dst.ip; 231 t = oldexp->tuple; 232 233 /* save original PAC call ID in nat_info */ 234 nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; 235 236 /* store new callID in ct_info, so conntrack works */ 237 //ct_pptp_info->pac_call_id = ntohs(tcph->source); 238 //new_cid = htons(ct_pptp_info->pac_call_id); 239 240 /* alter expectation */ 241 if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { 242 /* expectation for PNS->PAC direction */ 243 t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); 244 t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); 245 } else { 246 /* expectation for PAC->PNS direction */ 247 t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 248 DEBUGP("EXPECTATION IN WRONG DIRECTION!!!\n"); 249 } 250 251 if (!ip_conntrack_change_expect(oldexp, &t)) { 252 DEBUGP("successfully changed expect\n"); 253 } else { 254 DEBUGP("can't change expect\n"); 255 } 256 ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t); 257 /* reply keymap */ 258 t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; 259 t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 260 t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); 261 t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); 262 ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &t); 263 264 break; 265 case PPTP_IN_CALL_CONNECT: 266 pcid = &pptpReq.iccon->peersCallID; 267 if (!oldexp) 268 break; 269 old_dst_ip = oldexp->tuple.dst.ip; 270 t = oldexp->tuple; 271 272 /* alter expectation, no need for callID */ 273 if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { 274 /* expectation for PNS->PAC direction */ 275 t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 276 } else { 277 /* expectation for PAC->PNS direction */ 278 t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 279 } 280 281 if (!ip_conntrack_change_expect(oldexp, &t)) { 282 DEBUGP("successfully changed expect\n"); 283 } else { 284 DEBUGP("can't change expect\n"); 285 } 286 break; 287 case PPTP_IN_CALL_REQUEST: 288 /* only need to nat in case PAC is behind NAT box */ 289 break; 290 case PPTP_WAN_ERROR_NOTIFY: 291 pcid = &pptpReq.wanerr->peersCallID; 292 break; 293 default: 294 DEBUGP("unknown inbound packet %s\n", 295 (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); 296 /* fall through */ 297 298 case PPTP_START_SESSION_REQUEST: 299 case PPTP_START_SESSION_REPLY: 300 case PPTP_STOP_SESSION_REQUEST: 301 case PPTP_ECHO_REQUEST: 302 case PPTP_ECHO_REPLY: 303 /* no need to alter packet */ 304 return NF_ACCEPT; 305 } 306 307 /* mangle packet */ 308 IP_NF_ASSERT(pcid); 309 DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", 310 ntohs(*pcid), ntohs(new_pcid)); 311 tcph->check = ip_nat_cheat_check(*pcid^0xFFFF, 312 new_pcid, tcph->check); 313 *pcid = new_pcid; 314 315 if (new_cid) { 316 IP_NF_ASSERT(cid); 317 DEBUGP("altering call id from 0x%04x to 0x%04x\n", 318 ntohs(*cid), ntohs(new_cid)); 319 tcph->check = ip_nat_cheat_check(*cid^0xFFFF, 320 new_cid, tcph->check); 321 *cid = new_cid; 322 } 323 324 /* great, at least we don't need to resize packets */ 325 return NF_ACCEPT; 326} 327 328 329static unsigned int tcp_help(struct ip_conntrack *ct, 330 struct ip_conntrack_expect *exp, 331 struct ip_nat_info *info, 332 enum ip_conntrack_info ctinfo, 333 unsigned int hooknum, struct sk_buff **pskb) 334{ 335 struct iphdr *iph = (*pskb)->nh.iph; 336 struct tcphdr *tcph = (void *) iph + iph->ihl*4; 337 unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; 338 struct pptp_pkt_hdr *pptph; 339 void *datalimit; 340 341 int dir; 342 343 DEBUGP("entering\n"); 344 345 /* Only mangle things once: original direction in POST_ROUTING 346 and reply direction on PRE_ROUTING. */ 347 dir = CTINFO2DIR(ctinfo); 348 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) 349 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { 350 DEBUGP("Not touching dir %s at hook %s\n", 351 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", 352 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" 353 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" 354 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); 355 return NF_ACCEPT; 356 } 357 358 /* if packet is too small, just skip it */ 359 if (datalen < sizeof(struct pptp_pkt_hdr)+ 360 sizeof(struct PptpControlHeader)) { 361 DEBUGP("pptp packet too short\n"); 362 return NF_ACCEPT; 363 } 364 365 366 pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4); 367 datalimit = (void *) pptph + datalen; 368 369 LOCK_BH(&ip_pptp_lock); 370 371 if (dir == IP_CT_DIR_ORIGINAL) { 372 /* reuqests sent by client to server (PNS->PAC) */ 373 pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo, exp); 374 } else { 375 /* response from the server to the client (PAC->PNS) */ 376 pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo, exp); 377 } 378 379 UNLOCK_BH(&ip_pptp_lock); 380 381 return NF_ACCEPT; 382} 383 384/* nat helper struct for control connection */ 385static struct ip_nat_helper pptp_tcp_helper = { 386 { NULL, NULL }, 387 "pptp", IP_NAT_HELPER_F_ALWAYS, THIS_MODULE, 388 { { 0, { tcp: { port: __constant_htons(PPTP_CONTROL_PORT) } } }, 389 { 0, { 0 }, IPPROTO_TCP } }, 390 { { 0, { tcp: { port: 0xFFFF } } }, 391 { 0, { 0 }, 0xFFFF } }, 392 tcp_help, pptp_nat_expected }; 393 394 395static int __init init(void) 396{ 397 DEBUGP("init_module\n" ); 398 399 if (ip_nat_helper_register(&pptp_tcp_helper)) 400 return -EIO; 401 402 return 0; 403} 404 405static void __exit fini(void) 406{ 407 DEBUGP("cleanup_module\n" ); 408 ip_nat_helper_unregister(&pptp_tcp_helper); 409} 410 411module_init(init); 412module_exit(fini); 413