aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_metrics.c
diff options
context:
space:
mode:
authorChristoph Paasch <christoph.paasch@uclouvain.be>2014-01-08 10:05:59 -0500
committerDavid S. Miller <davem@davemloft.net>2014-01-10 17:38:18 -0500
commit3e7013ddf55af7bc191792b8aea0c2b94fb0fef5 (patch)
tree073ff3aa496669057f42e54112f0ecceaf760202 /net/ipv4/tcp_metrics.c
parentbbf852b96ebdc6d1be7a67143824523280bbcf44 (diff)
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: Christoph Paasch <christoph.paasch@uclouvain.be> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_metrics.c')
-rw-r--r--net/ipv4/tcp_metrics.c48
1 files changed, 38 insertions, 10 deletions
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index e150f264c8e2..699a42faab9c 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -877,44 +877,66 @@ done:
877 return skb->len; 877 return skb->len;
878} 878}
879 879
880static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, 880static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
881 unsigned int *hash, int optional) 881 unsigned int *hash, int optional, int v4, int v6)
882{ 882{
883 struct nlattr *a; 883 struct nlattr *a;
884 884
885 a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV4]; 885 a = info->attrs[v4];
886 if (a) { 886 if (a) {
887 addr->family = AF_INET; 887 addr->family = AF_INET;
888 addr->addr.a4 = nla_get_be32(a); 888 addr->addr.a4 = nla_get_be32(a);
889 *hash = (__force unsigned int) addr->addr.a4; 889 if (hash)
890 *hash = (__force unsigned int) addr->addr.a4;
890 return 0; 891 return 0;
891 } 892 }
892 a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; 893 a = info->attrs[v6];
893 if (a) { 894 if (a) {
894 if (nla_len(a) != sizeof(struct in6_addr)) 895 if (nla_len(a) != sizeof(struct in6_addr))
895 return -EINVAL; 896 return -EINVAL;
896 addr->family = AF_INET6; 897 addr->family = AF_INET6;
897 memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); 898 memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6));
898 *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); 899 if (hash)
900 *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6);
899 return 0; 901 return 0;
900 } 902 }
901 return optional ? 1 : -EAFNOSUPPORT; 903 return optional ? 1 : -EAFNOSUPPORT;
902} 904}
903 905
906static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
907 unsigned int *hash, int optional)
908{
909 return __parse_nl_addr(info, addr, hash, optional,
910 TCP_METRICS_ATTR_ADDR_IPV4,
911 TCP_METRICS_ATTR_ADDR_IPV6);
912}
913
914static int parse_nl_saddr(struct genl_info *info, struct inetpeer_addr *addr)
915{
916 return __parse_nl_addr(info, addr, NULL, 0,
917 TCP_METRICS_ATTR_SADDR_IPV4,
918 TCP_METRICS_ATTR_SADDR_IPV6);
919}
920
904static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) 921static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
905{ 922{
906 struct tcp_metrics_block *tm; 923 struct tcp_metrics_block *tm;
907 struct inetpeer_addr daddr; 924 struct inetpeer_addr saddr, daddr;
908 unsigned int hash; 925 unsigned int hash;
909 struct sk_buff *msg; 926 struct sk_buff *msg;
910 struct net *net = genl_info_net(info); 927 struct net *net = genl_info_net(info);
911 void *reply; 928 void *reply;
912 int ret; 929 int ret;
930 bool src = true;
913 931
914 ret = parse_nl_addr(info, &daddr, &hash, 0); 932 ret = parse_nl_addr(info, &daddr, &hash, 0);
915 if (ret < 0) 933 if (ret < 0)
916 return ret; 934 return ret;
917 935
936 ret = parse_nl_saddr(info, &saddr);
937 if (ret < 0)
938 src = false;
939
918 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 940 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
919 if (!msg) 941 if (!msg)
920 return -ENOMEM; 942 return -ENOMEM;
@@ -929,7 +951,8 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
929 rcu_read_lock(); 951 rcu_read_lock();
930 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; 952 for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm;
931 tm = rcu_dereference(tm->tcpm_next)) { 953 tm = rcu_dereference(tm->tcpm_next)) {
932 if (addr_same(&tm->tcpm_daddr, &daddr)) { 954 if (addr_same(&tm->tcpm_daddr, &daddr) &&
955 (!src || addr_same(&tm->tcpm_saddr, &saddr))) {
933 ret = tcp_metrics_fill_info(msg, tm); 956 ret = tcp_metrics_fill_info(msg, tm);
934 break; 957 break;
935 } 958 }
@@ -984,23 +1007,28 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info)
984 struct tcpm_hash_bucket *hb; 1007 struct tcpm_hash_bucket *hb;
985 struct tcp_metrics_block *tm, *tmlist = NULL; 1008 struct tcp_metrics_block *tm, *tmlist = NULL;
986 struct tcp_metrics_block __rcu **pp; 1009 struct tcp_metrics_block __rcu **pp;
987 struct inetpeer_addr daddr; 1010 struct inetpeer_addr saddr, daddr;
988 unsigned int hash; 1011 unsigned int hash;
989 struct net *net = genl_info_net(info); 1012 struct net *net = genl_info_net(info);
990 int ret; 1013 int ret;
1014 bool src = true;
991 1015
992 ret = parse_nl_addr(info, &daddr, &hash, 1); 1016 ret = parse_nl_addr(info, &daddr, &hash, 1);
993 if (ret < 0) 1017 if (ret < 0)
994 return ret; 1018 return ret;
995 if (ret > 0) 1019 if (ret > 0)
996 return tcp_metrics_flush_all(net); 1020 return tcp_metrics_flush_all(net);
1021 ret = parse_nl_saddr(info, &saddr);
1022 if (ret < 0)
1023 src = false;
997 1024
998 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); 1025 hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log);
999 hb = net->ipv4.tcp_metrics_hash + hash; 1026 hb = net->ipv4.tcp_metrics_hash + hash;
1000 pp = &hb->chain; 1027 pp = &hb->chain;
1001 spin_lock_bh(&tcp_metrics_lock); 1028 spin_lock_bh(&tcp_metrics_lock);
1002 for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { 1029 for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) {
1003 if (addr_same(&tm->tcpm_daddr, &daddr)) { 1030 if (addr_same(&tm->tcpm_daddr, &daddr) &&
1031 (!src || addr_same(&tm->tcpm_saddr, &saddr))) {
1004 *pp = tm->tcpm_next; 1032 *pp = tm->tcpm_next;
1005 tm->tcpm_next = tmlist; 1033 tm->tcpm_next = tmlist;
1006 tmlist = tm; 1034 tmlist = tm;