458 |
458 |
}
|
459 |
459 |
}
|
460 |
460 |
|
|
461 |
static int gtp_update_pmtu(struct pdp_ctx *pctx, struct sk_buff *skb,
|
|
462 |
struct net_device *dev, struct dst_entry *ndst,
|
|
463 |
__be16 df, int tnl_header_len,
|
|
464 |
const struct iphdr *inner_iph)
|
|
465 |
{
|
|
466 |
int mtu;
|
|
467 |
|
|
468 |
if (df) {
|
|
469 |
mtu = dst_mtu(ndst) - dev->hard_header_len - tnl_header_len;
|
|
470 |
switch (pctx->gtp_version) {
|
|
471 |
case GTP_V0:
|
|
472 |
mtu -= sizeof(struct gtp0_header);
|
|
473 |
break;
|
|
474 |
case GTP_V1:
|
|
475 |
mtu -= sizeof(struct gtp1_header);
|
|
476 |
break;
|
|
477 |
}
|
|
478 |
} else
|
|
479 |
mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
|
|
480 |
|
|
481 |
if (skb_dst(skb))
|
|
482 |
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
|
|
483 |
|
|
484 |
if (!skb_is_gso(skb) && (inner_iph->frag_off & htons(IP_DF)) &&
|
|
485 |
mtu < ntohs(inner_iph->tot_len)) {
|
|
486 |
netdev_dbg(dev, "packet too big, fragmentation needed\n");
|
|
487 |
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
|
488 |
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
|
|
489 |
return -E2BIG;
|
|
490 |
}
|
|
491 |
|
|
492 |
return 0;
|
|
493 |
}
|
|
494 |
|
461 |
495 |
static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
|
462 |
496 |
struct sock *sk, struct iphdr *iph,
|
463 |
497 |
struct pdp_ctx *pctx, struct rtable *rt,
|
... | ... | |
480 |
514 |
struct rtable *rt;
|
481 |
515 |
struct flowi4 fl4;
|
482 |
516 |
struct iphdr *iph;
|
483 |
|
__be16 df;
|
484 |
|
int mtu;
|
485 |
517 |
|
486 |
518 |
/* Read the IP destination address and resolve the PDP context.
|
487 |
519 |
* Prepend PDP header with TEI/TID from PDP ctx.
|
... | ... | |
516 |
548 |
goto err_rt;
|
517 |
549 |
}
|
518 |
550 |
|
|
551 |
if (gtp_update_pmtu(pctx, skb, dev, &rt->dst, iph->frag_off,
|
|
552 |
sizeof(struct iphdr) + sizeof(struct udphdr), iph))
|
|
553 |
goto err_rt;
|
|
554 |
|
519 |
555 |
skb_dst_drop(skb);
|
520 |
556 |
|
521 |
|
/* This is similar to tnl_update_pmtu(). */
|
522 |
|
df = iph->frag_off;
|
523 |
|
if (df) {
|
524 |
|
mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
|
525 |
|
sizeof(struct iphdr) - sizeof(struct udphdr);
|
526 |
|
switch (pctx->gtp_version) {
|
527 |
|
case GTP_V0:
|
528 |
|
mtu -= sizeof(struct gtp0_header);
|
529 |
|
break;
|
530 |
|
case GTP_V1:
|
531 |
|
mtu -= sizeof(struct gtp1_header);
|
532 |
|
break;
|
533 |
|
}
|
534 |
|
} else {
|
535 |
|
mtu = dst_mtu(&rt->dst);
|
536 |
|
}
|
537 |
|
|
538 |
|
rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu);
|
539 |
|
|
540 |
|
if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) &&
|
541 |
|
mtu < ntohs(iph->tot_len)) {
|
542 |
|
netdev_dbg(dev, "packet too big, fragmentation needed\n");
|
543 |
|
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
544 |
|
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
|
545 |
|
htonl(mtu));
|
546 |
|
goto err_rt;
|
547 |
|
}
|
548 |
|
|
549 |
557 |
gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
|
550 |
558 |
gtp_push_header(skb, pktinfo);
|
551 |
559 |
|