Skip to content

Commit 045a7c1

Browse files
committed
gtp: add helper function to build GTP packets from an IPv6 packet
Add routine to attach an IPv6 route for the encapsulated packet, deal with Path MTU and push GTP header. This helper function will be used to deal with IPv4-in-IPv6-GTP. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent b77732f commit 045a7c1

File tree

1 file changed

+62
-47
lines changed

1 file changed

+62
-47
lines changed

drivers/net/gtp.c

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,63 @@ static int gtp_build_skb_outer_ip4(struct sk_buff *skb, struct net_device *dev,
10471047
return -EBADMSG;
10481048
}
10491049

1050+
static int gtp_build_skb_outer_ip6(struct net *net, struct sk_buff *skb,
1051+
struct net_device *dev,
1052+
struct gtp_pktinfo *pktinfo,
1053+
struct pdp_ctx *pctx, __u8 tos)
1054+
{
1055+
struct dst_entry *dst;
1056+
struct rt6_info *rt;
1057+
struct flowi6 fl6;
1058+
int mtu;
1059+
1060+
rt = ip6_route_output_gtp(net, &fl6, pctx->sk, &pctx->peer.addr6,
1061+
&inet6_sk(pctx->sk)->saddr);
1062+
if (IS_ERR(rt)) {
1063+
netdev_dbg(dev, "no route to SSGN %pI6\n",
1064+
&pctx->peer.addr6);
1065+
dev->stats.tx_carrier_errors++;
1066+
goto err;
1067+
}
1068+
dst = &rt->dst;
1069+
1070+
if (rt->dst.dev == dev) {
1071+
netdev_dbg(dev, "circular route to SSGN %pI6\n",
1072+
&pctx->peer.addr6);
1073+
dev->stats.collisions++;
1074+
goto err_rt;
1075+
}
1076+
1077+
mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
1078+
sizeof(struct ipv6hdr) - sizeof(struct udphdr);
1079+
switch (pctx->gtp_version) {
1080+
case GTP_V0:
1081+
mtu -= sizeof(struct gtp0_header);
1082+
break;
1083+
case GTP_V1:
1084+
mtu -= sizeof(struct gtp1_header);
1085+
break;
1086+
}
1087+
1088+
skb_dst_update_pmtu_no_confirm(skb, mtu);
1089+
1090+
if ((!skb_is_gso(skb) && skb->len > mtu) ||
1091+
(skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) {
1092+
netdev_dbg(dev, "packet too big, fragmentation needed\n");
1093+
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
1094+
goto err_rt;
1095+
}
1096+
1097+
gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, tos, pctx, rt, &fl6, dev);
1098+
gtp_push_header(skb, pktinfo);
1099+
1100+
return 0;
1101+
err_rt:
1102+
dst_release(dst);
1103+
err:
1104+
return -EBADMSG;
1105+
}
1106+
10501107
static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
10511108
struct gtp_pktinfo *pktinfo)
10521109
{
@@ -1087,13 +1144,10 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
10871144
{
10881145
struct gtp_dev *gtp = netdev_priv(dev);
10891146
struct net *net = gtp->net;
1090-
struct dst_entry *dst;
10911147
struct pdp_ctx *pctx;
10921148
struct ipv6hdr *ip6h;
1093-
struct rt6_info *rt;
1094-
struct flowi6 fl6;
10951149
__u8 tos;
1096-
int mtu;
1150+
int ret;
10971151

10981152
/* Read the IP destination address and resolve the PDP context.
10991153
* Prepend PDP header with TEI/TID from PDP ctx.
@@ -1111,55 +1165,16 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
11111165
}
11121166
netdev_dbg(dev, "found PDP context %p\n", pctx);
11131167

1114-
rt = ip6_route_output_gtp(net, &fl6, pctx->sk, &pctx->peer.addr6,
1115-
&inet6_sk(pctx->sk)->saddr);
1116-
if (IS_ERR(rt)) {
1117-
netdev_dbg(dev, "no route to SSGN %pI6\n",
1118-
&pctx->peer.addr6);
1119-
dev->stats.tx_carrier_errors++;
1120-
goto err;
1121-
}
1122-
dst = &rt->dst;
1123-
1124-
if (rt->dst.dev == dev) {
1125-
netdev_dbg(dev, "circular route to SSGN %pI6\n",
1126-
&pctx->peer.addr6);
1127-
dev->stats.collisions++;
1128-
goto err_rt;
1129-
}
1130-
1131-
mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
1132-
sizeof(struct ipv6hdr) - sizeof(struct udphdr);
1133-
switch (pctx->gtp_version) {
1134-
case GTP_V0:
1135-
mtu -= sizeof(struct gtp0_header);
1136-
break;
1137-
case GTP_V1:
1138-
mtu -= sizeof(struct gtp1_header);
1139-
break;
1140-
}
1141-
1142-
skb_dst_update_pmtu_no_confirm(skb, mtu);
1143-
1144-
if ((!skb_is_gso(skb) && skb->len > mtu) ||
1145-
(skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) {
1146-
netdev_dbg(dev, "packet too big, fragmentation needed\n");
1147-
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
1148-
goto err_rt;
1149-
}
1150-
11511168
tos = ipv6_get_dsfield(ip6h);
1152-
gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, tos, pctx, rt, &fl6, dev);
1153-
gtp_push_header(skb, pktinfo);
1169+
1170+
ret = gtp_build_skb_outer_ip6(net, skb, dev, pktinfo, pctx, tos);
1171+
if (ret < 0)
1172+
return ret;
11541173

11551174
netdev_dbg(dev, "gtp -> IP src: %pI6 dst: %pI6\n",
11561175
&ip6h->saddr, &ip6h->daddr);
11571176

11581177
return 0;
1159-
err_rt:
1160-
dst_release(dst);
1161-
err:
1162-
return -EBADMSG;
11631178
}
11641179

11651180
static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)

0 commit comments

Comments
 (0)