From patchwork Thu Jul 13 00:44:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiannan Ouyang X-Patchwork-Id: 787463 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3x7HDb377nz9ryk for ; Thu, 13 Jul 2017 10:45:51 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=fb.com header.i=@fb.com header.b="SanrBTaH"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751356AbdGMApt (ORCPT ); Wed, 12 Jul 2017 20:45:49 -0400 Received: from mx0b-00082601.pphosted.com ([67.231.153.30]:47002 "EHLO mx0b-00082601.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751171AbdGMApk (ORCPT ); Wed, 12 Jul 2017 20:45:40 -0400 Received: from pps.filterd (m0109332.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v6D0hfgi005737 for ; Wed, 12 Jul 2017 17:45:39 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=facebook; bh=PQl/5vhtv4zkhEm5Akhd4ltAUxtFKK6dMZ7UCsdQzS0=; b=SanrBTaHq10Eq8q4yB1J+8MLYKlUN01mwITbsJ8NSjTx0kKQj5/YbDT5HxTwB6mg6lLS An8QlCN7O0Y9GGdtfnwX3gaCepGEFyBX+8sBTbGVJ3JXHE0lcuReBy3ADUQDNY+4NTL+ twjD+eiLTaIBm2+mljhu6pnM+7RTjNKzbS4= Received: from mail.thefacebook.com ([199.201.64.23]) by mx0a-00082601.pphosted.com with ESMTP id 2bntfdgrvd-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Wed, 12 Jul 2017 17:45:39 -0700 Received: from mx-out.facebook.com (192.168.52.123) by PRN-CHUB05.TheFacebook.com (192.168.16.15) with Microsoft SMTP Server (TLS) id 14.3.319.2; Wed, 12 Jul 2017 17:45:38 -0700 Received: from facebook.com (2401:db00:21:601e:face:0:45:0) by mx-out.facebook.com (2401:db00:2050:5102:face:0000:003b:0000) with ESMTP id 9630d68a676411e7b5e36f8865c8440f-70ed9150 for ; Wed, 12 Jul 2017 17:45:37 -0700 Received: by dev10567.prn2.facebook.com (Postfix, from userid 117791) id 35B9B2BE8915; Wed, 12 Jul 2017 17:45:36 -0700 (PDT) Smtp-Origin-Hostprefix: dev From: Jiannan Ouyang Smtp-Origin-Hostname: dev10567.prn2.facebook.com To: , , CC: , , , , , , , Jiannan Ouyang Smtp-Origin-Cluster: prn2c22 Subject: [PATCH net-next v1 2/3] gtp: Support creating flow-based gtp net_device Date: Wed, 12 Jul 2017 17:44:54 -0700 Message-ID: <20170713004455.3946570-3-ouyangj@fb.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170713004455.3946570-1-ouyangj@fb.com> References: <20170713004455.3946570-1-ouyangj@fb.com> X-FB-Internal: Safe MIME-Version: 1.0 X-Proofpoint-Spam-Reason: safe X-FB-Internal: Safe X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-07-12_08:, , signatures=0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add the gtp_create_flow_based_dev() interface to create flow-based gtp net_device, which sets gtp->collect_md. Under flow-based mode, UDP sockets are created and maintained in kernel. Signed-off-by: Jiannan Ouyang --- drivers/net/gtp.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++- include/net/gtp.h | 8 ++ 2 files changed, 217 insertions(+), 4 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 5a7b504..09712c9 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -642,9 +642,94 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); +static void gtp_hashtable_free(struct gtp_dev *gtp); +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); + +static int gtp_change_mtu(struct net_device *dev, int new_mtu, bool strict) +{ + int max_mtu = IP_MAX_MTU - dev->hard_header_len - sizeof(struct iphdr) + - sizeof(struct udphdr) - sizeof(struct gtp1_header); + + if (new_mtu < ETH_MIN_MTU) + return -EINVAL; + + if (new_mtu > max_mtu) { + if (strict) + return -EINVAL; + + new_mtu = max_mtu; + } + + dev->mtu = new_mtu; + return 0; +} + +static int gtp_dev_open(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + struct net *net = gtp->net; + struct socket *sock1u; + struct socket *sock0; + struct udp_tunnel_sock_cfg tunnel_cfg; + struct udp_port_cfg udp_conf; + int err; + + memset(&udp_conf, 0, sizeof(udp_conf)); + + udp_conf.family = AF_INET; + udp_conf.local_ip.s_addr = htonl(INADDR_ANY); + udp_conf.local_udp_port = htons(GTP1U_PORT); + + err = udp_sock_create(gtp->net, &udp_conf, &sock1u); + if (err < 0) + return err; + + udp_conf.local_udp_port = htons(GTP0_PORT); + err = udp_sock_create(gtp->net, &udp_conf, &sock0); + if (err < 0) + return err; + + memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); + tunnel_cfg.sk_user_data = gtp; + tunnel_cfg.encap_rcv = gtp_encap_recv; + tunnel_cfg.encap_destroy = gtp_encap_destroy; + tunnel_cfg.encap_type = UDP_ENCAP_GTP0; + setup_udp_tunnel_sock(net, sock0, &tunnel_cfg); + + tunnel_cfg.encap_type = UDP_ENCAP_GTP1U; + + setup_udp_tunnel_sock(net, sock1u, &tunnel_cfg); + + sock_hold(sock0->sk); + sock_hold(sock1u->sk); + + gtp->sk0 = sock0->sk; + gtp->sk1u = sock1u->sk; + + return 0; +} + +static int gtp_dev_stop(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + struct sock *sk = gtp->sk1u; + + udp_tunnel_sock_release(gtp->sk0->sk_socket); + udp_tunnel_sock_release(gtp->sk1u->sk_socket); + + udp_sk(sk)->encap_type = 0; + rcu_assign_sk_user_data(sk, NULL); + sock_put(sk); + + return 0; +} + static const struct net_device_ops gtp_netdev_ops = { .ndo_init = gtp_dev_init, .ndo_uninit = gtp_dev_uninit, + .ndo_open = gtp_dev_open, + .ndo_stop = gtp_dev_stop, .ndo_start_xmit = gtp_dev_xmit, .ndo_get_stats64 = ip_tunnel_get_stats64, }; @@ -672,10 +757,6 @@ static void gtp_link_setup(struct net_device *dev) sizeof(struct gtp0_header); } -static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); -static void gtp_hashtable_free(struct gtp_dev *gtp); -static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); - static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) @@ -780,6 +861,130 @@ static struct rtnl_link_ops gtp_link_ops __read_mostly = { .fill_info = gtp_fill_info, }; +static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port) +{ + memset(info, 0, sizeof(*info)); + info->key.tp_dst = htons(dst_port); +} + +static struct gtp_dev *gtp_find_flow_based_dev( + struct net *net) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp, *t = NULL; + + list_for_each_entry(gtp, &gn->gtp_dev_list, list) { + if (gtp->collect_md) + t = gtp; + } + + return t; +} + +static int gtp_configure(struct net *net, struct net_device *dev, + const struct ip_tunnel_info *info) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp = netdev_priv(dev); + int err; + + gtp->net = net; + gtp->dev = dev; + + if (gtp_find_flow_based_dev(net)) + return -EBUSY; + + dev->netdev_ops = >p_netdev_ops; + dev->priv_destructor = free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + + /* Zero header length. */ + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + + dev->priv_flags |= IFF_NO_QUEUE; + dev->features |= NETIF_F_LLTX; + netif_keep_dst(dev); + + /* Assume largest header, ie. GTPv0. */ + dev->needed_headroom = LL_MAX_HEADER + + sizeof(struct iphdr) + + sizeof(struct udphdr) + + sizeof(struct gtp0_header); + + dst_cache_reset(>p->info.dst_cache); + gtp->info = *info; + gtp->collect_md = true; + + err = gtp_hashtable_new(gtp, GTP_PDP_HASHSIZE); // JO: when to free?? + if (err < 0) { + pr_err("Error gtp_hashtable_new"); + return err; + } + + err = register_netdevice(dev); + if (err) { + pr_err("Error when registering net device"); + return err; + } + + list_add_rcu(>p->list, &gn->gtp_dev_list); + return 0; +} + +struct net_device *gtp_create_flow_based_dev(struct net *net, + const char *name, + u8 name_assign_type, + u16 dst_port) +{ + struct nlattr *tb[IFLA_MAX + 1]; + struct net_device *dev; + struct ip_tunnel_info info; + LIST_HEAD(list_kill); + int err; + + memset(&tb, 0, sizeof(tb)); + dev = rtnl_create_link(net, name, name_assign_type, + >p_link_ops, tb); + if (IS_ERR(dev)) { + pr_err("error rtnl_create_link"); + return dev; + } + + init_tnl_info(&info, dst_port); + err = gtp_configure(net, dev, &info); + if (err < 0) { + pr_err("error gtp_configure"); + free_netdev(dev); + return ERR_PTR(err); + } + + /* openvswitch users expect packet sizes to be unrestricted, + * so set the largest MTU we can. + */ + err = gtp_change_mtu(dev, IP_MAX_MTU, false); + if (err) { + pr_err("error gtp_change_mtu"); + goto err; + } + + err = rtnl_configure_link(dev, NULL); + if (err < 0) { + pr_err("error rtnl_configure_link"); + goto err; + } + + return dev; + +err: + gtp_dellink(dev, &list_kill); + unregister_netdevice_many(&list_kill); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(gtp_create_flow_based_dev); + static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) { int i; diff --git a/include/net/gtp.h b/include/net/gtp.h index 6398891..a76b26c 100644 --- a/include/net/gtp.h +++ b/include/net/gtp.h @@ -31,4 +31,12 @@ struct gtp1_header { /* According to 3GPP TS 29.060. */ #define GTP1_F_EXTHDR 0x04 #define GTP1_F_MASK 0x07 +#ifdef CONFIG_INET + +struct net_device *gtp_create_flow_based_dev( + struct net *net, const char *name, + u8 name_assign_type, u16 dst_port); + +#endif /*ifdef CONFIG_INET */ + #endif