ppp: Move PFC decompression to PPP generic layer
authorSam Protsenko <semen.protsenko@linaro.org>
Thu, 20 Dec 2018 18:29:20 +0000 (20:29 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Dec 2018 00:49:30 +0000 (16:49 -0800)
Extract "Protocol" field decompression code from transport protocols to
PPP generic layer, where it actually belongs. As a consequence, this
patch fixes incorrect place of PFC decompression in L2TP driver (when
it's not PPPOX_BOUND) and also enables this decompression for other
protocols, like PPPoE.

Protocol field decompression also happens in PPP Multilink Protocol
code and in PPP compression protocols implementations (bsd, deflate,
mppe). It looks like there is no easy way to get rid of that, so it was
decided to leave it as is, but provide those cases with appropriate
comments instead.

Changes in v2:
  - Fix the order of checking skb data room and proto decompression
  - Remove "inline" keyword from ppp_decompress_proto()
  - Don't split line before function name
  - Prefix ppp_decompress_proto() function with "__"
  - Add ppp_decompress_proto() function with skb data room checks
  - Add description for introduced functions
  - Fix comments (as per review on mailing list)

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ppp/ppp_async.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/ppp_synctty.c
drivers/net/ppp/pptp.c
net/l2tp/l2tp_ppp.c

index 288cf09..b287bb8 100644 (file)
@@ -770,7 +770,7 @@ process_input_packet(struct asyncppp *ap)
 {
        struct sk_buff *skb;
        unsigned char *p;
-       unsigned int len, fcs, proto;
+       unsigned int len, fcs;
 
        skb = ap->rpkt;
        if (ap->state & (SC_TOSS | SC_ESCAPE))
@@ -799,14 +799,14 @@ process_input_packet(struct asyncppp *ap)
                        goto err;
                p = skb_pull(skb, 2);
        }
-       proto = p[0];
-       if (proto & 1) {
-               /* protocol is compressed */
-               *(u8 *)skb_push(skb, 1) = 0;
-       } else {
+
+       /* If protocol field is not compressed, it can be LCP packet */
+       if (!(p[0] & 0x01)) {
+               unsigned int proto;
+
                if (skb->len < 2)
                        goto err;
-               proto = (proto << 8) + p[1];
+               proto = (p[0] << 8) + p[1];
                if (proto == PPP_LCP)
                        async_lcp_peek(ap, p, skb->len, 1);
        }
index 500bc00..c708400 100644 (file)
@@ -1965,6 +1965,46 @@ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
        ppp_recv_unlock(ppp);
 }
 
+/**
+ * __ppp_decompress_proto - Decompress protocol field, slim version.
+ * @skb: Socket buffer where protocol field should be decompressed. It must have
+ *      at least 1 byte of head room and 1 byte of linear data. First byte of
+ *      data must be a protocol field byte.
+ *
+ * Decompress protocol field in PPP header if it's compressed, e.g. when
+ * Protocol-Field-Compression (PFC) was negotiated. No checks w.r.t. skb data
+ * length are done in this function.
+ */
+static void __ppp_decompress_proto(struct sk_buff *skb)
+{
+       if (skb->data[0] & 0x01)
+               *(u8 *)skb_push(skb, 1) = 0x00;
+}
+
+/**
+ * ppp_decompress_proto - Check skb data room and decompress protocol field.
+ * @skb: Socket buffer where protocol field should be decompressed. First byte
+ *      of data must be a protocol field byte.
+ *
+ * Decompress protocol field in PPP header if it's compressed, e.g. when
+ * Protocol-Field-Compression (PFC) was negotiated. This function also makes
+ * sure that skb data room is sufficient for Protocol field, before and after
+ * decompression.
+ *
+ * Return: true - decompressed successfully, false - not enough room in skb.
+ */
+static bool ppp_decompress_proto(struct sk_buff *skb)
+{
+       /* At least one byte should be present (if protocol is compressed) */
+       if (!pskb_may_pull(skb, 1))
+               return false;
+
+       __ppp_decompress_proto(skb);
+
+       /* Protocol field should occupy 2 bytes when not compressed */
+       return pskb_may_pull(skb, 2);
+}
+
 void
 ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
 {
@@ -1977,7 +2017,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
        }
 
        read_lock_bh(&pch->upl);
-       if (!pskb_may_pull(skb, 2)) {
+       if (!ppp_decompress_proto(skb)) {
                kfree_skb(skb);
                if (pch->ppp) {
                        ++pch->ppp->dev->stats.rx_length_errors;
@@ -2074,6 +2114,9 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
        if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
                goto err;
 
+       /* At this point the "Protocol" field MUST be decompressed, either in
+        * ppp_input(), ppp_decompress_frame() or in ppp_receive_mp_frame().
+        */
        proto = PPP_PROTO(skb);
        switch (proto) {
        case PPP_VJC_COMP:
@@ -2245,6 +2288,9 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
                skb_put(skb, len);
                skb_pull(skb, 2);       /* pull off the A/C bytes */
 
+               /* Don't call __ppp_decompress_proto() here, but instead rely on
+                * corresponding algo (mppe/bsd/deflate) to decompress it.
+                */
        } else {
                /* Uncompressed frame - pass to decompressor so it
                   can update its dictionary if necessary. */
@@ -2290,9 +2336,11 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 
        /*
         * Do protocol ID decompression on the first fragment of each packet.
+        * We have to do that here, because ppp_receive_nonmp_frame() expects
+        * decompressed protocol field.
         */
-       if ((PPP_MP_CB(skb)->BEbits & B) && (skb->data[0] & 1))
-               *(u8 *)skb_push(skb, 1) = 0;
+       if (PPP_MP_CB(skb)->BEbits & B)
+               __ppp_decompress_proto(skb);
 
        /*
         * Expand sequence number to 32 bits, making it as close
index 047f6c6..d02ba24 100644 (file)
@@ -709,11 +709,10 @@ ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
                p = skb_pull(skb, 2);
        }
 
-       /* decompress protocol field if compressed */
-       if (p[0] & 1) {
-               /* protocol is compressed */
-               *(u8 *)skb_push(skb, 1) = 0;
-       } else if (skb->len < 2)
+       /* PPP packet length should be >= 2 bytes when protocol field is not
+        * compressed.
+        */
+       if (!(p[0] & 0x01) && skb->len < 2)
                goto err;
 
        /* queue the frame to be processed */
index 67ffe74..8f09edd 100644 (file)
@@ -325,11 +325,6 @@ allow_packet:
                        skb_pull(skb, 2);
                }
 
-               if ((*skb->data) & 1) {
-                       /* protocol is compressed */
-                       *(u8 *)skb_push(skb, 1) = 0;
-               }
-
                skb->ip_summed = CHECKSUM_NONE;
                skb_set_network_header(skb, skb->head-skb->data);
                ppp_input(&po->chan, skb);
index c03c646..04d9946 100644 (file)
@@ -236,10 +236,6 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int
            skb->data[1] == PPP_UI)
                skb_pull(skb, 2);
 
-       /* Decompress protocol field if PFC is enabled */
-       if ((*skb->data) & 0x1)
-               *(u8 *)skb_push(skb, 1) = 0;
-
        if (sk->sk_state & PPPOX_BOUND) {
                struct pppox_sock *po;