| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Point-to-Point Tunneling Protocol for Linux | 
|  | 3 | * | 
|  | 4 | *	Authors: Dmitry Kozlov <xeb@mail.ru> | 
|  | 5 | * | 
|  | 6 | *	This program is free software; you can redistribute it and/or | 
|  | 7 | *	modify it under the terms of the GNU General Public License | 
|  | 8 | *	as published by the Free Software Foundation; either version | 
|  | 9 | *	2 of the License, or (at your option) any later version. | 
|  | 10 | * | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <linux/string.h> | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/kernel.h> | 
|  | 16 | #include <linux/slab.h> | 
|  | 17 | #include <linux/errno.h> | 
|  | 18 | #include <linux/netdevice.h> | 
|  | 19 | #include <linux/net.h> | 
|  | 20 | #include <linux/skbuff.h> | 
|  | 21 | #include <linux/vmalloc.h> | 
|  | 22 | #include <linux/init.h> | 
|  | 23 | #include <linux/ppp_channel.h> | 
|  | 24 | #include <linux/ppp_defs.h> | 
|  | 25 | #include <linux/if_pppox.h> | 
|  | 26 | #include <linux/if_ppp.h> | 
|  | 27 | #include <linux/notifier.h> | 
|  | 28 | #include <linux/file.h> | 
|  | 29 | #include <linux/in.h> | 
|  | 30 | #include <linux/ip.h> | 
|  | 31 | #include <linux/netfilter.h> | 
|  | 32 | #include <linux/netfilter_ipv4.h> | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 33 | #include <linux/rcupdate.h> | 
|  | 34 | #include <linux/spinlock.h> | 
|  | 35 |  | 
|  | 36 | #include <net/sock.h> | 
|  | 37 | #include <net/protocol.h> | 
|  | 38 | #include <net/ip.h> | 
|  | 39 | #include <net/icmp.h> | 
|  | 40 | #include <net/route.h> | 
|  | 41 | #include <net/gre.h> | 
|  | 42 |  | 
|  | 43 | #include <linux/uaccess.h> | 
|  | 44 |  | 
|  | 45 | #define PPTP_DRIVER_VERSION "0.8.5" | 
|  | 46 |  | 
|  | 47 | #define MAX_CALLID 65535 | 
|  | 48 |  | 
|  | 49 | static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1); | 
|  | 50 | static struct pppox_sock **callid_sock; | 
|  | 51 |  | 
|  | 52 | static DEFINE_SPINLOCK(chan_lock); | 
|  | 53 |  | 
|  | 54 | static struct proto pptp_sk_proto __read_mostly; | 
| Eric Dumazet | 756e64a | 2010-09-21 06:43:54 +0000 | [diff] [blame] | 55 | static const struct ppp_channel_ops pptp_chan_ops; | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 56 | static const struct proto_ops pptp_ops; | 
|  | 57 |  | 
|  | 58 | #define PPP_LCP_ECHOREQ 0x09 | 
|  | 59 | #define PPP_LCP_ECHOREP 0x0A | 
|  | 60 | #define SC_RCV_BITS	(SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) | 
|  | 61 |  | 
|  | 62 | #define MISSING_WINDOW 20 | 
|  | 63 | #define WRAPPED(curseq, lastseq)\ | 
|  | 64 | ((((curseq) & 0xffffff00) == 0) &&\ | 
|  | 65 | (((lastseq) & 0xffffff00) == 0xffffff00)) | 
|  | 66 |  | 
|  | 67 | #define PPTP_GRE_PROTO  0x880B | 
|  | 68 | #define PPTP_GRE_VER    0x1 | 
|  | 69 |  | 
|  | 70 | #define PPTP_GRE_FLAG_C	0x80 | 
|  | 71 | #define PPTP_GRE_FLAG_R	0x40 | 
|  | 72 | #define PPTP_GRE_FLAG_K	0x20 | 
|  | 73 | #define PPTP_GRE_FLAG_S	0x10 | 
|  | 74 | #define PPTP_GRE_FLAG_A	0x80 | 
|  | 75 |  | 
|  | 76 | #define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) | 
|  | 77 | #define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) | 
|  | 78 | #define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) | 
|  | 79 | #define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) | 
|  | 80 | #define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) | 
|  | 81 |  | 
|  | 82 | #define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header)) | 
|  | 83 | struct pptp_gre_header { | 
|  | 84 | u8  flags; | 
|  | 85 | u8  ver; | 
|  | 86 | u16 protocol; | 
|  | 87 | u16 payload_len; | 
|  | 88 | u16 call_id; | 
|  | 89 | u32 seq; | 
|  | 90 | u32 ack; | 
|  | 91 | } __packed; | 
|  | 92 |  | 
|  | 93 | static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) | 
|  | 94 | { | 
|  | 95 | struct pppox_sock *sock; | 
|  | 96 | struct pptp_opt *opt; | 
|  | 97 |  | 
|  | 98 | rcu_read_lock(); | 
|  | 99 | sock = rcu_dereference(callid_sock[call_id]); | 
|  | 100 | if (sock) { | 
|  | 101 | opt = &sock->proto.pptp; | 
|  | 102 | if (opt->dst_addr.sin_addr.s_addr != s_addr) | 
|  | 103 | sock = NULL; | 
|  | 104 | else | 
|  | 105 | sock_hold(sk_pppox(sock)); | 
|  | 106 | } | 
|  | 107 | rcu_read_unlock(); | 
|  | 108 |  | 
|  | 109 | return sock; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | static int lookup_chan_dst(u16 call_id, __be32 d_addr) | 
|  | 113 | { | 
|  | 114 | struct pppox_sock *sock; | 
|  | 115 | struct pptp_opt *opt; | 
|  | 116 | int i; | 
|  | 117 |  | 
|  | 118 | rcu_read_lock(); | 
|  | 119 | for (i = find_next_bit(callid_bitmap, MAX_CALLID, 1); i < MAX_CALLID; | 
|  | 120 | i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) { | 
|  | 121 | sock = rcu_dereference(callid_sock[i]); | 
|  | 122 | if (!sock) | 
|  | 123 | continue; | 
|  | 124 | opt = &sock->proto.pptp; | 
|  | 125 | if (opt->dst_addr.call_id == call_id && | 
|  | 126 | opt->dst_addr.sin_addr.s_addr == d_addr) | 
|  | 127 | break; | 
|  | 128 | } | 
|  | 129 | rcu_read_unlock(); | 
|  | 130 |  | 
|  | 131 | return i < MAX_CALLID; | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | static int add_chan(struct pppox_sock *sock) | 
|  | 135 | { | 
|  | 136 | static int call_id; | 
|  | 137 |  | 
|  | 138 | spin_lock(&chan_lock); | 
|  | 139 | if (!sock->proto.pptp.src_addr.call_id)	{ | 
|  | 140 | call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1); | 
|  | 141 | if (call_id == MAX_CALLID) { | 
|  | 142 | call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1); | 
|  | 143 | if (call_id == MAX_CALLID) | 
|  | 144 | goto out_err; | 
|  | 145 | } | 
|  | 146 | sock->proto.pptp.src_addr.call_id = call_id; | 
|  | 147 | } else if (test_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap)) | 
|  | 148 | goto out_err; | 
|  | 149 |  | 
|  | 150 | set_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap); | 
|  | 151 | rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], sock); | 
|  | 152 | spin_unlock(&chan_lock); | 
|  | 153 |  | 
|  | 154 | return 0; | 
|  | 155 |  | 
|  | 156 | out_err: | 
|  | 157 | spin_unlock(&chan_lock); | 
|  | 158 | return -1; | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | static void del_chan(struct pppox_sock *sock) | 
|  | 162 | { | 
|  | 163 | spin_lock(&chan_lock); | 
|  | 164 | clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap); | 
|  | 165 | rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], NULL); | 
|  | 166 | spin_unlock(&chan_lock); | 
|  | 167 | synchronize_rcu(); | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | 
|  | 171 | { | 
|  | 172 | struct sock *sk = (struct sock *) chan->private; | 
|  | 173 | struct pppox_sock *po = pppox_sk(sk); | 
|  | 174 | struct pptp_opt *opt = &po->proto.pptp; | 
|  | 175 | struct pptp_gre_header *hdr; | 
|  | 176 | unsigned int header_len = sizeof(*hdr); | 
| David S. Miller | 31e4543 | 2011-05-03 20:25:42 -0700 | [diff] [blame] | 177 | struct flowi4 fl4; | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 178 | int islcp; | 
|  | 179 | int len; | 
|  | 180 | unsigned char *data; | 
|  | 181 | __u32 seq_recv; | 
|  | 182 |  | 
|  | 183 |  | 
|  | 184 | struct rtable *rt; | 
|  | 185 | struct net_device *tdev; | 
|  | 186 | struct iphdr  *iph; | 
|  | 187 | int    max_headroom; | 
|  | 188 |  | 
|  | 189 | if (sk_pppox(po)->sk_state & PPPOX_DEAD) | 
|  | 190 | goto tx_error; | 
|  | 191 |  | 
| David S. Miller | 31e4543 | 2011-05-03 20:25:42 -0700 | [diff] [blame] | 192 | rt = ip_route_output_ports(&init_net, &fl4, NULL, | 
| David S. Miller | 78fbfd8 | 2011-03-12 00:00:52 -0500 | [diff] [blame] | 193 | opt->dst_addr.sin_addr.s_addr, | 
|  | 194 | opt->src_addr.sin_addr.s_addr, | 
|  | 195 | 0, 0, IPPROTO_GRE, | 
|  | 196 | RT_TOS(0), 0); | 
|  | 197 | if (IS_ERR(rt)) | 
|  | 198 | goto tx_error; | 
|  | 199 |  | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 200 | tdev = rt->dst.dev; | 
|  | 201 |  | 
|  | 202 | max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2; | 
|  | 203 |  | 
|  | 204 | if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) { | 
|  | 205 | struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); | 
|  | 206 | if (!new_skb) { | 
|  | 207 | ip_rt_put(rt); | 
|  | 208 | goto tx_error; | 
|  | 209 | } | 
|  | 210 | if (skb->sk) | 
|  | 211 | skb_set_owner_w(new_skb, skb->sk); | 
|  | 212 | kfree_skb(skb); | 
|  | 213 | skb = new_skb; | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | data = skb->data; | 
|  | 217 | islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7; | 
|  | 218 |  | 
|  | 219 | /* compress protocol field */ | 
|  | 220 | if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp) | 
|  | 221 | skb_pull(skb, 1); | 
|  | 222 |  | 
|  | 223 | /* Put in the address/control bytes if necessary */ | 
|  | 224 | if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) { | 
|  | 225 | data = skb_push(skb, 2); | 
|  | 226 | data[0] = PPP_ALLSTATIONS; | 
|  | 227 | data[1] = PPP_UI; | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | len = skb->len; | 
|  | 231 |  | 
|  | 232 | seq_recv = opt->seq_recv; | 
|  | 233 |  | 
|  | 234 | if (opt->ack_sent == seq_recv) | 
|  | 235 | header_len -= sizeof(hdr->ack); | 
|  | 236 |  | 
|  | 237 | /* Push down and install GRE header */ | 
|  | 238 | skb_push(skb, header_len); | 
|  | 239 | hdr = (struct pptp_gre_header *)(skb->data); | 
|  | 240 |  | 
|  | 241 | hdr->flags       = PPTP_GRE_FLAG_K; | 
|  | 242 | hdr->ver         = PPTP_GRE_VER; | 
|  | 243 | hdr->protocol    = htons(PPTP_GRE_PROTO); | 
|  | 244 | hdr->call_id     = htons(opt->dst_addr.call_id); | 
|  | 245 |  | 
|  | 246 | hdr->flags      |= PPTP_GRE_FLAG_S; | 
|  | 247 | hdr->seq         = htonl(++opt->seq_sent); | 
|  | 248 | if (opt->ack_sent != seq_recv)	{ | 
|  | 249 | /* send ack with this message */ | 
|  | 250 | hdr->ver |= PPTP_GRE_FLAG_A; | 
|  | 251 | hdr->ack  = htonl(seq_recv); | 
|  | 252 | opt->ack_sent = seq_recv; | 
|  | 253 | } | 
|  | 254 | hdr->payload_len = htons(len); | 
|  | 255 |  | 
|  | 256 | /*	Push down and install the IP header. */ | 
|  | 257 |  | 
|  | 258 | skb_reset_transport_header(skb); | 
|  | 259 | skb_push(skb, sizeof(*iph)); | 
|  | 260 | skb_reset_network_header(skb); | 
|  | 261 | memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); | 
|  | 262 | IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); | 
|  | 263 |  | 
|  | 264 | iph =	ip_hdr(skb); | 
|  | 265 | iph->version =	4; | 
|  | 266 | iph->ihl =	sizeof(struct iphdr) >> 2; | 
|  | 267 | if (ip_dont_fragment(sk, &rt->dst)) | 
|  | 268 | iph->frag_off	=	htons(IP_DF); | 
|  | 269 | else | 
|  | 270 | iph->frag_off	=	0; | 
|  | 271 | iph->protocol = IPPROTO_GRE; | 
|  | 272 | iph->tos      = 0; | 
| David S. Miller | 28c90da | 2011-05-03 20:41:42 -0700 | [diff] [blame] | 273 | iph->daddr    = fl4.daddr; | 
|  | 274 | iph->saddr    = fl4.saddr; | 
| David S. Miller | 323e126 | 2010-12-12 21:55:08 -0800 | [diff] [blame] | 275 | iph->ttl      = ip4_dst_hoplimit(&rt->dst); | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 276 | iph->tot_len  = htons(skb->len); | 
|  | 277 |  | 
|  | 278 | skb_dst_drop(skb); | 
|  | 279 | skb_dst_set(skb, &rt->dst); | 
|  | 280 |  | 
|  | 281 | nf_reset(skb); | 
|  | 282 |  | 
|  | 283 | skb->ip_summed = CHECKSUM_NONE; | 
|  | 284 | ip_select_ident(iph, &rt->dst, NULL); | 
|  | 285 | ip_send_check(iph); | 
|  | 286 |  | 
|  | 287 | ip_local_out(skb); | 
| Eric Dumazet | 8bae8bd | 2011-10-17 17:01:47 +0000 | [diff] [blame^] | 288 | return 1; | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 289 |  | 
|  | 290 | tx_error: | 
| Eric Dumazet | 8bae8bd | 2011-10-17 17:01:47 +0000 | [diff] [blame^] | 291 | kfree_skb(skb); | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 292 | return 1; | 
|  | 293 | } | 
|  | 294 |  | 
|  | 295 | static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb) | 
|  | 296 | { | 
|  | 297 | struct pppox_sock *po = pppox_sk(sk); | 
|  | 298 | struct pptp_opt *opt = &po->proto.pptp; | 
|  | 299 | int headersize, payload_len, seq; | 
|  | 300 | __u8 *payload; | 
|  | 301 | struct pptp_gre_header *header; | 
|  | 302 |  | 
|  | 303 | if (!(sk->sk_state & PPPOX_CONNECTED)) { | 
|  | 304 | if (sock_queue_rcv_skb(sk, skb)) | 
|  | 305 | goto drop; | 
|  | 306 | return NET_RX_SUCCESS; | 
|  | 307 | } | 
|  | 308 |  | 
|  | 309 | header = (struct pptp_gre_header *)(skb->data); | 
|  | 310 |  | 
|  | 311 | /* test if acknowledgement present */ | 
|  | 312 | if (PPTP_GRE_IS_A(header->ver)) { | 
|  | 313 | __u32 ack = (PPTP_GRE_IS_S(header->flags)) ? | 
|  | 314 | header->ack : header->seq; /* ack in different place if S = 0 */ | 
|  | 315 |  | 
|  | 316 | ack = ntohl(ack); | 
|  | 317 |  | 
|  | 318 | if (ack > opt->ack_recv) | 
|  | 319 | opt->ack_recv = ack; | 
|  | 320 | /* also handle sequence number wrap-around  */ | 
|  | 321 | if (WRAPPED(ack, opt->ack_recv)) | 
|  | 322 | opt->ack_recv = ack; | 
|  | 323 | } | 
|  | 324 |  | 
|  | 325 | /* test if payload present */ | 
|  | 326 | if (!PPTP_GRE_IS_S(header->flags)) | 
|  | 327 | goto drop; | 
|  | 328 |  | 
|  | 329 | headersize  = sizeof(*header); | 
|  | 330 | payload_len = ntohs(header->payload_len); | 
|  | 331 | seq         = ntohl(header->seq); | 
|  | 332 |  | 
|  | 333 | /* no ack present? */ | 
|  | 334 | if (!PPTP_GRE_IS_A(header->ver)) | 
|  | 335 | headersize -= sizeof(header->ack); | 
|  | 336 | /* check for incomplete packet (length smaller than expected) */ | 
|  | 337 | if (skb->len - headersize < payload_len) | 
|  | 338 | goto drop; | 
|  | 339 |  | 
|  | 340 | payload = skb->data + headersize; | 
|  | 341 | /* check for expected sequence number */ | 
|  | 342 | if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { | 
|  | 343 | if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && | 
|  | 344 | (PPP_PROTOCOL(payload) == PPP_LCP) && | 
|  | 345 | ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP))) | 
|  | 346 | goto allow_packet; | 
|  | 347 | } else { | 
|  | 348 | opt->seq_recv = seq; | 
|  | 349 | allow_packet: | 
|  | 350 | skb_pull(skb, headersize); | 
|  | 351 |  | 
|  | 352 | if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) { | 
|  | 353 | /* chop off address/control */ | 
|  | 354 | if (skb->len < 3) | 
|  | 355 | goto drop; | 
|  | 356 | skb_pull(skb, 2); | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | if ((*skb->data) & 1) { | 
|  | 360 | /* protocol is compressed */ | 
|  | 361 | skb_push(skb, 1)[0] = 0; | 
|  | 362 | } | 
|  | 363 |  | 
|  | 364 | skb->ip_summed = CHECKSUM_NONE; | 
|  | 365 | skb_set_network_header(skb, skb->head-skb->data); | 
|  | 366 | ppp_input(&po->chan, skb); | 
|  | 367 |  | 
|  | 368 | return NET_RX_SUCCESS; | 
|  | 369 | } | 
|  | 370 | drop: | 
|  | 371 | kfree_skb(skb); | 
|  | 372 | return NET_RX_DROP; | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | static int pptp_rcv(struct sk_buff *skb) | 
|  | 376 | { | 
|  | 377 | struct pppox_sock *po; | 
|  | 378 | struct pptp_gre_header *header; | 
|  | 379 | struct iphdr *iph; | 
|  | 380 |  | 
|  | 381 | if (skb->pkt_type != PACKET_HOST) | 
|  | 382 | goto drop; | 
|  | 383 |  | 
|  | 384 | if (!pskb_may_pull(skb, 12)) | 
|  | 385 | goto drop; | 
|  | 386 |  | 
|  | 387 | iph = ip_hdr(skb); | 
|  | 388 |  | 
|  | 389 | header = (struct pptp_gre_header *)skb->data; | 
|  | 390 |  | 
|  | 391 | if (ntohs(header->protocol) != PPTP_GRE_PROTO || /* PPTP-GRE protocol for PPTP */ | 
|  | 392 | PPTP_GRE_IS_C(header->flags) ||                /* flag C should be clear */ | 
|  | 393 | PPTP_GRE_IS_R(header->flags) ||                /* flag R should be clear */ | 
|  | 394 | !PPTP_GRE_IS_K(header->flags) ||               /* flag K should be set */ | 
|  | 395 | (header->flags&0xF) != 0)                      /* routing and recursion ctrl = 0 */ | 
|  | 396 | /* if invalid, discard this packet */ | 
|  | 397 | goto drop; | 
|  | 398 |  | 
|  | 399 | po = lookup_chan(htons(header->call_id), iph->saddr); | 
|  | 400 | if (po) { | 
|  | 401 | skb_dst_drop(skb); | 
|  | 402 | nf_reset(skb); | 
|  | 403 | return sk_receive_skb(sk_pppox(po), skb, 0); | 
|  | 404 | } | 
|  | 405 | drop: | 
|  | 406 | kfree_skb(skb); | 
|  | 407 | return NET_RX_DROP; | 
|  | 408 | } | 
|  | 409 |  | 
|  | 410 | static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr, | 
|  | 411 | int sockaddr_len) | 
|  | 412 | { | 
|  | 413 | struct sock *sk = sock->sk; | 
|  | 414 | struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; | 
|  | 415 | struct pppox_sock *po = pppox_sk(sk); | 
|  | 416 | struct pptp_opt *opt = &po->proto.pptp; | 
|  | 417 | int error = 0; | 
|  | 418 |  | 
|  | 419 | lock_sock(sk); | 
|  | 420 |  | 
|  | 421 | opt->src_addr = sp->sa_addr.pptp; | 
|  | 422 | if (add_chan(po)) { | 
|  | 423 | release_sock(sk); | 
|  | 424 | error = -EBUSY; | 
|  | 425 | } | 
|  | 426 |  | 
|  | 427 | release_sock(sk); | 
|  | 428 | return error; | 
|  | 429 | } | 
|  | 430 |  | 
|  | 431 | static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, | 
|  | 432 | int sockaddr_len, int flags) | 
|  | 433 | { | 
|  | 434 | struct sock *sk = sock->sk; | 
|  | 435 | struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; | 
|  | 436 | struct pppox_sock *po = pppox_sk(sk); | 
|  | 437 | struct pptp_opt *opt = &po->proto.pptp; | 
|  | 438 | struct rtable *rt; | 
| David S. Miller | 31e4543 | 2011-05-03 20:25:42 -0700 | [diff] [blame] | 439 | struct flowi4 fl4; | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 440 | int error = 0; | 
|  | 441 |  | 
|  | 442 | if (sp->sa_protocol != PX_PROTO_PPTP) | 
|  | 443 | return -EINVAL; | 
|  | 444 |  | 
|  | 445 | if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr)) | 
|  | 446 | return -EALREADY; | 
|  | 447 |  | 
|  | 448 | lock_sock(sk); | 
|  | 449 | /* Check for already bound sockets */ | 
|  | 450 | if (sk->sk_state & PPPOX_CONNECTED) { | 
|  | 451 | error = -EBUSY; | 
|  | 452 | goto end; | 
|  | 453 | } | 
|  | 454 |  | 
|  | 455 | /* Check for already disconnected sockets, on attempts to disconnect */ | 
|  | 456 | if (sk->sk_state & PPPOX_DEAD) { | 
|  | 457 | error = -EALREADY; | 
|  | 458 | goto end; | 
|  | 459 | } | 
|  | 460 |  | 
|  | 461 | if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) { | 
|  | 462 | error = -EINVAL; | 
|  | 463 | goto end; | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | po->chan.private = sk; | 
|  | 467 | po->chan.ops = &pptp_chan_ops; | 
|  | 468 |  | 
| David S. Miller | 31e4543 | 2011-05-03 20:25:42 -0700 | [diff] [blame] | 469 | rt = ip_route_output_ports(&init_net, &fl4, sk, | 
| David S. Miller | 78fbfd8 | 2011-03-12 00:00:52 -0500 | [diff] [blame] | 470 | opt->dst_addr.sin_addr.s_addr, | 
|  | 471 | opt->src_addr.sin_addr.s_addr, | 
|  | 472 | 0, 0, | 
|  | 473 | IPPROTO_GRE, RT_CONN_FLAGS(sk), 0); | 
|  | 474 | if (IS_ERR(rt)) { | 
|  | 475 | error = -EHOSTUNREACH; | 
|  | 476 | goto end; | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 477 | } | 
| David S. Miller | 78fbfd8 | 2011-03-12 00:00:52 -0500 | [diff] [blame] | 478 | sk_setup_caps(sk, &rt->dst); | 
|  | 479 |  | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 480 | po->chan.mtu = dst_mtu(&rt->dst); | 
|  | 481 | if (!po->chan.mtu) | 
|  | 482 | po->chan.mtu = PPP_MTU; | 
|  | 483 | ip_rt_put(rt); | 
|  | 484 | po->chan.mtu -= PPTP_HEADER_OVERHEAD; | 
|  | 485 |  | 
|  | 486 | po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header); | 
|  | 487 | error = ppp_register_channel(&po->chan); | 
|  | 488 | if (error) { | 
|  | 489 | pr_err("PPTP: failed to register PPP channel (%d)\n", error); | 
|  | 490 | goto end; | 
|  | 491 | } | 
|  | 492 |  | 
|  | 493 | opt->dst_addr = sp->sa_addr.pptp; | 
|  | 494 | sk->sk_state = PPPOX_CONNECTED; | 
|  | 495 |  | 
|  | 496 | end: | 
|  | 497 | release_sock(sk); | 
|  | 498 | return error; | 
|  | 499 | } | 
|  | 500 |  | 
|  | 501 | static int pptp_getname(struct socket *sock, struct sockaddr *uaddr, | 
|  | 502 | int *usockaddr_len, int peer) | 
|  | 503 | { | 
|  | 504 | int len = sizeof(struct sockaddr_pppox); | 
|  | 505 | struct sockaddr_pppox sp; | 
|  | 506 |  | 
|  | 507 | sp.sa_family	  = AF_PPPOX; | 
|  | 508 | sp.sa_protocol  = PX_PROTO_PPTP; | 
|  | 509 | sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr; | 
|  | 510 |  | 
|  | 511 | memcpy(uaddr, &sp, len); | 
|  | 512 |  | 
|  | 513 | *usockaddr_len = len; | 
|  | 514 |  | 
|  | 515 | return 0; | 
|  | 516 | } | 
|  | 517 |  | 
|  | 518 | static int pptp_release(struct socket *sock) | 
|  | 519 | { | 
|  | 520 | struct sock *sk = sock->sk; | 
|  | 521 | struct pppox_sock *po; | 
|  | 522 | struct pptp_opt *opt; | 
|  | 523 | int error = 0; | 
|  | 524 |  | 
|  | 525 | if (!sk) | 
|  | 526 | return 0; | 
|  | 527 |  | 
|  | 528 | lock_sock(sk); | 
|  | 529 |  | 
|  | 530 | if (sock_flag(sk, SOCK_DEAD)) { | 
|  | 531 | release_sock(sk); | 
|  | 532 | return -EBADF; | 
|  | 533 | } | 
|  | 534 |  | 
|  | 535 | po = pppox_sk(sk); | 
|  | 536 | opt = &po->proto.pptp; | 
|  | 537 | del_chan(po); | 
|  | 538 |  | 
|  | 539 | pppox_unbind_sock(sk); | 
|  | 540 | sk->sk_state = PPPOX_DEAD; | 
|  | 541 |  | 
|  | 542 | sock_orphan(sk); | 
|  | 543 | sock->sk = NULL; | 
|  | 544 |  | 
|  | 545 | release_sock(sk); | 
|  | 546 | sock_put(sk); | 
|  | 547 |  | 
|  | 548 | return error; | 
|  | 549 | } | 
|  | 550 |  | 
|  | 551 | static void pptp_sock_destruct(struct sock *sk) | 
|  | 552 | { | 
|  | 553 | if (!(sk->sk_state & PPPOX_DEAD)) { | 
|  | 554 | del_chan(pppox_sk(sk)); | 
|  | 555 | pppox_unbind_sock(sk); | 
|  | 556 | } | 
|  | 557 | skb_queue_purge(&sk->sk_receive_queue); | 
|  | 558 | } | 
|  | 559 |  | 
|  | 560 | static int pptp_create(struct net *net, struct socket *sock) | 
|  | 561 | { | 
|  | 562 | int error = -ENOMEM; | 
|  | 563 | struct sock *sk; | 
|  | 564 | struct pppox_sock *po; | 
|  | 565 | struct pptp_opt *opt; | 
|  | 566 |  | 
|  | 567 | sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pptp_sk_proto); | 
|  | 568 | if (!sk) | 
|  | 569 | goto out; | 
|  | 570 |  | 
|  | 571 | sock_init_data(sock, sk); | 
|  | 572 |  | 
|  | 573 | sock->state = SS_UNCONNECTED; | 
|  | 574 | sock->ops   = &pptp_ops; | 
|  | 575 |  | 
|  | 576 | sk->sk_backlog_rcv = pptp_rcv_core; | 
|  | 577 | sk->sk_state       = PPPOX_NONE; | 
|  | 578 | sk->sk_type        = SOCK_STREAM; | 
|  | 579 | sk->sk_family      = PF_PPPOX; | 
|  | 580 | sk->sk_protocol    = PX_PROTO_PPTP; | 
|  | 581 | sk->sk_destruct    = pptp_sock_destruct; | 
|  | 582 |  | 
|  | 583 | po = pppox_sk(sk); | 
|  | 584 | opt = &po->proto.pptp; | 
|  | 585 |  | 
|  | 586 | opt->seq_sent = 0; opt->seq_recv = 0; | 
|  | 587 | opt->ack_recv = 0; opt->ack_sent = 0; | 
|  | 588 |  | 
|  | 589 | error = 0; | 
|  | 590 | out: | 
|  | 591 | return error; | 
|  | 592 | } | 
|  | 593 |  | 
|  | 594 | static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd, | 
|  | 595 | unsigned long arg) | 
|  | 596 | { | 
|  | 597 | struct sock *sk = (struct sock *) chan->private; | 
|  | 598 | struct pppox_sock *po = pppox_sk(sk); | 
|  | 599 | struct pptp_opt *opt = &po->proto.pptp; | 
|  | 600 | void __user *argp = (void __user *)arg; | 
|  | 601 | int __user *p = argp; | 
|  | 602 | int err, val; | 
|  | 603 |  | 
|  | 604 | err = -EFAULT; | 
|  | 605 | switch (cmd) { | 
|  | 606 | case PPPIOCGFLAGS: | 
|  | 607 | val = opt->ppp_flags; | 
|  | 608 | if (put_user(val, p)) | 
|  | 609 | break; | 
|  | 610 | err = 0; | 
|  | 611 | break; | 
|  | 612 | case PPPIOCSFLAGS: | 
|  | 613 | if (get_user(val, p)) | 
|  | 614 | break; | 
|  | 615 | opt->ppp_flags = val & ~SC_RCV_BITS; | 
|  | 616 | err = 0; | 
|  | 617 | break; | 
|  | 618 | default: | 
|  | 619 | err = -ENOTTY; | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | return err; | 
|  | 623 | } | 
|  | 624 |  | 
| Eric Dumazet | 756e64a | 2010-09-21 06:43:54 +0000 | [diff] [blame] | 625 | static const struct ppp_channel_ops pptp_chan_ops = { | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 626 | .start_xmit = pptp_xmit, | 
|  | 627 | .ioctl      = pptp_ppp_ioctl, | 
|  | 628 | }; | 
|  | 629 |  | 
|  | 630 | static struct proto pptp_sk_proto __read_mostly = { | 
|  | 631 | .name     = "PPTP", | 
|  | 632 | .owner    = THIS_MODULE, | 
|  | 633 | .obj_size = sizeof(struct pppox_sock), | 
|  | 634 | }; | 
|  | 635 |  | 
|  | 636 | static const struct proto_ops pptp_ops = { | 
|  | 637 | .family     = AF_PPPOX, | 
|  | 638 | .owner      = THIS_MODULE, | 
|  | 639 | .release    = pptp_release, | 
|  | 640 | .bind       = pptp_bind, | 
|  | 641 | .connect    = pptp_connect, | 
|  | 642 | .socketpair = sock_no_socketpair, | 
|  | 643 | .accept     = sock_no_accept, | 
|  | 644 | .getname    = pptp_getname, | 
|  | 645 | .poll       = sock_no_poll, | 
|  | 646 | .listen     = sock_no_listen, | 
|  | 647 | .shutdown   = sock_no_shutdown, | 
|  | 648 | .setsockopt = sock_no_setsockopt, | 
|  | 649 | .getsockopt = sock_no_getsockopt, | 
|  | 650 | .sendmsg    = sock_no_sendmsg, | 
|  | 651 | .recvmsg    = sock_no_recvmsg, | 
|  | 652 | .mmap       = sock_no_mmap, | 
|  | 653 | .ioctl      = pppox_ioctl, | 
|  | 654 | }; | 
|  | 655 |  | 
| Eric Dumazet | 756e64a | 2010-09-21 06:43:54 +0000 | [diff] [blame] | 656 | static const struct pppox_proto pppox_pptp_proto = { | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 657 | .create = pptp_create, | 
|  | 658 | .owner  = THIS_MODULE, | 
|  | 659 | }; | 
|  | 660 |  | 
| Eric Dumazet | 756e64a | 2010-09-21 06:43:54 +0000 | [diff] [blame] | 661 | static const struct gre_protocol gre_pptp_protocol = { | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 662 | .handler = pptp_rcv, | 
|  | 663 | }; | 
|  | 664 |  | 
|  | 665 | static int __init pptp_init_module(void) | 
|  | 666 | { | 
|  | 667 | int err = 0; | 
|  | 668 | pr_info("PPTP driver version " PPTP_DRIVER_VERSION "\n"); | 
|  | 669 |  | 
| Eric Dumazet | 89bf67f | 2010-11-22 00:15:06 +0000 | [diff] [blame] | 670 | callid_sock = vzalloc((MAX_CALLID + 1) * sizeof(void *)); | 
| Dmitry Kozlov | 00959ad | 2010-08-21 23:05:39 -0700 | [diff] [blame] | 671 | if (!callid_sock) { | 
|  | 672 | pr_err("PPTP: cann't allocate memory\n"); | 
|  | 673 | return -ENOMEM; | 
|  | 674 | } | 
|  | 675 |  | 
|  | 676 | err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP); | 
|  | 677 | if (err) { | 
|  | 678 | pr_err("PPTP: can't add gre protocol\n"); | 
|  | 679 | goto out_mem_free; | 
|  | 680 | } | 
|  | 681 |  | 
|  | 682 | err = proto_register(&pptp_sk_proto, 0); | 
|  | 683 | if (err) { | 
|  | 684 | pr_err("PPTP: can't register sk_proto\n"); | 
|  | 685 | goto out_gre_del_protocol; | 
|  | 686 | } | 
|  | 687 |  | 
|  | 688 | err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto); | 
|  | 689 | if (err) { | 
|  | 690 | pr_err("PPTP: can't register pppox_proto\n"); | 
|  | 691 | goto out_unregister_sk_proto; | 
|  | 692 | } | 
|  | 693 |  | 
|  | 694 | return 0; | 
|  | 695 |  | 
|  | 696 | out_unregister_sk_proto: | 
|  | 697 | proto_unregister(&pptp_sk_proto); | 
|  | 698 | out_gre_del_protocol: | 
|  | 699 | gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP); | 
|  | 700 | out_mem_free: | 
|  | 701 | vfree(callid_sock); | 
|  | 702 |  | 
|  | 703 | return err; | 
|  | 704 | } | 
|  | 705 |  | 
|  | 706 | static void __exit pptp_exit_module(void) | 
|  | 707 | { | 
|  | 708 | unregister_pppox_proto(PX_PROTO_PPTP); | 
|  | 709 | proto_unregister(&pptp_sk_proto); | 
|  | 710 | gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP); | 
|  | 711 | vfree(callid_sock); | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | module_init(pptp_init_module); | 
|  | 715 | module_exit(pptp_exit_module); | 
|  | 716 |  | 
|  | 717 | MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol"); | 
|  | 718 | MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); | 
|  | 719 | MODULE_LICENSE("GPL"); |