diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 67 |
1 files changed, 53 insertions, 14 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9d4b165837d..55a35c1dede 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -100,6 +100,7 @@ static int ip6_finish_output2(struct sk_buff *skb) | |||
100 | { | 100 | { |
101 | struct dst_entry *dst = skb_dst(skb); | 101 | struct dst_entry *dst = skb_dst(skb); |
102 | struct net_device *dev = dst->dev; | 102 | struct net_device *dev = dst->dev; |
103 | struct neighbour *neigh; | ||
103 | 104 | ||
104 | skb->protocol = htons(ETH_P_IPV6); | 105 | skb->protocol = htons(ETH_P_IPV6); |
105 | skb->dev = dev; | 106 | skb->dev = dev; |
@@ -134,11 +135,15 @@ static int ip6_finish_output2(struct sk_buff *skb) | |||
134 | skb->len); | 135 | skb->len); |
135 | } | 136 | } |
136 | 137 | ||
137 | if (dst->hh) | 138 | rcu_read_lock(); |
138 | return neigh_hh_output(dst->hh, skb); | 139 | neigh = dst_get_neighbour(dst); |
139 | else if (dst->neighbour) | 140 | if (neigh) { |
140 | return dst->neighbour->output(skb); | 141 | int res = neigh_output(neigh, skb); |
141 | 142 | ||
143 | rcu_read_unlock(); | ||
144 | return res; | ||
145 | } | ||
146 | rcu_read_unlock(); | ||
142 | IP6_INC_STATS_BH(dev_net(dst->dev), | 147 | IP6_INC_STATS_BH(dev_net(dst->dev), |
143 | ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); | 148 | ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); |
144 | kfree_skb(skb); | 149 | kfree_skb(skb); |
@@ -385,6 +390,7 @@ int ip6_forward(struct sk_buff *skb) | |||
385 | struct ipv6hdr *hdr = ipv6_hdr(skb); | 390 | struct ipv6hdr *hdr = ipv6_hdr(skb); |
386 | struct inet6_skb_parm *opt = IP6CB(skb); | 391 | struct inet6_skb_parm *opt = IP6CB(skb); |
387 | struct net *net = dev_net(dst->dev); | 392 | struct net *net = dev_net(dst->dev); |
393 | struct neighbour *n; | ||
388 | u32 mtu; | 394 | u32 mtu; |
389 | 395 | ||
390 | if (net->ipv6.devconf_all->forwarding == 0) | 396 | if (net->ipv6.devconf_all->forwarding == 0) |
@@ -459,11 +465,10 @@ int ip6_forward(struct sk_buff *skb) | |||
459 | send redirects to source routed frames. | 465 | send redirects to source routed frames. |
460 | We don't send redirects to frames decapsulated from IPsec. | 466 | We don't send redirects to frames decapsulated from IPsec. |
461 | */ | 467 | */ |
462 | if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 && | 468 | n = dst_get_neighbour(dst); |
463 | !skb_sec_path(skb)) { | 469 | if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) { |
464 | struct in6_addr *target = NULL; | 470 | struct in6_addr *target = NULL; |
465 | struct rt6_info *rt; | 471 | struct rt6_info *rt; |
466 | struct neighbour *n = dst->neighbour; | ||
467 | 472 | ||
468 | /* | 473 | /* |
469 | * incoming and outgoing devices are the same | 474 | * incoming and outgoing devices are the same |
@@ -596,6 +601,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | |||
596 | return offset; | 601 | return offset; |
597 | } | 602 | } |
598 | 603 | ||
604 | void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) | ||
605 | { | ||
606 | static atomic_t ipv6_fragmentation_id; | ||
607 | int old, new; | ||
608 | |||
609 | if (rt && !(rt->dst.flags & DST_NOPEER)) { | ||
610 | struct inet_peer *peer; | ||
611 | |||
612 | if (!rt->rt6i_peer) | ||
613 | rt6_bind_peer(rt, 1); | ||
614 | peer = rt->rt6i_peer; | ||
615 | if (peer) { | ||
616 | fhdr->identification = htonl(inet_getid(peer, 0)); | ||
617 | return; | ||
618 | } | ||
619 | } | ||
620 | do { | ||
621 | old = atomic_read(&ipv6_fragmentation_id); | ||
622 | new = old + 1; | ||
623 | if (!new) | ||
624 | new = 1; | ||
625 | } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); | ||
626 | fhdr->identification = htonl(new); | ||
627 | } | ||
628 | |||
599 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 629 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) |
600 | { | 630 | { |
601 | struct sk_buff *frag; | 631 | struct sk_buff *frag; |
@@ -680,7 +710,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
680 | skb_reset_network_header(skb); | 710 | skb_reset_network_header(skb); |
681 | memcpy(skb_network_header(skb), tmp_hdr, hlen); | 711 | memcpy(skb_network_header(skb), tmp_hdr, hlen); |
682 | 712 | ||
683 | ipv6_select_ident(fh); | 713 | ipv6_select_ident(fh, rt); |
684 | fh->nexthdr = nexthdr; | 714 | fh->nexthdr = nexthdr; |
685 | fh->reserved = 0; | 715 | fh->reserved = 0; |
686 | fh->frag_off = htons(IP6_MF); | 716 | fh->frag_off = htons(IP6_MF); |
@@ -826,7 +856,7 @@ slow_path: | |||
826 | fh->nexthdr = nexthdr; | 856 | fh->nexthdr = nexthdr; |
827 | fh->reserved = 0; | 857 | fh->reserved = 0; |
828 | if (!frag_id) { | 858 | if (!frag_id) { |
829 | ipv6_select_ident(fh); | 859 | ipv6_select_ident(fh, rt); |
830 | frag_id = fh->identification; | 860 | frag_id = fh->identification; |
831 | } else | 861 | } else |
832 | fh->identification = frag_id; | 862 | fh->identification = frag_id; |
@@ -920,8 +950,11 @@ out: | |||
920 | static int ip6_dst_lookup_tail(struct sock *sk, | 950 | static int ip6_dst_lookup_tail(struct sock *sk, |
921 | struct dst_entry **dst, struct flowi6 *fl6) | 951 | struct dst_entry **dst, struct flowi6 *fl6) |
922 | { | 952 | { |
923 | int err; | ||
924 | struct net *net = sock_net(sk); | 953 | struct net *net = sock_net(sk); |
954 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD | ||
955 | struct neighbour *n; | ||
956 | #endif | ||
957 | int err; | ||
925 | 958 | ||
926 | if (*dst == NULL) | 959 | if (*dst == NULL) |
927 | *dst = ip6_route_output(net, sk, fl6); | 960 | *dst = ip6_route_output(net, sk, fl6); |
@@ -947,11 +980,14 @@ static int ip6_dst_lookup_tail(struct sock *sk, | |||
947 | * dst entry and replace it instead with the | 980 | * dst entry and replace it instead with the |
948 | * dst entry of the nexthop router | 981 | * dst entry of the nexthop router |
949 | */ | 982 | */ |
950 | if ((*dst)->neighbour && !((*dst)->neighbour->nud_state & NUD_VALID)) { | 983 | rcu_read_lock(); |
984 | n = dst_get_neighbour(*dst); | ||
985 | if (n && !(n->nud_state & NUD_VALID)) { | ||
951 | struct inet6_ifaddr *ifp; | 986 | struct inet6_ifaddr *ifp; |
952 | struct flowi6 fl_gw6; | 987 | struct flowi6 fl_gw6; |
953 | int redirect; | 988 | int redirect; |
954 | 989 | ||
990 | rcu_read_unlock(); | ||
955 | ifp = ipv6_get_ifaddr(net, &fl6->saddr, | 991 | ifp = ipv6_get_ifaddr(net, &fl6->saddr, |
956 | (*dst)->dev, 1); | 992 | (*dst)->dev, 1); |
957 | 993 | ||
@@ -971,6 +1007,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, | |||
971 | if ((err = (*dst)->error)) | 1007 | if ((err = (*dst)->error)) |
972 | goto out_err_release; | 1008 | goto out_err_release; |
973 | } | 1009 | } |
1010 | } else { | ||
1011 | rcu_read_unlock(); | ||
974 | } | 1012 | } |
975 | #endif | 1013 | #endif |
976 | 1014 | ||
@@ -1072,7 +1110,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
1072 | int getfrag(void *from, char *to, int offset, int len, | 1110 | int getfrag(void *from, char *to, int offset, int len, |
1073 | int odd, struct sk_buff *skb), | 1111 | int odd, struct sk_buff *skb), |
1074 | void *from, int length, int hh_len, int fragheaderlen, | 1112 | void *from, int length, int hh_len, int fragheaderlen, |
1075 | int transhdrlen, int mtu,unsigned int flags) | 1113 | int transhdrlen, int mtu,unsigned int flags, |
1114 | struct rt6_info *rt) | ||
1076 | 1115 | ||
1077 | { | 1116 | { |
1078 | struct sk_buff *skb; | 1117 | struct sk_buff *skb; |
@@ -1116,7 +1155,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
1116 | skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - | 1155 | skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - |
1117 | sizeof(struct frag_hdr)) & ~7; | 1156 | sizeof(struct frag_hdr)) & ~7; |
1118 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; | 1157 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
1119 | ipv6_select_ident(&fhdr); | 1158 | ipv6_select_ident(&fhdr, rt); |
1120 | skb_shinfo(skb)->ip6_frag_id = fhdr.identification; | 1159 | skb_shinfo(skb)->ip6_frag_id = fhdr.identification; |
1121 | __skb_queue_tail(&sk->sk_write_queue, skb); | 1160 | __skb_queue_tail(&sk->sk_write_queue, skb); |
1122 | 1161 | ||
@@ -1282,7 +1321,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
1282 | 1321 | ||
1283 | err = ip6_ufo_append_data(sk, getfrag, from, length, | 1322 | err = ip6_ufo_append_data(sk, getfrag, from, length, |
1284 | hh_len, fragheaderlen, | 1323 | hh_len, fragheaderlen, |
1285 | transhdrlen, mtu, flags); | 1324 | transhdrlen, mtu, flags, rt); |
1286 | if (err) | 1325 | if (err) |
1287 | goto error; | 1326 | goto error; |
1288 | return 0; | 1327 | return 0; |