Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/kernel.h>
0003 #include <linux/netfilter.h>
0004 #include <linux/netfilter_ipv4.h>
0005 #include <linux/netfilter_ipv6.h>
0006 #include <net/netfilter/nf_queue.h>
0007 #include <net/ip6_checksum.h>
0008 
0009 #ifdef CONFIG_INET
0010 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
0011                unsigned int dataoff, u8 protocol)
0012 {
0013     const struct iphdr *iph = ip_hdr(skb);
0014     __sum16 csum = 0;
0015 
0016     switch (skb->ip_summed) {
0017     case CHECKSUM_COMPLETE:
0018         if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
0019             break;
0020         if ((protocol != IPPROTO_TCP && protocol != IPPROTO_UDP &&
0021             !csum_fold(skb->csum)) ||
0022             !csum_tcpudp_magic(iph->saddr, iph->daddr,
0023                        skb->len - dataoff, protocol,
0024                        skb->csum)) {
0025             skb->ip_summed = CHECKSUM_UNNECESSARY;
0026             break;
0027         }
0028         fallthrough;
0029     case CHECKSUM_NONE:
0030         if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
0031             skb->csum = 0;
0032         else
0033             skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
0034                                skb->len - dataoff,
0035                                protocol, 0);
0036         csum = __skb_checksum_complete(skb);
0037     }
0038     return csum;
0039 }
0040 EXPORT_SYMBOL(nf_ip_checksum);
0041 #endif
0042 
0043 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
0044                       unsigned int dataoff, unsigned int len,
0045                       u8 protocol)
0046 {
0047     const struct iphdr *iph = ip_hdr(skb);
0048     __sum16 csum = 0;
0049 
0050     switch (skb->ip_summed) {
0051     case CHECKSUM_COMPLETE:
0052         if (len == skb->len - dataoff)
0053             return nf_ip_checksum(skb, hook, dataoff, protocol);
0054         fallthrough;
0055     case CHECKSUM_NONE:
0056         skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol,
0057                            skb->len - dataoff, 0);
0058         skb->ip_summed = CHECKSUM_NONE;
0059         return __skb_checksum_complete_head(skb, dataoff + len);
0060     }
0061     return csum;
0062 }
0063 
0064 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
0065             unsigned int dataoff, u8 protocol)
0066 {
0067     const struct ipv6hdr *ip6h = ipv6_hdr(skb);
0068     __sum16 csum = 0;
0069 
0070     switch (skb->ip_summed) {
0071     case CHECKSUM_COMPLETE:
0072         if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
0073             break;
0074         if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
0075                      skb->len - dataoff, protocol,
0076                      csum_sub(skb->csum,
0077                           skb_checksum(skb, 0,
0078                                dataoff, 0)))) {
0079             skb->ip_summed = CHECKSUM_UNNECESSARY;
0080             break;
0081         }
0082         fallthrough;
0083     case CHECKSUM_NONE:
0084         skb->csum = ~csum_unfold(
0085                 csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
0086                          skb->len - dataoff,
0087                          protocol,
0088                          csum_sub(0,
0089                               skb_checksum(skb, 0,
0090                                    dataoff, 0))));
0091         csum = __skb_checksum_complete(skb);
0092     }
0093     return csum;
0094 }
0095 EXPORT_SYMBOL(nf_ip6_checksum);
0096 
0097 static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
0098                        unsigned int dataoff, unsigned int len,
0099                        u8 protocol)
0100 {
0101     const struct ipv6hdr *ip6h = ipv6_hdr(skb);
0102     __wsum hsum;
0103     __sum16 csum = 0;
0104 
0105     switch (skb->ip_summed) {
0106     case CHECKSUM_COMPLETE:
0107         if (len == skb->len - dataoff)
0108             return nf_ip6_checksum(skb, hook, dataoff, protocol);
0109         fallthrough;
0110     case CHECKSUM_NONE:
0111         hsum = skb_checksum(skb, 0, dataoff, 0);
0112         skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
0113                              &ip6h->daddr,
0114                              skb->len - dataoff,
0115                              protocol,
0116                              csum_sub(0, hsum)));
0117         skb->ip_summed = CHECKSUM_NONE;
0118         return __skb_checksum_complete_head(skb, dataoff + len);
0119     }
0120     return csum;
0121 };
0122 
0123 __sum16 nf_checksum(struct sk_buff *skb, unsigned int hook,
0124             unsigned int dataoff, u8 protocol,
0125             unsigned short family)
0126 {
0127     __sum16 csum = 0;
0128 
0129     switch (family) {
0130     case AF_INET:
0131         csum = nf_ip_checksum(skb, hook, dataoff, protocol);
0132         break;
0133     case AF_INET6:
0134         csum = nf_ip6_checksum(skb, hook, dataoff, protocol);
0135         break;
0136     }
0137 
0138     return csum;
0139 }
0140 EXPORT_SYMBOL_GPL(nf_checksum);
0141 
0142 __sum16 nf_checksum_partial(struct sk_buff *skb, unsigned int hook,
0143                 unsigned int dataoff, unsigned int len,
0144                 u8 protocol, unsigned short family)
0145 {
0146     __sum16 csum = 0;
0147 
0148     switch (family) {
0149     case AF_INET:
0150         csum = nf_ip_checksum_partial(skb, hook, dataoff, len,
0151                           protocol);
0152         break;
0153     case AF_INET6:
0154         csum = nf_ip6_checksum_partial(skb, hook, dataoff, len,
0155                            protocol);
0156         break;
0157     }
0158 
0159     return csum;
0160 }
0161 EXPORT_SYMBOL_GPL(nf_checksum_partial);
0162 
0163 int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
0164          bool strict, unsigned short family)
0165 {
0166     const struct nf_ipv6_ops *v6ops __maybe_unused;
0167     int ret = 0;
0168 
0169     switch (family) {
0170     case AF_INET:
0171         ret = nf_ip_route(net, dst, fl, strict);
0172         break;
0173     case AF_INET6:
0174         ret = nf_ip6_route(net, dst, fl, strict);
0175         break;
0176     }
0177 
0178     return ret;
0179 }
0180 EXPORT_SYMBOL_GPL(nf_route);
0181 
0182 static int nf_ip_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry)
0183 {
0184 #ifdef CONFIG_INET
0185     const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
0186 
0187     if (entry->state.hook == NF_INET_LOCAL_OUT) {
0188         const struct iphdr *iph = ip_hdr(skb);
0189 
0190         if (!(iph->tos == rt_info->tos &&
0191               skb->mark == rt_info->mark &&
0192               iph->daddr == rt_info->daddr &&
0193               iph->saddr == rt_info->saddr))
0194             return ip_route_me_harder(entry->state.net, entry->state.sk,
0195                           skb, RTN_UNSPEC);
0196     }
0197 #endif
0198     return 0;
0199 }
0200 
0201 int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)
0202 {
0203     const struct nf_ipv6_ops *v6ops;
0204     int ret = 0;
0205 
0206     switch (entry->state.pf) {
0207     case AF_INET:
0208         ret = nf_ip_reroute(skb, entry);
0209         break;
0210     case AF_INET6:
0211         v6ops = rcu_dereference(nf_ipv6_ops);
0212         if (v6ops)
0213             ret = v6ops->reroute(skb, entry);
0214         break;
0215     }
0216     return ret;
0217 }