ip_nat_helper_pptp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * ip_nat_pptp.c - Version 3.0
  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. * Changes:
  20. * 2002-02-10 - Version 1.3
  21. * - Use ip_nat_mangle_tcp_packet() because of cloned skb's
  22. * in local connections (Philip Craig <philipc@snapgear.com>)
  23. * - add checks for magicCookie and pptp version
  24. * - make argument list of pptp_{out,in}bound_packet() shorter
  25. * - move to C99 style initializers
  26. * - print version number at module loadtime
  27. * 2003-09-22 - Version 1.5
  28. * - use SNATed tcp sourceport as callid, since we get called before
  29. * TCP header is mangled (Philip Craig <philipc@snapgear.com>)
  30. * 2004-10-22 - Version 2.0
  31. * - kernel 2.6.x version
  32. * 2005-06-10 - Version 3.0
  33. * - kernel >= 2.6.11 version,
  34. * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
  35. *
  36. */
  37. #include <linux/module.h>
  38. #include <linux/ip.h>
  39. #include <linux/tcp.h>
  40. #include <net/tcp.h>
  41. #include <linux/netfilter_ipv4/ip_nat.h>
  42. #include <linux/netfilter_ipv4/ip_nat_rule.h>
  43. #include <linux/netfilter_ipv4/ip_nat_helper.h>
  44. #include <linux/netfilter_ipv4/ip_nat_pptp.h>
  45. #include <linux/netfilter_ipv4/ip_conntrack_core.h>
  46. #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
  47. #include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
  48. #include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
  49. #define IP_NAT_PPTP_VERSION "3.0"
  50. #define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off)))
  51. MODULE_LICENSE("GPL");
  52. MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
  53. MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
  54. #if 0
  55. extern const char *pptp_msg_name[];
  56. #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
  57. __FUNCTION__, ## args)
  58. #else
  59. #define DEBUGP(format, args...)
  60. #endif
  61. static void pptp_nat_expected(struct ip_conntrack *ct,
  62. struct ip_conntrack_expect *exp)
  63. {
  64. struct ip_conntrack *master = ct->master;
  65. struct ip_conntrack_expect *other_exp;
  66. struct ip_conntrack_tuple t;
  67. struct ip_ct_pptp_master *ct_pptp_info;
  68. struct ip_nat_pptp *nat_pptp_info;
  69. struct ip_nat_range range;
  70. ct_pptp_info = &master->help.ct_pptp_info;
  71. nat_pptp_info = &master->nat.help.nat_pptp_info;
  72. /* And here goes the grand finale of corrosion... */
  73. if (exp->dir == IP_CT_DIR_ORIGINAL) {
  74. DEBUGP("we are PNS->PAC\n");
  75. /* therefore, build tuple for PAC->PNS */
  76. t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
  77. t.src.u.gre.key = master->help.ct_pptp_info.pac_call_id;
  78. t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
  79. t.dst.u.gre.key = master->help.ct_pptp_info.pns_call_id;
  80. t.dst.protonum = IPPROTO_GRE;
  81. } else {
  82. DEBUGP("we are PAC->PNS\n");
  83. /* build tuple for PNS->PAC */
  84. t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
  85. t.src.u.gre.key = master->nat.help.nat_pptp_info.pns_call_id;
  86. t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
  87. t.dst.u.gre.key = master->nat.help.nat_pptp_info.pac_call_id;
  88. t.dst.protonum = IPPROTO_GRE;
  89. }
  90. DEBUGP("trying to unexpect other dir: ");
  91. DUMP_TUPLE(&t);
  92. other_exp = ip_conntrack_expect_find_get(&t);
  93. if (other_exp) {
  94. ip_conntrack_unexpect_related(other_exp);
  95. ip_conntrack_expect_put(other_exp);
  96. DEBUGP("success\n");
  97. } else {
  98. DEBUGP("not found!\n");
  99. }
  100. /* This must be a fresh one. */
  101. BUG_ON(ct->status & IPS_NAT_DONE_MASK);
  102. /* Change src to where master sends to */
  103. range.flags = IP_NAT_RANGE_MAP_IPS;
  104. range.min_ip = range.max_ip
  105. = ct->master->tuplehash[!exp->dir].tuple.dst.ip;
  106. if (exp->dir == IP_CT_DIR_ORIGINAL) {
  107. range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
  108. range.min = range.max = exp->saved_proto;
  109. }
  110. /* hook doesn't matter, but it has to do source manip */
  111. ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
  112. /* For DST manip, map port here to where it's expected. */
  113. range.flags = IP_NAT_RANGE_MAP_IPS;
  114. range.min_ip = range.max_ip
  115. = ct->master->tuplehash[!exp->dir].tuple.src.ip;
  116. if (exp->dir == IP_CT_DIR_REPLY) {
  117. range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
  118. range.min = range.max = exp->saved_proto;
  119. }
  120. /* hook doesn't matter, but it has to do destination manip */
  121. ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
  122. }
  123. /* outbound packets == from PNS to PAC */
  124. static int
  125. pptp_outbound_pkt(struct sk_buff **pskb,
  126. struct ip_conntrack *ct,
  127. enum ip_conntrack_info ctinfo,
  128. struct PptpControlHeader *ctlh,
  129. union pptp_ctrl_union *pptpReq)
  130. {
  131. struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
  132. struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
  133. u_int16_t msg;
  134. __be16 new_callid;
  135. unsigned int cid_off;
  136. new_callid = ct_pptp_info->pns_call_id;
  137. switch (msg = ntohs(ctlh->messageType)) {
  138. case PPTP_OUT_CALL_REQUEST:
  139. cid_off = offsetof(union pptp_ctrl_union, ocreq.callID);
  140. /* FIXME: ideally we would want to reserve a call ID
  141. * here. current netfilter NAT core is not able to do
  142. * this :( For now we use TCP source port. This breaks
  143. * multiple calls within one control session */
  144. /* save original call ID in nat_info */
  145. nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
  146. /* don't use tcph->source since we are at a DSTmanip
  147. * hook (e.g. PREROUTING) and pkt is not mangled yet */
  148. new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
  149. /* save new call ID in ct info */
  150. ct_pptp_info->pns_call_id = new_callid;
  151. break;
  152. case PPTP_IN_CALL_REPLY:
  153. cid_off = offsetof(union pptp_ctrl_union, icack.callID);
  154. break;
  155. case PPTP_CALL_CLEAR_REQUEST:
  156. cid_off = offsetof(union pptp_ctrl_union, clrreq.callID);
  157. break;
  158. default:
  159. DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
  160. (msg <= PPTP_MSG_MAX)?
  161. pptp_msg_name[msg]:pptp_msg_name[0]);
  162. /* fall through */
  163. case PPTP_SET_LINK_INFO:
  164. /* only need to NAT in case PAC is behind NAT box */
  165. case PPTP_START_SESSION_REQUEST:
  166. case PPTP_START_SESSION_REPLY:
  167. case PPTP_STOP_SESSION_REQUEST:
  168. case PPTP_STOP_SESSION_REPLY:
  169. case PPTP_ECHO_REQUEST:
  170. case PPTP_ECHO_REPLY:
  171. /* no need to alter packet */
  172. return NF_ACCEPT;
  173. }
  174. /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass
  175. * down to here */
  176. DEBUGP("altering call id from 0x%04x to 0x%04x\n",
  177. ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid));
  178. /* mangle packet */
  179. if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
  180. cid_off + sizeof(struct pptp_pkt_hdr) +
  181. sizeof(struct PptpControlHeader),
  182. sizeof(new_callid), (char *)&new_callid,
  183. sizeof(new_callid)) == 0)
  184. return NF_DROP;
  185. return NF_ACCEPT;
  186. }
  187. static void
  188. pptp_exp_gre(struct ip_conntrack_expect *expect_orig,
  189. struct ip_conntrack_expect *expect_reply)
  190. {
  191. struct ip_conntrack *ct = expect_orig->master;
  192. struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
  193. struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
  194. /* save original PAC call ID in nat_info */
  195. nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
  196. /* alter expectation for PNS->PAC direction */
  197. expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id;
  198. expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id;
  199. expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id;
  200. expect_orig->dir = IP_CT_DIR_ORIGINAL;
  201. /* alter expectation for PAC->PNS direction */
  202. expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id;
  203. expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id;
  204. expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id;
  205. expect_reply->dir = IP_CT_DIR_REPLY;
  206. }
  207. /* inbound packets == from PAC to PNS */
  208. static int
  209. pptp_inbound_pkt(struct sk_buff **pskb,
  210. struct ip_conntrack *ct,
  211. enum ip_conntrack_info ctinfo,
  212. struct PptpControlHeader *ctlh,
  213. union pptp_ctrl_union *pptpReq)
  214. {
  215. struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
  216. u_int16_t msg;
  217. __be16 new_pcid;
  218. unsigned int pcid_off;
  219. new_pcid = nat_pptp_info->pns_call_id;
  220. switch (msg = ntohs(ctlh->messageType)) {
  221. case PPTP_OUT_CALL_REPLY:
  222. pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID);
  223. break;
  224. case PPTP_IN_CALL_CONNECT:
  225. pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID);
  226. break;
  227. case PPTP_IN_CALL_REQUEST:
  228. /* only need to nat in case PAC is behind NAT box */
  229. return NF_ACCEPT;
  230. case PPTP_WAN_ERROR_NOTIFY:
  231. pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID);
  232. break;
  233. case PPTP_CALL_DISCONNECT_NOTIFY:
  234. pcid_off = offsetof(union pptp_ctrl_union, disc.callID);
  235. break;
  236. case PPTP_SET_LINK_INFO:
  237. pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
  238. break;
  239. default:
  240. DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)?
  241. pptp_msg_name[msg]:pptp_msg_name[0]);
  242. /* fall through */
  243. case PPTP_START_SESSION_REQUEST:
  244. case PPTP_START_SESSION_REPLY:
  245. case PPTP_STOP_SESSION_REQUEST:
  246. case PPTP_STOP_SESSION_REPLY:
  247. case PPTP_ECHO_REQUEST:
  248. case PPTP_ECHO_REPLY:
  249. /* no need to alter packet */
  250. return NF_ACCEPT;
  251. }
  252. /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST,
  253. * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */
  254. /* mangle packet */
  255. DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
  256. ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid));
  257. if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
  258. pcid_off + sizeof(struct pptp_pkt_hdr) +
  259. sizeof(struct PptpControlHeader),
  260. sizeof(new_pcid), (char *)&new_pcid,
  261. sizeof(new_pcid)) == 0)
  262. return NF_DROP;
  263. return NF_ACCEPT;
  264. }
  265. extern int __init ip_nat_proto_gre_init(void);
  266. extern void __exit ip_nat_proto_gre_fini(void);
  267. static int __init ip_nat_helper_pptp_init(void)
  268. {
  269. int ret;
  270. DEBUGP("%s: registering NAT helper\n", __FILE__);
  271. ret = ip_nat_proto_gre_init();
  272. if (ret < 0)
  273. return ret;
  274. BUG_ON(rcu_dereference(ip_nat_pptp_hook_outbound));
  275. rcu_assign_pointer(ip_nat_pptp_hook_outbound, pptp_outbound_pkt);
  276. BUG_ON(rcu_dereference(ip_nat_pptp_hook_inbound));
  277. rcu_assign_pointer(ip_nat_pptp_hook_inbound, pptp_inbound_pkt);
  278. BUG_ON(rcu_dereference(ip_nat_pptp_hook_exp_gre));
  279. rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, pptp_exp_gre);
  280. BUG_ON(rcu_dereference(ip_nat_pptp_hook_expectfn));
  281. rcu_assign_pointer(ip_nat_pptp_hook_expectfn, pptp_nat_expected);
  282. printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
  283. return 0;
  284. }
  285. static void __exit ip_nat_helper_pptp_fini(void)
  286. {
  287. DEBUGP("cleanup_module\n" );
  288. rcu_assign_pointer(ip_nat_pptp_hook_expectfn, NULL);
  289. rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, NULL);
  290. rcu_assign_pointer(ip_nat_pptp_hook_inbound, NULL);
  291. rcu_assign_pointer(ip_nat_pptp_hook_outbound, NULL);
  292. synchronize_rcu();
  293. ip_nat_proto_gre_fini();
  294. printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
  295. }
  296. module_init(ip_nat_helper_pptp_init);
  297. module_exit(ip_nat_helper_pptp_fini);