Commit 750771d0 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

gtp: prepare for IPv6 support

Use union artifact to prepare for IPv6 support.
Add and use GTP_{IPV4,TH}_MAXLEN.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent b6fc0956
...@@ -50,8 +50,12 @@ struct pdp_ctx { ...@@ -50,8 +50,12 @@ struct pdp_ctx {
u8 gtp_version; u8 gtp_version;
u16 af; u16 af;
struct in_addr ms_addr_ip4; union {
struct in_addr peer_addr_ip4; struct in_addr addr;
} ms;
union {
struct in_addr addr;
} peer;
struct sock *sk; struct sock *sk;
struct net_device *dev; struct net_device *dev;
...@@ -80,9 +84,15 @@ struct gtp_dev { ...@@ -80,9 +84,15 @@ struct gtp_dev {
}; };
struct echo_info { struct echo_info {
struct in_addr ms_addr_ip4; u16 af;
struct in_addr peer_addr_ip4;
u8 gtp_version; u8 gtp_version;
union {
struct in_addr addr;
} ms;
union {
struct in_addr addr;
} peer;
}; };
static unsigned int gtp_net_id __read_mostly; static unsigned int gtp_net_id __read_mostly;
...@@ -163,7 +173,7 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr) ...@@ -163,7 +173,7 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
hlist_for_each_entry_rcu(pdp, head, hlist_addr) { hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
if (pdp->af == AF_INET && if (pdp->af == AF_INET &&
pdp->ms_addr_ip4.s_addr == ms_addr) pdp->ms.addr.s_addr == ms_addr)
return pdp; return pdp;
} }
...@@ -181,9 +191,9 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, ...@@ -181,9 +191,9 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
iph = (struct iphdr *)(skb->data + hdrlen); iph = (struct iphdr *)(skb->data + hdrlen);
if (role == GTP_ROLE_SGSN) if (role == GTP_ROLE_SGSN)
return iph->daddr == pctx->ms_addr_ip4.s_addr; return iph->daddr == pctx->ms.addr.s_addr;
else else
return iph->saddr == pctx->ms_addr_ip4.s_addr; return iph->saddr == pctx->ms.addr.s_addr;
} }
/* Check if the inner IP address in this packet is assigned to any /* Check if the inner IP address in this packet is assigned to any
...@@ -292,13 +302,39 @@ static void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type) ...@@ -292,13 +302,39 @@ static void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type)
hdr->length = 0; hdr->length = 0;
} }
static int gtp0_send_echo_resp_ip(struct gtp_dev *gtp, struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
struct flowi4 fl4;
struct rtable *rt;
/* find route to the sender,
* src address becomes dst address and vice versa.
*/
rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr);
if (IS_ERR(rt)) {
netdev_dbg(gtp->dev, "no route for echo response from %pI4\n",
&iph->saddr);
return -1;
}
udp_tunnel_xmit_skb(rt, gtp->sk0, skb,
fl4.saddr, fl4.daddr,
iph->tos,
ip4_dst_hoplimit(&rt->dst),
0,
htons(GTP0_PORT), htons(GTP0_PORT),
!net_eq(sock_net(gtp->sk1u),
dev_net(gtp->dev)),
false);
return 0;
}
static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
{ {
struct gtp0_packet *gtp_pkt; struct gtp0_packet *gtp_pkt;
struct gtp0_header *gtp0; struct gtp0_header *gtp0;
struct rtable *rt;
struct flowi4 fl4;
struct iphdr *iph;
__be16 seq; __be16 seq;
gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));
...@@ -325,27 +361,15 @@ static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) ...@@ -325,27 +361,15 @@ static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
gtp_pkt->ie.tag = GTPIE_RECOVERY; gtp_pkt->ie.tag = GTPIE_RECOVERY;
gtp_pkt->ie.val = gtp->restart_count; gtp_pkt->ie.val = gtp->restart_count;
iph = ip_hdr(skb); switch (gtp->sk0->sk_family) {
case AF_INET:
/* find route to the sender, if (gtp0_send_echo_resp_ip(gtp, skb) < 0)
* src address becomes dst address and vice versa. return -1;
*/ break;
rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr); case AF_INET6:
if (IS_ERR(rt)) {
netdev_dbg(gtp->dev, "no route for echo response from %pI4\n",
&iph->saddr);
return -1; return -1;
} }
udp_tunnel_xmit_skb(rt, gtp->sk0, skb,
fl4.saddr, fl4.daddr,
iph->tos,
ip4_dst_hoplimit(&rt->dst),
0,
htons(GTP0_PORT), htons(GTP0_PORT),
!net_eq(sock_net(gtp->sk1u),
dev_net(gtp->dev)),
false);
return 0; return 0;
} }
...@@ -360,8 +384,8 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, ...@@ -360,8 +384,8 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
goto failure; goto failure;
if (nla_put_u32(skb, GTPA_VERSION, echo.gtp_version) || if (nla_put_u32(skb, GTPA_VERSION, echo.gtp_version) ||
nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer_addr_ip4.s_addr) || nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer.addr.s_addr) ||
nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms_addr_ip4.s_addr)) nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms.addr.s_addr))
goto failure; goto failure;
genlmsg_end(skb, genlh); genlmsg_end(skb, genlh);
...@@ -372,12 +396,20 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, ...@@ -372,12 +396,20 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
return -EMSGSIZE; return -EMSGSIZE;
} }
static void gtp0_handle_echo_resp_ip(struct sk_buff *skb, struct echo_info *echo)
{
struct iphdr *iph = ip_hdr(skb);
echo->ms.addr.s_addr = iph->daddr;
echo->peer.addr.s_addr = iph->saddr;
echo->gtp_version = GTP_V0;
}
static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
{ {
struct gtp0_header *gtp0; struct gtp0_header *gtp0;
struct echo_info echo; struct echo_info echo;
struct sk_buff *msg; struct sk_buff *msg;
struct iphdr *iph;
int ret; int ret;
gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));
...@@ -385,10 +417,13 @@ static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) ...@@ -385,10 +417,13 @@ static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
if (!gtp0_validate_echo_hdr(gtp0)) if (!gtp0_validate_echo_hdr(gtp0))
return -1; return -1;
iph = ip_hdr(skb); switch (gtp->sk0->sk_family) {
echo.ms_addr_ip4.s_addr = iph->daddr; case AF_INET:
echo.peer_addr_ip4.s_addr = iph->saddr; gtp0_handle_echo_resp_ip(skb, &echo);
echo.gtp_version = GTP_V0; break;
case AF_INET6:
return -1;
}
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!msg) if (!msg)
...@@ -549,8 +584,8 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) ...@@ -549,8 +584,8 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
return -1; return -1;
iph = ip_hdr(skb); iph = ip_hdr(skb);
echo.ms_addr_ip4.s_addr = iph->daddr; echo.ms.addr.s_addr = iph->daddr;
echo.peer_addr_ip4.s_addr = iph->saddr; echo.peer.addr.s_addr = iph->saddr;
echo.gtp_version = GTP_V1; echo.gtp_version = GTP_V1;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
...@@ -801,9 +836,15 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) ...@@ -801,9 +836,15 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
struct gtp_pktinfo { struct gtp_pktinfo {
struct sock *sk; struct sock *sk;
union {
struct iphdr *iph; struct iphdr *iph;
};
union {
struct flowi4 fl4; struct flowi4 fl4;
};
union {
struct rtable *rt; struct rtable *rt;
};
struct pdp_ctx *pctx; struct pdp_ctx *pctx;
struct net_device *dev; struct net_device *dev;
__be16 gtph_port; __be16 gtph_port;
...@@ -864,18 +905,18 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, ...@@ -864,18 +905,18 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
} }
netdev_dbg(dev, "found PDP context %p\n", pctx); netdev_dbg(dev, "found PDP context %p\n", pctx);
rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr, rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer.addr.s_addr,
inet_sk(pctx->sk)->inet_saddr); inet_sk(pctx->sk)->inet_saddr);
if (IS_ERR(rt)) { if (IS_ERR(rt)) {
netdev_dbg(dev, "no route to SSGN %pI4\n", netdev_dbg(dev, "no route to SSGN %pI4\n",
&pctx->peer_addr_ip4.s_addr); &pctx->peer.addr.s_addr);
dev->stats.tx_carrier_errors++; dev->stats.tx_carrier_errors++;
goto err; goto err;
} }
if (rt->dst.dev == dev) { if (rt->dst.dev == dev) {
netdev_dbg(dev, "circular route to SSGN %pI4\n", netdev_dbg(dev, "circular route to SSGN %pI4\n",
&pctx->peer_addr_ip4.s_addr); &pctx->peer.addr.s_addr);
dev->stats.collisions++; dev->stats.collisions++;
goto err_rt; goto err_rt;
} }
...@@ -977,11 +1018,11 @@ static const struct device_type gtp_type = { ...@@ -977,11 +1018,11 @@ static const struct device_type gtp_type = {
.name = "gtp", .name = "gtp",
}; };
#define GTP_TH_MAXLEN (sizeof(struct udphdr) + sizeof(struct gtp0_header))
#define GTP_IPV4_MAXLEN (sizeof(struct iphdr) + GTP_TH_MAXLEN)
static void gtp_link_setup(struct net_device *dev) static void gtp_link_setup(struct net_device *dev)
{ {
unsigned int max_gtp_header_len = sizeof(struct iphdr) +
sizeof(struct udphdr) +
sizeof(struct gtp0_header);
struct gtp_dev *gtp = netdev_priv(dev); struct gtp_dev *gtp = netdev_priv(dev);
dev->netdev_ops = &gtp_netdev_ops; dev->netdev_ops = &gtp_netdev_ops;
...@@ -990,7 +1031,7 @@ static void gtp_link_setup(struct net_device *dev) ...@@ -990,7 +1031,7 @@ static void gtp_link_setup(struct net_device *dev)
dev->hard_header_len = 0; dev->hard_header_len = 0;
dev->addr_len = 0; dev->addr_len = 0;
dev->mtu = ETH_DATA_LEN - max_gtp_header_len; dev->mtu = ETH_DATA_LEN - GTP_IPV4_MAXLEN;
/* Zero header length. */ /* Zero header length. */
dev->type = ARPHRD_NONE; dev->type = ARPHRD_NONE;
...@@ -1001,7 +1042,7 @@ static void gtp_link_setup(struct net_device *dev) ...@@ -1001,7 +1042,7 @@ static void gtp_link_setup(struct net_device *dev)
dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_LLTX;
netif_keep_dst(dev); netif_keep_dst(dev);
dev->needed_headroom = LL_MAX_HEADER + max_gtp_header_len; dev->needed_headroom = LL_MAX_HEADER + GTP_IPV4_MAXLEN;
gtp->dev = dev; gtp->dev = dev;
} }
...@@ -1341,9 +1382,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) ...@@ -1341,9 +1382,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
{ {
pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);
pctx->af = AF_INET; pctx->af = AF_INET;
pctx->peer_addr_ip4.s_addr = pctx->peer.addr.s_addr =
nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
pctx->ms_addr_ip4.s_addr = pctx->ms.addr.s_addr =
nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
switch (pctx->gtp_version) { switch (pctx->gtp_version) {
...@@ -1444,13 +1485,13 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, ...@@ -1444,13 +1485,13 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
switch (pctx->gtp_version) { switch (pctx->gtp_version) {
case GTP_V0: case GTP_V0:
netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n", netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n",
pctx->u.v0.tid, &pctx->peer_addr_ip4, pctx->u.v0.tid, &pctx->peer.addr,
&pctx->ms_addr_ip4, pctx); &pctx->ms.addr, pctx);
break; break;
case GTP_V1: case GTP_V1:
netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n", netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n",
pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx->u.v1.i_tei, pctx->u.v1.o_tei,
&pctx->peer_addr_ip4, &pctx->ms_addr_ip4, pctx); &pctx->peer.addr, &pctx->ms.addr, pctx);
break; break;
} }
...@@ -1622,8 +1663,8 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, ...@@ -1622,8 +1663,8 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) || if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) ||
nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex) || nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex) ||
nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr) || nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr) ||
nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr)) nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
goto nla_put_failure; goto nla_put_failure;
switch (pctx->gtp_version) { switch (pctx->gtp_version) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment