diff -uNr linux-2.2.1-clean/include/net/ip_masq_mod.h linux-2.2.1/include/net/ip_masq_mod.h --- linux-2.2.1-clean/include/net/ip_masq_mod.h Sun Oct 4 11:21:45 1998 +++ linux-2.2.1/include/net/ip_masq_mod.h Thu Apr 29 10:04:35 1999 @@ -37,11 +37,22 @@ struct ip_masq *); struct ip_masq * (*mmod_out_create) (const struct sk_buff *, const struct iphdr *, __u32); }; + +struct ip_masq_proto_mod { + struct ip_masq_proto_mod *next; /* next mod for addrs. lookups */ + __u16 protocol; + int (*masquerade) (struct sk_buff **skb_p, __u32 maddr); + int (*demasquerade) (struct sk_buff **skb_p); + atomic_t refcnt; +}; /* * Service routines (called from ip_masq.c) */ +int ip_masq_mod_init(void); +int ip_masquerade_proto(__u16 protocol, struct sk_buff **skb_ptr, __u32 maddr); +int ip_demasquerade_proto(__u16 protocol, struct sk_buff **skb_ptr); int ip_masq_mod_out_rule(const struct sk_buff *, const struct iphdr *); int ip_masq_mod_out_update(const struct sk_buff *, const struct iphdr *, struct ip_masq *ms); struct ip_masq * ip_masq_mod_out_create(const struct sk_buff *, const struct iphdr *iph, __u32 maddr); @@ -55,6 +66,8 @@ /* * ip_masq_mod registration functions */ +extern int register_ip_masq_proto_mod(struct ip_masq_proto_mod *mmod); +extern int unregister_ip_masq_proto_mod(struct ip_masq_proto_mod *mmod); extern int register_ip_masq_mod(struct ip_masq_mod *mmod); extern int unregister_ip_masq_mod(struct ip_masq_mod *mmod); extern int ip_masq_mod_lkp_unlink(struct ip_masq_mod *mmod); diff -uNr linux-2.2.1-clean/net/ipv4/Config.in linux-2.2.1/net/ipv4/Config.in --- linux-2.2.1-clean/net/ipv4/Config.in Sat Jan 9 11:01:56 1999 +++ linux-2.2.1/net/ipv4/Config.in Thu Apr 29 09:53:38 1999 @@ -39,6 +39,7 @@ bool 'IP: transparent proxy support' CONFIG_IP_TRANSPARENT_PROXY bool 'IP: masquerading' CONFIG_IP_MASQUERADE if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then + tristate 'IP: pptp masq support' CONFIG_IP_MASQUERADE_PPTP comment 'Protocol-specific masquerading support will be built as modules.' bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP comment 'Protocol-specific masquerading support will be built as modules.' diff -uNr linux-2.2.1-clean/net/ipv4/Makefile linux-2.2.1/net/ipv4/Makefile --- linux-2.2.1-clean/net/ipv4/Makefile Mon Jan 4 16:31:35 1999 +++ linux-2.2.1/net/ipv4/Makefile Thu Apr 29 09:53:38 1999 @@ -91,6 +91,14 @@ endif +ifeq ($(CONFIG_IP_MASQUERADE_PPTP),y) +IPV4_OBJS += ip_masq_pptp.o +else + ifeq ($(CONFIG_IP_MASQUERADE_PPTP),m) + M_OBJS += ip_masq_pptp.o + endif +endif + M_OBJS += ip_masq_user.o M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o M_OBJS += ip_masq_vdolive.o ip_masq_cuseeme.o diff -uNr linux-2.2.1-clean/net/ipv4/ip_masq.c linux-2.2.1/net/ipv4/ip_masq.c --- linux-2.2.1-clean/net/ipv4/ip_masq.c Sun Jan 17 11:00:00 1999 +++ linux-2.2.1/net/ipv4/ip_masq.c Thu Apr 29 09:53:38 1999 @@ -1060,7 +1060,7 @@ doff = proto_doff(iph->protocol, h.raw, size); - if (doff <= 0) { + if (doff < 0) { /* * Output path: do not pass other IP protos nor * invalid packets. @@ -1113,6 +1113,8 @@ } break; default: + if (ip_masquerade_proto(iph->protocol, skb_p, maddr) == 1) + return 0; return -1; } /* @@ -1825,16 +1827,13 @@ doff = proto_doff(iph->protocol, h.raw, size); - switch (doff) { - case 0: - /* - * Input path: other IP protos Ok, will - * reach local sockets path. - */ - return 0; - case -1: - IP_MASQ_DEBUG(0, "I-pkt invalid packet data size\n"); - return -1; + if (doff == -1) { + /* + * Input path: other IP protos Ok, will + * reach local sockets path. + */ + IP_MASQ_DEBUG(0, "I-pkt invalid packet data size\n"); + return -1; } maddr = iph->daddr; @@ -1888,7 +1887,7 @@ } break; default: - return 0; + return ip_demasquerade_proto(iph->protocol, skb_p); } @@ -2378,6 +2377,7 @@ ip_mfw_init(); #endif ip_masq_app_init(); + ip_masq_mod_init(); return 0; } diff -uNr linux-2.2.1-clean/net/ipv4/ip_masq_mod.c linux-2.2.1/net/ipv4/ip_masq_mod.c --- linux-2.2.1-clean/net/ipv4/ip_masq_mod.c Sun Oct 4 11:21:45 1998 +++ linux-2.2.1/net/ipv4/ip_masq_mod.c Thu Apr 29 09:53:38 1999 @@ -28,6 +28,8 @@ #include #endif +EXPORT_SYMBOL(register_ip_masq_proto_mod); +EXPORT_SYMBOL(unregister_ip_masq_proto_mod); EXPORT_SYMBOL(register_ip_masq_mod); EXPORT_SYMBOL(unregister_ip_masq_mod); EXPORT_SYMBOL(ip_masq_mod_lkp_link); @@ -38,6 +40,11 @@ #endif /* + * Base pointer for registered protocol modules + */ +struct ip_masq_proto_mod * ip_masq_proto_mod_base = NULL; + +/* * Base pointer for registered modules */ struct ip_masq_mod * ip_masq_mod_reg_base = NULL; @@ -321,4 +328,83 @@ return mmod->mmod_ctl(optname, mctl, optlen); #endif return ESRCH; +} + +int register_ip_masq_proto_mod(struct ip_masq_proto_mod *mod) +{ + if (!mod) { + IP_MASQ_ERR("register_ip_masq_mod(): NULL arg\n"); + return -EINVAL; + } + + mod->next = ip_masq_proto_mod_base; + ip_masq_proto_mod_base=mod; + + return 0; +} + +int unregister_ip_masq_proto_mod(struct ip_masq_proto_mod *mod) +{ + struct ip_masq_proto_mod **mod_p; + + if (!mod) { + IP_MASQ_ERR( "unregister_ip_masq_mod(): NULL arg\n"); + return -EINVAL; + } + + /* + * Only allow unregistration if it is not referenced + */ + if (atomic_read(&mod->refcnt)) { + IP_MASQ_ERR( "unregister_ip_masq_mod(): is in use by %d guys. failed\n", + atomic_read(&mod->refcnt)); + return -EINVAL; + } + + for (mod_p = &ip_masq_proto_mod_base; *mod_p ; mod_p = &(*mod_p)->next) { + if (mod == (*mod_p)) { + *mod_p = mod->next; + return 0; + } + } + + return -EINVAL; +} + +int ip_masquerade_proto(__u16 protocol, struct sk_buff **skb_ptr, __u32 maddr) +{ + struct ip_masq_proto_mod *mod; + int ret; + + for (mod = ip_masq_proto_mod_base; mod != NULL; mod = mod->next) { + if (mod->protocol == protocol) { + ret = mod->masquerade(skb_ptr, maddr); + if (ret == 1) + return ret; + } + } + return 0; +} + +int ip_demasquerade_proto(__u16 protocol, struct sk_buff **skb_ptr) +{ + struct ip_masq_proto_mod *mod; + int ret; + + for (mod = ip_masq_proto_mod_base; mod != NULL; mod = mod->next) { + if (mod->protocol == protocol) { + ret = mod->demasquerade(skb_ptr); + if (ret == 1) + return ret; + } + } + return 0; +} + +__initfunc(int ip_masq_mod_init(void)) +{ +#ifdef CONFIG_IP_MASQUERADE_PPTP + return ip_masq_pptp_init(); +#endif + return 0; } diff -uNr linux-2.2.1-clean/net/ipv4/ip_masq_pptp.c linux-2.2.1/net/ipv4/ip_masq_pptp.c --- linux-2.2.1-clean/net/ipv4/ip_masq_pptp.c Wed Dec 31 17:00:00 1969 +++ linux-2.2.1/net/ipv4/ip_masq_pptp.c Mon May 17 13:20:28 1999 @@ -0,0 +1,1090 @@ +/* + * ip_masq_pptp.c + * + * Masquerading for PPTP (Point to Point Tunneling Protocol). + * PPTP is a a protocol for creating virtual private networks. + * It is a specification defined by Microsoft and some vendors + * working with Microsoft. PPTP is built on top of a modified + * version of the Internet Generic Routing Encapsulation Protocol. + * GRE is defined in RFC 1701 and RFC 1702. Documentation of + * PPTP can be found on the Microsoft web site. + * + * Copyright (c) 1997-1998 Gordon Chaffee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_CONFIG_IP_MASQUERADE_GRE 0 + +#define PPTP_GRE_VERSION 0x1 +#define PPTP_GRE_PROTOCOL 0x880B + +#define PPTP_GRE_FLAG_C 0x80 +#define PPTP_GRE_FLAG_R 0x40 +#define PPTP_GRE_FLAG_K 0x20 +#define PPTP_GRE_FLAG_S 0x10 +#define PPTP_GRE_FLAG_A 0x80 + +#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) +#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) +#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) +#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) +#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) + +struct pptp_gre_hdr { + __u8 flags; /* bitfield */ + __u8 version; /* should be PPTP_GRE_VER (enhanced GRE) */ + __u16 protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */ + __u16 payload_len; /* size of ppp payload, not inc. gre header */ + __u16 call_id; /* peer's call_id for this session */ + __u32 seq; /* sequence number. Present if S==1 */ + __u32 ack; /* seq number of highest packet recieved by */ + /* sender in this session */ +}; + +#define PPTP_CONTROL_PACKET 1 +#define PPTP_MGMT_PACKET 2 +#define PPTP_MAGIC_COOKIE 0x1a2b3c4d + +struct pptp_pkt_hdr { + __u16 packetLength; + __u16 packetType; + __u32 magicCookie; +}; + +/* PptpControlMessageType values */ +#define PPTP_START_SESSION_REQUEST 1 +#define PPTP_START_SESSION_REPLY 2 +#define PPTP_STOP_SESSION_REQUEST 3 +#define PPTP_STOP_SESSION_REPLY 4 +#define PPTP_ECHO_REQUEST 5 +#define PPTP_ECHO_REPLY 6 +#define PPTP_OUT_CALL_REQUEST 7 +#define PPTP_OUT_CALL_REPLY 8 +#define PPTP_IN_CALL_REQUEST 9 +#define PPTP_IN_CALL_REPLY 10 +//#define PPTP_CALL_CLEAR_REQUEST 11 +#define PPTP_CALL_CLEAR_REQUEST 12 +//#define PPTP_CALL_DISCONNECT_NOTIFY 12 +#define PPTP_CALL_DISCONNECT_NOTIFY 13 +//#define PPTP_CALL_ERROR_NOTIFY 13 +#define PPTP_WAN_ERROR_NOTIFY 14 +#define PPTP_SET_LINK_INFO 15 + +/* PptpGeneralError values */ +#define PPTP_ERROR_CODE_NONE 0 +#define PPTP_NOT_CONNECTED 1 +#define PPTP_BAD_FORMAT 2 +#define PPTP_BAD_VALUE 3 +#define PPTP_NO_RESOURCE 4 +#define PPTP_BAD_CALLID 5 +#define PPTP_REMOVE_DEVICE_ERROR 6 + +typedef struct { + __u16 messageType; + __u16 reserved; +} PptpControlHeader; + +/* FramingCapability Bitmap Values */ +#define PPTP_FRAME_CAP_ASYNC 0x1 +#define PPTP_FRAME_CAP_SYNC 0x2 + +/* BearerCapability Bitmap Values */ +#define PPTP_BEARER_CAP_ANALOG 0x1 +#define PPTP_BEARER_CAP_DIGITAL 0x2 + +typedef struct { + __u16 protocolVersion; + __u8 reserved1; + __u8 reserved2; + __u32 framingCapability; + __u32 bearerCapability; + __u16 maxChannels; + __u16 firmwareRevision; + __u8 hostName[64]; + __u8 vendorString[64]; +} PptpStartSessionRequest; + +/* PptpStartSessionResultCode Values */ +#define PPTP_START_OK 1 +#define PPTP_START_GENERAL_ERROR 2 +#define PPTP_START_ALREADY_CONNECTED 3 +#define PPTP_START_NOT_AUTHORIZED 4 +#define PPTP_START_UNKNOWN_PROTOCOL 5 + +typedef struct { + __u16 protocolVersion; + __u8 resultCode; + __u8 generalErrorCode; + __u32 framingCapability; + __u32 bearerCapability; + __u16 maxChannels; + __u16 firmwareRevision; + __u8 hostName[64]; + __u8 vendorString[64]; +} PptpStartSessionReply; + +/* PptpStopReasons */ +#define PPTP_STOP_NONE 1 +#define PPTP_STOP_PROTOCOL 2 +#define PPTP_STOP_LOCAL_SHUTDOWN 3 + +typedef struct { + __u8 reason; +} PptpStopSessionRequest; + +/* PptpStopSessionResultCode */ +#define PPTP_STOP_OK 1 +#define PPTP_STOP_GENERAL_ERROR 2 + +typedef struct { + __u8 resultCode; + __u8 generalErrorCode; +} PptpStopSessionReply; + +typedef struct { + __u32 identNumber; +} PptpEchoRequest; + +/* PptpEchoReplyResultCode */ +#define PPTP_ECHO_OK 1 +#define PPTP_ECHO_GENERAL_ERROR 2 + +typedef struct { + __u32 identNumber; + __u8 resultCode; + __u8 generalErrorCode; + __u16 reserved; +} PptpEchoReply; + +/* PptpFramingType */ +#define PPTP_ASYNC_FRAMING 1 +#define PPTP_SYNC_FRAMING 2 +#define PPTP_DONT_CARE_FRAMING 3 + +/* PptpCallBearerType */ +#define PPTP_ANALOG_TYPE 1 +#define PPTP_DIGITAL_TYPE 2 +#define PPTP_DONT_CARE_BEARER_TYPE 3 + +typedef struct { + __u16 callID; + __u16 callSerialNumber; + __u32 minBPS; + __u32 maxBPS; + __u32 bearerType; + __u32 framingType; + __u16 packetWindow; + __u16 packetProcDelay; + __u16 reserved1; + __u16 phoneNumberLength; + __u16 reserved2; + __u8 phoneNumber[64]; + __u8 subAddress[64]; +} PptpOutCallRequest; + +/* PptpCallResultCode */ +#define PPTP_OUTCALL_CONNECT 1 +#define PPTP_OUTCALL_GENERAL_ERROR 2 +#define PPTP_OUTCALL_NO_CARRIER 3 +#define PPTP_OUTCALL_BUSY 4 +#define PPTP_OUTCALL_NO_DIAL_TONE 5 +#define PPTP_OUTCALL_TIMEOUT 6 +#define PPTP_OUTCALL_DONT_ACCEPT 7 + +typedef struct { + __u16 callID; + __u16 peersCallID; + __u8 resultCode; + __u8 generalErrorCode; + __u16 causeCode; + __u32 connectSpeed; + __u16 packetWindow; + __u16 packetProcDelay; + __u32 physChannelID; +} PptpOutCallReply; + +typedef struct { + __u16 callID; + __u16 callSerialNumber; + __u32 callBearerType; + __u32 physChannelID; + __u16 dialedNumberLength; + __u16 dialingNumberLength; + __u8 dialedNumber[64]; + __u8 dialingNumber[64]; + __u8 subAddress[64]; +} PptpInCallRequest; + +/* PptpInCallResultCode */ +#define PPTP_INCALL_ACCEPT 1 +#define PPTP_INCALL_GENERAL_ERROR 2 +#define PPTP_INCALL_DONT_ACCEPT 3 + +typedef struct { + __u16 callID; + __u16 peersCallID; + __u8 resultCode; + __u8 generalErrorCode; + __u16 packetWindow; + __u16 packetProcDelay; + __u16 reserved; +} PptpInCallReply; + +typedef struct { + __u16 callID; + __u16 peersCallID; + __u32 connectSpeed; + __u16 packetWindow; + __u16 packetProcDelay; + __u32 callFramingType; +} PptpInCallConnected; + +typedef struct { + __u16 callID; + __u16 reserved; +} PptpClearCallRequest; + +/* PptpCallDisconnectCode */ +typedef struct { + __u16 callID; + __u8 resultCode; + __u8 generalErrorCode; + __u16 causeCode; + __u16 reserved; + __u8 callStatistics[128]; +} PptpCallDisconnectNotify; + +typedef struct { + __u16 peersCallID; + __u16 reserved; + __u32 crcErrors; + __u32 framingErrors; + __u32 hardwareOverRuns; + __u32 bufferOverRuns; + __u32 timeoutErrors; + __u32 alignmentErrors; +} PptpWanErrorNotify; + +typedef struct { + __u16 peersCallID; + __u16 reserved; + __u32 sendAccm; + __u32 recvAccm; +} PptpSetLinkInfo; + + +struct pptp_priv_data { + __u16 call_id; + __u16 peer_call_id; + __u16 mcall_id; +}; + +static __u16 client_call_id = 42; + +/* + * If we handle the packet, return 1. If we do not, return 0. If there + * is some sort of error, return -1 + */ +static int masq_pptp(struct sk_buff **skb_ptr, __u32 maddr) +{ + struct sk_buff *skb=*skb_ptr; + struct iphdr *iph = skb->nh.iph; + struct ip_masq *ms; + struct pptp_gre_hdr *grehdr; + int len; + int n; + + + len = skb->len - iph->ihl*4; + if (len < 0) return 0; + + grehdr = (struct pptp_gre_hdr *) &(((char *)iph)[iph->ihl*4]); + n = sizeof(struct pptp_gre_hdr); + if (! PPTP_GRE_IS_S(grehdr->flags)) + n -= sizeof(grehdr->seq); + if (! PPTP_GRE_IS_A(grehdr->flags)) + n -= sizeof(grehdr->ack); + len -= n; + if (len < 0) return 0; + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "GRE callid -> %d\n", htons(grehdr->call_id) ); +#if 0 + { + unsigned char *data = (unsigned char *) grehdr; + int i, j, max; + char buf[200]; + + data += n; + for (i = 0; i < len; i += 16) { + max = 16; + if (i + 16 > len) { + max = len - i; + } + for (j = 0; j < max; j++) { + sprintf(&buf[j*3], "%02x ", data[i+j]); + } + buf[j*3] = '\n'; + buf[j*3+1] = 0; + printk( "GRE : %s", buf); + } + } +#endif +#endif + + if ((grehdr->version & 0x7F) != PPTP_GRE_VERSION) return 0; + if (ntohs(grehdr->protocol) != PPTP_GRE_PROTOCOL) return 0; + +#if 0 /* Incorrect. Should be starting with a PptpStartSessionRequest */ + pkt_hdr = (struct pptp_pkt_hdr *) &(((char *) grehdr)[n]); + if (len < sizeof(struct pptp_pkt_hdr)) return 0; + if (ntohl(pkt_hdr->magicCookie) != PPTP_MAGIC_COOKIE) return 0; +#endif + /* + * Now hunt the list to see if we have an old entry + */ + + ms = ip_masq_out_get(iph->protocol, + iph->saddr, grehdr->call_id, /* client->server w/ masq_server_callid */ + iph->daddr, 0); + + /* + * Nope, not found, create a new entry for it + */ + + if (ms==NULL) + { + printk( "GRE masq_pptp() : no ip_masq_ip_get() entry, packet discarded\n" ); + printk( "GRE masq_pptp() : iph->protocol=%d iph->saddr=%lX grehdr->call_id=%d, iph->daddr=%lX\n", + iph->protocol, htonl(iph->saddr), grehdr->call_id, htonl(iph->daddr) ); + + if (ms == NULL) + return 0; + } + +// printk( "GRE masq_pptp() : ms->protocol = %d\n", ms->protocol ); +// printk( "GRE masq_pptp() : ms->sport=%d ms->dport=%d ms->mport=%d\n", +// ms->sport, ms->dport, ms->mport ); +// printk( "GRE masq_pptp() : ms->saddr=%lX ms->daddr=%lX ms->maddr=%lX\n", +// ntohl(ms->saddr), ntohl(ms->daddr), ntohl(ms->maddr) ); + +// printk( "GRE masq_pptp() : ms->sport=%d ms->dport=%d\n", +// ms->sport, ms->dport ); + + /* + * Change the fragments origin + * Set iph addr from ip_masq obj. + */ + + iph->saddr = maddr; + + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + + ip_send_check(iph); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk("O-routed from %lX\n",ntohl(ms->maddr)); +#endif + ms->timeout = 120*HZ; + ip_masq_put(ms); + + return 1; +} + +/* + * If we handle the packet, return 1. If we do not, return 0. If there + * is some sort of error, return -1 + */ +static int demasq_pptp(struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->nh.iph; + struct ip_masq *ms; + struct pptp_gre_hdr *grehdr; + int len; + int n; + + + len = skb->len - iph->ihl*4; + if (len < 0) return 0; + + grehdr = (struct pptp_gre_hdr *) &(((char *)iph)[iph->ihl*4]); + n = sizeof(struct pptp_gre_hdr); + if (! PPTP_GRE_IS_S(grehdr->flags)) + n -= sizeof(grehdr->seq); + if (! PPTP_GRE_IS_A(grehdr->flags)) + n -= sizeof(grehdr->ack); + len -= n; + if (len < 0) return 0; + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "GRE callid -> %d\n", htons(grehdr->call_id) ); +#endif + if ((grehdr->version & 0x7F) != PPTP_GRE_VERSION) return 0; + if (ntohs(grehdr->protocol) != PPTP_GRE_PROTOCOL) return 0; + + /* + * reroute to original host if found... + */ + + ms = ip_masq_in_get(iph->protocol, + iph->saddr, 0, + iph->daddr, grehdr->call_id /* server->client w/ masq-client-callid */ + ); + + if (ms != NULL) + { + grehdr->call_id = ms->dport; + iph->daddr = ms->saddr; +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "GRE callid changed to %d\n", htons(grehdr->call_id) ); +#endif + + /* + * Adjust IP checksums. + */ + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + ip_send_check(iph); +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk("I-routed GRE to %lX\n",ntohl(iph->daddr)); +#endif + ms->timeout = 120*HZ; // davep + ip_masq_put(ms); + + return 1; + } + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "GRE demasq_pptp() : no ip_masq_in_get() entry, packet discarded\n" ); +#endif + /* sorry, all this trouble for a no-hit :) */ + return 0; +} + +static int +masq_pptp_init_1( struct ip_masq_app *mapp, struct ip_masq *ms ) +{ + struct pptp_priv_data *priv; + + printk( "PPTP INIT 1 : \n" ); + + MOD_INC_USE_COUNT; + + if ((ms->app_data = kmalloc(sizeof(struct pptp_priv_data), GFP_ATOMIC)) == NULL) + printk(KERN_INFO "PPTP Masq: No memory for application data\n"); + + priv = ms->app_data; + priv->call_id = 0; + priv->peer_call_id= 0; + priv->mcall_id = 0; + + return 0; +} + +static int +masq_pptp_done_1( struct ip_masq_app *mapp, struct ip_masq *ms ) +{ + printk( "PPTP DONE 1 : \n" ); + + MOD_DEC_USE_COUNT; + + if (ms->app_data) + kfree_s(ms->app_data, sizeof(struct pptp_priv_data)); + + return 0; +} + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE +static void +print_pptp_tcp( struct pptp_pkt_hdr *pptph ) +{ +PptpControlHeader *pptpctl; + +#if 0 + { + unsigned char *data = (unsigned char *) pptph; + int i, j, max; + char buf[200]; + + max = 16; + i=0; + for (j = 0; j < max; j++) { + sprintf(&buf[j*3], "%02x ", data[i+j]); + } + buf[j*3] = '\n'; + buf[j*3+1] = 0; + printk( "PPTP : %s", buf); + } +#endif + + if( ntohs(pptph->packetType) != PPTP_CONTROL_PACKET ) + { + /* XXX dumb message, should not happen */ + printk( "PPTP not a control packet!\n" ); + /* XXX need to log a better error message here but not til get a better + * handle on this stuff + */ + return; + } + + pptpctl = (PptpControlHeader *)((char *)pptph + sizeof(struct pptp_pkt_hdr)); + + switch( htons(pptpctl->messageType) ) + { + case PPTP_START_SESSION_REQUEST : + printk( "PPTP_START_SESSION_REQUEST\n" ); + break; + + case PPTP_START_SESSION_REPLY : + printk( "PPTP_START_SESSION_REPLY\n" ); + break; + + case PPTP_STOP_SESSION_REQUEST : + printk( "PPTP_STOP_SESSION_REQUEST\n" ); + break; + + case PPTP_STOP_SESSION_REPLY : + printk( "PPTP_STOP_SESSION_REPLY\n" ); + break; + + case PPTP_ECHO_REQUEST : + printk( "PPTP_ECHO_REQUEST\n" ); + break; + + case PPTP_ECHO_REPLY : + printk( "PPTP_ECHO_REPLY\n" ); + break; + + case PPTP_OUT_CALL_REQUEST : + printk( "PPTP_OUT_CALL_REQUEST\n" ); + break; + + case PPTP_OUT_CALL_REPLY : + printk( "PPTP_OUT_CALL_REPLY\n" ); + break; + + case PPTP_IN_CALL_REQUEST : + printk( "PPTP_IN_CALL_REQUEST\n" ); + break; + + case PPTP_IN_CALL_REPLY : + printk( "PPTP_IN_CALL_REPLY\n" ); + break; + + case PPTP_CALL_CLEAR_REQUEST : + printk( "PPTP_CALL_CLEAR_REQUEST\n" ); + break; + + case PPTP_CALL_DISCONNECT_NOTIFY : + printk( "PPTP_CALL_DISCONNECT_NOTIFY\n" ); + break; + + case PPTP_WAN_ERROR_NOTIFY : + printk( "PPTP_WAN_ERROR_NOTIFY\n" ); + break; + + case PPTP_SET_LINK_INFO : + printk( "PPTP_SET_LINK_INFO\n" ); + break; + + default: + printk( "PPTP TCP : unknown packetType -> %d\n", htons(pptph->packetType) ); + } +} +#endif + +/* + * outbound packets (from client through masq router to server) + * aka + * inbound packets (from inside private IP subnet through masq router to outside world public Internet) + * + */ + +int +masq_pptp_tcp_out( struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr ) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct tcphdr *th; + char *data, *data_limit; + struct pptp_pkt_hdr *pptph; + struct pptp_priv_data *priv; + PptpControlHeader *pptpctl; + struct ip_masq *n_ms; + + skb = *skb_p; + iph = skb->nh.iph; + th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + data = (char *)&th[1]; + data_limit = skb->h.raw + skb->len; + + priv = ms->app_data; + + data = &(((char *)th)[th->doff*4]); + + if( data == data_limit ) + { + /* no TCP data -- don't bother peeking into nonexistant PPTP header */ + return 0; + } + +#if 0 + if( ms->app_data ) + { + printk( "PPTP TCP OUT : ms->app_data = %p\n", ms->app_data ); + printk( "PPTP TCP OUT : priv->call_id=%d priv->peer_call_id=%d\n", + priv->call_id, priv->peer_call_id); + printk( "PPTP TCP OUT : priv->mcall_id=%d priv->mpeer_call_id=%d\n", + priv->mcall_id, priv->mpeer_call_id); + } +#endif + + pptph = (struct pptp_pkt_hdr *)data; + + if ( htonl(pptph->magicCookie) != PPTP_MAGIC_COOKIE ) + { + printk( "PPTP TCP OUT : bad magic cookie\n" ); + return 0; + } + + pptpctl = (PptpControlHeader *)((char *)pptph + sizeof(struct pptp_pkt_hdr)); + + switch( htons(pptpctl->messageType) ) + { + case PPTP_OUT_CALL_REQUEST : + { + PptpOutCallRequest *req; + + req = (PptpOutCallRequest *)((char *)pptpctl + sizeof(PptpControlHeader)); + + /* save client's real call ID and substitute our fake one */ + priv->call_id = req->callID; + priv->mcall_id = htons(client_call_id++); + + /* packet from the client to the server so we want to substitute + * the client's real callid with our fake callid + */ + req->callID = priv->mcall_id; + } + break; + + case PPTP_OUT_CALL_REPLY : +// printk( "PPTP TCP OUT : OUT_CALL_REPLY\n" ); + break; + + case PPTP_IN_CALL_REQUEST : +// printk( "PPTP TCP OUT : IN_CALL_REQUEST\n" ); + break; + + case PPTP_IN_CALL_REPLY : +// printk( "PPTP TCP OUT : IN_CALL_REPLY\n" ); + break; + + case PPTP_CALL_CLEAR_REQUEST : +// printk( "PPTP TCP OUT : CALL_CLEAR_REQUEST\n" ); + { + PptpClearCallRequest *pptp_ptr; + + pptp_ptr = (PptpClearCallRequest *)((char *)pptpctl + sizeof(PptpControlHeader)); + + pptp_ptr->callID = priv->mcall_id; + } + break; + + case PPTP_CALL_DISCONNECT_NOTIFY : +// printk( "PPTP TCP OUT : CALL_DISCONNECT_NOTIFY\n" ); + break; + +// case PPTP_CALL_ERROR_NOTIFY : +// printk( "PPTP TCP OUT : CALL_ERROR_NOTIFY\n" ); +// break; + + case PPTP_WAN_ERROR_NOTIFY : +// printk( "PPTP TCP OUT : WAN_ERROR_NOTIFY\n" ); + break; + + case PPTP_SET_LINK_INFO : +// printk( "PPTP TCP OUT : SET_LINK_INFO\n" ); + { + PptpSetLinkInfo *pptp_ptr; + + pptp_ptr = (PptpSetLinkInfo *)((char *)pptpctl + sizeof(PptpControlHeader)); + + /* this is a packet going from the client to the server + * the packet contains the peerCallID (the server's call id) + * + * insert the *real* server callID + */ + pptp_ptr->peersCallID = priv->peer_call_id; + } + break; + + case PPTP_ECHO_REPLY : +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "PPTP TCP OUT : PPTP_ECHO_REPLY renewing GRE timer\n" ); +#endif + + n_ms = ip_masq_out_get( 47, + ms->saddr, priv->call_id, + ms->daddr, 0 ); + + if( n_ms == NULL ) + { + printk( "PPTP TCP OUT : PPTP_ECHO_REPLY cannot find GRE masq entry\n" ); + break; + } + + ip_masq_put(n_ms); + break; + + case PPTP_ECHO_REQUEST : + case PPTP_START_SESSION_REQUEST : + case PPTP_START_SESSION_REPLY : + case PPTP_STOP_SESSION_REQUEST : + case PPTP_STOP_SESSION_REPLY : +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + print_pptp_tcp( pptph ); +#endif + break; + + default: + printk( "PPTP TCP OUT : unknown message type %d\n", htons( pptpctl->messageType ) ); + } + + return 0; +} + +/* + * inbound packets (from server through masq router back to client) + * aka + * inbound packets (from outside world public Internet through masq router back to private IP subnet) + * + */ + +int +masq_pptp_tcp_in( struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr ) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct tcphdr *th; + char *data, *data_limit; + struct pptp_pkt_hdr *pptph; + struct pptp_priv_data *priv; + PptpControlHeader *pptpctl; + struct ip_masq *n_ms; + + skb = *skb_p; + iph = skb->nh.iph; + th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + data = (char *)&th[1]; + data_limit = skb->h.raw + skb->len; + + priv = ms->app_data; + + data = &(((char *)th)[th->doff*4]); + + if( data == data_limit ) + { + return 0; + } + +#if 0 + if( ms->app_data ) + { + printk( "PPTP TCP IN : ms->app_data = %p\n", ms->app_data ); + printk( "PPTP TCP IN : priv->call_id=%d priv->peer_call_id=%d\n", + priv->call_id, priv->peer_call_id ); + printk( "PPTP TCP IN : priv->mcall_id=%d priv->mpeers_call_id=%d\n", + priv->mcall_id, priv->mpeer_call_id ); + } +#endif + + pptph = (struct pptp_pkt_hdr *)data; + + if ( htonl(pptph->magicCookie) != PPTP_MAGIC_COOKIE ) + { + printk( "PPTP TCP IN : bad magic cookie\n" ); + return 0; + } + + pptpctl = (PptpControlHeader *)((char *)pptph + sizeof(struct pptp_pkt_hdr)); + + switch( htons(pptpctl->messageType) ) + { + case PPTP_OUT_CALL_REQUEST : + break; + + case PPTP_OUT_CALL_REPLY : + printk( "PPTP TCP IN : OUT_CALL_REPLY \n" ); + { + PptpOutCallReply *rep; + + rep = (PptpOutCallReply *)((char *)pptpctl + sizeof(PptpControlHeader)); + + /* save the server's call id (as the client's peer) */ + /* XXX check if priv != NULL */ + priv->peer_call_id = rep->callID; + + /* packet from the server to the client + * so substitute the fake client callid we sent earlier back to the + * real one (in the peerCallID field) + * + * AAAAAND substitute the real server callid with our fake server callid + */ + rep->peersCallID = priv->call_id; + + n_ms = NULL; + + if( n_ms != NULL ) + { + printk( "PPTP TCP IN : ip_masq_out_get() retrieved %p\n", n_ms ); + printk( "PPTP TCP IN : n_ms->protocol = %d\n", n_ms->protocol ); + printk( "PPTP TCP IN : n_ms->sport=%d n_ms->dport=%d n_ms->mport=%d\n", + htons(n_ms->sport), htons(n_ms->dport), htons(n_ms->mport) ); + printk( "PPTP TCP IN : n_ms->saddr=%lX n_ms->daddr=%lX n_ms->maddr=%lX\n", + ntohl(n_ms->saddr), ntohl(n_ms->daddr), ntohl(n_ms->maddr) ); + + /* XXX if it is found, this is A Bad Thing (tm) + * because it implies another identical { server-ip, masq-ip, client-masq-callid } + */ + } + /* + * Nope, not found, create a new entry for it + */ + + if ( n_ms==NULL) + { + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "PPTP TCP IN : ip_masq_new() maddr=%lX priv->mcall_id=%d\n", + ntohl(maddr), ntohs(priv->mcall_id) ); + printk( "PPTP TCP IN : ip_masq_new() iph->daddr=%lX priv->peer_call_id=%d\n", + ntohl(iph->daddr), ntohs(priv->peer_call_id) ); + printk( "PPTP TCP IN : ip_masq_new() iph->saddr=%lX priv->call_id=%d\n", + ntohl(iph->saddr), ntohs(priv->call_id) ); +#endif + n_ms = ip_masq_new( 47, + maddr, priv->mcall_id, + iph->daddr, priv->peer_call_id, + iph->saddr, priv->call_id, + 0 ); + + if( n_ms == NULL ) + { + printk( "PPTP TCP IN : ip_masq_new() failed\n" ); + return 0; + } + + n_ms->timeout = 120*HZ; /* XXX 2 minutes is a guess */ + n_ms->flags |= (IP_MASQ_F_NO_DPORT|IP_MASQ_F_DLOOSE); + } + + ip_masq_put(n_ms); + } + break; + + case PPTP_IN_CALL_REQUEST : +// printk( "PPTP TCP IN : IN_CALL_REQUEST\n" ); + break; + + case PPTP_IN_CALL_REPLY : +// printk( "PPTP TCP IN : IN_CALL_REPLY\n" ); + break; + + case PPTP_CALL_CLEAR_REQUEST : +// printk( "PPTP TCP IN : CALL_CLEAR_REQUEST\n" ); + break; + + case PPTP_CALL_DISCONNECT_NOTIFY : + printk( "PPTP TCP IN : CALL_DISCONNECT_NOTIFY\n" ); + { + PptpCallDisconnectNotify *pptp_ptr; + + pptp_ptr = (PptpCallDisconnectNotify *)((char *)pptpctl + sizeof(PptpControlHeader)); + + /* packet from the server to the client */ +#ifdef DEBUG_CONFIG_IP_MASQUERADE_GRE + printk( "PPTP TCP IN : ms->sport=%d ms->dport=%d ms->mport=%d\n", + htons(ms->sport), htons(ms->dport), htons(ms->mport) ); + printk( "PPTP TCP IN : ms->saddr=%lX ms->daddr=%lX ms->maddr=%lX\n", + ntohl(ms->saddr), ntohl(ms->daddr), ntohl(ms->maddr) ); + printk( "PPTP TCP IN : iph->daddr=%lX priv->mcall_id=%d iph->saddr=%lX\n", + htonl(iph->daddr), htons(priv->mcall_id), htonl(iph->saddr) ); +#endif + + /* by the time masq_pptp_tcp_in() is called, our addresses + * have already been rearranged so we have to use the TCP masq entry + * to find the GRE masq entry we want. + */ + + n_ms = ip_masq_in_get( 47, + ms->daddr, 0, + ms->maddr, priv->mcall_id ); + + if( n_ms == NULL ) + { + printk( "PPTP TCP IN : CALL_DISCONNECT_NOTIFY cannot find GRE masq entry\n" ); + break; + } + + if( n_ms != NULL ) + { + /* delete the entry to prevent further GRE packets from entering the system + * (the masq entry will be allowed to timeout on its own because I can't + * figure out how to delete an ip_masq entry) + */ + printk( "PPTP TCP IN : Deleting GRE entry on CALL_DISCONNECT_NOTIFY\n" ); + printk( "PPTP TCP IN : n_ms->protocol = %d\n", n_ms->protocol ); + printk( "PPTP TCP IN : n_ms->sport=%d n_ms->dport=%d n_ms->mport=%d\n", + htons(n_ms->sport), htons(n_ms->dport), htons(n_ms->mport) ); + printk( "PPTP TCP IN : n_ms->saddr=%lX n_ms->daddr=%lX n_ms->maddr=%lX\n", + ntohl(n_ms->saddr), ntohl(n_ms->daddr), ntohl(n_ms->maddr) ); + + n_ms->timeout = 10*HZ; + ip_masq_put(n_ms); + } + } + break; + +// case PPTP_CALL_ERROR_NOTIFY : +// printk( "PPTP TCP IN : CALL_ERROR_NOTIFY\n" ); +// break; + + case PPTP_WAN_ERROR_NOTIFY : +// printk( "PPTP TCP IN : WAN_ERROR_NOTIFY\n" ); + break; + + case PPTP_SET_LINK_INFO : + printk( "PPTP TCP IN : SET_LINK_INFO\n" ); + { + PptpSetLinkInfo *pptp_ptr; + + pptp_ptr = (PptpSetLinkInfo *)((char *)pptpctl + sizeof(PptpControlHeader)); + + /* this is a packet going from the server to the client + * the packet contains the peerCallID (the client's call id) + * + * insert the *real* client callID + */ + pptp_ptr->peersCallID = priv->call_id; + } + break; + + case PPTP_ECHO_REPLY : + printk( "PPTP TCP IN : PPTP_ECHO_REPLY renewing GRE timer\n" ); + + n_ms = ip_masq_in_get( 47, + ms->daddr, 0, + ms->maddr, priv->mcall_id ); + + if( n_ms == NULL ) + { + printk( "PPTP TCP IN : PPTP_ECHO_REPLY cannot find GRE masq entry\n" ); + break; + } + + ip_masq_put(n_ms); + break; + + case PPTP_ECHO_REQUEST : + case PPTP_START_SESSION_REQUEST : + case PPTP_START_SESSION_REPLY : + case PPTP_STOP_SESSION_REQUEST : + case PPTP_STOP_SESSION_REPLY : + print_pptp_tcp( pptph ); + break; + + default: + printk( "PPTP TCP IN : unknown message type %d\n", htons( pptpctl->messageType ) ); + } + + return 0; +} + +#define IPPROTO_GRE 47 + +struct ip_masq_proto_mod ip_gre_pptp_mod = + { + NULL, /* next */ + IPPROTO_GRE, /* proto */ + masq_pptp, /* masquerade */ + demasq_pptp, /* de-masquerade */ + ATOMIC_INIT(0) /* refcount */ + }; + +struct ip_masq_app ip_masq_pptp_tcp = + { + NULL, /* next */ + "pptp-tcp", /* name */ + 0, /* type */ + 0, /* n_attach */ + masq_pptp_init_1, /* ip_masq_init_1 */ + masq_pptp_done_1, /* ip_masq_done_1 */ + masq_pptp_tcp_out, /* pkt_out */ + masq_pptp_tcp_in /* pkt_in */ + }; + + +/* + * ip_masq_pptp initialization + */ + +__initfunc(int ip_masq_pptp_init(void)) +{ +int retcode; + + retcode = register_ip_masq_app( &ip_masq_pptp_tcp, IPPROTO_TCP, 1723 ); + if( retcode ) + { + printk( "PPTP TCP register failed : %d", retcode ); + return retcode; + } + + return register_ip_masq_proto_mod(&ip_gre_pptp_mod); +} + +/* + * ip_masq_pptp fin. + */ + +int ip_masq_pptp_done(void) +{ + unregister_ip_masq_app( &ip_masq_pptp_tcp ); + + return unregister_ip_masq_proto_mod(&ip_gre_pptp_mod); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + printk( KERN_DEBUG"loading ip_masq_pptp\n" ); + if (ip_masq_pptp_init() != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (ip_masq_pptp_done() != 0) + printk("ip_masq_pptp: can't remove module"); +} + +#endif /* MODULE */ + +