Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #ifndef _IPV6_FRAG_H
0003 #define _IPV6_FRAG_H
0004 #include <linux/icmpv6.h>
0005 #include <linux/kernel.h>
0006 #include <net/addrconf.h>
0007 #include <net/ipv6.h>
0008 #include <net/inet_frag.h>
0009 
0010 enum ip6_defrag_users {
0011     IP6_DEFRAG_LOCAL_DELIVER,
0012     IP6_DEFRAG_CONNTRACK_IN,
0013     __IP6_DEFRAG_CONNTRACK_IN   = IP6_DEFRAG_CONNTRACK_IN + USHRT_MAX,
0014     IP6_DEFRAG_CONNTRACK_OUT,
0015     __IP6_DEFRAG_CONNTRACK_OUT  = IP6_DEFRAG_CONNTRACK_OUT + USHRT_MAX,
0016     IP6_DEFRAG_CONNTRACK_BRIDGE_IN,
0017     __IP6_DEFRAG_CONNTRACK_BRIDGE_IN = IP6_DEFRAG_CONNTRACK_BRIDGE_IN + USHRT_MAX,
0018 };
0019 
0020 /*
0021  *  Equivalent of ipv4 struct ip
0022  */
0023 struct frag_queue {
0024     struct inet_frag_queue  q;
0025 
0026     int         iif;
0027     __u16           nhoffset;
0028     u8          ecn;
0029 };
0030 
0031 #if IS_ENABLED(CONFIG_IPV6)
0032 static inline void ip6frag_init(struct inet_frag_queue *q, const void *a)
0033 {
0034     struct frag_queue *fq = container_of(q, struct frag_queue, q);
0035     const struct frag_v6_compare_key *key = a;
0036 
0037     q->key.v6 = *key;
0038     fq->ecn = 0;
0039 }
0040 
0041 static inline u32 ip6frag_key_hashfn(const void *data, u32 len, u32 seed)
0042 {
0043     return jhash2(data,
0044               sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
0045 }
0046 
0047 static inline u32 ip6frag_obj_hashfn(const void *data, u32 len, u32 seed)
0048 {
0049     const struct inet_frag_queue *fq = data;
0050 
0051     return jhash2((const u32 *)&fq->key.v6,
0052               sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
0053 }
0054 
0055 static inline int
0056 ip6frag_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
0057 {
0058     const struct frag_v6_compare_key *key = arg->key;
0059     const struct inet_frag_queue *fq = ptr;
0060 
0061     return !!memcmp(&fq->key, key, sizeof(*key));
0062 }
0063 
0064 static inline void
0065 ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
0066 {
0067     struct net_device *dev = NULL;
0068     struct sk_buff *head;
0069 
0070     rcu_read_lock();
0071     /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
0072     if (READ_ONCE(fq->q.fqdir->dead))
0073         goto out_rcu_unlock;
0074     spin_lock(&fq->q.lock);
0075 
0076     if (fq->q.flags & INET_FRAG_COMPLETE)
0077         goto out;
0078 
0079     inet_frag_kill(&fq->q);
0080 
0081     dev = dev_get_by_index_rcu(net, fq->iif);
0082     if (!dev)
0083         goto out;
0084 
0085     __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
0086     __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
0087 
0088     /* Don't send error if the first segment did not arrive. */
0089     if (!(fq->q.flags & INET_FRAG_FIRST_IN))
0090         goto out;
0091 
0092     /* sk_buff::dev and sk_buff::rbnode are unionized. So we
0093      * pull the head out of the tree in order to be able to
0094      * deal with head->dev.
0095      */
0096     head = inet_frag_pull_head(&fq->q);
0097     if (!head)
0098         goto out;
0099 
0100     head->dev = dev;
0101     spin_unlock(&fq->q.lock);
0102 
0103     icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
0104     kfree_skb(head);
0105     goto out_rcu_unlock;
0106 
0107 out:
0108     spin_unlock(&fq->q.lock);
0109 out_rcu_unlock:
0110     rcu_read_unlock();
0111     inet_frag_put(&fq->q);
0112 }
0113 
0114 /* Check if the upper layer header is truncated in the first fragment. */
0115 static inline bool
0116 ipv6frag_thdr_truncated(struct sk_buff *skb, int start, u8 *nexthdrp)
0117 {
0118     u8 nexthdr = *nexthdrp;
0119     __be16 frag_off;
0120     int offset;
0121 
0122     offset = ipv6_skip_exthdr(skb, start, &nexthdr, &frag_off);
0123     if (offset < 0 || (frag_off & htons(IP6_OFFSET)))
0124         return false;
0125     switch (nexthdr) {
0126     case NEXTHDR_TCP:
0127         offset += sizeof(struct tcphdr);
0128         break;
0129     case NEXTHDR_UDP:
0130         offset += sizeof(struct udphdr);
0131         break;
0132     case NEXTHDR_ICMP:
0133         offset += sizeof(struct icmp6hdr);
0134         break;
0135     default:
0136         offset += 1;
0137     }
0138     if (offset > skb->len)
0139         return true;
0140     return false;
0141 }
0142 
0143 #endif
0144 #endif