mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 13:34:30 +00:00
gtp: Implement GTP echo response
Adding GTP device through ip link creates the situation where there is no userspace daemon which would handle GTP messages (Echo Request for example). GTP-U instance which would not respond to echo requests would violate GTP specification. When GTP packet arrives with GTP_ECHO_REQ message type, GTP_ECHO_RSP is send to the sender. GTP_ECHO_RSP message should contain information element with GTPIE_RECOVERY tag and restart counter value. For GTPv1 restart counter is not used and should be equal to 0, for GTPv0 restart counter contains information provided from userspace(IFLA_GTP_RESTART_COUNT). Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com> Suggested-by: Harald Welte <laforge@gnumonks.org> Reviewed-by: Harald Welte <laforge@gnumonks.org> Tested-by: Harald Welte <laforge@gnumonks.org> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
parent
b20dc3c684
commit
9af41cc334
@ -75,6 +75,8 @@ struct gtp_dev {
|
||||
unsigned int hash_size;
|
||||
struct hlist_head *tid_hash;
|
||||
struct hlist_head *addr_hash;
|
||||
|
||||
u8 restart_count;
|
||||
};
|
||||
|
||||
static unsigned int gtp_net_id __read_mostly;
|
||||
@ -217,6 +219,106 @@ err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
|
||||
const struct sock *sk,
|
||||
__be32 daddr, __be32 saddr)
|
||||
{
|
||||
memset(fl4, 0, sizeof(*fl4));
|
||||
fl4->flowi4_oif = sk->sk_bound_dev_if;
|
||||
fl4->daddr = daddr;
|
||||
fl4->saddr = saddr;
|
||||
fl4->flowi4_tos = RT_CONN_FLAGS(sk);
|
||||
fl4->flowi4_proto = sk->sk_protocol;
|
||||
|
||||
return ip_route_output_key(sock_net(sk), fl4);
|
||||
}
|
||||
|
||||
/* GSM TS 09.60. 7.3
|
||||
* In all Path Management messages:
|
||||
* - TID: is not used and shall be set to 0.
|
||||
* - Flow Label is not used and shall be set to 0
|
||||
* In signalling messages:
|
||||
* - number: this field is not yet used in signalling messages.
|
||||
* It shall be set to 255 by the sender and shall be ignored
|
||||
* by the receiver
|
||||
* Returns true if the echo req was correct, false otherwise.
|
||||
*/
|
||||
static bool gtp0_validate_echo_req(struct gtp0_header *gtp0)
|
||||
{
|
||||
return !(gtp0->tid || (gtp0->flags ^ 0x1e) ||
|
||||
gtp0->number != 0xff || gtp0->flow);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (!gtp0_validate_echo_req(gtp0))
|
||||
return -1;
|
||||
|
||||
seq = gtp0->seq;
|
||||
|
||||
/* pull GTP and UDP headers */
|
||||
skb_pull_data(skb, sizeof(struct gtp0_header) + sizeof(struct udphdr));
|
||||
|
||||
gtp_pkt = skb_push(skb, sizeof(struct gtp0_packet));
|
||||
memset(gtp_pkt, 0, sizeof(struct gtp0_packet));
|
||||
|
||||
gtp_pkt->gtp0_h.flags = 0x1e; /* v0, GTP-non-prime. */
|
||||
gtp_pkt->gtp0_h.type = GTP_ECHO_RSP;
|
||||
gtp_pkt->gtp0_h.length =
|
||||
htons(sizeof(struct gtp0_packet) - sizeof(struct gtp0_header));
|
||||
|
||||
/* GSM TS 09.60. 7.3 The Sequence Number in a signalling response
|
||||
* message shall be copied from the signalling request message
|
||||
* that the GSN is replying to.
|
||||
*/
|
||||
gtp_pkt->gtp0_h.seq = seq;
|
||||
|
||||
/* GSM TS 09.60. 7.3 In all Path Management Flow Label and TID
|
||||
* are not used and shall be set to 0.
|
||||
*/
|
||||
gtp_pkt->gtp0_h.flow = 0;
|
||||
gtp_pkt->gtp0_h.tid = 0;
|
||||
gtp_pkt->gtp0_h.number = 0xff;
|
||||
gtp_pkt->gtp0_h.spare[0] = 0xff;
|
||||
gtp_pkt->gtp0_h.spare[1] = 0xff;
|
||||
gtp_pkt->gtp0_h.spare[2] = 0xff;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
|
||||
static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
{
|
||||
@ -233,6 +335,13 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
if ((gtp0->flags >> 5) != GTP_V0)
|
||||
return 1;
|
||||
|
||||
/* If the sockets were created in kernel, it means that
|
||||
* there is no daemon running in userspace which would
|
||||
* handle echo request.
|
||||
*/
|
||||
if (gtp0->type == GTP_ECHO_REQ && gtp->sk_created)
|
||||
return gtp0_send_echo_resp(gtp, skb);
|
||||
|
||||
if (gtp0->type != GTP_TPDU)
|
||||
return 1;
|
||||
|
||||
@ -245,6 +354,75 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
return gtp_rx(pctx, skb, hdrlen, gtp->role);
|
||||
}
|
||||
|
||||
static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
{
|
||||
struct gtp1_header_long *gtp1u;
|
||||
struct gtp1u_packet *gtp_pkt;
|
||||
struct rtable *rt;
|
||||
struct flowi4 fl4;
|
||||
struct iphdr *iph;
|
||||
|
||||
gtp1u = (struct gtp1_header_long *)(skb->data + sizeof(struct udphdr));
|
||||
|
||||
/* 3GPP TS 29.281 5.1 - For the Echo Request, Echo Response,
|
||||
* Error Indication and Supported Extension Headers Notification
|
||||
* messages, the S flag shall be set to 1 and TEID shall be set to 0.
|
||||
*/
|
||||
if (!(gtp1u->flags & GTP1_F_SEQ) || gtp1u->tid)
|
||||
return -1;
|
||||
|
||||
/* pull GTP and UDP headers */
|
||||
skb_pull_data(skb,
|
||||
sizeof(struct gtp1_header_long) + sizeof(struct udphdr));
|
||||
|
||||
gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet));
|
||||
memset(gtp_pkt, 0, sizeof(struct gtp1u_packet));
|
||||
|
||||
/* S flag must be set to 1 */
|
||||
gtp_pkt->gtp1u_h.flags = 0x32;
|
||||
gtp_pkt->gtp1u_h.type = GTP_ECHO_RSP;
|
||||
/* seq, npdu and next should be counted to the length of the GTP packet
|
||||
* that's why szie of gtp1_header should be subtracted,
|
||||
* not why szie of gtp1_header_long.
|
||||
*/
|
||||
gtp_pkt->gtp1u_h.length =
|
||||
htons(sizeof(struct gtp1u_packet) - sizeof(struct gtp1_header));
|
||||
/* 3GPP TS 29.281 5.1 - TEID has to be set to 0 */
|
||||
gtp_pkt->gtp1u_h.tid = 0;
|
||||
|
||||
/* 3GPP TS 29.281 7.7.2 - The Restart Counter value in the
|
||||
* Recovery information element shall not be used, i.e. it shall
|
||||
* be set to zero by the sender and shall be ignored by the receiver.
|
||||
* The Recovery information element is mandatory due to backwards
|
||||
* compatibility reasons.
|
||||
*/
|
||||
gtp_pkt->ie.tag = GTPIE_RECOVERY;
|
||||
gtp_pkt->ie.val = 0;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
/* find route to the sender,
|
||||
* src address becomes dst address and vice versa.
|
||||
*/
|
||||
rt = ip4_route_output_gtp(&fl4, gtp->sk1u, 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->sk1u, skb,
|
||||
fl4.saddr, fl4.daddr,
|
||||
iph->tos,
|
||||
ip4_dst_hoplimit(&rt->dst),
|
||||
0,
|
||||
htons(GTP1U_PORT), htons(GTP1U_PORT),
|
||||
!net_eq(sock_net(gtp->sk1u),
|
||||
dev_net(gtp->dev)),
|
||||
false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
{
|
||||
unsigned int hdrlen = sizeof(struct udphdr) +
|
||||
@ -260,6 +438,13 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||
if ((gtp1->flags >> 5) != GTP_V1)
|
||||
return 1;
|
||||
|
||||
/* If the sockets were created in kernel, it means that
|
||||
* there is no daemon running in userspace which would
|
||||
* handle echo request.
|
||||
*/
|
||||
if (gtp1->type == GTP_ECHO_REQ && gtp->sk_created)
|
||||
return gtp1u_send_echo_resp(gtp, skb);
|
||||
|
||||
if (gtp1->type != GTP_TPDU)
|
||||
return 1;
|
||||
|
||||
@ -398,20 +583,6 @@ static void gtp_dev_uninit(struct net_device *dev)
|
||||
free_percpu(dev->tstats);
|
||||
}
|
||||
|
||||
static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
|
||||
const struct sock *sk,
|
||||
__be32 daddr)
|
||||
{
|
||||
memset(fl4, 0, sizeof(*fl4));
|
||||
fl4->flowi4_oif = sk->sk_bound_dev_if;
|
||||
fl4->daddr = daddr;
|
||||
fl4->saddr = inet_sk(sk)->inet_saddr;
|
||||
fl4->flowi4_tos = RT_CONN_FLAGS(sk);
|
||||
fl4->flowi4_proto = sk->sk_protocol;
|
||||
|
||||
return ip_route_output_key(sock_net(sk), fl4);
|
||||
}
|
||||
|
||||
static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
|
||||
{
|
||||
int payload_len = skb->len;
|
||||
@ -517,7 +688,8 @@ 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_ip4.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);
|
||||
@ -746,6 +918,11 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
|
||||
}
|
||||
gtp->role = role;
|
||||
|
||||
if (!data[IFLA_GTP_RESTART_COUNT])
|
||||
gtp->restart_count = 0;
|
||||
else
|
||||
gtp->restart_count = nla_get_u8(data[IFLA_GTP_RESTART_COUNT]);
|
||||
|
||||
gtp->net = src_net;
|
||||
|
||||
err = gtp_hashtable_new(gtp, hashsize);
|
||||
@ -801,6 +978,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
|
||||
[IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 },
|
||||
[IFLA_GTP_ROLE] = { .type = NLA_U32 },
|
||||
[IFLA_GTP_CREATE_SOCKETS] = { .type = NLA_U8 },
|
||||
[IFLA_GTP_RESTART_COUNT] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
@ -815,7 +993,8 @@ static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
static size_t gtp_get_size(const struct net_device *dev)
|
||||
{
|
||||
return nla_total_size(sizeof(__u32)) + /* IFLA_GTP_PDP_HASHSIZE */
|
||||
nla_total_size(sizeof(__u32)); /* IFLA_GTP_ROLE */
|
||||
nla_total_size(sizeof(__u32)) + /* IFLA_GTP_ROLE */
|
||||
nla_total_size(sizeof(__u8)); /* IFLA_GTP_RESTART_COUNT */
|
||||
}
|
||||
|
||||
static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
@ -826,6 +1005,8 @@ static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(skb, IFLA_GTP_ROLE, gtp->role))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u8(skb, IFLA_GTP_RESTART_COUNT, gtp->restart_count))
|
||||
goto nla_put_failure;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -7,8 +7,13 @@
|
||||
#define GTP0_PORT 3386
|
||||
#define GTP1U_PORT 2152
|
||||
|
||||
/* GTP messages types */
|
||||
#define GTP_ECHO_REQ 1 /* Echo Request */
|
||||
#define GTP_ECHO_RSP 2 /* Echo Response */
|
||||
#define GTP_TPDU 255
|
||||
|
||||
#define GTPIE_RECOVERY 14
|
||||
|
||||
struct gtp0_header { /* According to GSM TS 09.60. */
|
||||
__u8 flags;
|
||||
__u8 type;
|
||||
@ -27,6 +32,32 @@ struct gtp1_header { /* According to 3GPP TS 29.060. */
|
||||
__be32 tid;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gtp1_header_long { /* According to 3GPP TS 29.060. */
|
||||
__u8 flags;
|
||||
__u8 type;
|
||||
__be16 length;
|
||||
__be32 tid;
|
||||
__be16 seq;
|
||||
__u8 npdu;
|
||||
__u8 next;
|
||||
} __packed;
|
||||
|
||||
/* GTP Information Element */
|
||||
struct gtp_ie {
|
||||
__u8 tag;
|
||||
__u8 val;
|
||||
} __packed;
|
||||
|
||||
struct gtp0_packet {
|
||||
struct gtp0_header gtp0_h;
|
||||
struct gtp_ie ie;
|
||||
} __packed;
|
||||
|
||||
struct gtp1u_packet {
|
||||
struct gtp1_header_long gtp1u_h;
|
||||
struct gtp_ie ie;
|
||||
} __packed;
|
||||
|
||||
#define GTP1_F_NPDU 0x01
|
||||
#define GTP1_F_SEQ 0x02
|
||||
#define GTP1_F_EXTHDR 0x04
|
||||
|
@ -888,6 +888,7 @@ enum {
|
||||
IFLA_GTP_PDP_HASHSIZE,
|
||||
IFLA_GTP_ROLE,
|
||||
IFLA_GTP_CREATE_SOCKETS,
|
||||
IFLA_GTP_RESTART_COUNT,
|
||||
__IFLA_GTP_MAX,
|
||||
};
|
||||
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user