mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-19 13:11:14 +00:00
Merge git://1984.lsi.us.es/net-next
This commit is contained in:
commit
a49d1a905e
15 changed files with 588 additions and 46 deletions
45
include/linux/netfilter/xt_HMARK.h
Normal file
45
include/linux/netfilter/xt_HMARK.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef XT_HMARK_H_
|
||||||
|
#define XT_HMARK_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
XT_HMARK_SADDR_MASK,
|
||||||
|
XT_HMARK_DADDR_MASK,
|
||||||
|
XT_HMARK_SPI,
|
||||||
|
XT_HMARK_SPI_MASK,
|
||||||
|
XT_HMARK_SPORT,
|
||||||
|
XT_HMARK_DPORT,
|
||||||
|
XT_HMARK_SPORT_MASK,
|
||||||
|
XT_HMARK_DPORT_MASK,
|
||||||
|
XT_HMARK_PROTO_MASK,
|
||||||
|
XT_HMARK_RND,
|
||||||
|
XT_HMARK_MODULUS,
|
||||||
|
XT_HMARK_OFFSET,
|
||||||
|
XT_HMARK_CT,
|
||||||
|
XT_HMARK_METHOD_L3,
|
||||||
|
XT_HMARK_METHOD_L3_4,
|
||||||
|
};
|
||||||
|
#define XT_HMARK_FLAG(flag) (1 << flag)
|
||||||
|
|
||||||
|
union hmark_ports {
|
||||||
|
struct {
|
||||||
|
__u16 src;
|
||||||
|
__u16 dst;
|
||||||
|
} p16;
|
||||||
|
__u32 v32;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xt_hmark_info {
|
||||||
|
union nf_inet_addr src_mask;
|
||||||
|
union nf_inet_addr dst_mask;
|
||||||
|
union hmark_ports port_mask;
|
||||||
|
union hmark_ports port_set;
|
||||||
|
__u32 flags;
|
||||||
|
__u16 proto_mask;
|
||||||
|
__u32 hashrnd;
|
||||||
|
__u32 hmodulus;
|
||||||
|
__u32 hoffset; /* Mark offset to start from */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* XT_HMARK_H_ */
|
|
@ -6,7 +6,11 @@
|
||||||
/* timings are in milliseconds. */
|
/* timings are in milliseconds. */
|
||||||
#define XT_HASHLIMIT_SCALE 10000
|
#define XT_HASHLIMIT_SCALE 10000
|
||||||
/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490
|
/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490
|
||||||
seconds, or one every 59 hours. */
|
* seconds, or one packet every 59 hours.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* packet length accounting is done in 16-byte steps */
|
||||||
|
#define XT_HASHLIMIT_BYTE_SHIFT 4
|
||||||
|
|
||||||
/* details of this structure hidden by the implementation */
|
/* details of this structure hidden by the implementation */
|
||||||
struct xt_hashlimit_htable;
|
struct xt_hashlimit_htable;
|
||||||
|
@ -17,6 +21,10 @@ enum {
|
||||||
XT_HASHLIMIT_HASH_SIP = 1 << 2,
|
XT_HASHLIMIT_HASH_SIP = 1 << 2,
|
||||||
XT_HASHLIMIT_HASH_SPT = 1 << 3,
|
XT_HASHLIMIT_HASH_SPT = 1 << 3,
|
||||||
XT_HASHLIMIT_INVERT = 1 << 4,
|
XT_HASHLIMIT_INVERT = 1 << 4,
|
||||||
|
XT_HASHLIMIT_BYTES = 1 << 5,
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
XT_HASHLIMIT_MAX = 1 << 6,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hashlimit_cfg {
|
struct hashlimit_cfg {
|
||||||
|
|
|
@ -298,9 +298,14 @@ ip6t_ext_hdr(u8 nexthdr)
|
||||||
(nexthdr == IPPROTO_DSTOPTS);
|
(nexthdr == IPPROTO_DSTOPTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IP6T_FH_F_FRAG = (1 << 0),
|
||||||
|
IP6T_FH_F_AUTH = (1 << 1),
|
||||||
|
};
|
||||||
|
|
||||||
/* find specified header and get offset to it */
|
/* find specified header and get offset to it */
|
||||||
extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
||||||
int target, unsigned short *fragoff);
|
int target, unsigned short *fragoff, int *fragflg);
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
#include <net/compat.h>
|
#include <net/compat.h>
|
||||||
|
|
|
@ -133,7 +133,7 @@ ip6_packet_match(const struct sk_buff *skb,
|
||||||
int protohdr;
|
int protohdr;
|
||||||
unsigned short _frag_off;
|
unsigned short _frag_off;
|
||||||
|
|
||||||
protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
|
protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off, NULL);
|
||||||
if (protohdr < 0) {
|
if (protohdr < 0) {
|
||||||
if (_frag_off == 0)
|
if (_frag_off == 0)
|
||||||
*hotdrop = true;
|
*hotdrop = true;
|
||||||
|
@ -362,6 +362,7 @@ ip6t_do_table(struct sk_buff *skb,
|
||||||
const struct xt_entry_match *ematch;
|
const struct xt_entry_match *ematch;
|
||||||
|
|
||||||
IP_NF_ASSERT(e);
|
IP_NF_ASSERT(e);
|
||||||
|
acpar.thoff = 0;
|
||||||
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
|
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
|
||||||
&acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
|
&acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
|
||||||
no_match:
|
no_match:
|
||||||
|
@ -2278,6 +2279,10 @@ static void __exit ip6_tables_fini(void)
|
||||||
* if target < 0. "last header" is transport protocol header, ESP, or
|
* if target < 0. "last header" is transport protocol header, ESP, or
|
||||||
* "No next header".
|
* "No next header".
|
||||||
*
|
*
|
||||||
|
* Note that *offset is used as input/output parameter. an if it is not zero,
|
||||||
|
* then it must be a valid offset to an inner IPv6 header. This can be used
|
||||||
|
* to explore inner IPv6 header, eg. ICMPv6 error messages.
|
||||||
|
*
|
||||||
* If target header is found, its offset is set in *offset and return protocol
|
* If target header is found, its offset is set in *offset and return protocol
|
||||||
* number. Otherwise, return -1.
|
* number. Otherwise, return -1.
|
||||||
*
|
*
|
||||||
|
@ -2289,17 +2294,33 @@ static void __exit ip6_tables_fini(void)
|
||||||
* *offset is meaningless and fragment offset is stored in *fragoff if fragoff
|
* *offset is meaningless and fragment offset is stored in *fragoff if fragoff
|
||||||
* isn't NULL.
|
* isn't NULL.
|
||||||
*
|
*
|
||||||
|
* if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG
|
||||||
|
* will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and
|
||||||
|
* target < 0, then this function will stop at the AH header.
|
||||||
*/
|
*/
|
||||||
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
||||||
int target, unsigned short *fragoff)
|
int target, unsigned short *fragoff, int *flags)
|
||||||
{
|
{
|
||||||
unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
|
unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
|
||||||
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
|
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
|
||||||
unsigned int len = skb->len - start;
|
unsigned int len;
|
||||||
|
|
||||||
if (fragoff)
|
if (fragoff)
|
||||||
*fragoff = 0;
|
*fragoff = 0;
|
||||||
|
|
||||||
|
if (*offset) {
|
||||||
|
struct ipv6hdr _ip6, *ip6;
|
||||||
|
|
||||||
|
ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
|
||||||
|
if (!ip6 || (ip6->version != 6)) {
|
||||||
|
printk(KERN_ERR "IPv6 header not found\n");
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
start = *offset + sizeof(struct ipv6hdr);
|
||||||
|
nexthdr = ip6->nexthdr;
|
||||||
|
}
|
||||||
|
len = skb->len - start;
|
||||||
|
|
||||||
while (nexthdr != target) {
|
while (nexthdr != target) {
|
||||||
struct ipv6_opt_hdr _hdr, *hp;
|
struct ipv6_opt_hdr _hdr, *hp;
|
||||||
unsigned int hdrlen;
|
unsigned int hdrlen;
|
||||||
|
@ -2316,6 +2337,9 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
||||||
if (nexthdr == NEXTHDR_FRAGMENT) {
|
if (nexthdr == NEXTHDR_FRAGMENT) {
|
||||||
unsigned short _frag_off;
|
unsigned short _frag_off;
|
||||||
__be16 *fp;
|
__be16 *fp;
|
||||||
|
|
||||||
|
if (flags) /* Indicate that this is a fragment */
|
||||||
|
*flags |= IP6T_FH_F_FRAG;
|
||||||
fp = skb_header_pointer(skb,
|
fp = skb_header_pointer(skb,
|
||||||
start+offsetof(struct frag_hdr,
|
start+offsetof(struct frag_hdr,
|
||||||
frag_off),
|
frag_off),
|
||||||
|
@ -2336,9 +2360,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
hdrlen = 8;
|
hdrlen = 8;
|
||||||
} else if (nexthdr == NEXTHDR_AUTH)
|
} else if (nexthdr == NEXTHDR_AUTH) {
|
||||||
|
if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0))
|
||||||
|
break;
|
||||||
hdrlen = (hp->hdrlen + 2) << 2;
|
hdrlen = (hp->hdrlen + 2) << 2;
|
||||||
else
|
} else
|
||||||
hdrlen = ipv6_optlen(hp);
|
hdrlen = ipv6_optlen(hp);
|
||||||
|
|
||||||
nexthdr = hp->nexthdr;
|
nexthdr = hp->nexthdr;
|
||||||
|
|
|
@ -41,11 +41,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
struct ip_auth_hdr _ah;
|
struct ip_auth_hdr _ah;
|
||||||
const struct ip_auth_hdr *ah;
|
const struct ip_auth_hdr *ah;
|
||||||
const struct ip6t_ah *ahinfo = par->matchinfo;
|
const struct ip6t_ah *ahinfo = par->matchinfo;
|
||||||
unsigned int ptr;
|
unsigned int ptr = 0;
|
||||||
unsigned int hdrlen = 0;
|
unsigned int hdrlen = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL);
|
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (err != -ENOENT)
|
if (err != -ENOENT)
|
||||||
par->hotdrop = true;
|
par->hotdrop = true;
|
||||||
|
|
|
@ -40,10 +40,10 @@ frag_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
struct frag_hdr _frag;
|
struct frag_hdr _frag;
|
||||||
const struct frag_hdr *fh;
|
const struct frag_hdr *fh;
|
||||||
const struct ip6t_frag *fraginfo = par->matchinfo;
|
const struct ip6t_frag *fraginfo = par->matchinfo;
|
||||||
unsigned int ptr;
|
unsigned int ptr = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL);
|
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (err != -ENOENT)
|
if (err != -ENOENT)
|
||||||
par->hotdrop = true;
|
par->hotdrop = true;
|
||||||
|
|
|
@ -50,7 +50,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
const struct ipv6_opt_hdr *oh;
|
const struct ipv6_opt_hdr *oh;
|
||||||
const struct ip6t_opts *optinfo = par->matchinfo;
|
const struct ip6t_opts *optinfo = par->matchinfo;
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
unsigned int ptr;
|
unsigned int ptr = 0;
|
||||||
unsigned int hdrlen = 0;
|
unsigned int hdrlen = 0;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
u8 _opttype;
|
u8 _opttype;
|
||||||
|
@ -62,7 +62,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
|
|
||||||
err = ipv6_find_hdr(skb, &ptr,
|
err = ipv6_find_hdr(skb, &ptr,
|
||||||
(par->match == &hbh_mt6_reg[0]) ?
|
(par->match == &hbh_mt6_reg[0]) ?
|
||||||
NEXTHDR_HOP : NEXTHDR_DEST, NULL);
|
NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (err != -ENOENT)
|
if (err != -ENOENT)
|
||||||
par->hotdrop = true;
|
par->hotdrop = true;
|
||||||
|
|
|
@ -42,14 +42,14 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
const struct ipv6_rt_hdr *rh;
|
const struct ipv6_rt_hdr *rh;
|
||||||
const struct ip6t_rt *rtinfo = par->matchinfo;
|
const struct ip6t_rt *rtinfo = par->matchinfo;
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
unsigned int ptr;
|
unsigned int ptr = 0;
|
||||||
unsigned int hdrlen = 0;
|
unsigned int hdrlen = 0;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
struct in6_addr _addr;
|
struct in6_addr _addr;
|
||||||
const struct in6_addr *ap;
|
const struct in6_addr *ap;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL);
|
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (err != -ENOENT)
|
if (err != -ENOENT)
|
||||||
par->hotdrop = true;
|
par->hotdrop = true;
|
||||||
|
|
|
@ -509,6 +509,21 @@ config NETFILTER_XT_TARGET_HL
|
||||||
since you can easily create immortal packets that loop
|
since you can easily create immortal packets that loop
|
||||||
forever on the network.
|
forever on the network.
|
||||||
|
|
||||||
|
config NETFILTER_XT_TARGET_HMARK
|
||||||
|
tristate '"HMARK" target support'
|
||||||
|
depends on (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)
|
||||||
|
depends on NETFILTER_ADVANCED
|
||||||
|
---help---
|
||||||
|
This option adds the "HMARK" target.
|
||||||
|
|
||||||
|
The target allows you to create rules in the "raw" and "mangle" tables
|
||||||
|
which set the skbuff mark by means of hash calculation within a given
|
||||||
|
range. The nfmark can influence the routing method (see "Use netfilter
|
||||||
|
MARK value as routing key") and can also be used by other subsystems to
|
||||||
|
change their behaviour.
|
||||||
|
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
config NETFILTER_XT_TARGET_IDLETIMER
|
config NETFILTER_XT_TARGET_IDLETIMER
|
||||||
tristate "IDLETIMER target support"
|
tristate "IDLETIMER target support"
|
||||||
depends on NETFILTER_ADVANCED
|
depends on NETFILTER_ADVANCED
|
||||||
|
|
|
@ -59,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
|
||||||
obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
|
obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
|
||||||
|
|
362
net/netfilter/xt_HMARK.c
Normal file
362
net/netfilter/xt_HMARK.c
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
/*
|
||||||
|
* xt_HMARK - Netfilter module to set mark by means of hashing
|
||||||
|
*
|
||||||
|
* (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
|
||||||
|
* (C) 2012 by Pablo Neira Ayuso <pablo@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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/icmp.h>
|
||||||
|
|
||||||
|
#include <linux/netfilter/x_tables.h>
|
||||||
|
#include <linux/netfilter/xt_HMARK.h>
|
||||||
|
|
||||||
|
#include <net/ip.h>
|
||||||
|
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
|
||||||
|
#include <net/netfilter/nf_conntrack.h>
|
||||||
|
#endif
|
||||||
|
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||||
|
#include <net/ipv6.h>
|
||||||
|
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
|
||||||
|
MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
|
||||||
|
MODULE_ALIAS("ipt_HMARK");
|
||||||
|
MODULE_ALIAS("ip6t_HMARK");
|
||||||
|
|
||||||
|
struct hmark_tuple {
|
||||||
|
u32 src;
|
||||||
|
u32 dst;
|
||||||
|
union hmark_ports uports;
|
||||||
|
uint8_t proto;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask)
|
||||||
|
{
|
||||||
|
return (addr32[0] & mask[0]) ^
|
||||||
|
(addr32[1] & mask[1]) ^
|
||||||
|
(addr32[2] & mask[2]) ^
|
||||||
|
(addr32[3] & mask[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask)
|
||||||
|
{
|
||||||
|
switch (l3num) {
|
||||||
|
case AF_INET:
|
||||||
|
return *addr32 & *mask;
|
||||||
|
case AF_INET6:
|
||||||
|
return hmark_addr6_mask(addr32, mask);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
|
||||||
|
const struct xt_hmark_info *info)
|
||||||
|
{
|
||||||
|
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
struct nf_conntrack_tuple *otuple;
|
||||||
|
struct nf_conntrack_tuple *rtuple;
|
||||||
|
|
||||||
|
if (ct == NULL || nf_ct_is_untracked(ct))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||||
|
rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
||||||
|
|
||||||
|
t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all,
|
||||||
|
info->src_mask.all);
|
||||||
|
t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all,
|
||||||
|
info->dst_mask.all);
|
||||||
|
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
t->proto = nf_ct_protonum(ct);
|
||||||
|
if (t->proto != IPPROTO_ICMP) {
|
||||||
|
t->uports.p16.src = otuple->src.u.all;
|
||||||
|
t->uports.p16.dst = rtuple->src.u.all;
|
||||||
|
t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
|
||||||
|
info->port_set.v32;
|
||||||
|
if (t->uports.p16.dst < t->uports.p16.src)
|
||||||
|
swap(t->uports.p16.dst, t->uports.p16.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
|
||||||
|
{
|
||||||
|
u32 hash;
|
||||||
|
|
||||||
|
if (t->dst < t->src)
|
||||||
|
swap(t->src, t->dst);
|
||||||
|
|
||||||
|
hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd);
|
||||||
|
hash = hash ^ (t->proto & info->proto_mask);
|
||||||
|
|
||||||
|
return (hash % info->hmodulus) + info->hoffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
|
||||||
|
struct hmark_tuple *t, const struct xt_hmark_info *info)
|
||||||
|
{
|
||||||
|
int protoff;
|
||||||
|
|
||||||
|
protoff = proto_ports_offset(t->proto);
|
||||||
|
if (protoff < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nhoff += protoff;
|
||||||
|
if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
|
||||||
|
info->port_set.v32;
|
||||||
|
|
||||||
|
if (t->uports.p16.dst < t->uports.p16.src)
|
||||||
|
swap(t->uports.p16.dst, t->uports.p16.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||||
|
static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
|
||||||
|
{
|
||||||
|
struct icmp6hdr *icmp6h, _ih6;
|
||||||
|
|
||||||
|
icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
|
||||||
|
if (icmp6h == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
|
||||||
|
*offset += sizeof(struct icmp6hdr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
|
||||||
|
const struct xt_hmark_info *info)
|
||||||
|
{
|
||||||
|
struct ipv6hdr *ip6, _ip6;
|
||||||
|
int flag = IP6T_FH_F_AUTH;
|
||||||
|
unsigned int nhoff = 0;
|
||||||
|
u16 fragoff = 0;
|
||||||
|
int nexthdr;
|
||||||
|
|
||||||
|
ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
|
||||||
|
nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
|
||||||
|
if (nexthdr < 0)
|
||||||
|
return 0;
|
||||||
|
/* No need to check for icmp errors on fragments */
|
||||||
|
if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
|
||||||
|
goto noicmp;
|
||||||
|
/* Use inner header in case of ICMP errors */
|
||||||
|
if (get_inner6_hdr(skb, &nhoff)) {
|
||||||
|
ip6 = skb_header_pointer(skb, nhoff, sizeof(_ip6), &_ip6);
|
||||||
|
if (ip6 == NULL)
|
||||||
|
return -1;
|
||||||
|
/* If AH present, use SPI like in ESP. */
|
||||||
|
flag = IP6T_FH_F_AUTH;
|
||||||
|
nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
|
||||||
|
if (nexthdr < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
noicmp:
|
||||||
|
t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.all);
|
||||||
|
t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.all);
|
||||||
|
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
t->proto = nexthdr;
|
||||||
|
if (t->proto == IPPROTO_ICMPV6)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (flag & IP6T_FH_F_FRAG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hmark_set_tuple_ports(skb, nhoff, t, info);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
|
||||||
|
{
|
||||||
|
const struct xt_hmark_info *info = par->targinfo;
|
||||||
|
struct hmark_tuple t;
|
||||||
|
|
||||||
|
memset(&t, 0, sizeof(struct hmark_tuple));
|
||||||
|
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
|
||||||
|
if (hmark_ct_set_htuple(skb, &t, info) < 0)
|
||||||
|
return XT_CONTINUE;
|
||||||
|
} else {
|
||||||
|
if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
|
||||||
|
return XT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb->mark = hmark_hash(&t, info);
|
||||||
|
return XT_CONTINUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
|
||||||
|
{
|
||||||
|
const struct icmphdr *icmph;
|
||||||
|
struct icmphdr _ih;
|
||||||
|
|
||||||
|
/* Not enough header? */
|
||||||
|
icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
|
||||||
|
if (icmph == NULL && icmph->type > NR_ICMP_TYPES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Error message? */
|
||||||
|
if (icmph->type != ICMP_DEST_UNREACH &&
|
||||||
|
icmph->type != ICMP_SOURCE_QUENCH &&
|
||||||
|
icmph->type != ICMP_TIME_EXCEEDED &&
|
||||||
|
icmph->type != ICMP_PARAMETERPROB &&
|
||||||
|
icmph->type != ICMP_REDIRECT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*nhoff += iphsz + sizeof(_ih);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
|
||||||
|
const struct xt_hmark_info *info)
|
||||||
|
{
|
||||||
|
struct iphdr *ip, _ip;
|
||||||
|
int nhoff = skb_network_offset(skb);
|
||||||
|
|
||||||
|
ip = (struct iphdr *) (skb->data + nhoff);
|
||||||
|
if (ip->protocol == IPPROTO_ICMP) {
|
||||||
|
/* Use inner header in case of ICMP errors */
|
||||||
|
if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
|
||||||
|
ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
|
||||||
|
if (ip == NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t->src = (__force u32) ip->saddr;
|
||||||
|
t->dst = (__force u32) ip->daddr;
|
||||||
|
|
||||||
|
t->src &= info->src_mask.ip;
|
||||||
|
t->dst &= info->dst_mask.ip;
|
||||||
|
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
t->proto = ip->protocol;
|
||||||
|
|
||||||
|
/* ICMP has no ports, skip */
|
||||||
|
if (t->proto == IPPROTO_ICMP)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* follow-up fragments don't contain ports, skip all fragments */
|
||||||
|
if (ip->frag_off & htons(IP_MF | IP_OFFSET))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
|
||||||
|
{
|
||||||
|
const struct xt_hmark_info *info = par->targinfo;
|
||||||
|
struct hmark_tuple t;
|
||||||
|
|
||||||
|
memset(&t, 0, sizeof(struct hmark_tuple));
|
||||||
|
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
|
||||||
|
if (hmark_ct_set_htuple(skb, &t, info) < 0)
|
||||||
|
return XT_CONTINUE;
|
||||||
|
} else {
|
||||||
|
if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
|
||||||
|
return XT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb->mark = hmark_hash(&t, info);
|
||||||
|
return XT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hmark_tg_check(const struct xt_tgchk_param *par)
|
||||||
|
{
|
||||||
|
const struct xt_hmark_info *info = par->targinfo;
|
||||||
|
|
||||||
|
if (!info->hmodulus) {
|
||||||
|
pr_info("xt_HMARK: hash modulus can't be zero\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (info->proto_mask &&
|
||||||
|
(info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))) {
|
||||||
|
pr_info("xt_HMARK: proto mask must be zero with L3 mode\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK) &&
|
||||||
|
(info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK) |
|
||||||
|
XT_HMARK_FLAG(XT_HMARK_DPORT_MASK)))) {
|
||||||
|
pr_info("xt_HMARK: spi-mask and port-mask can't be combined\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI) &&
|
||||||
|
(info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT) |
|
||||||
|
XT_HMARK_FLAG(XT_HMARK_DPORT)))) {
|
||||||
|
pr_info("xt_HMARK: spi-set and port-set can't be combined\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct xt_target hmark_tg_reg[] __read_mostly = {
|
||||||
|
{
|
||||||
|
.name = "HMARK",
|
||||||
|
.family = NFPROTO_IPV4,
|
||||||
|
.target = hmark_tg_v4,
|
||||||
|
.targetsize = sizeof(struct xt_hmark_info),
|
||||||
|
.checkentry = hmark_tg_check,
|
||||||
|
.me = THIS_MODULE,
|
||||||
|
},
|
||||||
|
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||||
|
{
|
||||||
|
.name = "HMARK",
|
||||||
|
.family = NFPROTO_IPV6,
|
||||||
|
.target = hmark_tg_v6,
|
||||||
|
.targetsize = sizeof(struct xt_hmark_info),
|
||||||
|
.checkentry = hmark_tg_check,
|
||||||
|
.me = THIS_MODULE,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init hmark_tg_init(void)
|
||||||
|
{
|
||||||
|
return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit hmark_tg_exit(void)
|
||||||
|
{
|
||||||
|
xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(hmark_tg_init);
|
||||||
|
module_exit(hmark_tg_exit);
|
|
@ -282,10 +282,10 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
const struct in6_addr *laddr;
|
const struct in6_addr *laddr;
|
||||||
__be16 lport;
|
__be16 lport;
|
||||||
int thoff;
|
int thoff = 0;
|
||||||
int tproto;
|
int tproto;
|
||||||
|
|
||||||
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
|
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
|
||||||
if (tproto < 0) {
|
if (tproto < 0) {
|
||||||
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
|
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
|
||||||
return NF_DROP;
|
return NF_DROP;
|
||||||
|
|
|
@ -388,9 +388,20 @@ static void htable_put(struct xt_hashlimit_htable *hinfo)
|
||||||
|
|
||||||
#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
|
#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
|
||||||
|
|
||||||
|
/* in byte mode, the lowest possible rate is one packet/second.
|
||||||
|
* credit_cap is used as a counter that tells us how many times we can
|
||||||
|
* refill the "credits available" counter when it becomes empty.
|
||||||
|
*/
|
||||||
|
#define MAX_CPJ_BYTES (0xFFFFFFFF / HZ)
|
||||||
|
#define CREDITS_PER_JIFFY_BYTES POW2_BELOW32(MAX_CPJ_BYTES)
|
||||||
|
|
||||||
|
static u32 xt_hashlimit_len_to_chunks(u32 len)
|
||||||
|
{
|
||||||
|
return (len >> XT_HASHLIMIT_BYTE_SHIFT) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Precision saver. */
|
/* Precision saver. */
|
||||||
static inline u_int32_t
|
static u32 user2credits(u32 user)
|
||||||
user2credits(u_int32_t user)
|
|
||||||
{
|
{
|
||||||
/* If multiplying would overflow... */
|
/* If multiplying would overflow... */
|
||||||
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
||||||
|
@ -400,12 +411,53 @@ user2credits(u_int32_t user)
|
||||||
return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE;
|
return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now)
|
static u32 user2credits_byte(u32 user)
|
||||||
{
|
{
|
||||||
dh->rateinfo.credit += (now - dh->rateinfo.prev) * CREDITS_PER_JIFFY;
|
u64 us = user;
|
||||||
if (dh->rateinfo.credit > dh->rateinfo.credit_cap)
|
us *= HZ * CREDITS_PER_JIFFY_BYTES;
|
||||||
dh->rateinfo.credit = dh->rateinfo.credit_cap;
|
return (u32) (us >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, u32 mode)
|
||||||
|
{
|
||||||
|
unsigned long delta = now - dh->rateinfo.prev;
|
||||||
|
u32 cap;
|
||||||
|
|
||||||
|
if (delta == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
dh->rateinfo.prev = now;
|
dh->rateinfo.prev = now;
|
||||||
|
|
||||||
|
if (mode & XT_HASHLIMIT_BYTES) {
|
||||||
|
u32 tmp = dh->rateinfo.credit;
|
||||||
|
dh->rateinfo.credit += CREDITS_PER_JIFFY_BYTES * delta;
|
||||||
|
cap = CREDITS_PER_JIFFY_BYTES * HZ;
|
||||||
|
if (tmp >= dh->rateinfo.credit) {/* overflow */
|
||||||
|
dh->rateinfo.credit = cap;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dh->rateinfo.credit += delta * CREDITS_PER_JIFFY;
|
||||||
|
cap = dh->rateinfo.credit_cap;
|
||||||
|
}
|
||||||
|
if (dh->rateinfo.credit > cap)
|
||||||
|
dh->rateinfo.credit = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rateinfo_init(struct dsthash_ent *dh,
|
||||||
|
struct xt_hashlimit_htable *hinfo)
|
||||||
|
{
|
||||||
|
dh->rateinfo.prev = jiffies;
|
||||||
|
if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {
|
||||||
|
dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ;
|
||||||
|
dh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg);
|
||||||
|
dh->rateinfo.credit_cap = hinfo->cfg.burst;
|
||||||
|
} else {
|
||||||
|
dh->rateinfo.credit = user2credits(hinfo->cfg.avg *
|
||||||
|
hinfo->cfg.burst);
|
||||||
|
dh->rateinfo.cost = user2credits(hinfo->cfg.avg);
|
||||||
|
dh->rateinfo.credit_cap = dh->rateinfo.credit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __be32 maskl(__be32 a, unsigned int l)
|
static inline __be32 maskl(__be32 a, unsigned int l)
|
||||||
|
@ -511,6 +563,21 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)
|
||||||
|
{
|
||||||
|
u64 tmp = xt_hashlimit_len_to_chunks(len);
|
||||||
|
tmp = tmp * dh->rateinfo.cost;
|
||||||
|
|
||||||
|
if (unlikely(tmp > CREDITS_PER_JIFFY_BYTES * HZ))
|
||||||
|
tmp = CREDITS_PER_JIFFY_BYTES * HZ;
|
||||||
|
|
||||||
|
if (dh->rateinfo.credit < tmp && dh->rateinfo.credit_cap) {
|
||||||
|
dh->rateinfo.credit_cap--;
|
||||||
|
dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ;
|
||||||
|
}
|
||||||
|
return (u32) tmp;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
{
|
{
|
||||||
|
@ -519,6 +586,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
unsigned long now = jiffies;
|
unsigned long now = jiffies;
|
||||||
struct dsthash_ent *dh;
|
struct dsthash_ent *dh;
|
||||||
struct dsthash_dst dst;
|
struct dsthash_dst dst;
|
||||||
|
u32 cost;
|
||||||
|
|
||||||
if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0)
|
if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0)
|
||||||
goto hotdrop;
|
goto hotdrop;
|
||||||
|
@ -532,21 +600,21 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
goto hotdrop;
|
goto hotdrop;
|
||||||
}
|
}
|
||||||
dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
|
dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
|
||||||
dh->rateinfo.prev = jiffies;
|
rateinfo_init(dh, hinfo);
|
||||||
dh->rateinfo.credit = user2credits(hinfo->cfg.avg *
|
|
||||||
hinfo->cfg.burst);
|
|
||||||
dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg *
|
|
||||||
hinfo->cfg.burst);
|
|
||||||
dh->rateinfo.cost = user2credits(hinfo->cfg.avg);
|
|
||||||
} else {
|
} else {
|
||||||
/* update expiration timeout */
|
/* update expiration timeout */
|
||||||
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
|
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
|
||||||
rateinfo_recalc(dh, now);
|
rateinfo_recalc(dh, now, hinfo->cfg.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dh->rateinfo.credit >= dh->rateinfo.cost) {
|
if (info->cfg.mode & XT_HASHLIMIT_BYTES)
|
||||||
|
cost = hashlimit_byte_cost(skb->len, dh);
|
||||||
|
else
|
||||||
|
cost = dh->rateinfo.cost;
|
||||||
|
|
||||||
|
if (dh->rateinfo.credit >= cost) {
|
||||||
/* below the limit */
|
/* below the limit */
|
||||||
dh->rateinfo.credit -= dh->rateinfo.cost;
|
dh->rateinfo.credit -= cost;
|
||||||
spin_unlock(&dh->lock);
|
spin_unlock(&dh->lock);
|
||||||
rcu_read_unlock_bh();
|
rcu_read_unlock_bh();
|
||||||
return !(info->cfg.mode & XT_HASHLIMIT_INVERT);
|
return !(info->cfg.mode & XT_HASHLIMIT_INVERT);
|
||||||
|
@ -568,14 +636,6 @@ static int hashlimit_mt_check(const struct xt_mtchk_param *par)
|
||||||
struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
|
struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Check for overflow. */
|
|
||||||
if (info->cfg.burst == 0 ||
|
|
||||||
user2credits(info->cfg.avg * info->cfg.burst) <
|
|
||||||
user2credits(info->cfg.avg)) {
|
|
||||||
pr_info("overflow, try lower: %u/%u\n",
|
|
||||||
info->cfg.avg, info->cfg.burst);
|
|
||||||
return -ERANGE;
|
|
||||||
}
|
|
||||||
if (info->cfg.gc_interval == 0 || info->cfg.expire == 0)
|
if (info->cfg.gc_interval == 0 || info->cfg.expire == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (info->name[sizeof(info->name)-1] != '\0')
|
if (info->name[sizeof(info->name)-1] != '\0')
|
||||||
|
@ -588,6 +648,26 @@ static int hashlimit_mt_check(const struct xt_mtchk_param *par)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->cfg.mode >= XT_HASHLIMIT_MAX) {
|
||||||
|
pr_info("Unknown mode mask %X, kernel too old?\n",
|
||||||
|
info->cfg.mode);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for overflow. */
|
||||||
|
if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
|
||||||
|
if (user2credits_byte(info->cfg.avg) == 0) {
|
||||||
|
pr_info("overflow, rate too high: %u\n", info->cfg.avg);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else if (info->cfg.burst == 0 ||
|
||||||
|
user2credits(info->cfg.avg * info->cfg.burst) <
|
||||||
|
user2credits(info->cfg.avg)) {
|
||||||
|
pr_info("overflow, try lower: %u/%u\n",
|
||||||
|
info->cfg.avg, info->cfg.burst);
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&hashlimit_mutex);
|
mutex_lock(&hashlimit_mutex);
|
||||||
info->hinfo = htable_find_get(net, info->name, par->family);
|
info->hinfo = htable_find_get(net, info->name, par->family);
|
||||||
if (info->hinfo == NULL) {
|
if (info->hinfo == NULL) {
|
||||||
|
@ -680,10 +760,11 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
|
||||||
struct seq_file *s)
|
struct seq_file *s)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
const struct xt_hashlimit_htable *ht = s->private;
|
||||||
|
|
||||||
spin_lock(&ent->lock);
|
spin_lock(&ent->lock);
|
||||||
/* recalculate to show accurate numbers */
|
/* recalculate to show accurate numbers */
|
||||||
rateinfo_recalc(ent, jiffies);
|
rateinfo_recalc(ent, jiffies, ht->cfg.mode);
|
||||||
|
|
||||||
switch (family) {
|
switch (family) {
|
||||||
case NFPROTO_IPV4:
|
case NFPROTO_IPV4:
|
||||||
|
|
|
@ -88,8 +88,7 @@ limit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Precision saver. */
|
/* Precision saver. */
|
||||||
static u_int32_t
|
static u32 user2credits(u32 user)
|
||||||
user2credits(u_int32_t user)
|
|
||||||
{
|
{
|
||||||
/* If multiplying would overflow... */
|
/* If multiplying would overflow... */
|
||||||
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
||||||
|
@ -123,7 +122,7 @@ static int limit_mt_check(const struct xt_mtchk_param *par)
|
||||||
128. */
|
128. */
|
||||||
priv->prev = jiffies;
|
priv->prev = jiffies;
|
||||||
priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
|
priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
|
||||||
r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
|
r->credit_cap = priv->credit; /* Credits full. */
|
||||||
r->cost = user2credits(r->avg);
|
r->cost = user2credits(r->avg);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -263,10 +263,10 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct in6_addr *daddr, *saddr;
|
struct in6_addr *daddr, *saddr;
|
||||||
__be16 dport, sport;
|
__be16 dport, sport;
|
||||||
int thoff, tproto;
|
int thoff = 0, tproto;
|
||||||
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
|
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
|
||||||
|
|
||||||
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
|
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
|
||||||
if (tproto < 0) {
|
if (tproto < 0) {
|
||||||
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
|
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
|
||||||
return NF_DROP;
|
return NF_DROP;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue