From patchwork Mon Nov 16 15:06:52 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Schultz X-Patchwork-Id: 545062 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (tmp.osmocom.org [144.76.43.76]) by ozlabs.org (Postfix) with ESMTP id D34AF140D97 for ; Tue, 17 Nov 2015 02:07:27 +1100 (AEDT) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id A56E09CB7; Mon, 16 Nov 2015 15:07:26 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail.tpip.net (mail.tpip.net [92.43.49.48]) by lists.osmocom.org (Postfix) with ESMTP id 508459C96 for ; Mon, 16 Nov 2015 15:07:22 +0000 (UTC) Received: from office.tpip.net (office.tpip.net [92.43.51.2]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.tpip.net (Postfix) with ESMTPS id 375954F4E3; Mon, 16 Nov 2015 15:07:08 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id 752C9A2CAA; Mon, 16 Nov 2015 16:07:09 +0100 (CET) Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id ypwhLr1InvVq; Mon, 16 Nov 2015 16:07:08 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id A0788A2CA9; Mon, 16 Nov 2015 16:07:08 +0100 (CET) X-Virus-Scanned: amavisd-new at tpip.net Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id atq81UrNB9ZN; Mon, 16 Nov 2015 16:07:08 +0100 (CET) Received: from alice.tpip.org (unknown [192.168.13.53]) by office.tpip.net (Postfix) with ESMTPSA id 499EDA2CAB; Mon, 16 Nov 2015 16:07:08 +0100 (CET) From: Andreas Schultz To: openbsc@lists.osmocom.org Subject: [PATCH 11/16] gtp: Split TID handling got GTPv0 and GTPv1 Date: Mon, 16 Nov 2015 16:06:52 +0100 Message-Id: <1447686417-3979-12-git-send-email-aschultz@tpip.net> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1447686417-3979-1-git-send-email-aschultz@tpip.net> References: <1447686417-3979-1-git-send-email-aschultz@tpip.net> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Pablo Neira Ayuso Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" GTPv1 tunnel use a separate TEI for each direction. Add a union to hold TID for v0 and v1 separately. Signed-off-by: Andreas Schultz --- gtp.c | 211 +++++++++++++++++++++++++++++++++++---------------------------- gtp_nl.h | 2 + 2 files changed, 118 insertions(+), 95 deletions(-) diff --git a/gtp.c b/gtp.c index 2ac6431..a4be31d 100644 --- a/gtp.c +++ b/gtp.c @@ -39,7 +39,17 @@ struct pdp_ctx { struct hlist_node hlist_tid; struct hlist_node hlist_addr; - u64 tid; + union { + uint64_t tid; + struct { + uint64_t tid; + u16 flow; + } v0 __attribute__((__packed__)); + struct { + uint32_t i_tid; + uint32_t o_tid; + } v1 __attribute__((__packed__)); + } u; u8 gtp_version; u16 af; @@ -53,7 +63,6 @@ struct pdp_ctx { struct in_addr ip4; } sgsn_addr; - u16 flow; atomic_t tx_seq; struct rcu_head rcu_head; @@ -113,7 +122,7 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_instance *gti, u64 tid) head = >i->tid_hash[gtp0_hashfn(tid) % gti->hash_size]; hlist_for_each_entry_rcu(pdp, head, hlist_tid) { - if (pdp->gtp_version == GTP_V0 && pdp->tid == tid) + if (pdp->gtp_version == GTP_V0 && pdp->u.v0.tid == tid) return pdp; } @@ -129,7 +138,7 @@ static struct pdp_ctx *gtp1_pdp_find(struct gtp_instance *gti, u32 tid) head = >i->tid_hash[gtp1u_hashfn(tid) % gti->hash_size]; hlist_for_each_entry_rcu(pdp, head, hlist_tid) { - if (pdp->gtp_version == GTP_V1 && pdp->tid == tid) + if (pdp->gtp_version == GTP_V1 && pdp->u.v1.i_tid == tid) return pdp; } @@ -456,10 +465,10 @@ gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) gtp0->type = GTP_TPDU; gtp0->length = htons(payload_len); gtp0->seq = htons((atomic_inc_return(&pctx->tx_seq)-1) % 0xffff); - gtp0->flow = htons(pctx->flow); + gtp0->flow = htons(pctx->u.v0.flow); gtp0->number = 0xFF; gtp0->spare[0] = gtp0->spare[1] = gtp0->spare[2] = 0xFF; - gtp0->tid = cpu_to_be64(pctx->tid); + gtp0->tid = cpu_to_be64(pctx->u.v0.tid); } static inline void @@ -480,7 +489,7 @@ gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) gtp1->flags = 0x38; /* V1, GTP-non-prime */ gtp1->type = GTP_TPDU; gtp1->length = htons(payload_len); - gtp1->tid = htonl((u32)pctx->tid); + gtp1->tid = htonl(pctx->u.v1.o_tid); /* TODO: Suppport for extension header, sequence number and N-PDU. * Update the length field if any of them is available. @@ -1021,43 +1030,45 @@ static struct net_device *gtp_find_dev(struct net *net, int ifindex) return NULL; } -static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) { - struct gtp_instance *gti = netdev_priv(dev); - struct pdp_ctx *pctx; - u16 flow = 0; - u32 gtp_version, sgsn_addr, ms_addr, hash_ms, hash_tid; - u64 tid; - bool found = false; + pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); + pctx->af = AF_INET; + pctx->sgsn_addr.ip4.s_addr = + nla_get_u32(info->attrs[GTPA_SGSN_ADDRESS]); + pctx->ms_addr.ip4.s_addr = + nla_get_u32(info->attrs[GTPA_MS_ADDRESS]); - gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - switch (gtp_version) { + switch (pctx->gtp_version) { case GTP_V0: - case GTP_V1: + + /* 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. + */ + pctx->u.v0.tid = nla_get_u64(info->attrs[GTPA_TID]); + pctx->u.v0.flow = nla_get_u16(info->attrs[GTPA_FLOW]); break; - default: - return -EINVAL; - } - tid = nla_get_u64(info->attrs[GTPA_TID]); - /* GTPv1 allows 32-bits tunnel IDs */ - if (gtp_version == GTP_V1 && tid > UINT_MAX) - return -EINVAL; + case GTP_V1: + pctx->u.v1.i_tid = nla_get_u32(info->attrs[GTPA_I_TID]); + pctx->u.v1.o_tid = nla_get_u32(info->attrs[GTPA_O_TID]); - /* 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; + break; - flow = nla_get_u16(info->attrs[GTPA_FLOW]); + default: + break; } +} - sgsn_addr = nla_get_u32(info->attrs[GTPA_SGSN_ADDRESS]); - ms_addr = nla_get_u32(info->attrs[GTPA_MS_ADDRESS]); +static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +{ + struct gtp_instance *gti = netdev_priv(dev); + struct pdp_ctx *pctx; + u32 ms_addr, hash_ms, hash_tid = 0; + bool found = false; + ms_addr = nla_get_u32(info->attrs[GTPA_MS_ADDRESS]); hash_ms = ipv4_hashfn(ms_addr) % gti->hash_size; hlist_for_each_entry_rcu(pctx, >i->addr_hash[hash_ms], hlist_addr) { @@ -1073,47 +1084,50 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; - pctx->af = AF_INET; - pctx->gtp_version = gtp_version; - pctx->tid = tid; - pctx->sgsn_addr.ip4.s_addr = sgsn_addr; - pctx->ms_addr.ip4.s_addr = ms_addr; + ipv4_pdp_fill(pctx, info); - netdev_dbg(dev, "update tunnel id = %llx (pdp %p)\n", - tid, pctx); + if (pctx->gtp_version == GTP_V0) + netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n", + pctx->u.v0.tid, pctx); + else if (pctx->gtp_version == GTP_V1) + netdev_dbg(dev, "GTPv1-U: update tunnel id = %x/%x (pdp %p)\n", + pctx->u.v1.i_tid, pctx->u.v1.o_tid, pctx); return 0; + } pctx = kmalloc(sizeof(struct pdp_ctx), GFP_KERNEL); if (pctx == NULL) return -ENOMEM; - pctx->af = AF_INET; - pctx->gtp_version = gtp_version; - pctx->tid = tid; - pctx->sgsn_addr.ip4.s_addr = sgsn_addr; - pctx->ms_addr.ip4.s_addr = ms_addr; - pctx->flow = flow; + ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); - switch (gtp_version) { + switch (pctx->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; + hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gti->hash_size; break; + case GTP_V1: - hash_tid = gtp1u_hashfn(tid) % gti->hash_size; + hash_tid = gtp1u_hashfn(pctx->u.v1.i_tid) % gti->hash_size; break; } + hlist_add_head_rcu(&pctx->hlist_addr, >i->addr_hash[hash_ms]); hlist_add_head_rcu(&pctx->hlist_tid, >i->tid_hash[hash_tid]); - netdev_dbg(dev, "adding tunnel id = %llx (pdp %p)\n", tid, pctx); + if (pctx->gtp_version == GTP_V0) + netdev_dbg(dev, "GTPv0-U: adding tunnel id = %llx (pdp %p)\n", + pctx->u.v0.tid, pctx); + else if (pctx->gtp_version == GTP_V1) + netdev_dbg(dev, "GTPv1-U: adding tunnel id = %x/%x (pdp %p)\n", + pctx->u.v1.i_tid, pctx->u.v1.o_tid, pctx); return 0; } @@ -1126,9 +1140,25 @@ static int gtp_genl_tunnel_new(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || !info->attrs[GTPA_SGSN_ADDRESS] || - !info->attrs[GTPA_MS_ADDRESS] || - !info->attrs[GTPA_TID]) + !info->attrs[GTPA_MS_ADDRESS]) + return -EINVAL; + + switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + case GTP_V0: + if (!info->attrs[GTPA_TID] || + !info->attrs[GTPA_FLOW]) + return -EINVAL; + break; + + case GTP_V1: + if (!info->attrs[GTPA_I_TID] || + !info->attrs[GTPA_O_TID]) + return -EINVAL; + break; + + default: return -EINVAL; + } net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); if (net == NULL) @@ -1148,28 +1178,9 @@ static int gtp_genl_tunnel_delete(struct sk_buff *skb, struct genl_info *info) struct gtp_instance *gti; struct net_device *dev; struct pdp_ctx *pctx; - u32 gtp_version; - u64 tid; if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK] || - !info->attrs[GTPA_SGSN_ADDRESS] || - !info->attrs[GTPA_MS_ADDRESS] || - !info->attrs[GTPA_TID]) - return -EINVAL; - - gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - switch (gtp_version) { - case GTP_V0: - case GTP_V1: - break; - default: - return -EINVAL; - } - - tid = nla_get_u64(info->attrs[GTPA_TID]); - /* GTPv1 allows 32-bits tunnel IDs */ - if (gtp_version == GTP_V1 && tid > UINT_MAX) + !info->attrs[GTPA_LINK]) return -EINVAL; net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); @@ -1183,20 +1194,32 @@ static int gtp_genl_tunnel_delete(struct sk_buff *skb, struct genl_info *info) gti = netdev_priv(dev); - switch (gtp_version) { + switch (nla_get_u32(info->attrs[GTPA_VERSION])) { case GTP_V0: + if (!info->attrs[GTPA_TID]) + return -EINVAL; pctx = gtp0_pdp_find(gti, nla_get_u64(info->attrs[GTPA_TID])); break; + case GTP_V1: - pctx = gtp1_pdp_find(gti, nla_get_u64(info->attrs[GTPA_TID])); + if (!info->attrs[GTPA_I_TID]) + return -EINVAL; + pctx = gtp1_pdp_find(gti, nla_get_u64(info->attrs[GTPA_I_TID])); break; + + default: + return -EINVAL; } if (pctx == NULL) return -ENOENT; - netdev_dbg(dev, "deleting tunnel with ID %lld\n", - (unsigned long long) nla_get_u64(info->attrs[GTPA_TID])); + if (pctx->gtp_version == GTP_V0) + netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + pctx->u.v0.tid, pctx); + else if (pctx->gtp_version == GTP_V1) + netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + pctx->u.v1.i_tid, pctx->u.v1.o_tid, pctx); hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); @@ -1228,9 +1251,12 @@ 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) || (pctx->gtp_version == GTP_V0 && - nla_put_u16(skb, GTPA_FLOW, pctx->flow))) + nla_put_u64(skb, GTPA_TID, pctx->u.v0.tid) && + nla_put_u16(skb, GTPA_FLOW, pctx->u.v0.flow)) || + (pctx->gtp_version == GTP_V1 && + nla_put_u32(skb, GTPA_I_TID, pctx->u.v1.i_tid) && + nla_put_u32(skb, GTPA_O_TID, pctx->u.v1.o_tid))) goto nla_put_failure; genlmsg_end(skb, genlh); @@ -1277,23 +1303,16 @@ static int gtp_genl_tunnel_get(struct sk_buff *skb, struct genl_info *info) gti = netdev_priv(dev); rcu_read_lock(); - if (info->attrs[GTPA_TID]) { + if (gtp_version == GTP_V0 && + info->attrs[GTPA_TID]) { u64 tid = nla_get_u64(info->attrs[GTPA_TID]); - /* GTPv1 allows 32-bits tunnel IDs */ - if (gtp_version == GTP_V1 && tid > UINT_MAX) { - err = -EINVAL; - goto err_unlock; - } + pctx = gtp0_pdp_find(gti, tid); + } else if (gtp_version == GTP_V1 && + info->attrs[GTPA_I_TID]) { + u32 tid = nla_get_u32(info->attrs[GTPA_I_TID]); - switch (gtp_version) { - case GTP_V0: - pctx = gtp0_pdp_find(gti, tid); - break; - case GTP_V1: - pctx = gtp1_pdp_find(gti, tid); - break; - } + pctx = gtp1_pdp_find(gti, tid); } else if (info->attrs[GTPA_MS_ADDRESS]) { u32 ip = nla_get_u32(info->attrs[GTPA_MS_ADDRESS]); @@ -1347,7 +1366,7 @@ gtp_genl_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) for (i = k; i < gti->hash_size; i++) { hlist_for_each_entry_rcu(pctx, >i->tid_hash[i], hlist_tid) { - if (tid && tid != pctx->tid) + if (tid && tid != pctx->u.tid) continue; else tid = 0; @@ -1358,7 +1377,7 @@ gtp_genl_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) cb->nlh->nlmsg_type, pctx); if (ret < 0) { cb->args[0] = i; - cb->args[1] = pctx->tid; + cb->args[1] = pctx->u.tid; cb->args[2] = (unsigned long)gti; goto out; } @@ -1378,6 +1397,8 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { [GTPA_MS_ADDRESS] = { .type = NLA_NESTED, }, [GTPA_FLOW] = { .type = NLA_U16, }, [GTPA_NET_NS_FD] = { .type = NLA_U32, }, + [GTPA_I_TID] = { .type = NLA_U32, }, + [GTPA_O_TID] = { .type = NLA_U32, }, }; static const struct genl_ops gtp_genl_ops[] = { diff --git a/gtp_nl.h b/gtp_nl.h index c20666d..370576b 100644 --- a/gtp_nl.h +++ b/gtp_nl.h @@ -41,6 +41,8 @@ enum gtp_attrs { GTPA_MS_ADDRESS, GTPA_FLOW, GTPA_NET_NS_FD, + GTPA_I_TID, + GTPA_O_TID, __GTPA_MAX, }; #define GTPA_MAX (__GTPA_MAX + 1)