Skip to content

Commit c6461ec

Browse files
committed
gtp: use IPv6 address /64 prefix for UE/MS
Harald Welte reports that according to 3GPP TS 29.060: PDN Connection: the association between a MS represented by one IPv4 address and/or one IPv6 prefix and a PDN represented by an APN. this clearly states that IPv4 is a single address while IPv6 is a single prefix. Then, 3GPP TS 29.061, Section 11.2.1.3: For APNs that are configured for IPv6 address allocation, the GGSN/P-GW shall only use the Prefix part of the IPv6 address for forwarding of mobile terminated IP packets. The size of the prefix shall be according to the maximum prefix length for a global IPv6 address as specified in the IPv6 Addressing Architecture, see RFC 4291 [82]. RFC 4291 section 2.5.4 states All Global Unicast addresses other than those that start with binary 000 have a 64-bit interface ID field (i.e., n + m = 64) ... 3GPP TS 29.61 Section 11.2.1.3.2a: In the procedure in the cases of using GTP-based S5/S8, P-GW acts as an access router, and allocates to a UE a globally unique /64 IPv6 prefix if the PLMN allocates the prefix. Therefore, compare IPv6 address /64 prefix only since MS/UE is not a single address like in the IPv4 case. Reject IPv6 address with EADDRNOTAVAIL if it lower 64 bits of the IPv6 address from the control plane are set. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 999cb27 commit c6461ec

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

drivers/net/gtp.c

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ static inline u32 ipv4_hashfn(__be32 ip)
136136

137137
static u32 ipv6_hashfn(const struct in6_addr *ip6)
138138
{
139-
return jhash(ip6, sizeof(*ip6), gtp_h_initval);
139+
return jhash_2words((__force u32)ip6->s6_addr32[0],
140+
(__force u32)ip6->s6_addr32[1], gtp_h_initval);
140141
}
141142

142143
/* Resolve a PDP context structure based on the 64bit TID. */
@@ -188,6 +189,24 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
188189
return NULL;
189190
}
190191

192+
/* 3GPP TS 29.060: PDN Connection: the association between a MS represented by
193+
* [...] one IPv6 *prefix* and a PDN represented by an APN.
194+
*
195+
* Then, 3GPP TS 29.061, Section 11.2.1.3 says: The size of the prefix shall be
196+
* according to the maximum prefix length for a global IPv6 address as
197+
* specified in the IPv6 Addressing Architecture, see RFC 4291.
198+
*
199+
* Finally, RFC 4291 section 2.5.4 states: All Global Unicast addresses other
200+
* than those that start with binary 000 have a 64-bit interface ID field
201+
* (i.e., n + m = 64).
202+
*/
203+
static bool ipv6_pdp_addr_equal(const struct in6_addr *a,
204+
const struct in6_addr *b)
205+
{
206+
return a->s6_addr32[0] == b->s6_addr32[0] &&
207+
a->s6_addr32[1] == b->s6_addr32[1];
208+
}
209+
191210
static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,
192211
const struct in6_addr *ms_addr)
193212
{
@@ -198,7 +217,7 @@ static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,
198217

199218
hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
200219
if (pdp->af == AF_INET6 &&
201-
memcmp(&pdp->ms.addr6, ms_addr, sizeof(struct in6_addr)) == 0)
220+
ipv6_pdp_addr_equal(&pdp->ms.addr6, ms_addr))
202221
return pdp;
203222
}
204223

@@ -233,14 +252,12 @@ static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,
233252
ip6h = (struct ipv6hdr *)(skb->data + hdrlen);
234253

235254
if (role == GTP_ROLE_SGSN) {
236-
ret = memcmp(&ip6h->daddr, &pctx->ms.addr6,
237-
sizeof(struct in6_addr));
255+
ret = ipv6_pdp_addr_equal(&ip6h->daddr, &pctx->ms.addr6);
238256
} else {
239-
ret = memcmp(&ip6h->saddr, &pctx->ms.addr6,
240-
sizeof(struct in6_addr));
257+
ret = ipv6_pdp_addr_equal(&ip6h->saddr, &pctx->ms.addr6);
241258
}
242259

243-
return ret == 0;
260+
return ret;
244261
}
245262

246263
/* Check if the inner IP address in this packet is assigned to any
@@ -1652,11 +1669,17 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
16521669
gtp_pdp_fill(pctx, info);
16531670
}
16541671

1655-
static void ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
1672+
static bool ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
16561673
{
16571674
pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]);
16581675
pctx->ms.addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
1676+
if (pctx->ms.addr6.s6_addr32[2] ||
1677+
pctx->ms.addr6.s6_addr32[3])
1678+
return false;
1679+
16591680
gtp_pdp_fill(pctx, info);
1681+
1682+
return true;
16601683
}
16611684

16621685
static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
@@ -1742,7 +1765,8 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
17421765
ipv4_pdp_fill(pctx, info);
17431766
break;
17441767
case AF_INET6:
1745-
ipv6_pdp_fill(pctx, info);
1768+
if (!ipv6_pdp_fill(pctx, info))
1769+
return ERR_PTR(-EADDRNOTAVAIL);
17461770
break;
17471771
}
17481772

@@ -1785,7 +1809,11 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
17851809
return ERR_PTR(-EINVAL);
17861810
}
17871811

1788-
ipv6_pdp_fill(pctx, info);
1812+
if (!ipv6_pdp_fill(pctx, info)) {
1813+
sock_put(sk);
1814+
kfree(pctx);
1815+
return ERR_PTR(-EADDRNOTAVAIL);
1816+
}
17891817
break;
17901818
}
17911819
atomic_set(&pctx->tx_seq, 0);
@@ -1919,6 +1947,10 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
19191947
} else if (nla[GTPA_MS_ADDR6]) {
19201948
struct in6_addr addr = nla_get_in6_addr(nla[GTPA_MS_ADDR6]);
19211949

1950+
if (addr.s6_addr32[2] ||
1951+
addr.s6_addr32[3])
1952+
return ERR_PTR(-EADDRNOTAVAIL);
1953+
19221954
return ipv6_pdp_find(gtp, &addr);
19231955
} else if (nla[GTPA_VERSION]) {
19241956
u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]);

0 commit comments

Comments
 (0)