nf: xt_qtaguid: fix handling for cases where tunnels are used.
* fix skb->dev vs par->in/out When there is some forwarding going on, it introduces extra state around devs associated with xt_action_param->in/out and sk_buff->dev. E.g. par->in and par->out are both set, or skb->dev and par->out are both set (and different) This would lead qtaguid to make the wrong assumption about the direction and update the wrong device stats. Now we rely more on par->in/out. * Fix handling when qtaguid is used as "owner" When qtaguid is used as an owner module, and sk_socket->file is not there (happens when tunnels are involved), it would incorrectly do a tag stats update. * Correct debug messages. Bug: 11687690 Change-Id: I2b1ff8bd7131969ce9e25f8291d83a6280b3ba7f CRs-Fixed: 747810 Signed-off-by: JP Abgrall <jpa@google.com> Git-commit: 2b71479d6f5fe8f33b335f713380f72037244395 Git-repo: https://www.codeaurora.org/cgit/quic/la/kernel/mediatek [imaund@codeaurora.org: Resolved trivial context conflicts.] Signed-off-by: Ian Maund <imaund@codeaurora.org> [bflowers@codeaurora.org: Resolved merge conflicts] Signed-off-by: Bryse Flowers <bflowers@codeaurora.org>
This commit is contained in:
parent
aaf356abef
commit
97a4ac881a
1 changed files with 69 additions and 81 deletions
|
@ -1175,6 +1175,38 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only)
|
|||
spin_unlock_bh(&iface_stat_list_lock);
|
||||
}
|
||||
|
||||
/* Guarantied to return a net_device that has a name */
|
||||
static void get_dev_and_dir(const struct sk_buff *skb,
|
||||
struct xt_action_param *par,
|
||||
enum ifs_tx_rx *direction,
|
||||
const struct net_device **el_dev)
|
||||
{
|
||||
BUG_ON(!direction || !el_dev);
|
||||
|
||||
if (par->in) {
|
||||
*el_dev = par->in;
|
||||
*direction = IFS_RX;
|
||||
} else if (par->out) {
|
||||
*el_dev = par->out;
|
||||
*direction = IFS_TX;
|
||||
} else {
|
||||
pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
|
||||
par->hooknum, __func__);
|
||||
BUG();
|
||||
}
|
||||
if (unlikely(!(*el_dev)->name)) {
|
||||
pr_err("qtaguid[%d]: %s(): no dev->name?!!\n",
|
||||
par->hooknum, __func__);
|
||||
BUG();
|
||||
}
|
||||
if (skb->dev && *el_dev != skb->dev) {
|
||||
MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n",
|
||||
par->hooknum, skb->dev, skb->dev->name,
|
||||
*direction == IFS_RX ? "in" : "out", *el_dev,
|
||||
(*el_dev)->name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update stats for the specified interface from the skb.
|
||||
* Do nothing if the entry
|
||||
|
@ -1186,50 +1218,27 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb,
|
|||
{
|
||||
struct iface_stat *entry;
|
||||
const struct net_device *el_dev;
|
||||
enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
|
||||
enum ifs_tx_rx direction;
|
||||
int bytes = skb->len;
|
||||
int proto;
|
||||
|
||||
if (!skb->dev) {
|
||||
MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
|
||||
el_dev = par->in ? : par->out;
|
||||
} else {
|
||||
const struct net_device *other_dev;
|
||||
el_dev = skb->dev;
|
||||
other_dev = par->in ? : par->out;
|
||||
if (el_dev != other_dev) {
|
||||
MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
|
||||
"par->(in/out)=%p %s\n",
|
||||
par->hooknum, el_dev, el_dev->name, other_dev,
|
||||
other_dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(!el_dev)) {
|
||||
pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n",
|
||||
par->hooknum, __func__);
|
||||
BUG();
|
||||
} else if (unlikely(!el_dev->name)) {
|
||||
pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n",
|
||||
par->hooknum, __func__);
|
||||
BUG();
|
||||
} else {
|
||||
proto = ipx_proto(skb, par);
|
||||
MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
|
||||
par->hooknum, el_dev->name, el_dev->type,
|
||||
par->family, proto);
|
||||
}
|
||||
get_dev_and_dir(skb, par, &direction, &el_dev);
|
||||
proto = ipx_proto(skb, par);
|
||||
MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): "
|
||||
"type=%d fam=%d proto=%d dir=%d\n",
|
||||
par->hooknum, __func__, el_dev->name, el_dev->type,
|
||||
par->family, proto, direction);
|
||||
|
||||
spin_lock_bh(&iface_stat_list_lock);
|
||||
entry = get_iface_entry(el_dev->name);
|
||||
if (entry == NULL) {
|
||||
IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
|
||||
__func__, el_dev->name);
|
||||
IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n",
|
||||
par->hooknum, __func__, el_dev->name);
|
||||
spin_unlock_bh(&iface_stat_list_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
|
||||
IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__,
|
||||
el_dev->name, entry);
|
||||
|
||||
data_counters_update(&entry->totals_via_skb, 0, direction, proto,
|
||||
|
@ -1294,14 +1303,14 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
|
|||
spin_lock_bh(&iface_stat_list_lock);
|
||||
iface_entry = get_iface_entry(ifname);
|
||||
if (!iface_entry) {
|
||||
pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
|
||||
pr_err_ratelimited("qtaguid: tag_stat: stat_update() "
|
||||
"%s not found\n", ifname);
|
||||
spin_unlock_bh(&iface_stat_list_lock);
|
||||
return;
|
||||
}
|
||||
/* It is ok to process data when an iface_entry is inactive */
|
||||
|
||||
MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n",
|
||||
MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%p\n",
|
||||
ifname, iface_entry);
|
||||
|
||||
/*
|
||||
|
@ -1318,7 +1327,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
|
|||
tag = combine_atag_with_uid(acct_tag, uid);
|
||||
uid_tag = make_tag_from_uid(uid);
|
||||
}
|
||||
MT_DEBUG("qtaguid: iface_stat: stat_update(): "
|
||||
MT_DEBUG("qtaguid: tag_stat: stat_update(): "
|
||||
" looking for tag=0x%llx (uid=%u) in ife=%p\n",
|
||||
tag, get_uid_from_tag(tag), iface_entry);
|
||||
/* Loop over tag list under this interface for {acct_tag,uid_tag} */
|
||||
|
@ -1578,8 +1587,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
|
|||
struct sock *sk;
|
||||
unsigned int hook_mask = (1 << par->hooknum);
|
||||
|
||||
MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb,
|
||||
par->hooknum, par->family);
|
||||
MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n",
|
||||
par->hooknum, skb, par->family);
|
||||
|
||||
/*
|
||||
* Let's not abuse the the xt_socket_get*_sk(), or else it will
|
||||
|
@ -1600,8 +1609,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
|
|||
}
|
||||
|
||||
if (sk) {
|
||||
MT_DEBUG("qtaguid: %p->sk_proto=%u "
|
||||
"->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
|
||||
MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n",
|
||||
par->hooknum, sk, sk->sk_protocol, sk->sk_state);
|
||||
/*
|
||||
* When in TCP_TIME_WAIT the sk is not a "struct sock" but
|
||||
* "struct inet_timewait_sock" which is missing fields.
|
||||
|
@ -1619,37 +1628,19 @@ static void account_for_uid(const struct sk_buff *skb,
|
|||
struct xt_action_param *par)
|
||||
{
|
||||
const struct net_device *el_dev;
|
||||
enum ifs_tx_rx direction;
|
||||
int proto;
|
||||
|
||||
if (!skb->dev) {
|
||||
MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
|
||||
el_dev = par->in ? : par->out;
|
||||
} else {
|
||||
const struct net_device *other_dev;
|
||||
el_dev = skb->dev;
|
||||
other_dev = par->in ? : par->out;
|
||||
if (el_dev != other_dev) {
|
||||
MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
|
||||
"par->(in/out)=%p %s\n",
|
||||
par->hooknum, el_dev, el_dev->name, other_dev,
|
||||
other_dev->name);
|
||||
}
|
||||
}
|
||||
get_dev_and_dir(skb, par, &direction, &el_dev);
|
||||
proto = ipx_proto(skb, par);
|
||||
MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n",
|
||||
par->hooknum, el_dev->name, el_dev->type,
|
||||
par->family, proto, direction);
|
||||
|
||||
if (unlikely(!el_dev)) {
|
||||
pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum);
|
||||
} else if (unlikely(!el_dev->name)) {
|
||||
pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum);
|
||||
} else {
|
||||
int proto = ipx_proto(skb, par);
|
||||
MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
|
||||
par->hooknum, el_dev->name, el_dev->type,
|
||||
par->family, proto);
|
||||
|
||||
if_tag_stat_update(el_dev->name, uid,
|
||||
skb->sk ? skb->sk : alternate_sk,
|
||||
par->in ? IFS_RX : IFS_TX,
|
||||
proto, skb->len);
|
||||
}
|
||||
if_tag_stat_update(el_dev->name, uid,
|
||||
skb->sk ? skb->sk : alternate_sk,
|
||||
direction,
|
||||
proto, skb->len);
|
||||
}
|
||||
|
||||
static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
|
@ -1661,6 +1652,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
kuid_t sock_uid;
|
||||
bool res;
|
||||
bool set_sk_callback_lock = false;
|
||||
/*
|
||||
* TODO: unhack how to force just accounting.
|
||||
* For now we only do tag stats when the uid-owner is not requested
|
||||
*/
|
||||
bool do_tag_stat = !(info->match & XT_QTAGUID_UID);
|
||||
|
||||
if (unlikely(module_passive))
|
||||
return (info->match ^ info->invert) == 0;
|
||||
|
@ -1734,12 +1730,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
* couldn't find the owner, so for now we just count them
|
||||
* against the system.
|
||||
*/
|
||||
/*
|
||||
* TODO: unhack how to force just accounting.
|
||||
* For now we only do iface stats when the uid-owner is not
|
||||
* requested.
|
||||
*/
|
||||
if (!(info->match & XT_QTAGUID_UID))
|
||||
if (do_tag_stat)
|
||||
account_for_uid(skb, sk, 0, par);
|
||||
MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
|
||||
par->hooknum,
|
||||
|
@ -1754,18 +1745,15 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
filp = sk->sk_socket->file;
|
||||
if (filp == NULL) {
|
||||
MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum);
|
||||
account_for_uid(skb, sk, 0, par);
|
||||
if (do_tag_stat)
|
||||
account_for_uid(skb, sk, 0, par);
|
||||
res = ((info->match ^ info->invert) &
|
||||
(XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0;
|
||||
atomic64_inc(&qtu_events.match_no_sk_file);
|
||||
goto put_sock_ret_res;
|
||||
}
|
||||
sock_uid = filp->f_cred->fsuid;
|
||||
/*
|
||||
* TODO: unhack how to force just accounting.
|
||||
* For now we only do iface stats when the uid-owner is not requested
|
||||
*/
|
||||
if (!(info->match & XT_QTAGUID_UID))
|
||||
if (do_tag_stat)
|
||||
account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue