summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2014-03-20 13:41:36 +0100
committerPablo Neira Ayuso <pablo@gnumonks.org>2014-03-20 13:43:04 +0100
commita03b3754d60f450e47d1d89b00080d0c3af31b35 (patch)
tree146a98aa1589c450ce44c896edd3ef7211db1a07
parent9b649117b698046f48e88e1007390f58018bbdf3 (diff)
gtp: set flow ID for PDP context created throught GTPv0
This field is very important, downlink and uplink packet shall use the same flow ID.
-rw-r--r--gtp.c29
-rw-r--r--gtp_nl.h1
2 files changed, 25 insertions, 5 deletions
diff --git a/gtp.c b/gtp.c
index fb048b3..85bb6f6 100644
--- a/gtp.c
+++ b/gtp.c
@@ -430,7 +430,7 @@ gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx, int payload_len)
gtp0->type = GTP_TPDU;
gtp0->length = htons(payload_len);
gtp0->seq = htons((atomic_inc_return(&pctx->tx_seq)-1) % 0xffff);
- gtp0->flow = htonl(pctx->flow);
+ gtp0->flow = htons(pctx->flow);
gtp0->number = 0xFF;
gtp0->spare[0] = gtp0->spare[1] = gtp0->spare[2] = 0xFF;
gtp0->tid = cpu_to_be64(pctx->tid);
@@ -952,10 +952,9 @@ static struct net_device *gtp_find_dev(int ifindex)
static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
{
struct gtp_instance *gti = netdev_priv(dev);
- u32 hash_ms;
- u32 hash_tid;
struct pdp_ctx *pctx;
- u32 gtp_version, link, sgsn_addr, ms_addr;
+ u16 flow = 0;
+ u32 gtp_version, link, sgsn_addr, ms_addr, hash_ms, hash_tid;
u64 tid;
bool found = false;
@@ -973,6 +972,17 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
if (gtp_version == GTP_V1 && tid > UINT_MAX)
return -EINVAL;
+ /* According to TS 09.60, sections 7.5.1 and 7.5.2, the flow label
+ * needs to be the same for uplink and downlink packets, so let's
+ * annotate this.
+ */
+ if (gtp_version == GTP_V0) {
+ if (!info->attrs[GTPA_FLOW])
+ return -EINVAL;
+
+ flow = nla_get_u16(info->attrs[GTPA_FLOW]);
+ }
+
link = nla_get_u32(info->attrs[GTPA_LINK]);
sgsn_addr = nla_get_u32(info->attrs[GTPA_SGSN_ADDRESS]);
ms_addr = nla_get_u32(info->attrs[GTPA_MS_ADDRESS]);
@@ -1013,10 +1023,16 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
pctx->tid = tid;
pctx->sgsn_addr.ip4.s_addr = sgsn_addr;
pctx->ms_addr.ip4.s_addr = ms_addr;
+ pctx->flow = flow;
atomic_set(&pctx->tx_seq, 0);
switch (gtp_version) {
case GTP_V0:
+ /* TS 09.60: "The flow label identifies unambiguously a GTP
+ * flow.". We use the tid for this instead, I cannot find a
+ * situation in which this doesn't unambiguosly identify the
+ * PDP context.
+ */
hash_tid = gtp0_hashfn(tid) % gti->hash_size;
break;
case GTP_V1:
@@ -1131,7 +1147,9 @@ 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_SGSN_ADDRESS, pctx->sgsn_addr.ip4.s_addr) ||
nla_put_u32(skb, GTPA_MS_ADDRESS, pctx->ms_addr.ip4.s_addr) ||
- nla_put_u64(skb, GTPA_TID, pctx->tid))
+ nla_put_u64(skb, GTPA_TID, pctx->tid) ||
+ (pctx->gtp_version == GTP_V0 &&
+ nla_put_u16(skb, GTPA_FLOW, pctx->flow)))
goto nla_put_failure;
return genlmsg_end(skb, genlh);
@@ -1269,6 +1287,7 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
[GTPA_TID] = { .type = NLA_U64, },
[GTPA_SGSN_ADDRESS] = { .type = NLA_NESTED, },
[GTPA_MS_ADDRESS] = { .type = NLA_NESTED, },
+ [GTPA_FLOW] = { .type = NLA_U16, },
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
diff --git a/gtp_nl.h b/gtp_nl.h
index ff45fba..7bdd2f5 100644
--- a/gtp_nl.h
+++ b/gtp_nl.h
@@ -39,6 +39,7 @@ enum gtp_attrs {
GTPA_TID, /* 64 bits for GTPv1 */
GTPA_SGSN_ADDRESS,
GTPA_MS_ADDRESS,
+ GTPA_FLOW,
__GTPA_MAX,
};
#define GTPA_MAX (__GTPA_MAX + 1)