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

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
Loading
Loading
Loading
Loading
+96 −55
Original line number Diff line number Diff line
@@ -50,8 +50,12 @@ struct pdp_ctx {
	u8			gtp_version;
	u16			af;

	struct in_addr		ms_addr_ip4;
	struct in_addr		peer_addr_ip4;
	union {
		struct in_addr	addr;
	} ms;
	union {
		struct in_addr	addr;
	} peer;

	struct sock		*sk;
	struct net_device       *dev;
@@ -80,9 +84,15 @@ struct gtp_dev {
};

struct echo_info {
	struct in_addr		ms_addr_ip4;
	struct in_addr		peer_addr_ip4;
	u16			af;
	u8			gtp_version;

	union {
		struct in_addr	addr;
	} ms;
	union {
		struct in_addr	addr;
	} peer;
};

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)

	hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
		if (pdp->af == AF_INET &&
		    pdp->ms_addr_ip4.s_addr == ms_addr)
		    pdp->ms.addr.s_addr == ms_addr)
			return pdp;
	}

@@ -181,9 +191,9 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
	iph = (struct iphdr *)(skb->data + hdrlen);

	if (role == GTP_ROLE_SGSN)
		return iph->daddr == pctx->ms_addr_ip4.s_addr;
		return iph->daddr == pctx->ms.addr.s_addr;
	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
@@ -292,13 +302,39 @@ static void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type)
		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)
{
	struct gtp0_packet *gtp_pkt;
	struct gtp0_header *gtp0;
	struct rtable *rt;
	struct flowi4 fl4;
	struct iphdr *iph;
	__be16 seq;

	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)
	gtp_pkt->ie.tag = GTPIE_RECOVERY;
	gtp_pkt->ie.val = gtp->restart_count;

	iph = ip_hdr(skb);

	/* 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);
	switch (gtp->sk0->sk_family) {
	case AF_INET:
		if (gtp0_send_echo_resp_ip(gtp, skb) < 0)
			return -1;
		break;
	case AF_INET6:
		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;
}

@@ -360,8 +384,8 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
		goto failure;

	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_MS_ADDRESS, echo.ms_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.s_addr))
		goto failure;

	genlmsg_end(skb, genlh);
@@ -372,12 +396,20 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
	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)
{
	struct gtp0_header *gtp0;
	struct echo_info echo;
	struct sk_buff *msg;
	struct iphdr *iph;
	int ret;

	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)
	if (!gtp0_validate_echo_hdr(gtp0))
		return -1;

	iph = ip_hdr(skb);
	echo.ms_addr_ip4.s_addr = iph->daddr;
	echo.peer_addr_ip4.s_addr = iph->saddr;
	echo.gtp_version = GTP_V0;
	switch (gtp->sk0->sk_family) {
	case AF_INET:
		gtp0_handle_echo_resp_ip(skb, &echo);
		break;
	case AF_INET6:
		return -1;
	}

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
	if (!msg)
@@ -549,8 +584,8 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
		return -1;

	iph = ip_hdr(skb);
	echo.ms_addr_ip4.s_addr = iph->daddr;
	echo.peer_addr_ip4.s_addr = iph->saddr;
	echo.ms.addr.s_addr = iph->daddr;
	echo.peer.addr.s_addr = iph->saddr;
	echo.gtp_version = GTP_V1;

	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)

struct gtp_pktinfo {
	struct sock		*sk;
	union {
		struct iphdr	*iph;
	};
	union {
		struct flowi4	fl4;
	};
	union {
		struct rtable	*rt;
	};
	struct pdp_ctx		*pctx;
	struct net_device	*dev;
	__be16			gtph_port;
@@ -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);

	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);
	if (IS_ERR(rt)) {
		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++;
		goto err;
	}

	if (rt->dst.dev == dev) {
		netdev_dbg(dev, "circular route to SSGN %pI4\n",
			   &pctx->peer_addr_ip4.s_addr);
			   &pctx->peer.addr.s_addr);
		dev->stats.collisions++;
		goto err_rt;
	}
@@ -977,11 +1018,11 @@ static const struct device_type gtp_type = {
	.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)
{
	unsigned int max_gtp_header_len = sizeof(struct iphdr) +
					  sizeof(struct udphdr) +
					  sizeof(struct gtp0_header);
	struct gtp_dev *gtp = netdev_priv(dev);

	dev->netdev_ops		= &gtp_netdev_ops;
@@ -990,7 +1031,7 @@ static void gtp_link_setup(struct net_device *dev)

	dev->hard_header_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. */
	dev->type = ARPHRD_NONE;
@@ -1001,7 +1042,7 @@ static void gtp_link_setup(struct net_device *dev)
	dev->features	|= NETIF_F_LLTX;
	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;
}

@@ -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->af = AF_INET;
	pctx->peer_addr_ip4.s_addr =
	pctx->peer.addr.s_addr =
		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]);

	switch (pctx->gtp_version) {
@@ -1444,13 +1485,13 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
	switch (pctx->gtp_version) {
	case GTP_V0:
		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->ms_addr_ip4, pctx);
			   pctx->u.v0.tid, &pctx->peer.addr,
			   &pctx->ms.addr, pctx);
		break;
	case GTP_V1:
		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->peer_addr_ip4, &pctx->ms_addr_ip4, pctx);
			   &pctx->peer.addr, &pctx->ms.addr, pctx);
		break;
	}

@@ -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) ||
	    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_MS_ADDRESS, pctx->ms_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.s_addr))
		goto nla_put_failure;

	switch (pctx->gtp_version) {