• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* (C) 1999-2001 Paul `Rusty' Russell
3  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
4  */
5 
6 #include <linux/types.h>
7 #include <linux/ip.h>
8 #include <linux/netfilter.h>
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <net/netns/generic.h>
12 #include <net/route.h>
13 #include <net/ip.h>
14 
15 #include <linux/netfilter_bridge.h>
16 #include <linux/netfilter_ipv4.h>
17 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
18 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
19 #include <net/netfilter/nf_conntrack.h>
20 #endif
21 #include <net/netfilter/nf_conntrack_zones.h>
22 
23 static DEFINE_MUTEX(defrag4_mutex);
24 
nf_ct_ipv4_gather_frags(struct net * net,struct sk_buff * skb,u_int32_t user)25 static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb,
26 				   u_int32_t user)
27 {
28 	int err;
29 
30 	local_bh_disable();
31 	err = ip_defrag(net, skb, user);
32 	local_bh_enable();
33 
34 	if (!err)
35 		skb->ignore_df = 1;
36 
37 	return err;
38 }
39 
nf_ct_defrag_user(unsigned int hooknum,struct sk_buff * skb)40 static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum,
41 					      struct sk_buff *skb)
42 {
43 	u16 zone_id = NF_CT_DEFAULT_ZONE_ID;
44 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
45 	if (skb_nfct(skb)) {
46 		enum ip_conntrack_info ctinfo;
47 		const struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
48 
49 		zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo));
50 	}
51 #endif
52 	if (nf_bridge_in_prerouting(skb))
53 		return IP_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id;
54 
55 	if (hooknum == NF_INET_PRE_ROUTING)
56 		return IP_DEFRAG_CONNTRACK_IN + zone_id;
57 	else
58 		return IP_DEFRAG_CONNTRACK_OUT + zone_id;
59 }
60 
ipv4_conntrack_defrag(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)61 static unsigned int ipv4_conntrack_defrag(void *priv,
62 					  struct sk_buff *skb,
63 					  const struct nf_hook_state *state)
64 {
65 	struct sock *sk = skb->sk;
66 
67 	if (sk && sk_fullsock(sk) && (sk->sk_family == PF_INET) &&
68 	    inet_sk(sk)->nodefrag)
69 		return NF_ACCEPT;
70 
71 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
72 #if !IS_ENABLED(CONFIG_NF_NAT)
73 	/* Previously seen (loopback)?  Ignore.  Do this before
74 	   fragment check. */
75 	if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb)))
76 		return NF_ACCEPT;
77 #endif
78 	if (skb->_nfct == IP_CT_UNTRACKED)
79 		return NF_ACCEPT;
80 #endif
81 	/* Gather fragments. */
82 	if (ip_is_fragment(ip_hdr(skb))) {
83 		enum ip_defrag_users user =
84 			nf_ct_defrag_user(state->hook, skb);
85 
86 		if (nf_ct_ipv4_gather_frags(state->net, skb, user))
87 			return NF_STOLEN;
88 	}
89 	return NF_ACCEPT;
90 }
91 
92 static const struct nf_hook_ops ipv4_defrag_ops[] = {
93 	{
94 		.hook		= ipv4_conntrack_defrag,
95 		.pf		= NFPROTO_IPV4,
96 		.hooknum	= NF_INET_PRE_ROUTING,
97 		.priority	= NF_IP_PRI_CONNTRACK_DEFRAG,
98 	},
99 	{
100 		.hook           = ipv4_conntrack_defrag,
101 		.pf             = NFPROTO_IPV4,
102 		.hooknum        = NF_INET_LOCAL_OUT,
103 		.priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
104 	},
105 };
106 
defrag4_net_exit(struct net * net)107 static void __net_exit defrag4_net_exit(struct net *net)
108 {
109 	if (net->nf.defrag_ipv4_users) {
110 		nf_unregister_net_hooks(net, ipv4_defrag_ops,
111 					ARRAY_SIZE(ipv4_defrag_ops));
112 		net->nf.defrag_ipv4_users = 0;
113 	}
114 }
115 
116 static struct pernet_operations defrag4_net_ops = {
117 	.exit = defrag4_net_exit,
118 };
119 
nf_defrag_init(void)120 static int __init nf_defrag_init(void)
121 {
122 	return register_pernet_subsys(&defrag4_net_ops);
123 }
124 
nf_defrag_fini(void)125 static void __exit nf_defrag_fini(void)
126 {
127 	unregister_pernet_subsys(&defrag4_net_ops);
128 }
129 
nf_defrag_ipv4_enable(struct net * net)130 int nf_defrag_ipv4_enable(struct net *net)
131 {
132 	int err = 0;
133 
134 	mutex_lock(&defrag4_mutex);
135 	if (net->nf.defrag_ipv4_users == UINT_MAX) {
136 		err = -EOVERFLOW;
137 		goto out_unlock;
138 	}
139 
140 	if (net->nf.defrag_ipv4_users) {
141 		net->nf.defrag_ipv4_users++;
142 		goto out_unlock;
143 	}
144 
145 	err = nf_register_net_hooks(net, ipv4_defrag_ops,
146 				    ARRAY_SIZE(ipv4_defrag_ops));
147 	if (err == 0)
148 		net->nf.defrag_ipv4_users = 1;
149 
150  out_unlock:
151 	mutex_unlock(&defrag4_mutex);
152 	return err;
153 }
154 EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable);
155 
nf_defrag_ipv4_disable(struct net * net)156 void nf_defrag_ipv4_disable(struct net *net)
157 {
158 	mutex_lock(&defrag4_mutex);
159 	if (net->nf.defrag_ipv4_users) {
160 		net->nf.defrag_ipv4_users--;
161 		if (net->nf.defrag_ipv4_users == 0)
162 			nf_unregister_net_hooks(net, ipv4_defrag_ops,
163 						ARRAY_SIZE(ipv4_defrag_ops));
164 	}
165 
166 	mutex_unlock(&defrag4_mutex);
167 }
168 EXPORT_SYMBOL_GPL(nf_defrag_ipv4_disable);
169 
170 module_init(nf_defrag_init);
171 module_exit(nf_defrag_fini);
172 
173 MODULE_LICENSE("GPL");
174