162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* GTP according to GSM TS 09.60 / 3GPP TS 29.060 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * (C) 2012-2014 by sysmocom - s.f.m.c. GmbH 562306a36Sopenharmony_ci * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Harald Welte <hwelte@sysmocom.de> 862306a36Sopenharmony_ci * Pablo Neira Ayuso <pablo@netfilter.org> 962306a36Sopenharmony_ci * Andreas Schultz <aschultz@travelping.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/udp.h> 1762306a36Sopenharmony_ci#include <linux/rculist.h> 1862306a36Sopenharmony_ci#include <linux/jhash.h> 1962306a36Sopenharmony_ci#include <linux/if_tunnel.h> 2062306a36Sopenharmony_ci#include <linux/net.h> 2162306a36Sopenharmony_ci#include <linux/file.h> 2262306a36Sopenharmony_ci#include <linux/gtp.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <net/net_namespace.h> 2562306a36Sopenharmony_ci#include <net/protocol.h> 2662306a36Sopenharmony_ci#include <net/ip.h> 2762306a36Sopenharmony_ci#include <net/udp.h> 2862306a36Sopenharmony_ci#include <net/udp_tunnel.h> 2962306a36Sopenharmony_ci#include <net/icmp.h> 3062306a36Sopenharmony_ci#include <net/xfrm.h> 3162306a36Sopenharmony_ci#include <net/genetlink.h> 3262306a36Sopenharmony_ci#include <net/netns/generic.h> 3362306a36Sopenharmony_ci#include <net/gtp.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* An active session for the subscriber. */ 3662306a36Sopenharmony_cistruct pdp_ctx { 3762306a36Sopenharmony_ci struct hlist_node hlist_tid; 3862306a36Sopenharmony_ci struct hlist_node hlist_addr; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci union { 4162306a36Sopenharmony_ci struct { 4262306a36Sopenharmony_ci u64 tid; 4362306a36Sopenharmony_ci u16 flow; 4462306a36Sopenharmony_ci } v0; 4562306a36Sopenharmony_ci struct { 4662306a36Sopenharmony_ci u32 i_tei; 4762306a36Sopenharmony_ci u32 o_tei; 4862306a36Sopenharmony_ci } v1; 4962306a36Sopenharmony_ci } u; 5062306a36Sopenharmony_ci u8 gtp_version; 5162306a36Sopenharmony_ci u16 af; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct in_addr ms_addr_ip4; 5462306a36Sopenharmony_ci struct in_addr peer_addr_ip4; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci struct sock *sk; 5762306a36Sopenharmony_ci struct net_device *dev; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci atomic_t tx_seq; 6062306a36Sopenharmony_ci struct rcu_head rcu_head; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* One instance of the GTP device. */ 6462306a36Sopenharmony_cistruct gtp_dev { 6562306a36Sopenharmony_ci struct list_head list; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci struct sock *sk0; 6862306a36Sopenharmony_ci struct sock *sk1u; 6962306a36Sopenharmony_ci u8 sk_created; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci struct net_device *dev; 7262306a36Sopenharmony_ci struct net *net; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci unsigned int role; 7562306a36Sopenharmony_ci unsigned int hash_size; 7662306a36Sopenharmony_ci struct hlist_head *tid_hash; 7762306a36Sopenharmony_ci struct hlist_head *addr_hash; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci u8 restart_count; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct echo_info { 8362306a36Sopenharmony_ci struct in_addr ms_addr_ip4; 8462306a36Sopenharmony_ci struct in_addr peer_addr_ip4; 8562306a36Sopenharmony_ci u8 gtp_version; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned int gtp_net_id __read_mostly; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct gtp_net { 9162306a36Sopenharmony_ci struct list_head gtp_dev_list; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic u32 gtp_h_initval; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct genl_family gtp_genl_family; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cienum gtp_multicast_groups { 9962306a36Sopenharmony_ci GTP_GENL_MCGRP, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const struct genl_multicast_group gtp_genl_mcgrps[] = { 10362306a36Sopenharmony_ci [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME }, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void pdp_context_delete(struct pdp_ctx *pctx); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline u32 gtp0_hashfn(u64 tid) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u32 *tid32 = (u32 *) &tid; 11162306a36Sopenharmony_ci return jhash_2words(tid32[0], tid32[1], gtp_h_initval); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic inline u32 gtp1u_hashfn(u32 tid) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return jhash_1word(tid, gtp_h_initval); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline u32 ipv4_hashfn(__be32 ip) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return jhash_1word((__force u32)ip, gtp_h_initval); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* Resolve a PDP context structure based on the 64bit TID. */ 12562306a36Sopenharmony_cistatic struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct hlist_head *head; 12862306a36Sopenharmony_ci struct pdp_ctx *pdp; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci head = >p->tid_hash[gtp0_hashfn(tid) % gtp->hash_size]; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci hlist_for_each_entry_rcu(pdp, head, hlist_tid) { 13362306a36Sopenharmony_ci if (pdp->gtp_version == GTP_V0 && 13462306a36Sopenharmony_ci pdp->u.v0.tid == tid) 13562306a36Sopenharmony_ci return pdp; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci return NULL; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Resolve a PDP context structure based on the 32bit TEI. */ 14162306a36Sopenharmony_cistatic struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct hlist_head *head; 14462306a36Sopenharmony_ci struct pdp_ctx *pdp; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci head = >p->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size]; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci hlist_for_each_entry_rcu(pdp, head, hlist_tid) { 14962306a36Sopenharmony_ci if (pdp->gtp_version == GTP_V1 && 15062306a36Sopenharmony_ci pdp->u.v1.i_tei == tid) 15162306a36Sopenharmony_ci return pdp; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci return NULL; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Resolve a PDP context based on IPv4 address of MS. */ 15762306a36Sopenharmony_cistatic struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct hlist_head *head; 16062306a36Sopenharmony_ci struct pdp_ctx *pdp; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci head = >p->addr_hash[ipv4_hashfn(ms_addr) % gtp->hash_size]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci hlist_for_each_entry_rcu(pdp, head, hlist_addr) { 16562306a36Sopenharmony_ci if (pdp->af == AF_INET && 16662306a36Sopenharmony_ci pdp->ms_addr_ip4.s_addr == ms_addr) 16762306a36Sopenharmony_ci return pdp; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, 17462306a36Sopenharmony_ci unsigned int hdrlen, unsigned int role) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct iphdr *iph; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr))) 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci iph = (struct iphdr *)(skb->data + hdrlen); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (role == GTP_ROLE_SGSN) 18462306a36Sopenharmony_ci return iph->daddr == pctx->ms_addr_ip4.s_addr; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci return iph->saddr == pctx->ms_addr_ip4.s_addr; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* Check if the inner IP address in this packet is assigned to any 19062306a36Sopenharmony_ci * existing mobile subscriber. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx, 19362306a36Sopenharmony_ci unsigned int hdrlen, unsigned int role) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci switch (ntohs(skb->protocol)) { 19662306a36Sopenharmony_ci case ETH_P_IP: 19762306a36Sopenharmony_ci return gtp_check_ms_ipv4(skb, pctx, hdrlen, role); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci return false; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, 20362306a36Sopenharmony_ci unsigned int hdrlen, unsigned int role) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (!gtp_check_ms(skb, pctx, hdrlen, role)) { 20662306a36Sopenharmony_ci netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); 20762306a36Sopenharmony_ci return 1; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Get rid of the GTP + UDP headers. */ 21162306a36Sopenharmony_ci if (iptunnel_pull_header(skb, hdrlen, skb->protocol, 21262306a36Sopenharmony_ci !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) { 21362306a36Sopenharmony_ci pctx->dev->stats.rx_length_errors++; 21462306a36Sopenharmony_ci goto err; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Now that the UDP and the GTP header have been removed, set up the 22062306a36Sopenharmony_ci * new network header. This is required by the upper layer to 22162306a36Sopenharmony_ci * calculate the transport header. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci skb_reset_network_header(skb); 22462306a36Sopenharmony_ci skb_reset_mac_header(skb); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci skb->dev = pctx->dev; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dev_sw_netstats_rx_add(pctx->dev, skb->len); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci __netif_rx(skb); 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cierr: 23462306a36Sopenharmony_ci pctx->dev->stats.rx_dropped++; 23562306a36Sopenharmony_ci return -1; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, 23962306a36Sopenharmony_ci const struct sock *sk, 24062306a36Sopenharmony_ci __be32 daddr, __be32 saddr) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci memset(fl4, 0, sizeof(*fl4)); 24362306a36Sopenharmony_ci fl4->flowi4_oif = sk->sk_bound_dev_if; 24462306a36Sopenharmony_ci fl4->daddr = daddr; 24562306a36Sopenharmony_ci fl4->saddr = saddr; 24662306a36Sopenharmony_ci fl4->flowi4_tos = ip_sock_rt_tos(sk); 24762306a36Sopenharmony_ci fl4->flowi4_scope = ip_sock_rt_scope(sk); 24862306a36Sopenharmony_ci fl4->flowi4_proto = sk->sk_protocol; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return ip_route_output_key(sock_net(sk), fl4); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* GSM TS 09.60. 7.3 25462306a36Sopenharmony_ci * In all Path Management messages: 25562306a36Sopenharmony_ci * - TID: is not used and shall be set to 0. 25662306a36Sopenharmony_ci * - Flow Label is not used and shall be set to 0 25762306a36Sopenharmony_ci * In signalling messages: 25862306a36Sopenharmony_ci * - number: this field is not yet used in signalling messages. 25962306a36Sopenharmony_ci * It shall be set to 255 by the sender and shall be ignored 26062306a36Sopenharmony_ci * by the receiver 26162306a36Sopenharmony_ci * Returns true if the echo req was correct, false otherwise. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic bool gtp0_validate_echo_hdr(struct gtp0_header *gtp0) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci return !(gtp0->tid || (gtp0->flags ^ 0x1e) || 26662306a36Sopenharmony_ci gtp0->number != 0xff || gtp0->flow); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* msg_type has to be GTP_ECHO_REQ or GTP_ECHO_RSP */ 27062306a36Sopenharmony_cistatic void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int len_pkt, len_hdr; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci hdr->flags = 0x1e; /* v0, GTP-non-prime. */ 27562306a36Sopenharmony_ci hdr->type = msg_type; 27662306a36Sopenharmony_ci /* GSM TS 09.60. 7.3 In all Path Management Flow Label and TID 27762306a36Sopenharmony_ci * are not used and shall be set to 0. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci hdr->flow = 0; 28062306a36Sopenharmony_ci hdr->tid = 0; 28162306a36Sopenharmony_ci hdr->number = 0xff; 28262306a36Sopenharmony_ci hdr->spare[0] = 0xff; 28362306a36Sopenharmony_ci hdr->spare[1] = 0xff; 28462306a36Sopenharmony_ci hdr->spare[2] = 0xff; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci len_pkt = sizeof(struct gtp0_packet); 28762306a36Sopenharmony_ci len_hdr = sizeof(struct gtp0_header); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (msg_type == GTP_ECHO_RSP) 29062306a36Sopenharmony_ci hdr->length = htons(len_pkt - len_hdr); 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci hdr->length = 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct gtp0_packet *gtp_pkt; 29862306a36Sopenharmony_ci struct gtp0_header *gtp0; 29962306a36Sopenharmony_ci struct rtable *rt; 30062306a36Sopenharmony_ci struct flowi4 fl4; 30162306a36Sopenharmony_ci struct iphdr *iph; 30262306a36Sopenharmony_ci __be16 seq; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!gtp0_validate_echo_hdr(gtp0)) 30762306a36Sopenharmony_ci return -1; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci seq = gtp0->seq; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* pull GTP and UDP headers */ 31262306a36Sopenharmony_ci skb_pull_data(skb, sizeof(struct gtp0_header) + sizeof(struct udphdr)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci gtp_pkt = skb_push(skb, sizeof(struct gtp0_packet)); 31562306a36Sopenharmony_ci memset(gtp_pkt, 0, sizeof(struct gtp0_packet)); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci gtp0_build_echo_msg(>p_pkt->gtp0_h, GTP_ECHO_RSP); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* GSM TS 09.60. 7.3 The Sequence Number in a signalling response 32062306a36Sopenharmony_ci * message shall be copied from the signalling request message 32162306a36Sopenharmony_ci * that the GSN is replying to. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci gtp_pkt->gtp0_h.seq = seq; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci gtp_pkt->ie.tag = GTPIE_RECOVERY; 32662306a36Sopenharmony_ci gtp_pkt->ie.val = gtp->restart_count; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci iph = ip_hdr(skb); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* find route to the sender, 33162306a36Sopenharmony_ci * src address becomes dst address and vice versa. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr); 33462306a36Sopenharmony_ci if (IS_ERR(rt)) { 33562306a36Sopenharmony_ci netdev_dbg(gtp->dev, "no route for echo response from %pI4\n", 33662306a36Sopenharmony_ci &iph->saddr); 33762306a36Sopenharmony_ci return -1; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci udp_tunnel_xmit_skb(rt, gtp->sk0, skb, 34162306a36Sopenharmony_ci fl4.saddr, fl4.daddr, 34262306a36Sopenharmony_ci iph->tos, 34362306a36Sopenharmony_ci ip4_dst_hoplimit(&rt->dst), 34462306a36Sopenharmony_ci 0, 34562306a36Sopenharmony_ci htons(GTP0_PORT), htons(GTP0_PORT), 34662306a36Sopenharmony_ci !net_eq(sock_net(gtp->sk1u), 34762306a36Sopenharmony_ci dev_net(gtp->dev)), 34862306a36Sopenharmony_ci false); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, 35362306a36Sopenharmony_ci int flags, u32 type, struct echo_info echo) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci void *genlh; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci genlh = genlmsg_put(skb, snd_portid, snd_seq, >p_genl_family, flags, 35862306a36Sopenharmony_ci type); 35962306a36Sopenharmony_ci if (!genlh) 36062306a36Sopenharmony_ci goto failure; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (nla_put_u32(skb, GTPA_VERSION, echo.gtp_version) || 36362306a36Sopenharmony_ci nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer_addr_ip4.s_addr) || 36462306a36Sopenharmony_ci nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms_addr_ip4.s_addr)) 36562306a36Sopenharmony_ci goto failure; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci genlmsg_end(skb, genlh); 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cifailure: 37162306a36Sopenharmony_ci genlmsg_cancel(skb, genlh); 37262306a36Sopenharmony_ci return -EMSGSIZE; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct gtp0_header *gtp0; 37862306a36Sopenharmony_ci struct echo_info echo; 37962306a36Sopenharmony_ci struct sk_buff *msg; 38062306a36Sopenharmony_ci struct iphdr *iph; 38162306a36Sopenharmony_ci int ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!gtp0_validate_echo_hdr(gtp0)) 38662306a36Sopenharmony_ci return -1; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci iph = ip_hdr(skb); 38962306a36Sopenharmony_ci echo.ms_addr_ip4.s_addr = iph->daddr; 39062306a36Sopenharmony_ci echo.peer_addr_ip4.s_addr = iph->saddr; 39162306a36Sopenharmony_ci echo.gtp_version = GTP_V0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 39462306a36Sopenharmony_ci if (!msg) 39562306a36Sopenharmony_ci return -ENOMEM; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = gtp_genl_fill_echo(msg, 0, 0, 0, GTP_CMD_ECHOREQ, echo); 39862306a36Sopenharmony_ci if (ret < 0) { 39962306a36Sopenharmony_ci nlmsg_free(msg); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return genlmsg_multicast_netns(>p_genl_family, dev_net(gtp->dev), 40462306a36Sopenharmony_ci msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ 40862306a36Sopenharmony_cistatic int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci unsigned int hdrlen = sizeof(struct udphdr) + 41162306a36Sopenharmony_ci sizeof(struct gtp0_header); 41262306a36Sopenharmony_ci struct gtp0_header *gtp0; 41362306a36Sopenharmony_ci struct pdp_ctx *pctx; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!pskb_may_pull(skb, hdrlen)) 41662306a36Sopenharmony_ci return -1; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if ((gtp0->flags >> 5) != GTP_V0) 42162306a36Sopenharmony_ci return 1; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* If the sockets were created in kernel, it means that 42462306a36Sopenharmony_ci * there is no daemon running in userspace which would 42562306a36Sopenharmony_ci * handle echo request. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci if (gtp0->type == GTP_ECHO_REQ && gtp->sk_created) 42862306a36Sopenharmony_ci return gtp0_send_echo_resp(gtp, skb); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (gtp0->type == GTP_ECHO_RSP && gtp->sk_created) 43162306a36Sopenharmony_ci return gtp0_handle_echo_resp(gtp, skb); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (gtp0->type != GTP_TPDU) 43462306a36Sopenharmony_ci return 1; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid)); 43762306a36Sopenharmony_ci if (!pctx) { 43862306a36Sopenharmony_ci netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); 43962306a36Sopenharmony_ci return 1; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return gtp_rx(pctx, skb, hdrlen, gtp->role); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* msg_type has to be GTP_ECHO_REQ or GTP_ECHO_RSP */ 44662306a36Sopenharmony_cistatic void gtp1u_build_echo_msg(struct gtp1_header_long *hdr, __u8 msg_type) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci int len_pkt, len_hdr; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* S flag must be set to 1 */ 45162306a36Sopenharmony_ci hdr->flags = 0x32; /* v1, GTP-non-prime. */ 45262306a36Sopenharmony_ci hdr->type = msg_type; 45362306a36Sopenharmony_ci /* 3GPP TS 29.281 5.1 - TEID has to be set to 0 */ 45462306a36Sopenharmony_ci hdr->tid = 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* seq, npdu and next should be counted to the length of the GTP packet 45762306a36Sopenharmony_ci * that's why szie of gtp1_header should be subtracted, 45862306a36Sopenharmony_ci * not size of gtp1_header_long. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci len_hdr = sizeof(struct gtp1_header); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (msg_type == GTP_ECHO_RSP) { 46462306a36Sopenharmony_ci len_pkt = sizeof(struct gtp1u_packet); 46562306a36Sopenharmony_ci hdr->length = htons(len_pkt - len_hdr); 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci /* GTP_ECHO_REQ does not carry GTP Information Element, 46862306a36Sopenharmony_ci * the why gtp1_header_long is used here. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci len_pkt = sizeof(struct gtp1_header_long); 47162306a36Sopenharmony_ci hdr->length = htons(len_pkt - len_hdr); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct gtp1_header_long *gtp1u; 47862306a36Sopenharmony_ci struct gtp1u_packet *gtp_pkt; 47962306a36Sopenharmony_ci struct rtable *rt; 48062306a36Sopenharmony_ci struct flowi4 fl4; 48162306a36Sopenharmony_ci struct iphdr *iph; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci gtp1u = (struct gtp1_header_long *)(skb->data + sizeof(struct udphdr)); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 3GPP TS 29.281 5.1 - For the Echo Request, Echo Response, 48662306a36Sopenharmony_ci * Error Indication and Supported Extension Headers Notification 48762306a36Sopenharmony_ci * messages, the S flag shall be set to 1 and TEID shall be set to 0. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci if (!(gtp1u->flags & GTP1_F_SEQ) || gtp1u->tid) 49062306a36Sopenharmony_ci return -1; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* pull GTP and UDP headers */ 49362306a36Sopenharmony_ci skb_pull_data(skb, 49462306a36Sopenharmony_ci sizeof(struct gtp1_header_long) + sizeof(struct udphdr)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet)); 49762306a36Sopenharmony_ci memset(gtp_pkt, 0, sizeof(struct gtp1u_packet)); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci gtp1u_build_echo_msg(>p_pkt->gtp1u_h, GTP_ECHO_RSP); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* 3GPP TS 29.281 7.7.2 - The Restart Counter value in the 50262306a36Sopenharmony_ci * Recovery information element shall not be used, i.e. it shall 50362306a36Sopenharmony_ci * be set to zero by the sender and shall be ignored by the receiver. 50462306a36Sopenharmony_ci * The Recovery information element is mandatory due to backwards 50562306a36Sopenharmony_ci * compatibility reasons. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci gtp_pkt->ie.tag = GTPIE_RECOVERY; 50862306a36Sopenharmony_ci gtp_pkt->ie.val = 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci iph = ip_hdr(skb); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* find route to the sender, 51362306a36Sopenharmony_ci * src address becomes dst address and vice versa. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci rt = ip4_route_output_gtp(&fl4, gtp->sk1u, iph->saddr, iph->daddr); 51662306a36Sopenharmony_ci if (IS_ERR(rt)) { 51762306a36Sopenharmony_ci netdev_dbg(gtp->dev, "no route for echo response from %pI4\n", 51862306a36Sopenharmony_ci &iph->saddr); 51962306a36Sopenharmony_ci return -1; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci udp_tunnel_xmit_skb(rt, gtp->sk1u, skb, 52362306a36Sopenharmony_ci fl4.saddr, fl4.daddr, 52462306a36Sopenharmony_ci iph->tos, 52562306a36Sopenharmony_ci ip4_dst_hoplimit(&rt->dst), 52662306a36Sopenharmony_ci 0, 52762306a36Sopenharmony_ci htons(GTP1U_PORT), htons(GTP1U_PORT), 52862306a36Sopenharmony_ci !net_eq(sock_net(gtp->sk1u), 52962306a36Sopenharmony_ci dev_net(gtp->dev)), 53062306a36Sopenharmony_ci false); 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct gtp1_header_long *gtp1u; 53762306a36Sopenharmony_ci struct echo_info echo; 53862306a36Sopenharmony_ci struct sk_buff *msg; 53962306a36Sopenharmony_ci struct iphdr *iph; 54062306a36Sopenharmony_ci int ret; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci gtp1u = (struct gtp1_header_long *)(skb->data + sizeof(struct udphdr)); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 3GPP TS 29.281 5.1 - For the Echo Request, Echo Response, 54562306a36Sopenharmony_ci * Error Indication and Supported Extension Headers Notification 54662306a36Sopenharmony_ci * messages, the S flag shall be set to 1 and TEID shall be set to 0. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (!(gtp1u->flags & GTP1_F_SEQ) || gtp1u->tid) 54962306a36Sopenharmony_ci return -1; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci iph = ip_hdr(skb); 55262306a36Sopenharmony_ci echo.ms_addr_ip4.s_addr = iph->daddr; 55362306a36Sopenharmony_ci echo.peer_addr_ip4.s_addr = iph->saddr; 55462306a36Sopenharmony_ci echo.gtp_version = GTP_V1; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 55762306a36Sopenharmony_ci if (!msg) 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = gtp_genl_fill_echo(msg, 0, 0, 0, GTP_CMD_ECHOREQ, echo); 56162306a36Sopenharmony_ci if (ret < 0) { 56262306a36Sopenharmony_ci nlmsg_free(msg); 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return genlmsg_multicast_netns(>p_genl_family, dev_net(gtp->dev), 56762306a36Sopenharmony_ci msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci unsigned int hdrlen = sizeof(struct udphdr) + 57362306a36Sopenharmony_ci sizeof(struct gtp1_header); 57462306a36Sopenharmony_ci struct gtp1_header *gtp1; 57562306a36Sopenharmony_ci struct pdp_ctx *pctx; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!pskb_may_pull(skb, hdrlen)) 57862306a36Sopenharmony_ci return -1; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if ((gtp1->flags >> 5) != GTP_V1) 58362306a36Sopenharmony_ci return 1; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* If the sockets were created in kernel, it means that 58662306a36Sopenharmony_ci * there is no daemon running in userspace which would 58762306a36Sopenharmony_ci * handle echo request. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci if (gtp1->type == GTP_ECHO_REQ && gtp->sk_created) 59062306a36Sopenharmony_ci return gtp1u_send_echo_resp(gtp, skb); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (gtp1->type == GTP_ECHO_RSP && gtp->sk_created) 59362306a36Sopenharmony_ci return gtp1u_handle_echo_resp(gtp, skb); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (gtp1->type != GTP_TPDU) 59662306a36Sopenharmony_ci return 1; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* From 29.060: "This field shall be present if and only if any one or 59962306a36Sopenharmony_ci * more of the S, PN and E flags are set.". 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * If any of the bit is set, then the remaining ones also have to be 60262306a36Sopenharmony_ci * set. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (gtp1->flags & GTP1_F_MASK) 60562306a36Sopenharmony_ci hdrlen += 4; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Make sure the header is larger enough, including extensions. */ 60862306a36Sopenharmony_ci if (!pskb_may_pull(skb, hdrlen)) 60962306a36Sopenharmony_ci return -1; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid)); 61462306a36Sopenharmony_ci if (!pctx) { 61562306a36Sopenharmony_ci netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); 61662306a36Sopenharmony_ci return 1; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return gtp_rx(pctx, skb, hdrlen, gtp->role); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void __gtp_encap_destroy(struct sock *sk) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct gtp_dev *gtp; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci lock_sock(sk); 62762306a36Sopenharmony_ci gtp = sk->sk_user_data; 62862306a36Sopenharmony_ci if (gtp) { 62962306a36Sopenharmony_ci if (gtp->sk0 == sk) 63062306a36Sopenharmony_ci gtp->sk0 = NULL; 63162306a36Sopenharmony_ci else 63262306a36Sopenharmony_ci gtp->sk1u = NULL; 63362306a36Sopenharmony_ci WRITE_ONCE(udp_sk(sk)->encap_type, 0); 63462306a36Sopenharmony_ci rcu_assign_sk_user_data(sk, NULL); 63562306a36Sopenharmony_ci release_sock(sk); 63662306a36Sopenharmony_ci sock_put(sk); 63762306a36Sopenharmony_ci return; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci release_sock(sk); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void gtp_encap_destroy(struct sock *sk) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci rtnl_lock(); 64562306a36Sopenharmony_ci __gtp_encap_destroy(sk); 64662306a36Sopenharmony_ci rtnl_unlock(); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void gtp_encap_disable_sock(struct sock *sk) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci if (!sk) 65262306a36Sopenharmony_ci return; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci __gtp_encap_destroy(sk); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void gtp_encap_disable(struct gtp_dev *gtp) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci if (gtp->sk_created) { 66062306a36Sopenharmony_ci udp_tunnel_sock_release(gtp->sk0->sk_socket); 66162306a36Sopenharmony_ci udp_tunnel_sock_release(gtp->sk1u->sk_socket); 66262306a36Sopenharmony_ci gtp->sk_created = false; 66362306a36Sopenharmony_ci gtp->sk0 = NULL; 66462306a36Sopenharmony_ci gtp->sk1u = NULL; 66562306a36Sopenharmony_ci } else { 66662306a36Sopenharmony_ci gtp_encap_disable_sock(gtp->sk0); 66762306a36Sopenharmony_ci gtp_encap_disable_sock(gtp->sk1u); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci/* UDP encapsulation receive handler. See net/ipv4/udp.c. 67262306a36Sopenharmony_ci * Return codes: 0: success, <0: error, >0: pass up to userspace UDP socket. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cistatic int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct gtp_dev *gtp; 67762306a36Sopenharmony_ci int ret = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci gtp = rcu_dereference_sk_user_data(sk); 68062306a36Sopenharmony_ci if (!gtp) 68162306a36Sopenharmony_ci return 1; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci switch (READ_ONCE(udp_sk(sk)->encap_type)) { 68662306a36Sopenharmony_ci case UDP_ENCAP_GTP0: 68762306a36Sopenharmony_ci netdev_dbg(gtp->dev, "received GTP0 packet\n"); 68862306a36Sopenharmony_ci ret = gtp0_udp_encap_recv(gtp, skb); 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci case UDP_ENCAP_GTP1U: 69162306a36Sopenharmony_ci netdev_dbg(gtp->dev, "received GTP1U packet\n"); 69262306a36Sopenharmony_ci ret = gtp1u_udp_encap_recv(gtp, skb); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci default: 69562306a36Sopenharmony_ci ret = -1; /* Shouldn't happen. */ 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci switch (ret) { 69962306a36Sopenharmony_ci case 1: 70062306a36Sopenharmony_ci netdev_dbg(gtp->dev, "pass up to the process\n"); 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci case 0: 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci case -1: 70562306a36Sopenharmony_ci netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); 70662306a36Sopenharmony_ci kfree_skb(skb); 70762306a36Sopenharmony_ci ret = 0; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int gtp_dev_init(struct net_device *dev) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci gtp->dev = dev; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 72162306a36Sopenharmony_ci if (!dev->tstats) 72262306a36Sopenharmony_ci return -ENOMEM; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void gtp_dev_uninit(struct net_device *dev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci gtp_encap_disable(gtp); 73262306a36Sopenharmony_ci free_percpu(dev->tstats); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci int payload_len = skb->len; 73862306a36Sopenharmony_ci struct gtp0_header *gtp0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci gtp0 = skb_push(skb, sizeof(*gtp0)); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci gtp0->flags = 0x1e; /* v0, GTP-non-prime. */ 74362306a36Sopenharmony_ci gtp0->type = GTP_TPDU; 74462306a36Sopenharmony_ci gtp0->length = htons(payload_len); 74562306a36Sopenharmony_ci gtp0->seq = htons((atomic_inc_return(&pctx->tx_seq) - 1) % 0xffff); 74662306a36Sopenharmony_ci gtp0->flow = htons(pctx->u.v0.flow); 74762306a36Sopenharmony_ci gtp0->number = 0xff; 74862306a36Sopenharmony_ci gtp0->spare[0] = gtp0->spare[1] = gtp0->spare[2] = 0xff; 74962306a36Sopenharmony_ci gtp0->tid = cpu_to_be64(pctx->u.v0.tid); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci int payload_len = skb->len; 75562306a36Sopenharmony_ci struct gtp1_header *gtp1; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci gtp1 = skb_push(skb, sizeof(*gtp1)); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Bits 8 7 6 5 4 3 2 1 76062306a36Sopenharmony_ci * +--+--+--+--+--+--+--+--+ 76162306a36Sopenharmony_ci * |version |PT| 0| E| S|PN| 76262306a36Sopenharmony_ci * +--+--+--+--+--+--+--+--+ 76362306a36Sopenharmony_ci * 0 0 1 1 1 0 0 0 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci gtp1->flags = 0x30; /* v1, GTP-non-prime. */ 76662306a36Sopenharmony_ci gtp1->type = GTP_TPDU; 76762306a36Sopenharmony_ci gtp1->length = htons(payload_len); 76862306a36Sopenharmony_ci gtp1->tid = htonl(pctx->u.v1.o_tei); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* TODO: Support for extension header, sequence number and N-PDU. 77162306a36Sopenharmony_ci * Update the length field if any of them is available. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistruct gtp_pktinfo { 77662306a36Sopenharmony_ci struct sock *sk; 77762306a36Sopenharmony_ci struct iphdr *iph; 77862306a36Sopenharmony_ci struct flowi4 fl4; 77962306a36Sopenharmony_ci struct rtable *rt; 78062306a36Sopenharmony_ci struct pdp_ctx *pctx; 78162306a36Sopenharmony_ci struct net_device *dev; 78262306a36Sopenharmony_ci __be16 gtph_port; 78362306a36Sopenharmony_ci}; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci switch (pktinfo->pctx->gtp_version) { 78862306a36Sopenharmony_ci case GTP_V0: 78962306a36Sopenharmony_ci pktinfo->gtph_port = htons(GTP0_PORT); 79062306a36Sopenharmony_ci gtp0_push_header(skb, pktinfo->pctx); 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci case GTP_V1: 79362306a36Sopenharmony_ci pktinfo->gtph_port = htons(GTP1U_PORT); 79462306a36Sopenharmony_ci gtp1_push_header(skb, pktinfo->pctx); 79562306a36Sopenharmony_ci break; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo, 80062306a36Sopenharmony_ci struct sock *sk, struct iphdr *iph, 80162306a36Sopenharmony_ci struct pdp_ctx *pctx, struct rtable *rt, 80262306a36Sopenharmony_ci struct flowi4 *fl4, 80362306a36Sopenharmony_ci struct net_device *dev) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci pktinfo->sk = sk; 80662306a36Sopenharmony_ci pktinfo->iph = iph; 80762306a36Sopenharmony_ci pktinfo->pctx = pctx; 80862306a36Sopenharmony_ci pktinfo->rt = rt; 80962306a36Sopenharmony_ci pktinfo->fl4 = *fl4; 81062306a36Sopenharmony_ci pktinfo->dev = dev; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, 81462306a36Sopenharmony_ci struct gtp_pktinfo *pktinfo) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 81762306a36Sopenharmony_ci struct pdp_ctx *pctx; 81862306a36Sopenharmony_ci struct rtable *rt; 81962306a36Sopenharmony_ci struct flowi4 fl4; 82062306a36Sopenharmony_ci struct iphdr *iph; 82162306a36Sopenharmony_ci __be16 df; 82262306a36Sopenharmony_ci int mtu; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* Read the IP destination address and resolve the PDP context. 82562306a36Sopenharmony_ci * Prepend PDP header with TEI/TID from PDP ctx. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci iph = ip_hdr(skb); 82862306a36Sopenharmony_ci if (gtp->role == GTP_ROLE_SGSN) 82962306a36Sopenharmony_ci pctx = ipv4_pdp_find(gtp, iph->saddr); 83062306a36Sopenharmony_ci else 83162306a36Sopenharmony_ci pctx = ipv4_pdp_find(gtp, iph->daddr); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (!pctx) { 83462306a36Sopenharmony_ci netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", 83562306a36Sopenharmony_ci &iph->daddr); 83662306a36Sopenharmony_ci return -ENOENT; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci netdev_dbg(dev, "found PDP context %p\n", pctx); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr, 84162306a36Sopenharmony_ci inet_sk(pctx->sk)->inet_saddr); 84262306a36Sopenharmony_ci if (IS_ERR(rt)) { 84362306a36Sopenharmony_ci netdev_dbg(dev, "no route to SSGN %pI4\n", 84462306a36Sopenharmony_ci &pctx->peer_addr_ip4.s_addr); 84562306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 84662306a36Sopenharmony_ci goto err; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (rt->dst.dev == dev) { 85062306a36Sopenharmony_ci netdev_dbg(dev, "circular route to SSGN %pI4\n", 85162306a36Sopenharmony_ci &pctx->peer_addr_ip4.s_addr); 85262306a36Sopenharmony_ci dev->stats.collisions++; 85362306a36Sopenharmony_ci goto err_rt; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* This is similar to tnl_update_pmtu(). */ 85762306a36Sopenharmony_ci df = iph->frag_off; 85862306a36Sopenharmony_ci if (df) { 85962306a36Sopenharmony_ci mtu = dst_mtu(&rt->dst) - dev->hard_header_len - 86062306a36Sopenharmony_ci sizeof(struct iphdr) - sizeof(struct udphdr); 86162306a36Sopenharmony_ci switch (pctx->gtp_version) { 86262306a36Sopenharmony_ci case GTP_V0: 86362306a36Sopenharmony_ci mtu -= sizeof(struct gtp0_header); 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case GTP_V1: 86662306a36Sopenharmony_ci mtu -= sizeof(struct gtp1_header); 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } else { 87062306a36Sopenharmony_ci mtu = dst_mtu(&rt->dst); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci skb_dst_update_pmtu_no_confirm(skb, mtu); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (iph->frag_off & htons(IP_DF) && 87662306a36Sopenharmony_ci ((!skb_is_gso(skb) && skb->len > mtu) || 87762306a36Sopenharmony_ci (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu)))) { 87862306a36Sopenharmony_ci netdev_dbg(dev, "packet too big, fragmentation needed\n"); 87962306a36Sopenharmony_ci icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, 88062306a36Sopenharmony_ci htonl(mtu)); 88162306a36Sopenharmony_ci goto err_rt; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev); 88562306a36Sopenharmony_ci gtp_push_header(skb, pktinfo); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_cierr_rt: 88962306a36Sopenharmony_ci ip_rt_put(rt); 89062306a36Sopenharmony_cierr: 89162306a36Sopenharmony_ci return -EBADMSG; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci unsigned int proto = ntohs(skb->protocol); 89762306a36Sopenharmony_ci struct gtp_pktinfo pktinfo; 89862306a36Sopenharmony_ci int err; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* Ensure there is sufficient headroom. */ 90162306a36Sopenharmony_ci if (skb_cow_head(skb, dev->needed_headroom)) 90262306a36Sopenharmony_ci goto tx_err; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci skb_reset_inner_headers(skb); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* PDP context lookups in gtp_build_skb_*() need rcu read-side lock. */ 90762306a36Sopenharmony_ci rcu_read_lock(); 90862306a36Sopenharmony_ci switch (proto) { 90962306a36Sopenharmony_ci case ETH_P_IP: 91062306a36Sopenharmony_ci err = gtp_build_skb_ip4(skb, dev, &pktinfo); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci default: 91362306a36Sopenharmony_ci err = -EOPNOTSUPP; 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci rcu_read_unlock(); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (err < 0) 91962306a36Sopenharmony_ci goto tx_err; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci switch (proto) { 92262306a36Sopenharmony_ci case ETH_P_IP: 92362306a36Sopenharmony_ci netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n", 92462306a36Sopenharmony_ci &pktinfo.iph->saddr, &pktinfo.iph->daddr); 92562306a36Sopenharmony_ci udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb, 92662306a36Sopenharmony_ci pktinfo.fl4.saddr, pktinfo.fl4.daddr, 92762306a36Sopenharmony_ci pktinfo.iph->tos, 92862306a36Sopenharmony_ci ip4_dst_hoplimit(&pktinfo.rt->dst), 92962306a36Sopenharmony_ci 0, 93062306a36Sopenharmony_ci pktinfo.gtph_port, pktinfo.gtph_port, 93162306a36Sopenharmony_ci !net_eq(sock_net(pktinfo.pctx->sk), 93262306a36Sopenharmony_ci dev_net(dev)), 93362306a36Sopenharmony_ci false); 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return NETDEV_TX_OK; 93862306a36Sopenharmony_citx_err: 93962306a36Sopenharmony_ci dev->stats.tx_errors++; 94062306a36Sopenharmony_ci dev_kfree_skb(skb); 94162306a36Sopenharmony_ci return NETDEV_TX_OK; 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic const struct net_device_ops gtp_netdev_ops = { 94562306a36Sopenharmony_ci .ndo_init = gtp_dev_init, 94662306a36Sopenharmony_ci .ndo_uninit = gtp_dev_uninit, 94762306a36Sopenharmony_ci .ndo_start_xmit = gtp_dev_xmit, 94862306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 94962306a36Sopenharmony_ci}; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic const struct device_type gtp_type = { 95262306a36Sopenharmony_ci .name = "gtp", 95362306a36Sopenharmony_ci}; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic void gtp_link_setup(struct net_device *dev) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci unsigned int max_gtp_header_len = sizeof(struct iphdr) + 95862306a36Sopenharmony_ci sizeof(struct udphdr) + 95962306a36Sopenharmony_ci sizeof(struct gtp0_header); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci dev->netdev_ops = >p_netdev_ops; 96262306a36Sopenharmony_ci dev->needs_free_netdev = true; 96362306a36Sopenharmony_ci SET_NETDEV_DEVTYPE(dev, >p_type); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci dev->hard_header_len = 0; 96662306a36Sopenharmony_ci dev->addr_len = 0; 96762306a36Sopenharmony_ci dev->mtu = ETH_DATA_LEN - max_gtp_header_len; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* Zero header length. */ 97062306a36Sopenharmony_ci dev->type = ARPHRD_NONE; 97162306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; 97462306a36Sopenharmony_ci dev->features |= NETIF_F_LLTX; 97562306a36Sopenharmony_ci netif_keep_dst(dev); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci dev->needed_headroom = LL_MAX_HEADER + max_gtp_header_len; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); 98162306a36Sopenharmony_cistatic int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic void gtp_destructor(struct net_device *dev) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci kfree(gtp->addr_hash); 98862306a36Sopenharmony_ci kfree(gtp->tid_hash); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic struct sock *gtp_create_sock(int type, struct gtp_dev *gtp) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct udp_tunnel_sock_cfg tuncfg = {}; 99462306a36Sopenharmony_ci struct udp_port_cfg udp_conf = { 99562306a36Sopenharmony_ci .local_ip.s_addr = htonl(INADDR_ANY), 99662306a36Sopenharmony_ci .family = AF_INET, 99762306a36Sopenharmony_ci }; 99862306a36Sopenharmony_ci struct net *net = gtp->net; 99962306a36Sopenharmony_ci struct socket *sock; 100062306a36Sopenharmony_ci int err; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (type == UDP_ENCAP_GTP0) 100362306a36Sopenharmony_ci udp_conf.local_udp_port = htons(GTP0_PORT); 100462306a36Sopenharmony_ci else if (type == UDP_ENCAP_GTP1U) 100562306a36Sopenharmony_ci udp_conf.local_udp_port = htons(GTP1U_PORT); 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci err = udp_sock_create(net, &udp_conf, &sock); 101062306a36Sopenharmony_ci if (err) 101162306a36Sopenharmony_ci return ERR_PTR(err); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci tuncfg.sk_user_data = gtp; 101462306a36Sopenharmony_ci tuncfg.encap_type = type; 101562306a36Sopenharmony_ci tuncfg.encap_rcv = gtp_encap_recv; 101662306a36Sopenharmony_ci tuncfg.encap_destroy = NULL; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci setup_udp_tunnel_sock(net, sock, &tuncfg); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return sock->sk; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[]) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct sock *sk1u = NULL; 102662306a36Sopenharmony_ci struct sock *sk0 = NULL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp); 102962306a36Sopenharmony_ci if (IS_ERR(sk0)) 103062306a36Sopenharmony_ci return PTR_ERR(sk0); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp); 103362306a36Sopenharmony_ci if (IS_ERR(sk1u)) { 103462306a36Sopenharmony_ci udp_tunnel_sock_release(sk0->sk_socket); 103562306a36Sopenharmony_ci return PTR_ERR(sk1u); 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci gtp->sk_created = true; 103962306a36Sopenharmony_ci gtp->sk0 = sk0; 104062306a36Sopenharmony_ci gtp->sk1u = sk1u; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int gtp_newlink(struct net *src_net, struct net_device *dev, 104662306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 104762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci unsigned int role = GTP_ROLE_GGSN; 105062306a36Sopenharmony_ci struct gtp_dev *gtp; 105162306a36Sopenharmony_ci struct gtp_net *gn; 105262306a36Sopenharmony_ci int hashsize, err; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci gtp = netdev_priv(dev); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (!data[IFLA_GTP_PDP_HASHSIZE]) { 105762306a36Sopenharmony_ci hashsize = 1024; 105862306a36Sopenharmony_ci } else { 105962306a36Sopenharmony_ci hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]); 106062306a36Sopenharmony_ci if (!hashsize) 106162306a36Sopenharmony_ci hashsize = 1024; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (data[IFLA_GTP_ROLE]) { 106562306a36Sopenharmony_ci role = nla_get_u32(data[IFLA_GTP_ROLE]); 106662306a36Sopenharmony_ci if (role > GTP_ROLE_SGSN) 106762306a36Sopenharmony_ci return -EINVAL; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci gtp->role = role; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (!data[IFLA_GTP_RESTART_COUNT]) 107262306a36Sopenharmony_ci gtp->restart_count = 0; 107362306a36Sopenharmony_ci else 107462306a36Sopenharmony_ci gtp->restart_count = nla_get_u8(data[IFLA_GTP_RESTART_COUNT]); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci gtp->net = src_net; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci err = gtp_hashtable_new(gtp, hashsize); 107962306a36Sopenharmony_ci if (err < 0) 108062306a36Sopenharmony_ci return err; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (data[IFLA_GTP_CREATE_SOCKETS]) 108362306a36Sopenharmony_ci err = gtp_create_sockets(gtp, data); 108462306a36Sopenharmony_ci else 108562306a36Sopenharmony_ci err = gtp_encap_enable(gtp, data); 108662306a36Sopenharmony_ci if (err < 0) 108762306a36Sopenharmony_ci goto out_hashtable; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci err = register_netdevice(dev); 109062306a36Sopenharmony_ci if (err < 0) { 109162306a36Sopenharmony_ci netdev_dbg(dev, "failed to register new netdev %d\n", err); 109262306a36Sopenharmony_ci goto out_encap; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci gn = net_generic(dev_net(dev), gtp_net_id); 109662306a36Sopenharmony_ci list_add_rcu(>p->list, &gn->gtp_dev_list); 109762306a36Sopenharmony_ci dev->priv_destructor = gtp_destructor; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci netdev_dbg(dev, "registered new GTP interface\n"); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return 0; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ciout_encap: 110462306a36Sopenharmony_ci gtp_encap_disable(gtp); 110562306a36Sopenharmony_ciout_hashtable: 110662306a36Sopenharmony_ci kfree(gtp->addr_hash); 110762306a36Sopenharmony_ci kfree(gtp->tid_hash); 110862306a36Sopenharmony_ci return err; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void gtp_dellink(struct net_device *dev, struct list_head *head) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 111462306a36Sopenharmony_ci struct pdp_ctx *pctx; 111562306a36Sopenharmony_ci int i; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci for (i = 0; i < gtp->hash_size; i++) 111862306a36Sopenharmony_ci hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) 111962306a36Sopenharmony_ci pdp_context_delete(pctx); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci list_del_rcu(>p->list); 112262306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { 112662306a36Sopenharmony_ci [IFLA_GTP_FD0] = { .type = NLA_U32 }, 112762306a36Sopenharmony_ci [IFLA_GTP_FD1] = { .type = NLA_U32 }, 112862306a36Sopenharmony_ci [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, 112962306a36Sopenharmony_ci [IFLA_GTP_ROLE] = { .type = NLA_U32 }, 113062306a36Sopenharmony_ci [IFLA_GTP_CREATE_SOCKETS] = { .type = NLA_U8 }, 113162306a36Sopenharmony_ci [IFLA_GTP_RESTART_COUNT] = { .type = NLA_U8 }, 113262306a36Sopenharmony_ci}; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int gtp_validate(struct nlattr *tb[], struct nlattr *data[], 113562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci if (!data) 113862306a36Sopenharmony_ci return -EINVAL; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci return 0; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic size_t gtp_get_size(const struct net_device *dev) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci return nla_total_size(sizeof(__u32)) + /* IFLA_GTP_PDP_HASHSIZE */ 114662306a36Sopenharmony_ci nla_total_size(sizeof(__u32)) + /* IFLA_GTP_ROLE */ 114762306a36Sopenharmony_ci nla_total_size(sizeof(__u8)); /* IFLA_GTP_RESTART_COUNT */ 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct gtp_dev *gtp = netdev_priv(dev); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_GTP_PDP_HASHSIZE, gtp->hash_size)) 115562306a36Sopenharmony_ci goto nla_put_failure; 115662306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_GTP_ROLE, gtp->role)) 115762306a36Sopenharmony_ci goto nla_put_failure; 115862306a36Sopenharmony_ci if (nla_put_u8(skb, IFLA_GTP_RESTART_COUNT, gtp->restart_count)) 115962306a36Sopenharmony_ci goto nla_put_failure; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return 0; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cinla_put_failure: 116462306a36Sopenharmony_ci return -EMSGSIZE; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic struct rtnl_link_ops gtp_link_ops __read_mostly = { 116862306a36Sopenharmony_ci .kind = "gtp", 116962306a36Sopenharmony_ci .maxtype = IFLA_GTP_MAX, 117062306a36Sopenharmony_ci .policy = gtp_policy, 117162306a36Sopenharmony_ci .priv_size = sizeof(struct gtp_dev), 117262306a36Sopenharmony_ci .setup = gtp_link_setup, 117362306a36Sopenharmony_ci .validate = gtp_validate, 117462306a36Sopenharmony_ci .newlink = gtp_newlink, 117562306a36Sopenharmony_ci .dellink = gtp_dellink, 117662306a36Sopenharmony_ci .get_size = gtp_get_size, 117762306a36Sopenharmony_ci .fill_info = gtp_fill_info, 117862306a36Sopenharmony_ci}; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci int i; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci gtp->addr_hash = kmalloc_array(hsize, sizeof(struct hlist_head), 118562306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 118662306a36Sopenharmony_ci if (gtp->addr_hash == NULL) 118762306a36Sopenharmony_ci return -ENOMEM; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci gtp->tid_hash = kmalloc_array(hsize, sizeof(struct hlist_head), 119062306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 119162306a36Sopenharmony_ci if (gtp->tid_hash == NULL) 119262306a36Sopenharmony_ci goto err1; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci gtp->hash_size = hsize; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci for (i = 0; i < hsize; i++) { 119762306a36Sopenharmony_ci INIT_HLIST_HEAD(>p->addr_hash[i]); 119862306a36Sopenharmony_ci INIT_HLIST_HEAD(>p->tid_hash[i]); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci return 0; 120162306a36Sopenharmony_cierr1: 120262306a36Sopenharmony_ci kfree(gtp->addr_hash); 120362306a36Sopenharmony_ci return -ENOMEM; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic struct sock *gtp_encap_enable_socket(int fd, int type, 120762306a36Sopenharmony_ci struct gtp_dev *gtp) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct udp_tunnel_sock_cfg tuncfg = {NULL}; 121062306a36Sopenharmony_ci struct socket *sock; 121162306a36Sopenharmony_ci struct sock *sk; 121262306a36Sopenharmony_ci int err; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci pr_debug("enable gtp on %d, %d\n", fd, type); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci sock = sockfd_lookup(fd, &err); 121762306a36Sopenharmony_ci if (!sock) { 121862306a36Sopenharmony_ci pr_debug("gtp socket fd=%d not found\n", fd); 121962306a36Sopenharmony_ci return NULL; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci sk = sock->sk; 122362306a36Sopenharmony_ci if (sk->sk_protocol != IPPROTO_UDP || 122462306a36Sopenharmony_ci sk->sk_type != SOCK_DGRAM || 122562306a36Sopenharmony_ci (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) { 122662306a36Sopenharmony_ci pr_debug("socket fd=%d not UDP\n", fd); 122762306a36Sopenharmony_ci sk = ERR_PTR(-EINVAL); 122862306a36Sopenharmony_ci goto out_sock; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci lock_sock(sk); 123262306a36Sopenharmony_ci if (sk->sk_user_data) { 123362306a36Sopenharmony_ci sk = ERR_PTR(-EBUSY); 123462306a36Sopenharmony_ci goto out_rel_sock; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci sock_hold(sk); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci tuncfg.sk_user_data = gtp; 124062306a36Sopenharmony_ci tuncfg.encap_type = type; 124162306a36Sopenharmony_ci tuncfg.encap_rcv = gtp_encap_recv; 124262306a36Sopenharmony_ci tuncfg.encap_destroy = gtp_encap_destroy; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ciout_rel_sock: 124762306a36Sopenharmony_ci release_sock(sock->sk); 124862306a36Sopenharmony_ciout_sock: 124962306a36Sopenharmony_ci sockfd_put(sock); 125062306a36Sopenharmony_ci return sk; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci struct sock *sk1u = NULL; 125662306a36Sopenharmony_ci struct sock *sk0 = NULL; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) 125962306a36Sopenharmony_ci return -EINVAL; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci if (data[IFLA_GTP_FD0]) { 126262306a36Sopenharmony_ci u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); 126562306a36Sopenharmony_ci if (IS_ERR(sk0)) 126662306a36Sopenharmony_ci return PTR_ERR(sk0); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (data[IFLA_GTP_FD1]) { 127062306a36Sopenharmony_ci u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); 127362306a36Sopenharmony_ci if (IS_ERR(sk1u)) { 127462306a36Sopenharmony_ci gtp_encap_disable_sock(sk0); 127562306a36Sopenharmony_ci return PTR_ERR(sk1u); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci gtp->sk0 = sk0; 128062306a36Sopenharmony_ci gtp->sk1u = sk1u; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[]) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct gtp_dev *gtp = NULL; 128862306a36Sopenharmony_ci struct net_device *dev; 128962306a36Sopenharmony_ci struct net *net; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* Examine the link attributes and figure out which network namespace 129262306a36Sopenharmony_ci * we are talking about. 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_ci if (nla[GTPA_NET_NS_FD]) 129562306a36Sopenharmony_ci net = get_net_ns_by_fd(nla_get_u32(nla[GTPA_NET_NS_FD])); 129662306a36Sopenharmony_ci else 129762306a36Sopenharmony_ci net = get_net(src_net); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (IS_ERR(net)) 130062306a36Sopenharmony_ci return NULL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* Check if there's an existing gtpX device to configure */ 130362306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, nla_get_u32(nla[GTPA_LINK])); 130462306a36Sopenharmony_ci if (dev && dev->netdev_ops == >p_netdev_ops) 130562306a36Sopenharmony_ci gtp = netdev_priv(dev); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci put_net(net); 130862306a36Sopenharmony_ci return gtp; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); 131462306a36Sopenharmony_ci pctx->af = AF_INET; 131562306a36Sopenharmony_ci pctx->peer_addr_ip4.s_addr = 131662306a36Sopenharmony_ci nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); 131762306a36Sopenharmony_ci pctx->ms_addr_ip4.s_addr = 131862306a36Sopenharmony_ci nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci switch (pctx->gtp_version) { 132162306a36Sopenharmony_ci case GTP_V0: 132262306a36Sopenharmony_ci /* According to TS 09.60, sections 7.5.1 and 7.5.2, the flow 132362306a36Sopenharmony_ci * label needs to be the same for uplink and downlink packets, 132462306a36Sopenharmony_ci * so let's annotate this. 132562306a36Sopenharmony_ci */ 132662306a36Sopenharmony_ci pctx->u.v0.tid = nla_get_u64(info->attrs[GTPA_TID]); 132762306a36Sopenharmony_ci pctx->u.v0.flow = nla_get_u16(info->attrs[GTPA_FLOW]); 132862306a36Sopenharmony_ci break; 132962306a36Sopenharmony_ci case GTP_V1: 133062306a36Sopenharmony_ci pctx->u.v1.i_tei = nla_get_u32(info->attrs[GTPA_I_TEI]); 133162306a36Sopenharmony_ci pctx->u.v1.o_tei = nla_get_u32(info->attrs[GTPA_O_TEI]); 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci default: 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, 133962306a36Sopenharmony_ci struct genl_info *info) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct pdp_ctx *pctx, *pctx_tid = NULL; 134262306a36Sopenharmony_ci struct net_device *dev = gtp->dev; 134362306a36Sopenharmony_ci u32 hash_ms, hash_tid = 0; 134462306a36Sopenharmony_ci unsigned int version; 134562306a36Sopenharmony_ci bool found = false; 134662306a36Sopenharmony_ci __be32 ms_addr; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); 134962306a36Sopenharmony_ci hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size; 135062306a36Sopenharmony_ci version = nla_get_u32(info->attrs[GTPA_VERSION]); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci pctx = ipv4_pdp_find(gtp, ms_addr); 135362306a36Sopenharmony_ci if (pctx) 135462306a36Sopenharmony_ci found = true; 135562306a36Sopenharmony_ci if (version == GTP_V0) 135662306a36Sopenharmony_ci pctx_tid = gtp0_pdp_find(gtp, 135762306a36Sopenharmony_ci nla_get_u64(info->attrs[GTPA_TID])); 135862306a36Sopenharmony_ci else if (version == GTP_V1) 135962306a36Sopenharmony_ci pctx_tid = gtp1_pdp_find(gtp, 136062306a36Sopenharmony_ci nla_get_u32(info->attrs[GTPA_I_TEI])); 136162306a36Sopenharmony_ci if (pctx_tid) 136262306a36Sopenharmony_ci found = true; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (found) { 136562306a36Sopenharmony_ci if (info->nlhdr->nlmsg_flags & NLM_F_EXCL) 136662306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 136762306a36Sopenharmony_ci if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE) 136862306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (pctx && pctx_tid) 137162306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 137262306a36Sopenharmony_ci if (!pctx) 137362306a36Sopenharmony_ci pctx = pctx_tid; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci ipv4_pdp_fill(pctx, info); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (pctx->gtp_version == GTP_V0) 137862306a36Sopenharmony_ci netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n", 137962306a36Sopenharmony_ci pctx->u.v0.tid, pctx); 138062306a36Sopenharmony_ci else if (pctx->gtp_version == GTP_V1) 138162306a36Sopenharmony_ci netdev_dbg(dev, "GTPv1-U: update tunnel id = %x/%x (pdp %p)\n", 138262306a36Sopenharmony_ci pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci return pctx; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci pctx = kmalloc(sizeof(*pctx), GFP_ATOMIC); 138962306a36Sopenharmony_ci if (pctx == NULL) 139062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci sock_hold(sk); 139362306a36Sopenharmony_ci pctx->sk = sk; 139462306a36Sopenharmony_ci pctx->dev = gtp->dev; 139562306a36Sopenharmony_ci ipv4_pdp_fill(pctx, info); 139662306a36Sopenharmony_ci atomic_set(&pctx->tx_seq, 0); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci switch (pctx->gtp_version) { 139962306a36Sopenharmony_ci case GTP_V0: 140062306a36Sopenharmony_ci /* TS 09.60: "The flow label identifies unambiguously a GTP 140162306a36Sopenharmony_ci * flow.". We use the tid for this instead, I cannot find a 140262306a36Sopenharmony_ci * situation in which this doesn't unambiguosly identify the 140362306a36Sopenharmony_ci * PDP context. 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gtp->hash_size; 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci case GTP_V1: 140862306a36Sopenharmony_ci hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gtp->hash_size; 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci hlist_add_head_rcu(&pctx->hlist_addr, >p->addr_hash[hash_ms]); 141362306a36Sopenharmony_ci hlist_add_head_rcu(&pctx->hlist_tid, >p->tid_hash[hash_tid]); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci switch (pctx->gtp_version) { 141662306a36Sopenharmony_ci case GTP_V0: 141762306a36Sopenharmony_ci netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n", 141862306a36Sopenharmony_ci pctx->u.v0.tid, &pctx->peer_addr_ip4, 141962306a36Sopenharmony_ci &pctx->ms_addr_ip4, pctx); 142062306a36Sopenharmony_ci break; 142162306a36Sopenharmony_ci case GTP_V1: 142262306a36Sopenharmony_ci netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n", 142362306a36Sopenharmony_ci pctx->u.v1.i_tei, pctx->u.v1.o_tei, 142462306a36Sopenharmony_ci &pctx->peer_addr_ip4, &pctx->ms_addr_ip4, pctx); 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci return pctx; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic void pdp_context_free(struct rcu_head *head) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci sock_put(pctx->sk); 143662306a36Sopenharmony_ci kfree(pctx); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic void pdp_context_delete(struct pdp_ctx *pctx) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci hlist_del_rcu(&pctx->hlist_tid); 144262306a36Sopenharmony_ci hlist_del_rcu(&pctx->hlist_addr); 144362306a36Sopenharmony_ci call_rcu(&pctx->rcu_head, pdp_context_free); 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic int gtp_tunnel_notify(struct pdp_ctx *pctx, u8 cmd, gfp_t allocation); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci unsigned int version; 145162306a36Sopenharmony_ci struct pdp_ctx *pctx; 145262306a36Sopenharmony_ci struct gtp_dev *gtp; 145362306a36Sopenharmony_ci struct sock *sk; 145462306a36Sopenharmony_ci int err; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci if (!info->attrs[GTPA_VERSION] || 145762306a36Sopenharmony_ci !info->attrs[GTPA_LINK] || 145862306a36Sopenharmony_ci !info->attrs[GTPA_PEER_ADDRESS] || 145962306a36Sopenharmony_ci !info->attrs[GTPA_MS_ADDRESS]) 146062306a36Sopenharmony_ci return -EINVAL; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci version = nla_get_u32(info->attrs[GTPA_VERSION]); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci switch (version) { 146562306a36Sopenharmony_ci case GTP_V0: 146662306a36Sopenharmony_ci if (!info->attrs[GTPA_TID] || 146762306a36Sopenharmony_ci !info->attrs[GTPA_FLOW]) 146862306a36Sopenharmony_ci return -EINVAL; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case GTP_V1: 147162306a36Sopenharmony_ci if (!info->attrs[GTPA_I_TEI] || 147262306a36Sopenharmony_ci !info->attrs[GTPA_O_TEI]) 147362306a36Sopenharmony_ci return -EINVAL; 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci default: 147762306a36Sopenharmony_ci return -EINVAL; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci rtnl_lock(); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); 148362306a36Sopenharmony_ci if (!gtp) { 148462306a36Sopenharmony_ci err = -ENODEV; 148562306a36Sopenharmony_ci goto out_unlock; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (version == GTP_V0) 148962306a36Sopenharmony_ci sk = gtp->sk0; 149062306a36Sopenharmony_ci else if (version == GTP_V1) 149162306a36Sopenharmony_ci sk = gtp->sk1u; 149262306a36Sopenharmony_ci else 149362306a36Sopenharmony_ci sk = NULL; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (!sk) { 149662306a36Sopenharmony_ci err = -ENODEV; 149762306a36Sopenharmony_ci goto out_unlock; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci pctx = gtp_pdp_add(gtp, sk, info); 150162306a36Sopenharmony_ci if (IS_ERR(pctx)) { 150262306a36Sopenharmony_ci err = PTR_ERR(pctx); 150362306a36Sopenharmony_ci } else { 150462306a36Sopenharmony_ci gtp_tunnel_notify(pctx, GTP_CMD_NEWPDP, GFP_KERNEL); 150562306a36Sopenharmony_ci err = 0; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ciout_unlock: 150962306a36Sopenharmony_ci rtnl_unlock(); 151062306a36Sopenharmony_ci return err; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic struct pdp_ctx *gtp_find_pdp_by_link(struct net *net, 151462306a36Sopenharmony_ci struct nlattr *nla[]) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci struct gtp_dev *gtp; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci gtp = gtp_find_dev(net, nla); 151962306a36Sopenharmony_ci if (!gtp) 152062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (nla[GTPA_MS_ADDRESS]) { 152362306a36Sopenharmony_ci __be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return ipv4_pdp_find(gtp, ip); 152662306a36Sopenharmony_ci } else if (nla[GTPA_VERSION]) { 152762306a36Sopenharmony_ci u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci if (gtp_version == GTP_V0 && nla[GTPA_TID]) 153062306a36Sopenharmony_ci return gtp0_pdp_find(gtp, nla_get_u64(nla[GTPA_TID])); 153162306a36Sopenharmony_ci else if (gtp_version == GTP_V1 && nla[GTPA_I_TEI]) 153262306a36Sopenharmony_ci return gtp1_pdp_find(gtp, nla_get_u32(nla[GTPA_I_TEI])); 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic struct pdp_ctx *gtp_find_pdp(struct net *net, struct nlattr *nla[]) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci struct pdp_ctx *pctx; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci if (nla[GTPA_LINK]) 154362306a36Sopenharmony_ci pctx = gtp_find_pdp_by_link(net, nla); 154462306a36Sopenharmony_ci else 154562306a36Sopenharmony_ci pctx = ERR_PTR(-EINVAL); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (!pctx) 154862306a36Sopenharmony_ci pctx = ERR_PTR(-ENOENT); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci return pctx; 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct pdp_ctx *pctx; 155662306a36Sopenharmony_ci int err = 0; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci if (!info->attrs[GTPA_VERSION]) 155962306a36Sopenharmony_ci return -EINVAL; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci rcu_read_lock(); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); 156462306a36Sopenharmony_ci if (IS_ERR(pctx)) { 156562306a36Sopenharmony_ci err = PTR_ERR(pctx); 156662306a36Sopenharmony_ci goto out_unlock; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (pctx->gtp_version == GTP_V0) 157062306a36Sopenharmony_ci netdev_dbg(pctx->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", 157162306a36Sopenharmony_ci pctx->u.v0.tid, pctx); 157262306a36Sopenharmony_ci else if (pctx->gtp_version == GTP_V1) 157362306a36Sopenharmony_ci netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", 157462306a36Sopenharmony_ci pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci gtp_tunnel_notify(pctx, GTP_CMD_DELPDP, GFP_ATOMIC); 157762306a36Sopenharmony_ci pdp_context_delete(pctx); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ciout_unlock: 158062306a36Sopenharmony_ci rcu_read_unlock(); 158162306a36Sopenharmony_ci return err; 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, 158562306a36Sopenharmony_ci int flags, u32 type, struct pdp_ctx *pctx) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci void *genlh; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci genlh = genlmsg_put(skb, snd_portid, snd_seq, >p_genl_family, flags, 159062306a36Sopenharmony_ci type); 159162306a36Sopenharmony_ci if (genlh == NULL) 159262306a36Sopenharmony_ci goto nlmsg_failure; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) || 159562306a36Sopenharmony_ci nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex) || 159662306a36Sopenharmony_ci nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr) || 159762306a36Sopenharmony_ci nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr)) 159862306a36Sopenharmony_ci goto nla_put_failure; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci switch (pctx->gtp_version) { 160162306a36Sopenharmony_ci case GTP_V0: 160262306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, GTPA_TID, pctx->u.v0.tid, GTPA_PAD) || 160362306a36Sopenharmony_ci nla_put_u16(skb, GTPA_FLOW, pctx->u.v0.flow)) 160462306a36Sopenharmony_ci goto nla_put_failure; 160562306a36Sopenharmony_ci break; 160662306a36Sopenharmony_ci case GTP_V1: 160762306a36Sopenharmony_ci if (nla_put_u32(skb, GTPA_I_TEI, pctx->u.v1.i_tei) || 160862306a36Sopenharmony_ci nla_put_u32(skb, GTPA_O_TEI, pctx->u.v1.o_tei)) 160962306a36Sopenharmony_ci goto nla_put_failure; 161062306a36Sopenharmony_ci break; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci genlmsg_end(skb, genlh); 161362306a36Sopenharmony_ci return 0; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cinlmsg_failure: 161662306a36Sopenharmony_cinla_put_failure: 161762306a36Sopenharmony_ci genlmsg_cancel(skb, genlh); 161862306a36Sopenharmony_ci return -EMSGSIZE; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic int gtp_tunnel_notify(struct pdp_ctx *pctx, u8 cmd, gfp_t allocation) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct sk_buff *msg; 162462306a36Sopenharmony_ci int ret; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, allocation); 162762306a36Sopenharmony_ci if (!msg) 162862306a36Sopenharmony_ci return -ENOMEM; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci ret = gtp_genl_fill_info(msg, 0, 0, 0, cmd, pctx); 163162306a36Sopenharmony_ci if (ret < 0) { 163262306a36Sopenharmony_ci nlmsg_free(msg); 163362306a36Sopenharmony_ci return ret; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci ret = genlmsg_multicast_netns(>p_genl_family, dev_net(pctx->dev), msg, 163762306a36Sopenharmony_ci 0, GTP_GENL_MCGRP, GFP_ATOMIC); 163862306a36Sopenharmony_ci return ret; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct pdp_ctx *pctx = NULL; 164462306a36Sopenharmony_ci struct sk_buff *skb2; 164562306a36Sopenharmony_ci int err; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci if (!info->attrs[GTPA_VERSION]) 164862306a36Sopenharmony_ci return -EINVAL; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci rcu_read_lock(); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); 165362306a36Sopenharmony_ci if (IS_ERR(pctx)) { 165462306a36Sopenharmony_ci err = PTR_ERR(pctx); 165562306a36Sopenharmony_ci goto err_unlock; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci skb2 = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 165962306a36Sopenharmony_ci if (skb2 == NULL) { 166062306a36Sopenharmony_ci err = -ENOMEM; 166162306a36Sopenharmony_ci goto err_unlock; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci err = gtp_genl_fill_info(skb2, NETLINK_CB(skb).portid, info->snd_seq, 166562306a36Sopenharmony_ci 0, info->nlhdr->nlmsg_type, pctx); 166662306a36Sopenharmony_ci if (err < 0) 166762306a36Sopenharmony_ci goto err_unlock_free; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci rcu_read_unlock(); 167062306a36Sopenharmony_ci return genlmsg_unicast(genl_info_net(info), skb2, info->snd_portid); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cierr_unlock_free: 167362306a36Sopenharmony_ci kfree_skb(skb2); 167462306a36Sopenharmony_cierr_unlock: 167562306a36Sopenharmony_ci rcu_read_unlock(); 167662306a36Sopenharmony_ci return err; 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int gtp_genl_dump_pdp(struct sk_buff *skb, 168062306a36Sopenharmony_ci struct netlink_callback *cb) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci struct gtp_dev *last_gtp = (struct gtp_dev *)cb->args[2], *gtp; 168362306a36Sopenharmony_ci int i, j, bucket = cb->args[0], skip = cb->args[1]; 168462306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 168562306a36Sopenharmony_ci struct pdp_ctx *pctx; 168662306a36Sopenharmony_ci struct gtp_net *gn; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci gn = net_generic(net, gtp_net_id); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci if (cb->args[4]) 169162306a36Sopenharmony_ci return 0; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci rcu_read_lock(); 169462306a36Sopenharmony_ci list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { 169562306a36Sopenharmony_ci if (last_gtp && last_gtp != gtp) 169662306a36Sopenharmony_ci continue; 169762306a36Sopenharmony_ci else 169862306a36Sopenharmony_ci last_gtp = NULL; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci for (i = bucket; i < gtp->hash_size; i++) { 170162306a36Sopenharmony_ci j = 0; 170262306a36Sopenharmony_ci hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], 170362306a36Sopenharmony_ci hlist_tid) { 170462306a36Sopenharmony_ci if (j >= skip && 170562306a36Sopenharmony_ci gtp_genl_fill_info(skb, 170662306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 170762306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 170862306a36Sopenharmony_ci NLM_F_MULTI, 170962306a36Sopenharmony_ci cb->nlh->nlmsg_type, pctx)) { 171062306a36Sopenharmony_ci cb->args[0] = i; 171162306a36Sopenharmony_ci cb->args[1] = j; 171262306a36Sopenharmony_ci cb->args[2] = (unsigned long)gtp; 171362306a36Sopenharmony_ci goto out; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci j++; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci skip = 0; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci bucket = 0; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci cb->args[4] = 1; 172262306a36Sopenharmony_ciout: 172362306a36Sopenharmony_ci rcu_read_unlock(); 172462306a36Sopenharmony_ci return skb->len; 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_cistatic int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci struct sk_buff *skb_to_send; 173062306a36Sopenharmony_ci __be32 src_ip, dst_ip; 173162306a36Sopenharmony_ci unsigned int version; 173262306a36Sopenharmony_ci struct gtp_dev *gtp; 173362306a36Sopenharmony_ci struct flowi4 fl4; 173462306a36Sopenharmony_ci struct rtable *rt; 173562306a36Sopenharmony_ci struct sock *sk; 173662306a36Sopenharmony_ci __be16 port; 173762306a36Sopenharmony_ci int len; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci if (!info->attrs[GTPA_VERSION] || 174062306a36Sopenharmony_ci !info->attrs[GTPA_LINK] || 174162306a36Sopenharmony_ci !info->attrs[GTPA_PEER_ADDRESS] || 174262306a36Sopenharmony_ci !info->attrs[GTPA_MS_ADDRESS]) 174362306a36Sopenharmony_ci return -EINVAL; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci version = nla_get_u32(info->attrs[GTPA_VERSION]); 174662306a36Sopenharmony_ci dst_ip = nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); 174762306a36Sopenharmony_ci src_ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); 175062306a36Sopenharmony_ci if (!gtp) 175162306a36Sopenharmony_ci return -ENODEV; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (!gtp->sk_created) 175462306a36Sopenharmony_ci return -EOPNOTSUPP; 175562306a36Sopenharmony_ci if (!(gtp->dev->flags & IFF_UP)) 175662306a36Sopenharmony_ci return -ENETDOWN; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (version == GTP_V0) { 175962306a36Sopenharmony_ci struct gtp0_header *gtp0_h; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci len = LL_RESERVED_SPACE(gtp->dev) + sizeof(struct gtp0_header) + 176262306a36Sopenharmony_ci sizeof(struct iphdr) + sizeof(struct udphdr); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); 176562306a36Sopenharmony_ci if (!skb_to_send) 176662306a36Sopenharmony_ci return -ENOMEM; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci sk = gtp->sk0; 176962306a36Sopenharmony_ci port = htons(GTP0_PORT); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci gtp0_h = skb_push(skb_to_send, sizeof(struct gtp0_header)); 177262306a36Sopenharmony_ci memset(gtp0_h, 0, sizeof(struct gtp0_header)); 177362306a36Sopenharmony_ci gtp0_build_echo_msg(gtp0_h, GTP_ECHO_REQ); 177462306a36Sopenharmony_ci } else if (version == GTP_V1) { 177562306a36Sopenharmony_ci struct gtp1_header_long *gtp1u_h; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci len = LL_RESERVED_SPACE(gtp->dev) + 177862306a36Sopenharmony_ci sizeof(struct gtp1_header_long) + 177962306a36Sopenharmony_ci sizeof(struct iphdr) + sizeof(struct udphdr); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); 178262306a36Sopenharmony_ci if (!skb_to_send) 178362306a36Sopenharmony_ci return -ENOMEM; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci sk = gtp->sk1u; 178662306a36Sopenharmony_ci port = htons(GTP1U_PORT); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci gtp1u_h = skb_push(skb_to_send, 178962306a36Sopenharmony_ci sizeof(struct gtp1_header_long)); 179062306a36Sopenharmony_ci memset(gtp1u_h, 0, sizeof(struct gtp1_header_long)); 179162306a36Sopenharmony_ci gtp1u_build_echo_msg(gtp1u_h, GTP_ECHO_REQ); 179262306a36Sopenharmony_ci } else { 179362306a36Sopenharmony_ci return -ENODEV; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci rt = ip4_route_output_gtp(&fl4, sk, dst_ip, src_ip); 179762306a36Sopenharmony_ci if (IS_ERR(rt)) { 179862306a36Sopenharmony_ci netdev_dbg(gtp->dev, "no route for echo request to %pI4\n", 179962306a36Sopenharmony_ci &dst_ip); 180062306a36Sopenharmony_ci kfree_skb(skb_to_send); 180162306a36Sopenharmony_ci return -ENODEV; 180262306a36Sopenharmony_ci } 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci udp_tunnel_xmit_skb(rt, sk, skb_to_send, 180562306a36Sopenharmony_ci fl4.saddr, fl4.daddr, 180662306a36Sopenharmony_ci fl4.flowi4_tos, 180762306a36Sopenharmony_ci ip4_dst_hoplimit(&rt->dst), 180862306a36Sopenharmony_ci 0, 180962306a36Sopenharmony_ci port, port, 181062306a36Sopenharmony_ci !net_eq(sock_net(sk), 181162306a36Sopenharmony_ci dev_net(gtp->dev)), 181262306a36Sopenharmony_ci false); 181362306a36Sopenharmony_ci return 0; 181462306a36Sopenharmony_ci} 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_cistatic const struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { 181762306a36Sopenharmony_ci [GTPA_LINK] = { .type = NLA_U32, }, 181862306a36Sopenharmony_ci [GTPA_VERSION] = { .type = NLA_U32, }, 181962306a36Sopenharmony_ci [GTPA_TID] = { .type = NLA_U64, }, 182062306a36Sopenharmony_ci [GTPA_PEER_ADDRESS] = { .type = NLA_U32, }, 182162306a36Sopenharmony_ci [GTPA_MS_ADDRESS] = { .type = NLA_U32, }, 182262306a36Sopenharmony_ci [GTPA_FLOW] = { .type = NLA_U16, }, 182362306a36Sopenharmony_ci [GTPA_NET_NS_FD] = { .type = NLA_U32, }, 182462306a36Sopenharmony_ci [GTPA_I_TEI] = { .type = NLA_U32, }, 182562306a36Sopenharmony_ci [GTPA_O_TEI] = { .type = NLA_U32, }, 182662306a36Sopenharmony_ci}; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cistatic const struct genl_small_ops gtp_genl_ops[] = { 182962306a36Sopenharmony_ci { 183062306a36Sopenharmony_ci .cmd = GTP_CMD_NEWPDP, 183162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 183262306a36Sopenharmony_ci .doit = gtp_genl_new_pdp, 183362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 183462306a36Sopenharmony_ci }, 183562306a36Sopenharmony_ci { 183662306a36Sopenharmony_ci .cmd = GTP_CMD_DELPDP, 183762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 183862306a36Sopenharmony_ci .doit = gtp_genl_del_pdp, 183962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 184062306a36Sopenharmony_ci }, 184162306a36Sopenharmony_ci { 184262306a36Sopenharmony_ci .cmd = GTP_CMD_GETPDP, 184362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 184462306a36Sopenharmony_ci .doit = gtp_genl_get_pdp, 184562306a36Sopenharmony_ci .dumpit = gtp_genl_dump_pdp, 184662306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 184762306a36Sopenharmony_ci }, 184862306a36Sopenharmony_ci { 184962306a36Sopenharmony_ci .cmd = GTP_CMD_ECHOREQ, 185062306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 185162306a36Sopenharmony_ci .doit = gtp_genl_send_echo_req, 185262306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 185362306a36Sopenharmony_ci }, 185462306a36Sopenharmony_ci}; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cistatic struct genl_family gtp_genl_family __ro_after_init = { 185762306a36Sopenharmony_ci .name = "gtp", 185862306a36Sopenharmony_ci .version = 0, 185962306a36Sopenharmony_ci .hdrsize = 0, 186062306a36Sopenharmony_ci .maxattr = GTPA_MAX, 186162306a36Sopenharmony_ci .policy = gtp_genl_policy, 186262306a36Sopenharmony_ci .netnsok = true, 186362306a36Sopenharmony_ci .module = THIS_MODULE, 186462306a36Sopenharmony_ci .small_ops = gtp_genl_ops, 186562306a36Sopenharmony_ci .n_small_ops = ARRAY_SIZE(gtp_genl_ops), 186662306a36Sopenharmony_ci .resv_start_op = GTP_CMD_ECHOREQ + 1, 186762306a36Sopenharmony_ci .mcgrps = gtp_genl_mcgrps, 186862306a36Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(gtp_genl_mcgrps), 186962306a36Sopenharmony_ci}; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic int __net_init gtp_net_init(struct net *net) 187262306a36Sopenharmony_ci{ 187362306a36Sopenharmony_ci struct gtp_net *gn = net_generic(net, gtp_net_id); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci INIT_LIST_HEAD(&gn->gtp_dev_list); 187662306a36Sopenharmony_ci return 0; 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_cistatic void __net_exit gtp_net_exit(struct net *net) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci struct gtp_net *gn = net_generic(net, gtp_net_id); 188262306a36Sopenharmony_ci struct gtp_dev *gtp; 188362306a36Sopenharmony_ci LIST_HEAD(list); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci rtnl_lock(); 188662306a36Sopenharmony_ci list_for_each_entry(gtp, &gn->gtp_dev_list, list) 188762306a36Sopenharmony_ci gtp_dellink(gtp->dev, &list); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci unregister_netdevice_many(&list); 189062306a36Sopenharmony_ci rtnl_unlock(); 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_cistatic struct pernet_operations gtp_net_ops = { 189462306a36Sopenharmony_ci .init = gtp_net_init, 189562306a36Sopenharmony_ci .exit = gtp_net_exit, 189662306a36Sopenharmony_ci .id = >p_net_id, 189762306a36Sopenharmony_ci .size = sizeof(struct gtp_net), 189862306a36Sopenharmony_ci}; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_cistatic int __init gtp_init(void) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci int err; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci get_random_bytes(>p_h_initval, sizeof(gtp_h_initval)); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci err = register_pernet_subsys(>p_net_ops); 190762306a36Sopenharmony_ci if (err < 0) 190862306a36Sopenharmony_ci goto error_out; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci err = rtnl_link_register(>p_link_ops); 191162306a36Sopenharmony_ci if (err < 0) 191262306a36Sopenharmony_ci goto unreg_pernet_subsys; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci err = genl_register_family(>p_genl_family); 191562306a36Sopenharmony_ci if (err < 0) 191662306a36Sopenharmony_ci goto unreg_rtnl_link; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci pr_info("GTP module loaded (pdp ctx size %zd bytes)\n", 191962306a36Sopenharmony_ci sizeof(struct pdp_ctx)); 192062306a36Sopenharmony_ci return 0; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ciunreg_rtnl_link: 192362306a36Sopenharmony_ci rtnl_link_unregister(>p_link_ops); 192462306a36Sopenharmony_ciunreg_pernet_subsys: 192562306a36Sopenharmony_ci unregister_pernet_subsys(>p_net_ops); 192662306a36Sopenharmony_cierror_out: 192762306a36Sopenharmony_ci pr_err("error loading GTP module loaded\n"); 192862306a36Sopenharmony_ci return err; 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_cilate_initcall(gtp_init); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic void __exit gtp_fini(void) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci genl_unregister_family(>p_genl_family); 193562306a36Sopenharmony_ci rtnl_link_unregister(>p_link_ops); 193662306a36Sopenharmony_ci unregister_pernet_subsys(>p_net_ops); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci pr_info("GTP module unloaded\n"); 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_cimodule_exit(gtp_fini); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 194362306a36Sopenharmony_ciMODULE_AUTHOR("Harald Welte <hwelte@sysmocom.de>"); 194462306a36Sopenharmony_ciMODULE_DESCRIPTION("Interface driver for GTP encapsulated traffic"); 194562306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("gtp"); 194662306a36Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY("gtp"); 1947