diff options
-rw-r--r-- | include/net/dst.h | 1 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 68 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 3 |
3 files changed, 54 insertions, 18 deletions
diff --git a/include/net/dst.h b/include/net/dst.h index bed833d9796a..8197eadca819 100644 --- a/include/net/dst.h +++ b/include/net/dst.h | |||
@@ -60,6 +60,7 @@ struct dst_entry { | |||
60 | #define DST_NOCOUNT 0x0020 | 60 | #define DST_NOCOUNT 0x0020 |
61 | #define DST_NOPEER 0x0040 | 61 | #define DST_NOPEER 0x0040 |
62 | #define DST_FAKE_RTABLE 0x0080 | 62 | #define DST_FAKE_RTABLE 0x0080 |
63 | #define DST_XFRM_TUNNEL 0x0100 | ||
63 | 64 | ||
64 | short error; | 65 | short error; |
65 | short obsolete; | 66 | short obsolete; |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d99fdc699625..17b8c67998bb 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src, | |||
1187 | return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; | 1187 | return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; |
1188 | } | 1188 | } |
1189 | 1189 | ||
1190 | static void ip6_append_data_mtu(int *mtu, | ||
1191 | int *maxfraglen, | ||
1192 | unsigned int fragheaderlen, | ||
1193 | struct sk_buff *skb, | ||
1194 | struct rt6_info *rt) | ||
1195 | { | ||
1196 | if (!(rt->dst.flags & DST_XFRM_TUNNEL)) { | ||
1197 | if (skb == NULL) { | ||
1198 | /* first fragment, reserve header_len */ | ||
1199 | *mtu = *mtu - rt->dst.header_len; | ||
1200 | |||
1201 | } else { | ||
1202 | /* | ||
1203 | * this fragment is not first, the headers | ||
1204 | * space is regarded as data space. | ||
1205 | */ | ||
1206 | *mtu = dst_mtu(rt->dst.path); | ||
1207 | } | ||
1208 | *maxfraglen = ((*mtu - fragheaderlen) & ~7) | ||
1209 | + fragheaderlen - sizeof(struct frag_hdr); | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1190 | int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | 1213 | int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, |
1191 | int offset, int len, int odd, struct sk_buff *skb), | 1214 | int offset, int len, int odd, struct sk_buff *skb), |
1192 | void *from, int length, int transhdrlen, | 1215 | void *from, int length, int transhdrlen, |
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
1196 | struct inet_sock *inet = inet_sk(sk); | 1219 | struct inet_sock *inet = inet_sk(sk); |
1197 | struct ipv6_pinfo *np = inet6_sk(sk); | 1220 | struct ipv6_pinfo *np = inet6_sk(sk); |
1198 | struct inet_cork *cork; | 1221 | struct inet_cork *cork; |
1199 | struct sk_buff *skb; | 1222 | struct sk_buff *skb, *skb_prev = NULL; |
1200 | unsigned int maxfraglen, fragheaderlen; | 1223 | unsigned int maxfraglen, fragheaderlen; |
1201 | int exthdrlen; | 1224 | int exthdrlen; |
1202 | int dst_exthdrlen; | 1225 | int dst_exthdrlen; |
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
1253 | inet->cork.fl.u.ip6 = *fl6; | 1276 | inet->cork.fl.u.ip6 = *fl6; |
1254 | np->cork.hop_limit = hlimit; | 1277 | np->cork.hop_limit = hlimit; |
1255 | np->cork.tclass = tclass; | 1278 | np->cork.tclass = tclass; |
1256 | mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? | 1279 | if (rt->dst.flags & DST_XFRM_TUNNEL) |
1257 | rt->dst.dev->mtu : dst_mtu(&rt->dst); | 1280 | mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? |
1281 | rt->dst.dev->mtu : dst_mtu(&rt->dst); | ||
1282 | else | ||
1283 | mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? | ||
1284 | rt->dst.dev->mtu : dst_mtu(rt->dst.path); | ||
1258 | if (np->frag_size < mtu) { | 1285 | if (np->frag_size < mtu) { |
1259 | if (np->frag_size) | 1286 | if (np->frag_size) |
1260 | mtu = np->frag_size; | 1287 | mtu = np->frag_size; |
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
1350 | unsigned int fraglen; | 1377 | unsigned int fraglen; |
1351 | unsigned int fraggap; | 1378 | unsigned int fraggap; |
1352 | unsigned int alloclen; | 1379 | unsigned int alloclen; |
1353 | struct sk_buff *skb_prev; | ||
1354 | alloc_new_skb: | 1380 | alloc_new_skb: |
1355 | skb_prev = skb; | ||
1356 | |||
1357 | /* There's no room in the current skb */ | 1381 | /* There's no room in the current skb */ |
1358 | if (skb_prev) | 1382 | if (skb) |
1359 | fraggap = skb_prev->len - maxfraglen; | 1383 | fraggap = skb->len - maxfraglen; |
1360 | else | 1384 | else |
1361 | fraggap = 0; | 1385 | fraggap = 0; |
1386 | /* update mtu and maxfraglen if necessary */ | ||
1387 | if (skb == NULL || skb_prev == NULL) | ||
1388 | ip6_append_data_mtu(&mtu, &maxfraglen, | ||
1389 | fragheaderlen, skb, rt); | ||
1390 | |||
1391 | skb_prev = skb; | ||
1362 | 1392 | ||
1363 | /* | 1393 | /* |
1364 | * If remaining data exceeds the mtu, | 1394 | * If remaining data exceeds the mtu, |
1365 | * we know we need more fragment(s). | 1395 | * we know we need more fragment(s). |
1366 | */ | 1396 | */ |
1367 | datalen = length + fraggap; | 1397 | datalen = length + fraggap; |
1368 | if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) | ||
1369 | datalen = maxfraglen - fragheaderlen; | ||
1370 | 1398 | ||
1371 | fraglen = datalen + fragheaderlen; | 1399 | if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) |
1400 | datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len; | ||
1372 | if ((flags & MSG_MORE) && | 1401 | if ((flags & MSG_MORE) && |
1373 | !(rt->dst.dev->features&NETIF_F_SG)) | 1402 | !(rt->dst.dev->features&NETIF_F_SG)) |
1374 | alloclen = mtu; | 1403 | alloclen = mtu; |
@@ -1377,13 +1406,16 @@ alloc_new_skb: | |||
1377 | 1406 | ||
1378 | alloclen += dst_exthdrlen; | 1407 | alloclen += dst_exthdrlen; |
1379 | 1408 | ||
1380 | /* | 1409 | if (datalen != length + fraggap) { |
1381 | * The last fragment gets additional space at tail. | 1410 | /* |
1382 | * Note: we overallocate on fragments with MSG_MODE | 1411 | * this is not the last fragment, the trailer |
1383 | * because we have no idea if we're the last one. | 1412 | * space is regarded as data space. |
1384 | */ | 1413 | */ |
1385 | if (datalen == length + fraggap) | 1414 | datalen += rt->dst.trailer_len; |
1386 | alloclen += rt->dst.trailer_len; | 1415 | } |
1416 | |||
1417 | alloclen += rt->dst.trailer_len; | ||
1418 | fraglen = datalen + fragheaderlen; | ||
1387 | 1419 | ||
1388 | /* | 1420 | /* |
1389 | * We just reserve space for fragment header. | 1421 | * We just reserve space for fragment header. |
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c53e8f42aa75..ccfbd328a69d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -1921,6 +1921,9 @@ no_transform: | |||
1921 | } | 1921 | } |
1922 | ok: | 1922 | ok: |
1923 | xfrm_pols_put(pols, drop_pols); | 1923 | xfrm_pols_put(pols, drop_pols); |
1924 | if (dst && dst->xfrm && | ||
1925 | dst->xfrm->props.mode == XFRM_MODE_TUNNEL) | ||
1926 | dst->flags |= DST_XFRM_TUNNEL; | ||
1924 | return dst; | 1927 | return dst; |
1925 | 1928 | ||
1926 | nopol: | 1929 | nopol: |