netfilter: conntrack: remove the l4proto->new() function
->new() gets invoked after ->error() and before ->packet() if a conntrack lookup has found no result for the tuple. We can fold it into ->packet() -- the packet() implementations can check if the conntrack is confirmed (new) or not (already in hash). If its unconfirmed, the conntrack isn't in the hash yet so current skb created a new conntrack entry. Only relevant side effect -- if packet() doesn't return NF_ACCEPT but -NF_ACCEPT (or drop), while the conntrack was just created, then the newly allocated conntrack is freed right away, rather than not created in the first place. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Родитель
93e66024b0
Коммит
9976fc6e6e
|
@ -48,11 +48,6 @@ struct nf_conntrack_l4proto {
|
||||||
enum ip_conntrack_info ctinfo,
|
enum ip_conntrack_info ctinfo,
|
||||||
const struct nf_hook_state *state);
|
const struct nf_hook_state *state);
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found;
|
|
||||||
* returns TRUE if it's OK. If so, packet() called next. */
|
|
||||||
bool (*new)(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff);
|
|
||||||
|
|
||||||
/* Called when a conntrack entry is destroyed */
|
/* Called when a conntrack entry is destroyed */
|
||||||
void (*destroy)(struct nf_conn *ct);
|
void (*destroy)(struct nf_conn *ct);
|
||||||
|
|
||||||
|
|
|
@ -1370,12 +1370,6 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
|
||||||
|
|
||||||
timeout_ext = tmpl ? nf_ct_timeout_find(tmpl) : NULL;
|
timeout_ext = tmpl ? nf_ct_timeout_find(tmpl) : NULL;
|
||||||
|
|
||||||
if (!l4proto->new(ct, skb, dataoff)) {
|
|
||||||
nf_conntrack_free(ct);
|
|
||||||
pr_debug("can't track with proto module\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout_ext)
|
if (timeout_ext)
|
||||||
nf_ct_timeout_ext_add(ct, rcu_dereference(timeout_ext->timeout),
|
nf_ct_timeout_ext_add(ct, rcu_dereference(timeout_ext->timeout),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
|
|
|
@ -389,18 +389,15 @@ static inline struct nf_dccp_net *dccp_pernet(struct net *net)
|
||||||
return &net->ct.nf_ct_proto.dccp;
|
return &net->ct.nf_ct_proto.dccp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
static noinline bool
|
||||||
unsigned int dataoff)
|
dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
||||||
|
const struct dccp_hdr *dh)
|
||||||
{
|
{
|
||||||
struct net *net = nf_ct_net(ct);
|
struct net *net = nf_ct_net(ct);
|
||||||
struct nf_dccp_net *dn;
|
struct nf_dccp_net *dn;
|
||||||
struct dccp_hdr _dh, *dh;
|
|
||||||
const char *msg;
|
const char *msg;
|
||||||
u_int8_t state;
|
u_int8_t state;
|
||||||
|
|
||||||
dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
|
|
||||||
BUG_ON(dh == NULL);
|
|
||||||
|
|
||||||
state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
|
state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
|
||||||
switch (state) {
|
switch (state) {
|
||||||
default:
|
default:
|
||||||
|
@ -449,8 +446,12 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
|
||||||
unsigned int *timeouts;
|
unsigned int *timeouts;
|
||||||
|
|
||||||
dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
|
dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
|
||||||
BUG_ON(dh == NULL);
|
if (!dh)
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
type = dh->dccph_type;
|
type = dh->dccph_type;
|
||||||
|
if (!nf_ct_is_confirmed(ct) && !dccp_new(ct, skb, dh))
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
|
||||||
if (type == DCCP_PKT_RESET &&
|
if (type == DCCP_PKT_RESET &&
|
||||||
!test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
|
!test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
|
||||||
|
@ -850,7 +851,6 @@ static struct nf_proto_net *dccp_get_net_proto(struct net *net)
|
||||||
const struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 = {
|
const struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 = {
|
||||||
.l3proto = AF_INET,
|
.l3proto = AF_INET,
|
||||||
.l4proto = IPPROTO_DCCP,
|
.l4proto = IPPROTO_DCCP,
|
||||||
.new = dccp_new,
|
|
||||||
.packet = dccp_packet,
|
.packet = dccp_packet,
|
||||||
.error = dccp_error,
|
.error = dccp_error,
|
||||||
.can_early_drop = dccp_can_early_drop,
|
.can_early_drop = dccp_can_early_drop,
|
||||||
|
@ -883,7 +883,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_dccp4);
|
||||||
const struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 = {
|
const struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 = {
|
||||||
.l3proto = AF_INET6,
|
.l3proto = AF_INET6,
|
||||||
.l4proto = IPPROTO_DCCP,
|
.l4proto = IPPROTO_DCCP,
|
||||||
.new = dccp_new,
|
|
||||||
.packet = dccp_packet,
|
.packet = dccp_packet,
|
||||||
.error = dccp_error,
|
.error = dccp_error,
|
||||||
.can_early_drop = dccp_can_early_drop,
|
.can_early_drop = dccp_can_early_drop,
|
||||||
|
|
|
@ -51,6 +51,12 @@ static int generic_packet(struct nf_conn *ct,
|
||||||
{
|
{
|
||||||
const unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
const unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
||||||
|
|
||||||
|
if (!nf_generic_should_process(nf_ct_protonum(ct))) {
|
||||||
|
pr_warn_once("conntrack: generic helper won't handle protocol %d. Please consider loading the specific helper module.\n",
|
||||||
|
nf_ct_protonum(ct));
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
timeout = &generic_pernet(nf_ct_net(ct))->timeout;
|
timeout = &generic_pernet(nf_ct_net(ct))->timeout;
|
||||||
|
|
||||||
|
@ -58,19 +64,6 @@ static int generic_packet(struct nf_conn *ct,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
ret = nf_generic_should_process(nf_ct_protonum(ct));
|
|
||||||
if (!ret)
|
|
||||||
pr_warn_once("conntrack: generic helper won't handle protocol %d. Please consider loading the specific helper module.\n",
|
|
||||||
nf_ct_protonum(ct));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
||||||
|
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
|
@ -164,7 +157,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_generic =
|
||||||
.l4proto = 255,
|
.l4proto = 255,
|
||||||
.pkt_to_tuple = generic_pkt_to_tuple,
|
.pkt_to_tuple = generic_pkt_to_tuple,
|
||||||
.packet = generic_packet,
|
.packet = generic_packet,
|
||||||
.new = generic_new,
|
|
||||||
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
||||||
.ctnl_timeout = {
|
.ctnl_timeout = {
|
||||||
.nlattr_to_obj = generic_timeout_nlattr_to_obj,
|
.nlattr_to_obj = generic_timeout_nlattr_to_obj,
|
||||||
|
|
|
@ -238,6 +238,18 @@ static int gre_packet(struct nf_conn *ct,
|
||||||
enum ip_conntrack_info ctinfo,
|
enum ip_conntrack_info ctinfo,
|
||||||
const struct nf_hook_state *state)
|
const struct nf_hook_state *state)
|
||||||
{
|
{
|
||||||
|
if (!nf_ct_is_confirmed(ct)) {
|
||||||
|
unsigned int *timeouts = nf_ct_timeout_lookup(ct);
|
||||||
|
|
||||||
|
if (!timeouts)
|
||||||
|
timeouts = gre_get_timeouts(nf_ct_net(ct));
|
||||||
|
|
||||||
|
/* initialize to sane value. Ideally a conntrack helper
|
||||||
|
* (e.g. in case of pptp) is increasing them */
|
||||||
|
ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED];
|
||||||
|
ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED];
|
||||||
|
}
|
||||||
|
|
||||||
/* If we've seen traffic both ways, this is a GRE connection.
|
/* If we've seen traffic both ways, this is a GRE connection.
|
||||||
* Extend timeout. */
|
* Extend timeout. */
|
||||||
if (ct->status & IPS_SEEN_REPLY) {
|
if (ct->status & IPS_SEEN_REPLY) {
|
||||||
|
@ -253,26 +265,6 @@ static int gre_packet(struct nf_conn *ct,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool gre_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
unsigned int *timeouts = nf_ct_timeout_lookup(ct);
|
|
||||||
|
|
||||||
if (!timeouts)
|
|
||||||
timeouts = gre_get_timeouts(nf_ct_net(ct));
|
|
||||||
|
|
||||||
pr_debug(": ");
|
|
||||||
nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
|
||||||
|
|
||||||
/* initialize to sane value. Ideally a conntrack helper
|
|
||||||
* (e.g. in case of pptp) is increasing them */
|
|
||||||
ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED];
|
|
||||||
ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called when a conntrack entry has already been removed from the hashes
|
/* Called when a conntrack entry has already been removed from the hashes
|
||||||
* and is about to be deleted from memory */
|
* and is about to be deleted from memory */
|
||||||
static void gre_destroy(struct nf_conn *ct)
|
static void gre_destroy(struct nf_conn *ct)
|
||||||
|
@ -359,7 +351,6 @@ static const struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 = {
|
||||||
.print_conntrack = gre_print_conntrack,
|
.print_conntrack = gre_print_conntrack,
|
||||||
#endif
|
#endif
|
||||||
.packet = gre_packet,
|
.packet = gre_packet,
|
||||||
.new = gre_new,
|
|
||||||
.destroy = gre_destroy,
|
.destroy = gre_destroy,
|
||||||
.me = THIS_MODULE,
|
.me = THIS_MODULE,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
|
|
|
@ -72,11 +72,6 @@ static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int *icmp_get_timeouts(struct net *net)
|
|
||||||
{
|
|
||||||
return &icmp_pernet(net)->timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns verdict for packet, or -1 for invalid. */
|
/* Returns verdict for packet, or -1 for invalid. */
|
||||||
static int icmp_packet(struct nf_conn *ct,
|
static int icmp_packet(struct nf_conn *ct,
|
||||||
const struct sk_buff *skb,
|
const struct sk_buff *skb,
|
||||||
|
@ -88,19 +83,6 @@ static int icmp_packet(struct nf_conn *ct,
|
||||||
successful reply to avoid excessive conntrackd traffic
|
successful reply to avoid excessive conntrackd traffic
|
||||||
and also to handle correctly ICMP echo reply duplicates. */
|
and also to handle correctly ICMP echo reply duplicates. */
|
||||||
unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
||||||
|
|
||||||
if (!timeout)
|
|
||||||
timeout = icmp_get_timeouts(nf_ct_net(ct));
|
|
||||||
|
|
||||||
nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
|
|
||||||
|
|
||||||
return NF_ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
static const u_int8_t valid_new[] = {
|
static const u_int8_t valid_new[] = {
|
||||||
[ICMP_ECHO] = 1,
|
[ICMP_ECHO] = 1,
|
||||||
[ICMP_TIMESTAMP] = 1,
|
[ICMP_TIMESTAMP] = 1,
|
||||||
|
@ -114,9 +96,14 @@ static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
||||||
pr_debug("icmp: can't create new conn with type %u\n",
|
pr_debug("icmp: can't create new conn with type %u\n",
|
||||||
ct->tuplehash[0].tuple.dst.u.icmp.type);
|
ct->tuplehash[0].tuple.dst.u.icmp.type);
|
||||||
nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
|
nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
|
||||||
return false;
|
return -NF_ACCEPT;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
if (!timeout)
|
||||||
|
timeout = &icmp_pernet(nf_ct_net(ct))->timeout;
|
||||||
|
|
||||||
|
nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
|
||||||
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
|
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
|
||||||
|
@ -368,7 +355,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp =
|
||||||
.pkt_to_tuple = icmp_pkt_to_tuple,
|
.pkt_to_tuple = icmp_pkt_to_tuple,
|
||||||
.invert_tuple = icmp_invert_tuple,
|
.invert_tuple = icmp_invert_tuple,
|
||||||
.packet = icmp_packet,
|
.packet = icmp_packet,
|
||||||
.new = icmp_new,
|
|
||||||
.error = icmp_error,
|
.error = icmp_error,
|
||||||
.destroy = NULL,
|
.destroy = NULL,
|
||||||
.me = NULL,
|
.me = NULL,
|
||||||
|
|
|
@ -98,6 +98,22 @@ static int icmpv6_packet(struct nf_conn *ct,
|
||||||
const struct nf_hook_state *state)
|
const struct nf_hook_state *state)
|
||||||
{
|
{
|
||||||
unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
unsigned int *timeout = nf_ct_timeout_lookup(ct);
|
||||||
|
static const u8 valid_new[] = {
|
||||||
|
[ICMPV6_ECHO_REQUEST - 128] = 1,
|
||||||
|
[ICMPV6_NI_QUERY - 128] = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!nf_ct_is_confirmed(ct)) {
|
||||||
|
int type = ct->tuplehash[0].tuple.dst.u.icmp.type - 128;
|
||||||
|
|
||||||
|
if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
|
||||||
|
/* Can't create a new ICMPv6 `conn' with this. */
|
||||||
|
pr_debug("icmpv6: can't create new conn with type %u\n",
|
||||||
|
type + 128);
|
||||||
|
nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
timeout = icmpv6_get_timeouts(nf_ct_net(ct));
|
timeout = icmpv6_get_timeouts(nf_ct_net(ct));
|
||||||
|
@ -110,26 +126,6 @@ static int icmpv6_packet(struct nf_conn *ct,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
static const u_int8_t valid_new[] = {
|
|
||||||
[ICMPV6_ECHO_REQUEST - 128] = 1,
|
|
||||||
[ICMPV6_NI_QUERY - 128] = 1
|
|
||||||
};
|
|
||||||
int type = ct->tuplehash[0].tuple.dst.u.icmp.type - 128;
|
|
||||||
|
|
||||||
if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
|
|
||||||
/* Can't create a new ICMPv6 `conn' with this. */
|
|
||||||
pr_debug("icmpv6: can't create new conn with type %u\n",
|
|
||||||
type + 128);
|
|
||||||
nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
|
icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
|
@ -370,7 +366,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 =
|
||||||
.pkt_to_tuple = icmpv6_pkt_to_tuple,
|
.pkt_to_tuple = icmpv6_pkt_to_tuple,
|
||||||
.invert_tuple = icmpv6_invert_tuple,
|
.invert_tuple = icmpv6_invert_tuple,
|
||||||
.packet = icmpv6_packet,
|
.packet = icmpv6_packet,
|
||||||
.new = icmpv6_new,
|
|
||||||
.error = icmpv6_error,
|
.error = icmpv6_error,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
.tuple_to_nlattr = icmpv6_tuple_to_nlattr,
|
.tuple_to_nlattr = icmpv6_tuple_to_nlattr,
|
||||||
|
|
|
@ -273,6 +273,63 @@ static int sctp_new_state(enum ip_conntrack_dir dir,
|
||||||
return sctp_conntracks[dir][i][cur_state];
|
return sctp_conntracks[dir][i][cur_state];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't need lock here: this conntrack not in circulation yet */
|
||||||
|
static noinline bool
|
||||||
|
sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
||||||
|
const struct sctphdr *sh, unsigned int dataoff)
|
||||||
|
{
|
||||||
|
enum sctp_conntrack new_state;
|
||||||
|
const struct sctp_chunkhdr *sch;
|
||||||
|
struct sctp_chunkhdr _sch;
|
||||||
|
u32 offset, count;
|
||||||
|
|
||||||
|
memset(&ct->proto.sctp, 0, sizeof(ct->proto.sctp));
|
||||||
|
new_state = SCTP_CONNTRACK_MAX;
|
||||||
|
for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) {
|
||||||
|
new_state = sctp_new_state(IP_CT_DIR_ORIGINAL,
|
||||||
|
SCTP_CONNTRACK_NONE, sch->type);
|
||||||
|
|
||||||
|
/* Invalid: delete conntrack */
|
||||||
|
if (new_state == SCTP_CONNTRACK_NONE ||
|
||||||
|
new_state == SCTP_CONNTRACK_MAX) {
|
||||||
|
pr_debug("nf_conntrack_sctp: invalid new deleting.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the vtag into the state info */
|
||||||
|
if (sch->type == SCTP_CID_INIT) {
|
||||||
|
struct sctp_inithdr _inithdr, *ih;
|
||||||
|
/* Sec 8.5.1 (A) */
|
||||||
|
if (sh->vtag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ih = skb_header_pointer(skb, offset + sizeof(_sch),
|
||||||
|
sizeof(_inithdr), &_inithdr);
|
||||||
|
if (!ih)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pr_debug("Setting vtag %x for new conn\n",
|
||||||
|
ih->init_tag);
|
||||||
|
|
||||||
|
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag;
|
||||||
|
} else if (sch->type == SCTP_CID_HEARTBEAT) {
|
||||||
|
pr_debug("Setting vtag %x for secondary conntrack\n",
|
||||||
|
sh->vtag);
|
||||||
|
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag;
|
||||||
|
} else {
|
||||||
|
/* If it is a shutdown ack OOTB packet, we expect a return
|
||||||
|
shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
|
||||||
|
pr_debug("Setting vtag %x for new conn OOTB\n",
|
||||||
|
sh->vtag);
|
||||||
|
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
|
||||||
|
}
|
||||||
|
|
||||||
|
ct->proto.sctp.state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns verdict for packet, or -NF_ACCEPT for invalid. */
|
/* Returns verdict for packet, or -NF_ACCEPT for invalid. */
|
||||||
static int sctp_packet(struct nf_conn *ct,
|
static int sctp_packet(struct nf_conn *ct,
|
||||||
const struct sk_buff *skb,
|
const struct sk_buff *skb,
|
||||||
|
@ -297,6 +354,17 @@ static int sctp_packet(struct nf_conn *ct,
|
||||||
if (do_basic_checks(ct, skb, dataoff, map) != 0)
|
if (do_basic_checks(ct, skb, dataoff, map) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (!nf_ct_is_confirmed(ct)) {
|
||||||
|
/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
|
||||||
|
if (test_bit(SCTP_CID_ABORT, map) ||
|
||||||
|
test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) ||
|
||||||
|
test_bit(SCTP_CID_COOKIE_ACK, map))
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
|
||||||
|
if (!sctp_new(ct, skb, sh, dataoff))
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check the verification tag (Sec 8.5) */
|
/* Check the verification tag (Sec 8.5) */
|
||||||
if (!test_bit(SCTP_CID_INIT, map) &&
|
if (!test_bit(SCTP_CID_INIT, map) &&
|
||||||
!test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
|
!test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
|
||||||
|
@ -398,80 +466,6 @@ out:
|
||||||
return -NF_ACCEPT;
|
return -NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
enum sctp_conntrack new_state;
|
|
||||||
const struct sctphdr *sh;
|
|
||||||
struct sctphdr _sctph;
|
|
||||||
const struct sctp_chunkhdr *sch;
|
|
||||||
struct sctp_chunkhdr _sch;
|
|
||||||
u_int32_t offset, count;
|
|
||||||
unsigned long map[256 / sizeof(unsigned long)] = { 0 };
|
|
||||||
|
|
||||||
sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
|
|
||||||
if (sh == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (do_basic_checks(ct, skb, dataoff, map) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
|
|
||||||
if (test_bit(SCTP_CID_ABORT, map) ||
|
|
||||||
test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) ||
|
|
||||||
test_bit(SCTP_CID_COOKIE_ACK, map))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset(&ct->proto.sctp, 0, sizeof(ct->proto.sctp));
|
|
||||||
new_state = SCTP_CONNTRACK_MAX;
|
|
||||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
|
||||||
/* Don't need lock here: this conntrack not in circulation yet */
|
|
||||||
new_state = sctp_new_state(IP_CT_DIR_ORIGINAL,
|
|
||||||
SCTP_CONNTRACK_NONE, sch->type);
|
|
||||||
|
|
||||||
/* Invalid: delete conntrack */
|
|
||||||
if (new_state == SCTP_CONNTRACK_NONE ||
|
|
||||||
new_state == SCTP_CONNTRACK_MAX) {
|
|
||||||
pr_debug("nf_conntrack_sctp: invalid new deleting.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the vtag into the state info */
|
|
||||||
if (sch->type == SCTP_CID_INIT) {
|
|
||||||
struct sctp_inithdr _inithdr, *ih;
|
|
||||||
/* Sec 8.5.1 (A) */
|
|
||||||
if (sh->vtag)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ih = skb_header_pointer(skb, offset + sizeof(_sch),
|
|
||||||
sizeof(_inithdr), &_inithdr);
|
|
||||||
if (!ih)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pr_debug("Setting vtag %x for new conn\n",
|
|
||||||
ih->init_tag);
|
|
||||||
|
|
||||||
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag;
|
|
||||||
} else if (sch->type == SCTP_CID_HEARTBEAT) {
|
|
||||||
pr_debug("Setting vtag %x for secondary conntrack\n",
|
|
||||||
sh->vtag);
|
|
||||||
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag;
|
|
||||||
}
|
|
||||||
/* If it is a shutdown ack OOTB packet, we expect a return
|
|
||||||
shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
|
|
||||||
else {
|
|
||||||
pr_debug("Setting vtag %x for new conn OOTB\n",
|
|
||||||
sh->vtag);
|
|
||||||
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
|
|
||||||
}
|
|
||||||
|
|
||||||
ct->proto.sctp.state = new_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sctp_error(struct nf_conn *tpl, struct sk_buff *skb,
|
static int sctp_error(struct nf_conn *tpl, struct sk_buff *skb,
|
||||||
unsigned int dataoff,
|
unsigned int dataoff,
|
||||||
const struct nf_hook_state *state)
|
const struct nf_hook_state *state)
|
||||||
|
@ -769,7 +763,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 = {
|
||||||
.print_conntrack = sctp_print_conntrack,
|
.print_conntrack = sctp_print_conntrack,
|
||||||
#endif
|
#endif
|
||||||
.packet = sctp_packet,
|
.packet = sctp_packet,
|
||||||
.new = sctp_new,
|
|
||||||
.error = sctp_error,
|
.error = sctp_error,
|
||||||
.can_early_drop = sctp_can_early_drop,
|
.can_early_drop = sctp_can_early_drop,
|
||||||
.me = THIS_MODULE,
|
.me = THIS_MODULE,
|
||||||
|
@ -803,7 +796,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 = {
|
||||||
.print_conntrack = sctp_print_conntrack,
|
.print_conntrack = sctp_print_conntrack,
|
||||||
#endif
|
#endif
|
||||||
.packet = sctp_packet,
|
.packet = sctp_packet,
|
||||||
.new = sctp_new,
|
|
||||||
.error = sctp_error,
|
.error = sctp_error,
|
||||||
.can_early_drop = sctp_can_early_drop,
|
.can_early_drop = sctp_can_early_drop,
|
||||||
.me = THIS_MODULE,
|
.me = THIS_MODULE,
|
||||||
|
|
|
@ -770,6 +770,78 @@ static int tcp_error(struct nf_conn *tmpl,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
||||||
|
unsigned int dataoff,
|
||||||
|
const struct tcphdr *th)
|
||||||
|
{
|
||||||
|
enum tcp_conntrack new_state;
|
||||||
|
struct net *net = nf_ct_net(ct);
|
||||||
|
const struct nf_tcp_net *tn = tcp_pernet(net);
|
||||||
|
const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[0];
|
||||||
|
const struct ip_ct_tcp_state *receiver = &ct->proto.tcp.seen[1];
|
||||||
|
|
||||||
|
/* Don't need lock here: this conntrack not in circulation yet */
|
||||||
|
new_state = tcp_conntracks[0][get_conntrack_index(th)][TCP_CONNTRACK_NONE];
|
||||||
|
|
||||||
|
/* Invalid: delete conntrack */
|
||||||
|
if (new_state >= TCP_CONNTRACK_MAX) {
|
||||||
|
pr_debug("nf_ct_tcp: invalid new deleting.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_state == TCP_CONNTRACK_SYN_SENT) {
|
||||||
|
memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
|
||||||
|
/* SYN packet */
|
||||||
|
ct->proto.tcp.seen[0].td_end =
|
||||||
|
segment_seq_plus_len(ntohl(th->seq), skb->len,
|
||||||
|
dataoff, th);
|
||||||
|
ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
|
||||||
|
if (ct->proto.tcp.seen[0].td_maxwin == 0)
|
||||||
|
ct->proto.tcp.seen[0].td_maxwin = 1;
|
||||||
|
ct->proto.tcp.seen[0].td_maxend =
|
||||||
|
ct->proto.tcp.seen[0].td_end;
|
||||||
|
|
||||||
|
tcp_options(skb, dataoff, th, &ct->proto.tcp.seen[0]);
|
||||||
|
} else if (tn->tcp_loose == 0) {
|
||||||
|
/* Don't try to pick up connections. */
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
|
||||||
|
/*
|
||||||
|
* We are in the middle of a connection,
|
||||||
|
* its history is lost for us.
|
||||||
|
* Let's try to use the data from the packet.
|
||||||
|
*/
|
||||||
|
ct->proto.tcp.seen[0].td_end =
|
||||||
|
segment_seq_plus_len(ntohl(th->seq), skb->len,
|
||||||
|
dataoff, th);
|
||||||
|
ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
|
||||||
|
if (ct->proto.tcp.seen[0].td_maxwin == 0)
|
||||||
|
ct->proto.tcp.seen[0].td_maxwin = 1;
|
||||||
|
ct->proto.tcp.seen[0].td_maxend =
|
||||||
|
ct->proto.tcp.seen[0].td_end +
|
||||||
|
ct->proto.tcp.seen[0].td_maxwin;
|
||||||
|
|
||||||
|
/* We assume SACK and liberal window checking to handle
|
||||||
|
* window scaling */
|
||||||
|
ct->proto.tcp.seen[0].flags =
|
||||||
|
ct->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
|
||||||
|
IP_CT_TCP_FLAG_BE_LIBERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tcp_packet will set them */
|
||||||
|
ct->proto.tcp.last_index = TCP_NONE_SET;
|
||||||
|
|
||||||
|
pr_debug("%s: sender end=%u maxend=%u maxwin=%u scale=%i "
|
||||||
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
||||||
|
__func__,
|
||||||
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
||||||
|
sender->td_scale,
|
||||||
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
||||||
|
receiver->td_scale);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns verdict for packet, or -1 for invalid. */
|
/* Returns verdict for packet, or -1 for invalid. */
|
||||||
static int tcp_packet(struct nf_conn *ct,
|
static int tcp_packet(struct nf_conn *ct,
|
||||||
const struct sk_buff *skb,
|
const struct sk_buff *skb,
|
||||||
|
@ -788,7 +860,11 @@ static int tcp_packet(struct nf_conn *ct,
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
|
th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
|
||||||
BUG_ON(th == NULL);
|
if (th == NULL)
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
|
||||||
|
if (!nf_ct_is_confirmed(ct) && !tcp_new(ct, skb, dataoff, th))
|
||||||
|
return -NF_ACCEPT;
|
||||||
|
|
||||||
spin_lock_bh(&ct->lock);
|
spin_lock_bh(&ct->lock);
|
||||||
old_state = ct->proto.tcp.state;
|
old_state = ct->proto.tcp.state;
|
||||||
|
@ -1069,82 +1145,6 @@ static int tcp_packet(struct nf_conn *ct,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
enum tcp_conntrack new_state;
|
|
||||||
const struct tcphdr *th;
|
|
||||||
struct tcphdr _tcph;
|
|
||||||
struct net *net = nf_ct_net(ct);
|
|
||||||
struct nf_tcp_net *tn = tcp_pernet(net);
|
|
||||||
const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[0];
|
|
||||||
const struct ip_ct_tcp_state *receiver = &ct->proto.tcp.seen[1];
|
|
||||||
|
|
||||||
th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
|
|
||||||
BUG_ON(th == NULL);
|
|
||||||
|
|
||||||
/* Don't need lock here: this conntrack not in circulation yet */
|
|
||||||
new_state = tcp_conntracks[0][get_conntrack_index(th)][TCP_CONNTRACK_NONE];
|
|
||||||
|
|
||||||
/* Invalid: delete conntrack */
|
|
||||||
if (new_state >= TCP_CONNTRACK_MAX) {
|
|
||||||
pr_debug("nf_ct_tcp: invalid new deleting.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_state == TCP_CONNTRACK_SYN_SENT) {
|
|
||||||
memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
|
|
||||||
/* SYN packet */
|
|
||||||
ct->proto.tcp.seen[0].td_end =
|
|
||||||
segment_seq_plus_len(ntohl(th->seq), skb->len,
|
|
||||||
dataoff, th);
|
|
||||||
ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
|
|
||||||
if (ct->proto.tcp.seen[0].td_maxwin == 0)
|
|
||||||
ct->proto.tcp.seen[0].td_maxwin = 1;
|
|
||||||
ct->proto.tcp.seen[0].td_maxend =
|
|
||||||
ct->proto.tcp.seen[0].td_end;
|
|
||||||
|
|
||||||
tcp_options(skb, dataoff, th, &ct->proto.tcp.seen[0]);
|
|
||||||
} else if (tn->tcp_loose == 0) {
|
|
||||||
/* Don't try to pick up connections. */
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
|
|
||||||
/*
|
|
||||||
* We are in the middle of a connection,
|
|
||||||
* its history is lost for us.
|
|
||||||
* Let's try to use the data from the packet.
|
|
||||||
*/
|
|
||||||
ct->proto.tcp.seen[0].td_end =
|
|
||||||
segment_seq_plus_len(ntohl(th->seq), skb->len,
|
|
||||||
dataoff, th);
|
|
||||||
ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
|
|
||||||
if (ct->proto.tcp.seen[0].td_maxwin == 0)
|
|
||||||
ct->proto.tcp.seen[0].td_maxwin = 1;
|
|
||||||
ct->proto.tcp.seen[0].td_maxend =
|
|
||||||
ct->proto.tcp.seen[0].td_end +
|
|
||||||
ct->proto.tcp.seen[0].td_maxwin;
|
|
||||||
|
|
||||||
/* We assume SACK and liberal window checking to handle
|
|
||||||
* window scaling */
|
|
||||||
ct->proto.tcp.seen[0].flags =
|
|
||||||
ct->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
|
|
||||||
IP_CT_TCP_FLAG_BE_LIBERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tcp_packet will set them */
|
|
||||||
ct->proto.tcp.last_index = TCP_NONE_SET;
|
|
||||||
|
|
||||||
pr_debug("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
||||||
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
||||||
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
||||||
sender->td_scale,
|
|
||||||
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
||||||
receiver->td_scale);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool tcp_can_early_drop(const struct nf_conn *ct)
|
static bool tcp_can_early_drop(const struct nf_conn *ct)
|
||||||
{
|
{
|
||||||
switch (ct->proto.tcp.state) {
|
switch (ct->proto.tcp.state) {
|
||||||
|
@ -1548,7 +1548,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 =
|
||||||
.print_conntrack = tcp_print_conntrack,
|
.print_conntrack = tcp_print_conntrack,
|
||||||
#endif
|
#endif
|
||||||
.packet = tcp_packet,
|
.packet = tcp_packet,
|
||||||
.new = tcp_new,
|
|
||||||
.error = tcp_error,
|
.error = tcp_error,
|
||||||
.can_early_drop = tcp_can_early_drop,
|
.can_early_drop = tcp_can_early_drop,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
|
@ -1583,7 +1582,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 =
|
||||||
.print_conntrack = tcp_print_conntrack,
|
.print_conntrack = tcp_print_conntrack,
|
||||||
#endif
|
#endif
|
||||||
.packet = tcp_packet,
|
.packet = tcp_packet,
|
||||||
.new = tcp_new,
|
|
||||||
.error = tcp_error,
|
.error = tcp_error,
|
||||||
.can_early_drop = tcp_can_early_drop,
|
.can_early_drop = tcp_can_early_drop,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
|
|
|
@ -70,13 +70,6 @@ static int udp_packet(struct nf_conn *ct,
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new connection for this protocol found. */
|
|
||||||
static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb,
|
|
||||||
unsigned int dataoff)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_NF_CT_PROTO_UDPLITE
|
#ifdef CONFIG_NF_CT_PROTO_UDPLITE
|
||||||
static void udplite_error_log(const struct sk_buff *skb,
|
static void udplite_error_log(const struct sk_buff *skb,
|
||||||
const struct nf_hook_state *state,
|
const struct nf_hook_state *state,
|
||||||
|
@ -288,7 +281,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 =
|
||||||
.l4proto = IPPROTO_UDP,
|
.l4proto = IPPROTO_UDP,
|
||||||
.allow_clash = true,
|
.allow_clash = true,
|
||||||
.packet = udp_packet,
|
.packet = udp_packet,
|
||||||
.new = udp_new,
|
|
||||||
.error = udp_error,
|
.error = udp_error,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
||||||
|
@ -317,7 +309,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 =
|
||||||
.l4proto = IPPROTO_UDPLITE,
|
.l4proto = IPPROTO_UDPLITE,
|
||||||
.allow_clash = true,
|
.allow_clash = true,
|
||||||
.packet = udp_packet,
|
.packet = udp_packet,
|
||||||
.new = udp_new,
|
|
||||||
.error = udplite_error,
|
.error = udplite_error,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
||||||
|
@ -346,7 +337,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 =
|
||||||
.l4proto = IPPROTO_UDP,
|
.l4proto = IPPROTO_UDP,
|
||||||
.allow_clash = true,
|
.allow_clash = true,
|
||||||
.packet = udp_packet,
|
.packet = udp_packet,
|
||||||
.new = udp_new,
|
|
||||||
.error = udp_error,
|
.error = udp_error,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
||||||
|
@ -375,7 +365,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 =
|
||||||
.l4proto = IPPROTO_UDPLITE,
|
.l4proto = IPPROTO_UDPLITE,
|
||||||
.allow_clash = true,
|
.allow_clash = true,
|
||||||
.packet = udp_packet,
|
.packet = udp_packet,
|
||||||
.new = udp_new,
|
|
||||||
.error = udplite_error,
|
.error = udplite_error,
|
||||||
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
|
||||||
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче