diff options
-rw-r--r-- | drivers/net/gtp.c | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 84ff502a38bc..8e861778c4e9 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -136,7 +136,8 @@ static inline u32 ipv4_hashfn(__be32 ip) static u32 ipv6_hashfn(const struct in6_addr *ip6) { - return jhash(ip6, sizeof(*ip6), gtp_h_initval); + return jhash_2words((__force u32)ip6->s6_addr32[0], + (__force u32)ip6->s6_addr32[1], gtp_h_initval); } /* 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) return NULL; } +/* 3GPP TS 29.060: PDN Connection: the association between a MS represented by + * [...] one IPv6 *prefix* and a PDN represented by an APN. + * + * Then, 3GPP TS 29.061, Section 11.2.1.3 says: 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. + * + * Finally, 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). + */ +static bool ipv6_pdp_addr_equal(const struct in6_addr *a, + const struct in6_addr *b) +{ + return a->s6_addr32[0] == b->s6_addr32[0] && + a->s6_addr32[1] == b->s6_addr32[1]; +} + static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp, const struct in6_addr *ms_addr) { @@ -198,7 +217,7 @@ static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp, hlist_for_each_entry_rcu(pdp, head, hlist_addr) { if (pdp->af == AF_INET6 && - memcmp(&pdp->ms.addr6, ms_addr, sizeof(struct in6_addr)) == 0) + ipv6_pdp_addr_equal(&pdp->ms.addr6, ms_addr)) return pdp; } @@ -233,14 +252,12 @@ static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx, ip6h = (struct ipv6hdr *)(skb->data + hdrlen); if (role == GTP_ROLE_SGSN) { - ret = memcmp(&ip6h->daddr, &pctx->ms.addr6, - sizeof(struct in6_addr)); + ret = ipv6_pdp_addr_equal(&ip6h->daddr, &pctx->ms.addr6); } else { - ret = memcmp(&ip6h->saddr, &pctx->ms.addr6, - sizeof(struct in6_addr)); + ret = ipv6_pdp_addr_equal(&ip6h->saddr, &pctx->ms.addr6); } - return ret == 0; + return ret; } /* 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) gtp_pdp_fill(pctx, info); } -static void ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) +static bool ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) { pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]); pctx->ms.addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]); + if (pctx->ms.addr6.s6_addr32[2] || + pctx->ms.addr6.s6_addr32[3]) + return false; + gtp_pdp_fill(pctx, info); + + return true; } 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, ipv4_pdp_fill(pctx, info); break; case AF_INET6: - ipv6_pdp_fill(pctx, info); + if (!ipv6_pdp_fill(pctx, info)) + return ERR_PTR(-EADDRNOTAVAIL); break; } @@ -1785,7 +1809,11 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, return ERR_PTR(-EINVAL); } - ipv6_pdp_fill(pctx, info); + if (!ipv6_pdp_fill(pctx, info)) { + sock_put(sk); + kfree(pctx); + return ERR_PTR(-EADDRNOTAVAIL); + } break; } atomic_set(&pctx->tx_seq, 0); @@ -1919,6 +1947,10 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net, } else if (nla[GTPA_MS_ADDR6]) { struct in6_addr addr = nla_get_in6_addr(nla[GTPA_MS_ADDR6]); + if (addr.s6_addr32[2] || + addr.s6_addr32[3]) + return ERR_PTR(-EADDRNOTAVAIL); + return ipv6_pdp_find(gtp, &addr); } else if (nla[GTPA_VERSION]) { u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]); |