Tomato 1.28
[tomato.git] / release / src / router / pptp-client / pptp_gre.c
blobed14882fda66fe16b4d239164493db24a53293a0
1 /* pptp_gre.c -- encapsulate PPP in PPTP-GRE.
2 * Handle the IP Protocol 47 portion of PPTP.
3 * C. Scott Ananian <cananian@alumni.princeton.edu>
5 * $Id: pptp_gre.c,v 1.1.1.1 2002/07/25 06:52:39 honor Exp $
6 */
8 #include <sys/types.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
11 #include <sys/socket.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include "ppp_fcs.h"
19 #include "pptp_msg.h"
20 #include "util.h"
22 #define PACKET_MAX 8196
23 /* test for a 32 bit counter overflow */
24 #define WRAPPED( curseq, lastseq) \
25 ((((curseq) & 0xffffff00)==0) && (((lastseq) & 0xffffff00 )==0xffffff00))
27 static u_int32_t ack_sent, ack_recv;
28 static u_int32_t seq_sent, seq_recv;
29 static u_int16_t pptp_gre_call_id, pptp_gre_peer_call_id;
31 /* decaps gets all the packets possible with ONE blocking read */
32 /* returns <0 if read() call fails */
33 int decaps_hdlc(int fd, int (*cb)(int cl, void *pack, unsigned int len), int cl);
34 int encaps_hdlc(int fd, void *pack, unsigned int len);
35 int decaps_gre (int fd, int (*cb)(int cl, void *pack, unsigned int len), int cl);
36 int encaps_gre (int fd, void *pack, unsigned int len);
38 #if 1
39 #include <stdio.h>
40 void print_packet(int fd, void *pack, unsigned int len) {
41 unsigned char *b = (unsigned char *)pack;
42 unsigned int i,j;
43 FILE *out = fdopen(fd, "w");
45 fprintf(out,"-- begin packet (%u) --\n", len);
46 for (i=0; i<len; i+=16) {
47 for (j=0; j<8; j++)
48 if (i+2*j+1<len)
49 fprintf(out, "%02x%02x ",
50 (unsigned int) b[i+2*j], (unsigned int) b[i+2*j+1]);
51 else if (i+2*j<len)
52 fprintf(out, "%02x ", (unsigned int) b[i+2*j]);
53 fprintf(out, "\n");
55 fprintf(out, "-- end packet --\n");
56 fflush(out);
58 #endif
60 void pptp_gre_copy(u_int16_t call_id, u_int16_t peer_call_id,
61 int pty_fd, struct in_addr inetaddr) {
62 struct sockaddr_in src_addr;
63 int s, n;
65 pptp_gre_call_id = call_id;
66 pptp_gre_peer_call_id = peer_call_id;
68 /* Open IP protocol socket */
69 s = socket(AF_INET, SOCK_RAW, PPTP_PROTO);
70 if (s<0) { warn("socket: %s", strerror(errno)); return; }
71 src_addr.sin_family = AF_INET;
72 src_addr.sin_addr = inetaddr;
73 src_addr.sin_port = 0;
74 if (connect(s, (struct sockaddr *) &src_addr, sizeof(src_addr))<0) {
75 warn("connect: %s", strerror(errno)); return;
77 /* Pseudo-terminal already open. */
79 ack_sent = ack_recv = seq_sent = seq_recv = 0;
81 n = (s>pty_fd)?(s+1):(pty_fd+1); /* weird select semantics */
83 /* Dispatch loop */
84 for (;;) { /* until error happens on s or pty_fd */
85 struct timeval tv = {0, 0}; /* non-blocking select */
86 fd_set rfds;
87 int retval;
89 /* watch terminal and socket for input */
90 FD_ZERO(&rfds);
91 FD_SET(s, &rfds);
92 FD_SET(pty_fd,&rfds);
94 /* if there is a pending ACK, do non-blocking select,
95 otherwise, block until data is available */
96 retval = select(n, &rfds, NULL, NULL, (ack_sent != seq_recv) ? &tv : NULL);
98 if (retval == 0 && ack_sent != seq_recv) /* if outstanding ack */
99 encaps_gre(s, NULL, 0); /* send ack with no payload */
101 if ((FD_ISSET(pty_fd, &rfds) && (decaps_hdlc(pty_fd, encaps_gre, s) < 0))
102 || (FD_ISSET(s, &rfds) && (decaps_gre(s, encaps_hdlc, pty_fd) < 0)))
103 break;
106 /* Close up when done. */
107 close(s); close(pty_fd);
110 #define HDLC_FLAG 0x7E
111 #define HDLC_ESCAPE 0x7D
112 #define HDLC_TRANSPARENCY 0x20
114 /* ONE blocking read per call; dispatches all packets possible */
115 /* returns 0 on success, or <0 on read failure */
116 int decaps_hdlc(int fd, int (*cb)(int cl, void *pack, unsigned int len), int cl) {
117 unsigned char buffer[PACKET_MAX];
118 unsigned int start = 0;
119 int end;
120 int status;
122 static unsigned int len = 0, escape = 0;
123 static unsigned char copy[PACKET_MAX];
125 /* start is start of packet. end is end of buffer data */
126 /* this is the only blocking read we will allow */
128 if ((end = read (fd, buffer, sizeof(buffer))) <= 0) {
129 log ("short read (%u): %s", end, strerror(errno));
130 return -1;
133 while (start < end) {
135 /* Copy to 'copy' and un-escape as we go. */
137 while (buffer[start] != HDLC_FLAG) {
138 if ((escape == 0) && buffer[start] == HDLC_ESCAPE) {
139 escape = HDLC_TRANSPARENCY;
140 } else {
141 if (len < PACKET_MAX)
142 copy [len++] = buffer[start] ^ escape;
143 escape = 0;
145 start++;
147 if (start >= end)
148 return 0; /* No more data, but the frame is not complete yet. */
151 /* found flag. skip past it */
152 start++;
154 /* check for over-short packets and silently discard, as per RFC1662 */
155 if ((len < 4) || (escape != 0)) {
156 len = 0; escape = 0;
157 continue;
159 /* check, then remove the 16-bit FCS checksum field */
160 if (pppfcs16 (PPPINITFCS16, copy, len) != PPPGOODFCS16)
161 log("Bad Frame Check Sequence during PPP to GRE decapsulation");
162 len -= sizeof(u_int16_t);
164 /* so now we have a packet of length 'len' in 'copy' */
165 if ((status = cb (cl, copy, len)) < 0)
166 return status; /* error-check */
168 /* Great! Let's do more! */
169 len = 0; escape = 0;
172 return 0;
173 /* No more data to process. */
176 /* Make stripped packet into HDLC packet */
177 int encaps_hdlc(int fd, void *pack, unsigned int len) {
178 unsigned char *source = (unsigned char *)pack;
179 unsigned char dest[2*PACKET_MAX+2]; /* largest expansion possible */
180 unsigned int pos=0, i;
181 u_int16_t fcs;
183 /* Compute the FCS */
184 fcs = pppfcs16(PPPINITFCS16, source, len) ^ 0xFFFF;
186 /* start character */
187 dest[pos++]=HDLC_FLAG;
188 /* escape the payload */
189 for (i=0; i<len+2; i++) {
190 /* wacked out assignment to add FCS to end of source buffer */
191 unsigned char c = (i<len)?source[i]:(i==len)?(fcs&0xFF):((fcs>>8)&0xFF);
192 if (pos>=sizeof(dest)) break; /* truncate on overflow */
193 if ( (c<0x20) || (c==HDLC_FLAG) || (c==HDLC_ESCAPE) ) {
194 dest[pos++]=HDLC_ESCAPE;
195 if (pos<sizeof(dest))
196 dest[pos++]=c^0x20;
197 } else
198 dest[pos++]=c;
200 /* tack on the end-flag */
201 if (pos<sizeof(dest))
202 dest[pos++]=HDLC_FLAG;
204 /* now write this packet */
205 return write(fd, dest, pos);
208 int decaps_gre (int fd, int (*cb)(int cl, void *pack, unsigned int len), int cl) {
209 unsigned char buffer[PACKET_MAX+64/*ip header*/];
210 struct pptp_gre_header *header;
211 int status, ip_len=0;
212 static int first=1;
214 if ((status = read (fd, buffer, sizeof(buffer))) <= 0) {
215 log("short read (%u): %s", status, strerror(errno));
216 return -1;
219 /* strip off IP header, if present */
220 if ((buffer[0]&0xF0)==0x40)
221 ip_len = (buffer[0]&0xF)*4;
222 header = (struct pptp_gre_header *)(buffer+ip_len);
224 /* verify packet (else discard) */
225 if (((ntoh8(header->ver)&0x7F)!=PPTP_GRE_VER) || /* version should be 1 */
226 (ntoh16(header->protocol)!=PPTP_GRE_PROTO)|| /* GRE protocol for PPTP */
227 PPTP_GRE_IS_C(ntoh8(header->flags)) || /* flag C should be clear */
228 PPTP_GRE_IS_R(ntoh8(header->flags)) || /* flag R should be clear */
229 (!PPTP_GRE_IS_K(ntoh8(header->flags))) || /* flag K should be set */
230 ((ntoh8(header->flags)&0xF)!=0)) { /* routing and recursion ctrl = 0 */
231 /* if invalid, discard this packet */
232 log("Discarding GRE: %X %X %X %X %X %X",
233 ntoh8(header->ver)&0x7F, ntoh16(header->protocol),
234 PPTP_GRE_IS_C(ntoh8(header->flags)),
235 PPTP_GRE_IS_R(ntoh8(header->flags)),
236 PPTP_GRE_IS_K(ntoh8(header->flags)),
237 ntoh8(header->flags)&0xF);
238 return 0;
240 if (PPTP_GRE_IS_A(ntoh8(header->ver))) { /* acknowledgement present */
241 u_int32_t ack = (PPTP_GRE_IS_S(ntoh8(header->flags)))?
242 header->ack:header->seq; /* ack in different place if S=0 */
243 if (ack > ack_recv) ack_recv = ack;
244 /* also handle sequence number wrap-around */
245 if (WRAPPED(ack,ack_recv)) ack_recv=ack;
247 if (PPTP_GRE_IS_S(ntoh8(header->flags))) { /* payload present */
248 unsigned int headersize = sizeof(*header);
249 unsigned int payload_len= ntoh16(header->payload_len);
250 u_int32_t seq = ntoh32(header->seq);
251 if (!PPTP_GRE_IS_A(ntoh8(header->ver))) headersize-=sizeof(header->ack);
252 /* check for incomplete packet (length smaller than expected) */
253 if (status-headersize<payload_len) return 0;
254 /* check for out-of-order sequence number */
255 /* (handle sequence number wrap-around, and try to do it right) */
256 if ( first || (seq > seq_recv) ||
257 WRAPPED( seq, seq_recv)){
258 seq_recv = seq;
259 first=0;
260 return cb(cl, buffer+ip_len+headersize, payload_len);
261 } else {
262 log("discarding out-of-order seq is %d seqrecv is %d", seq, seq_recv);
263 return 0; /* discard out-of-order packets */
266 return 0; /* ack, but no payload */
268 int encaps_gre (int fd, void *pack, unsigned int len) {
269 union {
270 struct pptp_gre_header header;
271 unsigned char buffer[PACKET_MAX+sizeof(struct pptp_gre_header)];
272 } u;
273 static u_int32_t seq=0;
274 unsigned int header_len;
276 /* package this up in a GRE shell. */
277 u.header.flags = hton8 (PPTP_GRE_FLAG_K);
278 u.header.ver = hton8 (PPTP_GRE_VER);
279 u.header.protocol = hton16(PPTP_GRE_PROTO);
280 u.header.payload_len = hton16(len);
281 u.header.call_id = hton16(pptp_gre_peer_call_id);
283 /* special case ACK with no payload */
284 if (pack==NULL) {
285 if (ack_sent != seq_recv) {
286 u.header.ver |= hton8(PPTP_GRE_FLAG_A);
287 u.header.payload_len = hton16(0);
288 u.header.seq = hton32(seq_recv); /* ack is in odd place because S=0 */
289 ack_sent = seq_recv;
290 return write(fd, &u.header, sizeof(u.header)-sizeof(u.header.seq));
291 } else return 0; /* we don't need to send ACK */
292 } /* explicit brace to avoid ambiguous `else' warning */
293 /* send packet with payload */
294 u.header.flags |= hton8(PPTP_GRE_FLAG_S);
295 u.header.seq = hton32(seq);
296 if (ack_sent != seq_recv) { /* send ack with this message */
297 u.header.ver |= hton8(PPTP_GRE_FLAG_A);
298 u.header.ack = hton32(seq_recv);
299 ack_sent = seq_recv;
300 header_len = sizeof(u.header);
301 } else { /* don't send ack */
302 header_len = sizeof(u.header) - sizeof(u.header.ack);
304 if (header_len+len>=sizeof(u.buffer)) return 0; /* drop this, it's too big */
305 /* copy payload into buffer */
306 memcpy(u.buffer+header_len, pack, len);
307 /* record and increment sequence numbers */
308 seq_sent = seq; seq++;
309 /* write this baby out to the net */
310 /* print_packet(2, u.buffer, header_len+len); */
311 return write(fd, u.buffer, header_len+len);