drivers/net/wan/hdlc_fr: Correctly handle special skb->protocol values
The fr_hard_header function is used to prepend the header to skbs before transmission. It is used in 3 situations: 1) When a control packet is generated internally in this driver; 2) When a user sends an skb on an Ethernet-emulating PVC device; 3) When a user sends an skb on a normal PVC device. These 3 situations need to be handled differently by fr_hard_header. Different headers should be prepended to the skb in different situations. Currently fr_hard_header distinguishes these 3 situations using skb->protocol. For situation 1 and 2, a special skb->protocol value will be assigned before calling fr_hard_header, so that it can recognize these 2 situations. All skb->protocol values other than these special ones are treated by fr_hard_header as situation 3. However, it is possible that in situation 3, the user sends an skb with one of the special skb->protocol values. In this case, fr_hard_header would incorrectly treat it as situation 1 or 2. This patch tries to solve this issue by using skb->dev instead of skb->protocol to distinguish between these 3 situations. For situation 1, skb->dev would be NULL; for situation 2, skb->dev->type would be ARPHRD_ETHER; and for situation 3, skb->dev->type would be ARPHRD_DLCI. This way fr_hard_header would be able to distinguish these 3 situations correctly regardless what skb->protocol value the user tries to use in situation 3. Cc: Krzysztof Halasa <khc@pm.waw.pl> Signed-off-by: Xie He <xie.he.0141@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
23a1f682a9
commit
8306266c1d
|
@ -273,63 +273,69 @@ static inline struct net_device **get_dev_p(struct pvc_device *pvc,
|
|||
|
||||
static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
|
||||
{
|
||||
u16 head_len;
|
||||
struct sk_buff *skb = *skb_p;
|
||||
|
||||
switch (skb->protocol) {
|
||||
case cpu_to_be16(NLPID_CCITT_ANSI_LMI):
|
||||
head_len = 4;
|
||||
skb_push(skb, head_len);
|
||||
skb->data[3] = NLPID_CCITT_ANSI_LMI;
|
||||
break;
|
||||
if (!skb->dev) { /* Control packets */
|
||||
switch (dlci) {
|
||||
case LMI_CCITT_ANSI_DLCI:
|
||||
skb_push(skb, 4);
|
||||
skb->data[3] = NLPID_CCITT_ANSI_LMI;
|
||||
break;
|
||||
|
||||
case cpu_to_be16(NLPID_CISCO_LMI):
|
||||
head_len = 4;
|
||||
skb_push(skb, head_len);
|
||||
skb->data[3] = NLPID_CISCO_LMI;
|
||||
break;
|
||||
case LMI_CISCO_DLCI:
|
||||
skb_push(skb, 4);
|
||||
skb->data[3] = NLPID_CISCO_LMI;
|
||||
break;
|
||||
|
||||
case cpu_to_be16(ETH_P_IP):
|
||||
head_len = 4;
|
||||
skb_push(skb, head_len);
|
||||
skb->data[3] = NLPID_IP;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case cpu_to_be16(ETH_P_IPV6):
|
||||
head_len = 4;
|
||||
skb_push(skb, head_len);
|
||||
skb->data[3] = NLPID_IPV6;
|
||||
break;
|
||||
} else if (skb->dev->type == ARPHRD_DLCI) {
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
skb_push(skb, 4);
|
||||
skb->data[3] = NLPID_IP;
|
||||
break;
|
||||
|
||||
case cpu_to_be16(ETH_P_802_3):
|
||||
head_len = 10;
|
||||
if (skb_headroom(skb) < head_len) {
|
||||
struct sk_buff *skb2 = skb_realloc_headroom(skb,
|
||||
head_len);
|
||||
case htons(ETH_P_IPV6):
|
||||
skb_push(skb, 4);
|
||||
skb->data[3] = NLPID_IPV6;
|
||||
break;
|
||||
|
||||
default:
|
||||
skb_push(skb, 10);
|
||||
skb->data[3] = FR_PAD;
|
||||
skb->data[4] = NLPID_SNAP;
|
||||
/* OUI 00-00-00 indicates an Ethertype follows */
|
||||
skb->data[5] = 0x00;
|
||||
skb->data[6] = 0x00;
|
||||
skb->data[7] = 0x00;
|
||||
/* This should be an Ethertype: */
|
||||
*(__be16 *)(skb->data + 8) = skb->protocol;
|
||||
}
|
||||
|
||||
} else if (skb->dev->type == ARPHRD_ETHER) {
|
||||
if (skb_headroom(skb) < 10) {
|
||||
struct sk_buff *skb2 = skb_realloc_headroom(skb, 10);
|
||||
if (!skb2)
|
||||
return -ENOBUFS;
|
||||
dev_kfree_skb(skb);
|
||||
skb = *skb_p = skb2;
|
||||
}
|
||||
skb_push(skb, head_len);
|
||||
skb_push(skb, 10);
|
||||
skb->data[3] = FR_PAD;
|
||||
skb->data[4] = NLPID_SNAP;
|
||||
skb->data[5] = FR_PAD;
|
||||
/* OUI 00-80-C2 stands for the 802.1 organization */
|
||||
skb->data[5] = 0x00;
|
||||
skb->data[6] = 0x80;
|
||||
skb->data[7] = 0xC2;
|
||||
/* PID 00-07 stands for Ethernet frames without FCS */
|
||||
skb->data[8] = 0x00;
|
||||
skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */
|
||||
break;
|
||||
skb->data[9] = 0x07;
|
||||
|
||||
default:
|
||||
head_len = 10;
|
||||
skb_push(skb, head_len);
|
||||
skb->data[3] = FR_PAD;
|
||||
skb->data[4] = NLPID_SNAP;
|
||||
skb->data[5] = FR_PAD;
|
||||
skb->data[6] = FR_PAD;
|
||||
skb->data[7] = FR_PAD;
|
||||
*(__be16*)(skb->data + 8) = skb->protocol;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dlci_to_q922(skb->data, dlci);
|
||||
|
@ -425,8 +431,8 @@ static netdev_tx_t pvc_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
skb_put(skb, pad);
|
||||
memset(skb->data + len, 0, pad);
|
||||
}
|
||||
skb->protocol = cpu_to_be16(ETH_P_802_3);
|
||||
}
|
||||
skb->dev = dev;
|
||||
if (!fr_hard_header(&skb, pvc->dlci)) {
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_packets++;
|
||||
|
@ -494,10 +500,8 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
|
|||
memset(skb->data, 0, len);
|
||||
skb_reserve(skb, 4);
|
||||
if (lmi == LMI_CISCO) {
|
||||
skb->protocol = cpu_to_be16(NLPID_CISCO_LMI);
|
||||
fr_hard_header(&skb, LMI_CISCO_DLCI);
|
||||
} else {
|
||||
skb->protocol = cpu_to_be16(NLPID_CCITT_ANSI_LMI);
|
||||
fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
|
||||
}
|
||||
data = skb_tail_pointer(skb);
|
||||
|
|
Loading…
Reference in New Issue