cregit-Linux how code gets into the kernel

Release 4.11 net/netfilter/x_tables.c

Directory: net/netfilter
/*
 * x_tables core - Backend for {ip,ip6,arp}_tables
 *
 * Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org>
 * Copyright (C) 2006-2012 Patrick McHardy <kaber@trash.net>
 *
 * Based on existing ip_tables code which is
 *   Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
 *   Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/audit.h>
#include <linux/user_namespace.h>
#include <net/net_namespace.h>

#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_arp/arp_tables.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module");


#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))

#define XT_PCPU_BLOCK_SIZE 4096


struct compat_delta {
	
unsigned int offset; /* offset in kernel */
	
int delta; /* delta in 32bit user land */
};


struct xt_af {
	
struct mutex mutex;
	
struct list_head match;
	
struct list_head target;
#ifdef CONFIG_COMPAT
	
struct mutex compat_mutex;
	
struct compat_delta *compat_tab;
	
unsigned int number; /* number of slots in compat_tab[] */
	
unsigned int cur; /* number of used slots in compat_tab[] */
#endif
};


static struct xt_af *xt;


static const char *const xt_prefix[NFPROTO_NUMPROTO] = {
	[NFPROTO_UNSPEC] = "x",
	[NFPROTO_IPV4]   = "ip",
	[NFPROTO_ARP]    = "arp",
	[NFPROTO_BRIDGE] = "eb",
	[NFPROTO_IPV6]   = "ip6",
};

/* Registration hooks for targets. */

int xt_register_target(struct xt_target *target) { u_int8_t af = target->family; mutex_lock(&xt[af].mutex); list_add(&target->list, &xt[af].target); mutex_unlock(&xt[af].mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte4882.76%120.00%
Pablo Neira Ayuso813.79%240.00%
Ingo Molnar11.72%120.00%
Jan Engelhardt11.72%120.00%
Total58100.00%5100.00%

EXPORT_SYMBOL(xt_register_target);
void xt_unregister_target(struct xt_target *target) { u_int8_t af = target->family; mutex_lock(&xt[af].mutex); list_del(&target->list); mutex_unlock(&xt[af].mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte3574.47%120.00%
Pablo Neira Ayuso612.77%120.00%
Patrick McHardy36.38%120.00%
Ingo Molnar24.26%120.00%
Jan Engelhardt12.13%120.00%
Total47100.00%5100.00%

EXPORT_SYMBOL(xt_unregister_target);
int xt_register_targets(struct xt_target *target, unsigned int n) { unsigned int i; int err = 0; for (i = 0; i < n; i++) { err = xt_register_target(&target[i]); if (err) goto err; } return err; err: if (i > 0) xt_unregister_targets(target, i); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy7698.70%150.00%
Harald Welte11.30%150.00%
Total77100.00%2100.00%

EXPORT_SYMBOL(xt_register_targets);
void xt_unregister_targets(struct xt_target *target, unsigned int n) { while (n-- > 0) xt_unregister_target(&target[n]); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2376.67%150.00%
Changli Gao723.33%150.00%
Total30100.00%2100.00%

EXPORT_SYMBOL(xt_unregister_targets);
int xt_register_match(struct xt_match *match) { u_int8_t af = match->family; mutex_lock(&xt[af].mutex); list_add(&match->list, &xt[af].match); mutex_unlock(&xt[af].mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte4882.76%120.00%
Pablo Neira Ayuso813.79%240.00%
Ingo Molnar11.72%120.00%
Jan Engelhardt11.72%120.00%
Total58100.00%5100.00%

EXPORT_SYMBOL(xt_register_match);
void xt_unregister_match(struct xt_match *match) { u_int8_t af = match->family; mutex_lock(&xt[af].mutex); list_del(&match->list); mutex_unlock(&xt[af].mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte3574.47%120.00%
Pablo Neira Ayuso612.77%120.00%
Patrick McHardy36.38%120.00%
Ingo Molnar24.26%120.00%
Jan Engelhardt12.13%120.00%
Total47100.00%5100.00%

EXPORT_SYMBOL(xt_unregister_match);
int xt_register_matches(struct xt_match *match, unsigned int n) { unsigned int i; int err = 0; for (i = 0; i < n; i++) { err = xt_register_match(&match[i]); if (err) goto err; } return err; err: if (i > 0) xt_unregister_matches(match, i); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy77100.00%1100.00%
Total77100.00%1100.00%

EXPORT_SYMBOL(xt_register_matches);
void xt_unregister_matches(struct xt_match *match, unsigned int n) { while (n-- > 0) xt_unregister_match(&match[n]); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2376.67%150.00%
Changli Gao723.33%150.00%
Total30100.00%2100.00%

EXPORT_SYMBOL(xt_unregister_matches); /* * These are weird, but module loading must not be done with mutex * held (since they will register), and we have to have a single * function to use. */ /* Find match, grabs ref. Returns ERR_PTR() on error. */
struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) { struct xt_match *m; int err = -ENOENT; mutex_lock(&xt[af].mutex); list_for_each_entry(m, &xt[af].match, list) { if (strcmp(m->name, name) == 0) { if (m->revision == revision) { if (try_module_get(m->me)) { mutex_unlock(&xt[af].mutex); return m; } } else err = -EPROTOTYPE; /* Found something. */ } } mutex_unlock(&xt[af].mutex); if (af != NFPROTO_UNSPEC) /* Try searching again in the family-independent list */ return xt_find_match(NFPROTO_UNSPEC, name, revision); return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte11782.98%116.67%
Jan Engelhardt1812.77%233.33%
Pablo Neira Ayuso21.42%116.67%
Ingo Molnar21.42%116.67%
Patrick McHardy21.42%116.67%
Total141100.00%6100.00%

EXPORT_SYMBOL(xt_find_match);
struct xt_match * xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision) { struct xt_match *match; match = xt_find_match(nfproto, name, revision); if (IS_ERR(match)) { request_module("%st_%s", xt_prefix[nfproto], name); match = xt_find_match(nfproto, name, revision); } return match; }

Contributors

PersonTokensPropCommitsCommitProp
Jan Engelhardt4768.12%150.00%
Stephen Hemminger2231.88%150.00%
Total69100.00%2100.00%

EXPORT_SYMBOL_GPL(xt_request_find_match); /* Find target, grabs ref. Returns ERR_PTR() on error. */
struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) { struct xt_target *t; int err = -ENOENT; mutex_lock(&xt[af].mutex); list_for_each_entry(t, &xt[af].target, list) { if (strcmp(t->name, name) == 0) { if (t->revision == revision) { if (try_module_get(t->me)) { mutex_unlock(&xt[af].mutex); return t; } } else err = -EPROTOTYPE; /* Found something. */ } } mutex_unlock(&xt[af].mutex); if (af != NFPROTO_UNSPEC) /* Try searching again in the family-independent list */ return xt_find_target(NFPROTO_UNSPEC, name, revision); return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte11782.98%116.67%
Jan Engelhardt1812.77%233.33%
Pablo Neira Ayuso21.42%116.67%
Patrick McHardy21.42%116.67%
Ingo Molnar21.42%116.67%
Total141100.00%6100.00%

EXPORT_SYMBOL(xt_find_target);
struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision) { struct xt_target *target; target = xt_find_target(af, name, revision); if (IS_ERR(target)) { request_module("%st_%s", xt_prefix[af], name); target = xt_find_target(af, name, revision); } return target; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte4260.87%120.00%
Stephen Hemminger2231.88%120.00%
Jan Engelhardt34.35%240.00%
Patrick McHardy22.90%120.00%
Total69100.00%5100.00%

EXPORT_SYMBOL_GPL(xt_request_find_target);
static int xt_obj_to_user(u16 __user *psize, u16 size, void __user *pname, const char *name, u8 __user *prev, u8 rev) { if (put_user(size, psize)) return -EFAULT; if (copy_to_user(pname, name, strlen(name) + 1)) return -EFAULT; if (put_user(rev, prev)) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Willem de Bruijn81100.00%1100.00%
Total81100.00%1100.00%

#define XT_OBJ_TO_USER(U, K, TYPE, C_SIZE) \ xt_obj_to_user(&U->u.TYPE##_size, C_SIZE ? : K->u.TYPE##_size, \ U->u.user.name, K->u.kernel.TYPE->name, \ &U->u.user.revision, K->u.kernel.TYPE->revision)
int xt_data_to_user(void __user *dst, const void *src, int usersize, int size) { usersize = usersize ? : size; if (copy_to_user(dst, src, usersize)) return -EFAULT; if (usersize != size && clear_user(dst + usersize, size - usersize)) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Willem de Bruijn67100.00%1100.00%
Total67100.00%1100.00%

EXPORT_SYMBOL_GPL(xt_data_to_user); #define XT_DATA_TO_USER(U, K, TYPE, C_SIZE) \ xt_data_to_user(U->data, K->data, \ K->u.kernel.TYPE->usersize, \ C_SIZE ? : K->u.kernel.TYPE->TYPE##size)
int xt_match_to_user(const struct xt_entry_match *m, struct xt_entry_match __user *u) { return XT_OBJ_TO_USER(u, m, match, 0) || XT_DATA_TO_USER(u, m, match, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Willem de Bruijn40100.00%1100.00%
Total40100.00%1100.00%

EXPORT_SYMBOL_GPL(xt_match_to_user);
int xt_target_to_user(const struct xt_entry_target *t, struct xt_entry_target __user *u) { return XT_OBJ_TO_USER(u, t, target, 0) || XT_DATA_TO_USER(u, t, target, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Willem de Bruijn40100.00%1100.00%
Total40100.00%1100.00%

EXPORT_SYMBOL_GPL(xt_target_to_user);
static int match_revfn(u8 af, const char *name, u8 revision, int *bestp) { const struct xt_match *m; int have_rev = 0; list_for_each_entry(m, &xt[af].match, list) { if (strcmp(m->name, name) == 0) { if (m->revision > *bestp) *bestp = m->revision; if (m->revision == revision) have_rev = 1; } } if (af != NFPROTO_UNSPEC && !have_rev) return match_revfn(NFPROTO_UNSPEC, name, revision, bestp); return have_rev; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte8678.90%125.00%
Patrick McHardy2119.27%125.00%
Jan Engelhardt21.83%250.00%
Total109100.00%4100.00%


static int target_revfn(u8 af, const char *name, u8 revision, int *bestp) { const struct xt_target *t; int have_rev = 0; list_for_each_entry(t, &xt[af].target, list) { if (strcmp(t->name, name) == 0) { if (t->revision > *bestp) *bestp = t->revision; if (t->revision == revision) have_rev = 1; } } if (af != NFPROTO_UNSPEC && !have_rev) return target_revfn(NFPROTO_UNSPEC, name, revision, bestp); return have_rev; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte8678.90%125.00%
Patrick McHardy2119.27%125.00%
Jan Engelhardt21.83%250.00%
Total109100.00%4100.00%

/* Returns true or false (if no such extension at all) */
int xt_find_revision(u8 af, const char *name, u8 revision, int target, int *err) { int have_rev, best = -1; mutex_lock(&xt[af].mutex); if (target == 1) have_rev = target_revfn(af, name, revision, &best); else have_rev = match_revfn(af, name, revision, &best); mutex_unlock(&xt[af].mutex); /* Nothing at all? Return 0 to try loading module. */ if (best == -1) { *err = -ENOENT; return 0; } *err = best; if (!have_rev) *err = -EPROTONOSUPPORT; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Harald Welte12397.62%125.00%
Ingo Molnar10.79%125.00%
Pablo Neira Ayuso10.79%125.00%
Jan Engelhardt10.79%125.00%
Total126100.00%4100.00%

EXPORT_SYMBOL_GPL(xt_find_revision);
static char * textify_hooks(char *buf, size_t size, unsigned int mask, uint8_t nfproto) { static const char *const inetbr_names[] = { "PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING", "BROUTING", }; static const char *const arp_names[] = { "INPUT", "FORWARD", "OUTPUT", }; const char *const *names; unsigned int i, max; char *p = buf; bool np = false; int res; names = (nfproto == NFPROTO_ARP) ? arp_names : inetbr_names; max = (nfproto == NFPROTO_ARP) ? ARRAY_SIZE(arp_names) : ARRAY_SIZE(inetbr_names); *p = '\0'; for (i = 0; i < max; ++i) { if (!(mask & (1 << i))) continue; res = snprintf(p, size, "%s%s", np ? "/" : "", names[i]); if (res > 0) { size -= res; p += res; } np = true; } return buf; }

Contributors

PersonTokensPropCommitsCommitProp
Jan Engelhardt195100.00%2100.00%
Total195100.00%2100.00%


int xt_check_match(struct xt_mtchk_param *par, unsigned int size, u_int8_t proto, bool inv_proto) { int ret; if (XT_ALIGN(par->match->matchsize) != size && par->match->matchsize != -1) { /* * ebt_among is exempt from centralized matchsize checking * because it uses a dynamic-size data set. */ pr_err("%s_tables: %s.%u match: invalid size " "%u (kernel) != (user) %u\n", xt_prefix[par->family], par->match->name, par->match->revision, XT_ALIGN(par->match->matchsize), size); return -EINVAL; } if (par->match->table != NULL && strcmp(par->match->table, par->table) != 0) { pr_err("%s_tables: %s match: only valid in %s table, not %s\n", xt_prefix[par->family], par->match->name, par->match->table, par->table); return -EINVAL; } if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { char used[64], allow[64]; pr_err("%s_tables: %s match: used from hooks %s, but only " "valid from %s\n", xt_prefix[par->family], par->match->name, textify_hooks(used, sizeof(used), par->hook_mask, par->family), textify_hooks(allow, sizeof(allow), par->match->hooks, par->family)); return -EINVAL; } if (par->match->proto && (par->match->proto != proto || inv_proto)) { pr_err("%s_tables: %s match: only valid for protocol %u\n", xt_prefix[par->family], par->match->name, par->match->proto); return -EINVAL; } if (par->match->checkentry != NULL) { ret = par->match->checkentry(par); if (ret < 0) return ret; else if (ret > 0) /* Flag up potential errors. */ return -EIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy17350.73%19.09%
Jan Engelhardt16046.92%872.73%
Joe Perches41.17%19.09%
Balazs Scheidler41.17%19.09%
Total341100.00%11100.00%

EXPORT_SYMBOL_GPL(xt_check_match); /** xt_check_entry_match - check that matches end before start of target * * @match: beginning of xt_entry_match * @target: beginning of this rules target (alleged end of matches) * @alignment: alignment requirement of match structures * * Validates that all matches add up to the beginning of the target, * and that each match covers at least the base structure size. * * Return: 0 on success, negative errno on failure. */
static int xt_check_entry_match(const char *match, const char *target, const size_t alignment) { const struct xt_entry_match *pos; int length = target - match; if (length == 0) /* no matches */ return 0; pos = (struct xt_entry_match *)match; do { if ((unsigned long)pos % alignment) return -EINVAL; if (length < (int)sizeof(struct xt_entry_match)) return -EINVAL; if (pos->u.match_size < sizeof(struct xt_entry_match)) return -EINVAL; if (pos->u.match_size > length) return -EINVAL; length -= pos->u.match_size; pos = ((void *)((char *)(pos) + (pos)->u.match_size)); } while (length > 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Florian Westphal162100.00%1100.00%
Total162100.00%1100.00%

#ifdef CONFIG_COMPAT
int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta) { struct xt_af *xp = &xt[af]; if (!xp->compat_tab) { if (!xp->number) return -EINVAL; xp->compat_tab = vmalloc(sizeof(struct compat_delta) * xp->number); if (!xp->compat_tab) return -ENOMEM; xp->cur = 0; } if (xp->cur >= xp->number) return -EINVAL; if (xp->cur) delta += xp->compat_tab[xp->cur - 1].delta; xp->compat_tab[xp->cur].offset = offset; xp->compat_tab[xp->cur].delta = delta; xp->cur++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet8859.06%133.33%
Patrick McHardy6040.27%133.33%
Jan Engelhardt10.67%133.33%
Total149100.00%3100.00%

EXPORT_SYMBOL_GPL(xt_compat_add_offset);
void xt_compat_flush_offsets(u_int8_t af) { if (xt[af].compat_tab) { vfree(xt[af].compat_tab); xt[af].compat_tab = NULL; xt[af].number = 0; xt[af].cur = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy3257.14%125.00%
Eric Dumazet2341.07%250.00%
Jan Engelhardt11.79%125.00%
Total56100.00%4100.00%

EXPORT_SYMBOL_GPL(xt_compat_flush_offsets);
int xt_compat_calc_jump(u_int8_t af, unsigned int offset) { struct compat_delta *tmp = xt[af].compat_tab; int mid, left = 0, right = xt[af].cur - 1; while (left <= right) { mid = (left + right) >> 1; if (offset > tmp[mid].offset) left = mid + 1; else if (offset < tmp[mid].offset) right = mid - 1; else return mid ? tmp[mid - 1].delta : 0; } return left ? tmp[left - 1].delta : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet8870.97%240.00%
Patrick McHardy3326.61%120.00%
Florian Westphal21.61%120.00%
Jan Engelhardt10.81%120.00%
Total124100.00%5100.00%

EXPORT_SYMBOL_GPL(xt_compat_calc_jump);
void xt_compat_init_offsets(u_int8_t af, unsigned int number) { xt[af].number = number; xt[af].cur = 0; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet30100.00%1100.00%
Total30100.00%1100.00%

EXPORT_SYMBOL(xt_compat_init_offsets);
int xt_compat_match_offset(const struct xt_match *match) { u_int16_t csize = match->compatsize ? : match->matchsize; return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy3186.11%133.33%
Dmitry Mishin411.11%133.33%
Jan Engelhardt12.78%133.33%
Total36100.00%3100.00%

EXPORT_SYMBOL_GPL(