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 8 #ifdef CONFIG_INET 9 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, 10 unsigned int dataoff, u8 protocol) 11 { 12 const struct iphdr *iph = ip_hdr(skb); 13 __sum16 csum = 0; 14 15 switch (skb->ip_summed) { 16 case CHECKSUM_COMPLETE: 17 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN) 18 break; 19 if ((protocol == 0 && !csum_fold(skb->csum)) || 20 !csum_tcpudp_magic(iph->saddr, iph->daddr, 21 skb->len - dataoff, protocol, 22 skb->csum)) { 23 skb->ip_summed = CHECKSUM_UNNECESSARY; 24 break; 25 } 26 /* fall through */ 27 case CHECKSUM_NONE: 28 if (protocol == 0) 29 skb->csum = 0; 30 else 31 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, 32 skb->len - dataoff, 33 protocol, 0); 34 csum = __skb_checksum_complete(skb); 35 } 36 return csum; 37 } 38 EXPORT_SYMBOL(nf_ip_checksum); 39 #endif 40 41 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook, 42 unsigned int dataoff, unsigned int len, 43 u8 protocol) 44 { 45 const struct iphdr *iph = ip_hdr(skb); 46 __sum16 csum = 0; 47 48 switch (skb->ip_summed) { 49 case CHECKSUM_COMPLETE: 50 if (len == skb->len - dataoff) 51 return nf_ip_checksum(skb, hook, dataoff, protocol); 52 /* fall through */ 53 case CHECKSUM_NONE: 54 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol, 55 skb->len - dataoff, 0); 56 skb->ip_summed = CHECKSUM_NONE; 57 return __skb_checksum_complete_head(skb, dataoff + len); 58 } 59 return csum; 60 } 61 62 __sum16 nf_checksum(struct sk_buff *skb, unsigned int hook, 63 unsigned int dataoff, u_int8_t protocol, 64 unsigned short family) 65 { 66 const struct nf_ipv6_ops *v6ops; 67 __sum16 csum = 0; 68 69 switch (family) { 70 case AF_INET: 71 csum = nf_ip_checksum(skb, hook, dataoff, protocol); 72 break; 73 case AF_INET6: 74 v6ops = rcu_dereference(nf_ipv6_ops); 75 if (v6ops) 76 csum = v6ops->checksum(skb, hook, dataoff, protocol); 77 break; 78 } 79 80 return csum; 81 } 82 EXPORT_SYMBOL_GPL(nf_checksum); 83 84 __sum16 nf_checksum_partial(struct sk_buff *skb, unsigned int hook, 85 unsigned int dataoff, unsigned int len, 86 u_int8_t protocol, unsigned short family) 87 { 88 const struct nf_ipv6_ops *v6ops; 89 __sum16 csum = 0; 90 91 switch (family) { 92 case AF_INET: 93 csum = nf_ip_checksum_partial(skb, hook, dataoff, len, 94 protocol); 95 break; 96 case AF_INET6: 97 v6ops = rcu_dereference(nf_ipv6_ops); 98 if (v6ops) 99 csum = v6ops->checksum_partial(skb, hook, dataoff, len, 100 protocol); 101 break; 102 } 103 104 return csum; 105 } 106 EXPORT_SYMBOL_GPL(nf_checksum_partial); 107 108 int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl, 109 bool strict, unsigned short family) 110 { 111 const struct nf_ipv6_ops *v6ops; 112 int ret = 0; 113 114 switch (family) { 115 case AF_INET: 116 ret = nf_ip_route(net, dst, fl, strict); 117 break; 118 case AF_INET6: 119 v6ops = rcu_dereference(nf_ipv6_ops); 120 if (v6ops) 121 ret = v6ops->route(net, dst, fl, strict); 122 break; 123 } 124 125 return ret; 126 } 127 EXPORT_SYMBOL_GPL(nf_route); 128 129 int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry) 130 { 131 const struct nf_ipv6_ops *v6ops; 132 int ret = 0; 133 134 switch (entry->state.pf) { 135 case AF_INET: 136 ret = nf_ip_reroute(skb, entry); 137 break; 138 case AF_INET6: 139 v6ops = rcu_dereference(nf_ipv6_ops); 140 if (v6ops) 141 ret = v6ops->reroute(skb, entry); 142 break; 143 } 144 return ret; 145 } 146