]> git.ozlabs.org Git - ppp.git/blobdiff - linux/ppp.c
make sure pppd notices hangups in demand mode
[ppp.git] / linux / ppp.c
index 66c5e185f1306e46c3d3f492dadb538bffb4e674..8cc2727179ff912ee64c66f4de3907d26a33b87e 100644 (file)
@@ -4,7 +4,7 @@
  *  Al Longyear <longyear@netcom.com>
  *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  ==FILEVERSION 980704==
+ *  ==FILEVERSION 990412==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -45,7 +45,7 @@
 
 #define PPP_MAX_RCV_QLEN       32      /* max # frames we queue up for pppd */
 
-/* $Id: ppp.c,v 1.19 1998/07/07 04:27:37 paulus Exp $ */
+/* $Id: ppp.c,v 1.25 1999/04/16 11:29:13 paulus Exp $ */
 
 #include <linux/version.h>
 #include <linux/config.h>
 #include <linux/kerneld.h>
 #endif
 
+#undef PPP_VERSION
+#define PPP_VERSION    "2.3.7"
+
 #if LINUX_VERSION_CODE >= VERSION(2,1,4)
 
 #if LINUX_VERSION_CODE >= VERSION(2,1,5)
 #endif
 
 #if LINUX_VERSION_CODE < VERSION(2,1,57)
-#define signal_pending(tsk)    ((tsk)->pending & ~(tsk)->blocked)
+#define signal_pending(tsk)    ((tsk)->signal & ~(tsk)->blocked)
 #endif
 
 #if LINUX_VERSION_CODE < VERSION(2,1,60)
@@ -153,6 +156,24 @@ typedef ssize_t            rw_ret_t;
 typedef size_t         rw_count_t;
 #endif
 
+#if LINUX_VERSION_CODE < VERSION(2,1,86)
+#define KFREE_SKB(s)   dev_kfree_skb((s), FREE_WRITE)
+#else
+#define KFREE_SKB(s)   kfree_skb(s)
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+#define LIBERATE_SKB(s)        ((s)->free = 1)
+#else
+#define LIBERATE_SKB(s)        do { } while (0)
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,95)
+#define SUSER()                suser()
+#else
+#define SUSER()                capable(CAP_NET_ADMIN)
+#endif
+
 /*
  * Local functions
  */
@@ -164,9 +185,11 @@ static void ppp_unregister_compressor (struct compressor *cp);
 
 static void ppp_async_init(struct ppp *ppp);
 static void ppp_async_release(struct ppp *ppp);
+static int ppp_tty_sync_push(struct ppp *ppp);
 static int ppp_tty_push(struct ppp *ppp);
 static int ppp_async_encode(struct ppp *ppp);
 static int ppp_async_send(struct ppp *, struct sk_buff *);
+static int ppp_sync_send(struct ppp *, struct sk_buff *);
 
 static int ppp_ioctl(struct ppp *, unsigned int, unsigned long);
 static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp);
@@ -177,6 +200,7 @@ static void ppp_receive_error(struct ppp *ppp);
 static void ppp_output_wakeup(struct ppp *ppp);
 static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb);
 static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_send_frames(struct ppp *ppp);
 static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
 
 static struct ppp *ppp_find (int pid_value);
@@ -238,7 +262,6 @@ static struct symbol_table ppp_syms = {
 #include <linux/symtab_begin.h>
        X(ppp_register_compressor),
        X(ppp_unregister_compressor),
-       X(ppp_crc16_table),
 #include <linux/symtab_end.h>
 };
 #else
@@ -315,7 +338,6 @@ __u16 ppp_crc16_table[256] =
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
-EXPORT_SYMBOL(ppp_crc16_table);
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
@@ -350,7 +372,9 @@ ppp_first_time(void)
         */
        (void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
        ppp_ldisc.magic         = TTY_LDISC_MAGIC;
+#if LINUX_VERSION_CODE >= VERSION(2,1,28)
        ppp_ldisc.name          = "ppp";
+#endif
        ppp_ldisc.open          = ppp_tty_open;
        ppp_ldisc.close         = ppp_tty_close;
        ppp_ldisc.read          = ppp_tty_read;
@@ -438,10 +462,10 @@ ppp_async_release(struct ppp *ppp)
        struct sk_buff *skb;
 
        if ((skb = ppp->rpkt) != NULL)
-               kfree_skb(skb);
+               KFREE_SKB(skb);
        ppp->rpkt = NULL;
        if ((skb = ppp->tpkt) != NULL)
-               kfree_skb(skb);
+               KFREE_SKB(skb);
        ppp->tpkt = NULL;
 }
 
@@ -526,6 +550,9 @@ ppp_tty_close (struct tty_struct *tty)
                return;
        if (ppp->backup_tty) {
                ppp->tty = ppp->backup_tty;
+               if (ppp_tty_push(ppp))
+                       ppp_output_wakeup(ppp);
+               wake_up_interruptible(&ppp->read_wait);
        } else {
                ppp->tty = 0;
                ppp->sc_xfer = 0;
@@ -535,7 +562,6 @@ ppp_tty_close (struct tty_struct *tty)
 
                ppp_async_release(ppp);
                ppp_release(ppp);
-               ppp->inuse = 0;
                MOD_DEC_USE_COUNT;
        }
 }
@@ -592,7 +618,6 @@ ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
                if (file->f_flags & O_NONBLOCK)
                        break;
 
-               current->timeout = 0;
                interruptible_sleep_on(&ppp->read_wait);
                err = -EINTR;
                if (signal_pending(current))
@@ -620,12 +645,12 @@ ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
        /*
         * Copy the received data from the buffer to the caller's area.
         */
-       err = COPY_TO_USER(buf, skb->data, len);
-       if (err == 0)
-               err = len;
+       err = len;
+       if (COPY_TO_USER(buf, skb->data, len))
+               err = -EFAULT;
 
 out:
-       kfree_skb(skb);
+       KFREE_SKB(skb);
        return err;
 }
 
@@ -672,13 +697,14 @@ ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
                printk(KERN_ERR "ppp_tty_write: no memory\n");
                return 0;
        }
+       LIBERATE_SKB(skb);
        new_data = skb_put(skb, count);
 
        /*
         * Retrieve the user's buffer
         */
        if (COPY_FROM_USER(new_data, data, count)) {
-               kfree_skb(skb);
+               KFREE_SKB(skb);
                return -EFAULT;
        }
 
@@ -712,7 +738,7 @@ ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
        /*
         * The user must have an euid of root to do these requests.
         */
-       if (!capable(CAP_NET_ADMIN))
+       if (!SUSER())
                return -EPERM;
 
        switch (param2) {
@@ -931,6 +957,135 @@ ppp_tty_wakeup (struct tty_struct *tty)
                ppp_output_wakeup(ppp);
 }
 
+/*
+ * Send a packet to the peer over a synchronous tty line.
+ * All encoding and FCS are handled by hardware.
+ * Addr/Ctrl and Protocol field compression implemented.
+ * Returns -1 iff the packet could not be accepted at present,
+ * 0 if the packet was accepted but we can't accept another yet, or
+ * 1 if we can accept another packet immediately.
+ * If this procedure returns 0, ppp_output_wakeup will be called
+ * exactly once.
+ */
+static int
+ppp_sync_send(struct ppp *ppp, struct sk_buff *skb)
+{
+       unsigned char *data;
+       int islcp;
+       
+       CHECK_PPP(0);
+
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
+
+       data = ppp->tpkt->data;
+       
+       /*
+        * LCP packets with code values between 1 (configure-reqest)
+        * and 7 (code-reject) must be sent as though no options
+        * had been negotiated.
+        */
+       islcp = PPP_PROTOCOL(data) == PPP_LCP
+               && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
+
+       /* only reset idle time for data packets */
+       if (PPP_PROTOCOL(data) < 0x8000)
+               ppp->last_xmit = jiffies;
+       ++ppp->stats.ppp_opackets;
+       ppp->stats.ppp_ooctects += ppp->tpkt->len;
+
+       if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) {
+               /* compress protocol field */
+               data[2] = data[1];
+               data[1] = data[0];
+               skb_pull(ppp->tpkt,1);
+               data = ppp->tpkt->data;
+       }
+       
+       /*
+        * Do address/control compression
+        */
+       if ((ppp->flags & SC_COMP_AC) && !islcp
+           && PPP_ADDRESS(data) == PPP_ALLSTATIONS
+           && PPP_CONTROL(data) == PPP_UI) {
+               /* strip addr and control field */
+               skb_pull(ppp->tpkt,2);
+       }
+
+       return ppp_tty_sync_push(ppp);
+}
+
+/*
+ * Push a synchronous frame out to the tty.
+ * Returns 1 if frame accepted (or discarded), 0 otherwise.
+ */
+static int
+ppp_tty_sync_push(struct ppp *ppp)
+{
+       int sent;
+       struct tty_struct *tty = ppp2tty(ppp);
+       unsigned long flags;
+               
+       CHECK_PPP(0);
+
+       if (ppp->tpkt == NULL)
+               return 0;
+               
+       /* prevent reentrancy with tty_pushing flag */          
+       save_flags(flags);
+       cli();
+       if (ppp->tty_pushing) {
+               /* record wakeup attempt so we don't loose */
+               /* a wakeup call while doing push processing */
+               ppp->woke_up=1;
+               restore_flags(flags);
+               return 0;
+       }
+       ppp->tty_pushing = 1;
+       restore_flags(flags);
+       
+       if (tty == NULL || tty->disc_data != (void *) ppp)
+               goto flush;
+               
+       for(;;){
+               ppp->woke_up=0;
+               
+               /* Note: Sync driver accepts complete frame or nothing */
+               tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+               sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
+               if (sent < 0) {
+                       /* write error (possible loss of CD) */
+                       /* record error and discard current packet */
+                       ppp->stats.ppp_oerrors++;
+                       break;
+               }
+               ppp->stats.ppp_obytes += sent;
+               if (sent < ppp->tpkt->len) {
+                       /* driver unable to accept frame just yet */
+                       save_flags(flags);
+                       cli();
+                       if (ppp->woke_up) {
+                               /* wake up called while processing */
+                               /* try to send the frame again */
+                               restore_flags(flags);
+                               continue;
+                       }
+                       /* wait for wakeup callback to try send again */
+                       ppp->tty_pushing = 0;
+                       restore_flags(flags);
+                       return 0;
+               }
+               break;
+       }
+flush: 
+       /* done with current packet (sent or discarded) */
+       KFREE_SKB(ppp->tpkt);
+       ppp->tpkt = 0;
+       ppp->tty_pushing = 0;
+       return 1;
+}
+
 /*
  * Send a packet to the peer over an async tty line.
  * Returns -1 iff the packet could not be accepted at present,
@@ -963,6 +1118,9 @@ ppp_tty_push(struct ppp *ppp)
 {
        int avail, sent, done = 0;
        struct tty_struct *tty = ppp2tty(ppp);
+       
+       if ( ppp->flags & SC_SYNC ) 
+               return ppp_tty_sync_push(ppp);
 
        CHECK_PPP(0);
        if (ppp->tty_pushing)
@@ -994,7 +1152,7 @@ flush:
        ppp->tty_pushing = 1;
        ppp->stats.ppp_oerrors++;
        if (ppp->tpkt != 0) {
-               kfree_skb(ppp->tpkt);
+               KFREE_SKB(ppp->tpkt);
                ppp->tpkt = 0;
                done = 1;
        }
@@ -1098,7 +1256,7 @@ ppp_async_encode(struct ppp *ppp)
                *buf++ = PPP_FLAG;
                ppp->olim = buf;
 
-               kfree_skb(ppp->tpkt);
+               KFREE_SKB(ppp->tpkt);
                ppp->tpkt = 0;
                return 1;
        }
@@ -1161,6 +1319,74 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
 
        ppp->stats.ppp_ibytes += count;
        skb = ppp->rpkt;
+       
+       if ( ppp->flags & SC_SYNC ) {
+               /* synchronous mode */
+               
+               if (ppp->toss==0xE0) {
+                       /* this is the 1st frame, reset vj comp */
+                       ppp_receive_error(ppp);
+                       ppp->toss = 0;
+               }
+               
+               /*
+                * Allocate an skbuff for frame.
+                * The 128 is room for VJ header expansion.
+                */
+               
+               if (skb == NULL)
+                       skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+                       
+               if (skb == NULL) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "couldn't "
+                                      "alloc skb for recv\n");
+               } else {
+                       LIBERATE_SKB(skb);
+                       /*
+                        * Decompress A/C and protocol compression here.
+                        */
+                       p = skb_put(skb, 2);
+                       p[0] = PPP_ALLSTATIONS;
+                       p[1] = PPP_UI;
+                       if (*data == PPP_ALLSTATIONS) {
+                               data += 2;
+                               count -= 2;
+                       }
+                       if ((*data & 1) != 0) {
+                               p = skb_put(skb, 1);
+                               p[0] = 0;
+                       }
+
+                       /* copy frame to socket buffer */
+                       p = skb_put(skb, count);
+                       memcpy(p,data,count);
+                       
+                       /*
+                        * Check if we've overflowed the MRU
+                        */
+                       if (skb->len >= ppp->mru + PPP_HDRLEN + 2
+                           || skb_tailroom(skb) <= 0) {
+                               ++ppp->estats.rx_length_errors;
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "rcv frame too long: "
+                                              "len=%d mru=%d hroom=%d troom=%d\n",
+                                              skb->len, ppp->mru, skb_headroom(skb),
+                                              skb_tailroom(skb));
+                       } else {
+                               if (!ppp_receive_frame(ppp, skb)) {
+                                       KFREE_SKB(skb);
+                                       ppp_receive_error(ppp);
+                               }
+                       }
+               
+                       /* Reset for the next frame */
+                       skb = NULL;
+               }
+               ppp->rpkt = skb;
+               return;
+       }
+       
        while (count-- > 0) {
                /*
                 * Collect the character and error condition for the character.
@@ -1227,7 +1453,7 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
                                               "ppp: tossing frame (%x)\n",
                                               ppp->toss);
                                if (skb != NULL)
-                                       kfree_skb(skb);
+                                       KFREE_SKB(skb);
                                if (!(ppp->toss == 0xE0 || ppp->toss == 0x80))
                                        ++ppp->stats.ppp_ierrors;
                                ppp_receive_error(ppp);
@@ -1276,6 +1502,7 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
                                ppp->toss = 1;
                                continue;
                        }
+                       LIBERATE_SKB(skb);
                }
 
                /*
@@ -1355,6 +1582,7 @@ typedef struct ppp_proto_struct {
 } ppp_proto_type;
 
 static int rcv_proto_ip                (struct ppp *, struct sk_buff *);
+static int rcv_proto_ipv6      (struct ppp *, struct sk_buff *);
 static int rcv_proto_ipx       (struct ppp *, struct sk_buff *);
 static int rcv_proto_at                (struct ppp *, struct sk_buff *);
 static int rcv_proto_vjc_comp  (struct ppp *, struct sk_buff *);
@@ -1365,6 +1593,7 @@ static int rcv_proto_unknown      (struct ppp *, struct sk_buff *);
 static
 ppp_proto_type proto_list[] = {
        { PPP_IP,         rcv_proto_ip         },
+       { PPP_IPV6,       rcv_proto_ipv6       },
        { PPP_IPX,        rcv_proto_ipx        },
        { PPP_AT,         rcv_proto_at         },
        { PPP_VJC_COMP,   rcv_proto_vjc_comp   },
@@ -1504,7 +1733,9 @@ ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
                return -EINVAL;
        }
 
-       return COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb);
+       if (COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb))
+               return -EFAULT;
+       return 0;
 }
 
 /*
@@ -1525,7 +1756,7 @@ ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
        /*
         * The user must have an euid of root to do these requests.
         */
-       if (!capable(CAP_NET_ADMIN))
+       if (!SUSER())
                return -EPERM;
 
        switch (param2) {
@@ -1537,6 +1768,7 @@ ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
                        break;
                if (temp_i < PPP_MRU)
                        temp_i = PPP_MRU;
+               ppp->mru = temp_i;
                if (ppp->flags & SC_DEBUG)
                        printk(KERN_INFO
                               "ppp_ioctl: set mru to %x\n", temp_i);
@@ -1736,8 +1968,9 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
        /*
         * Fetch the compression parameters
         */
+       error = -EFAULT;
        if (COPY_FROM_USER(&data, odp, sizeof (data)))
-               return -EFAULT;
+               goto out;
 
        nb  = data.length;
        ptr = data.ptr;
@@ -1745,10 +1978,11 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
                nb = CCP_MAX_OPTION_LENGTH;
 
        if (COPY_FROM_USER(ccp_option, ptr, nb))
-               return -EFAULT;
+               goto out;
 
+       error = -EINVAL;
        if (ccp_option[1] < 2)  /* preliminary check on the length byte */
-               return -EINVAL;
+               goto out;
 
        save_flags(flags);
        cli();
@@ -1771,7 +2005,7 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
                               "%s: no compressor for [%x %x %x], %x\n",
                               ppp->name, ccp_option[0], ccp_option[1],
                               ccp_option[2], nb);
-               return -EINVAL;         /* compressor not loaded */
+               goto out;               /* compressor not loaded */
        }
 
        /*
@@ -1786,7 +2020,6 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
 
                ppp->sc_xcomp    = cp;
                ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
-
                if (ppp->sc_xc_state == NULL) {
                        if (ppp->flags & SC_DEBUG)
                                printk(KERN_DEBUG "%s: comp_alloc failed\n",
@@ -1807,7 +2040,7 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
                        error = -ENOBUFS;
                }
        }
-
+out:
        return error;
 }
 
@@ -1977,7 +2210,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
        if (skb == NULL)
                return 1;
        if (skb->len == 0) {
-               kfree_skb(skb);
+               KFREE_SKB(skb);
                return 1;
        }
        data = skb->data;
@@ -1994,23 +2227,25 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
                return 0;
        }
 
-       /*
-        * Verify the FCS of the frame and discard the FCS characters
-        * from the end of the buffer.
-        */
-       if (ppp->rfcs != PPP_GOODFCS) {
-               if (ppp->flags & SC_DEBUG) {
-                       printk(KERN_DEBUG
-                              "ppp: frame with bad fcs, length = %d\n",
-                              count);
-                       ppp_print_buffer("bad frame", data, count);
+       if ( !(ppp->flags & SC_SYNC) ) { 
+               /*
+                * Verify the FCS of the frame and discard the FCS characters
+                * from the end of the buffer.
+                */
+               if (ppp->rfcs != PPP_GOODFCS) {
+                       if (ppp->flags & SC_DEBUG) {
+                               printk(KERN_DEBUG
+                                      "ppp: frame with bad fcs, length = %d\n",
+                                      count);
+                               ppp_print_buffer("bad frame", data, count);
+                       }
+                       ++ppp->estats.rx_crc_errors;
+                       return 0;
                }
-               ++ppp->estats.rx_crc_errors;
-               return 0;
+               count -= 2;             /* ignore the fcs characters */
+               skb_trim(skb, count);
        }
-       count -= 2;             /* ignore the fcs characters */
-       skb_trim(skb, count);
-
+       
        /*
         * Process the active decompressor.
         */
@@ -2026,13 +2261,14 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
                                printk(KERN_ERR "ppp_recv_frame: no memory\n");
                                new_count = DECOMP_ERROR;
                        } else {
+                               LIBERATE_SKB(new_skb);
                                new_count = (*ppp->sc_rcomp->decompress)
                                        (ppp->sc_rc_state, data, count,
                                         new_skb->data, ppp->mru + PPP_HDRLEN);
                        }
                        if (new_count > 0) {
                                /* Frame was decompressed OK */
-                               kfree_skb(skb);
+                               KFREE_SKB(skb);
                                skb = new_skb;
                                count = new_count;
                                data = skb_put(skb, count);
@@ -2047,7 +2283,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
                                        printk(KERN_INFO "%s: decomp err %d\n",
                                               ppp->name, new_count);
                                if (new_skb != 0)
-                                       kfree_skb(new_skb);
+                                       KFREE_SKB(new_skb);
                                if (ppp->slcomp != 0)
                                        slhc_toss(ppp->slcomp);
                                ++ppp->stats.ppp_ierrors;
@@ -2095,7 +2331,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
         * Update the appropriate statistic counter.
         */
        if (!(*proto_ptr->func)(ppp, skb)) {
-               kfree_skb(skb);
+               KFREE_SKB(skb);
                ++ppp->stats.ppp_discards;
        }
 
@@ -2127,12 +2363,9 @@ ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
         */
        skb->dev      = ppp2dev(ppp);   /* We are the device */
        skb->protocol = htons(proto);
-       skb->mac.raw  = skb->data;
        skb_pull(skb, PPP_HDRLEN);      /* pull off ppp header */
+       skb->mac.raw   = skb->data;
        ppp->last_recv = jiffies;
-#if LINUX_VERSION_CODE < VERSION(2,1,15)
-       skb->free = 1;
-#endif
        netif_rx (skb);
        return 1;
 }
@@ -2150,6 +2383,19 @@ rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
        return 0;
 }
 
+/*
+ * Process the receipt of an IPv6 frame
+ */
+static int
+rcv_proto_ipv6(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+       if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+           && ppp->sc_npmode[NP_IPV6] == NPMODE_PASS)
+               return ppp_rcv_rx(ppp, ETH_P_IPV6, skb);
+       return 0;
+}
+
 /*
  * Process the receipt of an IPX frame
  */
@@ -2189,13 +2435,17 @@ rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
                return 0;
        new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN,
                                    skb->len - PPP_HDRLEN);
-       if (new_count < 0) {
+       if (new_count <= 0) {
                if (ppp->flags & SC_DEBUG)
                        printk(KERN_NOTICE
                               "ppp: error in VJ decompression\n");
                return 0;
        }
-       skb_put(skb, new_count + PPP_HDRLEN - skb->len);
+       new_count += PPP_HDRLEN;
+       if (new_count > skb->len)
+               skb_put(skb, new_count - skb->len);
+       else
+               skb_trim(skb, new_count);
        return rcv_proto_ip(ppp, skb);
 }
 
@@ -2240,7 +2490,7 @@ rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
        while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) {
                struct sk_buff *skb = skb_dequeue(&ppp->rcv_q);
                if (skb)
-                       kfree_skb(skb);
+                       KFREE_SKB(skb);
        }
 
        wake_up_interruptible (&ppp->read_wait);
@@ -2265,8 +2515,8 @@ extern inline __u8 * store_long (register __u8 *p, register int value) {
 
 /*
  * Compress and send an frame to the peer.
- * Should be called with dev->tbusy == 1, having been set by the caller.
- * That is, we use dev->tbusy as a lock to prevent reentry of this
+ * Should be called with xmit_busy == 1, having been set by the caller.
+ * That is, we use xmit_busy as a lock to prevent reentry of this
  * procedure.
  */
 static void
@@ -2337,10 +2587,11 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
                new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
                if (new_skb == NULL) {
                        printk(KERN_ERR "ppp_send_frame: no memory\n");
-                       kfree_skb(skb);
-                       ppp->dev.tbusy = 0;
+                       KFREE_SKB(skb);
+                       ppp->xmit_busy = 0;
                        return;
                }
+               LIBERATE_SKB(new_skb);
 
                /* Compress the frame. */
                new_count = (*ppp->sc_xcomp->compress)
@@ -2350,26 +2601,29 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
                /* Did it compress? */
                if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
                        skb_put(new_skb, new_count);
-                       kfree_skb(skb);
+                       KFREE_SKB(skb);
                        skb = new_skb;
                } else {
                        /*
                         * The frame could not be compressed, or it could not
                         * be sent in compressed form because CCP is down.
                         */
-                       kfree_skb(new_skb);
+                       KFREE_SKB(new_skb);
                }
        }
 
        /*
         * Send the frame
         */
-       ret = ppp_async_send(ppp, skb);
+       if ( ppp->flags & SC_SYNC ) 
+               ret = ppp_sync_send(ppp, skb);
+       else
+               ret = ppp_async_send(ppp, skb);
        if (ret > 0) {
                /* we can release the lock */
-               ppp->dev.tbusy = 0;
+               ppp->xmit_busy = 0;
        } else if (ret < 0) {
-               /* this can't happen, since the caller got the tbusy lock */
+               /* can't happen, since the caller got the xmit_busy lock */
                printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
        }
 }
@@ -2389,6 +2643,7 @@ ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
                printk(KERN_ERR "ppp: no memory for vj compression\n");
                return skb;
        }
+       LIBERATE_SKB(new_skb);
 
        orig_data = data = skb->data + PPP_HDRLEN;
        len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN,
@@ -2397,7 +2652,7 @@ ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
 
        if (data == orig_data) {
                /* Couldn't compress the data */
-               kfree_skb(new_skb);
+               KFREE_SKB(new_skb);
                return skb;
        }
 
@@ -2419,7 +2674,7 @@ ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
        data[2] = 0;
        data[3] = proto;
 
-       kfree_skb(skb);
+       KFREE_SKB(skb);
        return new_skb;
 }
 
@@ -2428,15 +2683,18 @@ ppp_send_frames(struct ppp *ppp)
 {
        struct sk_buff *skb;
 
-       while (!test_and_set_bit(0, &ppp->dev.tbusy)) {
+       while (!test_and_set_bit(0, &ppp->xmit_busy)) {
                skb = skb_dequeue(&ppp->xmt_q);
                if (skb == NULL) {
-                       ppp->dev.tbusy = 0;
-                       mark_bh(NET_BH);
+                       ppp->xmit_busy = 0;
                        break;
                }
                ppp_send_frame(ppp, skb);
        }
+       if (!ppp->xmit_busy && ppp->dev.tbusy) {
+               ppp->dev.tbusy = 0;
+               mark_bh(NET_BH);
+       }
 }
 
 /*
@@ -2448,11 +2706,11 @@ ppp_output_wakeup(struct ppp *ppp)
 {
        CHECK_PPP_VOID();
 
-       if (!ppp->dev.tbusy) {
-               printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n");
+       if (!ppp->xmit_busy) {
+               printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n");
                return;
        }
-       ppp->dev.tbusy = 0;
+       ppp->xmit_busy = 0;
        ppp_send_frames(ppp);
 }
 
@@ -2494,7 +2752,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
        if (skb == NULL)
                return 0;
        if (skb->data == NULL) {
-               kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
        }
 
@@ -2503,7 +2761,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
         * queued to be sent.
         */
        if (!ppp->inuse) {
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
        }
 
@@ -2515,7 +2773,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                        printk(KERN_ERR
                               "ppp_dev_xmit: %s not connected to a TTY!\n",
                               dev->name);
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
        }
 
@@ -2528,6 +2786,10 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                proto = PPP_IP;
                npmode = ppp->sc_npmode[NP_IP];
                break;
+       case ETH_P_IPV6:
+               proto = PPP_IPV6;
+               npmode = ppp->sc_npmode[NP_IPV6];
+               break;
        case ETH_P_IPX:
                proto = PPP_IPX;
                npmode = ppp->sc_npmode[NP_IPX];
@@ -2541,7 +2803,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                if (ppp->flags & SC_DEBUG)
                        printk(KERN_INFO "%s: packet for unknown proto %x\n",
                               ppp->name, ntohs(skb->protocol));
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
        }
 
@@ -2560,7 +2822,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                 */
                if (ppp->flags & SC_DEBUG)
                        printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
 
        case NPMODE_ERROR:
@@ -2569,7 +2831,7 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                        printk(KERN_DEBUG
                               "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
                               npmode, ppp->name);
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                return 0;
        }
 
@@ -2577,9 +2839,15 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
         * The dev->tbusy field acts as a lock to allow only
         * one packet to be processed at a time.  If we can't
         * get the lock, try again later.
+        * We deliberately queue as little as possible inside
+        * the ppp driver in order to minimize the latency
+        * for high-priority packets.
         */
-       if (test_and_set_bit(0, &dev->tbusy))
+       if (test_and_set_bit(0, &ppp->xmit_busy)) {
+               dev->tbusy = 1; /* can't take it now */
                return 1;
+       }
+       dev->tbusy = 0;
 
        /*
         * Put the 4-byte PPP header on the packet.
@@ -2592,13 +2860,15 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
                if (new_skb == NULL) {
                        printk(KERN_ERR "%s: skb hdr alloc failed\n",
                               ppp->name);
-                       dev_kfree_skb(skb);
-                       dev->tbusy = 0;
+                       KFREE_SKB(skb);
+                       ppp->xmit_busy = 0;
+                       ppp_send_frames(ppp);
                        return 0;
                }
+               LIBERATE_SKB(new_skb);
                skb_reserve(new_skb, PPP_HDRLEN);
                memcpy(skb_put(new_skb, skb->len), skb->data, skb->len);
-               dev_kfree_skb(skb);
+               KFREE_SKB(skb);
                skb = new_skb;
        }
 
@@ -2609,6 +2879,8 @@ ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
        hdr[3] = proto;
 
        ppp_send_frame(ppp, skb);
+       if (!ppp->xmit_busy)
+               ppp_send_frames(ppp);
        return 0;
 }
 
@@ -2718,7 +2990,12 @@ ppp_alloc(void)
         */
        dev = ppp2dev(ppp);
        dev->name = ppp->name;
+#if LINUX_VERSION_CODE < VERSION(2,1,31)
+       if_num = (ppp_list == 0)? 0: ppp_last->line + 1;
+       sprintf(ppp->name, "ppp%d", if_num);
+#else
        if_num = dev_alloc_name(dev, "ppp%d");
+#endif
        if (if_num < 0) {
                printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num);
                kfree(ppp);
@@ -2772,6 +3049,7 @@ ppp_generic_init(struct ppp *ppp)
 
        ppp->last_xmit  = jiffies;
        ppp->last_recv  = jiffies;
+       ppp->xmit_busy  = 0;
 
        /* clear statistics */
        memset(&ppp->stats, 0, sizeof (struct pppstat));
@@ -2809,9 +3087,15 @@ ppp_release(struct ppp *ppp)
        }
 
        while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
-               kfree_skb(skb);
+               KFREE_SKB(skb);
        while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
-               kfree_skb(skb);
+               KFREE_SKB(skb);
+
+       ppp->inuse = 0;
+       if (ppp->dev.tbusy) {
+               ppp->dev.tbusy = 0;
+               mark_bh(NET_BH);
+       }
 }
 
 /*