File: ctrlpacket.c

package info (click to toggle)
pptpd 1.4.0-11
  • links: PTS
  • area: main
  • in suites: buster
  • size: 1,452 kB
  • sloc: ansic: 4,938; sh: 793; makefile: 105; perl: 87
file content (706 lines) | stat: -rw-r--r-- 26,848 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
/*
 * ctrlpacket.c
 *
 * PPTP Control Message packet reading, formatting and writing.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if HAVE_SYSLOG_H
#include <syslog.h>
#else
#include "our_syslog.h"
#endif

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "pptpdefs.h"
#include "pptpctrl.h"
#include "ctrlpacket.h"

#ifndef HAVE_STRERROR
#include "compat.h"
#endif

/* Local function prototypes */
static ssize_t read_pptp_header(int clientFd, unsigned char *packet, int *ctrl_message_type);
static void deal_start_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
static void deal_stop_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
static void deal_out_call(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
static void deal_echo(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
static void deal_call_clr(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size);
static void deal_set_link_info(unsigned char *packet);
static u_int16_t getcall();
static u_int16_t freecall();

#if notyet
static int make_out_call_rqst(unsigned char *rply_packet, ssize_t * rply_size);
#endif

/*
 * read_pptp_packet
 *
 * Sees if a packet can be read and if so what type of packet it is. The
 * method then calls the appropriate function to examine the details of the
 * packet and form a suitable reply packet.
 *
 * args:        clientFd (IN) - Client socket to read from.
 *              packet (OUT) - Packet read from the client.
 *              rply_packet (OUT) - Reply packet for the client.
 *              rply_size (OUT) - Size of the reply packet.
 *
 * retn:        PPTP control message type of the packet on success.
 *              -1 on retryable error.
 *              0 on error to abort on.
 */
int read_pptp_packet(int clientFd, void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{

        ssize_t bytes_read;
        int pptp_ctrl_type = 0; /* Control Message Type */

        /* read a packet and parse header */
        if ((bytes_read = read_pptp_header(clientFd, packet, &pptp_ctrl_type)) <= 0) {
                /* error reading packet */
                syslog(LOG_ERR, "CTRL: couldn't read packet header (%s)", bytes_read ? "retry" : "exit");
                return bytes_read;
        }

        /* launch appropriate method to form suitable reply to the packet */
        switch (pptp_ctrl_type) {
        case START_CTRL_CONN_RQST:      /* Start Control Connection Request */
                deal_start_ctrl_conn(packet, rply_packet, rply_size);
                break;

        case STOP_CTRL_CONN_RQST:
                deal_stop_ctrl_conn(packet, rply_packet, rply_size);
                break;

        case OUT_CALL_RQST:             /* Outgoing Call Request */
                deal_out_call(packet, rply_packet, rply_size);
                break;

        case ECHO_RQST:                 /* Echo Request */
                deal_echo(packet, rply_packet, rply_size);
                break;

        case CALL_CLR_RQST:             /* Call Clear Request (Disconnect Request) */
                deal_call_clr(packet, rply_packet, rply_size);
                break;

        case SET_LINK_INFO:             /* Set Link Info */
                /* no reply packet but process it */
                deal_set_link_info(packet);
                break;

        case ECHO_RPLY:                 /* Echo Reply */
        case STOP_CTRL_CONN_RPLY:       /* Stop Control Connection Reply */
        case CALL_DISCONN_NTFY:         /* Call Disconnect Notify */
                /* no reply packet */
                break;

        default:
                syslog(LOG_ERR, "CTRL: PPTP Control Message type %d not supported.", pptp_ctrl_type);
                pptp_ctrl_type = -1;
        }

        return pptp_ctrl_type;
}


/*
 * send_pptp_packet
 *
 * Sends a PPTP packet to a file descriptor.
 *
 * args:        clientFd (IN) - file descriptor to write the packet to.
 *              packet (IN) - the packet data to write.
 *              packet_size (IN) - the packet size.
 *
 * retn:        Number of bytes written on success.
 *              -1 on write failure.
 */
ssize_t send_pptp_packet(int clientFd, void *packet, size_t packet_size)
{

        ssize_t bytes_written;

        if ((bytes_written = write(clientFd, packet, packet_size)) == -1) {
                /* write failed */
                syslog(LOG_ERR, "CTRL: Couldn't write packet to client.");
                return -1;

        } else {
                /* debugging */
                if (pptpctrl_debug) {
                        syslog(LOG_DEBUG, "CTRL: I wrote %lu bytes to the client.", (unsigned long) packet_size);
                        syslog(LOG_DEBUG, "CTRL: Sent packet to client");
                }
                return bytes_written;
        }
}

/*
 * ignoreErrno
 *
 * Check if an errno represents a read error which should be ignored, and
 * put back to be select()ed on again later.
 *
 * Very similar to the function in Squid by Duane Wessels (under GPL).
 *
 * args: an errno value
 *
 * retn: 1 if the error is unimportant
 *       0 if the error is important
 */
static int ignoreErrno(int ierrno) {
        switch (ierrno) {
        case EAGAIN:            /* nothing to read */
        case EINTR:             /* signal received */
#ifdef ERESTART
#if ERESTART != EINTR
        case ERESTART:          /* signal received, should restart syscall */
#endif
#endif
#if EWOULDBLOCK != EAGAIN
        case EWOULDBLOCK:       /* shouldn't get this one but anyway, just in case */
#endif
                return 1;
        default:
                return 0;
        }
}

/*
 * read_pptp_header
 *
 * Reads a packet from a file descriptor and determines whether it is a
 * valid PPTP Control Message. If a valid PPTP Control Message is detected
 * it extracts the Control Message type from the packet header.
 *
 * args:        clientFd (IN) - Clients file descriptor.
 *              packet (OUT) - Packet we read from the client.
 *              pptp_ctrl_type (OUT) - PPTP Control Message type of the packet.
 *
 * retn:        Number of bytes read on success.
 *              -1 on retryable error.
 *              0 on error to exit on.
 */
ssize_t read_pptp_header(int clientFd, unsigned char *packet, int *pptp_ctrl_type)
{

        ssize_t bytes_ttl, bytes_this;  /* quantities read (total and this read) */
        u_int16_t length;               /* length of this packet */
        struct pptp_header *header;     /* the received header */

        static char *buffer = NULL;     /* buffer between calls */
        static int buffered = 0;        /* size of buffer */

        *pptp_ctrl_type = 0;            /* initialise return arg        */

        /* read any previously buffered data */
        if (buffered) {
                memcpy(packet, buffer, buffered);
                free(buffer);
                buffer = NULL;
                bytes_ttl = buffered;
                buffered = 0;
                if (pptpctrl_debug)
                        syslog(LOG_DEBUG, "CTRL: Read in previous incomplete ctrl packet");
        } else
                bytes_ttl = 0;

        /* try and get the length in */
        if (bytes_ttl < 2) {
                bytes_this = read(clientFd, packet + bytes_ttl, 2 - bytes_ttl);
                switch (bytes_this) {
                case -1:
                        if (ignoreErrno(errno)) {
                                /* re-tryable error, re-buffer and return */
                                if (bytes_ttl) {
                                        buffered = bytes_ttl;
                                        buffer = malloc(bytes_ttl);
                                        if (!buffer)
                                                return(0);
                                        memcpy(buffer, packet, bytes_ttl);
                                }
                                syslog(LOG_ERR, "CTRL: Error reading ctrl packet length (bytes_ttl=%lu): %s", (unsigned long) bytes_ttl, strerror(errno));
                                return -1;
                        }
                        /* FALLTHRU */
                case 0:
                        syslog(LOG_ERR, "CTRL: EOF or bad error reading ctrl packet length.");
                        return 0;
                default:
                        bytes_ttl += bytes_this;
                        /* Not enough data to proceed */
                        if (bytes_ttl == 1) {
                                buffered = bytes_ttl;
                                buffer = malloc(bytes_ttl);
                                if (!buffer)
                                        return(0);
                                memcpy(buffer, packet, bytes_ttl);
                                if (pptpctrl_debug)
                                        syslog(LOG_DEBUG, "CTRL: Incomplete ctrl packet length, retry later");
                                return -1;
                        }
                }
        }
        /* OK, we have (at least) the first 2 bytes, and there is data waiting
         *
         * length includes the header,  so a length less than 2 is someone
         * trying to hack into us or a badly corrupted packet.
         * Why not require length to be at least 10? Since we later cast
         * packet to struct pptp_header and use at least the 10 first bytes..
         * Thanks to Timo Sirainen for mentioning this.
         */
        length = htons(*(u_int16_t *) packet);
        if (length <= 10 || length > PPTP_MAX_CTRL_PCKT_SIZE) {
                syslog(LOG_ERR, "CTRL: 11 < Control packet (length=%d) < "
                                "PPTP_MAX_CTRL_PCKT_SIZE (%d)",
                                length, PPTP_MAX_CTRL_PCKT_SIZE);
                /* we loose sync (unless we malloc something big, which isn't a good
                 * idea - potential DoS) so we must close connection (draft states that
                 * if you loose sync you must close the control connection immediately)
                 */
                return 0;
        }
        /* Now read the actual control packet */
        bytes_this = read(clientFd, packet + bytes_ttl, length - bytes_ttl);
        switch (bytes_this) {
        case -1:
                if(ignoreErrno(errno)) {
                        /* re-tryable error, re-buffer and return */
                        if (bytes_ttl) {
                                buffered = bytes_ttl;
                                buffer = malloc(bytes_ttl);
                                if (!buffer)
                                        return(0);
                                memcpy(buffer, packet, bytes_ttl);
                        }
                        syslog(LOG_ERR, "CTRL: Error reading ctrl packet (bytes_ttl=%lu,length=%d): %s", (unsigned long) bytes_ttl, length, strerror(errno));
                        return -1;
                }
                /* FALLTHRU */
        case 0:
                syslog(LOG_ERR, "CTRL: EOF or bad error reading ctrl packet.");
                return 0;
        default:
                bytes_ttl += bytes_this;
                /* not enough data to proceed */
                if (bytes_ttl != length) {
                        buffered = bytes_ttl;
                        buffer = malloc(bytes_ttl);
                        if (!buffer)
                                return(0);
                        memcpy(buffer, packet, bytes_ttl);
                        if (pptpctrl_debug)
                                syslog(LOG_DEBUG, "CTRL: Incomplete ctrl packet, retry later");
                        return -1;
                }
        }

        /* We got one :-) */

        /* Cast the packet into the PPTP Control Message format */
        header = (struct pptp_header *) packet;

        /* Packet sanity check on magic cookie */
        if (ntohl(header->magic) != PPTP_MAGIC_COOKIE) {
                /* Oops! Not a valid Control Message */
                syslog(LOG_ERR, "CTRL: Bad magic cookie - lost synchronization, closing control connection.");
                /* draft states loss of syncronization must result in
                 * immediate closing of the control connection
                 */
                return 0;
        }
        /* Woohoo! Looks like we got a valid PPTP packet */
        *pptp_ctrl_type = (int) (ntohs(header->ctrl_type));
        if (pptpctrl_debug)
                syslog(LOG_DEBUG, "CTRL: Received PPTP Control Message (type: %d)", *pptp_ctrl_type);
        return bytes_ttl;
}

/* Macros to use in making response packets */

#define MAKE_CTRL_HEADER(where, what) \
        where.header.length = htons(sizeof(where)); \
        where.header.pptp_type = htons(PPTP_CTRL_MESSAGE); \
        where.header.magic = htonl(PPTP_MAGIC_COOKIE); \
        where.header.ctrl_type = htons(what); \
        where.header.reserved0 = htons(RESERVED)

#define COPY_CTRL_PACKET(from, to, size) \
        memcpy(to, &from, ((*size) = sizeof(from)))

#define DEBUG_PACKET(what) \
        if(pptpctrl_debug) \
                syslog(LOG_DEBUG, "CTRL: Made a " what " packet")

/*
 * deal_start_ctrl_conn
 *
 * This method 'deals' with a START-CONTROL-CONNECTION-REQUEST. After
 * stripping down the connection request a suitable reply is formed and
 * stored in 'rply_packet' ready for sending.
 *
 * args: packet (IN) - the packet that we have to deal with (should be a
 *                     START-CONTROL-CONNECTION-REQUEST packet)
 *       rply_packet (OUT) - suitable reply to the 'packet' we got.
 *       rply_size (OUT) - size of the reply packet
 */
void deal_start_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        struct pptp_start_ctrl_conn_rply start_ctrl_conn_rply;

        MAKE_CTRL_HEADER(start_ctrl_conn_rply, START_CTRL_CONN_RPLY);
        start_ctrl_conn_rply.version = htons(PPTP_VERSION);
        start_ctrl_conn_rply.result_code = CONNECTED;
        start_ctrl_conn_rply.error_code = NO_ERROR;
        start_ctrl_conn_rply.framing_cap = htons(OUR_FRAMING);
        start_ctrl_conn_rply.bearer_cap = htons(OUR_BEARER);
        start_ctrl_conn_rply.max_channels = htons(MAX_CHANNELS);
        start_ctrl_conn_rply.firmware_rev = htons(PPTP_FIRMWARE_VERSION);
        memset(start_ctrl_conn_rply.hostname, 0, MAX_HOSTNAME_SIZE);
        strncpy((char *)start_ctrl_conn_rply.hostname, PPTP_HOSTNAME, MAX_HOSTNAME_SIZE);
        memset(start_ctrl_conn_rply.vendor, 0, MAX_VENDOR_SIZE);
        strncpy((char *)start_ctrl_conn_rply.vendor, PPTP_VENDOR, MAX_VENDOR_SIZE);
        COPY_CTRL_PACKET(start_ctrl_conn_rply, rply_packet, rply_size);
        DEBUG_PACKET("START CTRL CONN RPLY");
}

/*
 * deal_stop_ctrl_conn
 *
 * This method response to a STOP-CONTROL-CONNECTION-REQUEST with a
 * STOP-CONTROL-CONNECTION-REPLY.
 */
void deal_stop_ctrl_conn(void *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        struct pptp_stop_ctrl_conn_rply stop_ctrl_conn_rply;

        MAKE_CTRL_HEADER(stop_ctrl_conn_rply, STOP_CTRL_CONN_RPLY);
        stop_ctrl_conn_rply.result_code = DISCONNECTED;
        stop_ctrl_conn_rply.error_code = NO_ERROR;
        stop_ctrl_conn_rply.reserved1 = htons(RESERVED);
        COPY_CTRL_PACKET(stop_ctrl_conn_rply, rply_packet, rply_size);
        DEBUG_PACKET("STOP CTRL CONN RPLY");
}

/*
 * deal_out_call
 *
 * This method 'deals' with a OUT-GOING-CALL-REQUEST. After
 * stripping down the request a suitable reply is formed and stored in
 * 'rply_packet' ready for sending.
 *
 * args: packet (IN) - the packet that we have to deal with (should be a
 *                      OUT-GOING-CALL-REQUEST packet)
 *       rply_packet (OUT) - suitable reply to the 'packet' we got.
 *       rply_size (OUT) - size of the reply packet
 *
 */
void deal_out_call(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        u_int16_t pac_call_id;
        struct pptp_out_call_rqst *out_call_rqst;
        struct pptp_out_call_rply out_call_rply;

        out_call_rqst = (struct pptp_out_call_rqst *) packet;

        if ((pac_call_id = getcall()) == htons(-1)) {
                /* XXX should reject call */
                syslog(LOG_ERR, "CTRL: No free Call IDs!");
                pac_call_id = 0;
        }
        MAKE_CTRL_HEADER(out_call_rply, OUT_CALL_RPLY);
        /* call_id is used for ctrl, call_id_peer is used for GRE
         * call_id_peer is what we were sent by the other end in ctrl initilization
         */
        out_call_rply.call_id = pac_call_id;
        out_call_rply.call_id_peer = out_call_rqst->call_id;
        out_call_rply.result_code = CONNECTED;
        out_call_rply.error_code = NO_ERROR;
        out_call_rply.cause_code = NO_ERROR;
        /* maybe limit to pppd speed? but pppd doesn't accept 10Mbps as a speed and yet
         * still performs at over 115200, eg, 60kbyte/sec and higher observed.
         */
        out_call_rply.speed = out_call_rqst->max_bps;
        /* lets match their window size for now... was htons(PCKT_RECV_WINDOW_SIZE)
         */
        out_call_rply.pckt_recv_size = out_call_rqst->pckt_recv_size;
        if(pptpctrl_debug)
                syslog(LOG_DEBUG, "CTRL: Set parameters to %d maxbps, %d window size",
                        ntohl(out_call_rply.speed), ntohs(out_call_rply.pckt_recv_size));
        out_call_rply.pckt_delay = htons(PCKT_PROCESS_DELAY);
        out_call_rply.channel_id = htonl(CHANNEL_ID);
        COPY_CTRL_PACKET(out_call_rply, rply_packet, rply_size);
        DEBUG_PACKET("OUT CALL RPLY");
}


/*
 * deal_echo
 *
 * This method 'deals' with a ECHO-REQUEST. After stripping down the
 * connection request a suitable reply is formed and stored in
 * 'rply_packet' ready for sending.
 *
 * args: packet (IN) - the packet that we have to deal with (should be a
 *                      ECHO-REQUEST packet)
 *       rply_packet (OUT) - suitable reply to the 'packet' we got.
 *       rply_size (OUT) - size of the reply packet
 *
 */
void deal_echo(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        struct pptp_echo_rqst *echo_rqst;
        struct pptp_echo_rply echo_rply;

        echo_rqst = (struct pptp_echo_rqst *) packet;

        MAKE_CTRL_HEADER(echo_rply, ECHO_RPLY);
        echo_rply.identifier = echo_rqst->identifier;
        echo_rply.result_code = CONNECTED;
        echo_rply.error_code = NO_ERROR;
        echo_rply.reserved1 = htons(RESERVED);
        COPY_CTRL_PACKET(echo_rply, rply_packet, rply_size);
        DEBUG_PACKET("ECHO RPLY");
}

/*
 * deal_call_clr
 *
 * This method 'deals' with a CALL-CLEAR-REQUEST. After stripping down the
 * connection request a suitable reply is formed and stored in
 * 'rply_packet' ready for sending.
 *
 * args: packet (IN) - the packet that we have to deal with (should be a
 *                      CALL-CLEAR-REQUEST packet)
 *       rply_packet (OUT) - suitable reply to the 'packet' we got.
 *       rply_size (OUT) - size of the reply packet
 *
 */
void deal_call_clr(unsigned char *packet, struct pptp_out_call_rply *rply_packet, ssize_t *rply_size)
{
        struct pptp_call_disconn_ntfy call_disconn_ntfy;
        u_int16_t pac_call_id;

        /* Form a reply
         * The reply packet is a CALL-DISCONECT-NOTIFY
         * In single call mode we don't care what peer's call ID is, so don't even bother looking
         */
        if ((pac_call_id = freecall()) == htons(-1)) {
                /* XXX should return an error */
                syslog(LOG_ERR, "CTRL: Could not free Call ID [call clear]!");
        }
        MAKE_CTRL_HEADER(call_disconn_ntfy, CALL_DISCONN_NTFY);
        call_disconn_ntfy.call_id = pac_call_id;
        call_disconn_ntfy.result_code = CALL_CLEAR_REQUEST;     /* disconnected by call_clr_rqst */
        call_disconn_ntfy.error_code = NO_ERROR;
        call_disconn_ntfy.cause_code = htons(NO_ERROR);
        call_disconn_ntfy.reserved1 = htons(RESERVED);
        memset(call_disconn_ntfy.call_stats, 0, 128);
        COPY_CTRL_PACKET(call_disconn_ntfy, rply_packet, rply_size);
        DEBUG_PACKET("CALL DISCONNECT RPLY");
}

/*
 * deal_set_link_info
 *
 * @FIXME This function is *not* completed
 *
 * This method 'deals' with a SET-LINK-INFO. After stripping down the
 * connection request a suitable reply is formed and stored in
 * 'rply_packet' ready for sending.
 *
 * args: packet (IN) - the packet that we have to deal with (should be a
 *                      SET-LINK-INFO packet)
 *       rply_packet (OUT) - suitable reply to the 'packet' we got.
 *       rply_size (OUT) - size of the reply packet
 *
 */
void deal_set_link_info(unsigned char *packet)
{
        struct pptp_set_link_info *set_link_info;

        set_link_info = (struct pptp_set_link_info *) packet;
        if (set_link_info->send_accm != 0xffffffff || set_link_info->recv_accm != 0xffffffff) {
                /* Async-Control-Character-Map (ACCM) are bits that
                   show which control characters should be escaped by the
                   PPP implementation ... pptpd leaves pppd to negotiate
                   that via LCP and does not process SET LINK INFO
                   packets ... this is not complaint with the RFC but
                   still works. */
                if (pptpctrl_debug)
                        syslog(LOG_DEBUG, "CTRL: Ignored a SET LINK INFO packet with real ACCMs! (intentional non-compliance with section 2.15 of RFC 2637, ACCM is negotiated by PPP LCP asyncmap)");
        } else if (pptpctrl_debug)
                syslog(LOG_DEBUG, "CTRL: Got a SET LINK INFO packet with standard ACCMs");
}

void make_echo_req_packet(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size, u_int32_t echo_id)
{
        struct pptp_echo_rqst echo_packet;

        MAKE_CTRL_HEADER(echo_packet, ECHO_RQST);
        echo_packet.identifier = echo_id;
        COPY_CTRL_PACKET(echo_packet, rply_packet, rply_size);
        DEBUG_PACKET("ECHO REQ");
}

void make_stop_ctrl_req(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        struct pptp_stop_ctrl_conn_rqst stop_ctrl;

        MAKE_CTRL_HEADER(stop_ctrl, STOP_CTRL_CONN_RQST);
        stop_ctrl.reason = GENERAL_STOP_CTRL;
        stop_ctrl.reserved1 = RESERVED;
        stop_ctrl.reserved2 = htons(RESERVED);
        COPY_CTRL_PACKET(stop_ctrl, rply_packet, rply_size);
        DEBUG_PACKET("STOP CTRL REQ");
}

void make_call_admin_shutdown(struct pptp_out_call_rply *rply_packet, ssize_t * rply_size)
{
        struct pptp_call_disconn_ntfy call_disconn_ntfy;
        u_int16_t pac_call_id;

        /* Form a reply
         * The reply packet is a CALL-DISCONECT-NOTIFY
         * In single call mode we don't care what peer's call ID is, so don't even bother looking
         */
        if ((pac_call_id = freecall()) == htons(-1)) {
                /* XXX should return an error */
                syslog(LOG_ERR, "CTRL: Could not free Call ID [admin shutdown]!");
        }
        MAKE_CTRL_HEADER(call_disconn_ntfy, CALL_DISCONN_NTFY);
        call_disconn_ntfy.call_id = pac_call_id;
        call_disconn_ntfy.result_code = ADMIN_SHUTDOWN;         /* disconnected by admin shutdown */
        call_disconn_ntfy.error_code = NO_ERROR;
        call_disconn_ntfy.cause_code = htons(NO_ERROR);
        call_disconn_ntfy.reserved1 = htons(RESERVED);
        memset(call_disconn_ntfy.call_stats, 0, 128);
        COPY_CTRL_PACKET(call_disconn_ntfy, rply_packet, rply_size);
        DEBUG_PACKET("CALL DISCONNECT RPLY");
}

#if PNS_MODE
/* out of date.  really PNS isn't 'trivially different', it's quite different */

#define C_BITS (sizeof(unsigned int) * 8)
#define C_SEG(x) (x/C_BITS)
#define C_BIT(x) ((1U)<<(x%C_BITS))
static unsigned int activeCalls[(MAX_CALLS / C_BITS) + 1];

/*
 * get_call_id
 *
 * Assigns a call ID and peer call ID to the session.
 *
 * args: call_id (OUT) - the call ID for the session
 * retn: 0 on success, -1 on failure
 */
int get_call_id(u_int16_t * loc)
{
        for (i = 0; i < MAX_CALLS; i++) {
                if (!(activeCalls[C_SEG(i)] & C_BIT(i))) {
                        activeCalls[C_SEG(i)] |= C_BIT(i);
                        *loc = i;
                        return 0;
                }
        }
        return -1;
}

/*
 * free_call_id
 *
 * args: call_id (IN) - the call ID for a terminated session
 * retn: 0 on success, -1 on failure
 */
int free_call_id(u_int16_t call_id)
{
        if (!(activeCalls[C_SEG(i)] & C_BIT(i)))
                return -1;
        activeCalls[C_SEG(i)] &= ~C_BIT(i);
        return 0;
}
#else
static int _pac_call_id;
static u_int16_t _pac_init = 0;

/*
 * getcall
 *
 * Assigns a call ID to the session and stores/returns it
 *
 * we only permit one call at a time, so the chance of wrapping 65k on one
 * control connection is zero to none...
 */
u_int16_t getcall()
{
        static u_int16_t i = 0;
        extern u_int16_t unique_call_id;

        /* Start with a random Call ID.  This is to allocate unique
         * Call ID's across multiple TCP PPTP connections.  In this
         * way remote clients masqueraded by a firewall will put
         * unique peer call ID's into GRE packets that will have the
         * same source IP address of the firewall. */

        if (!i) {
                if (unique_call_id == 0xFFFF) {
                        struct timeval tv;
                        if (gettimeofday(&tv, NULL) == 0) {
                                i = ((tv.tv_sec & 0x0FFF) << 4) + 
                                    (tv.tv_usec >> 16);
                        }
                } else {
                        i = unique_call_id;
                }
        }

        if(!_pac_init) {
                _pac_call_id = htons(-1);
                _pac_init = 1;
        }
        if(_pac_call_id != htons(-1))
                syslog(LOG_ERR, "CTRL: Asked to allocate call id when call open, not handled well");
        _pac_call_id = htons(i);
        i++;
        return _pac_call_id;
}

/*
 * freecall
 *
 * Notes termination of current call
 *
 * retn: -1 on failure, PAC call ID on success
 */
u_int16_t freecall()
{
        u_int16_t ret;

        if(!_pac_init) {
                _pac_call_id = htons(-1);
                _pac_init = 1;
        }
        ret = _pac_call_id;
        if(_pac_call_id == htons(-1))
                syslog(LOG_ERR, "CTRL: Asked to free call when no call open, not handled well");
        _pac_call_id = htons(-1);
        return ret;
}
#endif