diff options
Diffstat (limited to 'net/ipv4/tcp_metrics.c')
-rw-r--r-- | net/ipv4/tcp_metrics.c | 48 |
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 | ||
880 | static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, | 880 | static 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 | ||
906 | static 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 | |||
914 | static 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 | |||
904 | static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) | 921 | static 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; |