diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
| -rw-r--r-- | net/ipv6/ip6_output.c | 43 |
1 files changed, 34 insertions, 9 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7fde1f265c90..bc09cb97b840 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
| @@ -886,22 +886,45 @@ static int ip6_dst_lookup_tail(struct sock *sk, | |||
| 886 | #endif | 886 | #endif |
| 887 | int err; | 887 | int err; |
| 888 | 888 | ||
| 889 | if (!*dst) | 889 | /* The correct way to handle this would be to do |
| 890 | *dst = ip6_route_output(net, sk, fl6); | 890 | * ip6_route_get_saddr, and then ip6_route_output; however, |
| 891 | 891 | * the route-specific preferred source forces the | |
| 892 | err = (*dst)->error; | 892 | * ip6_route_output call _before_ ip6_route_get_saddr. |
| 893 | if (err) | 893 | * |
| 894 | goto out_err_release; | 894 | * In source specific routing (no src=any default route), |
| 895 | * ip6_route_output will fail given src=any saddr, though, so | ||
| 896 | * that's why we try it again later. | ||
| 897 | */ | ||
| 898 | if (ipv6_addr_any(&fl6->saddr) && (!*dst || !(*dst)->error)) { | ||
| 899 | struct rt6_info *rt; | ||
| 900 | bool had_dst = *dst != NULL; | ||
| 895 | 901 | ||
| 896 | if (ipv6_addr_any(&fl6->saddr)) { | 902 | if (!had_dst) |
| 897 | struct rt6_info *rt = (struct rt6_info *) *dst; | 903 | *dst = ip6_route_output(net, sk, fl6); |
| 904 | rt = (*dst)->error ? NULL : (struct rt6_info *)*dst; | ||
| 898 | err = ip6_route_get_saddr(net, rt, &fl6->daddr, | 905 | err = ip6_route_get_saddr(net, rt, &fl6->daddr, |
| 899 | sk ? inet6_sk(sk)->srcprefs : 0, | 906 | sk ? inet6_sk(sk)->srcprefs : 0, |
| 900 | &fl6->saddr); | 907 | &fl6->saddr); |
| 901 | if (err) | 908 | if (err) |
| 902 | goto out_err_release; | 909 | goto out_err_release; |
| 910 | |||
| 911 | /* If we had an erroneous initial result, pretend it | ||
| 912 | * never existed and let the SA-enabled version take | ||
| 913 | * over. | ||
| 914 | */ | ||
| 915 | if (!had_dst && (*dst)->error) { | ||
| 916 | dst_release(*dst); | ||
| 917 | *dst = NULL; | ||
| 918 | } | ||
| 903 | } | 919 | } |
| 904 | 920 | ||
| 921 | if (!*dst) | ||
| 922 | *dst = ip6_route_output(net, sk, fl6); | ||
| 923 | |||
| 924 | err = (*dst)->error; | ||
| 925 | if (err) | ||
| 926 | goto out_err_release; | ||
| 927 | |||
| 905 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD | 928 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD |
| 906 | /* | 929 | /* |
| 907 | * Here if the dst entry we've looked up | 930 | * Here if the dst entry we've looked up |
| @@ -1277,8 +1300,10 @@ emsgsize: | |||
| 1277 | 1300 | ||
| 1278 | /* If this is the first and only packet and device | 1301 | /* If this is the first and only packet and device |
| 1279 | * supports checksum offloading, let's use it. | 1302 | * supports checksum offloading, let's use it. |
| 1303 | * Use transhdrlen, same as IPv4, because partial | ||
| 1304 | * sums only work when transhdrlen is set. | ||
| 1280 | */ | 1305 | */ |
| 1281 | if (!skb && sk->sk_protocol == IPPROTO_UDP && | 1306 | if (transhdrlen && sk->sk_protocol == IPPROTO_UDP && |
| 1282 | length + fragheaderlen < mtu && | 1307 | length + fragheaderlen < mtu && |
| 1283 | rt->dst.dev->features & NETIF_F_V6_CSUM && | 1308 | rt->dst.dev->features & NETIF_F_V6_CSUM && |
| 1284 | !exthdrlen) | 1309 | !exthdrlen) |
