18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* L2TPv3 IP encapsulation support 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008,2009,2010 Katalix Systems Ltd 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <asm/ioctls.h> 108c2ecf20Sopenharmony_ci#include <linux/icmp.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/random.h> 148c2ecf20Sopenharmony_ci#include <linux/socket.h> 158c2ecf20Sopenharmony_ci#include <linux/l2tp.h> 168c2ecf20Sopenharmony_ci#include <linux/in.h> 178c2ecf20Sopenharmony_ci#include <net/sock.h> 188c2ecf20Sopenharmony_ci#include <net/ip.h> 198c2ecf20Sopenharmony_ci#include <net/icmp.h> 208c2ecf20Sopenharmony_ci#include <net/udp.h> 218c2ecf20Sopenharmony_ci#include <net/inet_common.h> 228c2ecf20Sopenharmony_ci#include <net/tcp_states.h> 238c2ecf20Sopenharmony_ci#include <net/protocol.h> 248c2ecf20Sopenharmony_ci#include <net/xfrm.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "l2tp_core.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct l2tp_ip_sock { 298c2ecf20Sopenharmony_ci /* inet_sock has to be the first member of l2tp_ip_sock */ 308c2ecf20Sopenharmony_ci struct inet_sock inet; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci u32 conn_id; 338c2ecf20Sopenharmony_ci u32 peer_conn_id; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(l2tp_ip_lock); 378c2ecf20Sopenharmony_cistatic struct hlist_head l2tp_ip_table; 388c2ecf20Sopenharmony_cistatic struct hlist_head l2tp_ip_bind_table; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline struct l2tp_ip_sock *l2tp_ip_sk(const struct sock *sk) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return (struct l2tp_ip_sock *)sk; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct sock *__l2tp_ip_bind_lookup(const struct net *net, __be32 laddr, 468c2ecf20Sopenharmony_ci __be32 raddr, int dif, u32 tunnel_id) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct sock *sk; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci sk_for_each_bound(sk, &l2tp_ip_bind_table) { 518c2ecf20Sopenharmony_ci const struct l2tp_ip_sock *l2tp = l2tp_ip_sk(sk); 528c2ecf20Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 558c2ecf20Sopenharmony_ci continue; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (sk->sk_bound_dev_if && dif && sk->sk_bound_dev_if != dif) 588c2ecf20Sopenharmony_ci continue; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (inet->inet_rcv_saddr && laddr && 618c2ecf20Sopenharmony_ci inet->inet_rcv_saddr != laddr) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (inet->inet_daddr && raddr && inet->inet_daddr != raddr) 658c2ecf20Sopenharmony_ci continue; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (l2tp->conn_id != tunnel_id) 688c2ecf20Sopenharmony_ci continue; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci goto found; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci sk = NULL; 748c2ecf20Sopenharmony_cifound: 758c2ecf20Sopenharmony_ci return sk; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* When processing receive frames, there are two cases to 798c2ecf20Sopenharmony_ci * consider. Data frames consist of a non-zero session-id and an 808c2ecf20Sopenharmony_ci * optional cookie. Control frames consist of a regular L2TP header 818c2ecf20Sopenharmony_ci * preceded by 32-bits of zeros. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * L2TPv3 Session Header Over IP 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * 0 1 2 3 868c2ecf20Sopenharmony_ci * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 878c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 888c2ecf20Sopenharmony_ci * | Session ID | 898c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 908c2ecf20Sopenharmony_ci * | Cookie (optional, maximum 64 bits)... 918c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 928c2ecf20Sopenharmony_ci * | 938c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * L2TPv3 Control Message Header Over IP 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * 0 1 2 3 988c2ecf20Sopenharmony_ci * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 998c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1008c2ecf20Sopenharmony_ci * | (32 bits of zeros) | 1018c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1028c2ecf20Sopenharmony_ci * |T|L|x|x|S|x|x|x|x|x|x|x| Ver | Length | 1038c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1048c2ecf20Sopenharmony_ci * | Control Connection ID | 1058c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1068c2ecf20Sopenharmony_ci * | Ns | Nr | 1078c2ecf20Sopenharmony_ci * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * All control frames are passed to userspace. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic int l2tp_ip_recv(struct sk_buff *skb) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 1148c2ecf20Sopenharmony_ci struct sock *sk; 1158c2ecf20Sopenharmony_ci u32 session_id; 1168c2ecf20Sopenharmony_ci u32 tunnel_id; 1178c2ecf20Sopenharmony_ci unsigned char *ptr, *optr; 1188c2ecf20Sopenharmony_ci struct l2tp_session *session; 1198c2ecf20Sopenharmony_ci struct l2tp_tunnel *tunnel = NULL; 1208c2ecf20Sopenharmony_ci struct iphdr *iph; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 4)) 1238c2ecf20Sopenharmony_ci goto discard; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Point to L2TP header */ 1268c2ecf20Sopenharmony_ci optr = skb->data; 1278c2ecf20Sopenharmony_ci ptr = skb->data; 1288c2ecf20Sopenharmony_ci session_id = ntohl(*((__be32 *)ptr)); 1298c2ecf20Sopenharmony_ci ptr += 4; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* RFC3931: L2TP/IP packets have the first 4 bytes containing 1328c2ecf20Sopenharmony_ci * the session_id. If it is 0, the packet is a L2TP control 1338c2ecf20Sopenharmony_ci * frame and the session_id value can be discarded. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (session_id == 0) { 1368c2ecf20Sopenharmony_ci __skb_pull(skb, 4); 1378c2ecf20Sopenharmony_ci goto pass_up; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Ok, this is a data packet. Lookup the session. */ 1418c2ecf20Sopenharmony_ci session = l2tp_session_get(net, session_id); 1428c2ecf20Sopenharmony_ci if (!session) 1438c2ecf20Sopenharmony_ci goto discard; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci tunnel = session->tunnel; 1468c2ecf20Sopenharmony_ci if (!tunnel) 1478c2ecf20Sopenharmony_ci goto discard_sess; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) 1508c2ecf20Sopenharmony_ci goto discard_sess; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci l2tp_recv_common(session, skb, ptr, optr, 0, skb->len); 1538c2ecf20Sopenharmony_ci l2tp_session_dec_refcount(session); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cipass_up: 1588c2ecf20Sopenharmony_ci /* Get the tunnel_id from the L2TP header */ 1598c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 12)) 1608c2ecf20Sopenharmony_ci goto discard; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if ((skb->data[0] & 0xc0) != 0xc0) 1638c2ecf20Sopenharmony_ci goto discard; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci tunnel_id = ntohl(*(__be32 *)&skb->data[4]); 1668c2ecf20Sopenharmony_ci iph = (struct iphdr *)skb_network_header(skb); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci read_lock_bh(&l2tp_ip_lock); 1698c2ecf20Sopenharmony_ci sk = __l2tp_ip_bind_lookup(net, iph->daddr, iph->saddr, inet_iif(skb), 1708c2ecf20Sopenharmony_ci tunnel_id); 1718c2ecf20Sopenharmony_ci if (!sk) { 1728c2ecf20Sopenharmony_ci read_unlock_bh(&l2tp_ip_lock); 1738c2ecf20Sopenharmony_ci goto discard; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci sock_hold(sk); 1768c2ecf20Sopenharmony_ci read_unlock_bh(&l2tp_ip_lock); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) 1798c2ecf20Sopenharmony_ci goto discard_put; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci nf_reset_ct(skb); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return sk_receive_skb(sk, skb, 1); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cidiscard_sess: 1868c2ecf20Sopenharmony_ci l2tp_session_dec_refcount(session); 1878c2ecf20Sopenharmony_ci goto discard; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cidiscard_put: 1908c2ecf20Sopenharmony_ci sock_put(sk); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cidiscard: 1938c2ecf20Sopenharmony_ci kfree_skb(skb); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int l2tp_ip_hash(struct sock *sk) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci if (sk_unhashed(sk)) { 2008c2ecf20Sopenharmony_ci write_lock_bh(&l2tp_ip_lock); 2018c2ecf20Sopenharmony_ci sk_add_node(sk, &l2tp_ip_table); 2028c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void l2tp_ip_unhash(struct sock *sk) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci if (sk_unhashed(sk)) 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci write_lock_bh(&l2tp_ip_lock); 2128c2ecf20Sopenharmony_ci sk_del_node_init(sk); 2138c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int l2tp_ip_open(struct sock *sk) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci /* Prevent autobind. We don't have ports. */ 2198c2ecf20Sopenharmony_ci inet_sk(sk)->inet_num = IPPROTO_L2TP; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci l2tp_ip_hash(sk); 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void l2tp_ip_close(struct sock *sk, long timeout) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci write_lock_bh(&l2tp_ip_lock); 2288c2ecf20Sopenharmony_ci hlist_del_init(&sk->sk_bind_node); 2298c2ecf20Sopenharmony_ci sk_del_node_init(sk); 2308c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 2318c2ecf20Sopenharmony_ci sk_common_release(sk); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void l2tp_ip_destroy_sock(struct sock *sk) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk); 2378c2ecf20Sopenharmony_ci struct sk_buff *skb; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) 2408c2ecf20Sopenharmony_ci kfree_skb(skb); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (tunnel) 2438c2ecf20Sopenharmony_ci l2tp_tunnel_delete(tunnel); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 2498c2ecf20Sopenharmony_ci struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *)uaddr; 2508c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 2518c2ecf20Sopenharmony_ci int ret; 2528c2ecf20Sopenharmony_ci int chk_addr_ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_l2tpip)) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci if (addr->l2tp_family != AF_INET) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci lock_sock(sk); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ret = -EINVAL; 2628c2ecf20Sopenharmony_ci if (!sock_flag(sk, SOCK_ZAPPED)) 2638c2ecf20Sopenharmony_ci goto out; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_CLOSE) 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci chk_addr_ret = inet_addr_type(net, addr->l2tp_addr.s_addr); 2698c2ecf20Sopenharmony_ci ret = -EADDRNOTAVAIL; 2708c2ecf20Sopenharmony_ci if (addr->l2tp_addr.s_addr && chk_addr_ret != RTN_LOCAL && 2718c2ecf20Sopenharmony_ci chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (addr->l2tp_addr.s_addr) { 2758c2ecf20Sopenharmony_ci inet->inet_rcv_saddr = addr->l2tp_addr.s_addr; 2768c2ecf20Sopenharmony_ci inet->inet_saddr = addr->l2tp_addr.s_addr; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) 2798c2ecf20Sopenharmony_ci inet->inet_saddr = 0; /* Use device */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci write_lock_bh(&l2tp_ip_lock); 2828c2ecf20Sopenharmony_ci if (__l2tp_ip_bind_lookup(net, addr->l2tp_addr.s_addr, 0, 2838c2ecf20Sopenharmony_ci sk->sk_bound_dev_if, addr->l2tp_conn_id)) { 2848c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 2858c2ecf20Sopenharmony_ci ret = -EADDRINUSE; 2868c2ecf20Sopenharmony_ci goto out; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci sk_dst_reset(sk); 2908c2ecf20Sopenharmony_ci l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci sk_add_bind_node(sk, &l2tp_ip_bind_table); 2938c2ecf20Sopenharmony_ci sk_del_node_init(sk); 2948c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = 0; 2978c2ecf20Sopenharmony_ci sock_reset_flag(sk, SOCK_ZAPPED); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciout: 3008c2ecf20Sopenharmony_ci release_sock(sk); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr; 3088c2ecf20Sopenharmony_ci int rc; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (addr_len < sizeof(*lsa)) 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (ipv4_is_multicast(lsa->l2tp_addr.s_addr)) 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci lock_sock(sk); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Must bind first - autobinding does not work */ 3198c2ecf20Sopenharmony_ci if (sock_flag(sk, SOCK_ZAPPED)) { 3208c2ecf20Sopenharmony_ci rc = -EINVAL; 3218c2ecf20Sopenharmony_ci goto out_sk; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rc = __ip4_datagram_connect(sk, uaddr, addr_len); 3258c2ecf20Sopenharmony_ci if (rc < 0) 3268c2ecf20Sopenharmony_ci goto out_sk; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci write_lock_bh(&l2tp_ip_lock); 3318c2ecf20Sopenharmony_ci hlist_del_init(&sk->sk_bind_node); 3328c2ecf20Sopenharmony_ci sk_add_bind_node(sk, &l2tp_ip_bind_table); 3338c2ecf20Sopenharmony_ci write_unlock_bh(&l2tp_ip_lock); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciout_sk: 3368c2ecf20Sopenharmony_ci release_sock(sk); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return rc; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int l2tp_ip_disconnect(struct sock *sk, int flags) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci if (sock_flag(sk, SOCK_ZAPPED)) 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return __udp_disconnect(sk, flags); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr, 3508c2ecf20Sopenharmony_ci int peer) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 3538c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 3548c2ecf20Sopenharmony_ci struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk); 3558c2ecf20Sopenharmony_ci struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci memset(lsa, 0, sizeof(*lsa)); 3588c2ecf20Sopenharmony_ci lsa->l2tp_family = AF_INET; 3598c2ecf20Sopenharmony_ci if (peer) { 3608c2ecf20Sopenharmony_ci if (!inet->inet_dport) 3618c2ecf20Sopenharmony_ci return -ENOTCONN; 3628c2ecf20Sopenharmony_ci lsa->l2tp_conn_id = lsk->peer_conn_id; 3638c2ecf20Sopenharmony_ci lsa->l2tp_addr.s_addr = inet->inet_daddr; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci __be32 addr = inet->inet_rcv_saddr; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!addr) 3688c2ecf20Sopenharmony_ci addr = inet->inet_saddr; 3698c2ecf20Sopenharmony_ci lsa->l2tp_conn_id = lsk->conn_id; 3708c2ecf20Sopenharmony_ci lsa->l2tp_addr.s_addr = addr; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci return sizeof(*lsa); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int rc; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Charge it to the socket, dropping if the queue is full. */ 3808c2ecf20Sopenharmony_ci rc = sock_queue_rcv_skb(sk, skb); 3818c2ecf20Sopenharmony_ci if (rc < 0) 3828c2ecf20Sopenharmony_ci goto drop; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cidrop: 3878c2ecf20Sopenharmony_ci IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS); 3888c2ecf20Sopenharmony_ci kfree_skb(skb); 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* Userspace will call sendmsg() on the tunnel socket to send L2TP 3938c2ecf20Sopenharmony_ci * control frames. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_cistatic int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct sk_buff *skb; 3988c2ecf20Sopenharmony_ci int rc; 3998c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 4008c2ecf20Sopenharmony_ci struct rtable *rt = NULL; 4018c2ecf20Sopenharmony_ci struct flowi4 *fl4; 4028c2ecf20Sopenharmony_ci int connected = 0; 4038c2ecf20Sopenharmony_ci __be32 daddr; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci lock_sock(sk); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci rc = -ENOTCONN; 4088c2ecf20Sopenharmony_ci if (sock_flag(sk, SOCK_DEAD)) 4098c2ecf20Sopenharmony_ci goto out; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Get and verify the address. */ 4128c2ecf20Sopenharmony_ci if (msg->msg_name) { 4138c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_l2tpip *, lip, msg->msg_name); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci rc = -EINVAL; 4168c2ecf20Sopenharmony_ci if (msg->msg_namelen < sizeof(*lip)) 4178c2ecf20Sopenharmony_ci goto out; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (lip->l2tp_family != AF_INET) { 4208c2ecf20Sopenharmony_ci rc = -EAFNOSUPPORT; 4218c2ecf20Sopenharmony_ci if (lip->l2tp_family != AF_UNSPEC) 4228c2ecf20Sopenharmony_ci goto out; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci daddr = lip->l2tp_addr.s_addr; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci rc = -EDESTADDRREQ; 4288c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 4298c2ecf20Sopenharmony_ci goto out; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci daddr = inet->inet_daddr; 4328c2ecf20Sopenharmony_ci connected = 1; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Allocate a socket buffer */ 4368c2ecf20Sopenharmony_ci rc = -ENOMEM; 4378c2ecf20Sopenharmony_ci skb = sock_wmalloc(sk, 2 + NET_SKB_PAD + sizeof(struct iphdr) + 4388c2ecf20Sopenharmony_ci 4 + len, 0, GFP_KERNEL); 4398c2ecf20Sopenharmony_ci if (!skb) 4408c2ecf20Sopenharmony_ci goto error; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Reserve space for headers, putting IP header on 4-byte boundary. */ 4438c2ecf20Sopenharmony_ci skb_reserve(skb, 2 + NET_SKB_PAD); 4448c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 4458c2ecf20Sopenharmony_ci skb_reserve(skb, sizeof(struct iphdr)); 4468c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Insert 0 session_id */ 4498c2ecf20Sopenharmony_ci *((__be32 *)skb_put(skb, 4)) = 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Copy user data into skb */ 4528c2ecf20Sopenharmony_ci rc = memcpy_from_msg(skb_put(skb, len), msg, len); 4538c2ecf20Sopenharmony_ci if (rc < 0) { 4548c2ecf20Sopenharmony_ci kfree_skb(skb); 4558c2ecf20Sopenharmony_ci goto error; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci fl4 = &inet->cork.fl.u.ip4; 4598c2ecf20Sopenharmony_ci if (connected) 4608c2ecf20Sopenharmony_ci rt = (struct rtable *)__sk_dst_check(sk, 0); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci rcu_read_lock(); 4638c2ecf20Sopenharmony_ci if (!rt) { 4648c2ecf20Sopenharmony_ci const struct ip_options_rcu *inet_opt; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci inet_opt = rcu_dereference(inet->inet_opt); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Use correct destination address if we have options. */ 4698c2ecf20Sopenharmony_ci if (inet_opt && inet_opt->opt.srr) 4708c2ecf20Sopenharmony_ci daddr = inet_opt->opt.faddr; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* If this fails, retransmit mechanism of transport layer will 4738c2ecf20Sopenharmony_ci * keep trying until route appears or the connection times 4748c2ecf20Sopenharmony_ci * itself out. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci rt = ip_route_output_ports(sock_net(sk), fl4, sk, 4778c2ecf20Sopenharmony_ci daddr, inet->inet_saddr, 4788c2ecf20Sopenharmony_ci inet->inet_dport, inet->inet_sport, 4798c2ecf20Sopenharmony_ci sk->sk_protocol, RT_CONN_FLAGS(sk), 4808c2ecf20Sopenharmony_ci sk->sk_bound_dev_if); 4818c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 4828c2ecf20Sopenharmony_ci goto no_route; 4838c2ecf20Sopenharmony_ci if (connected) { 4848c2ecf20Sopenharmony_ci sk_setup_caps(sk, &rt->dst); 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 4878c2ecf20Sopenharmony_ci goto xmit; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* We dont need to clone dst here, it is guaranteed to not disappear. 4928c2ecf20Sopenharmony_ci * __dev_xmit_skb() might force a refcount if needed. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci skb_dst_set_noref(skb, &rt->dst); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cixmit: 4978c2ecf20Sopenharmony_ci /* Queue the packet to IP for output */ 4988c2ecf20Sopenharmony_ci rc = ip_queue_xmit(sk, skb, &inet->cork.fl); 4998c2ecf20Sopenharmony_ci rcu_read_unlock(); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cierror: 5028c2ecf20Sopenharmony_ci if (rc >= 0) 5038c2ecf20Sopenharmony_ci rc = len; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciout: 5068c2ecf20Sopenharmony_ci release_sock(sk); 5078c2ecf20Sopenharmony_ci return rc; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cino_route: 5108c2ecf20Sopenharmony_ci rcu_read_unlock(); 5118c2ecf20Sopenharmony_ci IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); 5128c2ecf20Sopenharmony_ci kfree_skb(skb); 5138c2ecf20Sopenharmony_ci rc = -EHOSTUNREACH; 5148c2ecf20Sopenharmony_ci goto out; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int l2tp_ip_recvmsg(struct sock *sk, struct msghdr *msg, 5188c2ecf20Sopenharmony_ci size_t len, int noblock, int flags, int *addr_len) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 5218c2ecf20Sopenharmony_ci size_t copied = 0; 5228c2ecf20Sopenharmony_ci int err = -EOPNOTSUPP; 5238c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 5248c2ecf20Sopenharmony_ci struct sk_buff *skb; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (flags & MSG_OOB) 5278c2ecf20Sopenharmony_ci goto out; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci skb = skb_recv_datagram(sk, flags, noblock, &err); 5308c2ecf20Sopenharmony_ci if (!skb) 5318c2ecf20Sopenharmony_ci goto out; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci copied = skb->len; 5348c2ecf20Sopenharmony_ci if (len < copied) { 5358c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 5368c2ecf20Sopenharmony_ci copied = len; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 5408c2ecf20Sopenharmony_ci if (err) 5418c2ecf20Sopenharmony_ci goto done; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci sock_recv_timestamp(msg, sk, skb); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Copy the address. */ 5468c2ecf20Sopenharmony_ci if (sin) { 5478c2ecf20Sopenharmony_ci sin->sin_family = AF_INET; 5488c2ecf20Sopenharmony_ci sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 5498c2ecf20Sopenharmony_ci sin->sin_port = 0; 5508c2ecf20Sopenharmony_ci memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); 5518c2ecf20Sopenharmony_ci *addr_len = sizeof(*sin); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci if (inet->cmsg_flags) 5548c2ecf20Sopenharmony_ci ip_cmsg_recv(msg, skb); 5558c2ecf20Sopenharmony_ci if (flags & MSG_TRUNC) 5568c2ecf20Sopenharmony_ci copied = skb->len; 5578c2ecf20Sopenharmony_cidone: 5588c2ecf20Sopenharmony_ci skb_free_datagram(sk, skb); 5598c2ecf20Sopenharmony_ciout: 5608c2ecf20Sopenharmony_ci return err ? err : copied; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ciint l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct sk_buff *skb; 5668c2ecf20Sopenharmony_ci int amount; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci switch (cmd) { 5698c2ecf20Sopenharmony_ci case SIOCOUTQ: 5708c2ecf20Sopenharmony_ci amount = sk_wmem_alloc_get(sk); 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case SIOCINQ: 5738c2ecf20Sopenharmony_ci spin_lock_bh(&sk->sk_receive_queue.lock); 5748c2ecf20Sopenharmony_ci skb = skb_peek(&sk->sk_receive_queue); 5758c2ecf20Sopenharmony_ci amount = skb ? skb->len : 0; 5768c2ecf20Sopenharmony_ci spin_unlock_bh(&sk->sk_receive_queue.lock); 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return put_user(amount, (int __user *)arg); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(l2tp_ioctl); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic struct proto l2tp_ip_prot = { 5888c2ecf20Sopenharmony_ci .name = "L2TP/IP", 5898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5908c2ecf20Sopenharmony_ci .init = l2tp_ip_open, 5918c2ecf20Sopenharmony_ci .close = l2tp_ip_close, 5928c2ecf20Sopenharmony_ci .bind = l2tp_ip_bind, 5938c2ecf20Sopenharmony_ci .connect = l2tp_ip_connect, 5948c2ecf20Sopenharmony_ci .disconnect = l2tp_ip_disconnect, 5958c2ecf20Sopenharmony_ci .ioctl = l2tp_ioctl, 5968c2ecf20Sopenharmony_ci .destroy = l2tp_ip_destroy_sock, 5978c2ecf20Sopenharmony_ci .setsockopt = ip_setsockopt, 5988c2ecf20Sopenharmony_ci .getsockopt = ip_getsockopt, 5998c2ecf20Sopenharmony_ci .sendmsg = l2tp_ip_sendmsg, 6008c2ecf20Sopenharmony_ci .recvmsg = l2tp_ip_recvmsg, 6018c2ecf20Sopenharmony_ci .backlog_rcv = l2tp_ip_backlog_recv, 6028c2ecf20Sopenharmony_ci .hash = l2tp_ip_hash, 6038c2ecf20Sopenharmony_ci .unhash = l2tp_ip_unhash, 6048c2ecf20Sopenharmony_ci .obj_size = sizeof(struct l2tp_ip_sock), 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic const struct proto_ops l2tp_ip_ops = { 6088c2ecf20Sopenharmony_ci .family = PF_INET, 6098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6108c2ecf20Sopenharmony_ci .release = inet_release, 6118c2ecf20Sopenharmony_ci .bind = inet_bind, 6128c2ecf20Sopenharmony_ci .connect = inet_dgram_connect, 6138c2ecf20Sopenharmony_ci .socketpair = sock_no_socketpair, 6148c2ecf20Sopenharmony_ci .accept = sock_no_accept, 6158c2ecf20Sopenharmony_ci .getname = l2tp_ip_getname, 6168c2ecf20Sopenharmony_ci .poll = datagram_poll, 6178c2ecf20Sopenharmony_ci .ioctl = inet_ioctl, 6188c2ecf20Sopenharmony_ci .gettstamp = sock_gettstamp, 6198c2ecf20Sopenharmony_ci .listen = sock_no_listen, 6208c2ecf20Sopenharmony_ci .shutdown = inet_shutdown, 6218c2ecf20Sopenharmony_ci .setsockopt = sock_common_setsockopt, 6228c2ecf20Sopenharmony_ci .getsockopt = sock_common_getsockopt, 6238c2ecf20Sopenharmony_ci .sendmsg = inet_sendmsg, 6248c2ecf20Sopenharmony_ci .recvmsg = sock_common_recvmsg, 6258c2ecf20Sopenharmony_ci .mmap = sock_no_mmap, 6268c2ecf20Sopenharmony_ci .sendpage = sock_no_sendpage, 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic struct inet_protosw l2tp_ip_protosw = { 6308c2ecf20Sopenharmony_ci .type = SOCK_DGRAM, 6318c2ecf20Sopenharmony_ci .protocol = IPPROTO_L2TP, 6328c2ecf20Sopenharmony_ci .prot = &l2tp_ip_prot, 6338c2ecf20Sopenharmony_ci .ops = &l2tp_ip_ops, 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic struct net_protocol l2tp_ip_protocol __read_mostly = { 6378c2ecf20Sopenharmony_ci .handler = l2tp_ip_recv, 6388c2ecf20Sopenharmony_ci .netns_ok = 1, 6398c2ecf20Sopenharmony_ci}; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int __init l2tp_ip_init(void) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci int err; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci pr_info("L2TP IP encapsulation support (L2TPv3)\n"); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci err = proto_register(&l2tp_ip_prot, 1); 6488c2ecf20Sopenharmony_ci if (err != 0) 6498c2ecf20Sopenharmony_ci goto out; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci err = inet_add_protocol(&l2tp_ip_protocol, IPPROTO_L2TP); 6528c2ecf20Sopenharmony_ci if (err) 6538c2ecf20Sopenharmony_ci goto out1; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci inet_register_protosw(&l2tp_ip_protosw); 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ciout1: 6598c2ecf20Sopenharmony_ci proto_unregister(&l2tp_ip_prot); 6608c2ecf20Sopenharmony_ciout: 6618c2ecf20Sopenharmony_ci return err; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void __exit l2tp_ip_exit(void) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci inet_unregister_protosw(&l2tp_ip_protosw); 6678c2ecf20Sopenharmony_ci inet_del_protocol(&l2tp_ip_protocol, IPPROTO_L2TP); 6688c2ecf20Sopenharmony_ci proto_unregister(&l2tp_ip_prot); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cimodule_init(l2tp_ip_init); 6728c2ecf20Sopenharmony_cimodule_exit(l2tp_ip_exit); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6758c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); 6768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("L2TP over IP"); 6778c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* Use the value of SOCK_DGRAM (2) directory, because __stringify doesn't like 6808c2ecf20Sopenharmony_ci * enums 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 2, IPPROTO_L2TP); 6838c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_L2TP); 684