Commit c4f3db15 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: conntrack: add and use nf_l4proto_log_invalid

We currently pass down the l4 protocol to the conntrack ->packet()
function, but the only user of this is the debug info decision.

Same information can be derived from struct nf_conn.
As a first step, add and use a new log function for this, similar to
nf_ct_helper_log().

Add __cold annotation -- invalid packets should be infrequent so
gcc can consider all call paths that lead to such a function as
unlikely.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 2420770b
...@@ -152,8 +152,18 @@ extern const struct nla_policy nf_ct_port_nla_policy[]; ...@@ -152,8 +152,18 @@ extern const struct nla_policy nf_ct_port_nla_policy[];
#define LOG_INVALID(net, proto) \ #define LOG_INVALID(net, proto) \
((net)->ct.sysctl_log_invalid == (proto) || \ ((net)->ct.sysctl_log_invalid == (proto) || \
(net)->ct.sysctl_log_invalid == IPPROTO_RAW) (net)->ct.sysctl_log_invalid == IPPROTO_RAW)
__printf(5, 6) __cold
void nf_l4proto_log_invalid(const struct sk_buff *skb,
struct net *net,
u16 pf, u8 protonum,
const char *fmt, ...);
#else #else
static inline int LOG_INVALID(struct net *net, int proto) { return 0; } static inline int LOG_INVALID(struct net *net, int proto) { return 0; }
static inline __printf(5, 6) __cold
void nf_l4proto_log_invalid(const struct sk_buff *skb, struct net *net,
u16 pf, u8 protonum, const char *fmt, ...) {}
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
#endif /*_NF_CONNTRACK_PROTOCOL_H*/ #endif /*_NF_CONNTRACK_PROTOCOL_H*/
...@@ -165,6 +165,12 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, ...@@ -165,6 +165,12 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
static void icmp_error_log(const struct sk_buff *skb, struct net *net,
u8 pf, const char *msg)
{
nf_l4proto_log_invalid(skb, net, pf, IPPROTO_ICMP, "%s", msg);
}
/* Small and modified version of icmp_rcv */ /* Small and modified version of icmp_rcv */
static int static int
icmp_error(struct net *net, struct nf_conn *tmpl, icmp_error(struct net *net, struct nf_conn *tmpl,
...@@ -177,18 +183,14 @@ icmp_error(struct net *net, struct nf_conn *tmpl, ...@@ -177,18 +183,14 @@ icmp_error(struct net *net, struct nf_conn *tmpl,
/* Not enough header? */ /* Not enough header? */
icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
if (icmph == NULL) { if (icmph == NULL) {
if (LOG_INVALID(net, IPPROTO_ICMP)) icmp_error_log(skb, net, pf, "short packet");
nf_log_packet(net, PF_INET, 0, skb, NULL, NULL,
NULL, "nf_ct_icmp: short packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
/* See ip_conntrack_proto_tcp.c */ /* See ip_conntrack_proto_tcp.c */
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
nf_ip_checksum(skb, hooknum, dataoff, 0)) { nf_ip_checksum(skb, hooknum, dataoff, 0)) {
if (LOG_INVALID(net, IPPROTO_ICMP)) icmp_error_log(skb, net, pf, "bad hw icmp checksum");
nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL,
"nf_ct_icmp: bad HW ICMP checksum ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -199,9 +201,7 @@ icmp_error(struct net *net, struct nf_conn *tmpl, ...@@ -199,9 +201,7 @@ icmp_error(struct net *net, struct nf_conn *tmpl,
* discarded. * discarded.
*/ */
if (icmph->type > NR_ICMP_TYPES) { if (icmph->type > NR_ICMP_TYPES) {
if (LOG_INVALID(net, IPPROTO_ICMP)) icmp_error_log(skb, net, pf, "invalid icmp type");
nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL,
"nf_ct_icmp: invalid ICMP type ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
...@@ -176,6 +176,12 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl, ...@@ -176,6 +176,12 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
return NF_ACCEPT; return NF_ACCEPT;
} }
static void icmpv6_error_log(const struct sk_buff *skb, struct net *net,
u8 pf, const char *msg)
{
nf_l4proto_log_invalid(skb, net, pf, IPPROTO_ICMPV6, "%s", msg);
}
static int static int
icmpv6_error(struct net *net, struct nf_conn *tmpl, icmpv6_error(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb, unsigned int dataoff, struct sk_buff *skb, unsigned int dataoff,
...@@ -187,17 +193,13 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, ...@@ -187,17 +193,13 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl,
icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih); icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
if (icmp6h == NULL) { if (icmp6h == NULL) {
if (LOG_INVALID(net, IPPROTO_ICMPV6)) icmpv6_error_log(skb, net, pf, "short packet");
nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL,
"nf_ct_icmpv6: short packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) { nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) {
if (LOG_INVALID(net, IPPROTO_ICMPV6)) icmpv6_error_log(skb, net, pf, "ICMPv6 checksum failed");
nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL,
"nf_ct_icmpv6: ICMPv6 checksum failed ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_log.h>
static struct nf_conntrack_l4proto __rcu **nf_ct_protos[NFPROTO_NUMPROTO] __read_mostly; static struct nf_conntrack_l4proto __rcu **nf_ct_protos[NFPROTO_NUMPROTO] __read_mostly;
struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO] __read_mostly; struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO] __read_mostly;
...@@ -63,6 +64,29 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header, ...@@ -63,6 +64,29 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header,
*header = NULL; *header = NULL;
*table = NULL; *table = NULL;
} }
__printf(5, 6)
void nf_l4proto_log_invalid(const struct sk_buff *skb,
struct net *net,
u16 pf, u8 protonum,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
if (net->ct.sysctl_log_invalid != protonum ||
net->ct.sysctl_log_invalid != IPPROTO_RAW)
return;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_proto_%d: %pV ", protonum, &vaf);
va_end(args);
}
EXPORT_SYMBOL_GPL(nf_l4proto_log_invalid);
#endif #endif
const struct nf_conntrack_l4proto * const struct nf_conntrack_l4proto *
......
...@@ -604,8 +604,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl, ...@@ -604,8 +604,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl,
return NF_ACCEPT; return NF_ACCEPT;
out_invalid: out_invalid:
if (LOG_INVALID(net, IPPROTO_DCCP)) nf_l4proto_log_invalid(skb, net, pf, IPPROTO_DCCP, "%s", msg);
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, "%s", msg);
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
...@@ -522,8 +522,7 @@ static int sctp_error(struct net *net, struct nf_conn *tpl, struct sk_buff *skb, ...@@ -522,8 +522,7 @@ static int sctp_error(struct net *net, struct nf_conn *tpl, struct sk_buff *skb,
} }
return NF_ACCEPT; return NF_ACCEPT;
out_invalid: out_invalid:
if (LOG_INVALID(net, IPPROTO_SCTP)) nf_l4proto_log_invalid(skb, net, pf, IPPROTO_SCTP, "%s", logmsg);
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, "%s", logmsg);
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
...@@ -738,6 +738,12 @@ static const u8 tcp_valid_flags[(TCPHDR_FIN|TCPHDR_SYN|TCPHDR_RST|TCPHDR_ACK| ...@@ -738,6 +738,12 @@ static const u8 tcp_valid_flags[(TCPHDR_FIN|TCPHDR_SYN|TCPHDR_RST|TCPHDR_ACK|
[TCPHDR_ACK|TCPHDR_URG] = 1, [TCPHDR_ACK|TCPHDR_URG] = 1,
}; };
static void tcp_error_log(const struct sk_buff *skb, struct net *net,
u8 pf, const char *msg)
{
nf_l4proto_log_invalid(skb, net, pf, IPPROTO_TCP, "%s", msg);
}
/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ /* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */
static int tcp_error(struct net *net, struct nf_conn *tmpl, static int tcp_error(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb, struct sk_buff *skb,
...@@ -753,17 +759,13 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl, ...@@ -753,17 +759,13 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
/* Smaller that minimal TCP header? */ /* Smaller that minimal TCP header? */
th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph); th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
if (th == NULL) { if (th == NULL) {
if (LOG_INVALID(net, IPPROTO_TCP)) tcp_error_log(skb, net, pf, "short packet");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: short packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
/* Not whole TCP header or malformed packet */ /* Not whole TCP header or malformed packet */
if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
if (LOG_INVALID(net, IPPROTO_TCP)) tcp_error_log(skb, net, pf, "truncated packet");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: truncated/malformed packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -774,18 +776,14 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl, ...@@ -774,18 +776,14 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
/* FIXME: Source route IP option packets --RR */ /* FIXME: Source route IP option packets --RR */
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) { nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {
if (LOG_INVALID(net, IPPROTO_TCP)) tcp_error_log(skb, net, pf, "bad checksum");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: bad TCP checksum ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
/* Check TCP flags. */ /* Check TCP flags. */
tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH)); tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH));
if (!tcp_valid_flags[tcpflags]) { if (!tcp_valid_flags[tcpflags]) {
if (LOG_INVALID(net, IPPROTO_TCP)) tcp_error_log(skb, net, pf, "invalid tcp flag combination");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: invalid TCP flag combination ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
...@@ -99,6 +99,12 @@ static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -99,6 +99,12 @@ static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb,
} }
#ifdef CONFIG_NF_CT_PROTO_UDPLITE #ifdef CONFIG_NF_CT_PROTO_UDPLITE
static void udplite_error_log(const struct sk_buff *skb, struct net *net,
u8 pf, const char *msg)
{
nf_l4proto_log_invalid(skb, net, pf, IPPROTO_UDPLITE, "%s", msg);
}
static int udplite_error(struct net *net, struct nf_conn *tmpl, static int udplite_error(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb, struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
...@@ -112,9 +118,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, ...@@ -112,9 +118,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
/* Header is too small? */ /* Header is too small? */
hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
if (!hdr) { if (!hdr) {
if (LOG_INVALID(net, IPPROTO_UDPLITE)) udplite_error_log(skb, net, pf, "short packet");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udplite: short packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -122,17 +126,13 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, ...@@ -122,17 +126,13 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
if (cscov == 0) { if (cscov == 0) {
cscov = udplen; cscov = udplen;
} else if (cscov < sizeof(*hdr) || cscov > udplen) { } else if (cscov < sizeof(*hdr) || cscov > udplen) {
if (LOG_INVALID(net, IPPROTO_UDPLITE)) udplite_error_log(skb, net, pf, "invalid checksum coverage");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udplite: invalid checksum coverage ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
/* UDPLITE mandates checksums */ /* UDPLITE mandates checksums */
if (!hdr->check) { if (!hdr->check) {
if (LOG_INVALID(net, IPPROTO_UDPLITE)) udplite_error_log(skb, net, pf, "checksum missing");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udplite: checksum missing ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -140,9 +140,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, ...@@ -140,9 +140,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP, nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP,
pf)) { pf)) {
if (LOG_INVALID(net, IPPROTO_UDPLITE)) udplite_error_log(skb, net, pf, "bad checksum");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udplite: bad UDPLite checksum ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -150,6 +148,12 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, ...@@ -150,6 +148,12 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
} }
#endif #endif
static void udp_error_log(const struct sk_buff *skb, struct net *net,
u8 pf, const char *msg)
{
nf_l4proto_log_invalid(skb, net, pf, IPPROTO_UDP, "%s", msg);
}
static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
u_int8_t pf, u_int8_t pf,
...@@ -162,17 +166,13 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, ...@@ -162,17 +166,13 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
/* Header is too small? */ /* Header is too small? */
hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
if (hdr == NULL) { if (hdr == NULL) {
if (LOG_INVALID(net, IPPROTO_UDP)) udp_error_log(skb, net, pf, "short packet");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udp: short packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
/* Truncated/malformed packets */ /* Truncated/malformed packets */
if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
if (LOG_INVALID(net, IPPROTO_UDP)) udp_error_log(skb, net, pf, "truncated/malformed packet");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udp: truncated/malformed packet ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -186,9 +186,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, ...@@ -186,9 +186,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
* FIXME: Source route IP option packets --RR */ * FIXME: Source route IP option packets --RR */
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) { nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) {
if (LOG_INVALID(net, IPPROTO_UDP)) udp_error_log(skb, net, pf, "bad checksum");
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udp: bad UDP checksum ");
return -NF_ACCEPT; return -NF_ACCEPT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment