1/* 2 * nf_nat_pptp.c 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-2005 by Harald Welte <laforge@gnumonks.org> 13 * 14 * Development of this code funded by Astaro AG (http://www.astaro.com/) 15 * 16 * TODO: - NAT to a unique tuple, not to TCP source port 17 * (needs netfilter tuple reservation) 18 */ 19 20#include <linux/module.h> 21#include <linux/tcp.h> 22 23#include <net/netfilter/nf_nat.h> 24#include <net/netfilter/nf_nat_helper.h> 25#include <net/netfilter/nf_nat_rule.h> 26#include <net/netfilter/nf_conntrack_helper.h> 27#include <net/netfilter/nf_conntrack_expect.h> 28#include <net/netfilter/nf_conntrack_zones.h> 29#include <linux/netfilter/nf_conntrack_proto_gre.h> 30#include <linux/netfilter/nf_conntrack_pptp.h> 31 32#define NF_NAT_PPTP_VERSION "3.0" 33 34#define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off))) 35 36MODULE_LICENSE("GPL"); 37MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 38MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); 39MODULE_ALIAS("ip_nat_pptp"); 40 41static void pptp_nat_expected(struct nf_conn *ct, 42 struct nf_conntrack_expect *exp) 43{ 44 struct net *net = nf_ct_net(ct); 45 const struct nf_conn *master = ct->master; 46 struct nf_conntrack_expect *other_exp; 47 struct nf_conntrack_tuple t; 48 const struct nf_ct_pptp_master *ct_pptp_info; 49 const struct nf_nat_pptp *nat_pptp_info; 50 struct nf_nat_range range; 51 52 ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; 53 nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; 54 55 /* And here goes the grand finale of corrosion... */ 56 if (exp->dir == IP_CT_DIR_ORIGINAL) { 57 pr_debug("we are PNS->PAC\n"); 58 /* therefore, build tuple for PAC->PNS */ 59 t.src.l3num = AF_INET; 60 t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip; 61 t.src.u.gre.key = ct_pptp_info->pac_call_id; 62 t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip; 63 t.dst.u.gre.key = ct_pptp_info->pns_call_id; 64 t.dst.protonum = IPPROTO_GRE; 65 } else { 66 pr_debug("we are PAC->PNS\n"); 67 /* build tuple for PNS->PAC */ 68 t.src.l3num = AF_INET; 69 t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip; 70 t.src.u.gre.key = nat_pptp_info->pns_call_id; 71 t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip; 72 t.dst.u.gre.key = nat_pptp_info->pac_call_id; 73 t.dst.protonum = IPPROTO_GRE; 74 } 75 76 pr_debug("trying to unexpect other dir: "); 77 nf_ct_dump_tuple_ip(&t); 78 other_exp = nf_ct_expect_find_get(net, nf_ct_zone(ct), &t); 79 if (other_exp) { 80 nf_ct_unexpect_related(other_exp); 81 nf_ct_expect_put(other_exp); 82 pr_debug("success\n"); 83 } else { 84 pr_debug("not found!\n"); 85 } 86 87 /* This must be a fresh one. */ 88 BUG_ON(ct->status & IPS_NAT_DONE_MASK); 89 90 /* Change src to where master sends to */ 91 range.flags = IP_NAT_RANGE_MAP_IPS; 92 range.min_ip = range.max_ip 93 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; 94 if (exp->dir == IP_CT_DIR_ORIGINAL) { 95 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 96 range.min = range.max = exp->saved_proto; 97 } 98 nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); 99 100 /* For DST manip, map port here to where it's expected. */ 101 range.flags = IP_NAT_RANGE_MAP_IPS; 102 range.min_ip = range.max_ip 103 = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; 104 if (exp->dir == IP_CT_DIR_REPLY) { 105 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 106 range.min = range.max = exp->saved_proto; 107 } 108 nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); 109} 110 111/* outbound packets == from PNS to PAC */ 112static int 113pptp_outbound_pkt(struct sk_buff *skb, 114 struct nf_conn *ct, 115 enum ip_conntrack_info ctinfo, 116 struct PptpControlHeader *ctlh, 117 union pptp_ctrl_union *pptpReq) 118 119{ 120 struct nf_ct_pptp_master *ct_pptp_info; 121 struct nf_nat_pptp *nat_pptp_info; 122 u_int16_t msg; 123 __be16 new_callid; 124 unsigned int cid_off; 125 126 ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; 127 nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 128 129 new_callid = ct_pptp_info->pns_call_id; 130 131 switch (msg = ntohs(ctlh->messageType)) { 132 case PPTP_OUT_CALL_REQUEST: 133 cid_off = offsetof(union pptp_ctrl_union, ocreq.callID); 134 135 /* save original call ID in nat_info */ 136 nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; 137 138 /* don't use tcph->source since we are at a DSTmanip 139 * hook (e.g. PREROUTING) and pkt is not mangled yet */ 140 new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; 141 142 /* save new call ID in ct info */ 143 ct_pptp_info->pns_call_id = new_callid; 144 break; 145 case PPTP_IN_CALL_REPLY: 146 cid_off = offsetof(union pptp_ctrl_union, icack.callID); 147 break; 148 case PPTP_CALL_CLEAR_REQUEST: 149 cid_off = offsetof(union pptp_ctrl_union, clrreq.callID); 150 break; 151 default: 152 pr_debug("unknown outbound packet 0x%04x:%s\n", msg, 153 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : 154 pptp_msg_name[0]); 155 /* fall through */ 156 case PPTP_SET_LINK_INFO: 157 /* only need to NAT in case PAC is behind NAT box */ 158 case PPTP_START_SESSION_REQUEST: 159 case PPTP_START_SESSION_REPLY: 160 case PPTP_STOP_SESSION_REQUEST: 161 case PPTP_STOP_SESSION_REPLY: 162 case PPTP_ECHO_REQUEST: 163 case PPTP_ECHO_REPLY: 164 /* no need to alter packet */ 165 return NF_ACCEPT; 166 } 167 168 /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass 169 * down to here */ 170 pr_debug("altering call id from 0x%04x to 0x%04x\n", 171 ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); 172 173 /* mangle packet */ 174 if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 175 cid_off + sizeof(struct pptp_pkt_hdr) + 176 sizeof(struct PptpControlHeader), 177 sizeof(new_callid), (char *)&new_callid, 178 sizeof(new_callid)) == 0) 179 return NF_DROP; 180 return NF_ACCEPT; 181} 182 183static void 184pptp_exp_gre(struct nf_conntrack_expect *expect_orig, 185 struct nf_conntrack_expect *expect_reply) 186{ 187 const struct nf_conn *ct = expect_orig->master; 188 struct nf_ct_pptp_master *ct_pptp_info; 189 struct nf_nat_pptp *nat_pptp_info; 190 191 ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; 192 nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 193 194 /* save original PAC call ID in nat_info */ 195 nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; 196 197 /* alter expectation for PNS->PAC direction */ 198 expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id; 199 expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id; 200 expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id; 201 expect_orig->dir = IP_CT_DIR_ORIGINAL; 202 203 /* alter expectation for PAC->PNS direction */ 204 expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id; 205 expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id; 206 expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id; 207 expect_reply->dir = IP_CT_DIR_REPLY; 208} 209 210/* inbound packets == from PAC to PNS */ 211static int 212pptp_inbound_pkt(struct sk_buff *skb, 213 struct nf_conn *ct, 214 enum ip_conntrack_info ctinfo, 215 struct PptpControlHeader *ctlh, 216 union pptp_ctrl_union *pptpReq) 217{ 218 const struct nf_nat_pptp *nat_pptp_info; 219 u_int16_t msg; 220 __be16 new_pcid; 221 unsigned int pcid_off; 222 223 nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 224 new_pcid = nat_pptp_info->pns_call_id; 225 226 switch (msg = ntohs(ctlh->messageType)) { 227 case PPTP_OUT_CALL_REPLY: 228 pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID); 229 break; 230 case PPTP_IN_CALL_CONNECT: 231 pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID); 232 break; 233 case PPTP_IN_CALL_REQUEST: 234 /* only need to nat in case PAC is behind NAT box */ 235 return NF_ACCEPT; 236 case PPTP_WAN_ERROR_NOTIFY: 237 pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID); 238 break; 239 case PPTP_CALL_DISCONNECT_NOTIFY: 240 pcid_off = offsetof(union pptp_ctrl_union, disc.callID); 241 break; 242 case PPTP_SET_LINK_INFO: 243 pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); 244 break; 245 default: 246 pr_debug("unknown inbound packet %s\n", 247 msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : 248 pptp_msg_name[0]); 249 /* fall through */ 250 case PPTP_START_SESSION_REQUEST: 251 case PPTP_START_SESSION_REPLY: 252 case PPTP_STOP_SESSION_REQUEST: 253 case PPTP_STOP_SESSION_REPLY: 254 case PPTP_ECHO_REQUEST: 255 case PPTP_ECHO_REPLY: 256 /* no need to alter packet */ 257 return NF_ACCEPT; 258 } 259 260 /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, 261 * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ 262 263 /* mangle packet */ 264 pr_debug("altering peer call id from 0x%04x to 0x%04x\n", 265 ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); 266 267 if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 268 pcid_off + sizeof(struct pptp_pkt_hdr) + 269 sizeof(struct PptpControlHeader), 270 sizeof(new_pcid), (char *)&new_pcid, 271 sizeof(new_pcid)) == 0) 272 return NF_DROP; 273 return NF_ACCEPT; 274} 275 276static int __init nf_nat_helper_pptp_init(void) 277{ 278 nf_nat_need_gre(); 279 280 BUG_ON(nf_nat_pptp_hook_outbound != NULL); 281 rcu_assign_pointer(nf_nat_pptp_hook_outbound, pptp_outbound_pkt); 282 283 BUG_ON(nf_nat_pptp_hook_inbound != NULL); 284 rcu_assign_pointer(nf_nat_pptp_hook_inbound, pptp_inbound_pkt); 285 286 BUG_ON(nf_nat_pptp_hook_exp_gre != NULL); 287 rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, pptp_exp_gre); 288 289 BUG_ON(nf_nat_pptp_hook_expectfn != NULL); 290 rcu_assign_pointer(nf_nat_pptp_hook_expectfn, pptp_nat_expected); 291 return 0; 292} 293 294static void __exit nf_nat_helper_pptp_fini(void) 295{ 296 rcu_assign_pointer(nf_nat_pptp_hook_expectfn, NULL); 297 rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, NULL); 298 rcu_assign_pointer(nf_nat_pptp_hook_inbound, NULL); 299 rcu_assign_pointer(nf_nat_pptp_hook_outbound, NULL); 300 synchronize_rcu(); 301} 302 303module_init(nf_nat_helper_pptp_init); 304module_exit(nf_nat_helper_pptp_fini); 305