|
@@ -48,7 +48,8 @@ static __sum16 *rmnet_map_get_csum_field(unsigned char protocol,
|
|
|
|
|
|
static int
|
|
|
rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
|
|
|
- struct rmnet_map_dl_csum_trailer *csum_trailer)
|
|
|
+ struct rmnet_map_dl_csum_trailer *csum_trailer,
|
|
|
+ struct rmnet_priv *priv)
|
|
|
{
|
|
|
__sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum;
|
|
|
u16 csum_value, csum_value_final;
|
|
@@ -58,19 +59,25 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
|
|
|
|
|
|
ip4h = (struct iphdr *)(skb->data);
|
|
|
if ((ntohs(ip4h->frag_off) & IP_MF) ||
|
|
|
- ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0))
|
|
|
+ ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) {
|
|
|
+ priv->stats.csum_fragmented_pkt++;
|
|
|
return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
|
|
|
txporthdr = skb->data + ip4h->ihl * 4;
|
|
|
|
|
|
csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr);
|
|
|
|
|
|
- if (!csum_field)
|
|
|
+ if (!csum_field) {
|
|
|
+ priv->stats.csum_err_invalid_transport++;
|
|
|
return -EPROTONOSUPPORT;
|
|
|
+ }
|
|
|
|
|
|
/* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */
|
|
|
- if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP)
|
|
|
+ if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) {
|
|
|
+ priv->stats.csum_skipped++;
|
|
|
return 0;
|
|
|
+ }
|
|
|
|
|
|
csum_value = ~ntohs(csum_trailer->csum_value);
|
|
|
hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl);
|
|
@@ -102,16 +109,20 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (csum_value_final == ntohs((__force __be16)*csum_field))
|
|
|
+ if (csum_value_final == ntohs((__force __be16)*csum_field)) {
|
|
|
+ priv->stats.csum_ok++;
|
|
|
return 0;
|
|
|
- else
|
|
|
+ } else {
|
|
|
+ priv->stats.csum_validation_failed++;
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
static int
|
|
|
rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
|
|
|
- struct rmnet_map_dl_csum_trailer *csum_trailer)
|
|
|
+ struct rmnet_map_dl_csum_trailer *csum_trailer,
|
|
|
+ struct rmnet_priv *priv)
|
|
|
{
|
|
|
__sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp;
|
|
|
u16 csum_value, csum_value_final;
|
|
@@ -125,8 +136,10 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
|
|
|
txporthdr = skb->data + sizeof(struct ipv6hdr);
|
|
|
csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr);
|
|
|
|
|
|
- if (!csum_field)
|
|
|
+ if (!csum_field) {
|
|
|
+ priv->stats.csum_err_invalid_transport++;
|
|
|
return -EPROTONOSUPPORT;
|
|
|
+ }
|
|
|
|
|
|
csum_value = ~ntohs(csum_trailer->csum_value);
|
|
|
ip6_hdr_csum = (__force __be16)
|
|
@@ -164,10 +177,13 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (csum_value_final == ntohs((__force __be16)*csum_field))
|
|
|
+ if (csum_value_final == ntohs((__force __be16)*csum_field)) {
|
|
|
+ priv->stats.csum_ok++;
|
|
|
return 0;
|
|
|
- else
|
|
|
+ } else {
|
|
|
+ priv->stats.csum_validation_failed++;
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
}
|
|
|
#endif
|
|
|
|
|
@@ -339,24 +355,34 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
|
|
|
*/
|
|
|
int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len)
|
|
|
{
|
|
|
+ struct rmnet_priv *priv = netdev_priv(skb->dev);
|
|
|
struct rmnet_map_dl_csum_trailer *csum_trailer;
|
|
|
|
|
|
- if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM)))
|
|
|
+ if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) {
|
|
|
+ priv->stats.csum_sw++;
|
|
|
return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
|
|
|
csum_trailer = (struct rmnet_map_dl_csum_trailer *)(skb->data + len);
|
|
|
|
|
|
- if (!csum_trailer->valid)
|
|
|
+ if (!csum_trailer->valid) {
|
|
|
+ priv->stats.csum_valid_unset++;
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- if (skb->protocol == htons(ETH_P_IP))
|
|
|
- return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer);
|
|
|
- else if (skb->protocol == htons(ETH_P_IPV6))
|
|
|
+ if (skb->protocol == htons(ETH_P_IP)) {
|
|
|
+ return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer, priv);
|
|
|
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
- return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer);
|
|
|
+ return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer, priv);
|
|
|
#else
|
|
|
+ priv->stats.csum_err_invalid_ip_version++;
|
|
|
return -EPROTONOSUPPORT;
|
|
|
#endif
|
|
|
+ } else {
|
|
|
+ priv->stats.csum_err_invalid_ip_version++;
|
|
|
+ return -EPROTONOSUPPORT;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -367,6 +393,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len)
|
|
|
void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
|
|
|
struct net_device *orig_dev)
|
|
|
{
|
|
|
+ struct rmnet_priv *priv = netdev_priv(orig_dev);
|
|
|
struct rmnet_map_ul_csum_header *ul_header;
|
|
|
void *iphdr;
|
|
|
|
|
@@ -389,8 +416,11 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
|
|
|
rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
|
|
|
return;
|
|
|
#else
|
|
|
+ priv->stats.csum_err_invalid_ip_version++;
|
|
|
goto sw_csum;
|
|
|
#endif
|
|
|
+ } else {
|
|
|
+ priv->stats.csum_err_invalid_ip_version++;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -399,4 +429,6 @@ sw_csum:
|
|
|
ul_header->csum_insert_offset = 0;
|
|
|
ul_header->csum_enabled = 0;
|
|
|
ul_header->udp_ip4_ind = 0;
|
|
|
+
|
|
|
+ priv->stats.csum_sw++;
|
|
|
}
|