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