4 * originally by C. S. Ananian
7 * $Id: pptpgre.c,v 1.9 2007/04/16 00:21:02 quozl Exp $
15 #define _GNU_SOURCE 1 /* broken arpa/inet.h */
18 #include "our_syslog.h"
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
48 #define PACKET_MAX 8196
50 typedef int (*callback_t
)(int cl
, void *pack
, unsigned int len
);
52 /* test for a 32 bit counter overflow */
53 #define WRAPPED( curseq, lastseq) \
54 ((((curseq) & 0xffffff00) == 0) && \
55 (((lastseq) & 0xffffff00 ) == 0xffffff00))
57 static struct gre_state gre
;
60 static uint64_t time_now_usecs()
63 gettimeofday(&tv
, NULL
);
64 return (tv
.tv_sec
* 1000000) + tv
.tv_usec
;
67 int pptp_gre_init(u_int32_t call_id_pair
, int pty_fd
, struct in_addr
*inetaddrs
)
69 struct sockaddr_in addr
;
72 /* Open IP protocol socket */
73 gre_fd
= socket(AF_INET
, SOCK_RAW
, PPTP_PROTO
);
75 syslog(LOG_ERR
, "GRE: socket() failed");
79 memset(&addr
, 0, sizeof(addr
));
81 addr
.sin_family
= AF_INET
;
82 addr
.sin_addr
= inetaddrs
[0];
84 if (bind(gre_fd
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
85 syslog(LOG_ERR
, "GRE: bind() failed: %s", strerror(errno
));
86 syslog(LOG_ERR
, "GRE: continuing, but may not work if multi-homed");
89 addr
.sin_family
= AF_INET
;
90 addr
.sin_addr
= inetaddrs
[1];
92 if (connect(gre_fd
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
93 syslog(LOG_ERR
, "GRE: connect() failed: %s", strerror(errno
));
98 gre
.ack_sent
= gre
.ack_recv
= gre
.seq_recv
= 0xFFFFFFFF;
99 /* seq_recv is -1, therefore next packet expected is seq 0,
100 to comply with RFC 2637: 'The sequence number for each
101 user session is set to zero at session startup.' */
103 gre
.call_id_pair
= call_id_pair
; /* network byte order */
107 /* ONE blocking read per call; dispatches all packets possible */
108 /* returns 0 on success, or <0 on read failure */
109 int decaps_hdlc(int fd
, int (*cb
) (int cl
, void *pack
, unsigned len
), int cl
)
111 static unsigned char buffer
[PACKET_MAX
], copy
[PACKET_MAX
];
112 static unsigned start
= 0, end
= 0;
113 static unsigned len
= 0, escape
= 0;
114 static u_int16_t fcs
= PPPINITFCS16
;
115 static unsigned char err
= 0;
119 /* we do one read only, since it may block. and only if the
120 * buffer is empty (start == end)
128 /* re-xmit and nothing queued */
129 syslog(LOG_ERR
, "GRE: Re-xmit called with nothing queued");
135 /* All known data is processed. This true unless the last
136 * network write failed.
138 if ((status
= read(fd
, buffer
, sizeof(buffer
))) <= 0) {
139 syslog(LOG_ERR
, "GRE: read(fd=%d,buffer=%lx,len=%d) from PTY failed: status = %d error = %s%s",
140 fd
, (unsigned long) buffer
, sizeof(buffer
),
141 status
, status
? strerror(errno
) : "No error",
142 errno
!= EIO
? "" : ", usually caused by unexpected termination of pppd, check option syntax and pppd logs");
143 /* FAQ: mistakes in pppd option spelling in
144 * /etc/ppp/options.pptpd often cause EIO,
145 * with pppd not reporting the problem to any
146 * logs. Termination of pppd by signal can
147 * *also* cause this situation. -- James Cameron
154 /* We're here because of a network write failure. Try again.
155 * Then do what we would do normally and enter the loop as if
156 * just continuing the while(1). Not sure that this ever
157 * really happens, but since we error-check status then we
158 * should have the code to handle an error :-)
161 if ((status
= cb(cl
, copy
, len
)) < 0) {
162 syslog(LOG_ERR
, "GRE: re-xmit failed from decaps_hdlc: %s", strerror(errno
));
164 return status
; /* return error */
166 /* Great! Let's do more! */
173 /* Infinite loop, we return when we're out of data */
175 /* Check if out of data */
179 /* Add to the packet up till the next HDLC_FLAG (start/end of
180 * packet marker). Copy to 'copy', un-escape and checksum as we go.
182 while (buffer
[start
] != HDLC_FLAG
) {
184 /* Dispose of 'too long' packets */
185 if (len
>= PACKET_MAX
) {
186 syslog(LOG_ERR
, "GRE: Received too long packet from pppd.");
187 while (buffer
[start
] != HDLC_FLAG
&& start
< end
)
194 /* Read a character, un-escaping if needed */
195 if (buffer
[start
] == HDLC_ESCAPE
&& !escape
)
199 copy
[len
] = c
= buffer
[start
] ^ 0x20;
202 copy
[len
] = c
= buffer
[start
];
203 fcs
= (fcs
>> 8) ^ fcstab
[(fcs
^ c
) & 0xff];
208 /* Check if out of data */
213 /* Found flag. Skip past it */
216 /* Check for over-short packets and silently discard, as per RFC1662 */
217 if ((len
< 4) || (escape
== 1)) {
218 /* len == 0 is possible, we generate it :-) [using HDLC_ESCAPE at
219 * start and end of packet]. Others are worth recording.
222 syslog(LOG_ERR
, "GRE: Received too short packet from pppd.");
224 syslog(LOG_ERR
, "GRE: Received bad packet from pppd.");
227 /* Check, then remove the 16-bit FCS checksum field */
228 if (fcs
!= PPPGOODFCS16
) {
229 syslog(LOG_ERR
, "GRE: Bad checksum from pppd.");
232 len
-= sizeof(u_int16_t
);
234 /* So now we have a packet of length 'len' in 'copy' */
235 if ((status
= cb(cl
, copy
, len
)) < 0) {
236 syslog(LOG_ERR
, "GRE: xmit failed from decaps_hdlc: %s", strerror(errno
));
238 return status
; /* return error */
241 /* Great! Let's do more! */
248 #define seq_greater(A,B) ((A)>(B) || \
249 (((u_int32_t)(A)<0xff) && ((~((u_int32_t)(B)))<0xff)))
251 /* Macro used in encaps_hdlc(). add "val" to "dest" at position "pos",
252 * incrementing "pos" to point after the added value. set "tmp" to "val"
255 #define ADD_CHAR(dest, pos, val, tmp) \
257 if ((tmp<0x20) || (tmp==HDLC_FLAG) || (tmp==HDLC_ESCAPE)) { \
258 dest[pos++]=HDLC_ESCAPE; \
259 dest[pos++]=tmp^0x20; \
263 /* Make stripped packet into HDLC packet */
264 int encaps_hdlc(int fd
, void *pack
, unsigned len
)
266 unsigned char *source
= (unsigned char *) pack
;
267 /* largest expansion possible - double all + double fcs + 2 flags */
268 static unsigned char dest
[2 * PACKET_MAX
+ 6];
275 /* make sure overflow is impossible so we don't have to bounds check
276 * in loop. drop large packets.
278 if (len
> PACKET_MAX
) {
279 syslog(LOG_ERR
, "GRE: Asked to encapsulate too large packet (len = %d)", len
);
282 /* start character */
285 /* escape the payload */
286 for (i
= 0; i
< len
; i
++) {
287 ADD_CHAR(dest
, pos
, source
[i
], c
);
288 fcs
= (fcs
>> 8) ^ fcstab
[(fcs
^ c
) & 0xff];
293 ADD_CHAR(dest
, pos
, fcs
& 0xFF, c
);
294 ADD_CHAR(dest
, pos
, fcs
>> 8, c
);
296 /* tack on the end-flag */
297 dest
[pos
++] = HDLC_FLAG
;
299 /* now write this packet */
300 return write(fd
, dest
, pos
);
306 static int dequeue_gre (callback_t callback
, int cl
)
310 /* process packets in the queue that either are expected or
312 head
= pqueue_head();
313 while ( head
!= NULL
&&
314 ( (head
->seq
== gre
.seq_recv
+ 1) || /* wrap-around safe */
315 (pqueue_expiry_time(head
) <= 0)
318 /* if it is timed out... */
319 if (head
->seq
!= gre
.seq_recv
+ 1 ) { /* wrap-around safe */
320 stats
.rx_lost
+= head
->seq
- gre
.seq_recv
- 1;
323 "GRE: timeout waiting for %d packets",
324 head
->seq
- gre
.seq_recv
- 1);
327 syslog(LOG_DEBUG
, "GRE: accepting #%d from queue",
329 gre
.seq_recv
= head
->seq
;
330 status
= callback(cl
, head
->packet
, head
->packlen
);
332 if (status
< 0) return status
;
333 head
= pqueue_head();
339 int decaps_gre(int fd
, int (*cb
) (int cl
, void *pack
, unsigned len
), int cl
)
341 static unsigned char buffer
[PACKET_MAX
+ 64 /*ip header */ ];
342 struct pptp_gre_header
*header
;
343 int status
, ip_len
= 0;
346 if ((status
= read(fd
, buffer
, sizeof(buffer
))) <= 0) {
347 syslog(LOG_ERR
, "GRE: read(fd=%d,buffer=%lx,len=%d) from network failed: status = %d error = %s",
348 fd
, (unsigned long) buffer
, sizeof(buffer
), status
, status
? strerror(errno
) : "No error");
352 /* strip off IP header, if present */
353 if ((buffer
[0] & 0xF0) == 0x40)
354 ip_len
= (buffer
[0] & 0xF) * 4;
355 header
= (struct pptp_gre_header
*) (buffer
+ ip_len
);
357 /* verify packet (else discard) */
358 if (((ntoh8(header
->ver
) & 0x7F) != PPTP_GRE_VER
) || /* version should be 1 */
359 (ntoh16(header
->protocol
) != PPTP_GRE_PROTO
) || /* GRE protocol for PPTP */
360 PPTP_GRE_IS_C(ntoh8(header
->flags
)) || /* flag C should be clear */
361 PPTP_GRE_IS_R(ntoh8(header
->flags
)) || /* flag R should be clear */
362 (!PPTP_GRE_IS_K(ntoh8(header
->flags
))) || /* flag K should be set */
363 ((ntoh8(header
->flags
) & 0xF) != 0)) { /* routing and recursion ctrl = 0 */
364 /* if invalid, discard this packet */
365 syslog(LOG_ERR
, "GRE: Discarding packet by header check");
369 if (header
->call_id
!= GET_VALUE(PAC
, gre
.call_id_pair
)) {
371 * Discard silently to allow more than one GRE tunnel from
372 * the same IP address in case clients are behind the
375 * syslog(LOG_ERR, "GRE: Discarding for incorrect call");
379 if (PPTP_GRE_IS_A(ntoh8(header
->ver
))) { /* acknowledgement present */
380 u_int32_t ack
= (PPTP_GRE_IS_S(ntoh8(header
->flags
))) ?
381 ntoh32(header
->ack
) : ntoh32(header
->seq
);
382 /* ack in different place if S=0 */
384 if (seq_greater(ack
, gre
.ack_recv
))
387 /* also handle sequence number wrap-around */
388 if (WRAPPED(ack
,gre
.ack_recv
)) gre
.ack_recv
= ack
;
389 if (gre
.ack_recv
== stats
.pt
.seq
) {
390 int rtt
= time_now_usecs() - stats
.pt
.time
;
391 stats
.rtt
= (stats
.rtt
+ rtt
) / 2;
394 if (PPTP_GRE_IS_S(ntoh8(header
->flags
))) { /* payload present */
395 unsigned headersize
= sizeof(*header
);
396 unsigned payload_len
= ntoh16(header
->payload_len
);
397 u_int32_t seq
= ntoh32(header
->seq
);
399 if (!PPTP_GRE_IS_A(ntoh8(header
->ver
)))
400 headersize
-= sizeof(header
->ack
);
401 /* check for incomplete packet (length smaller than expected) */
402 if (status
- headersize
< payload_len
) {
403 stats
.rx_truncated
++;
406 /* check for out-of-order sequence number */
407 if (seq
== gre
.seq_recv
+ 1) {
409 syslog(LOG_DEBUG
, "GRE: accepting packet #%d",
413 return cb(cl
, buffer
+ ip_len
+ headersize
, payload_len
);
414 } else if (seq
== gre
.seq_recv
) {
417 "GRE: discarding duplicate or old packet #%d (expecting #%d)",
418 seq
, gre
.seq_recv
+ 1);
419 return 0; /* discard duplicate packets */
424 "GRE: buffering packet #%d (expecting #%d, lost or reordered)",
425 seq
, gre
.seq_recv
+ 1);
426 pqueue_add(seq
, buffer
+ ip_len
+ headersize
, payload_len
);
427 return 0; /* discard out-of-order packets */
430 return 0; /* ack, but no payload */
434 int encaps_gre(int fd
, void *pack
, unsigned len
)
437 struct pptp_gre_header header
;
438 unsigned char buffer
[PACKET_MAX
+ sizeof(struct pptp_gre_header
)];
442 struct iovec iovec
[2];
447 return (gre
.ack_sent
== gre
.seq_recv
) ? 0 : -1;
449 /* package this up in a GRE shell. */
450 u
.header
.flags
= hton8(PPTP_GRE_FLAG_K
);
451 u
.header
.ver
= hton8(PPTP_GRE_VER
);
452 u
.header
.protocol
= hton16(PPTP_GRE_PROTO
);
453 u
.header
.payload_len
= hton16(len
);
454 u
.header
.call_id
= GET_VALUE(PNS
, gre
.call_id_pair
);
456 /* special case ACK with no payload */
458 if (gre
.ack_sent
!= gre
.seq_recv
) {
459 u
.header
.ver
|= hton8(PPTP_GRE_FLAG_A
);
460 u
.header
.payload_len
= hton16(0);
461 u
.header
.seq
= hton32(gre
.seq_recv
); /* ack is in odd place because S=0 */
462 gre
.ack_sent
= gre
.seq_recv
;
463 /* don't sent ACK field, ACK is in SYN field */
464 return write(fd
, u
.buffer
, sizeof(u
.header
) - sizeof(u
.header
.ack
));
466 return 0; /* we don't need to send ACK */
468 /* send packet with payload */
469 u
.header
.flags
|= hton8(PPTP_GRE_FLAG_S
);
470 u
.header
.seq
= hton32(gre
.seq_sent
);
472 if (gre
.ack_sent
!= gre
.seq_recv
) { /* send ack with this message */
473 u
.header
.ver
|= hton8(PPTP_GRE_FLAG_A
);
474 u
.header
.ack
= hton32(gre
.seq_recv
);
475 gre
.ack_sent
= gre
.seq_recv
;
476 header_len
= sizeof(u
.header
);
477 } else { /* don't send ack */
478 header_len
= sizeof(u
.header
) - sizeof(u
.header
.ack
);
480 if (len
> PACKET_MAX
) {
481 syslog(LOG_ERR
, "GRE: packet is too large %d", len
);
483 return 0; /* drop this, it's too big */
486 /* write header and buffer without copying. */
487 iovec
[0].iov_base
= u
.buffer
;
488 iovec
[0].iov_len
= header_len
;
489 iovec
[1].iov_base
= pack
;
490 iovec
[1].iov_len
= len
;
491 return writev(fd
, iovec
, 2);
493 /* copy payload into buffer */
494 memcpy(u
.buffer
+ header_len
, pack
, len
);
495 /* record and increment sequence numbers */
496 /* write this baby out to the net */
497 return write(fd
, u
.buffer
, header_len
+ len
);