1*309795f4SJames Chapman /* 2*309795f4SJames Chapman * L2TP netlink layer, for management 3*309795f4SJames Chapman * 4*309795f4SJames Chapman * Copyright (c) 2008,2009,2010 Katalix Systems Ltd 5*309795f4SJames Chapman * 6*309795f4SJames Chapman * Partly based on the IrDA nelink implementation 7*309795f4SJames Chapman * (see net/irda/irnetlink.c) which is: 8*309795f4SJames Chapman * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org> 9*309795f4SJames Chapman * which is in turn partly based on the wireless netlink code: 10*309795f4SJames Chapman * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 11*309795f4SJames Chapman * 12*309795f4SJames Chapman * This program is free software; you can redistribute it and/or modify 13*309795f4SJames Chapman * it under the terms of the GNU General Public License version 2 as 14*309795f4SJames Chapman * published by the Free Software Foundation. 15*309795f4SJames Chapman */ 16*309795f4SJames Chapman 17*309795f4SJames Chapman #include <net/sock.h> 18*309795f4SJames Chapman #include <net/genetlink.h> 19*309795f4SJames Chapman #include <net/udp.h> 20*309795f4SJames Chapman #include <linux/in.h> 21*309795f4SJames Chapman #include <linux/udp.h> 22*309795f4SJames Chapman #include <linux/socket.h> 23*309795f4SJames Chapman #include <linux/module.h> 24*309795f4SJames Chapman #include <linux/list.h> 25*309795f4SJames Chapman #include <net/net_namespace.h> 26*309795f4SJames Chapman 27*309795f4SJames Chapman #include <linux/l2tp.h> 28*309795f4SJames Chapman 29*309795f4SJames Chapman #include "l2tp_core.h" 30*309795f4SJames Chapman 31*309795f4SJames Chapman 32*309795f4SJames Chapman static struct genl_family l2tp_nl_family = { 33*309795f4SJames Chapman .id = GENL_ID_GENERATE, 34*309795f4SJames Chapman .name = L2TP_GENL_NAME, 35*309795f4SJames Chapman .version = L2TP_GENL_VERSION, 36*309795f4SJames Chapman .hdrsize = 0, 37*309795f4SJames Chapman .maxattr = L2TP_ATTR_MAX, 38*309795f4SJames Chapman }; 39*309795f4SJames Chapman 40*309795f4SJames Chapman /* Accessed under genl lock */ 41*309795f4SJames Chapman static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; 42*309795f4SJames Chapman 43*309795f4SJames Chapman static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info) 44*309795f4SJames Chapman { 45*309795f4SJames Chapman u32 tunnel_id; 46*309795f4SJames Chapman u32 session_id; 47*309795f4SJames Chapman char *ifname; 48*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 49*309795f4SJames Chapman struct l2tp_session *session = NULL; 50*309795f4SJames Chapman struct net *net = genl_info_net(info); 51*309795f4SJames Chapman 52*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_IFNAME]) { 53*309795f4SJames Chapman ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); 54*309795f4SJames Chapman session = l2tp_session_find_by_ifname(net, ifname); 55*309795f4SJames Chapman } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && 56*309795f4SJames Chapman (info->attrs[L2TP_ATTR_CONN_ID])) { 57*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 58*309795f4SJames Chapman session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); 59*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 60*309795f4SJames Chapman if (tunnel) 61*309795f4SJames Chapman session = l2tp_session_find(net, tunnel, session_id); 62*309795f4SJames Chapman } 63*309795f4SJames Chapman 64*309795f4SJames Chapman return session; 65*309795f4SJames Chapman } 66*309795f4SJames Chapman 67*309795f4SJames Chapman static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) 68*309795f4SJames Chapman { 69*309795f4SJames Chapman struct sk_buff *msg; 70*309795f4SJames Chapman void *hdr; 71*309795f4SJames Chapman int ret = -ENOBUFS; 72*309795f4SJames Chapman 73*309795f4SJames Chapman msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 74*309795f4SJames Chapman if (!msg) { 75*309795f4SJames Chapman ret = -ENOMEM; 76*309795f4SJames Chapman goto out; 77*309795f4SJames Chapman } 78*309795f4SJames Chapman 79*309795f4SJames Chapman hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, 80*309795f4SJames Chapman &l2tp_nl_family, 0, L2TP_CMD_NOOP); 81*309795f4SJames Chapman if (IS_ERR(hdr)) { 82*309795f4SJames Chapman ret = PTR_ERR(hdr); 83*309795f4SJames Chapman goto err_out; 84*309795f4SJames Chapman } 85*309795f4SJames Chapman 86*309795f4SJames Chapman genlmsg_end(msg, hdr); 87*309795f4SJames Chapman 88*309795f4SJames Chapman return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); 89*309795f4SJames Chapman 90*309795f4SJames Chapman err_out: 91*309795f4SJames Chapman nlmsg_free(msg); 92*309795f4SJames Chapman 93*309795f4SJames Chapman out: 94*309795f4SJames Chapman return ret; 95*309795f4SJames Chapman } 96*309795f4SJames Chapman 97*309795f4SJames Chapman static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) 98*309795f4SJames Chapman { 99*309795f4SJames Chapman u32 tunnel_id; 100*309795f4SJames Chapman u32 peer_tunnel_id; 101*309795f4SJames Chapman int proto_version; 102*309795f4SJames Chapman int fd; 103*309795f4SJames Chapman int ret = 0; 104*309795f4SJames Chapman struct l2tp_tunnel_cfg cfg = { 0, }; 105*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 106*309795f4SJames Chapman struct net *net = genl_info_net(info); 107*309795f4SJames Chapman 108*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_CONN_ID]) { 109*309795f4SJames Chapman ret = -EINVAL; 110*309795f4SJames Chapman goto out; 111*309795f4SJames Chapman } 112*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 113*309795f4SJames Chapman 114*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) { 115*309795f4SJames Chapman ret = -EINVAL; 116*309795f4SJames Chapman goto out; 117*309795f4SJames Chapman } 118*309795f4SJames Chapman peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]); 119*309795f4SJames Chapman 120*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) { 121*309795f4SJames Chapman ret = -EINVAL; 122*309795f4SJames Chapman goto out; 123*309795f4SJames Chapman } 124*309795f4SJames Chapman proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]); 125*309795f4SJames Chapman 126*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) { 127*309795f4SJames Chapman ret = -EINVAL; 128*309795f4SJames Chapman goto out; 129*309795f4SJames Chapman } 130*309795f4SJames Chapman cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); 131*309795f4SJames Chapman 132*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_FD]) { 133*309795f4SJames Chapman ret = -EINVAL; 134*309795f4SJames Chapman goto out; 135*309795f4SJames Chapman } 136*309795f4SJames Chapman fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); 137*309795f4SJames Chapman 138*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DEBUG]) 139*309795f4SJames Chapman cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); 140*309795f4SJames Chapman 141*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 142*309795f4SJames Chapman if (tunnel != NULL) { 143*309795f4SJames Chapman ret = -EEXIST; 144*309795f4SJames Chapman goto out; 145*309795f4SJames Chapman } 146*309795f4SJames Chapman 147*309795f4SJames Chapman ret = -EINVAL; 148*309795f4SJames Chapman switch (cfg.encap) { 149*309795f4SJames Chapman case L2TP_ENCAPTYPE_UDP: 150*309795f4SJames Chapman case L2TP_ENCAPTYPE_IP: 151*309795f4SJames Chapman ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id, 152*309795f4SJames Chapman peer_tunnel_id, &cfg, &tunnel); 153*309795f4SJames Chapman break; 154*309795f4SJames Chapman } 155*309795f4SJames Chapman 156*309795f4SJames Chapman out: 157*309795f4SJames Chapman return ret; 158*309795f4SJames Chapman } 159*309795f4SJames Chapman 160*309795f4SJames Chapman static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info) 161*309795f4SJames Chapman { 162*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 163*309795f4SJames Chapman u32 tunnel_id; 164*309795f4SJames Chapman int ret = 0; 165*309795f4SJames Chapman struct net *net = genl_info_net(info); 166*309795f4SJames Chapman 167*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_CONN_ID]) { 168*309795f4SJames Chapman ret = -EINVAL; 169*309795f4SJames Chapman goto out; 170*309795f4SJames Chapman } 171*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 172*309795f4SJames Chapman 173*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 174*309795f4SJames Chapman if (tunnel == NULL) { 175*309795f4SJames Chapman ret = -ENODEV; 176*309795f4SJames Chapman goto out; 177*309795f4SJames Chapman } 178*309795f4SJames Chapman 179*309795f4SJames Chapman (void) l2tp_tunnel_delete(tunnel); 180*309795f4SJames Chapman 181*309795f4SJames Chapman out: 182*309795f4SJames Chapman return ret; 183*309795f4SJames Chapman } 184*309795f4SJames Chapman 185*309795f4SJames Chapman static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info) 186*309795f4SJames Chapman { 187*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 188*309795f4SJames Chapman u32 tunnel_id; 189*309795f4SJames Chapman int ret = 0; 190*309795f4SJames Chapman struct net *net = genl_info_net(info); 191*309795f4SJames Chapman 192*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_CONN_ID]) { 193*309795f4SJames Chapman ret = -EINVAL; 194*309795f4SJames Chapman goto out; 195*309795f4SJames Chapman } 196*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 197*309795f4SJames Chapman 198*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 199*309795f4SJames Chapman if (tunnel == NULL) { 200*309795f4SJames Chapman ret = -ENODEV; 201*309795f4SJames Chapman goto out; 202*309795f4SJames Chapman } 203*309795f4SJames Chapman 204*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DEBUG]) 205*309795f4SJames Chapman tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); 206*309795f4SJames Chapman 207*309795f4SJames Chapman out: 208*309795f4SJames Chapman return ret; 209*309795f4SJames Chapman } 210*309795f4SJames Chapman 211*309795f4SJames Chapman static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, 212*309795f4SJames Chapman struct l2tp_tunnel *tunnel) 213*309795f4SJames Chapman { 214*309795f4SJames Chapman void *hdr; 215*309795f4SJames Chapman struct nlattr *nest; 216*309795f4SJames Chapman struct sock *sk = NULL; 217*309795f4SJames Chapman struct inet_sock *inet; 218*309795f4SJames Chapman 219*309795f4SJames Chapman hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, 220*309795f4SJames Chapman L2TP_CMD_TUNNEL_GET); 221*309795f4SJames Chapman if (IS_ERR(hdr)) 222*309795f4SJames Chapman return PTR_ERR(hdr); 223*309795f4SJames Chapman 224*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version); 225*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id); 226*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id); 227*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_DEBUG, tunnel->debug); 228*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap); 229*309795f4SJames Chapman 230*309795f4SJames Chapman nest = nla_nest_start(skb, L2TP_ATTR_STATS); 231*309795f4SJames Chapman if (nest == NULL) 232*309795f4SJames Chapman goto nla_put_failure; 233*309795f4SJames Chapman 234*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_PACKETS, tunnel->stats.tx_packets); 235*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_BYTES, tunnel->stats.tx_bytes); 236*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_ERRORS, tunnel->stats.tx_errors); 237*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_PACKETS, tunnel->stats.rx_packets); 238*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_BYTES, tunnel->stats.rx_bytes); 239*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, tunnel->stats.rx_seq_discards); 240*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_OOS_PACKETS, tunnel->stats.rx_oos_packets); 241*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_ERRORS, tunnel->stats.rx_errors); 242*309795f4SJames Chapman nla_nest_end(skb, nest); 243*309795f4SJames Chapman 244*309795f4SJames Chapman sk = tunnel->sock; 245*309795f4SJames Chapman if (!sk) 246*309795f4SJames Chapman goto out; 247*309795f4SJames Chapman 248*309795f4SJames Chapman inet = inet_sk(sk); 249*309795f4SJames Chapman 250*309795f4SJames Chapman switch (tunnel->encap) { 251*309795f4SJames Chapman case L2TP_ENCAPTYPE_UDP: 252*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)); 253*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)); 254*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_UDP_CSUM, (sk->sk_no_check != UDP_CSUM_NOXMIT)); 255*309795f4SJames Chapman /* NOBREAK */ 256*309795f4SJames Chapman case L2TP_ENCAPTYPE_IP: 257*309795f4SJames Chapman NLA_PUT_BE32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr); 258*309795f4SJames Chapman NLA_PUT_BE32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr); 259*309795f4SJames Chapman break; 260*309795f4SJames Chapman } 261*309795f4SJames Chapman 262*309795f4SJames Chapman out: 263*309795f4SJames Chapman return genlmsg_end(skb, hdr); 264*309795f4SJames Chapman 265*309795f4SJames Chapman nla_put_failure: 266*309795f4SJames Chapman genlmsg_cancel(skb, hdr); 267*309795f4SJames Chapman return -1; 268*309795f4SJames Chapman } 269*309795f4SJames Chapman 270*309795f4SJames Chapman static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) 271*309795f4SJames Chapman { 272*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 273*309795f4SJames Chapman struct sk_buff *msg; 274*309795f4SJames Chapman u32 tunnel_id; 275*309795f4SJames Chapman int ret = -ENOBUFS; 276*309795f4SJames Chapman struct net *net = genl_info_net(info); 277*309795f4SJames Chapman 278*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_CONN_ID]) { 279*309795f4SJames Chapman ret = -EINVAL; 280*309795f4SJames Chapman goto out; 281*309795f4SJames Chapman } 282*309795f4SJames Chapman 283*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 284*309795f4SJames Chapman 285*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 286*309795f4SJames Chapman if (tunnel == NULL) { 287*309795f4SJames Chapman ret = -ENODEV; 288*309795f4SJames Chapman goto out; 289*309795f4SJames Chapman } 290*309795f4SJames Chapman 291*309795f4SJames Chapman msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 292*309795f4SJames Chapman if (!msg) { 293*309795f4SJames Chapman ret = -ENOMEM; 294*309795f4SJames Chapman goto out; 295*309795f4SJames Chapman } 296*309795f4SJames Chapman 297*309795f4SJames Chapman ret = l2tp_nl_tunnel_send(msg, info->snd_pid, info->snd_seq, 298*309795f4SJames Chapman NLM_F_ACK, tunnel); 299*309795f4SJames Chapman if (ret < 0) 300*309795f4SJames Chapman goto err_out; 301*309795f4SJames Chapman 302*309795f4SJames Chapman return genlmsg_unicast(net, msg, info->snd_pid); 303*309795f4SJames Chapman 304*309795f4SJames Chapman err_out: 305*309795f4SJames Chapman nlmsg_free(msg); 306*309795f4SJames Chapman 307*309795f4SJames Chapman out: 308*309795f4SJames Chapman return ret; 309*309795f4SJames Chapman } 310*309795f4SJames Chapman 311*309795f4SJames Chapman static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) 312*309795f4SJames Chapman { 313*309795f4SJames Chapman int ti = cb->args[0]; 314*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 315*309795f4SJames Chapman struct net *net = sock_net(skb->sk); 316*309795f4SJames Chapman 317*309795f4SJames Chapman for (;;) { 318*309795f4SJames Chapman tunnel = l2tp_tunnel_find_nth(net, ti); 319*309795f4SJames Chapman if (tunnel == NULL) 320*309795f4SJames Chapman goto out; 321*309795f4SJames Chapman 322*309795f4SJames Chapman if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).pid, 323*309795f4SJames Chapman cb->nlh->nlmsg_seq, NLM_F_MULTI, 324*309795f4SJames Chapman tunnel) <= 0) 325*309795f4SJames Chapman goto out; 326*309795f4SJames Chapman 327*309795f4SJames Chapman ti++; 328*309795f4SJames Chapman } 329*309795f4SJames Chapman 330*309795f4SJames Chapman out: 331*309795f4SJames Chapman cb->args[0] = ti; 332*309795f4SJames Chapman 333*309795f4SJames Chapman return skb->len; 334*309795f4SJames Chapman } 335*309795f4SJames Chapman 336*309795f4SJames Chapman static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) 337*309795f4SJames Chapman { 338*309795f4SJames Chapman u32 tunnel_id = 0; 339*309795f4SJames Chapman u32 session_id; 340*309795f4SJames Chapman u32 peer_session_id; 341*309795f4SJames Chapman int ret = 0; 342*309795f4SJames Chapman struct l2tp_tunnel *tunnel; 343*309795f4SJames Chapman struct l2tp_session *session; 344*309795f4SJames Chapman struct l2tp_session_cfg cfg = { 0, }; 345*309795f4SJames Chapman struct net *net = genl_info_net(info); 346*309795f4SJames Chapman 347*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_CONN_ID]) { 348*309795f4SJames Chapman ret = -EINVAL; 349*309795f4SJames Chapman goto out; 350*309795f4SJames Chapman } 351*309795f4SJames Chapman tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 352*309795f4SJames Chapman tunnel = l2tp_tunnel_find(net, tunnel_id); 353*309795f4SJames Chapman if (!tunnel) { 354*309795f4SJames Chapman ret = -ENODEV; 355*309795f4SJames Chapman goto out; 356*309795f4SJames Chapman } 357*309795f4SJames Chapman 358*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_SESSION_ID]) { 359*309795f4SJames Chapman ret = -EINVAL; 360*309795f4SJames Chapman goto out; 361*309795f4SJames Chapman } 362*309795f4SJames Chapman session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); 363*309795f4SJames Chapman session = l2tp_session_find(net, tunnel, session_id); 364*309795f4SJames Chapman if (session) { 365*309795f4SJames Chapman ret = -EEXIST; 366*309795f4SJames Chapman goto out; 367*309795f4SJames Chapman } 368*309795f4SJames Chapman 369*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { 370*309795f4SJames Chapman ret = -EINVAL; 371*309795f4SJames Chapman goto out; 372*309795f4SJames Chapman } 373*309795f4SJames Chapman peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]); 374*309795f4SJames Chapman 375*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_PW_TYPE]) { 376*309795f4SJames Chapman ret = -EINVAL; 377*309795f4SJames Chapman goto out; 378*309795f4SJames Chapman } 379*309795f4SJames Chapman cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]); 380*309795f4SJames Chapman if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { 381*309795f4SJames Chapman ret = -EINVAL; 382*309795f4SJames Chapman goto out; 383*309795f4SJames Chapman } 384*309795f4SJames Chapman 385*309795f4SJames Chapman if (tunnel->version > 2) { 386*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_OFFSET]) 387*309795f4SJames Chapman cfg.offset = nla_get_u16(info->attrs[L2TP_ATTR_OFFSET]); 388*309795f4SJames Chapman 389*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DATA_SEQ]) 390*309795f4SJames Chapman cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); 391*309795f4SJames Chapman 392*309795f4SJames Chapman cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; 393*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) 394*309795f4SJames Chapman cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]); 395*309795f4SJames Chapman 396*309795f4SJames Chapman cfg.l2specific_len = 4; 397*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_L2SPEC_LEN]) 398*309795f4SJames Chapman cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]); 399*309795f4SJames Chapman 400*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_COOKIE]) { 401*309795f4SJames Chapman u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]); 402*309795f4SJames Chapman if (len > 8) { 403*309795f4SJames Chapman ret = -EINVAL; 404*309795f4SJames Chapman goto out; 405*309795f4SJames Chapman } 406*309795f4SJames Chapman cfg.cookie_len = len; 407*309795f4SJames Chapman memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); 408*309795f4SJames Chapman } 409*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { 410*309795f4SJames Chapman u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]); 411*309795f4SJames Chapman if (len > 8) { 412*309795f4SJames Chapman ret = -EINVAL; 413*309795f4SJames Chapman goto out; 414*309795f4SJames Chapman } 415*309795f4SJames Chapman cfg.peer_cookie_len = len; 416*309795f4SJames Chapman memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); 417*309795f4SJames Chapman } 418*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_IFNAME]) 419*309795f4SJames Chapman cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); 420*309795f4SJames Chapman 421*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_VLAN_ID]) 422*309795f4SJames Chapman cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]); 423*309795f4SJames Chapman } 424*309795f4SJames Chapman 425*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DEBUG]) 426*309795f4SJames Chapman cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); 427*309795f4SJames Chapman 428*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_RECV_SEQ]) 429*309795f4SJames Chapman cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); 430*309795f4SJames Chapman 431*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_SEND_SEQ]) 432*309795f4SJames Chapman cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); 433*309795f4SJames Chapman 434*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_LNS_MODE]) 435*309795f4SJames Chapman cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); 436*309795f4SJames Chapman 437*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) 438*309795f4SJames Chapman cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); 439*309795f4SJames Chapman 440*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_MTU]) 441*309795f4SJames Chapman cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); 442*309795f4SJames Chapman 443*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_MRU]) 444*309795f4SJames Chapman cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); 445*309795f4SJames Chapman 446*309795f4SJames Chapman if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) || 447*309795f4SJames Chapman (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) { 448*309795f4SJames Chapman ret = -EPROTONOSUPPORT; 449*309795f4SJames Chapman goto out; 450*309795f4SJames Chapman } 451*309795f4SJames Chapman 452*309795f4SJames Chapman /* Check that pseudowire-specific params are present */ 453*309795f4SJames Chapman switch (cfg.pw_type) { 454*309795f4SJames Chapman case L2TP_PWTYPE_NONE: 455*309795f4SJames Chapman break; 456*309795f4SJames Chapman case L2TP_PWTYPE_ETH_VLAN: 457*309795f4SJames Chapman if (!info->attrs[L2TP_ATTR_VLAN_ID]) { 458*309795f4SJames Chapman ret = -EINVAL; 459*309795f4SJames Chapman goto out; 460*309795f4SJames Chapman } 461*309795f4SJames Chapman break; 462*309795f4SJames Chapman case L2TP_PWTYPE_ETH: 463*309795f4SJames Chapman break; 464*309795f4SJames Chapman case L2TP_PWTYPE_PPP: 465*309795f4SJames Chapman case L2TP_PWTYPE_PPP_AC: 466*309795f4SJames Chapman break; 467*309795f4SJames Chapman case L2TP_PWTYPE_IP: 468*309795f4SJames Chapman default: 469*309795f4SJames Chapman ret = -EPROTONOSUPPORT; 470*309795f4SJames Chapman break; 471*309795f4SJames Chapman } 472*309795f4SJames Chapman 473*309795f4SJames Chapman ret = -EPROTONOSUPPORT; 474*309795f4SJames Chapman if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create) 475*309795f4SJames Chapman ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, 476*309795f4SJames Chapman session_id, peer_session_id, &cfg); 477*309795f4SJames Chapman 478*309795f4SJames Chapman out: 479*309795f4SJames Chapman return ret; 480*309795f4SJames Chapman } 481*309795f4SJames Chapman 482*309795f4SJames Chapman static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) 483*309795f4SJames Chapman { 484*309795f4SJames Chapman int ret = 0; 485*309795f4SJames Chapman struct l2tp_session *session; 486*309795f4SJames Chapman u16 pw_type; 487*309795f4SJames Chapman 488*309795f4SJames Chapman session = l2tp_nl_session_find(info); 489*309795f4SJames Chapman if (session == NULL) { 490*309795f4SJames Chapman ret = -ENODEV; 491*309795f4SJames Chapman goto out; 492*309795f4SJames Chapman } 493*309795f4SJames Chapman 494*309795f4SJames Chapman pw_type = session->pwtype; 495*309795f4SJames Chapman if (pw_type < __L2TP_PWTYPE_MAX) 496*309795f4SJames Chapman if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) 497*309795f4SJames Chapman ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session); 498*309795f4SJames Chapman 499*309795f4SJames Chapman out: 500*309795f4SJames Chapman return ret; 501*309795f4SJames Chapman } 502*309795f4SJames Chapman 503*309795f4SJames Chapman static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) 504*309795f4SJames Chapman { 505*309795f4SJames Chapman int ret = 0; 506*309795f4SJames Chapman struct l2tp_session *session; 507*309795f4SJames Chapman 508*309795f4SJames Chapman session = l2tp_nl_session_find(info); 509*309795f4SJames Chapman if (session == NULL) { 510*309795f4SJames Chapman ret = -ENODEV; 511*309795f4SJames Chapman goto out; 512*309795f4SJames Chapman } 513*309795f4SJames Chapman 514*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DEBUG]) 515*309795f4SJames Chapman session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); 516*309795f4SJames Chapman 517*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_DATA_SEQ]) 518*309795f4SJames Chapman session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); 519*309795f4SJames Chapman 520*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_RECV_SEQ]) 521*309795f4SJames Chapman session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); 522*309795f4SJames Chapman 523*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_SEND_SEQ]) 524*309795f4SJames Chapman session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); 525*309795f4SJames Chapman 526*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_LNS_MODE]) 527*309795f4SJames Chapman session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); 528*309795f4SJames Chapman 529*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) 530*309795f4SJames Chapman session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); 531*309795f4SJames Chapman 532*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_MTU]) 533*309795f4SJames Chapman session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); 534*309795f4SJames Chapman 535*309795f4SJames Chapman if (info->attrs[L2TP_ATTR_MRU]) 536*309795f4SJames Chapman session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); 537*309795f4SJames Chapman 538*309795f4SJames Chapman out: 539*309795f4SJames Chapman return ret; 540*309795f4SJames Chapman } 541*309795f4SJames Chapman 542*309795f4SJames Chapman static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, 543*309795f4SJames Chapman struct l2tp_session *session) 544*309795f4SJames Chapman { 545*309795f4SJames Chapman void *hdr; 546*309795f4SJames Chapman struct nlattr *nest; 547*309795f4SJames Chapman struct l2tp_tunnel *tunnel = session->tunnel; 548*309795f4SJames Chapman struct sock *sk = NULL; 549*309795f4SJames Chapman 550*309795f4SJames Chapman sk = tunnel->sock; 551*309795f4SJames Chapman 552*309795f4SJames Chapman hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); 553*309795f4SJames Chapman if (IS_ERR(hdr)) 554*309795f4SJames Chapman return PTR_ERR(hdr); 555*309795f4SJames Chapman 556*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id); 557*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_SESSION_ID, session->session_id); 558*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id); 559*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id); 560*309795f4SJames Chapman NLA_PUT_U32(skb, L2TP_ATTR_DEBUG, session->debug); 561*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_PW_TYPE, session->pwtype); 562*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_MTU, session->mtu); 563*309795f4SJames Chapman if (session->mru) 564*309795f4SJames Chapman NLA_PUT_U16(skb, L2TP_ATTR_MRU, session->mru); 565*309795f4SJames Chapman 566*309795f4SJames Chapman if (session->ifname && session->ifname[0]) 567*309795f4SJames Chapman NLA_PUT_STRING(skb, L2TP_ATTR_IFNAME, session->ifname); 568*309795f4SJames Chapman if (session->cookie_len) 569*309795f4SJames Chapman NLA_PUT(skb, L2TP_ATTR_COOKIE, session->cookie_len, &session->cookie[0]); 570*309795f4SJames Chapman if (session->peer_cookie_len) 571*309795f4SJames Chapman NLA_PUT(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, &session->peer_cookie[0]); 572*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq); 573*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq); 574*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode); 575*309795f4SJames Chapman #ifdef CONFIG_XFRM 576*309795f4SJames Chapman if ((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) 577*309795f4SJames Chapman NLA_PUT_U8(skb, L2TP_ATTR_USING_IPSEC, 1); 578*309795f4SJames Chapman #endif 579*309795f4SJames Chapman if (session->reorder_timeout) 580*309795f4SJames Chapman NLA_PUT_MSECS(skb, L2TP_ATTR_RECV_TIMEOUT, session->reorder_timeout); 581*309795f4SJames Chapman 582*309795f4SJames Chapman nest = nla_nest_start(skb, L2TP_ATTR_STATS); 583*309795f4SJames Chapman if (nest == NULL) 584*309795f4SJames Chapman goto nla_put_failure; 585*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_PACKETS, session->stats.tx_packets); 586*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_BYTES, session->stats.tx_bytes); 587*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_TX_ERRORS, session->stats.tx_errors); 588*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_PACKETS, session->stats.rx_packets); 589*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_BYTES, session->stats.rx_bytes); 590*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, session->stats.rx_seq_discards); 591*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_OOS_PACKETS, session->stats.rx_oos_packets); 592*309795f4SJames Chapman NLA_PUT_U64(skb, L2TP_ATTR_RX_ERRORS, session->stats.rx_errors); 593*309795f4SJames Chapman nla_nest_end(skb, nest); 594*309795f4SJames Chapman 595*309795f4SJames Chapman return genlmsg_end(skb, hdr); 596*309795f4SJames Chapman 597*309795f4SJames Chapman nla_put_failure: 598*309795f4SJames Chapman genlmsg_cancel(skb, hdr); 599*309795f4SJames Chapman return -1; 600*309795f4SJames Chapman } 601*309795f4SJames Chapman 602*309795f4SJames Chapman static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) 603*309795f4SJames Chapman { 604*309795f4SJames Chapman struct l2tp_session *session; 605*309795f4SJames Chapman struct sk_buff *msg; 606*309795f4SJames Chapman int ret; 607*309795f4SJames Chapman 608*309795f4SJames Chapman session = l2tp_nl_session_find(info); 609*309795f4SJames Chapman if (session == NULL) { 610*309795f4SJames Chapman ret = -ENODEV; 611*309795f4SJames Chapman goto out; 612*309795f4SJames Chapman } 613*309795f4SJames Chapman 614*309795f4SJames Chapman msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 615*309795f4SJames Chapman if (!msg) { 616*309795f4SJames Chapman ret = -ENOMEM; 617*309795f4SJames Chapman goto out; 618*309795f4SJames Chapman } 619*309795f4SJames Chapman 620*309795f4SJames Chapman ret = l2tp_nl_session_send(msg, info->snd_pid, info->snd_seq, 621*309795f4SJames Chapman 0, session); 622*309795f4SJames Chapman if (ret < 0) 623*309795f4SJames Chapman goto err_out; 624*309795f4SJames Chapman 625*309795f4SJames Chapman return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); 626*309795f4SJames Chapman 627*309795f4SJames Chapman err_out: 628*309795f4SJames Chapman nlmsg_free(msg); 629*309795f4SJames Chapman 630*309795f4SJames Chapman out: 631*309795f4SJames Chapman return ret; 632*309795f4SJames Chapman } 633*309795f4SJames Chapman 634*309795f4SJames Chapman static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) 635*309795f4SJames Chapman { 636*309795f4SJames Chapman struct net *net = sock_net(skb->sk); 637*309795f4SJames Chapman struct l2tp_session *session; 638*309795f4SJames Chapman struct l2tp_tunnel *tunnel = NULL; 639*309795f4SJames Chapman int ti = cb->args[0]; 640*309795f4SJames Chapman int si = cb->args[1]; 641*309795f4SJames Chapman 642*309795f4SJames Chapman for (;;) { 643*309795f4SJames Chapman if (tunnel == NULL) { 644*309795f4SJames Chapman tunnel = l2tp_tunnel_find_nth(net, ti); 645*309795f4SJames Chapman if (tunnel == NULL) 646*309795f4SJames Chapman goto out; 647*309795f4SJames Chapman } 648*309795f4SJames Chapman 649*309795f4SJames Chapman session = l2tp_session_find_nth(tunnel, si); 650*309795f4SJames Chapman if (session == NULL) { 651*309795f4SJames Chapman ti++; 652*309795f4SJames Chapman tunnel = NULL; 653*309795f4SJames Chapman si = 0; 654*309795f4SJames Chapman continue; 655*309795f4SJames Chapman } 656*309795f4SJames Chapman 657*309795f4SJames Chapman if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).pid, 658*309795f4SJames Chapman cb->nlh->nlmsg_seq, NLM_F_MULTI, 659*309795f4SJames Chapman session) <= 0) 660*309795f4SJames Chapman break; 661*309795f4SJames Chapman 662*309795f4SJames Chapman si++; 663*309795f4SJames Chapman } 664*309795f4SJames Chapman 665*309795f4SJames Chapman out: 666*309795f4SJames Chapman cb->args[0] = ti; 667*309795f4SJames Chapman cb->args[1] = si; 668*309795f4SJames Chapman 669*309795f4SJames Chapman return skb->len; 670*309795f4SJames Chapman } 671*309795f4SJames Chapman 672*309795f4SJames Chapman static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { 673*309795f4SJames Chapman [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, 674*309795f4SJames Chapman [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, 675*309795f4SJames Chapman [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, 676*309795f4SJames Chapman [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, 677*309795f4SJames Chapman [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, 678*309795f4SJames Chapman [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, 679*309795f4SJames Chapman [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, 680*309795f4SJames Chapman [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, 681*309795f4SJames Chapman [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, 682*309795f4SJames Chapman [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, 683*309795f4SJames Chapman [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, 684*309795f4SJames Chapman [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, 685*309795f4SJames Chapman [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, 686*309795f4SJames Chapman [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, 687*309795f4SJames Chapman [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, 688*309795f4SJames Chapman [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, 689*309795f4SJames Chapman [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, 690*309795f4SJames Chapman [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, 691*309795f4SJames Chapman [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, 692*309795f4SJames Chapman [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, 693*309795f4SJames Chapman [L2TP_ATTR_FD] = { .type = NLA_U32, }, 694*309795f4SJames Chapman [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, 695*309795f4SJames Chapman [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, 696*309795f4SJames Chapman [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, 697*309795f4SJames Chapman [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, 698*309795f4SJames Chapman [L2TP_ATTR_MTU] = { .type = NLA_U16, }, 699*309795f4SJames Chapman [L2TP_ATTR_MRU] = { .type = NLA_U16, }, 700*309795f4SJames Chapman [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, 701*309795f4SJames Chapman [L2TP_ATTR_IFNAME] = { 702*309795f4SJames Chapman .type = NLA_NUL_STRING, 703*309795f4SJames Chapman .len = IFNAMSIZ - 1, 704*309795f4SJames Chapman }, 705*309795f4SJames Chapman [L2TP_ATTR_COOKIE] = { 706*309795f4SJames Chapman .type = NLA_BINARY, 707*309795f4SJames Chapman .len = 8, 708*309795f4SJames Chapman }, 709*309795f4SJames Chapman [L2TP_ATTR_PEER_COOKIE] = { 710*309795f4SJames Chapman .type = NLA_BINARY, 711*309795f4SJames Chapman .len = 8, 712*309795f4SJames Chapman }, 713*309795f4SJames Chapman }; 714*309795f4SJames Chapman 715*309795f4SJames Chapman static struct genl_ops l2tp_nl_ops[] = { 716*309795f4SJames Chapman { 717*309795f4SJames Chapman .cmd = L2TP_CMD_NOOP, 718*309795f4SJames Chapman .doit = l2tp_nl_cmd_noop, 719*309795f4SJames Chapman .policy = l2tp_nl_policy, 720*309795f4SJames Chapman /* can be retrieved by unprivileged users */ 721*309795f4SJames Chapman }, 722*309795f4SJames Chapman { 723*309795f4SJames Chapman .cmd = L2TP_CMD_TUNNEL_CREATE, 724*309795f4SJames Chapman .doit = l2tp_nl_cmd_tunnel_create, 725*309795f4SJames Chapman .policy = l2tp_nl_policy, 726*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 727*309795f4SJames Chapman }, 728*309795f4SJames Chapman { 729*309795f4SJames Chapman .cmd = L2TP_CMD_TUNNEL_DELETE, 730*309795f4SJames Chapman .doit = l2tp_nl_cmd_tunnel_delete, 731*309795f4SJames Chapman .policy = l2tp_nl_policy, 732*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 733*309795f4SJames Chapman }, 734*309795f4SJames Chapman { 735*309795f4SJames Chapman .cmd = L2TP_CMD_TUNNEL_MODIFY, 736*309795f4SJames Chapman .doit = l2tp_nl_cmd_tunnel_modify, 737*309795f4SJames Chapman .policy = l2tp_nl_policy, 738*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 739*309795f4SJames Chapman }, 740*309795f4SJames Chapman { 741*309795f4SJames Chapman .cmd = L2TP_CMD_TUNNEL_GET, 742*309795f4SJames Chapman .doit = l2tp_nl_cmd_tunnel_get, 743*309795f4SJames Chapman .dumpit = l2tp_nl_cmd_tunnel_dump, 744*309795f4SJames Chapman .policy = l2tp_nl_policy, 745*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 746*309795f4SJames Chapman }, 747*309795f4SJames Chapman { 748*309795f4SJames Chapman .cmd = L2TP_CMD_SESSION_CREATE, 749*309795f4SJames Chapman .doit = l2tp_nl_cmd_session_create, 750*309795f4SJames Chapman .policy = l2tp_nl_policy, 751*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 752*309795f4SJames Chapman }, 753*309795f4SJames Chapman { 754*309795f4SJames Chapman .cmd = L2TP_CMD_SESSION_DELETE, 755*309795f4SJames Chapman .doit = l2tp_nl_cmd_session_delete, 756*309795f4SJames Chapman .policy = l2tp_nl_policy, 757*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 758*309795f4SJames Chapman }, 759*309795f4SJames Chapman { 760*309795f4SJames Chapman .cmd = L2TP_CMD_SESSION_MODIFY, 761*309795f4SJames Chapman .doit = l2tp_nl_cmd_session_modify, 762*309795f4SJames Chapman .policy = l2tp_nl_policy, 763*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 764*309795f4SJames Chapman }, 765*309795f4SJames Chapman { 766*309795f4SJames Chapman .cmd = L2TP_CMD_SESSION_GET, 767*309795f4SJames Chapman .doit = l2tp_nl_cmd_session_get, 768*309795f4SJames Chapman .dumpit = l2tp_nl_cmd_session_dump, 769*309795f4SJames Chapman .policy = l2tp_nl_policy, 770*309795f4SJames Chapman .flags = GENL_ADMIN_PERM, 771*309795f4SJames Chapman }, 772*309795f4SJames Chapman }; 773*309795f4SJames Chapman 774*309795f4SJames Chapman int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) 775*309795f4SJames Chapman { 776*309795f4SJames Chapman int ret; 777*309795f4SJames Chapman 778*309795f4SJames Chapman ret = -EINVAL; 779*309795f4SJames Chapman if (pw_type >= __L2TP_PWTYPE_MAX) 780*309795f4SJames Chapman goto err; 781*309795f4SJames Chapman 782*309795f4SJames Chapman genl_lock(); 783*309795f4SJames Chapman ret = -EBUSY; 784*309795f4SJames Chapman if (l2tp_nl_cmd_ops[pw_type]) 785*309795f4SJames Chapman goto out; 786*309795f4SJames Chapman 787*309795f4SJames Chapman l2tp_nl_cmd_ops[pw_type] = ops; 788*309795f4SJames Chapman 789*309795f4SJames Chapman out: 790*309795f4SJames Chapman genl_unlock(); 791*309795f4SJames Chapman err: 792*309795f4SJames Chapman return 0; 793*309795f4SJames Chapman } 794*309795f4SJames Chapman EXPORT_SYMBOL_GPL(l2tp_nl_register_ops); 795*309795f4SJames Chapman 796*309795f4SJames Chapman void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) 797*309795f4SJames Chapman { 798*309795f4SJames Chapman if (pw_type < __L2TP_PWTYPE_MAX) { 799*309795f4SJames Chapman genl_lock(); 800*309795f4SJames Chapman l2tp_nl_cmd_ops[pw_type] = NULL; 801*309795f4SJames Chapman genl_unlock(); 802*309795f4SJames Chapman } 803*309795f4SJames Chapman } 804*309795f4SJames Chapman EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); 805*309795f4SJames Chapman 806*309795f4SJames Chapman static int l2tp_nl_init(void) 807*309795f4SJames Chapman { 808*309795f4SJames Chapman int err; 809*309795f4SJames Chapman 810*309795f4SJames Chapman printk(KERN_INFO "L2TP netlink interface\n"); 811*309795f4SJames Chapman err = genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops, 812*309795f4SJames Chapman ARRAY_SIZE(l2tp_nl_ops)); 813*309795f4SJames Chapman 814*309795f4SJames Chapman return err; 815*309795f4SJames Chapman } 816*309795f4SJames Chapman 817*309795f4SJames Chapman static void l2tp_nl_cleanup(void) 818*309795f4SJames Chapman { 819*309795f4SJames Chapman genl_unregister_family(&l2tp_nl_family); 820*309795f4SJames Chapman } 821*309795f4SJames Chapman 822*309795f4SJames Chapman module_init(l2tp_nl_init); 823*309795f4SJames Chapman module_exit(l2tp_nl_cleanup); 824*309795f4SJames Chapman 825*309795f4SJames Chapman MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); 826*309795f4SJames Chapman MODULE_DESCRIPTION("L2TP netlink"); 827*309795f4SJames Chapman MODULE_LICENSE("GPL"); 828*309795f4SJames Chapman MODULE_VERSION("1.0"); 829*309795f4SJames Chapman MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK) "-proto-" \ 830*309795f4SJames Chapman __stringify(NETLINK_GENERIC) "-type-" "l2tp") 831