@@ -71,6 +71,7 @@ struct gtp_dev {
struct net_device *dev;
+ enum ifla_gtp_role role;
unsigned int hash_size;
struct hlist_head *tid_hash;
struct hlist_head *addr_hash;
@@ -149,8 +150,8 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
return NULL;
}
-static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
- unsigned int hdrlen)
+static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
+ unsigned int hdrlen, enum ifla_gtp_role role)
{
struct iphdr *iph;
@@ -159,18 +160,21 @@ static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
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
* existing mobile subscriber.
*/
-static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
- unsigned int hdrlen)
+static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
+ unsigned int hdrlen, enum ifla_gtp_role role)
{
switch (ntohs(skb->protocol)) {
case ETH_P_IP:
- return gtp_check_src_ms_ipv4(skb, pctx, hdrlen);
+ return gtp_check_ms_ipv4(skb, pctx, hdrlen, role);
}
return false;
}
@@ -204,7 +208,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
goto out_rcu;
}
- if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
+ if (!gtp_check_ms(skb, pctx, hdrlen, gtp->role)) {
netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
ret = -1;
goto out_rcu;
@@ -261,7 +265,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
goto out_rcu;
}
- if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
+ if (!gtp_check_ms(skb, pctx, hdrlen, gtp->role)) {
netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
ret = -1;
goto out_rcu;
@@ -490,7 +494,11 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
* Prepend PDP header with TEI/TID from PDP ctx.
*/
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) {
netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
&iph->daddr);
@@ -665,12 +673,26 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
int hashsize, err, fd0, fd1;
struct gtp_dev *gtp;
struct gtp_net *gn;
+ unsigned int role;
+
+ /* The default role is GGSN, and for backwards compatibility
+ * reasons, the lack of a IFLA_GTP_ROLE IE means that we're
+ * operating in GGSN role. */
+ if (data[IFLA_GTP_ROLE]) {
+ role = nla_get_u32(data[IFLA_GTP_ROLE]);
+ if (role != GTP_ROLE_GGSN &&
+ role != GTP_ROLE_SGSN)
+ return -EINVAL;
+ } else
+ role = GTP_ROLE_GGSN;
if (!data[IFLA_GTP_FD0] || !data[IFLA_GTP_FD1])
return -EINVAL;
gtp = netdev_priv(dev);
+ gtp->role = role;
+
fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
fd1 = nla_get_u32(data[IFLA_GTP_FD1]);
@@ -722,6 +744,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
[IFLA_GTP_FD0] = { .type = NLA_U32 },
[IFLA_GTP_FD1] = { .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[])
@@ -19,7 +19,7 @@ enum gtp_attrs {
GTPA_LINK,
GTPA_VERSION,
GTPA_TID, /* for GTPv0 only */
- GTPA_SGSN_ADDRESS,
+ GTPA_SGSN_ADDRESS, /* Remote GSN, either SGSN or GGSN */
GTPA_MS_ADDRESS,
GTPA_FLOW,
GTPA_NET_NS_FD,
@@ -536,11 +536,18 @@ enum {
#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
/* GTP section */
+
+enum ifla_gtp_role {
+ GTP_ROLE_GGSN = 0,
+ GTP_ROLE_SGSN,
+};
+
enum {
IFLA_GTP_UNSPEC,
IFLA_GTP_FD0,
IFLA_GTP_FD1,
IFLA_GTP_PDP_HASHSIZE,
+ IFLA_GTP_ROLE,
__IFLA_GTP_MAX,
};
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)