1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * sctp_offload - GRO/GSO Offloading for SCTP 4 * 5 * Copyright (C) 2015, Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/kernel.h> 11 #include <linux/kprobes.h> 12 #include <linux/socket.h> 13 #include <linux/sctp.h> 14 #include <linux/proc_fs.h> 15 #include <linux/vmalloc.h> 16 #include <linux/module.h> 17 #include <linux/kfifo.h> 18 #include <linux/time.h> 19 #include <net/net_namespace.h> 20 21 #include <linux/skbuff.h> 22 #include <net/sctp/sctp.h> 23 #include <net/sctp/checksum.h> 24 #include <net/protocol.h> 25 26 static __le32 sctp_gso_make_checksum(struct sk_buff *skb) 27 { 28 skb->ip_summed = CHECKSUM_NONE; 29 skb->csum_not_inet = 0; 30 gso_reset_checksum(skb, ~0); 31 return sctp_compute_cksum(skb, skb_transport_offset(skb)); 32 } 33 34 static struct sk_buff *sctp_gso_segment(struct sk_buff *skb, 35 netdev_features_t features) 36 { 37 struct sk_buff *segs = ERR_PTR(-EINVAL); 38 struct sctphdr *sh; 39 40 if (!skb_is_gso_sctp(skb)) 41 goto out; 42 43 sh = sctp_hdr(skb); 44 if (!pskb_may_pull(skb, sizeof(*sh))) 45 goto out; 46 47 __skb_pull(skb, sizeof(*sh)); 48 49 if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { 50 /* Packet is from an untrusted source, reset gso_segs. */ 51 struct skb_shared_info *pinfo = skb_shinfo(skb); 52 struct sk_buff *frag_iter; 53 54 pinfo->gso_segs = 0; 55 if (skb->len != skb->data_len) { 56 /* Means we have chunks in here too */ 57 pinfo->gso_segs++; 58 } 59 60 skb_walk_frags(skb, frag_iter) 61 pinfo->gso_segs++; 62 63 segs = NULL; 64 goto out; 65 } 66 67 segs = skb_segment(skb, features | NETIF_F_HW_CSUM | NETIF_F_SG); 68 if (IS_ERR(segs)) 69 goto out; 70 71 /* All that is left is update SCTP CRC if necessary */ 72 if (!(features & NETIF_F_SCTP_CRC)) { 73 for (skb = segs; skb; skb = skb->next) { 74 if (skb->ip_summed == CHECKSUM_PARTIAL) { 75 sh = sctp_hdr(skb); 76 sh->checksum = sctp_gso_make_checksum(skb); 77 } 78 } 79 } 80 81 out: 82 return segs; 83 } 84 85 static const struct net_offload sctp_offload = { 86 .callbacks = { 87 .gso_segment = sctp_gso_segment, 88 }, 89 }; 90 91 static const struct net_offload sctp6_offload = { 92 .callbacks = { 93 .gso_segment = sctp_gso_segment, 94 }, 95 }; 96 97 int __init sctp_offload_init(void) 98 { 99 int ret; 100 101 ret = inet_add_offload(&sctp_offload, IPPROTO_SCTP); 102 if (ret) 103 goto out; 104 105 ret = inet6_add_offload(&sctp6_offload, IPPROTO_SCTP); 106 if (ret) 107 goto ipv4; 108 109 crc32c_csum_stub = &sctp_csum_ops; 110 return ret; 111 112 ipv4: 113 inet_del_offload(&sctp_offload, IPPROTO_SCTP); 114 out: 115 return ret; 116 } 117