Commit 3e7013dd authored by Christoph Paasch's avatar Christoph Paasch Committed by David S. Miller

tcp: metrics: Allow selective get/del of tcp-metrics based on src IP

We want to be able to get/del tcp-metrics based on the src IP. This
patch adds the necessary parsing of the netlink attribute and if the
source address is set, it will match on this one too.
Signed-off-by: default avatarChristoph Paasch <christoph.paasch@uclouvain.be>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bbf852b9
...@@ -877,44 +877,66 @@ static int tcp_metrics_nl_dump(struct sk_buff *skb, ...@@ -877,44 +877,66 @@ static int tcp_metrics_nl_dump(struct sk_buff *skb,
return skb->len; return skb->len;
} }
static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
unsigned int *hash, int optional) unsigned int *hash, int optional, int v4, int v6)
{ {
struct nlattr *a; struct nlattr *a;
a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV4]; a = info->attrs[v4];
if (a) { if (a) {
addr->family = AF_INET; addr->family = AF_INET;
addr->addr.a4 = nla_get_be32(a); addr->addr.a4 = nla_get_be32(a);
*hash = (__force unsigned int) addr->addr.a4; if (hash)
*hash = (__force unsigned int) addr->addr.a4;
return 0; return 0;
} }
a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; a = info->attrs[v6];
if (a) { if (a) {
if (nla_len(a) != sizeof(struct in6_addr)) if (nla_len(a) != sizeof(struct in6_addr))
return -EINVAL; return -EINVAL;
addr->family = AF_INET6; addr->family = AF_INET6;
memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6));
*hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); if (hash)
*hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6);
return 0; return 0;
} }
return optional ? 1 : -EAFNOSUPPORT; return optional ? 1 : -EAFNOSUPPORT;
} }
static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
unsigned int *hash, int optional)
{
return __parse_nl_addr(info, addr, hash, optional,
TCP_METRICS_ATTR_ADDR_IPV4,
TCP_METRICS_ATTR_ADDR_IPV6);
}
static int parse_nl_saddr(struct genl_info *info, struct inetpeer_addr *addr)
{
return __parse_nl_addr(info, addr, NULL, 0,
TCP_METRICS_ATTR_SADDR_IPV4,
TCP_METRICS_ATTR_SADDR_IPV6);
}
static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
{ {
struct tcp_metrics_block *tm; struct tcp_metrics_block *tm;
struct inetpeer_addr daddr; struct inetpeer_addr saddr, daddr;
unsigned int hash; unsigned int hash;
struct sk_buff *msg; struct sk_buff *msg;
struct net *net = genl_info_net(info); struct net *net = genl_info_net(info);
void *reply; void *reply;
int ret; int ret;
bool src = true;
ret = parse_nl_addr(info, &daddr, &hash, 0); ret = parse_nl_addr(info, &daddr, &hash, 0);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = parse_nl_saddr(info, &saddr);
if (ret < 0)
src = false;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
...@@ -929,7 +951,8 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -929,7 +951,8 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
rcu_read_lock(); rcu_read_lock();
for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm;
tm = rcu_dereference(tm->tcpm_next)) { tm = rcu_dereference(tm->tcpm_next)) {
if (addr_same(&tm->tcpm_daddr, &daddr)) { if (addr_same(&tm->tcpm_daddr, &daddr) &&
(!src || addr_same(&tm->tcpm_saddr, &saddr))) {
ret = tcp_metrics_fill_info(msg, tm); ret = tcp_metrics_fill_info(msg, tm);
break; break;
} }
...@@ -984,23 +1007,28 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -984,23 +1007,28 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info)
struct tcpm_hash_bucket *hb; struct tcpm_hash_bucket *hb;
struct tcp_metrics_block *tm, *tmlist = NULL; struct tcp_metrics_block *tm, *tmlist = NULL;
struct tcp_metrics_block __rcu **pp; struct tcp_metrics_block __rcu **pp;
struct inetpeer_addr daddr; struct inetpeer_addr saddr, daddr;
unsigned int hash; unsigned int hash;
struct net *net = genl_info_net(info); struct net *net = genl_info_net(info);
int ret; int ret;
bool src = true;
ret = parse_nl_addr(info, &daddr, &hash, 1); ret = parse_nl_addr(info, &daddr, &hash, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret > 0) if (ret > 0)
return tcp_metrics_flush_all(net); return tcp_metrics_flush_all(net);
ret = parse_nl_saddr(info, &saddr);
if (ret < 0)
src = false;
hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log);
hb = net->ipv4.tcp_metrics_hash + hash; hb = net->ipv4.tcp_metrics_hash + hash;
pp = &hb->chain; pp = &hb->chain;
spin_lock_bh(&tcp_metrics_lock); spin_lock_bh(&tcp_metrics_lock);
for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) {
if (addr_same(&tm->tcpm_daddr, &daddr)) { if (addr_same(&tm->tcpm_daddr, &daddr) &&
(!src || addr_same(&tm->tcpm_saddr, &saddr))) {
*pp = tm->tcpm_next; *pp = tm->tcpm_next;
tm->tcpm_next = tmlist; tm->tcpm_next = tmlist;
tmlist = tm; tmlist = tm;
......
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