diff options
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_xmit.c | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 034a282a6f8c..fa2fdd7421b7 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c | |||
| @@ -213,17 +213,57 @@ static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu) | |||
| 213 | ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu); | 213 | ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | static inline bool ensure_mtu_is_adequate(int skb_af, int rt_mode, | ||
| 217 | struct ip_vs_iphdr *ipvsh, | ||
| 218 | struct sk_buff *skb, int mtu) | ||
| 219 | { | ||
| 220 | #ifdef CONFIG_IP_VS_IPV6 | ||
| 221 | if (skb_af == AF_INET6) { | ||
| 222 | struct net *net = dev_net(skb_dst(skb)->dev); | ||
| 223 | |||
| 224 | if (unlikely(__mtu_check_toobig_v6(skb, mtu))) { | ||
| 225 | if (!skb->dev) | ||
| 226 | skb->dev = net->loopback_dev; | ||
| 227 | /* only send ICMP too big on first fragment */ | ||
| 228 | if (!ipvsh->fragoffs) | ||
| 229 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
| 230 | IP_VS_DBG(1, "frag needed for %pI6c\n", | ||
| 231 | &ipv6_hdr(skb)->saddr); | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | } else | ||
| 235 | #endif | ||
| 236 | { | ||
| 237 | struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); | ||
| 238 | |||
| 239 | /* If we're going to tunnel the packet and pmtu discovery | ||
| 240 | * is disabled, we'll just fragment it anyway | ||
| 241 | */ | ||
| 242 | if ((rt_mode & IP_VS_RT_MODE_TUNNEL) && !sysctl_pmtu_disc(ipvs)) | ||
| 243 | return true; | ||
| 244 | |||
| 245 | if (unlikely(ip_hdr(skb)->frag_off & htons(IP_DF) && | ||
| 246 | skb->len > mtu && !skb_is_gso(skb))) { | ||
| 247 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, | ||
| 248 | htonl(mtu)); | ||
| 249 | IP_VS_DBG(1, "frag needed for %pI4\n", | ||
| 250 | &ip_hdr(skb)->saddr); | ||
| 251 | return false; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return true; | ||
| 256 | } | ||
| 257 | |||
| 216 | /* Get route to destination or remote server */ | 258 | /* Get route to destination or remote server */ |
| 217 | static int | 259 | static int |
| 218 | __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, | 260 | __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, |
| 219 | __be32 daddr, int rt_mode, __be32 *ret_saddr) | 261 | __be32 daddr, int rt_mode, __be32 *ret_saddr, |
| 262 | struct ip_vs_iphdr *ipvsh) | ||
| 220 | { | 263 | { |
| 221 | struct net *net = dev_net(skb_dst(skb)->dev); | 264 | struct net *net = dev_net(skb_dst(skb)->dev); |
| 222 | struct netns_ipvs *ipvs = net_ipvs(net); | ||
| 223 | struct ip_vs_dest_dst *dest_dst; | 265 | struct ip_vs_dest_dst *dest_dst; |
| 224 | struct rtable *rt; /* Route to the other host */ | 266 | struct rtable *rt; /* Route to the other host */ |
| 225 | struct iphdr *iph; | ||
| 226 | __be16 df; | ||
| 227 | int mtu; | 267 | int mtu; |
| 228 | int local, noref = 1; | 268 | int local, noref = 1; |
| 229 | 269 | ||
| @@ -279,7 +319,6 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, | |||
| 279 | " daddr=%pI4\n", &dest->addr.ip); | 319 | " daddr=%pI4\n", &dest->addr.ip); |
| 280 | goto err_put; | 320 | goto err_put; |
| 281 | } | 321 | } |
| 282 | iph = ip_hdr(skb); | ||
| 283 | 322 | ||
| 284 | if (unlikely(local)) { | 323 | if (unlikely(local)) { |
| 285 | /* skb to local stack, preserve old route */ | 324 | /* skb to local stack, preserve old route */ |
| @@ -290,7 +329,6 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, | |||
| 290 | 329 | ||
| 291 | if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) { | 330 | if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) { |
| 292 | mtu = dst_mtu(&rt->dst); | 331 | mtu = dst_mtu(&rt->dst); |
| 293 | df = iph->frag_off & htons(IP_DF); | ||
| 294 | } else { | 332 | } else { |
| 295 | mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr); | 333 | mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr); |
| 296 | if (mtu < 68) { | 334 | if (mtu < 68) { |
| @@ -298,16 +336,10 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, | |||
| 298 | goto err_put; | 336 | goto err_put; |
| 299 | } | 337 | } |
| 300 | maybe_update_pmtu(skb_af, skb, mtu); | 338 | maybe_update_pmtu(skb_af, skb, mtu); |
| 301 | /* MTU check allowed? */ | ||
| 302 | df = sysctl_pmtu_disc(ipvs) ? iph->frag_off & htons(IP_DF) : 0; | ||
| 303 | } | 339 | } |
| 304 | 340 | ||
| 305 | /* MTU checking */ | 341 | if (!ensure_mtu_is_adequate(skb_af, rt_mode, ipvsh, skb, mtu)) |
| 306 | if (unlikely(df && skb->len > mtu && !skb_is_gso(skb))) { | ||
| 307 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); | ||
| 308 | IP_VS_DBG(1, "frag needed for %pI4\n", &iph->saddr); | ||
| 309 | goto err_put; | 342 | goto err_put; |
| 310 | } | ||
| 311 | 343 | ||
| 312 | skb_dst_drop(skb); | 344 | skb_dst_drop(skb); |
| 313 | if (noref) { | 345 | if (noref) { |
| @@ -450,15 +482,8 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, | |||
| 450 | maybe_update_pmtu(skb_af, skb, mtu); | 482 | maybe_update_pmtu(skb_af, skb, mtu); |
| 451 | } | 483 | } |
| 452 | 484 | ||
| 453 | if (unlikely(__mtu_check_toobig_v6(skb, mtu))) { | 485 | if (!ensure_mtu_is_adequate(skb_af, rt_mode, ipvsh, skb, mtu)) |
| 454 | if (!skb->dev) | ||
| 455 | skb->dev = net->loopback_dev; | ||
| 456 | /* only send ICMP too big on first fragment */ | ||
| 457 | if (!ipvsh->fragoffs) | ||
| 458 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
| 459 | IP_VS_DBG(1, "frag needed for %pI6c\n", &ipv6_hdr(skb)->saddr); | ||
| 460 | goto err_put; | 486 | goto err_put; |
| 461 | } | ||
| 462 | 487 | ||
| 463 | skb_dst_drop(skb); | 488 | skb_dst_drop(skb); |
| 464 | if (noref) { | 489 | if (noref) { |
| @@ -565,7 +590,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 565 | 590 | ||
| 566 | rcu_read_lock(); | 591 | rcu_read_lock(); |
| 567 | if (__ip_vs_get_out_rt(cp->af, skb, NULL, iph->daddr, | 592 | if (__ip_vs_get_out_rt(cp->af, skb, NULL, iph->daddr, |
| 568 | IP_VS_RT_MODE_NON_LOCAL, NULL) < 0) | 593 | IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) |
| 569 | goto tx_error; | 594 | goto tx_error; |
| 570 | 595 | ||
| 571 | ip_send_check(iph); | 596 | ip_send_check(iph); |
| @@ -644,7 +669,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 644 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, | 669 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, |
| 645 | IP_VS_RT_MODE_LOCAL | | 670 | IP_VS_RT_MODE_LOCAL | |
| 646 | IP_VS_RT_MODE_NON_LOCAL | | 671 | IP_VS_RT_MODE_NON_LOCAL | |
| 647 | IP_VS_RT_MODE_RDR, NULL); | 672 | IP_VS_RT_MODE_RDR, NULL, ipvsh); |
| 648 | if (local < 0) | 673 | if (local < 0) |
| 649 | goto tx_error; | 674 | goto tx_error; |
| 650 | rt = skb_rtable(skb); | 675 | rt = skb_rtable(skb); |
| @@ -841,7 +866,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 841 | IP_VS_RT_MODE_LOCAL | | 866 | IP_VS_RT_MODE_LOCAL | |
| 842 | IP_VS_RT_MODE_NON_LOCAL | | 867 | IP_VS_RT_MODE_NON_LOCAL | |
| 843 | IP_VS_RT_MODE_CONNECT | | 868 | IP_VS_RT_MODE_CONNECT | |
| 844 | IP_VS_RT_MODE_TUNNEL, &saddr); | 869 | IP_VS_RT_MODE_TUNNEL, &saddr, ipvsh); |
| 845 | if (local < 0) | 870 | if (local < 0) |
| 846 | goto tx_error; | 871 | goto tx_error; |
| 847 | if (local) { | 872 | if (local) { |
| @@ -1032,7 +1057,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 1032 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, | 1057 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, |
| 1033 | IP_VS_RT_MODE_LOCAL | | 1058 | IP_VS_RT_MODE_LOCAL | |
| 1034 | IP_VS_RT_MODE_NON_LOCAL | | 1059 | IP_VS_RT_MODE_NON_LOCAL | |
| 1035 | IP_VS_RT_MODE_KNOWN_NH, NULL); | 1060 | IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh); |
| 1036 | if (local < 0) | 1061 | if (local < 0) |
| 1037 | goto tx_error; | 1062 | goto tx_error; |
| 1038 | if (local) { | 1063 | if (local) { |
| @@ -1137,7 +1162,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 1137 | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; | 1162 | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; |
| 1138 | rcu_read_lock(); | 1163 | rcu_read_lock(); |
| 1139 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, | 1164 | local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, |
| 1140 | NULL); | 1165 | NULL, iph); |
| 1141 | if (local < 0) | 1166 | if (local < 0) |
| 1142 | goto tx_error; | 1167 | goto tx_error; |
| 1143 | rt = skb_rtable(skb); | 1168 | rt = skb_rtable(skb); |
