nf_ct_ext_find() might return NULL if ct extension is not found.
Add also the null checks to:
- nfct_help()
- nfct_help_data()
- nfct_seqadj()
- nfct_nat()
This is defensive, for safety reasons.
nf_ct_ext_find() used to return NULL if the extension is stale for
unconfirmed conntracks if the genid validation fails.
Skip NULL check in nf_nat_inet_fn() given this is valid to be NULL
for non-initialized ct nat extensions.
While at it, fetch ct helper area in nf_ct_expect_related_report() only
once and pass it on to other ancilliary functions. Replace WARN_ON()
by WARN_ON_ONCE() in nf_ct_unlink_expect_report().
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
struct nf_conn_help *help;
help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER);
+ if (!help)
+ return NULL;
return (void *)help->data;
}
__be16 port;
union nf_inet_addr addr;
+ if (!info)
+ return -1;
+
for (i = 0; i < count; i++) {
if (get_h225_addr(ct, *data, &taddr[i], &addr, &port)) {
if (addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
int i;
u_int16_t nated_port;
+ if (!info)
+ return -1;
+
/* Set expectations for NAT */
rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
rtp_exp->expectfn = nf_nat_follow_master;
int dir = CTINFO2DIR(ctinfo);
u_int16_t nated_port = ntohs(port);
+ if (!info)
+ return -1;
+
/* Set expectations for NAT */
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
exp->expectfn = nf_nat_follow_master;
u_int16_t nated_port = ntohs(port);
union nf_inet_addr addr;
+ if (!info)
+ return -1;
+
/* Set expectations for NAT */
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
exp->expectfn = ip_nat_q931_expect;
struct nf_conn_nat *nat;
nat = nf_ct_nat_ext_add(ct);
- if (WARN_ON_ONCE(!nat))
+ if (!nat)
return;
nat_pptp_info = &nat->help.nat_pptp_info;
ct_pptp_info = nfct_help_data(master);
+ if (!ct_pptp_info)
+ return;
/* And here goes the grand finale of corrosion... */
if (exp->dir == IP_CT_DIR_ORIGINAL) {
__be16 new_callid;
unsigned int cid_off;
- if (WARN_ON_ONCE(!nat))
+ if (!nat)
return NF_DROP;
nat_pptp_info = &nat->help.nat_pptp_info;
ct_pptp_info = nfct_help_data(ct);
+ if (!ct_pptp_info)
+ return NF_DROP;
new_callid = ct_pptp_info->pns_call_id;
struct nf_ct_pptp_master *ct_pptp_info;
struct nf_nat_pptp *nat_pptp_info;
- if (WARN_ON_ONCE(!nat))
+ if (!nat)
return;
nat_pptp_info = &nat->help.nat_pptp_info;
ct_pptp_info = nfct_help_data(ct);
+ if (!ct_pptp_info)
+ return;
/* save original PAC call ID in nat_info */
nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
__be16 new_pcid;
unsigned int pcid_off;
- if (WARN_ON_ONCE(!nat))
+ if (!nat)
return NF_DROP;
nat_pptp_info = &nat->help.nat_pptp_info;
struct nf_conn_help *help = nfct_help(ct);
__be32 mask = 0;
+ if (!help)
+ goto out;
+
/* we're only interested in locally generated packets */
if (skb->sk == NULL || !net_eq(nf_ct_net(ct), sock_net(skb->sk)))
goto out;
struct nf_conntrack_net *cnet;
lockdep_nfct_expect_lock_held();
- WARN_ON(!master_help);
- WARN_ON(timer_pending(&exp->timeout));
+ WARN_ON_ONCE(timer_pending(&exp->timeout));
hlist_del_rcu(&exp->hnode);
cnet->expect_count--;
hlist_del_rcu(&exp->lnode);
- master_help->expecting[exp->class]--;
+ if (master_help)
+ master_help->expecting[exp->class]--;
nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report);
nf_ct_expect_put(exp);
}
EXPORT_SYMBOL_GPL(nf_ct_expect_put);
-static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
+static void nf_ct_expect_insert(struct nf_conntrack_expect *exp,
+ struct nf_conn_help *master_help)
{
struct nf_conntrack_net *cnet;
- struct nf_conn_help *master_help = nfct_help(exp->master);
struct nf_conntrack_helper *helper;
struct net *net = nf_ct_exp_net(exp);
unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple);
}
/* Race with expectations being used means we could have none to find; OK. */
-static void evict_oldest_expect(struct nf_conn *master,
+static void evict_oldest_expect(struct nf_conn_help *master_help,
struct nf_conntrack_expect *new)
{
- struct nf_conn_help *master_help = nfct_help(master);
struct nf_conntrack_expect *exp, *last = NULL;
hlist_for_each_entry(exp, &master_help->expectations, lnode) {
}
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
+ struct nf_conn_help *master_help,
unsigned int flags)
{
const struct nf_conntrack_expect_policy *p;
struct nf_conntrack_expect *i;
struct nf_conntrack_net *cnet;
- struct nf_conn *master = expect->master;
- struct nf_conn_help *master_help = nfct_help(master);
struct nf_conntrack_helper *helper;
struct net *net = nf_ct_exp_net(expect);
struct hlist_node *next;
lockdep_nfct_expect_lock_held();
- if (!master_help) {
- ret = -ESHUTDOWN;
- goto out;
- }
h = nf_ct_expect_dst_hash(net, &expect->tuple);
hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) {
if (master_matches(i, expect, flags) &&
p = &helper->expect_policy[expect->class];
if (p->max_expected &&
master_help->expecting[expect->class] >= p->max_expected) {
- evict_oldest_expect(master, expect);
+ evict_oldest_expect(master_help, expect);
if (master_help->expecting[expect->class]
>= p->max_expected) {
ret = -EMFILE;
int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
u32 portid, int report, unsigned int flags)
{
+ struct nf_conn_help *master_help;
int ret;
spin_lock_bh(&nf_conntrack_expect_lock);
- ret = __nf_ct_expect_check(expect, flags);
+ master_help = nfct_help(expect->master);
+ if (!master_help) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ ret = __nf_ct_expect_check(expect, master_help, flags);
if (ret < 0)
goto out;
- nf_ct_expect_insert(expect);
+ nf_ct_expect_insert(expect, master_help);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
spin_unlock_bh(&nf_conntrack_expect_lock);
int found = 0, ends_in_nl;
nf_nat_ftp_hook_fn *nf_nat_ftp;
+ if (!ct_ftp_info)
+ return NF_DROP;
+
/* Until there's been traffic both ways, don't look in packets. */
if (ctinfo != IP_CT_ESTABLISHED &&
ctinfo != IP_CT_ESTABLISHED_REPLY) {
{
struct nf_ct_ftp_master *ftp = nfct_help_data(ct);
+ if (!ftp)
+ return -ENOENT;
+
/* This conntrack has been injected from user-space, always pick up
* sequence tracking. Otherwise, the first FTP command after the
* failover breaks.
int tpktlen;
int tpktoff;
+ if (!info)
+ return 0;
+
/* Get TCP header */
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
if (th == NULL)
union nf_inet_addr addr;
struct nf_conntrack_expect *exp;
+ if (!info)
+ return -1;
+
/* Look for the first related address */
for (i = 0; i < count; i++) {
if (get_h225_addr(ct, *data, &taddr[i], &addr, &port) &&
const struct nfct_h323_nat_hooks *nathook;
int ret;
+ if (!info)
+ return -1;
+
pr_debug("nf_ct_ras: RRQ\n");
ret = expect_q931(skb, ct, ctinfo, protoff, data,
int ret;
struct nf_conntrack_expect *exp;
+ if (!info)
+ return -1;
+
pr_debug("nf_ct_ras: RCF\n");
nathook = rcu_dereference(nfct_h323_nat_hook);
int dir = CTINFO2DIR(ctinfo);
int ret;
+ if (!info)
+ return -1;
+
pr_debug("nf_ct_ras: URQ\n");
nathook = rcu_dereference(nfct_h323_nat_hook);
__be16 port;
union nf_inet_addr addr;
+ if (!info)
+ return 0;
+
pr_debug("nf_ct_ras: ARQ\n");
nathook = rcu_dereference(nfct_h323_nat_hook);
u_int16_t msg;
__be16 cid = 0, pcid = 0;
+ if (!info)
+ return NF_DROP;
+
msg = ntohs(ctlh->messageType);
pr_debug("inbound control message %s\n", pptp_msg_name(msg));
u_int16_t msg;
__be16 cid = 0, pcid = 0;
+ if (!info)
+ return NF_DROP;
+
msg = ntohs(ctlh->messageType);
pr_debug("outbound control message %s\n", pptp_msg_name(msg));
int ret;
u_int16_t msg;
+ if (!info)
+ return NF_DROP;
+
#if IS_ENABLED(CONFIG_NF_NAT)
if (!nf_ct_is_confirmed(ct) && (ct->status & IPS_NAT_MASK)) {
struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT);
struct nf_ct_gre_keymap *km_orig, *km_repl;
bool ret = false;
+ if (!ct_pptp_info)
+ return false;
+
km_orig = kmalloc_obj(*km_orig, GFP_ATOMIC);
if (!km_orig)
return false;
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
enum ip_conntrack_dir dir;
+ if (!ct_pptp_info)
+ return;
+
pr_debug("entering for ct %p\n", ct);
spin_lock_bh(&keymap_lock);
const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
struct nf_conntrack_tuple t;
+ if (!ct_pptp_info)
+ return;
+
nf_ct_gre_keymap_destroy(ct);
/* try original (pns->pac) tuple */
struct sane_reply_net_start repl;
} buf;
+ if (!ct_sane_info)
+ return NF_DROP;
+
/* Until there's been traffic both ways, don't look in packets. */
if (ctinfo != IP_CT_ESTABLISHED &&
ctinfo != IP_CT_ESTABLISHED_REPLY)
return 0;
spin_lock_bh(&ct->lock);
- set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
-
seqadj = nfct_seqadj(ct);
+ if (!seqadj) {
+ spin_unlock_bh(&ct->lock);
+ return 0;
+ }
+ set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
this_way = &seqadj->seq[dir];
this_way->offset_before = off;
this_way->offset_after = off;
if (off == 0)
return 0;
- if (unlikely(!seqadj)) {
- WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n");
+ if (unlikely(!seqadj))
return 0;
- }
set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
unsigned int dir, optoff, optend;
+ if (!seqadj)
+ return 0;
+
optoff = protoff + sizeof(struct tcphdr);
optend = protoff + tcph->doff * 4;
struct nf_ct_seqadj *this_way, *other_way;
int res = 1;
+ if (!seqadj)
+ return 0;
+
this_way = &seqadj->seq[dir];
other_way = &seqadj->seq[!dir];
struct hlist_node *next;
int found = 0;
+ if (!help)
+ return 0;
+
spin_lock_bh(&nf_conntrack_expect_lock);
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
if (exp->class != SIP_EXPECT_SIGNALLING ||
struct nf_conntrack_expect *exp;
struct hlist_node *next;
+ if (!help)
+ return;
+
spin_lock_bh(&nf_conntrack_expect_lock);
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
u_int16_t base_port;
__be16 rtp_port, rtcp_port;
const struct nf_nat_sip_hooks *hooks;
+ struct nf_conn_help *help;
+
+ help = nfct_help(ct);
+ if (!help)
+ return NF_DROP;
saddr = NULL;
if (sip_direct_media) {
exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple);
if (!exp || exp->master == ct ||
- exp->helper != nfct_help(ct)->helper ||
+ exp->helper != help->helper ||
exp->class != class)
break;
#if IS_ENABLED(CONFIG_NF_NAT)
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
+ if (!ct_sip_info)
+ return NF_DROP;
+
if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299))
return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq);
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
+ if (!ct_sip_info)
+ return NF_DROP;
+
if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299))
return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq);
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
+ if (!ct_sip_info)
+ return NF_DROP;
+
if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299))
return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
unsigned int ret;
+ if (!ct_sip_info)
+ return NF_DROP;
+
flush_expectations(ct, true);
ret = process_sdp(skb, protoff, dataoff, dptr, datalen, cseq);
if (ret == NF_ACCEPT)
union nf_inet_addr *saddr, daddr;
const struct nf_nat_sip_hooks *hooks;
struct nf_conntrack_helper *helper;
+ struct nf_conn_help *help;
__be16 port;
u8 proto;
unsigned int expires = 0;
int ret;
+ if (!ct_sip_info)
+ return NF_DROP;
+
/* Expected connections can not register again. */
if (ct->status & IPS_EXPECTED)
return NF_ACCEPT;
goto store_cseq;
}
- helper = rcu_dereference(nfct_help(ct)->helper);
+ help = nfct_help(ct);
+ if (!help)
+ return NF_DROP;
+
+ helper = rcu_dereference(help->helper);
if (!helper)
return NF_DROP;
unsigned int expires = 0;
int in_contact = 0, ret;
+ if (!ct_sip_info)
+ return NF_DROP;
+
/* According to RFC 3261, "UAs MUST NOT send a new registration until
* they have received a final response from the registrar for the
* previous one or the previous REGISTER request has timed out".
union nf_inet_addr addr;
__be16 port;
+ if (!ct_sip_info)
+ return NF_DROP;
+
/* Many Cisco IP phones use a high source port for SIP requests, but
* listen for the response on port 5060. If we are the local
* router for one of these phones, save the port number from the
union nf_inet_addr newaddr;
__be16 newport;
+ if (!ct_sip_info)
+ return 0;
+
if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) &&
ct->tuplehash[dir].tuple.src.u.udp.port == port) {
newaddr = ct->tuplehash[!dir].tuple.dst.u3;
__be16 port;
int request, in_header;
+ if (!ct_sip_info)
+ return NF_DROP;
+
/* Basic rules: requests and responses. */
if (strncasecmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
if (ct_sip_parse_request(ct, *dptr, *datalen,
int range_set_for_snat = 0;
struct nf_nat_range2 range;
+ if (!help)
+ return;
+
/* This must be a fresh one. */
BUG_ON(ct->status & IPS_NAT_DONE_MASK);
char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
unsigned int buflen;
+ if (!ct_sip_info)
+ return NF_DROP;
+
/* Connection will come from reply */
if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3))
struct nf_conn_help *help = nfct_help(ct);
const struct nf_conntrack_helper *helper;
+ if (!help)
+ return -EINVAL;
+
if (attr == NULL)
return -EINVAL;
const struct nf_conn_help *help = nfct_help(ct);
const struct nf_conntrack_helper *helper;
+ if (!help)
+ return 0;
+
helper = rcu_dereference(help->helper);
if (helper && helper->data_len &&
nla_put(skb, CTA_HELP_INFO, helper->data_len, &help->data))