Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -3906,6 +3906,12 @@ static inline bool skb_is_gso_v6(const struct sk_buff *skb)
return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6;
}

/* Note: Should be called only if skb_is_gso(skb) is true */
static inline bool skb_is_gso_sctp(const struct sk_buff *skb)
{
return skb_shinfo(skb)->gso_type & SKB_GSO_SCTP;
}

void __skb_warn_lro_forwarding(const struct sk_buff *skb);

static inline bool skb_warn_if_lro(const struct sk_buff *skb)
Expand Down Expand Up @@ -4023,5 +4029,10 @@ static inline __wsum lco_csum(struct sk_buff *skb)
return csum_partial(l4_hdr, csum_start - l4_hdr, partial);
}

static inline bool skb_csum_is_sctp(struct sk_buff *skb)
{
return skb->csum_not_inet;
}

#endif /* __KERNEL__ */
#endif /* _LINUX_SKBUFF_H */
7 changes: 7 additions & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
return ip_hdr(skb)->ihl * 4;
}

static inline int ip_check_csum_start(const struct sk_buff *skb)
{
if (unlikely(skb->csum_start < skb->network_header + ip_hdrlen(skb)))
return -EINVAL;
return 0;
}

struct ipcm_cookie {
__be32 addr;
int oif;
Expand Down
1 change: 1 addition & 0 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2825,6 +2825,7 @@ int skb_crc32c_csum_help(struct sk_buff *skb)
out:
return ret;
}
EXPORT_SYMBOL(skb_crc32c_csum_help);

__be16 skb_network_protocol(struct sk_buff *skb, int *depth)
{
Expand Down
10 changes: 7 additions & 3 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,13 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
dev = rt->dst.dev;

/* for offloaded checksums cleanup checksum before fragmentation */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
goto fail;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
err = ip_check_csum_start(skb);
if (!err)
err = skb_checksum_help(skb);
if (err)
goto fail;
}

/*
* Point into the IP datagram header.
Expand Down
19 changes: 17 additions & 2 deletions net/netfilter/nfnetlink_queue_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <linux/list.h>
#include <net/ip.h>
#include <net/sock.h>
#include <net/netfilter/nf_queue.h>
#include <net/netns/generic.h>
Expand Down Expand Up @@ -245,6 +246,20 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0;
}

static int nf_queue_checksum_help(struct sk_buff *entskb)
{
if (entskb->protocol == htons(ETH_P_IP)) {
if (!pskb_network_may_pull(entskb, sizeof(struct iphdr)) ||
ip_check_csum_start(entskb))
return -EINVAL;
}

if (skb_csum_is_sctp(entskb))
return skb_crc32c_csum_help(entskb);

return skb_checksum_help(entskb);
}

static struct sk_buff *
nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
Expand Down Expand Up @@ -297,7 +312,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
case NFQNL_COPY_PACKET:
if (!(queue->flags & NFQA_CFG_F_GSO) &&
entskb->ip_summed == CHECKSUM_PARTIAL &&
skb_checksum_help(entskb))
nf_queue_checksum_help(entskb))
return NULL;

data_len = ACCESS_ONCE(queue->copy_range);
Expand Down Expand Up @@ -614,7 +629,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
break;
}

if ((queue->flags & NFQA_CFG_F_GSO) || !skb_is_gso(skb))
if (!skb_is_gso(skb) || ((queue->flags & NFQA_CFG_F_GSO) && !skb_is_gso_sctp(skb)))
return __nfqnl_enqueue_packet(net, queue, entry);

nf_bridge_adjust_skb_data(skb);
Expand Down
6 changes: 5 additions & 1 deletion net/netfilter/xt_CHECKSUM.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_CHECKSUM.h>
#include <net/ip.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael S. Tsirkin <mst@redhat.com>");
Expand All @@ -25,8 +26,11 @@ MODULE_ALIAS("ip6t_CHECKSUM");
static unsigned int
checksum_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
if (skb->ip_summed == CHECKSUM_PARTIAL)
if (skb->ip_summed == CHECKSUM_PARTIAL) {
if (xt_family(par) == NFPROTO_IPV4 && ip_check_csum_start(skb))
return NF_DROP;
skb_checksum_help(skb);
}

return XT_CONTINUE;
}
Expand Down
16 changes: 11 additions & 5 deletions net/xfrm/xfrm_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,21 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
return xfrm_output_gso(sk, skb);

if (skb->ip_summed == CHECKSUM_PARTIAL) {
err = skb_checksum_help(skb);
if (err) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
kfree_skb(skb);
return err;
if (skb->protocol == htons(ETH_P_IP)) {
err = ip_check_csum_start(skb);
if (err)
goto error;
}
err = skb_checksum_help(skb);
if (err)
goto error;
}

return xfrm_output2(sk, skb);
error:
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
kfree_skb(skb);
return err;
}

int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
Expand Down
Loading