ppp: Move PFC decompression to PPP generic layer

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>
This commit is contained in:
Sam Protsenko 2018-12-20 20:29:20 +02:00 committed by David S. Miller
parent e69fbf31ca
commit 7fb1b8ca8f
5 changed files with 62 additions and 24 deletions

View 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);
}

View 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

View 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 */

View File

@ -325,11 +325,6 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
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);

View 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;