1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/kernel.h> 3 #include <linux/netfilter.h> 4 #include <linux/netfilter_ipv4.h> 5 #include <linux/netfilter_ipv6.h> 6 #include <net/netfilter/nf_queue.h> 7 #include <net/ip6_checksum.h> 8 9 #ifdef CONFIG_INET 10 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, 11 unsigned int dataoff, u8 protocol) 12 { 13 const struct iphdr *iph = ip_hdr(skb); 14 __sum16 csum = 0; 15 16 switch (skb->ip_summed) { 17 case CHECKSUM_COMPLETE: 18 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN) 19 break; 20 if ((protocol == 0 && !csum_fold(skb->csum)) || 21 !csum_tcpudp_magic(iph->saddr, iph->daddr, 22 skb->len - dataoff, protocol, 23 skb->csum)) { 24 skb->ip_summed = CHECKSUM_UNNECESSARY; 25 break; 26 } 27 /* fall through */ 28 case CHECKSUM_NONE: 29 if (protocol == 0) 30 skb->csum = 0; 31 else 32 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, 33 skb->len - dataoff, 34 protocol, 0); 35 csum = __skb_checksum_complete(skb); 36 } 37 return csum; 38 } 39 EXPORT_SYMBOL(nf_ip_checksum); 40 #endif 41 42 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook, 43 unsigned int dataoff, unsigned int len, 44 u8 protocol) 45 { 46 const struct iphdr *iph = ip_hdr(skb); 47 __sum16 csum = 0; 48 49 switch (skb->ip_summed) { 50 case CHECKSUM_COMPLETE: 51 if (len == skb->len - dataoff) 52 return nf_ip_checksum(skb, hook, dataoff, protocol); 53 /* fall through */ 54 case CHECKSUM_NONE: 55 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol, 56 skb->len - dataoff, 0); 57 skb->ip_summed = CHECKSUM_NONE; 58 return __skb_checksum_complete_head(skb, dataoff + len); 59 } 60 return csum; 61 } 62 63 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, 64 unsigned int dataoff, u8 protocol) 65 { 66 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 67 __sum16 csum = 0; 68 69 switch (skb->ip_summed) { 70 case CHECKSUM_COMPLETE: 71 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN) 72 break; 73 if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 74 skb->len - dataoff, protocol, 75 csum_sub(skb->csum, 76 skb_checksum(skb, 0, 77 dataoff, 0)))) { 78 skb->ip_summed = CHECKSUM_UNNECESSARY; 79 break; 80 } 81 /* fall through */ 82 case CHECKSUM_NONE: 83 skb->csum = ~csum_unfold( 84 csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 85 skb->len - dataoff, 86 protocol, 87 csum_sub(0, 88 skb_checksum(skb, 0, 89 dataoff, 0)))); 90 csum = __skb_checksum_complete(skb); 91 } 92 return csum; 93 } 94 EXPORT_SYMBOL(nf_ip6_checksum); 95 96 static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook, 97 unsigned int dataoff, unsigned int len, 98 u8 protocol) 99 { 100 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 101 __wsum hsum; 102 __sum16 csum = 0; 103 104 switch (skb->ip_summed) { 105 case CHECKSUM_COMPLETE: 106 if (len == skb->len - dataoff) 107 return nf_ip6_checksum(skb, hook, dataoff, protocol); 108 /* fall through */ 109 case CHECKSUM_NONE: 110 hsum = skb_checksum(skb, 0, dataoff, 0); 111 skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, 112 &ip6h->daddr, 113 skb->len - dataoff, 114 protocol, 115 csum_sub(0, hsum))); 116 skb->ip_summed = CHECKSUM_NONE; 117 return __skb_checksum_complete_head(skb, dataoff + len); 118 } 119 return csum; 120 }; 121 122 __sum16 nf_checksum(struct sk_buff *skb, unsigned int hook, 123 unsigned int dataoff, u8 protocol, 124 unsigned short family) 125 { 126 __sum16 csum = 0; 127 128 switch (family) { 129 case AF_INET: 130 csum = nf_ip_checksum(skb, hook, dataoff, protocol); 131 break; 132 case AF_INET6: 133 csum = nf_ip6_checksum(skb, hook, dataoff, protocol); 134 break; 135 } 136 137 return csum; 138 } 139 EXPORT_SYMBOL_GPL(nf_checksum); 140 141 __sum16 nf_checksum_partial(struct sk_buff *skb, unsigned int hook, 142 unsigned int dataoff, unsigned int len, 143 u8 protocol, unsigned short family) 144 { 145 __sum16 csum = 0; 146 147 switch (family) { 148 case AF_INET: 149 csum = nf_ip_checksum_partial(skb, hook, dataoff, len, 150 protocol); 151 break; 152 case AF_INET6: 153 csum = nf_ip6_checksum_partial(skb, hook, dataoff, len, 154 protocol); 155 break; 156 } 157 158 return csum; 159 } 160 EXPORT_SYMBOL_GPL(nf_checksum_partial); 161 162 int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl, 163 bool strict, unsigned short family) 164 { 165 const struct nf_ipv6_ops *v6ops; 166 int ret = 0; 167 168 switch (family) { 169 case AF_INET: 170 ret = nf_ip_route(net, dst, fl, strict); 171 break; 172 case AF_INET6: 173 v6ops = rcu_dereference(nf_ipv6_ops); 174 if (v6ops) 175 ret = v6ops->route(net, dst, fl, strict); 176 break; 177 } 178 179 return ret; 180 } 181 EXPORT_SYMBOL_GPL(nf_route); 182 183 int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry) 184 { 185 const struct nf_ipv6_ops *v6ops; 186 int ret = 0; 187 188 switch (entry->state.pf) { 189 case AF_INET: 190 ret = nf_ip_reroute(skb, entry); 191 break; 192 case AF_INET6: 193 v6ops = rcu_dereference(nf_ipv6_ops); 194 if (v6ops) 195 ret = v6ops->reroute(skb, entry); 196 break; 197 } 198 return ret; 199 } 200