~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/xt_CT.c

Version: ~ [ linux-5.15-rc1 ] ~ [ linux-5.14.5 ] ~ [ linux-5.13.18 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.66 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.147 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.206 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.246 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.282 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.283 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
  3  *
  4  * This program is free software; you can redistribute it and/or modify
  5  * it under the terms of the GNU General Public License version 2 as
  6  * published by the Free Software Foundation.
  7  */
  8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9 #include <linux/module.h>
 10 #include <linux/gfp.h>
 11 #include <linux/skbuff.h>
 12 #include <linux/netfilter_ipv4/ip_tables.h>
 13 #include <linux/netfilter_ipv6/ip6_tables.h>
 14 #include <linux/netfilter/x_tables.h>
 15 #include <linux/netfilter/xt_CT.h>
 16 #include <net/netfilter/nf_conntrack.h>
 17 #include <net/netfilter/nf_conntrack_l4proto.h>
 18 #include <net/netfilter/nf_conntrack_helper.h>
 19 #include <net/netfilter/nf_conntrack_ecache.h>
 20 #include <net/netfilter/nf_conntrack_timeout.h>
 21 #include <net/netfilter/nf_conntrack_zones.h>
 22 
 23 static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
 24 {
 25         /* Previously seen (loopback)? Ignore. */
 26         if (skb->_nfct != 0)
 27                 return XT_CONTINUE;
 28 
 29         /* special case the untracked ct : we want the percpu object */
 30         if (!ct)
 31                 ct = nf_ct_untracked_get();
 32         atomic_inc(&ct->ct_general.use);
 33         nf_ct_set(skb, ct, IP_CT_NEW);
 34 
 35         return XT_CONTINUE;
 36 }
 37 
 38 static unsigned int xt_ct_target_v0(struct sk_buff *skb,
 39                                     const struct xt_action_param *par)
 40 {
 41         const struct xt_ct_target_info *info = par->targinfo;
 42         struct nf_conn *ct = info->ct;
 43 
 44         return xt_ct_target(skb, ct);
 45 }
 46 
 47 static unsigned int xt_ct_target_v1(struct sk_buff *skb,
 48                                     const struct xt_action_param *par)
 49 {
 50         const struct xt_ct_target_info_v1 *info = par->targinfo;
 51         struct nf_conn *ct = info->ct;
 52 
 53         return xt_ct_target(skb, ct);
 54 }
 55 
 56 static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
 57 {
 58         if (par->family == NFPROTO_IPV4) {
 59                 const struct ipt_entry *e = par->entryinfo;
 60 
 61                 if (e->ip.invflags & IPT_INV_PROTO)
 62                         return 0;
 63                 return e->ip.proto;
 64         } else if (par->family == NFPROTO_IPV6) {
 65                 const struct ip6t_entry *e = par->entryinfo;
 66 
 67                 if (e->ipv6.invflags & IP6T_INV_PROTO)
 68                         return 0;
 69                 return e->ipv6.proto;
 70         } else
 71                 return 0;
 72 }
 73 
 74 static int
 75 xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
 76                  const struct xt_tgchk_param *par)
 77 {
 78         struct nf_conntrack_helper *helper;
 79         struct nf_conn_help *help;
 80         u8 proto;
 81 
 82         proto = xt_ct_find_proto(par);
 83         if (!proto) {
 84                 pr_info("You must specify a L4 protocol, and not use "
 85                         "inversions on it.\n");
 86                 return -ENOENT;
 87         }
 88 
 89         helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
 90                                                     proto);
 91         if (helper == NULL) {
 92                 pr_info("No such helper \"%s\"\n", helper_name);
 93                 return -ENOENT;
 94         }
 95 
 96         help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
 97         if (help == NULL) {
 98                 module_put(helper->me);
 99                 return -ENOMEM;
100         }
101 
102         help->helper = helper;
103         return 0;
104 }
105 
106 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
107 static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
108 {
109         typeof(nf_ct_timeout_put_hook) timeout_put;
110 
111         timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
112         if (timeout_put)
113                 timeout_put(timeout);
114 }
115 #endif
116 
117 static int
118 xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
119                   const char *timeout_name)
120 {
121 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
122         typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
123         struct ctnl_timeout *timeout;
124         struct nf_conn_timeout *timeout_ext;
125         struct nf_conntrack_l4proto *l4proto;
126         int ret = 0;
127         u8 proto;
128 
129         rcu_read_lock();
130         timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
131         if (timeout_find_get == NULL) {
132                 ret = -ENOENT;
133                 pr_info("Timeout policy base is empty\n");
134                 goto out;
135         }
136 
137         proto = xt_ct_find_proto(par);
138         if (!proto) {
139                 ret = -EINVAL;
140                 pr_info("You must specify a L4 protocol, and not use "
141                         "inversions on it.\n");
142                 goto out;
143         }
144 
145         timeout = timeout_find_get(par->net, timeout_name);
146         if (timeout == NULL) {
147                 ret = -ENOENT;
148                 pr_info("No such timeout policy \"%s\"\n", timeout_name);
149                 goto out;
150         }
151 
152         if (timeout->l3num != par->family) {
153                 ret = -EINVAL;
154                 pr_info("Timeout policy `%s' can only be used by L3 protocol "
155                         "number %d\n", timeout_name, timeout->l3num);
156                 goto err_put_timeout;
157         }
158         /* Make sure the timeout policy matches any existing protocol tracker,
159          * otherwise default to generic.
160          */
161         l4proto = __nf_ct_l4proto_find(par->family, proto);
162         if (timeout->l4proto->l4proto != l4proto->l4proto) {
163                 ret = -EINVAL;
164                 pr_info("Timeout policy `%s' can only be used by L4 protocol "
165                         "number %d\n",
166                         timeout_name, timeout->l4proto->l4proto);
167                 goto err_put_timeout;
168         }
169         timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
170         if (timeout_ext == NULL)
171                 ret = -ENOMEM;
172 
173         rcu_read_unlock();
174         return ret;
175 
176 err_put_timeout:
177         __xt_ct_tg_timeout_put(timeout);
178 out:
179         rcu_read_unlock();
180         return ret;
181 #else
182         return -EOPNOTSUPP;
183 #endif
184 }
185 
186 static u16 xt_ct_flags_to_dir(const struct xt_ct_target_info_v1 *info)
187 {
188         switch (info->flags & (XT_CT_ZONE_DIR_ORIG |
189                                XT_CT_ZONE_DIR_REPL)) {
190         case XT_CT_ZONE_DIR_ORIG:
191                 return NF_CT_ZONE_DIR_ORIG;
192         case XT_CT_ZONE_DIR_REPL:
193                 return NF_CT_ZONE_DIR_REPL;
194         default:
195                 return NF_CT_DEFAULT_ZONE_DIR;
196         }
197 }
198 
199 static int xt_ct_tg_check(const struct xt_tgchk_param *par,
200                           struct xt_ct_target_info_v1 *info)
201 {
202         struct nf_conntrack_zone zone;
203         struct nf_conn *ct;
204         int ret = -EOPNOTSUPP;
205 
206         if (info->flags & XT_CT_NOTRACK) {
207                 ct = NULL;
208                 goto out;
209         }
210 
211 #ifndef CONFIG_NF_CONNTRACK_ZONES
212         if (info->zone || info->flags & (XT_CT_ZONE_DIR_ORIG |
213                                          XT_CT_ZONE_DIR_REPL |
214                                          XT_CT_ZONE_MARK))
215                 goto err1;
216 #endif
217 
218         ret = nf_ct_netns_get(par->net, par->family);
219         if (ret < 0)
220                 goto err1;
221 
222         memset(&zone, 0, sizeof(zone));
223         zone.id = info->zone;
224         zone.dir = xt_ct_flags_to_dir(info);
225         if (info->flags & XT_CT_ZONE_MARK)
226                 zone.flags |= NF_CT_FLAG_MARK;
227 
228         ct = nf_ct_tmpl_alloc(par->net, &zone, GFP_KERNEL);
229         if (!ct) {
230                 ret = -ENOMEM;
231                 goto err2;
232         }
233 
234         ret = 0;
235         if ((info->ct_events || info->exp_events) &&
236             !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
237                                   GFP_KERNEL)) {
238                 ret = -EINVAL;
239                 goto err3;
240         }
241 
242         if (info->helper[0]) {
243                 ret = xt_ct_set_helper(ct, info->helper, par);
244                 if (ret < 0)
245                         goto err3;
246         }
247 
248         if (info->timeout[0]) {
249                 ret = xt_ct_set_timeout(ct, par, info->timeout);
250                 if (ret < 0)
251                         goto err3;
252         }
253         __set_bit(IPS_CONFIRMED_BIT, &ct->status);
254         nf_conntrack_get(&ct->ct_general);
255 out:
256         info->ct = ct;
257         return 0;
258 
259 err3:
260         nf_ct_tmpl_free(ct);
261 err2:
262         nf_ct_netns_put(par->net, par->family);
263 err1:
264         return ret;
265 }
266 
267 static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
268 {
269         struct xt_ct_target_info *info = par->targinfo;
270         struct xt_ct_target_info_v1 info_v1 = {
271                 .flags          = info->flags,
272                 .zone           = info->zone,
273                 .ct_events      = info->ct_events,
274                 .exp_events     = info->exp_events,
275         };
276         int ret;
277 
278         if (info->flags & ~XT_CT_NOTRACK)
279                 return -EINVAL;
280 
281         memcpy(info_v1.helper, info->helper, sizeof(info->helper));
282 
283         ret = xt_ct_tg_check(par, &info_v1);
284         if (ret < 0)
285                 return ret;
286 
287         info->ct = info_v1.ct;
288 
289         return ret;
290 }
291 
292 static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
293 {
294         struct xt_ct_target_info_v1 *info = par->targinfo;
295 
296         if (info->flags & ~XT_CT_NOTRACK)
297                 return -EINVAL;
298 
299         return xt_ct_tg_check(par, par->targinfo);
300 }
301 
302 static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
303 {
304         struct xt_ct_target_info_v1 *info = par->targinfo;
305 
306         if (info->flags & ~XT_CT_MASK)
307                 return -EINVAL;
308 
309         return xt_ct_tg_check(par, par->targinfo);
310 }
311 
312 static void xt_ct_destroy_timeout(struct nf_conn *ct)
313 {
314 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
315         struct nf_conn_timeout *timeout_ext;
316         typeof(nf_ct_timeout_put_hook) timeout_put;
317 
318         rcu_read_lock();
319         timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
320 
321         if (timeout_put) {
322                 timeout_ext = nf_ct_timeout_find(ct);
323                 if (timeout_ext) {
324                         timeout_put(timeout_ext->timeout);
325                         RCU_INIT_POINTER(timeout_ext->timeout, NULL);
326                 }
327         }
328         rcu_read_unlock();
329 #endif
330 }
331 
332 static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
333                              struct xt_ct_target_info_v1 *info)
334 {
335         struct nf_conn *ct = info->ct;
336         struct nf_conn_help *help;
337 
338         if (ct && !nf_ct_is_untracked(ct)) {
339                 help = nfct_help(ct);
340                 if (help)
341                         module_put(help->helper->me);
342 
343                 nf_ct_netns_put(par->net, par->family);
344 
345                 xt_ct_destroy_timeout(ct);
346                 nf_ct_put(info->ct);
347         }
348 }
349 
350 static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
351 {
352         struct xt_ct_target_info *info = par->targinfo;
353         struct xt_ct_target_info_v1 info_v1 = {
354                 .flags          = info->flags,
355                 .zone           = info->zone,
356                 .ct_events      = info->ct_events,
357                 .exp_events     = info->exp_events,
358                 .ct             = info->ct,
359         };
360         memcpy(info_v1.helper, info->helper, sizeof(info->helper));
361 
362         xt_ct_tg_destroy(par, &info_v1);
363 }
364 
365 static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
366 {
367         xt_ct_tg_destroy(par, par->targinfo);
368 }
369 
370 static struct xt_target xt_ct_tg_reg[] __read_mostly = {
371         {
372                 .name           = "CT",
373                 .family         = NFPROTO_UNSPEC,
374                 .targetsize     = sizeof(struct xt_ct_target_info),
375                 .usersize       = offsetof(struct xt_ct_target_info, ct),
376                 .checkentry     = xt_ct_tg_check_v0,
377                 .destroy        = xt_ct_tg_destroy_v0,
378                 .target         = xt_ct_target_v0,
379                 .table          = "raw",
380                 .me             = THIS_MODULE,
381         },
382         {
383                 .name           = "CT",
384                 .family         = NFPROTO_UNSPEC,
385                 .revision       = 1,
386                 .targetsize     = sizeof(struct xt_ct_target_info_v1),
387                 .usersize       = offsetof(struct xt_ct_target_info, ct),
388                 .checkentry     = xt_ct_tg_check_v1,
389                 .destroy        = xt_ct_tg_destroy_v1,
390                 .target         = xt_ct_target_v1,
391                 .table          = "raw",
392                 .me             = THIS_MODULE,
393         },
394         {
395                 .name           = "CT",
396                 .family         = NFPROTO_UNSPEC,
397                 .revision       = 2,
398                 .targetsize     = sizeof(struct xt_ct_target_info_v1),
399                 .usersize       = offsetof(struct xt_ct_target_info, ct),
400                 .checkentry     = xt_ct_tg_check_v2,
401                 .destroy        = xt_ct_tg_destroy_v1,
402                 .target         = xt_ct_target_v1,
403                 .table          = "raw",
404                 .me             = THIS_MODULE,
405         },
406 };
407 
408 static unsigned int
409 notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
410 {
411         /* Previously seen (loopback)? Ignore. */
412         if (skb->_nfct != 0)
413                 return XT_CONTINUE;
414 
415         nf_ct_set(skb, nf_ct_untracked_get(), IP_CT_NEW);
416         nf_conntrack_get(skb_nfct(skb));
417 
418         return XT_CONTINUE;
419 }
420 
421 static int notrack_chk(const struct xt_tgchk_param *par)
422 {
423         if (!par->net->xt.notrack_deprecated_warning) {
424                 pr_info("netfilter: NOTRACK target is deprecated, "
425                         "use CT instead or upgrade iptables\n");
426                 par->net->xt.notrack_deprecated_warning = true;
427         }
428         return 0;
429 }
430 
431 static struct xt_target notrack_tg_reg __read_mostly = {
432         .name           = "NOTRACK",
433         .revision       = 0,
434         .family         = NFPROTO_UNSPEC,
435         .checkentry     = notrack_chk,
436         .target         = notrack_tg,
437         .table          = "raw",
438         .me             = THIS_MODULE,
439 };
440 
441 static int __init xt_ct_tg_init(void)
442 {
443         int ret;
444 
445         ret = xt_register_target(&notrack_tg_reg);
446         if (ret < 0)
447                 return ret;
448 
449         ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
450         if (ret < 0) {
451                 xt_unregister_target(&notrack_tg_reg);
452                 return ret;
453         }
454         return 0;
455 }
456 
457 static void __exit xt_ct_tg_exit(void)
458 {
459         xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
460         xt_unregister_target(&notrack_tg_reg);
461 }
462 
463 module_init(xt_ct_tg_init);
464 module_exit(xt_ct_tg_exit);
465 
466 MODULE_LICENSE("GPL");
467 MODULE_DESCRIPTION("Xtables: connection tracking target");
468 MODULE_ALIAS("ipt_CT");
469 MODULE_ALIAS("ip6t_CT");
470 MODULE_ALIAS("ipt_NOTRACK");
471 MODULE_ALIAS("ip6t_NOTRACK");
472 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp