mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-21 03:54:59 +02:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
47f5391a37 | ||
![]() |
16a64492ae | ||
![]() |
cdcf874366 | ||
![]() |
1b4b4347c5 | ||
![]() |
2f37af43c5 | ||
![]() |
56e5970c64 | ||
![]() |
2b76b68c65 | ||
![]() |
d2eeac4c32 | ||
![]() |
0e9037b000 | ||
![]() |
0a6091b64a | ||
![]() |
b565a85fb6 | ||
![]() |
89c80f5981 | ||
![]() |
8579fd2b3b | ||
![]() |
0a836e9677 | ||
![]() |
90b0f3a51f | ||
![]() |
89d1b808b9 | ||
![]() |
c839e87bbb | ||
![]() |
a587f9526d | ||
![]() |
1874fcd519 | ||
![]() |
21ea7b76ec | ||
![]() |
ee8da2b1ac | ||
![]() |
19a4359368 | ||
![]() |
1b379667d3 |
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([xtables-addons], [2.12])
|
AC_INIT([xtables-addons], [2.15])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
@@ -57,7 +57,7 @@ if test -n "$kbuilddir"; then
|
|||||||
echo "WARNING: Version detection did not succeed. Continue at own luck.";
|
echo "WARNING: Version detection did not succeed. Continue at own luck.";
|
||||||
else
|
else
|
||||||
echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
|
echo "$kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
|
||||||
if test "$kmajor" -gt 4 -o "$kmajor" -eq 4 -a "$kminor" -gt 10; then
|
if test "$kmajor" -gt 4 -o "$kmajor" -eq 4 -a "$kminor" -gt 12; then
|
||||||
echo "WARNING: That kernel version is not officially supported yet. Continue at own luck.";
|
echo "WARNING: That kernel version is not officially supported yet. Continue at own luck.";
|
||||||
elif test "$kmajor" -eq 4 -a "$kminor" -le 10; then
|
elif test "$kmajor" -eq 4 -a "$kminor" -le 10; then
|
||||||
:;
|
:;
|
||||||
|
@@ -1,6 +1,27 @@
|
|||||||
|
|
||||||
HEAD
|
v2.15 (2021-02-05)
|
||||||
====
|
==================
|
||||||
|
Enhancements:
|
||||||
|
- support for Linux up to 4.15
|
||||||
|
- xt_lscan: add --mirai option
|
||||||
|
|
||||||
|
|
||||||
|
v2.14 (2017-11-22)
|
||||||
|
==================
|
||||||
|
Enhancements:
|
||||||
|
- support for Linux up to 4.14
|
||||||
|
Fixes:
|
||||||
|
- xt_DNETMAP: fix some reports from PVSStudio (a static checker)
|
||||||
|
|
||||||
|
|
||||||
|
v2.13 (2017-06-29)
|
||||||
|
==================
|
||||||
|
Enhancements:
|
||||||
|
- support for Linux up to 4.12
|
||||||
|
- xt_condition: namespace support
|
||||||
|
Fixes:
|
||||||
|
- xt_geoip: check for allocation overflow
|
||||||
|
- xt_DNETMAP: fix a buffer overflow
|
||||||
|
|
||||||
|
|
||||||
v2.12 (2017-01-11)
|
v2.12 (2017-01-11)
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
ntohs((addr).s6_addr16[5]), \
|
ntohs((addr).s6_addr16[5]), \
|
||||||
ntohs((addr).s6_addr16[6]), \
|
ntohs((addr).s6_addr16[6]), \
|
||||||
ntohs((addr).s6_addr16[7])
|
ntohs((addr).s6_addr16[7])
|
||||||
# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
|
# define NIP6_FMT "%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx"
|
||||||
#endif
|
#endif
|
||||||
#if !defined(NIPQUAD) && !defined(NIPQUAD_FMT)
|
#if !defined(NIPQUAD) && !defined(NIPQUAD_FMT)
|
||||||
# define NIPQUAD(addr) \
|
# define NIPQUAD(addr) \
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
((const unsigned char *)&addr)[1], \
|
((const unsigned char *)&addr)[1], \
|
||||||
((const unsigned char *)&addr)[2], \
|
((const unsigned char *)&addr)[2], \
|
||||||
((const unsigned char *)&addr)[3]
|
((const unsigned char *)&addr)[3]
|
||||||
# define NIPQUAD_FMT "%u.%u.%u.%u"
|
# define NIPQUAD_FMT "%hhu.%hhu.%hhu.%hhu"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
||||||
@@ -93,4 +93,8 @@ static inline struct net *par_net(const struct xt_action_param *par)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NF_CT_ASSERT
|
||||||
|
# define NF_CT_ASSERT(x) WARN_ON(!(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _XTABLES_COMPAT_H */
|
#endif /* _XTABLES_COMPAT_H */
|
||||||
|
@@ -24,6 +24,7 @@ static const struct option lscan_mt_opts[] = {
|
|||||||
{.name = "synscan", .has_arg = false, .val = 's'},
|
{.name = "synscan", .has_arg = false, .val = 's'},
|
||||||
{.name = "cnscan", .has_arg = false, .val = 'c'},
|
{.name = "cnscan", .has_arg = false, .val = 'c'},
|
||||||
{.name = "grscan", .has_arg = false, .val = 'g'},
|
{.name = "grscan", .has_arg = false, .val = 'g'},
|
||||||
|
{.name = "mirai", .has_arg = false, .val = 'm'},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,7 +36,8 @@ static void lscan_mt_help(void)
|
|||||||
" --stealth Match TCP Stealth packets\n"
|
" --stealth Match TCP Stealth packets\n"
|
||||||
" --synscan Match TCP SYN scans\n"
|
" --synscan Match TCP SYN scans\n"
|
||||||
" --cnscan Match TCP Connect scans\n"
|
" --cnscan Match TCP Connect scans\n"
|
||||||
" --grscan Match Banner Grabbing scans\n");
|
" --grscan Match Banner Grabbing scans\n"
|
||||||
|
" --mirai Match TCP scan with ISN = dest. IP\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lscan_mt_parse(int c, char **argv, int invert,
|
static int lscan_mt_parse(int c, char **argv, int invert,
|
||||||
@@ -45,16 +47,19 @@ static int lscan_mt_parse(int c, char **argv, int invert,
|
|||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
info->match_cn = true;
|
info->match_fl3 |= LSCAN_FL3_CN;
|
||||||
return true;
|
return true;
|
||||||
case 'g':
|
case 'g':
|
||||||
info->match_gr = true;
|
info->match_fl4 |= LSCAN_FL4_GR;
|
||||||
|
return true;
|
||||||
|
case 'm':
|
||||||
|
info->match_fl1 |= LSCAN_FL1_MIRAI;
|
||||||
return true;
|
return true;
|
||||||
case 's':
|
case 's':
|
||||||
info->match_syn = true;
|
info->match_fl2 |= LSCAN_FL2_SYN;
|
||||||
return true;
|
return true;
|
||||||
case 'x':
|
case 'x':
|
||||||
info->match_stealth = true;
|
info->match_fl1 |= LSCAN_FL1_STEALTH;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -68,14 +73,16 @@ static void lscan_mt_save(const void *ip, const struct xt_entry_match *match)
|
|||||||
{
|
{
|
||||||
const struct xt_lscan_mtinfo *info = (const void *)(match->data);
|
const struct xt_lscan_mtinfo *info = (const void *)(match->data);
|
||||||
|
|
||||||
if (info->match_stealth)
|
if (info->match_fl1 & LSCAN_FL1_STEALTH)
|
||||||
printf(" --stealth ");
|
printf(" --stealth ");
|
||||||
if (info->match_syn)
|
if (info->match_fl2 & LSCAN_FL2_SYN)
|
||||||
printf(" --synscan ");
|
printf(" --synscan ");
|
||||||
if (info->match_cn)
|
if (info->match_fl3 & LSCAN_FL3_CN)
|
||||||
printf(" --cnscan ");
|
printf(" --cnscan ");
|
||||||
if (info->match_gr)
|
if (info->match_fl4 & LSCAN_FL4_GR)
|
||||||
printf(" --grscan ");
|
printf(" --grscan ");
|
||||||
|
if (info->match_fl1 & LSCAN_FL1_MIRAI)
|
||||||
|
printf(" --mirai ");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lscan_mt_print(const void *ip,
|
static void lscan_mt_print(const void *ip,
|
||||||
|
@@ -27,6 +27,11 @@ warranted single-direction data flows, usually bulk data transfers such as
|
|||||||
FTP DATA connections or IRC DCC. Grab Scan Detection should only be used on
|
FTP DATA connections or IRC DCC. Grab Scan Detection should only be used on
|
||||||
ports where a protocol runs that is guaranteed to do a bidirectional exchange
|
ports where a protocol runs that is guaranteed to do a bidirectional exchange
|
||||||
of bytes.
|
of bytes.
|
||||||
|
.TP
|
||||||
|
\fB\-\-mirai\fP
|
||||||
|
Match if the TCP ISN is equal to the IPv4 destination address; this is used
|
||||||
|
by the devices in the Mirai botnet as a form of TCP SYN scan, so you will
|
||||||
|
have to explicitly specify --syn for the rule.
|
||||||
.PP
|
.PP
|
||||||
NOTE: Some clients (Windows XP for example) may do what looks like a SYN scan,
|
NOTE: Some clients (Windows XP for example) may do what looks like a SYN scan,
|
||||||
so be advised to carefully use xt_lscan in conjunction with blocking rules,
|
so be advised to carefully use xt_lscan in conjunction with blocking rules,
|
||||||
|
@@ -357,11 +357,18 @@ has_logged_during_this_minute(const struct peer *peer)
|
|||||||
*
|
*
|
||||||
* @r: rule
|
* @r: rule
|
||||||
*/
|
*/
|
||||||
static void
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||||
peer_gc(unsigned long r)
|
static void peer_gc(struct timer_list *tl)
|
||||||
|
#else
|
||||||
|
static void peer_gc(unsigned long r)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||||
|
struct xt_pknock_rule *rule = from_timer(rule, tl, timer);
|
||||||
|
#else
|
||||||
struct xt_pknock_rule *rule = (struct xt_pknock_rule *)r;
|
struct xt_pknock_rule *rule = (struct xt_pknock_rule *)r;
|
||||||
|
#endif
|
||||||
struct peer *peer;
|
struct peer *peer;
|
||||||
struct list_head *pos, *n;
|
struct list_head *pos, *n;
|
||||||
|
|
||||||
@@ -469,9 +476,13 @@ add_rule(struct xt_pknock_mtinfo *info)
|
|||||||
if (rule->peer_head == NULL)
|
if (rule->peer_head == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||||
|
timer_setup(&rule->timer, peer_gc, 0);
|
||||||
|
#else
|
||||||
init_timer(&rule->timer);
|
init_timer(&rule->timer);
|
||||||
rule->timer.function = peer_gc;
|
rule->timer.function = peer_gc;
|
||||||
rule->timer.data = (unsigned long)rule;
|
rule->timer.data = (unsigned long)rule;
|
||||||
|
#endif
|
||||||
|
|
||||||
rule->status_proc = proc_create_data(info->rule_name, 0, pde,
|
rule->status_proc = proc_create_data(info->rule_name, 0, pde,
|
||||||
&pknock_proc_ops, rule);
|
&pknock_proc_ops, rule);
|
||||||
@@ -619,8 +630,9 @@ static void add_peer(struct peer *peer, struct xt_pknock_rule *rule)
|
|||||||
*/
|
*/
|
||||||
static void remove_peer(struct peer *peer)
|
static void remove_peer(struct peer *peer)
|
||||||
{
|
{
|
||||||
|
if (peer == NULL)
|
||||||
|
return;
|
||||||
list_del(&peer->head);
|
list_del(&peer->head);
|
||||||
if (peer != NULL)
|
|
||||||
kfree(peer);
|
kfree(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -79,7 +79,7 @@ static void delude_send_reset(struct net *net, struct sk_buff *oldskb,
|
|||||||
tcph->doff = sizeof(struct tcphdr) / 4;
|
tcph->doff = sizeof(struct tcphdr) / 4;
|
||||||
|
|
||||||
/* DELUDE essential part */
|
/* DELUDE essential part */
|
||||||
if (oth->syn && !oth->ack && !oth->rst && !oth->fin) {
|
if (oth->syn && !oth->ack && !oth->fin) {
|
||||||
tcph->syn = true;
|
tcph->syn = true;
|
||||||
tcph->seq = 0;
|
tcph->seq = 0;
|
||||||
tcph->ack = true;
|
tcph->ack = true;
|
||||||
|
@@ -81,7 +81,7 @@ struct dnetmap_entry {
|
|||||||
|
|
||||||
struct dnetmap_prefix {
|
struct dnetmap_prefix {
|
||||||
struct nf_nat_range prefix;
|
struct nf_nat_range prefix;
|
||||||
char prefix_str[16];
|
char prefix_str[20];
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
char proc_str_data[20];
|
char proc_str_data[20];
|
||||||
char proc_str_stat[25];
|
char proc_str_stat[25];
|
||||||
@@ -376,10 +376,6 @@ dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
|||||||
#else
|
#else
|
||||||
unsigned int hooknum = par->hooknum;
|
unsigned int hooknum = par->hooknum;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING ||
|
|
||||||
hooknum == NF_INET_LOCAL_OUT ||
|
|
||||||
hooknum == NF_INET_PRE_ROUTING);
|
|
||||||
ct = nf_ct_get(skb, &ctinfo);
|
ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
|
||||||
jttl = tginfo->flags & XT_DNETMAP_TTL ? tginfo->ttl * HZ : jtimeout;
|
jttl = tginfo->flags & XT_DNETMAP_TTL ? tginfo->ttl * HZ : jtimeout;
|
||||||
@@ -398,7 +394,7 @@ dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
|||||||
/* if prefix is specified, we check if
|
/* if prefix is specified, we check if
|
||||||
it matches lookedup entry */
|
it matches lookedup entry */
|
||||||
if (tginfo->flags & XT_DNETMAP_PREFIX)
|
if (tginfo->flags & XT_DNETMAP_PREFIX)
|
||||||
if (memcmp(mr, &e->prefix, sizeof(*mr)))
|
if (memcmp(mr, &e->prefix->prefix, sizeof(*mr)))
|
||||||
goto no_rev_map;
|
goto no_rev_map;
|
||||||
/* don't reset ttl if flag is set */
|
/* don't reset ttl if flag is set */
|
||||||
if (jttl >= 0 && (! (e->flags & XT_DNETMAP_STATIC) ) ) {
|
if (jttl >= 0 && (! (e->flags & XT_DNETMAP_STATIC) ) ) {
|
||||||
|
@@ -52,12 +52,18 @@ static void logmark_ct(const struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
|||||||
printk("EXPECTED");
|
printk("EXPECTED");
|
||||||
prev = true;
|
prev = true;
|
||||||
}
|
}
|
||||||
if (ct->status & IPS_SEEN_REPLY)
|
if (ct->status & IPS_SEEN_REPLY) {
|
||||||
printk("%s""SEEN_REPLY", prev++ ? "," : "");
|
printk("%s""SEEN_REPLY", prev ? "," : "");
|
||||||
if (ct->status & IPS_ASSURED)
|
prev = true;
|
||||||
printk("%s""ASSURED", prev++ ? "," : "");
|
}
|
||||||
if (ct->status & IPS_CONFIRMED)
|
if (ct->status & IPS_ASSURED) {
|
||||||
printk("%s""CONFIRMED", prev++ ? "," : "");
|
printk("%s""ASSURED", prev ? "," : "");
|
||||||
|
prev = true;
|
||||||
|
}
|
||||||
|
if (ct->status & IPS_CONFIRMED) {
|
||||||
|
printk("%s""CONFIRMED", prev ? "," : "");
|
||||||
|
prev = true;
|
||||||
|
}
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
|
||||||
printk(" lifetime=%lus", nf_ct_expires(ct) / HZ);
|
printk(" lifetime=%lus", nf_ct_expires(ct) / HZ);
|
||||||
#else
|
#else
|
||||||
@@ -87,8 +93,10 @@ logmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
|||||||
printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]);
|
printk(" ctdir=%s", dir_names[ctinfo >= IP_CT_IS_REPLY]);
|
||||||
if (ct == NULL)
|
if (ct == NULL)
|
||||||
printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE");
|
printk(" ct=NULL ctmark=NULL ctstate=INVALID ctstatus=NONE");
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
||||||
else if (nf_ct_is_untracked(ct))
|
else if (nf_ct_is_untracked(ct))
|
||||||
printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE");
|
printk(" ct=UNTRACKED ctmark=NULL ctstate=UNTRACKED ctstatus=NONE");
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
logmark_ct(ct, ctinfo);
|
logmark_ct(ct, ctinfo);
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
* Authors:
|
* Authors:
|
||||||
* Stephane Ouellette <ouellettes [at] videotron ca>, 2002-10-22
|
* Stephane Ouellette <ouellettes [at] videotron ca>, 2002-10-22
|
||||||
* Massimiliano Hofer <max [at] nucleus it>, 2006-05-15
|
* Massimiliano Hofer <max [at] nucleus it>, 2006-05-15
|
||||||
|
* Grzegorz Kuczyński <grzegorz.kuczynski [at] koba pl>, 2017-02-27
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License; either version 2
|
* under the terms of the GNU General Public License; either version 2
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/netfilter/x_tables.h>
|
#include <linux/netfilter/x_tables.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
#include <net/net_namespace.h>
|
||||||
|
#include <net/netns/generic.h>
|
||||||
#include "xt_condition.h"
|
#include "xt_condition.h"
|
||||||
#include "compat_xtables.h"
|
#include "compat_xtables.h"
|
||||||
|
|
||||||
@@ -59,8 +62,18 @@ struct condition_variable {
|
|||||||
/* to the conditions' list. */
|
/* to the conditions' list. */
|
||||||
static DEFINE_MUTEX(proc_lock);
|
static DEFINE_MUTEX(proc_lock);
|
||||||
|
|
||||||
static LIST_HEAD(conditions_list);
|
struct condition_net {
|
||||||
static struct proc_dir_entry *proc_net_condition;
|
struct list_head conditions_list;
|
||||||
|
struct proc_dir_entry *proc_net_condition;
|
||||||
|
bool after_clear;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int condition_net_id;
|
||||||
|
|
||||||
|
static inline struct condition_net *condition_pernet(struct net *net)
|
||||||
|
{
|
||||||
|
return net_generic(net, condition_net_id);
|
||||||
|
}
|
||||||
|
|
||||||
static int condition_proc_show(struct seq_file *m, void *data)
|
static int condition_proc_show(struct seq_file *m, void *data)
|
||||||
{
|
{
|
||||||
@@ -119,6 +132,7 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
|
|||||||
{
|
{
|
||||||
struct xt_condition_mtinfo *info = par->matchinfo;
|
struct xt_condition_mtinfo *info = par->matchinfo;
|
||||||
struct condition_variable *var;
|
struct condition_variable *var;
|
||||||
|
struct condition_net *condition_net = condition_pernet(par->net);
|
||||||
|
|
||||||
/* Forbid certain names */
|
/* Forbid certain names */
|
||||||
if (*info->name == '\0' || *info->name == '.' ||
|
if (*info->name == '\0' || *info->name == '.' ||
|
||||||
@@ -134,7 +148,7 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
|
|||||||
* or increase the reference counter.
|
* or increase the reference counter.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&proc_lock);
|
mutex_lock(&proc_lock);
|
||||||
list_for_each_entry(var, &conditions_list, list) {
|
list_for_each_entry(var, &condition_net->conditions_list, list) {
|
||||||
if (strcmp(info->name, var->name) == 0) {
|
if (strcmp(info->name, var->name) == 0) {
|
||||||
var->refcount++;
|
var->refcount++;
|
||||||
mutex_unlock(&proc_lock);
|
mutex_unlock(&proc_lock);
|
||||||
@@ -153,7 +167,7 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
|
|||||||
memcpy(var->name, info->name, sizeof(info->name));
|
memcpy(var->name, info->name, sizeof(info->name));
|
||||||
/* Create the condition variable's proc file entry. */
|
/* Create the condition variable's proc file entry. */
|
||||||
var->status_proc = proc_create_data(info->name, condition_list_perms,
|
var->status_proc = proc_create_data(info->name, condition_list_perms,
|
||||||
proc_net_condition, &condition_proc_fops, var);
|
condition_net->proc_net_condition, &condition_proc_fops, var);
|
||||||
if (var->status_proc == NULL) {
|
if (var->status_proc == NULL) {
|
||||||
kfree(var);
|
kfree(var);
|
||||||
mutex_unlock(&proc_lock);
|
mutex_unlock(&proc_lock);
|
||||||
@@ -166,7 +180,7 @@ static int condition_mt_check(const struct xt_mtchk_param *par)
|
|||||||
var->refcount = 1;
|
var->refcount = 1;
|
||||||
var->enabled = false;
|
var->enabled = false;
|
||||||
wmb();
|
wmb();
|
||||||
list_add(&var->list, &conditions_list);
|
list_add(&var->list, &condition_net->conditions_list);
|
||||||
mutex_unlock(&proc_lock);
|
mutex_unlock(&proc_lock);
|
||||||
info->condvar = var;
|
info->condvar = var;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -176,11 +190,15 @@ static void condition_mt_destroy(const struct xt_mtdtor_param *par)
|
|||||||
{
|
{
|
||||||
const struct xt_condition_mtinfo *info = par->matchinfo;
|
const struct xt_condition_mtinfo *info = par->matchinfo;
|
||||||
struct condition_variable *var = info->condvar;
|
struct condition_variable *var = info->condvar;
|
||||||
|
struct condition_net *cnet = condition_pernet(par->net);
|
||||||
|
|
||||||
|
if (cnet->after_clear)
|
||||||
|
return;
|
||||||
|
|
||||||
mutex_lock(&proc_lock);
|
mutex_lock(&proc_lock);
|
||||||
if (--var->refcount == 0) {
|
if (--var->refcount == 0) {
|
||||||
list_del(&var->list);
|
list_del(&var->list);
|
||||||
proc_remove(var->status_proc);
|
remove_proc_entry(var->name, cnet->proc_net_condition);
|
||||||
mutex_unlock(&proc_lock);
|
mutex_unlock(&proc_lock);
|
||||||
kfree(var);
|
kfree(var);
|
||||||
return;
|
return;
|
||||||
@@ -213,18 +231,54 @@ static struct xt_match condition_mt_reg[] __read_mostly = {
|
|||||||
|
|
||||||
static const char *const dir_name = "nf_condition";
|
static const char *const dir_name = "nf_condition";
|
||||||
|
|
||||||
|
static int __net_init condition_net_init(struct net *net)
|
||||||
|
{
|
||||||
|
struct condition_net *condition_net = condition_pernet(net);
|
||||||
|
INIT_LIST_HEAD(&condition_net->conditions_list);
|
||||||
|
condition_net->proc_net_condition = proc_mkdir(dir_name, net->proc_net);
|
||||||
|
if (condition_net->proc_net_condition == NULL)
|
||||||
|
return -EACCES;
|
||||||
|
condition_net->after_clear = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __net_exit condition_net_exit(struct net *net)
|
||||||
|
{
|
||||||
|
struct condition_net *condition_net = condition_pernet(net);
|
||||||
|
struct list_head *pos, *q;
|
||||||
|
struct condition_variable *var = NULL;
|
||||||
|
|
||||||
|
remove_proc_subtree(dir_name, net->proc_net);
|
||||||
|
mutex_lock(&proc_lock);
|
||||||
|
list_for_each_safe(pos, q, &condition_net->conditions_list) {
|
||||||
|
var = list_entry(pos, struct condition_variable, list);
|
||||||
|
list_del(pos);
|
||||||
|
kfree(var);
|
||||||
|
}
|
||||||
|
mutex_unlock(&proc_lock);
|
||||||
|
condition_net->after_clear = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pernet_operations condition_net_ops = {
|
||||||
|
.init = condition_net_init,
|
||||||
|
.exit = condition_net_exit,
|
||||||
|
.id = &condition_net_id,
|
||||||
|
.size = sizeof(struct condition_net),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static int __init condition_mt_init(void)
|
static int __init condition_mt_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_init(&proc_lock);
|
mutex_init(&proc_lock);
|
||||||
proc_net_condition = proc_mkdir(dir_name, init_net.proc_net);
|
ret = register_pernet_subsys(&condition_net_ops);
|
||||||
if (proc_net_condition == NULL)
|
if (ret != 0)
|
||||||
return -EACCES;
|
return ret;
|
||||||
|
|
||||||
ret = xt_register_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
|
ret = xt_register_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
remove_proc_entry(dir_name, init_net.proc_net);
|
unregister_pernet_subsys(&condition_net_ops);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +288,7 @@ static int __init condition_mt_init(void)
|
|||||||
static void __exit condition_mt_exit(void)
|
static void __exit condition_mt_exit(void)
|
||||||
{
|
{
|
||||||
xt_unregister_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
|
xt_unregister_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg));
|
||||||
remove_proc_entry(dir_name, init_net.proc_net);
|
unregister_pernet_subsys(&condition_net_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(condition_mt_init);
|
module_init(condition_mt_init);
|
||||||
|
@@ -75,7 +75,8 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr,
|
|||||||
|
|
||||||
if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0)
|
if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0)
|
||||||
return ERR_PTR(-EFAULT);
|
return ERR_PTR(-EFAULT);
|
||||||
|
if (umem.count > SIZE_MAX / geoproto_size[proto])
|
||||||
|
return ERR_PTR(-E2BIG);
|
||||||
p = kmalloc(sizeof(struct geoip_country_kernel), GFP_KERNEL);
|
p = kmalloc(sizeof(struct geoip_country_kernel), GFP_KERNEL);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
@@ -511,7 +511,7 @@ search_bittorrent(const unsigned char *payload, const unsigned int plen)
|
|||||||
* but *must have* one (or more) of strings listed below (true for scrape and announce)
|
* but *must have* one (or more) of strings listed below (true for scrape and announce)
|
||||||
*/
|
*/
|
||||||
if (memcmp(payload, "GET /", 5) == 0) {
|
if (memcmp(payload, "GET /", 5) == 0) {
|
||||||
if (HX_memmem(payload, plen, "info_hash=", 9) != NULL)
|
if (HX_memmem(payload, plen, "info_hash=", 10) != NULL)
|
||||||
return IPP2P_BIT * 100 + 1;
|
return IPP2P_BIT * 100 + 1;
|
||||||
if (HX_memmem(payload, plen, "peer_id=", 8) != NULL)
|
if (HX_memmem(payload, plen, "peer_id=", 8) != NULL)
|
||||||
return IPP2P_BIT * 100 + 2;
|
return IPP2P_BIT * 100 + 2;
|
||||||
|
@@ -175,6 +175,7 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||||||
{
|
{
|
||||||
const struct xt_lscan_mtinfo *info = par->matchinfo;
|
const struct xt_lscan_mtinfo *info = par->matchinfo;
|
||||||
enum ip_conntrack_info ctstate;
|
enum ip_conntrack_info ctstate;
|
||||||
|
const struct iphdr *iph = ip_hdr(skb);
|
||||||
const struct tcphdr *tcph;
|
const struct tcphdr *tcph;
|
||||||
struct nf_conn *ctdata;
|
struct nf_conn *ctdata;
|
||||||
struct tcphdr tcph_buf;
|
struct tcphdr tcph_buf;
|
||||||
@@ -182,10 +183,13 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||||||
tcph = skb_header_pointer(skb, par->thoff, sizeof(tcph_buf), &tcph_buf);
|
tcph = skb_header_pointer(skb, par->thoff, sizeof(tcph_buf), &tcph_buf);
|
||||||
if (tcph == NULL)
|
if (tcph == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
if (info->match_fl1 & LSCAN_FL1_MIRAI && iph != NULL &&
|
||||||
|
iph->version == 4 && iph->daddr == tcph->seq)
|
||||||
|
return true;
|
||||||
|
|
||||||
/* Check for invalid packets: -m conntrack --ctstate INVALID */
|
/* Check for invalid packets: -m conntrack --ctstate INVALID */
|
||||||
if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) {
|
if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) {
|
||||||
if (info->match_stealth)
|
if (info->match_fl1 & LSCAN_FL1_STEALTH)
|
||||||
return lscan_mt_stealth(tcph);
|
return lscan_mt_stealth(tcph);
|
||||||
/*
|
/*
|
||||||
* If @ctdata is NULL, we cannot match the other scan
|
* If @ctdata is NULL, we cannot match the other scan
|
||||||
@@ -215,17 +219,19 @@ lscan_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||||||
skb_nfmark(skb) = (skb_nfmark(skb) & ~packet_mask) ^ mark_seen;
|
skb_nfmark(skb) = (skb_nfmark(skb) & ~packet_mask) ^ mark_seen;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (info->match_syn && ctdata->mark == mark_synscan) ||
|
return (info->match_fl1 & LSCAN_FL1_STEALTH && ctdata->mark == mark_synscan) ||
|
||||||
(info->match_cn && ctdata->mark == mark_cnscan) ||
|
(info->match_fl3 & LSCAN_FL3_CN && ctdata->mark == mark_cnscan) ||
|
||||||
(info->match_gr && ctdata->mark == mark_grscan);
|
(info->match_fl4 & LSCAN_FL4_GR && ctdata->mark == mark_grscan);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lscan_mt_check(const struct xt_mtchk_param *par)
|
static int lscan_mt_check(const struct xt_mtchk_param *par)
|
||||||
{
|
{
|
||||||
const struct xt_lscan_mtinfo *info = par->matchinfo;
|
const struct xt_lscan_mtinfo *info = par->matchinfo;
|
||||||
|
|
||||||
if ((info->match_stealth & ~1) || (info->match_syn & ~1) ||
|
if ((info->match_fl1 & ~(LSCAN_FL1_STEALTH | LSCAN_FL1_MIRAI)) ||
|
||||||
(info->match_cn & ~1) || (info->match_gr & ~1)) {
|
(info->match_fl2 & ~LSCAN_FL2_SYN) ||
|
||||||
|
(info->match_fl3 & ~LSCAN_FL3_CN) ||
|
||||||
|
(info->match_fl4 & ~LSCAN_FL4_GR)) {
|
||||||
printk(KERN_WARNING PFX "Invalid flags\n");
|
printk(KERN_WARNING PFX "Invalid flags\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,16 @@
|
|||||||
#ifndef _LINUX_NETFILTER_XT_LSCAN_H
|
#ifndef _LINUX_NETFILTER_XT_LSCAN_H
|
||||||
#define _LINUX_NETFILTER_XT_LSCAN_H 1
|
#define _LINUX_NETFILTER_XT_LSCAN_H 1
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LSCAN_FL1_STEALTH = 1 << 0,
|
||||||
|
LSCAN_FL1_MIRAI = 1 << 1,
|
||||||
|
LSCAN_FL2_SYN = 1 << 0,
|
||||||
|
LSCAN_FL3_CN = 1 << 0,
|
||||||
|
LSCAN_FL4_GR = 1 << 0,
|
||||||
|
};
|
||||||
|
|
||||||
struct xt_lscan_mtinfo {
|
struct xt_lscan_mtinfo {
|
||||||
uint8_t match_stealth, match_syn, match_cn, match_gr;
|
uint8_t match_fl1, match_fl2, match_fl3, match_fl4;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _LINUX_NETFILTER_XT_LSCAN_H */
|
#endif /* _LINUX_NETFILTER_XT_LSCAN_H */
|
||||||
|
@@ -45,12 +45,12 @@ MODULE_ALIAS("ip6t_psd");
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep track of up to LIST_SIZE source addresses, using a hash table of
|
* Keep track of up to LIST_SIZE source addresses, using a hash table of
|
||||||
* HASH_SIZE entries for faster lookups, but limiting hash collisions to
|
* PSD_HASH_SIZE entries for faster lookups, but limiting hash collisions to
|
||||||
* HASH_MAX source addresses per the same hash value.
|
* HASH_MAX source addresses per the same hash value.
|
||||||
*/
|
*/
|
||||||
#define LIST_SIZE 0x100
|
#define LIST_SIZE 0x100
|
||||||
#define HASH_LOG 9
|
#define HASH_LOG 9
|
||||||
#define HASH_SIZE (1 << HASH_LOG)
|
#define PSD_HASH_SIZE (1 << HASH_LOG)
|
||||||
#define HASH_MAX 0x10
|
#define HASH_MAX 0x10
|
||||||
|
|
||||||
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
|
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
|
||||||
@@ -108,7 +108,7 @@ struct host6 {
|
|||||||
static struct {
|
static struct {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct host4 list[LIST_SIZE];
|
struct host4 list[LIST_SIZE];
|
||||||
struct host *hash[HASH_SIZE];
|
struct host *hash[PSD_HASH_SIZE];
|
||||||
int index;
|
int index;
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
@@ -143,13 +143,12 @@ static bool state6_alloc_mem(void)
|
|||||||
if (state6.list == NULL)
|
if (state6.list == NULL)
|
||||||
return false;
|
return false;
|
||||||
memset(state6.list, 0, LIST_SIZE * sizeof(struct host6));
|
memset(state6.list, 0, LIST_SIZE * sizeof(struct host6));
|
||||||
|
state6.hash = vmalloc(PSD_HASH_SIZE * sizeof(struct host *));
|
||||||
state6.hash = vmalloc(HASH_SIZE * sizeof(struct host*));
|
|
||||||
if (state6.hash == NULL) {
|
if (state6.hash == NULL) {
|
||||||
vfree(state6.list);
|
vfree(state6.list);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memset(state6.hash, 0, HASH_SIZE * sizeof(struct host *));
|
memset(state6.hash, 0, PSD_HASH_SIZE * sizeof(struct host *));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -167,8 +166,7 @@ static unsigned int hashfunc(__be32 addr)
|
|||||||
do {
|
do {
|
||||||
hash ^= value;
|
hash ^= value;
|
||||||
} while ((value >>= HASH_LOG) != 0);
|
} while ((value >>= HASH_LOG) != 0);
|
||||||
|
return hash & (PSD_HASH_SIZE - 1);
|
||||||
return hash & (HASH_SIZE - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int hashfunc6(const struct in6_addr *addr)
|
static inline unsigned int hashfunc6(const struct in6_addr *addr)
|
||||||
|
@@ -8,23 +8,45 @@ use IO::Handle;
|
|||||||
use Text::CSV_XS; # or trade for Text::CSV
|
use Text::CSV_XS; # or trade for Text::CSV
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
|
my $le32 = pack("V", 0x10000000);
|
||||||
|
my $be32 = pack("N", 0x10000000);
|
||||||
|
my $u32 = undef;
|
||||||
|
|
||||||
|
sub wantBE { return !$u32 || $u32 eq $be32; }
|
||||||
|
sub wantLE { return !$u32 || $u32 eq $le32; }
|
||||||
|
|
||||||
my $csv = Text::CSV_XS->new({
|
my $csv = Text::CSV_XS->new({
|
||||||
allow_whitespace => 1,
|
allow_whitespace => 1,
|
||||||
binary => 1,
|
binary => 1,
|
||||||
eol => $/,
|
eol => $/,
|
||||||
}); # or Text::CSV
|
}); # or Text::CSV
|
||||||
my $target_dir = ".";
|
my $target_dir = ".";
|
||||||
|
my $native_only = 0;
|
||||||
|
|
||||||
&Getopt::Long::Configure(qw(bundling));
|
&Getopt::Long::Configure(qw(bundling));
|
||||||
&GetOptions(
|
&GetOptions(
|
||||||
"D=s" => \$target_dir,
|
"D=s" => \$target_dir,
|
||||||
|
"n" => \$native_only,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!-d $target_dir) {
|
if (!-d $target_dir) {
|
||||||
print STDERR "Target directory $target_dir does not exist.\n";
|
print STDERR "Target directory $target_dir does not exist.\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
foreach (qw(LE BE)) {
|
my @dbs = qw(LE BE);
|
||||||
|
if ($native_only) {
|
||||||
|
$u32 = pack("L", 0x10000000);
|
||||||
|
if ($u32 eq $le32) {
|
||||||
|
@dbs = qw(LE);
|
||||||
|
} elsif ($u32 eq $be32) {
|
||||||
|
@dbs = qw(BE);
|
||||||
|
} else {
|
||||||
|
print STDERRR "Cannot determine endianness.\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (@dbs) {
|
||||||
my $dir = "$target_dir/$_";
|
my $dir = "$target_dir/$_";
|
||||||
if (!-e $dir && !mkdir($dir)) {
|
if (!-e $dir && !mkdir($dir)) {
|
||||||
print STDERR "Could not mkdir $dir: $!\n";
|
print STDERR "Could not mkdir $dir: $!\n";
|
||||||
@@ -80,11 +102,18 @@ sub dump_one
|
|||||||
scalar(@{$country->{pool_v6}}),
|
scalar(@{$country->{pool_v6}}),
|
||||||
$iso_code, $country->{name};
|
$iso_code, $country->{name};
|
||||||
|
|
||||||
|
if (wantLE) {
|
||||||
$file = "$target_dir/LE/".uc($iso_code).".iv6";
|
$file = "$target_dir/LE/".uc($iso_code).".iv6";
|
||||||
if (!open($fh_le, "> $file")) {
|
if (!open($fh_le, "> $file")) {
|
||||||
print STDERR "Error opening $file: $!\n";
|
print STDERR "Error opening $file: $!\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
foreach my $range (@{$country->{pool_v6}}) {
|
||||||
|
print $fh_le &ip6_swap($range->[0]), &ip6_swap($range->[1]);
|
||||||
|
}
|
||||||
|
close $fh_le;
|
||||||
|
}
|
||||||
|
if (wantBE) {
|
||||||
$file = "$target_dir/BE/".uc($iso_code).".iv6";
|
$file = "$target_dir/BE/".uc($iso_code).".iv6";
|
||||||
if (!open($fh_be, "> $file")) {
|
if (!open($fh_be, "> $file")) {
|
||||||
print STDERR "Error opening $file: $!\n";
|
print STDERR "Error opening $file: $!\n";
|
||||||
@@ -92,32 +121,37 @@ sub dump_one
|
|||||||
}
|
}
|
||||||
foreach my $range (@{$country->{pool_v6}}) {
|
foreach my $range (@{$country->{pool_v6}}) {
|
||||||
print $fh_be $range->[0], $range->[1];
|
print $fh_be $range->[0], $range->[1];
|
||||||
print $fh_le &ip6_swap($range->[0]), &ip6_swap($range->[1]);
|
|
||||||
}
|
}
|
||||||
close $fh_le;
|
|
||||||
close $fh_be;
|
close $fh_be;
|
||||||
|
}
|
||||||
|
|
||||||
printf "%5u IPv4 ranges for %s %s\n",
|
printf "%5u IPv4 ranges for %s %s\n",
|
||||||
scalar(@{$country->{pool_v4}}),
|
scalar(@{$country->{pool_v4}}),
|
||||||
$iso_code, $country->{name};
|
$iso_code, $country->{name};
|
||||||
|
|
||||||
|
if (wantLE) {
|
||||||
$file = "$target_dir/LE/".uc($iso_code).".iv4";
|
$file = "$target_dir/LE/".uc($iso_code).".iv4";
|
||||||
if (!open($fh_le, "> $file")) {
|
if (!open($fh_le, "> $file")) {
|
||||||
print STDERR "Error opening $file: $!\n";
|
print STDERR "Error opening $file: $!\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
foreach my $range (@{$country->{pool_v4}}) {
|
||||||
|
print $fh_le pack("VV", $range->[0], $range->[1]);
|
||||||
|
}
|
||||||
|
close $fh_le;
|
||||||
|
}
|
||||||
|
if (wantBE) {
|
||||||
$file = "$target_dir/BE/".uc($iso_code).".iv4";
|
$file = "$target_dir/BE/".uc($iso_code).".iv4";
|
||||||
if (!open($fh_be, "> $file")) {
|
if (!open($fh_be, "> $file")) {
|
||||||
print STDERR "Error opening $file: $!\n";
|
print STDERR "Error opening $file: $!\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
foreach my $range (@{$country->{pool_v4}}) {
|
foreach my $range (@{$country->{pool_v4}}) {
|
||||||
print $fh_le pack("VV", $range->[0], $range->[1]);
|
|
||||||
print $fh_be pack("NN", $range->[0], $range->[1]);
|
print $fh_be pack("NN", $range->[0], $range->[1]);
|
||||||
}
|
}
|
||||||
close $fh_le;
|
|
||||||
close $fh_be;
|
close $fh_be;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub ip6_pack
|
sub ip6_pack
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
.TH xtables-addons 8 "Not For Workgroups" "" "v2.12 (2017-01-11)"
|
.TH xtables-addons 8 "" "" "v2.15 (2021-02-05)"
|
||||||
.SH Name
|
.SH Name
|
||||||
Xtables-addons \(em additional extensions for iptables, ip6tables, etc.
|
Xtables-addons \(em additional extensions for iptables, ip6tables, etc.
|
||||||
.SH Targets
|
.SH Targets
|
||||||
|
Reference in New Issue
Block a user