aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_metrics.c
diff options
context:
space:
mode:
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;