diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 374 |
1 files changed, 241 insertions, 133 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index ce69a12ae48c..d33df4cbd872 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -537,20 +537,6 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) | |||
537 | skb_copy_secmark(to, from); | 537 | skb_copy_secmark(to, from); |
538 | } | 538 | } |
539 | 539 | ||
540 | static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) | ||
541 | { | ||
542 | static u32 ip6_idents_hashrnd __read_mostly; | ||
543 | u32 hash, id; | ||
544 | |||
545 | net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); | ||
546 | |||
547 | hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); | ||
548 | hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash); | ||
549 | |||
550 | id = ip_idents_reserve(hash, 1); | ||
551 | fhdr->identification = htonl(id); | ||
552 | } | ||
553 | |||
554 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 540 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) |
555 | { | 541 | { |
556 | struct sk_buff *frag; | 542 | struct sk_buff *frag; |
@@ -1041,6 +1027,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, | |||
1041 | EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); | 1027 | EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); |
1042 | 1028 | ||
1043 | static inline int ip6_ufo_append_data(struct sock *sk, | 1029 | static inline int ip6_ufo_append_data(struct sock *sk, |
1030 | struct sk_buff_head *queue, | ||
1044 | int getfrag(void *from, char *to, int offset, int len, | 1031 | int getfrag(void *from, char *to, int offset, int len, |
1045 | int odd, struct sk_buff *skb), | 1032 | int odd, struct sk_buff *skb), |
1046 | void *from, int length, int hh_len, int fragheaderlen, | 1033 | void *from, int length, int hh_len, int fragheaderlen, |
@@ -1056,7 +1043,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
1056 | * device, so create one single skb packet containing complete | 1043 | * device, so create one single skb packet containing complete |
1057 | * udp datagram | 1044 | * udp datagram |
1058 | */ | 1045 | */ |
1059 | skb = skb_peek_tail(&sk->sk_write_queue); | 1046 | skb = skb_peek_tail(queue); |
1060 | if (skb == NULL) { | 1047 | if (skb == NULL) { |
1061 | skb = sock_alloc_send_skb(sk, | 1048 | skb = sock_alloc_send_skb(sk, |
1062 | hh_len + fragheaderlen + transhdrlen + 20, | 1049 | hh_len + fragheaderlen + transhdrlen + 20, |
@@ -1079,7 +1066,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
1079 | skb->protocol = htons(ETH_P_IPV6); | 1066 | skb->protocol = htons(ETH_P_IPV6); |
1080 | skb->csum = 0; | 1067 | skb->csum = 0; |
1081 | 1068 | ||
1082 | __skb_queue_tail(&sk->sk_write_queue, skb); | 1069 | __skb_queue_tail(queue, skb); |
1083 | } else if (skb_is_gso(skb)) { | 1070 | } else if (skb_is_gso(skb)) { |
1084 | goto append; | 1071 | goto append; |
1085 | } | 1072 | } |
@@ -1135,99 +1122,106 @@ static void ip6_append_data_mtu(unsigned int *mtu, | |||
1135 | } | 1122 | } |
1136 | } | 1123 | } |
1137 | 1124 | ||
1138 | int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | 1125 | static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, |
1139 | int offset, int len, int odd, struct sk_buff *skb), | 1126 | struct inet6_cork *v6_cork, |
1140 | void *from, int length, int transhdrlen, | 1127 | int hlimit, int tclass, struct ipv6_txoptions *opt, |
1141 | int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, | 1128 | struct rt6_info *rt, struct flowi6 *fl6) |
1142 | struct rt6_info *rt, unsigned int flags, int dontfrag) | ||
1143 | { | 1129 | { |
1144 | struct inet_sock *inet = inet_sk(sk); | ||
1145 | struct ipv6_pinfo *np = inet6_sk(sk); | 1130 | struct ipv6_pinfo *np = inet6_sk(sk); |
1146 | struct inet_cork *cork; | 1131 | unsigned int mtu; |
1132 | |||
1133 | /* | ||
1134 | * setup for corking | ||
1135 | */ | ||
1136 | if (opt) { | ||
1137 | if (WARN_ON(v6_cork->opt)) | ||
1138 | return -EINVAL; | ||
1139 | |||
1140 | v6_cork->opt = kzalloc(opt->tot_len, sk->sk_allocation); | ||
1141 | if (unlikely(v6_cork->opt == NULL)) | ||
1142 | return -ENOBUFS; | ||
1143 | |||
1144 | v6_cork->opt->tot_len = opt->tot_len; | ||
1145 | v6_cork->opt->opt_flen = opt->opt_flen; | ||
1146 | v6_cork->opt->opt_nflen = opt->opt_nflen; | ||
1147 | |||
1148 | v6_cork->opt->dst0opt = ip6_opt_dup(opt->dst0opt, | ||
1149 | sk->sk_allocation); | ||
1150 | if (opt->dst0opt && !v6_cork->opt->dst0opt) | ||
1151 | return -ENOBUFS; | ||
1152 | |||
1153 | v6_cork->opt->dst1opt = ip6_opt_dup(opt->dst1opt, | ||
1154 | sk->sk_allocation); | ||
1155 | if (opt->dst1opt && !v6_cork->opt->dst1opt) | ||
1156 | return -ENOBUFS; | ||
1157 | |||
1158 | v6_cork->opt->hopopt = ip6_opt_dup(opt->hopopt, | ||
1159 | sk->sk_allocation); | ||
1160 | if (opt->hopopt && !v6_cork->opt->hopopt) | ||
1161 | return -ENOBUFS; | ||
1162 | |||
1163 | v6_cork->opt->srcrt = ip6_rthdr_dup(opt->srcrt, | ||
1164 | sk->sk_allocation); | ||
1165 | if (opt->srcrt && !v6_cork->opt->srcrt) | ||
1166 | return -ENOBUFS; | ||
1167 | |||
1168 | /* need source address above miyazawa*/ | ||
1169 | } | ||
1170 | dst_hold(&rt->dst); | ||
1171 | cork->base.dst = &rt->dst; | ||
1172 | cork->fl.u.ip6 = *fl6; | ||
1173 | v6_cork->hop_limit = hlimit; | ||
1174 | v6_cork->tclass = tclass; | ||
1175 | if (rt->dst.flags & DST_XFRM_TUNNEL) | ||
1176 | mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? | ||
1177 | rt->dst.dev->mtu : dst_mtu(&rt->dst); | ||
1178 | else | ||
1179 | mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? | ||
1180 | rt->dst.dev->mtu : dst_mtu(rt->dst.path); | ||
1181 | if (np->frag_size < mtu) { | ||
1182 | if (np->frag_size) | ||
1183 | mtu = np->frag_size; | ||
1184 | } | ||
1185 | cork->base.fragsize = mtu; | ||
1186 | if (dst_allfrag(rt->dst.path)) | ||
1187 | cork->base.flags |= IPCORK_ALLFRAG; | ||
1188 | cork->base.length = 0; | ||
1189 | |||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | static int __ip6_append_data(struct sock *sk, | ||
1194 | struct flowi6 *fl6, | ||
1195 | struct sk_buff_head *queue, | ||
1196 | struct inet_cork *cork, | ||
1197 | struct inet6_cork *v6_cork, | ||
1198 | struct page_frag *pfrag, | ||
1199 | int getfrag(void *from, char *to, int offset, | ||
1200 | int len, int odd, struct sk_buff *skb), | ||
1201 | void *from, int length, int transhdrlen, | ||
1202 | unsigned int flags, int dontfrag) | ||
1203 | { | ||
1147 | struct sk_buff *skb, *skb_prev = NULL; | 1204 | struct sk_buff *skb, *skb_prev = NULL; |
1148 | unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu; | 1205 | unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu; |
1149 | int exthdrlen; | 1206 | int exthdrlen = 0; |
1150 | int dst_exthdrlen; | 1207 | int dst_exthdrlen = 0; |
1151 | int hh_len; | 1208 | int hh_len; |
1152 | int copy; | 1209 | int copy; |
1153 | int err; | 1210 | int err; |
1154 | int offset = 0; | 1211 | int offset = 0; |
1155 | __u8 tx_flags = 0; | 1212 | __u8 tx_flags = 0; |
1156 | u32 tskey = 0; | 1213 | u32 tskey = 0; |
1214 | struct rt6_info *rt = (struct rt6_info *)cork->dst; | ||
1215 | struct ipv6_txoptions *opt = v6_cork->opt; | ||
1216 | int csummode = CHECKSUM_NONE; | ||
1157 | 1217 | ||
1158 | if (flags&MSG_PROBE) | 1218 | skb = skb_peek_tail(queue); |
1159 | return 0; | 1219 | if (!skb) { |
1160 | cork = &inet->cork.base; | 1220 | exthdrlen = opt ? opt->opt_flen : 0; |
1161 | if (skb_queue_empty(&sk->sk_write_queue)) { | ||
1162 | /* | ||
1163 | * setup for corking | ||
1164 | */ | ||
1165 | if (opt) { | ||
1166 | if (WARN_ON(np->cork.opt)) | ||
1167 | return -EINVAL; | ||
1168 | |||
1169 | np->cork.opt = kzalloc(opt->tot_len, sk->sk_allocation); | ||
1170 | if (unlikely(np->cork.opt == NULL)) | ||
1171 | return -ENOBUFS; | ||
1172 | |||
1173 | np->cork.opt->tot_len = opt->tot_len; | ||
1174 | np->cork.opt->opt_flen = opt->opt_flen; | ||
1175 | np->cork.opt->opt_nflen = opt->opt_nflen; | ||
1176 | |||
1177 | np->cork.opt->dst0opt = ip6_opt_dup(opt->dst0opt, | ||
1178 | sk->sk_allocation); | ||
1179 | if (opt->dst0opt && !np->cork.opt->dst0opt) | ||
1180 | return -ENOBUFS; | ||
1181 | |||
1182 | np->cork.opt->dst1opt = ip6_opt_dup(opt->dst1opt, | ||
1183 | sk->sk_allocation); | ||
1184 | if (opt->dst1opt && !np->cork.opt->dst1opt) | ||
1185 | return -ENOBUFS; | ||
1186 | |||
1187 | np->cork.opt->hopopt = ip6_opt_dup(opt->hopopt, | ||
1188 | sk->sk_allocation); | ||
1189 | if (opt->hopopt && !np->cork.opt->hopopt) | ||
1190 | return -ENOBUFS; | ||
1191 | |||
1192 | np->cork.opt->srcrt = ip6_rthdr_dup(opt->srcrt, | ||
1193 | sk->sk_allocation); | ||
1194 | if (opt->srcrt && !np->cork.opt->srcrt) | ||
1195 | return -ENOBUFS; | ||
1196 | |||
1197 | /* need source address above miyazawa*/ | ||
1198 | } | ||
1199 | dst_hold(&rt->dst); | ||
1200 | cork->dst = &rt->dst; | ||
1201 | inet->cork.fl.u.ip6 = *fl6; | ||
1202 | np->cork.hop_limit = hlimit; | ||
1203 | np->cork.tclass = tclass; | ||
1204 | if (rt->dst.flags & DST_XFRM_TUNNEL) | ||
1205 | mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? | ||
1206 | rt->dst.dev->mtu : dst_mtu(&rt->dst); | ||
1207 | else | ||
1208 | mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? | ||
1209 | rt->dst.dev->mtu : dst_mtu(rt->dst.path); | ||
1210 | if (np->frag_size < mtu) { | ||
1211 | if (np->frag_size) | ||
1212 | mtu = np->frag_size; | ||
1213 | } | ||
1214 | cork->fragsize = mtu; | ||
1215 | if (dst_allfrag(rt->dst.path)) | ||
1216 | cork->flags |= IPCORK_ALLFRAG; | ||
1217 | cork->length = 0; | ||
1218 | exthdrlen = (opt ? opt->opt_flen : 0); | ||
1219 | length += exthdrlen; | ||
1220 | transhdrlen += exthdrlen; | ||
1221 | dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len; | 1221 | dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len; |
1222 | } else { | ||
1223 | rt = (struct rt6_info *)cork->dst; | ||
1224 | fl6 = &inet->cork.fl.u.ip6; | ||
1225 | opt = np->cork.opt; | ||
1226 | transhdrlen = 0; | ||
1227 | exthdrlen = 0; | ||
1228 | dst_exthdrlen = 0; | ||
1229 | mtu = cork->fragsize; | ||
1230 | } | 1222 | } |
1223 | |||
1224 | mtu = cork->fragsize; | ||
1231 | orig_mtu = mtu; | 1225 | orig_mtu = mtu; |
1232 | 1226 | ||
1233 | hh_len = LL_RESERVED_SPACE(rt->dst.dev); | 1227 | hh_len = LL_RESERVED_SPACE(rt->dst.dev); |
@@ -1276,6 +1270,14 @@ emsgsize: | |||
1276 | tskey = sk->sk_tskey++; | 1270 | tskey = sk->sk_tskey++; |
1277 | } | 1271 | } |
1278 | 1272 | ||
1273 | /* If this is the first and only packet and device | ||
1274 | * supports checksum offloading, let's use it. | ||
1275 | */ | ||
1276 | if (!skb && | ||
1277 | length + fragheaderlen < mtu && | ||
1278 | rt->dst.dev->features & NETIF_F_V6_CSUM && | ||
1279 | !exthdrlen) | ||
1280 | csummode = CHECKSUM_PARTIAL; | ||
1279 | /* | 1281 | /* |
1280 | * Let's try using as much space as possible. | 1282 | * Let's try using as much space as possible. |
1281 | * Use MTU if total length of the message fits into the MTU. | 1283 | * Use MTU if total length of the message fits into the MTU. |
@@ -1292,13 +1294,12 @@ emsgsize: | |||
1292 | * --yoshfuji | 1294 | * --yoshfuji |
1293 | */ | 1295 | */ |
1294 | 1296 | ||
1295 | skb = skb_peek_tail(&sk->sk_write_queue); | ||
1296 | cork->length += length; | 1297 | cork->length += length; |
1297 | if (((length > mtu) || | 1298 | if (((length > mtu) || |
1298 | (skb && skb_is_gso(skb))) && | 1299 | (skb && skb_is_gso(skb))) && |
1299 | (sk->sk_protocol == IPPROTO_UDP) && | 1300 | (sk->sk_protocol == IPPROTO_UDP) && |
1300 | (rt->dst.dev->features & NETIF_F_UFO)) { | 1301 | (rt->dst.dev->features & NETIF_F_UFO)) { |
1301 | err = ip6_ufo_append_data(sk, getfrag, from, length, | 1302 | err = ip6_ufo_append_data(sk, queue, getfrag, from, length, |
1302 | hh_len, fragheaderlen, | 1303 | hh_len, fragheaderlen, |
1303 | transhdrlen, mtu, flags, rt); | 1304 | transhdrlen, mtu, flags, rt); |
1304 | if (err) | 1305 | if (err) |
@@ -1389,7 +1390,7 @@ alloc_new_skb: | |||
1389 | * Fill in the control structures | 1390 | * Fill in the control structures |
1390 | */ | 1391 | */ |
1391 | skb->protocol = htons(ETH_P_IPV6); | 1392 | skb->protocol = htons(ETH_P_IPV6); |
1392 | skb->ip_summed = CHECKSUM_NONE; | 1393 | skb->ip_summed = csummode; |
1393 | skb->csum = 0; | 1394 | skb->csum = 0; |
1394 | /* reserve for fragmentation and ipsec header */ | 1395 | /* reserve for fragmentation and ipsec header */ |
1395 | skb_reserve(skb, hh_len + sizeof(struct frag_hdr) + | 1396 | skb_reserve(skb, hh_len + sizeof(struct frag_hdr) + |
@@ -1439,7 +1440,7 @@ alloc_new_skb: | |||
1439 | /* | 1440 | /* |
1440 | * Put the packet on the pending queue | 1441 | * Put the packet on the pending queue |
1441 | */ | 1442 | */ |
1442 | __skb_queue_tail(&sk->sk_write_queue, skb); | 1443 | __skb_queue_tail(queue, skb); |
1443 | continue; | 1444 | continue; |
1444 | } | 1445 | } |
1445 | 1446 | ||
@@ -1458,7 +1459,6 @@ alloc_new_skb: | |||
1458 | } | 1459 | } |
1459 | } else { | 1460 | } else { |
1460 | int i = skb_shinfo(skb)->nr_frags; | 1461 | int i = skb_shinfo(skb)->nr_frags; |
1461 | struct page_frag *pfrag = sk_page_frag(sk); | ||
1462 | 1462 | ||
1463 | err = -ENOMEM; | 1463 | err = -ENOMEM; |
1464 | if (!sk_page_frag_refill(sk, pfrag)) | 1464 | if (!sk_page_frag_refill(sk, pfrag)) |
@@ -1501,43 +1501,81 @@ error: | |||
1501 | IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); | 1501 | IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); |
1502 | return err; | 1502 | return err; |
1503 | } | 1503 | } |
1504 | |||
1505 | int ip6_append_data(struct sock *sk, | ||
1506 | int getfrag(void *from, char *to, int offset, int len, | ||
1507 | int odd, struct sk_buff *skb), | ||
1508 | void *from, int length, int transhdrlen, int hlimit, | ||
1509 | int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, | ||
1510 | struct rt6_info *rt, unsigned int flags, int dontfrag) | ||
1511 | { | ||
1512 | struct inet_sock *inet = inet_sk(sk); | ||
1513 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
1514 | int exthdrlen; | ||
1515 | int err; | ||
1516 | |||
1517 | if (flags&MSG_PROBE) | ||
1518 | return 0; | ||
1519 | if (skb_queue_empty(&sk->sk_write_queue)) { | ||
1520 | /* | ||
1521 | * setup for corking | ||
1522 | */ | ||
1523 | err = ip6_setup_cork(sk, &inet->cork, &np->cork, hlimit, | ||
1524 | tclass, opt, rt, fl6); | ||
1525 | if (err) | ||
1526 | return err; | ||
1527 | |||
1528 | exthdrlen = (opt ? opt->opt_flen : 0); | ||
1529 | length += exthdrlen; | ||
1530 | transhdrlen += exthdrlen; | ||
1531 | } else { | ||
1532 | fl6 = &inet->cork.fl.u.ip6; | ||
1533 | transhdrlen = 0; | ||
1534 | } | ||
1535 | |||
1536 | return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base, | ||
1537 | &np->cork, sk_page_frag(sk), getfrag, | ||
1538 | from, length, transhdrlen, flags, dontfrag); | ||
1539 | } | ||
1504 | EXPORT_SYMBOL_GPL(ip6_append_data); | 1540 | EXPORT_SYMBOL_GPL(ip6_append_data); |
1505 | 1541 | ||
1506 | static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np) | 1542 | static void ip6_cork_release(struct inet_cork_full *cork, |
1543 | struct inet6_cork *v6_cork) | ||
1507 | { | 1544 | { |
1508 | if (np->cork.opt) { | 1545 | if (v6_cork->opt) { |
1509 | kfree(np->cork.opt->dst0opt); | 1546 | kfree(v6_cork->opt->dst0opt); |
1510 | kfree(np->cork.opt->dst1opt); | 1547 | kfree(v6_cork->opt->dst1opt); |
1511 | kfree(np->cork.opt->hopopt); | 1548 | kfree(v6_cork->opt->hopopt); |
1512 | kfree(np->cork.opt->srcrt); | 1549 | kfree(v6_cork->opt->srcrt); |
1513 | kfree(np->cork.opt); | 1550 | kfree(v6_cork->opt); |
1514 | np->cork.opt = NULL; | 1551 | v6_cork->opt = NULL; |
1515 | } | 1552 | } |
1516 | 1553 | ||
1517 | if (inet->cork.base.dst) { | 1554 | if (cork->base.dst) { |
1518 | dst_release(inet->cork.base.dst); | 1555 | dst_release(cork->base.dst); |
1519 | inet->cork.base.dst = NULL; | 1556 | cork->base.dst = NULL; |
1520 | inet->cork.base.flags &= ~IPCORK_ALLFRAG; | 1557 | cork->base.flags &= ~IPCORK_ALLFRAG; |
1521 | } | 1558 | } |
1522 | memset(&inet->cork.fl, 0, sizeof(inet->cork.fl)); | 1559 | memset(&cork->fl, 0, sizeof(cork->fl)); |
1523 | } | 1560 | } |
1524 | 1561 | ||
1525 | int ip6_push_pending_frames(struct sock *sk) | 1562 | struct sk_buff *__ip6_make_skb(struct sock *sk, |
1563 | struct sk_buff_head *queue, | ||
1564 | struct inet_cork_full *cork, | ||
1565 | struct inet6_cork *v6_cork) | ||
1526 | { | 1566 | { |
1527 | struct sk_buff *skb, *tmp_skb; | 1567 | struct sk_buff *skb, *tmp_skb; |
1528 | struct sk_buff **tail_skb; | 1568 | struct sk_buff **tail_skb; |
1529 | struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; | 1569 | struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; |
1530 | struct inet_sock *inet = inet_sk(sk); | ||
1531 | struct ipv6_pinfo *np = inet6_sk(sk); | 1570 | struct ipv6_pinfo *np = inet6_sk(sk); |
1532 | struct net *net = sock_net(sk); | 1571 | struct net *net = sock_net(sk); |
1533 | struct ipv6hdr *hdr; | 1572 | struct ipv6hdr *hdr; |
1534 | struct ipv6_txoptions *opt = np->cork.opt; | 1573 | struct ipv6_txoptions *opt = v6_cork->opt; |
1535 | struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst; | 1574 | struct rt6_info *rt = (struct rt6_info *)cork->base.dst; |
1536 | struct flowi6 *fl6 = &inet->cork.fl.u.ip6; | 1575 | struct flowi6 *fl6 = &cork->fl.u.ip6; |
1537 | unsigned char proto = fl6->flowi6_proto; | 1576 | unsigned char proto = fl6->flowi6_proto; |
1538 | int err = 0; | ||
1539 | 1577 | ||
1540 | skb = __skb_dequeue(&sk->sk_write_queue); | 1578 | skb = __skb_dequeue(queue); |
1541 | if (skb == NULL) | 1579 | if (skb == NULL) |
1542 | goto out; | 1580 | goto out; |
1543 | tail_skb = &(skb_shinfo(skb)->frag_list); | 1581 | tail_skb = &(skb_shinfo(skb)->frag_list); |
@@ -1545,7 +1583,7 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1545 | /* move skb->data to ip header from ext header */ | 1583 | /* move skb->data to ip header from ext header */ |
1546 | if (skb->data < skb_network_header(skb)) | 1584 | if (skb->data < skb_network_header(skb)) |
1547 | __skb_pull(skb, skb_network_offset(skb)); | 1585 | __skb_pull(skb, skb_network_offset(skb)); |
1548 | while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { | 1586 | while ((tmp_skb = __skb_dequeue(queue)) != NULL) { |
1549 | __skb_pull(tmp_skb, skb_network_header_len(skb)); | 1587 | __skb_pull(tmp_skb, skb_network_header_len(skb)); |
1550 | *tail_skb = tmp_skb; | 1588 | *tail_skb = tmp_skb; |
1551 | tail_skb = &(tmp_skb->next); | 1589 | tail_skb = &(tmp_skb->next); |
@@ -1570,10 +1608,10 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1570 | skb_reset_network_header(skb); | 1608 | skb_reset_network_header(skb); |
1571 | hdr = ipv6_hdr(skb); | 1609 | hdr = ipv6_hdr(skb); |
1572 | 1610 | ||
1573 | ip6_flow_hdr(hdr, np->cork.tclass, | 1611 | ip6_flow_hdr(hdr, v6_cork->tclass, |
1574 | ip6_make_flowlabel(net, skb, fl6->flowlabel, | 1612 | ip6_make_flowlabel(net, skb, fl6->flowlabel, |
1575 | np->autoflowlabel)); | 1613 | np->autoflowlabel)); |
1576 | hdr->hop_limit = np->cork.hop_limit; | 1614 | hdr->hop_limit = v6_cork->hop_limit; |
1577 | hdr->nexthdr = proto; | 1615 | hdr->nexthdr = proto; |
1578 | hdr->saddr = fl6->saddr; | 1616 | hdr->saddr = fl6->saddr; |
1579 | hdr->daddr = *final_dst; | 1617 | hdr->daddr = *final_dst; |
@@ -1590,34 +1628,104 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1590 | ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); | 1628 | ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); |
1591 | } | 1629 | } |
1592 | 1630 | ||
1631 | ip6_cork_release(cork, v6_cork); | ||
1632 | out: | ||
1633 | return skb; | ||
1634 | } | ||
1635 | |||
1636 | int ip6_send_skb(struct sk_buff *skb) | ||
1637 | { | ||
1638 | struct net *net = sock_net(skb->sk); | ||
1639 | struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); | ||
1640 | int err; | ||
1641 | |||
1593 | err = ip6_local_out(skb); | 1642 | err = ip6_local_out(skb); |
1594 | if (err) { | 1643 | if (err) { |
1595 | if (err > 0) | 1644 | if (err > 0) |
1596 | err = net_xmit_errno(err); | 1645 | err = net_xmit_errno(err); |
1597 | if (err) | 1646 | if (err) |
1598 | goto error; | 1647 | IP6_INC_STATS(net, rt->rt6i_idev, |
1648 | IPSTATS_MIB_OUTDISCARDS); | ||
1599 | } | 1649 | } |
1600 | 1650 | ||
1601 | out: | ||
1602 | ip6_cork_release(inet, np); | ||
1603 | return err; | 1651 | return err; |
1604 | error: | 1652 | } |
1605 | IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); | 1653 | |
1606 | goto out; | 1654 | int ip6_push_pending_frames(struct sock *sk) |
1655 | { | ||
1656 | struct sk_buff *skb; | ||
1657 | |||
1658 | skb = ip6_finish_skb(sk); | ||
1659 | if (!skb) | ||
1660 | return 0; | ||
1661 | |||
1662 | return ip6_send_skb(skb); | ||
1607 | } | 1663 | } |
1608 | EXPORT_SYMBOL_GPL(ip6_push_pending_frames); | 1664 | EXPORT_SYMBOL_GPL(ip6_push_pending_frames); |
1609 | 1665 | ||
1610 | void ip6_flush_pending_frames(struct sock *sk) | 1666 | static void __ip6_flush_pending_frames(struct sock *sk, |
1667 | struct sk_buff_head *queue, | ||
1668 | struct inet_cork_full *cork, | ||
1669 | struct inet6_cork *v6_cork) | ||
1611 | { | 1670 | { |
1612 | struct sk_buff *skb; | 1671 | struct sk_buff *skb; |
1613 | 1672 | ||
1614 | while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) { | 1673 | while ((skb = __skb_dequeue_tail(queue)) != NULL) { |
1615 | if (skb_dst(skb)) | 1674 | if (skb_dst(skb)) |
1616 | IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)), | 1675 | IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)), |
1617 | IPSTATS_MIB_OUTDISCARDS); | 1676 | IPSTATS_MIB_OUTDISCARDS); |
1618 | kfree_skb(skb); | 1677 | kfree_skb(skb); |
1619 | } | 1678 | } |
1620 | 1679 | ||
1621 | ip6_cork_release(inet_sk(sk), inet6_sk(sk)); | 1680 | ip6_cork_release(cork, v6_cork); |
1681 | } | ||
1682 | |||
1683 | void ip6_flush_pending_frames(struct sock *sk) | ||
1684 | { | ||
1685 | __ip6_flush_pending_frames(sk, &sk->sk_write_queue, | ||
1686 | &inet_sk(sk)->cork, &inet6_sk(sk)->cork); | ||
1622 | } | 1687 | } |
1623 | EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); | 1688 | EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); |
1689 | |||
1690 | struct sk_buff *ip6_make_skb(struct sock *sk, | ||
1691 | int getfrag(void *from, char *to, int offset, | ||
1692 | int len, int odd, struct sk_buff *skb), | ||
1693 | void *from, int length, int transhdrlen, | ||
1694 | int hlimit, int tclass, | ||
1695 | struct ipv6_txoptions *opt, struct flowi6 *fl6, | ||
1696 | struct rt6_info *rt, unsigned int flags, | ||
1697 | int dontfrag) | ||
1698 | { | ||
1699 | struct inet_cork_full cork; | ||
1700 | struct inet6_cork v6_cork; | ||
1701 | struct sk_buff_head queue; | ||
1702 | int exthdrlen = (opt ? opt->opt_flen : 0); | ||
1703 | int err; | ||
1704 | |||
1705 | if (flags & MSG_PROBE) | ||
1706 | return NULL; | ||
1707 | |||
1708 | __skb_queue_head_init(&queue); | ||
1709 | |||
1710 | cork.base.flags = 0; | ||
1711 | cork.base.addr = 0; | ||
1712 | cork.base.opt = NULL; | ||
1713 | v6_cork.opt = NULL; | ||
1714 | err = ip6_setup_cork(sk, &cork, &v6_cork, hlimit, tclass, opt, rt, fl6); | ||
1715 | if (err) | ||
1716 | return ERR_PTR(err); | ||
1717 | |||
1718 | if (dontfrag < 0) | ||
1719 | dontfrag = inet6_sk(sk)->dontfrag; | ||
1720 | |||
1721 | err = __ip6_append_data(sk, fl6, &queue, &cork.base, &v6_cork, | ||
1722 | ¤t->task_frag, getfrag, from, | ||
1723 | length + exthdrlen, transhdrlen + exthdrlen, | ||
1724 | flags, dontfrag); | ||
1725 | if (err) { | ||
1726 | __ip6_flush_pending_frames(sk, &queue, &cork, &v6_cork); | ||
1727 | return ERR_PTR(err); | ||
1728 | } | ||
1729 | |||
1730 | return __ip6_make_skb(sk, &queue, &cork, &v6_cork); | ||
1731 | } | ||