Currently, when the user wants to create GTP device, he has to provide file handles to the sockets created in userspace (IFLA_GTP_FD0, IFLA_GTP_FD1). This behaviour is not ideal, considering the option of adding support for GTP device creation through ip link. Ip link application is not a good place to create such sockets. This patch allows to create GTP device without providing IFLA_GTP_FD0 and IFLA_GTP_FD1 arguments. If the user sets IFLA_GTP_CREATE_SOCKETS attribute, then GTP module takes care of creating UDP sockets by itself. Sockets are created with the commonly known UDP ports used for GTP protocol (GTP0_PORT and GTP1U_PORT). In this case we don't have to provide encap_destroy because no extra deinitialization is needed, everything is covered by udp_tunnel_sock_release. Note: GTP instance created with only this change applied, does not handle GTP Echo Requests. This is implemented in the following patch.
Unified · Split
| @@ -66,8 +66,10 @@ struct gtp_dev { · 65 unmodified lines | ||
| 66 | 66 | |
| 67 | 67 | struct sock *sk0; |
| 68 | 68 | struct sock *sk1u; |
| 69 | + u8 sk_created; | |
| 69 | 70 | |
| 70 | 71 | struct net_device *dev; |
| 72 | + struct net *net; | |
| 71 | 73 | |
| 72 | 74 | unsigned int role; |
| 73 | 75 | unsigned int hash_size; |
| @@ -320,8 +322,16 @@ static void gtp_encap_disable_sock(struct sock *sk) · 246 unmodified lines | ||
| 320 | 322 | |
| 321 | 323 | static void gtp_encap_disable(struct gtp_dev *gtp) |
| 322 | 324 | { |
| 323 | − gtp_encap_disable_sock(gtp->sk0); | |
| 324 | − gtp_encap_disable_sock(gtp->sk1u); | |
| 325 | + if (gtp->sk_created) { | |
| 326 | + udp_tunnel_sock_release(gtp->sk0->sk_socket); | |
| 327 | + udp_tunnel_sock_release(gtp->sk1u->sk_socket); | |
| 328 | + gtp->sk_created = false; | |
| 329 | + gtp->sk0 = NULL; | |
| 330 | + gtp->sk1u = NULL; | |
| 331 | + } else { | |
| 332 | + gtp_encap_disable_sock(gtp->sk0); | |
| 333 | + gtp_encap_disable_sock(gtp->sk1u); | |
| 334 | + } | |
| 325 | 335 | } |
| 326 | 336 | |
| 327 | 337 | /* UDP encapsulation receive handler. See net/ipv4/udp.c. |
| @@ -656,17 +666,69 @@ static void gtp_destructor(struct net_device *dev) · 328 unmodified lines | ||
| 656 | 666 | kfree(gtp->tid_hash); |
| 657 | 667 | } |
| 658 | 668 | |
| 669 | +static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp) | |
| 670 | +{ | |
| 671 | + struct udp_tunnel_sock_cfg tuncfg = {}; | |
| 672 | + struct udp_port_cfg udp_conf = { | |
| 673 | + .local_ip.s_addr = htonl(INADDR_ANY), | |
| 674 | + .family = AF_INET, | |
| 675 | + }; | |
| 676 | + struct net *net = gtp->net; | |
| 677 | + struct socket *sock; | |
| 678 | + int err; | |
| 679 | + | |
| 680 | + if (type == UDP_ENCAP_GTP0) | |
| 681 | + udp_conf.local_udp_port = htons(GTP0_PORT); | |
| 682 | + else if (type == UDP_ENCAP_GTP1U) | |
| 683 | + udp_conf.local_udp_port = htons(GTP1U_PORT); | |
| 684 | + else | |
| 685 | + return ERR_PTR(-EINVAL); | |
| 686 | + | |
| 687 | + err = udp_sock_create(net, &udp_conf, &sock); | |
| 688 | + if (err) | |
| 689 | + return ERR_PTR(err); | |
| 690 | + | |
| 691 | + tuncfg.sk_user_data = gtp; | |
| 692 | + tuncfg.encap_type = type; | |
| 693 | + tuncfg.encap_rcv = gtp_encap_recv; | |
| 694 | + tuncfg.encap_destroy = NULL; | |
| 695 | + | |
| 696 | + setup_udp_tunnel_sock(net, sock, &tuncfg); | |
| 697 | + | |
| 698 | + return sock->sk; | |
| 699 | +} | |
| 700 | + | |
| 701 | +static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[]) | |
| 702 | +{ | |
| 703 | + struct sock *sk1u = NULL; | |
| 704 | + struct sock *sk0 = NULL; | |
| 705 | + | |
| 706 | + sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp); | |
| 707 | + if (IS_ERR(sk0)) | |
| 708 | + return PTR_ERR(sk0); | |
| 709 | + | |
| 710 | + sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp); | |
| 711 | + if (IS_ERR(sk1u)) { | |
| 712 | + udp_tunnel_sock_release(sk0->sk_socket); | |
| 713 | + return PTR_ERR(sk1u); | |
| 714 | + } | |
| 715 | + | |
| 716 | + gtp->sk_created = true; | |
| 717 | + gtp->sk0 = sk0; | |
| 718 | + gtp->sk1u = sk1u; | |
| 719 | + | |
| 720 | + return 0; | |
| 721 | +} | |
| 722 | + | |
| 659 | 723 | static int gtp_newlink(struct net *src_net, struct net_device *dev, |
| 660 | 724 | struct nlattr *tb[], struct nlattr *data[], |
| 661 | 725 | struct netlink_ext_ack *extack) |
| 662 | 726 | { |
| 727 | + unsigned int role = GTP_ROLE_GGSN; | |
| 663 | 728 | struct gtp_dev *gtp; |
| 664 | 729 | struct gtp_net *gn; |
| 665 | 730 | int hashsize, err; |
| 666 | 731 | |
| 667 | − if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) | |
| 668 | − return -EINVAL; | |
| 669 | − | |
| 670 | 732 | gtp = netdev_priv(dev); |
| 671 | 733 | |
| 672 | 734 | if (!data[IFLA_GTP_PDP_HASHSIZE]) { |
| @@ -677,11 +739,23 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, · 4 unmodified lines | ||
| 677 | 739 | hashsize = 1024; |
| 678 | 740 | } |
| 679 | 741 | |
| 742 | + if (data[IFLA_GTP_ROLE]) { | |
| 743 | + role = nla_get_u32(data[IFLA_GTP_ROLE]); | |
| 744 | + if (role > GTP_ROLE_SGSN) | |
| 745 | + return -EINVAL; | |
| 746 | + } | |
| 747 | + gtp->role = role; | |
| 748 | + | |
| 749 | + gtp->net = src_net; | |
| 750 | + | |
| 680 | 751 | err = gtp_hashtable_new(gtp, hashsize); |
| 681 | 752 | if (err < 0) |
| 682 | 753 | return err; |
| 683 | 754 | |
| 684 | − err = gtp_encap_enable(gtp, data); | |
| 755 | + if (data[IFLA_GTP_CREATE_SOCKETS]) | |
| 756 | + err = gtp_create_sockets(gtp, data); | |
| 757 | + else | |
| 758 | + err = gtp_encap_enable(gtp, data); | |
| 685 | 759 | if (err < 0) |
| 686 | 760 | goto out_hashtable; |
| 687 | 761 | |
| @@ -726,6 +800,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { · 38 unmodified lines | ||
| 726 | 800 | [IFLA_GTP_FD1] = { .type = NLA_U32 }, |
| 727 | 801 | [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, |
| 728 | 802 | [IFLA_GTP_ROLE] = { .type = NLA_U32 }, |
| 803 | + [IFLA_GTP_CREATE_SOCKETS] = { .type = NLA_U8 }, | |
| 729 | 804 | }; |
| 730 | 805 | |
| 731 | 806 | static int gtp_validate(struct nlattr *tb[], struct nlattr *data[], |
| @@ -848,7 +923,9 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) · 116 unmodified lines | ||
| 848 | 923 | { |
| 849 | 924 | struct sock *sk1u = NULL; |
| 850 | 925 | struct sock *sk0 = NULL; |
| 851 | − unsigned int role = GTP_ROLE_GGSN; | |
| 926 | + | |
| 927 | + if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) | |
| 928 | + return -EINVAL; | |
| 852 | 929 | |
| 853 | 930 | if (data[IFLA_GTP_FD0]) { |
| 854 | 931 | u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); |
| @@ -868,18 +945,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) · 13 unmodified lines | ||
| 868 | 945 | } |
| 869 | 946 | } |
| 870 | 947 | |
| 871 | − if (data[IFLA_GTP_ROLE]) { | |
| 872 | − role = nla_get_u32(data[IFLA_GTP_ROLE]); | |
| 873 | − if (role > GTP_ROLE_SGSN) { | |
| 874 | − gtp_encap_disable_sock(sk0); | |
| 875 | − gtp_encap_disable_sock(sk1u); | |
| 876 | − return -EINVAL; | |
| 877 | − } | |
| 878 | − } | |
| 879 | − | |
| 880 | 948 | gtp->sk0 = sk0; |
| 881 | 949 | gtp->sk1u = sk1u; |
| 882 | − gtp->role = role; | |
| 883 | 950 | |
| 884 | 951 | return 0; |
| 885 | 952 | } |
| @@ -887,6 +887,7 @@ enum { · 886 unmodified lines | ||
| 887 | 887 | IFLA_GTP_FD1, |
| 888 | 888 | IFLA_GTP_PDP_HASHSIZE, |
| 889 | 889 | IFLA_GTP_ROLE, |
| 890 | + IFLA_GTP_CREATE_SOCKETS, | |
| 890 | 891 | __IFLA_GTP_MAX, |
| 891 | 892 | }; |
| 892 | 893 | #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) |