gtp: support SGSN-side tunnels
The GTP-tunnel driver is explicitly GGSN-side as it searches for PDP contexts based on the incoming packets _destination_ address. If we want to place ourselves on the SGSN side of the tunnel, then we want to be identifying PDP contexts based on _source_ address. Let it be noted that in a "real" configuration this module would never be used: the SGSN normally does not see IP packets as input. The justification for this functionality is for PGW load-testing applications where the input to the SGSN is locally generally IP traffic. This patch adds a "role" argument at GTP-link creation time to specify whether we are on the GGSN or SGSN side of the tunnel; this flag is then used to determine which part of the IP packet to use in determining the PDP context. Signed-off-by: Jonas Bonn <jonas@southpole.se> Acked-by: Pablo Neira Ayuso <pablo@netfilter.org> Acked-by: Harald Welte <laforge@gnumonks.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ae6336b57e
commit
91ed81f9ab
2 changed files with 37 additions and 12 deletions
|
@ -74,6 +74,7 @@ struct gtp_dev {
|
||||||
|
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
|
||||||
|
unsigned int role;
|
||||||
unsigned int hash_size;
|
unsigned int hash_size;
|
||||||
struct hlist_head *tid_hash;
|
struct hlist_head *tid_hash;
|
||||||
struct hlist_head *addr_hash;
|
struct hlist_head *addr_hash;
|
||||||
|
@ -154,8 +155,8 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
|
static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
|
||||||
unsigned int hdrlen)
|
unsigned int hdrlen, unsigned int role)
|
||||||
{
|
{
|
||||||
struct iphdr *iph;
|
struct iphdr *iph;
|
||||||
|
|
||||||
|
@ -164,27 +165,31 @@ static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
|
||||||
|
|
||||||
iph = (struct iphdr *)(skb->data + hdrlen);
|
iph = (struct iphdr *)(skb->data + hdrlen);
|
||||||
|
|
||||||
return iph->saddr == pctx->ms_addr_ip4.s_addr;
|
if (role == GTP_ROLE_SGSN)
|
||||||
|
return iph->daddr == pctx->ms_addr_ip4.s_addr;
|
||||||
|
else
|
||||||
|
return iph->saddr == pctx->ms_addr_ip4.s_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the inner IP source address in this packet is assigned to any
|
/* Check if the inner IP address in this packet is assigned to any
|
||||||
* existing mobile subscriber.
|
* existing mobile subscriber.
|
||||||
*/
|
*/
|
||||||
static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
|
static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
|
||||||
unsigned int hdrlen)
|
unsigned int hdrlen, unsigned int role)
|
||||||
{
|
{
|
||||||
switch (ntohs(skb->protocol)) {
|
switch (ntohs(skb->protocol)) {
|
||||||
case ETH_P_IP:
|
case ETH_P_IP:
|
||||||
return gtp_check_src_ms_ipv4(skb, pctx, hdrlen);
|
return gtp_check_ms_ipv4(skb, pctx, hdrlen, role);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen)
|
static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
|
||||||
|
unsigned int hdrlen, unsigned int role)
|
||||||
{
|
{
|
||||||
struct pcpu_sw_netstats *stats;
|
struct pcpu_sw_netstats *stats;
|
||||||
|
|
||||||
if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
|
if (!gtp_check_ms(skb, pctx, hdrlen, role)) {
|
||||||
netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
|
netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -239,7 +244,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gtp_rx(pctx, skb, hdrlen);
|
return gtp_rx(pctx, skb, hdrlen, gtp->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||||
|
@ -281,7 +286,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gtp_rx(pctx, skb, hdrlen);
|
return gtp_rx(pctx, skb, hdrlen, gtp->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gtp_encap_destroy(struct sock *sk)
|
static void gtp_encap_destroy(struct sock *sk)
|
||||||
|
@ -481,7 +486,11 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
|
||||||
* Prepend PDP header with TEI/TID from PDP ctx.
|
* Prepend PDP header with TEI/TID from PDP ctx.
|
||||||
*/
|
*/
|
||||||
iph = ip_hdr(skb);
|
iph = ip_hdr(skb);
|
||||||
pctx = ipv4_pdp_find(gtp, iph->daddr);
|
if (gtp->role == GTP_ROLE_SGSN)
|
||||||
|
pctx = ipv4_pdp_find(gtp, iph->saddr);
|
||||||
|
else
|
||||||
|
pctx = ipv4_pdp_find(gtp, iph->daddr);
|
||||||
|
|
||||||
if (!pctx) {
|
if (!pctx) {
|
||||||
netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
|
netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
|
||||||
&iph->daddr);
|
&iph->daddr);
|
||||||
|
@ -685,6 +694,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
|
||||||
[IFLA_GTP_FD0] = { .type = NLA_U32 },
|
[IFLA_GTP_FD0] = { .type = NLA_U32 },
|
||||||
[IFLA_GTP_FD1] = { .type = NLA_U32 },
|
[IFLA_GTP_FD1] = { .type = NLA_U32 },
|
||||||
[IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 },
|
[IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 },
|
||||||
|
[IFLA_GTP_ROLE] = { .type = NLA_U32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int gtp_validate(struct nlattr *tb[], struct nlattr *data[])
|
static int gtp_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||||
|
@ -810,6 +820,7 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
|
||||||
{
|
{
|
||||||
struct sock *sk1u = NULL;
|
struct sock *sk1u = NULL;
|
||||||
struct sock *sk0 = NULL;
|
struct sock *sk0 = NULL;
|
||||||
|
unsigned int role = GTP_ROLE_GGSN;
|
||||||
|
|
||||||
if (data[IFLA_GTP_FD0]) {
|
if (data[IFLA_GTP_FD0]) {
|
||||||
u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
|
u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
|
||||||
|
@ -830,8 +841,15 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data[IFLA_GTP_ROLE]) {
|
||||||
|
role = nla_get_u32(data[IFLA_GTP_ROLE]);
|
||||||
|
if (role > GTP_ROLE_SGSN)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
gtp->sk0 = sk0;
|
gtp->sk0 = sk0;
|
||||||
gtp->sk1u = sk1u;
|
gtp->sk1u = sk1u;
|
||||||
|
gtp->role = role;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -538,11 +538,18 @@ enum {
|
||||||
#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
|
#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
|
||||||
|
|
||||||
/* GTP section */
|
/* GTP section */
|
||||||
|
|
||||||
|
enum ifla_gtp_role {
|
||||||
|
GTP_ROLE_GGSN = 0,
|
||||||
|
GTP_ROLE_SGSN,
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
IFLA_GTP_UNSPEC,
|
IFLA_GTP_UNSPEC,
|
||||||
IFLA_GTP_FD0,
|
IFLA_GTP_FD0,
|
||||||
IFLA_GTP_FD1,
|
IFLA_GTP_FD1,
|
||||||
IFLA_GTP_PDP_HASHSIZE,
|
IFLA_GTP_PDP_HASHSIZE,
|
||||||
|
IFLA_GTP_ROLE,
|
||||||
__IFLA_GTP_MAX,
|
__IFLA_GTP_MAX,
|
||||||
};
|
};
|
||||||
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
|
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
|
||||||
|
|
Loading…
Reference in a new issue