diff options
Diffstat (limited to 'net/ipv4/tcp_metrics.c')
-rw-r--r-- | net/ipv4/tcp_metrics.c | 239 |
1 files changed, 160 insertions, 79 deletions
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 06493736fbc8..d547075d8300 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c | |||
@@ -22,6 +22,10 @@ | |||
22 | 22 | ||
23 | int sysctl_tcp_nometrics_save __read_mostly; | 23 | int sysctl_tcp_nometrics_save __read_mostly; |
24 | 24 | ||
25 | static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *saddr, | ||
26 | const struct inetpeer_addr *daddr, | ||
27 | struct net *net, unsigned int hash); | ||
28 | |||
25 | struct tcp_fastopen_metrics { | 29 | struct tcp_fastopen_metrics { |
26 | u16 mss; | 30 | u16 mss; |
27 | u16 syn_loss:10; /* Recurring Fast Open SYN losses */ | 31 | u16 syn_loss:10; /* Recurring Fast Open SYN losses */ |
@@ -31,7 +35,8 @@ struct tcp_fastopen_metrics { | |||
31 | 35 | ||
32 | struct tcp_metrics_block { | 36 | struct tcp_metrics_block { |
33 | struct tcp_metrics_block __rcu *tcpm_next; | 37 | struct tcp_metrics_block __rcu *tcpm_next; |
34 | struct inetpeer_addr tcpm_addr; | 38 | struct inetpeer_addr tcpm_saddr; |
39 | struct inetpeer_addr tcpm_daddr; | ||
35 | unsigned long tcpm_stamp; | 40 | unsigned long tcpm_stamp; |
36 | u32 tcpm_ts; | 41 | u32 tcpm_ts; |
37 | u32 tcpm_ts_stamp; | 42 | u32 tcpm_ts_stamp; |
@@ -130,16 +135,42 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst, | |||
130 | } | 135 | } |
131 | } | 136 | } |
132 | 137 | ||
138 | #define TCP_METRICS_TIMEOUT (60 * 60 * HZ) | ||
139 | |||
140 | static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst) | ||
141 | { | ||
142 | if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT))) | ||
143 | tcpm_suck_dst(tm, dst, false); | ||
144 | } | ||
145 | |||
146 | #define TCP_METRICS_RECLAIM_DEPTH 5 | ||
147 | #define TCP_METRICS_RECLAIM_PTR (struct tcp_metrics_block *) 0x1UL | ||
148 | |||
133 | static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, | 149 | static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, |
134 | struct inetpeer_addr *addr, | 150 | struct inetpeer_addr *saddr, |
135 | unsigned int hash, | 151 | struct inetpeer_addr *daddr, |
136 | bool reclaim) | 152 | unsigned int hash) |
137 | { | 153 | { |
138 | struct tcp_metrics_block *tm; | 154 | struct tcp_metrics_block *tm; |
139 | struct net *net; | 155 | struct net *net; |
156 | bool reclaim = false; | ||
140 | 157 | ||
141 | spin_lock_bh(&tcp_metrics_lock); | 158 | spin_lock_bh(&tcp_metrics_lock); |
142 | net = dev_net(dst->dev); | 159 | net = dev_net(dst->dev); |
160 | |||
161 | /* While waiting for the spin-lock the cache might have been populated | ||
162 | * with this entry and so we have to check again. | ||
163 | */ | ||
164 | tm = __tcp_get_metrics(saddr, daddr, net, hash); | ||
165 | if (tm == TCP_METRICS_RECLAIM_PTR) { | ||
166 | reclaim = true; | ||
167 | tm = NULL; | ||
168 | } | ||
169 | if (tm) { | ||
170 | tcpm_check_stamp(tm, dst); | ||
171 | goto out_unlock; | ||
172 | } | ||
173 | |||
143 | if (unlikely(reclaim)) { | 174 | if (unlikely(reclaim)) { |
144 | struct tcp_metrics_block *oldest; | 175 | struct tcp_metrics_block *oldest; |
145 | 176 | ||
@@ -155,7 +186,8 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, | |||
155 | if (!tm) | 186 | if (!tm) |
156 | goto out_unlock; | 187 | goto out_unlock; |
157 | } | 188 | } |
158 | tm->tcpm_addr = *addr; | 189 | tm->tcpm_saddr = *saddr; |
190 | tm->tcpm_daddr = *daddr; | ||
159 | 191 | ||
160 | tcpm_suck_dst(tm, dst, true); | 192 | tcpm_suck_dst(tm, dst, true); |
161 | 193 | ||
@@ -169,17 +201,6 @@ out_unlock: | |||
169 | return tm; | 201 | return tm; |
170 | } | 202 | } |
171 | 203 | ||
172 | #define TCP_METRICS_TIMEOUT (60 * 60 * HZ) | ||
173 | |||
174 | static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst) | ||
175 | { | ||
176 | if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT))) | ||
177 | tcpm_suck_dst(tm, dst, false); | ||
178 | } | ||
179 | |||
180 | #define TCP_METRICS_RECLAIM_DEPTH 5 | ||
181 | #define TCP_METRICS_RECLAIM_PTR (struct tcp_metrics_block *) 0x1UL | ||
182 | |||
183 | static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, int depth) | 204 | static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, int depth) |
184 | { | 205 | { |
185 | if (tm) | 206 | if (tm) |
@@ -189,7 +210,8 @@ static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, in | |||
189 | return NULL; | 210 | return NULL; |
190 | } | 211 | } |
191 | 212 | ||
192 | static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *addr, | 213 | static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *saddr, |
214 | const struct inetpeer_addr *daddr, | ||
193 | struct net *net, unsigned int hash) | 215 | struct net *net, unsigned int hash) |
194 | { | 216 | { |
195 | struct tcp_metrics_block *tm; | 217 | struct tcp_metrics_block *tm; |
@@ -197,7 +219,8 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *a | |||
197 | 219 | ||
198 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 220 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
199 | tm = rcu_dereference(tm->tcpm_next)) { | 221 | tm = rcu_dereference(tm->tcpm_next)) { |
200 | if (addr_same(&tm->tcpm_addr, addr)) | 222 | if (addr_same(&tm->tcpm_saddr, saddr) && |
223 | addr_same(&tm->tcpm_daddr, daddr)) | ||
201 | break; | 224 | break; |
202 | depth++; | 225 | depth++; |
203 | } | 226 | } |
@@ -208,19 +231,22 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
208 | struct dst_entry *dst) | 231 | struct dst_entry *dst) |
209 | { | 232 | { |
210 | struct tcp_metrics_block *tm; | 233 | struct tcp_metrics_block *tm; |
211 | struct inetpeer_addr addr; | 234 | struct inetpeer_addr saddr, daddr; |
212 | unsigned int hash; | 235 | unsigned int hash; |
213 | struct net *net; | 236 | struct net *net; |
214 | 237 | ||
215 | addr.family = req->rsk_ops->family; | 238 | saddr.family = req->rsk_ops->family; |
216 | switch (addr.family) { | 239 | daddr.family = req->rsk_ops->family; |
240 | switch (daddr.family) { | ||
217 | case AF_INET: | 241 | case AF_INET: |
218 | addr.addr.a4 = inet_rsk(req)->ir_rmt_addr; | 242 | saddr.addr.a4 = inet_rsk(req)->ir_loc_addr; |
219 | hash = (__force unsigned int) addr.addr.a4; | 243 | daddr.addr.a4 = inet_rsk(req)->ir_rmt_addr; |
244 | hash = (__force unsigned int) daddr.addr.a4; | ||
220 | break; | 245 | break; |
221 | #if IS_ENABLED(CONFIG_IPV6) | 246 | #if IS_ENABLED(CONFIG_IPV6) |
222 | case AF_INET6: | 247 | case AF_INET6: |
223 | *(struct in6_addr *)addr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; | 248 | *(struct in6_addr *)saddr.addr.a6 = inet_rsk(req)->ir_v6_loc_addr; |
249 | *(struct in6_addr *)daddr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; | ||
224 | hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); | 250 | hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); |
225 | break; | 251 | break; |
226 | #endif | 252 | #endif |
@@ -233,7 +259,8 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
233 | 259 | ||
234 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 260 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
235 | tm = rcu_dereference(tm->tcpm_next)) { | 261 | tm = rcu_dereference(tm->tcpm_next)) { |
236 | if (addr_same(&tm->tcpm_addr, &addr)) | 262 | if (addr_same(&tm->tcpm_saddr, &saddr) && |
263 | addr_same(&tm->tcpm_daddr, &daddr)) | ||
237 | break; | 264 | break; |
238 | } | 265 | } |
239 | tcpm_check_stamp(tm, dst); | 266 | tcpm_check_stamp(tm, dst); |
@@ -243,32 +270,44 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
243 | static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) | 270 | static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) |
244 | { | 271 | { |
245 | struct tcp_metrics_block *tm; | 272 | struct tcp_metrics_block *tm; |
246 | struct inetpeer_addr addr; | 273 | struct inetpeer_addr saddr, daddr; |
247 | unsigned int hash; | 274 | unsigned int hash; |
248 | struct net *net; | 275 | struct net *net; |
249 | 276 | ||
250 | addr.family = tw->tw_family; | 277 | if (tw->tw_family == AF_INET) { |
251 | switch (addr.family) { | 278 | saddr.family = AF_INET; |
252 | case AF_INET: | 279 | saddr.addr.a4 = tw->tw_rcv_saddr; |
253 | addr.addr.a4 = tw->tw_daddr; | 280 | daddr.family = AF_INET; |
254 | hash = (__force unsigned int) addr.addr.a4; | 281 | daddr.addr.a4 = tw->tw_daddr; |
255 | break; | 282 | hash = (__force unsigned int) daddr.addr.a4; |
283 | } | ||
256 | #if IS_ENABLED(CONFIG_IPV6) | 284 | #if IS_ENABLED(CONFIG_IPV6) |
257 | case AF_INET6: | 285 | else if (tw->tw_family == AF_INET6) { |
258 | *(struct in6_addr *)addr.addr.a6 = tw->tw_v6_daddr; | 286 | if (ipv6_addr_v4mapped(&tw->tw_v6_daddr)) { |
259 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); | 287 | saddr.family = AF_INET; |
260 | break; | 288 | saddr.addr.a4 = tw->tw_rcv_saddr; |
289 | daddr.family = AF_INET; | ||
290 | daddr.addr.a4 = tw->tw_daddr; | ||
291 | hash = (__force unsigned int) daddr.addr.a4; | ||
292 | } else { | ||
293 | saddr.family = AF_INET6; | ||
294 | *(struct in6_addr *)saddr.addr.a6 = tw->tw_v6_rcv_saddr; | ||
295 | daddr.family = AF_INET6; | ||
296 | *(struct in6_addr *)daddr.addr.a6 = tw->tw_v6_daddr; | ||
297 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); | ||
298 | } | ||
299 | } | ||
261 | #endif | 300 | #endif |
262 | default: | 301 | else |
263 | return NULL; | 302 | return NULL; |
264 | } | ||
265 | 303 | ||
266 | net = twsk_net(tw); | 304 | net = twsk_net(tw); |
267 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 305 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
268 | 306 | ||
269 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 307 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
270 | tm = rcu_dereference(tm->tcpm_next)) { | 308 | tm = rcu_dereference(tm->tcpm_next)) { |
271 | if (addr_same(&tm->tcpm_addr, &addr)) | 309 | if (addr_same(&tm->tcpm_saddr, &saddr) && |
310 | addr_same(&tm->tcpm_daddr, &daddr)) | ||
272 | break; | 311 | break; |
273 | } | 312 | } |
274 | return tm; | 313 | return tm; |
@@ -279,38 +318,45 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, | |||
279 | bool create) | 318 | bool create) |
280 | { | 319 | { |
281 | struct tcp_metrics_block *tm; | 320 | struct tcp_metrics_block *tm; |
282 | struct inetpeer_addr addr; | 321 | struct inetpeer_addr saddr, daddr; |
283 | unsigned int hash; | 322 | unsigned int hash; |
284 | struct net *net; | 323 | struct net *net; |
285 | bool reclaim; | ||
286 | 324 | ||
287 | addr.family = sk->sk_family; | 325 | if (sk->sk_family == AF_INET) { |
288 | switch (addr.family) { | 326 | saddr.family = AF_INET; |
289 | case AF_INET: | 327 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; |
290 | addr.addr.a4 = inet_sk(sk)->inet_daddr; | 328 | daddr.family = AF_INET; |
291 | hash = (__force unsigned int) addr.addr.a4; | 329 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; |
292 | break; | 330 | hash = (__force unsigned int) daddr.addr.a4; |
331 | } | ||
293 | #if IS_ENABLED(CONFIG_IPV6) | 332 | #if IS_ENABLED(CONFIG_IPV6) |
294 | case AF_INET6: | 333 | else if (sk->sk_family == AF_INET6) { |
295 | *(struct in6_addr *)addr.addr.a6 = sk->sk_v6_daddr; | 334 | if (ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { |
296 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); | 335 | saddr.family = AF_INET; |
297 | break; | 336 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; |
337 | daddr.family = AF_INET; | ||
338 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; | ||
339 | hash = (__force unsigned int) daddr.addr.a4; | ||
340 | } else { | ||
341 | saddr.family = AF_INET6; | ||
342 | *(struct in6_addr *)saddr.addr.a6 = sk->sk_v6_rcv_saddr; | ||
343 | daddr.family = AF_INET6; | ||
344 | *(struct in6_addr *)daddr.addr.a6 = sk->sk_v6_daddr; | ||
345 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); | ||
346 | } | ||
347 | } | ||
298 | #endif | 348 | #endif |
299 | default: | 349 | else |
300 | return NULL; | 350 | return NULL; |
301 | } | ||
302 | 351 | ||
303 | net = dev_net(dst->dev); | 352 | net = dev_net(dst->dev); |
304 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 353 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
305 | 354 | ||
306 | tm = __tcp_get_metrics(&addr, net, hash); | 355 | tm = __tcp_get_metrics(&saddr, &daddr, net, hash); |
307 | reclaim = false; | 356 | if (tm == TCP_METRICS_RECLAIM_PTR) |
308 | if (tm == TCP_METRICS_RECLAIM_PTR) { | ||
309 | reclaim = true; | ||
310 | tm = NULL; | 357 | tm = NULL; |
311 | } | ||
312 | if (!tm && create) | 358 | if (!tm && create) |
313 | tm = tcpm_new(dst, &addr, hash, reclaim); | 359 | tm = tcpm_new(dst, &saddr, &daddr, hash); |
314 | else | 360 | else |
315 | tcpm_check_stamp(tm, dst); | 361 | tcpm_check_stamp(tm, dst); |
316 | 362 | ||
@@ -724,15 +770,21 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, | |||
724 | struct nlattr *nest; | 770 | struct nlattr *nest; |
725 | int i; | 771 | int i; |
726 | 772 | ||
727 | switch (tm->tcpm_addr.family) { | 773 | switch (tm->tcpm_daddr.family) { |
728 | case AF_INET: | 774 | case AF_INET: |
729 | if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, | 775 | if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, |
730 | tm->tcpm_addr.addr.a4) < 0) | 776 | tm->tcpm_daddr.addr.a4) < 0) |
777 | goto nla_put_failure; | ||
778 | if (nla_put_be32(msg, TCP_METRICS_ATTR_SADDR_IPV4, | ||
779 | tm->tcpm_saddr.addr.a4) < 0) | ||
731 | goto nla_put_failure; | 780 | goto nla_put_failure; |
732 | break; | 781 | break; |
733 | case AF_INET6: | 782 | case AF_INET6: |
734 | if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, | 783 | if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, |
735 | tm->tcpm_addr.addr.a6) < 0) | 784 | tm->tcpm_daddr.addr.a6) < 0) |
785 | goto nla_put_failure; | ||
786 | if (nla_put(msg, TCP_METRICS_ATTR_SADDR_IPV6, 16, | ||
787 | tm->tcpm_saddr.addr.a6) < 0) | ||
736 | goto nla_put_failure; | 788 | goto nla_put_failure; |
737 | break; | 789 | break; |
738 | default: | 790 | default: |
@@ -855,44 +907,66 @@ done: | |||
855 | return skb->len; | 907 | return skb->len; |
856 | } | 908 | } |
857 | 909 | ||
858 | static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, | 910 | static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, |
859 | unsigned int *hash, int optional) | 911 | unsigned int *hash, int optional, int v4, int v6) |
860 | { | 912 | { |
861 | struct nlattr *a; | 913 | struct nlattr *a; |
862 | 914 | ||
863 | a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV4]; | 915 | a = info->attrs[v4]; |
864 | if (a) { | 916 | if (a) { |
865 | addr->family = AF_INET; | 917 | addr->family = AF_INET; |
866 | addr->addr.a4 = nla_get_be32(a); | 918 | addr->addr.a4 = nla_get_be32(a); |
867 | *hash = (__force unsigned int) addr->addr.a4; | 919 | if (hash) |
920 | *hash = (__force unsigned int) addr->addr.a4; | ||
868 | return 0; | 921 | return 0; |
869 | } | 922 | } |
870 | a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; | 923 | a = info->attrs[v6]; |
871 | if (a) { | 924 | if (a) { |
872 | if (nla_len(a) != sizeof(struct in6_addr)) | 925 | if (nla_len(a) != sizeof(struct in6_addr)) |
873 | return -EINVAL; | 926 | return -EINVAL; |
874 | addr->family = AF_INET6; | 927 | addr->family = AF_INET6; |
875 | memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); | 928 | memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); |
876 | *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); | 929 | if (hash) |
930 | *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); | ||
877 | return 0; | 931 | return 0; |
878 | } | 932 | } |
879 | return optional ? 1 : -EAFNOSUPPORT; | 933 | return optional ? 1 : -EAFNOSUPPORT; |
880 | } | 934 | } |
881 | 935 | ||
936 | static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, | ||
937 | unsigned int *hash, int optional) | ||
938 | { | ||
939 | return __parse_nl_addr(info, addr, hash, optional, | ||
940 | TCP_METRICS_ATTR_ADDR_IPV4, | ||
941 | TCP_METRICS_ATTR_ADDR_IPV6); | ||
942 | } | ||
943 | |||
944 | static int parse_nl_saddr(struct genl_info *info, struct inetpeer_addr *addr) | ||
945 | { | ||
946 | return __parse_nl_addr(info, addr, NULL, 0, | ||
947 | TCP_METRICS_ATTR_SADDR_IPV4, | ||
948 | TCP_METRICS_ATTR_SADDR_IPV6); | ||
949 | } | ||
950 | |||
882 | static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) | 951 | static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) |
883 | { | 952 | { |
884 | struct tcp_metrics_block *tm; | 953 | struct tcp_metrics_block *tm; |
885 | struct inetpeer_addr addr; | 954 | struct inetpeer_addr saddr, daddr; |
886 | unsigned int hash; | 955 | unsigned int hash; |
887 | struct sk_buff *msg; | 956 | struct sk_buff *msg; |
888 | struct net *net = genl_info_net(info); | 957 | struct net *net = genl_info_net(info); |
889 | void *reply; | 958 | void *reply; |
890 | int ret; | 959 | int ret; |
960 | bool src = true; | ||
891 | 961 | ||
892 | ret = parse_nl_addr(info, &addr, &hash, 0); | 962 | ret = parse_nl_addr(info, &daddr, &hash, 0); |
893 | if (ret < 0) | 963 | if (ret < 0) |
894 | return ret; | 964 | return ret; |
895 | 965 | ||
966 | ret = parse_nl_saddr(info, &saddr); | ||
967 | if (ret < 0) | ||
968 | src = false; | ||
969 | |||
896 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 970 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
897 | if (!msg) | 971 | if (!msg) |
898 | return -ENOMEM; | 972 | return -ENOMEM; |
@@ -907,7 +981,8 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) | |||
907 | rcu_read_lock(); | 981 | rcu_read_lock(); |
908 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 982 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
909 | tm = rcu_dereference(tm->tcpm_next)) { | 983 | tm = rcu_dereference(tm->tcpm_next)) { |
910 | if (addr_same(&tm->tcpm_addr, &addr)) { | 984 | if (addr_same(&tm->tcpm_daddr, &daddr) && |
985 | (!src || addr_same(&tm->tcpm_saddr, &saddr))) { | ||
911 | ret = tcp_metrics_fill_info(msg, tm); | 986 | ret = tcp_metrics_fill_info(msg, tm); |
912 | break; | 987 | break; |
913 | } | 988 | } |
@@ -962,32 +1037,38 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) | |||
962 | struct tcpm_hash_bucket *hb; | 1037 | struct tcpm_hash_bucket *hb; |
963 | struct tcp_metrics_block *tm; | 1038 | struct tcp_metrics_block *tm; |
964 | struct tcp_metrics_block __rcu **pp; | 1039 | struct tcp_metrics_block __rcu **pp; |
965 | struct inetpeer_addr addr; | 1040 | struct inetpeer_addr saddr, daddr; |
966 | unsigned int hash; | 1041 | unsigned int hash; |
967 | struct net *net = genl_info_net(info); | 1042 | struct net *net = genl_info_net(info); |
968 | int ret; | 1043 | int ret; |
1044 | bool src = true, found = false; | ||
969 | 1045 | ||
970 | ret = parse_nl_addr(info, &addr, &hash, 1); | 1046 | ret = parse_nl_addr(info, &daddr, &hash, 1); |
971 | if (ret < 0) | 1047 | if (ret < 0) |
972 | return ret; | 1048 | return ret; |
973 | if (ret > 0) | 1049 | if (ret > 0) |
974 | return tcp_metrics_flush_all(net); | 1050 | return tcp_metrics_flush_all(net); |
1051 | ret = parse_nl_saddr(info, &saddr); | ||
1052 | if (ret < 0) | ||
1053 | src = false; | ||
975 | 1054 | ||
976 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 1055 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
977 | hb = net->ipv4.tcp_metrics_hash + hash; | 1056 | hb = net->ipv4.tcp_metrics_hash + hash; |
978 | pp = &hb->chain; | 1057 | pp = &hb->chain; |
979 | spin_lock_bh(&tcp_metrics_lock); | 1058 | spin_lock_bh(&tcp_metrics_lock); |
980 | for (tm = deref_locked_genl(*pp); tm; | 1059 | for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { |
981 | pp = &tm->tcpm_next, tm = deref_locked_genl(*pp)) { | 1060 | if (addr_same(&tm->tcpm_daddr, &daddr) && |
982 | if (addr_same(&tm->tcpm_addr, &addr)) { | 1061 | (!src || addr_same(&tm->tcpm_saddr, &saddr))) { |
983 | *pp = tm->tcpm_next; | 1062 | *pp = tm->tcpm_next; |
984 | break; | 1063 | kfree_rcu(tm, rcu_head); |
1064 | found = true; | ||
1065 | } else { | ||
1066 | pp = &tm->tcpm_next; | ||
985 | } | 1067 | } |
986 | } | 1068 | } |
987 | spin_unlock_bh(&tcp_metrics_lock); | 1069 | spin_unlock_bh(&tcp_metrics_lock); |
988 | if (!tm) | 1070 | if (!found) |
989 | return -ESRCH; | 1071 | return -ESRCH; |
990 | kfree_rcu(tm, rcu_head); | ||
991 | return 0; | 1072 | return 0; |
992 | } | 1073 | } |
993 | 1074 | ||