162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* L2TP netlink layer, for management 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2008,2009,2010 Katalix Systems Ltd 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Partly based on the IrDA nelink implementation 762306a36Sopenharmony_ci * (see net/irda/irnetlink.c) which is: 862306a36Sopenharmony_ci * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org> 962306a36Sopenharmony_ci * which is in turn partly based on the wireless netlink code: 1062306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <net/sock.h> 1662306a36Sopenharmony_ci#include <net/genetlink.h> 1762306a36Sopenharmony_ci#include <net/udp.h> 1862306a36Sopenharmony_ci#include <linux/in.h> 1962306a36Sopenharmony_ci#include <linux/udp.h> 2062306a36Sopenharmony_ci#include <linux/socket.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/list.h> 2362306a36Sopenharmony_ci#include <net/net_namespace.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/l2tp.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "l2tp_core.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct genl_family l2tp_nl_family; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const struct genl_multicast_group l2tp_multicast_group[] = { 3262306a36Sopenharmony_ci { 3362306a36Sopenharmony_ci .name = L2TP_GENL_MCGROUP, 3462306a36Sopenharmony_ci }, 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, 3862306a36Sopenharmony_ci int flags, struct l2tp_tunnel *tunnel, u8 cmd); 3962306a36Sopenharmony_cistatic int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, 4062306a36Sopenharmony_ci int flags, struct l2tp_session *session, 4162306a36Sopenharmony_ci u8 cmd); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Accessed under genl lock */ 4462306a36Sopenharmony_cistatic const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct l2tp_session *l2tp_nl_session_get(struct genl_info *info) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci u32 tunnel_id; 4962306a36Sopenharmony_ci u32 session_id; 5062306a36Sopenharmony_ci char *ifname; 5162306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 5262306a36Sopenharmony_ci struct l2tp_session *session = NULL; 5362306a36Sopenharmony_ci struct net *net = genl_info_net(info); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_IFNAME]) { 5662306a36Sopenharmony_ci ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); 5762306a36Sopenharmony_ci session = l2tp_session_get_by_ifname(net, ifname); 5862306a36Sopenharmony_ci } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && 5962306a36Sopenharmony_ci (info->attrs[L2TP_ATTR_CONN_ID])) { 6062306a36Sopenharmony_ci tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 6162306a36Sopenharmony_ci session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); 6262306a36Sopenharmony_ci tunnel = l2tp_tunnel_get(net, tunnel_id); 6362306a36Sopenharmony_ci if (tunnel) { 6462306a36Sopenharmony_ci session = l2tp_tunnel_get_session(tunnel, session_id); 6562306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return session; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct sk_buff *msg; 7562306a36Sopenharmony_ci void *hdr; 7662306a36Sopenharmony_ci int ret = -ENOBUFS; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!msg) { 8062306a36Sopenharmony_ci ret = -ENOMEM; 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, 8562306a36Sopenharmony_ci &l2tp_nl_family, 0, L2TP_CMD_NOOP); 8662306a36Sopenharmony_ci if (!hdr) { 8762306a36Sopenharmony_ci ret = -EMSGSIZE; 8862306a36Sopenharmony_ci goto err_out; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci genlmsg_end(msg, hdr); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cierr_out: 9662306a36Sopenharmony_ci nlmsg_free(msg); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciout: 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int l2tp_tunnel_notify(struct genl_family *family, 10362306a36Sopenharmony_ci struct genl_info *info, 10462306a36Sopenharmony_ci struct l2tp_tunnel *tunnel, 10562306a36Sopenharmony_ci u8 cmd) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct sk_buff *msg; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 11162306a36Sopenharmony_ci if (!msg) 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, 11562306a36Sopenharmony_ci NLM_F_ACK, tunnel, cmd); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (ret >= 0) { 11862306a36Sopenharmony_ci ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); 11962306a36Sopenharmony_ci /* We don't care if no one is listening */ 12062306a36Sopenharmony_ci if (ret == -ESRCH) 12162306a36Sopenharmony_ci ret = 0; 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci nlmsg_free(msg); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return ret; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int l2tp_session_notify(struct genl_family *family, 13162306a36Sopenharmony_ci struct genl_info *info, 13262306a36Sopenharmony_ci struct l2tp_session *session, 13362306a36Sopenharmony_ci u8 cmd) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct sk_buff *msg; 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 13962306a36Sopenharmony_ci if (!msg) 14062306a36Sopenharmony_ci return -ENOMEM; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, 14362306a36Sopenharmony_ci NLM_F_ACK, session, cmd); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (ret >= 0) { 14662306a36Sopenharmony_ci ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); 14762306a36Sopenharmony_ci /* We don't care if no one is listening */ 14862306a36Sopenharmony_ci if (ret == -ESRCH) 14962306a36Sopenharmony_ci ret = 0; 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci nlmsg_free(msg); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_create_get_addr(struct nlattr **attrs, struct l2tp_tunnel_cfg *cfg) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci if (attrs[L2TP_ATTR_UDP_SPORT]) 16162306a36Sopenharmony_ci cfg->local_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_SPORT]); 16262306a36Sopenharmony_ci if (attrs[L2TP_ATTR_UDP_DPORT]) 16362306a36Sopenharmony_ci cfg->peer_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_DPORT]); 16462306a36Sopenharmony_ci cfg->use_udp_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_CSUM]); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Must have either AF_INET or AF_INET6 address for source and destination */ 16762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 16862306a36Sopenharmony_ci if (attrs[L2TP_ATTR_IP6_SADDR] && attrs[L2TP_ATTR_IP6_DADDR]) { 16962306a36Sopenharmony_ci cfg->local_ip6 = nla_data(attrs[L2TP_ATTR_IP6_SADDR]); 17062306a36Sopenharmony_ci cfg->peer_ip6 = nla_data(attrs[L2TP_ATTR_IP6_DADDR]); 17162306a36Sopenharmony_ci cfg->udp6_zero_tx_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); 17262306a36Sopenharmony_ci cfg->udp6_zero_rx_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci#endif 17662306a36Sopenharmony_ci if (attrs[L2TP_ATTR_IP_SADDR] && attrs[L2TP_ATTR_IP_DADDR]) { 17762306a36Sopenharmony_ci cfg->local_ip.s_addr = nla_get_in_addr(attrs[L2TP_ATTR_IP_SADDR]); 17862306a36Sopenharmony_ci cfg->peer_ip.s_addr = nla_get_in_addr(attrs[L2TP_ATTR_IP_DADDR]); 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 tunnel_id; 18762306a36Sopenharmony_ci u32 peer_tunnel_id; 18862306a36Sopenharmony_ci int proto_version; 18962306a36Sopenharmony_ci int fd = -1; 19062306a36Sopenharmony_ci int ret = 0; 19162306a36Sopenharmony_ci struct l2tp_tunnel_cfg cfg = { 0, }; 19262306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 19362306a36Sopenharmony_ci struct net *net = genl_info_net(info); 19462306a36Sopenharmony_ci struct nlattr **attrs = info->attrs; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!attrs[L2TP_ATTR_CONN_ID]) { 19762306a36Sopenharmony_ci ret = -EINVAL; 19862306a36Sopenharmony_ci goto out; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci tunnel_id = nla_get_u32(attrs[L2TP_ATTR_CONN_ID]); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!attrs[L2TP_ATTR_PEER_CONN_ID]) { 20362306a36Sopenharmony_ci ret = -EINVAL; 20462306a36Sopenharmony_ci goto out; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci peer_tunnel_id = nla_get_u32(attrs[L2TP_ATTR_PEER_CONN_ID]); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!attrs[L2TP_ATTR_PROTO_VERSION]) { 20962306a36Sopenharmony_ci ret = -EINVAL; 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci proto_version = nla_get_u8(attrs[L2TP_ATTR_PROTO_VERSION]); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!attrs[L2TP_ATTR_ENCAP_TYPE]) { 21562306a36Sopenharmony_ci ret = -EINVAL; 21662306a36Sopenharmony_ci goto out; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci cfg.encap = nla_get_u16(attrs[L2TP_ATTR_ENCAP_TYPE]); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Managed tunnels take the tunnel socket from userspace. 22162306a36Sopenharmony_ci * Unmanaged tunnels must call out the source and destination addresses 22262306a36Sopenharmony_ci * for the kernel to create the tunnel socket itself. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (attrs[L2TP_ATTR_FD]) { 22562306a36Sopenharmony_ci fd = nla_get_u32(attrs[L2TP_ATTR_FD]); 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci ret = l2tp_nl_cmd_tunnel_create_get_addr(attrs, &cfg); 22862306a36Sopenharmony_ci if (ret < 0) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = -EINVAL; 23362306a36Sopenharmony_ci switch (cfg.encap) { 23462306a36Sopenharmony_ci case L2TP_ENCAPTYPE_UDP: 23562306a36Sopenharmony_ci case L2TP_ENCAPTYPE_IP: 23662306a36Sopenharmony_ci ret = l2tp_tunnel_create(fd, proto_version, tunnel_id, 23762306a36Sopenharmony_ci peer_tunnel_id, &cfg, &tunnel); 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (ret < 0) 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci l2tp_tunnel_inc_refcount(tunnel); 24562306a36Sopenharmony_ci ret = l2tp_tunnel_register(tunnel, net, &cfg); 24662306a36Sopenharmony_ci if (ret < 0) { 24762306a36Sopenharmony_ci kfree(tunnel); 24862306a36Sopenharmony_ci goto out; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel, 25162306a36Sopenharmony_ci L2TP_CMD_TUNNEL_CREATE); 25262306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciout: 25562306a36Sopenharmony_ci return ret; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 26162306a36Sopenharmony_ci u32 tunnel_id; 26262306a36Sopenharmony_ci int ret = 0; 26362306a36Sopenharmony_ci struct net *net = genl_info_net(info); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_CONN_ID]) { 26662306a36Sopenharmony_ci ret = -EINVAL; 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci tunnel = l2tp_tunnel_get(net, tunnel_id); 27262306a36Sopenharmony_ci if (!tunnel) { 27362306a36Sopenharmony_ci ret = -ENODEV; 27462306a36Sopenharmony_ci goto out; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci l2tp_tunnel_notify(&l2tp_nl_family, info, 27862306a36Sopenharmony_ci tunnel, L2TP_CMD_TUNNEL_DELETE); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci l2tp_tunnel_delete(tunnel); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciout: 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 29162306a36Sopenharmony_ci u32 tunnel_id; 29262306a36Sopenharmony_ci int ret = 0; 29362306a36Sopenharmony_ci struct net *net = genl_info_net(info); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_CONN_ID]) { 29662306a36Sopenharmony_ci ret = -EINVAL; 29762306a36Sopenharmony_ci goto out; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci tunnel = l2tp_tunnel_get(net, tunnel_id); 30262306a36Sopenharmony_ci if (!tunnel) { 30362306a36Sopenharmony_ci ret = -ENODEV; 30462306a36Sopenharmony_ci goto out; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ret = l2tp_tunnel_notify(&l2tp_nl_family, info, 30862306a36Sopenharmony_ci tunnel, L2TP_CMD_TUNNEL_MODIFY); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciout: 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 31762306a36Sopenharmony_cistatic int l2tp_nl_tunnel_send_addr6(struct sk_buff *skb, struct sock *sk, 31862306a36Sopenharmony_ci enum l2tp_encap_type encap) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 32162306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci switch (encap) { 32462306a36Sopenharmony_ci case L2TP_ENCAPTYPE_UDP: 32562306a36Sopenharmony_ci if (udp_get_no_check6_tx(sk) && 32662306a36Sopenharmony_ci nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_TX)) 32762306a36Sopenharmony_ci return -1; 32862306a36Sopenharmony_ci if (udp_get_no_check6_rx(sk) && 32962306a36Sopenharmony_ci nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_RX)) 33062306a36Sopenharmony_ci return -1; 33162306a36Sopenharmony_ci if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || 33262306a36Sopenharmony_ci nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) 33362306a36Sopenharmony_ci return -1; 33462306a36Sopenharmony_ci fallthrough; 33562306a36Sopenharmony_ci case L2TP_ENCAPTYPE_IP: 33662306a36Sopenharmony_ci if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR, &np->saddr) || 33762306a36Sopenharmony_ci nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR, &sk->sk_v6_daddr)) 33862306a36Sopenharmony_ci return -1; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci#endif 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int l2tp_nl_tunnel_send_addr4(struct sk_buff *skb, struct sock *sk, 34662306a36Sopenharmony_ci enum l2tp_encap_type encap) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci switch (encap) { 35162306a36Sopenharmony_ci case L2TP_ENCAPTYPE_UDP: 35262306a36Sopenharmony_ci if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx) || 35362306a36Sopenharmony_ci nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || 35462306a36Sopenharmony_ci nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) 35562306a36Sopenharmony_ci return -1; 35662306a36Sopenharmony_ci fallthrough; 35762306a36Sopenharmony_ci case L2TP_ENCAPTYPE_IP: 35862306a36Sopenharmony_ci if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) || 35962306a36Sopenharmony_ci nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr)) 36062306a36Sopenharmony_ci return -1; 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Append attributes for the tunnel address, handling the different attribute types 36862306a36Sopenharmony_ci * used for different tunnel encapsulation and AF_INET v.s. AF_INET6. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistatic int l2tp_nl_tunnel_send_addr(struct sk_buff *skb, struct l2tp_tunnel *tunnel) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct sock *sk = tunnel->sock; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!sk) 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 37862306a36Sopenharmony_ci if (sk->sk_family == AF_INET6) 37962306a36Sopenharmony_ci return l2tp_nl_tunnel_send_addr6(skb, sk, tunnel->encap); 38062306a36Sopenharmony_ci#endif 38162306a36Sopenharmony_ci return l2tp_nl_tunnel_send_addr4(skb, sk, tunnel->encap); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, 38562306a36Sopenharmony_ci struct l2tp_tunnel *tunnel, u8 cmd) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci void *hdr; 38862306a36Sopenharmony_ci struct nlattr *nest; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); 39162306a36Sopenharmony_ci if (!hdr) 39262306a36Sopenharmony_ci return -EMSGSIZE; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) || 39562306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || 39662306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || 39762306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_DEBUG, 0) || 39862306a36Sopenharmony_ci nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap)) 39962306a36Sopenharmony_ci goto nla_put_failure; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS); 40262306a36Sopenharmony_ci if (!nest) 40362306a36Sopenharmony_ci goto nla_put_failure; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS, 40662306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.tx_packets), 40762306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 40862306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES, 40962306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.tx_bytes), 41062306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 41162306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS, 41262306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.tx_errors), 41362306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 41462306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS, 41562306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_packets), 41662306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 41762306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES, 41862306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_bytes), 41962306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 42062306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS, 42162306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_seq_discards), 42262306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 42362306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_COOKIE_DISCARDS, 42462306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_cookie_discards), 42562306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 42662306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS, 42762306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_oos_packets), 42862306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 42962306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS, 43062306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_errors), 43162306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 43262306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_INVALID, 43362306a36Sopenharmony_ci atomic_long_read(&tunnel->stats.rx_invalid), 43462306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD)) 43562306a36Sopenharmony_ci goto nla_put_failure; 43662306a36Sopenharmony_ci nla_nest_end(skb, nest); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (l2tp_nl_tunnel_send_addr(skb, tunnel)) 43962306a36Sopenharmony_ci goto nla_put_failure; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci genlmsg_end(skb, hdr); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cinla_put_failure: 44562306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 44662306a36Sopenharmony_ci return -1; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 45262306a36Sopenharmony_ci struct sk_buff *msg; 45362306a36Sopenharmony_ci u32 tunnel_id; 45462306a36Sopenharmony_ci int ret = -ENOBUFS; 45562306a36Sopenharmony_ci struct net *net = genl_info_net(info); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_CONN_ID]) { 45862306a36Sopenharmony_ci ret = -EINVAL; 45962306a36Sopenharmony_ci goto err; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 46562306a36Sopenharmony_ci if (!msg) { 46662306a36Sopenharmony_ci ret = -ENOMEM; 46762306a36Sopenharmony_ci goto err; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci tunnel = l2tp_tunnel_get(net, tunnel_id); 47162306a36Sopenharmony_ci if (!tunnel) { 47262306a36Sopenharmony_ci ret = -ENODEV; 47362306a36Sopenharmony_ci goto err_nlmsg; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, 47762306a36Sopenharmony_ci NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET); 47862306a36Sopenharmony_ci if (ret < 0) 47962306a36Sopenharmony_ci goto err_nlmsg_tunnel; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return genlmsg_unicast(net, msg, info->snd_portid); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cierr_nlmsg_tunnel: 48662306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 48762306a36Sopenharmony_cierr_nlmsg: 48862306a36Sopenharmony_ci nlmsg_free(msg); 48962306a36Sopenharmony_cierr: 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci int ti = cb->args[0]; 49662306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 49762306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (;;) { 50062306a36Sopenharmony_ci tunnel = l2tp_tunnel_get_nth(net, ti); 50162306a36Sopenharmony_ci if (!tunnel) 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, 50562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 50662306a36Sopenharmony_ci tunnel, L2TP_CMD_TUNNEL_GET) < 0) { 50762306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 50862306a36Sopenharmony_ci goto out; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ti++; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ciout: 51662306a36Sopenharmony_ci cb->args[0] = ti; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return skb->len; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci u32 tunnel_id = 0; 52462306a36Sopenharmony_ci u32 session_id; 52562306a36Sopenharmony_ci u32 peer_session_id; 52662306a36Sopenharmony_ci int ret = 0; 52762306a36Sopenharmony_ci struct l2tp_tunnel *tunnel; 52862306a36Sopenharmony_ci struct l2tp_session *session; 52962306a36Sopenharmony_ci struct l2tp_session_cfg cfg = { 0, }; 53062306a36Sopenharmony_ci struct net *net = genl_info_net(info); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_CONN_ID]) { 53362306a36Sopenharmony_ci ret = -EINVAL; 53462306a36Sopenharmony_ci goto out; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 53862306a36Sopenharmony_ci tunnel = l2tp_tunnel_get(net, tunnel_id); 53962306a36Sopenharmony_ci if (!tunnel) { 54062306a36Sopenharmony_ci ret = -ENODEV; 54162306a36Sopenharmony_ci goto out; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_SESSION_ID]) { 54562306a36Sopenharmony_ci ret = -EINVAL; 54662306a36Sopenharmony_ci goto out_tunnel; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { 55162306a36Sopenharmony_ci ret = -EINVAL; 55262306a36Sopenharmony_ci goto out_tunnel; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (!info->attrs[L2TP_ATTR_PW_TYPE]) { 55762306a36Sopenharmony_ci ret = -EINVAL; 55862306a36Sopenharmony_ci goto out_tunnel; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]); 56162306a36Sopenharmony_ci if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { 56262306a36Sopenharmony_ci ret = -EINVAL; 56362306a36Sopenharmony_ci goto out_tunnel; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* L2TPv2 only accepts PPP pseudo-wires */ 56762306a36Sopenharmony_ci if (tunnel->version == 2 && cfg.pw_type != L2TP_PWTYPE_PPP) { 56862306a36Sopenharmony_ci ret = -EPROTONOSUPPORT; 56962306a36Sopenharmony_ci goto out_tunnel; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (tunnel->version > 2) { 57362306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) { 57462306a36Sopenharmony_ci cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]); 57562306a36Sopenharmony_ci if (cfg.l2specific_type != L2TP_L2SPECTYPE_DEFAULT && 57662306a36Sopenharmony_ci cfg.l2specific_type != L2TP_L2SPECTYPE_NONE) { 57762306a36Sopenharmony_ci ret = -EINVAL; 57862306a36Sopenharmony_ci goto out_tunnel; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_COOKIE]) { 58562306a36Sopenharmony_ci u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (len > 8) { 58862306a36Sopenharmony_ci ret = -EINVAL; 58962306a36Sopenharmony_ci goto out_tunnel; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci cfg.cookie_len = len; 59262306a36Sopenharmony_ci memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { 59562306a36Sopenharmony_ci u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (len > 8) { 59862306a36Sopenharmony_ci ret = -EINVAL; 59962306a36Sopenharmony_ci goto out_tunnel; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci cfg.peer_cookie_len = len; 60262306a36Sopenharmony_ci memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_IFNAME]) 60562306a36Sopenharmony_ci cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_RECV_SEQ]) 60962306a36Sopenharmony_ci cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_SEND_SEQ]) 61262306a36Sopenharmony_ci cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_LNS_MODE]) 61562306a36Sopenharmony_ci cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) 61862306a36Sopenharmony_ci cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci#ifdef CONFIG_MODULES 62162306a36Sopenharmony_ci if (!l2tp_nl_cmd_ops[cfg.pw_type]) { 62262306a36Sopenharmony_ci genl_unlock(); 62362306a36Sopenharmony_ci request_module("net-l2tp-type-%u", cfg.pw_type); 62462306a36Sopenharmony_ci genl_lock(); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci#endif 62762306a36Sopenharmony_ci if (!l2tp_nl_cmd_ops[cfg.pw_type] || !l2tp_nl_cmd_ops[cfg.pw_type]->session_create) { 62862306a36Sopenharmony_ci ret = -EPROTONOSUPPORT; 62962306a36Sopenharmony_ci goto out_tunnel; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel, 63362306a36Sopenharmony_ci session_id, 63462306a36Sopenharmony_ci peer_session_id, 63562306a36Sopenharmony_ci &cfg); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (ret >= 0) { 63862306a36Sopenharmony_ci session = l2tp_tunnel_get_session(tunnel, session_id); 63962306a36Sopenharmony_ci if (session) { 64062306a36Sopenharmony_ci ret = l2tp_session_notify(&l2tp_nl_family, info, session, 64162306a36Sopenharmony_ci L2TP_CMD_SESSION_CREATE); 64262306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciout_tunnel: 64762306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 64862306a36Sopenharmony_ciout: 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int ret = 0; 65562306a36Sopenharmony_ci struct l2tp_session *session; 65662306a36Sopenharmony_ci u16 pw_type; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci session = l2tp_nl_session_get(info); 65962306a36Sopenharmony_ci if (!session) { 66062306a36Sopenharmony_ci ret = -ENODEV; 66162306a36Sopenharmony_ci goto out; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci l2tp_session_notify(&l2tp_nl_family, info, 66562306a36Sopenharmony_ci session, L2TP_CMD_SESSION_DELETE); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci pw_type = session->pwtype; 66862306a36Sopenharmony_ci if (pw_type < __L2TP_PWTYPE_MAX) 66962306a36Sopenharmony_ci if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) 67062306a36Sopenharmony_ci l2tp_nl_cmd_ops[pw_type]->session_delete(session); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciout: 67562306a36Sopenharmony_ci return ret; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci int ret = 0; 68162306a36Sopenharmony_ci struct l2tp_session *session; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci session = l2tp_nl_session_get(info); 68462306a36Sopenharmony_ci if (!session) { 68562306a36Sopenharmony_ci ret = -ENODEV; 68662306a36Sopenharmony_ci goto out; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_RECV_SEQ]) 69062306a36Sopenharmony_ci session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_SEND_SEQ]) { 69362306a36Sopenharmony_ci session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); 69462306a36Sopenharmony_ci l2tp_session_set_header_len(session, session->tunnel->version); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_LNS_MODE]) 69862306a36Sopenharmony_ci session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) 70162306a36Sopenharmony_ci session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret = l2tp_session_notify(&l2tp_nl_family, info, 70462306a36Sopenharmony_ci session, L2TP_CMD_SESSION_MODIFY); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ciout: 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, 71362306a36Sopenharmony_ci struct l2tp_session *session, u8 cmd) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci void *hdr; 71662306a36Sopenharmony_ci struct nlattr *nest; 71762306a36Sopenharmony_ci struct l2tp_tunnel *tunnel = session->tunnel; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); 72062306a36Sopenharmony_ci if (!hdr) 72162306a36Sopenharmony_ci return -EMSGSIZE; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || 72462306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) || 72562306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || 72662306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id) || 72762306a36Sopenharmony_ci nla_put_u32(skb, L2TP_ATTR_DEBUG, 0) || 72862306a36Sopenharmony_ci nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype)) 72962306a36Sopenharmony_ci goto nla_put_failure; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if ((session->ifname[0] && 73262306a36Sopenharmony_ci nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) || 73362306a36Sopenharmony_ci (session->cookie_len && 73462306a36Sopenharmony_ci nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len, session->cookie)) || 73562306a36Sopenharmony_ci (session->peer_cookie_len && 73662306a36Sopenharmony_ci nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, session->peer_cookie)) || 73762306a36Sopenharmony_ci nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) || 73862306a36Sopenharmony_ci nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) || 73962306a36Sopenharmony_ci nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) || 74062306a36Sopenharmony_ci (l2tp_tunnel_uses_xfrm(tunnel) && 74162306a36Sopenharmony_ci nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) || 74262306a36Sopenharmony_ci (session->reorder_timeout && 74362306a36Sopenharmony_ci nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT, 74462306a36Sopenharmony_ci session->reorder_timeout, L2TP_ATTR_PAD))) 74562306a36Sopenharmony_ci goto nla_put_failure; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS); 74862306a36Sopenharmony_ci if (!nest) 74962306a36Sopenharmony_ci goto nla_put_failure; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS, 75262306a36Sopenharmony_ci atomic_long_read(&session->stats.tx_packets), 75362306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 75462306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES, 75562306a36Sopenharmony_ci atomic_long_read(&session->stats.tx_bytes), 75662306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 75762306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS, 75862306a36Sopenharmony_ci atomic_long_read(&session->stats.tx_errors), 75962306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 76062306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS, 76162306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_packets), 76262306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 76362306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES, 76462306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_bytes), 76562306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 76662306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS, 76762306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_seq_discards), 76862306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 76962306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_COOKIE_DISCARDS, 77062306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_cookie_discards), 77162306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 77262306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS, 77362306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_oos_packets), 77462306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 77562306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS, 77662306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_errors), 77762306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD) || 77862306a36Sopenharmony_ci nla_put_u64_64bit(skb, L2TP_ATTR_RX_INVALID, 77962306a36Sopenharmony_ci atomic_long_read(&session->stats.rx_invalid), 78062306a36Sopenharmony_ci L2TP_ATTR_STATS_PAD)) 78162306a36Sopenharmony_ci goto nla_put_failure; 78262306a36Sopenharmony_ci nla_nest_end(skb, nest); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci genlmsg_end(skb, hdr); 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci nla_put_failure: 78862306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 78962306a36Sopenharmony_ci return -1; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct l2tp_session *session; 79562306a36Sopenharmony_ci struct sk_buff *msg; 79662306a36Sopenharmony_ci int ret; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci session = l2tp_nl_session_get(info); 79962306a36Sopenharmony_ci if (!session) { 80062306a36Sopenharmony_ci ret = -ENODEV; 80162306a36Sopenharmony_ci goto err; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 80562306a36Sopenharmony_ci if (!msg) { 80662306a36Sopenharmony_ci ret = -ENOMEM; 80762306a36Sopenharmony_ci goto err_ref; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, 81162306a36Sopenharmony_ci 0, session, L2TP_CMD_SESSION_GET); 81262306a36Sopenharmony_ci if (ret < 0) 81362306a36Sopenharmony_ci goto err_ref_msg; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cierr_ref_msg: 82262306a36Sopenharmony_ci nlmsg_free(msg); 82362306a36Sopenharmony_cierr_ref: 82462306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 82562306a36Sopenharmony_cierr: 82662306a36Sopenharmony_ci return ret; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 83262306a36Sopenharmony_ci struct l2tp_session *session; 83362306a36Sopenharmony_ci struct l2tp_tunnel *tunnel = NULL; 83462306a36Sopenharmony_ci int ti = cb->args[0]; 83562306a36Sopenharmony_ci int si = cb->args[1]; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci for (;;) { 83862306a36Sopenharmony_ci if (!tunnel) { 83962306a36Sopenharmony_ci tunnel = l2tp_tunnel_get_nth(net, ti); 84062306a36Sopenharmony_ci if (!tunnel) 84162306a36Sopenharmony_ci goto out; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci session = l2tp_session_get_nth(tunnel, si); 84562306a36Sopenharmony_ci if (!session) { 84662306a36Sopenharmony_ci ti++; 84762306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 84862306a36Sopenharmony_ci tunnel = NULL; 84962306a36Sopenharmony_ci si = 0; 85062306a36Sopenharmony_ci continue; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, 85462306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 85562306a36Sopenharmony_ci session, L2TP_CMD_SESSION_GET) < 0) { 85662306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 85762306a36Sopenharmony_ci l2tp_tunnel_dec_refcount(tunnel); 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci l2tp_session_dec_refcount(session); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci si++; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciout: 86662306a36Sopenharmony_ci cb->args[0] = ti; 86762306a36Sopenharmony_ci cb->args[1] = si; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return skb->len; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { 87362306a36Sopenharmony_ci [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, 87462306a36Sopenharmony_ci [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, 87562306a36Sopenharmony_ci [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, 87662306a36Sopenharmony_ci [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, 87762306a36Sopenharmony_ci [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, 87862306a36Sopenharmony_ci [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, 87962306a36Sopenharmony_ci [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, 88062306a36Sopenharmony_ci [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, 88162306a36Sopenharmony_ci [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, 88262306a36Sopenharmony_ci [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, 88362306a36Sopenharmony_ci [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, 88462306a36Sopenharmony_ci [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, 88562306a36Sopenharmony_ci [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, 88662306a36Sopenharmony_ci [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, 88762306a36Sopenharmony_ci [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, 88862306a36Sopenharmony_ci [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, 88962306a36Sopenharmony_ci [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, 89062306a36Sopenharmony_ci [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, 89162306a36Sopenharmony_ci [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, 89262306a36Sopenharmony_ci [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, 89362306a36Sopenharmony_ci [L2TP_ATTR_FD] = { .type = NLA_U32, }, 89462306a36Sopenharmony_ci [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, 89562306a36Sopenharmony_ci [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, 89662306a36Sopenharmony_ci [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, 89762306a36Sopenharmony_ci [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, 89862306a36Sopenharmony_ci [L2TP_ATTR_MTU] = { .type = NLA_U16, }, 89962306a36Sopenharmony_ci [L2TP_ATTR_MRU] = { .type = NLA_U16, }, 90062306a36Sopenharmony_ci [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, 90162306a36Sopenharmony_ci [L2TP_ATTR_IP6_SADDR] = { 90262306a36Sopenharmony_ci .type = NLA_BINARY, 90362306a36Sopenharmony_ci .len = sizeof(struct in6_addr), 90462306a36Sopenharmony_ci }, 90562306a36Sopenharmony_ci [L2TP_ATTR_IP6_DADDR] = { 90662306a36Sopenharmony_ci .type = NLA_BINARY, 90762306a36Sopenharmony_ci .len = sizeof(struct in6_addr), 90862306a36Sopenharmony_ci }, 90962306a36Sopenharmony_ci [L2TP_ATTR_IFNAME] = { 91062306a36Sopenharmony_ci .type = NLA_NUL_STRING, 91162306a36Sopenharmony_ci .len = IFNAMSIZ - 1, 91262306a36Sopenharmony_ci }, 91362306a36Sopenharmony_ci [L2TP_ATTR_COOKIE] = { 91462306a36Sopenharmony_ci .type = NLA_BINARY, 91562306a36Sopenharmony_ci .len = 8, 91662306a36Sopenharmony_ci }, 91762306a36Sopenharmony_ci [L2TP_ATTR_PEER_COOKIE] = { 91862306a36Sopenharmony_ci .type = NLA_BINARY, 91962306a36Sopenharmony_ci .len = 8, 92062306a36Sopenharmony_ci }, 92162306a36Sopenharmony_ci}; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic const struct genl_small_ops l2tp_nl_ops[] = { 92462306a36Sopenharmony_ci { 92562306a36Sopenharmony_ci .cmd = L2TP_CMD_NOOP, 92662306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 92762306a36Sopenharmony_ci .doit = l2tp_nl_cmd_noop, 92862306a36Sopenharmony_ci /* can be retrieved by unprivileged users */ 92962306a36Sopenharmony_ci }, 93062306a36Sopenharmony_ci { 93162306a36Sopenharmony_ci .cmd = L2TP_CMD_TUNNEL_CREATE, 93262306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 93362306a36Sopenharmony_ci .doit = l2tp_nl_cmd_tunnel_create, 93462306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 93562306a36Sopenharmony_ci }, 93662306a36Sopenharmony_ci { 93762306a36Sopenharmony_ci .cmd = L2TP_CMD_TUNNEL_DELETE, 93862306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 93962306a36Sopenharmony_ci .doit = l2tp_nl_cmd_tunnel_delete, 94062306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 94162306a36Sopenharmony_ci }, 94262306a36Sopenharmony_ci { 94362306a36Sopenharmony_ci .cmd = L2TP_CMD_TUNNEL_MODIFY, 94462306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 94562306a36Sopenharmony_ci .doit = l2tp_nl_cmd_tunnel_modify, 94662306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 94762306a36Sopenharmony_ci }, 94862306a36Sopenharmony_ci { 94962306a36Sopenharmony_ci .cmd = L2TP_CMD_TUNNEL_GET, 95062306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 95162306a36Sopenharmony_ci .doit = l2tp_nl_cmd_tunnel_get, 95262306a36Sopenharmony_ci .dumpit = l2tp_nl_cmd_tunnel_dump, 95362306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 95462306a36Sopenharmony_ci }, 95562306a36Sopenharmony_ci { 95662306a36Sopenharmony_ci .cmd = L2TP_CMD_SESSION_CREATE, 95762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 95862306a36Sopenharmony_ci .doit = l2tp_nl_cmd_session_create, 95962306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 96062306a36Sopenharmony_ci }, 96162306a36Sopenharmony_ci { 96262306a36Sopenharmony_ci .cmd = L2TP_CMD_SESSION_DELETE, 96362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 96462306a36Sopenharmony_ci .doit = l2tp_nl_cmd_session_delete, 96562306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 96662306a36Sopenharmony_ci }, 96762306a36Sopenharmony_ci { 96862306a36Sopenharmony_ci .cmd = L2TP_CMD_SESSION_MODIFY, 96962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 97062306a36Sopenharmony_ci .doit = l2tp_nl_cmd_session_modify, 97162306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 97262306a36Sopenharmony_ci }, 97362306a36Sopenharmony_ci { 97462306a36Sopenharmony_ci .cmd = L2TP_CMD_SESSION_GET, 97562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 97662306a36Sopenharmony_ci .doit = l2tp_nl_cmd_session_get, 97762306a36Sopenharmony_ci .dumpit = l2tp_nl_cmd_session_dump, 97862306a36Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, 97962306a36Sopenharmony_ci }, 98062306a36Sopenharmony_ci}; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic struct genl_family l2tp_nl_family __ro_after_init = { 98362306a36Sopenharmony_ci .name = L2TP_GENL_NAME, 98462306a36Sopenharmony_ci .version = L2TP_GENL_VERSION, 98562306a36Sopenharmony_ci .hdrsize = 0, 98662306a36Sopenharmony_ci .maxattr = L2TP_ATTR_MAX, 98762306a36Sopenharmony_ci .policy = l2tp_nl_policy, 98862306a36Sopenharmony_ci .netnsok = true, 98962306a36Sopenharmony_ci .module = THIS_MODULE, 99062306a36Sopenharmony_ci .small_ops = l2tp_nl_ops, 99162306a36Sopenharmony_ci .n_small_ops = ARRAY_SIZE(l2tp_nl_ops), 99262306a36Sopenharmony_ci .resv_start_op = L2TP_CMD_SESSION_GET + 1, 99362306a36Sopenharmony_ci .mcgrps = l2tp_multicast_group, 99462306a36Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(l2tp_multicast_group), 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ciint l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci int ret; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret = -EINVAL; 100262306a36Sopenharmony_ci if (pw_type >= __L2TP_PWTYPE_MAX) 100362306a36Sopenharmony_ci goto err; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci genl_lock(); 100662306a36Sopenharmony_ci ret = -EBUSY; 100762306a36Sopenharmony_ci if (l2tp_nl_cmd_ops[pw_type]) 100862306a36Sopenharmony_ci goto out; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci l2tp_nl_cmd_ops[pw_type] = ops; 101162306a36Sopenharmony_ci ret = 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ciout: 101462306a36Sopenharmony_ci genl_unlock(); 101562306a36Sopenharmony_cierr: 101662306a36Sopenharmony_ci return ret; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(l2tp_nl_register_ops); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_civoid l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci if (pw_type < __L2TP_PWTYPE_MAX) { 102362306a36Sopenharmony_ci genl_lock(); 102462306a36Sopenharmony_ci l2tp_nl_cmd_ops[pw_type] = NULL; 102562306a36Sopenharmony_ci genl_unlock(); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int __init l2tp_nl_init(void) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci pr_info("L2TP netlink interface\n"); 103362306a36Sopenharmony_ci return genl_register_family(&l2tp_nl_family); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void l2tp_nl_cleanup(void) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci genl_unregister_family(&l2tp_nl_family); 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cimodule_init(l2tp_nl_init); 104262306a36Sopenharmony_cimodule_exit(l2tp_nl_cleanup); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ciMODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); 104562306a36Sopenharmony_ciMODULE_DESCRIPTION("L2TP netlink"); 104662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 104762306a36Sopenharmony_ciMODULE_VERSION("1.0"); 104862306a36Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY("l2tp"); 1049