19641458dSRémi Denis-Courmont /* 29641458dSRémi Denis-Courmont * File: pep.c 39641458dSRémi Denis-Courmont * 49641458dSRémi Denis-Courmont * Phonet pipe protocol end point socket 59641458dSRémi Denis-Courmont * 69641458dSRémi Denis-Courmont * Copyright (C) 2008 Nokia Corporation. 79641458dSRémi Denis-Courmont * 831fdc555SRémi Denis-Courmont * Author: Rémi Denis-Courmont 99641458dSRémi Denis-Courmont * 109641458dSRémi Denis-Courmont * This program is free software; you can redistribute it and/or 119641458dSRémi Denis-Courmont * modify it under the terms of the GNU General Public License 129641458dSRémi Denis-Courmont * version 2 as published by the Free Software Foundation. 139641458dSRémi Denis-Courmont * 149641458dSRémi Denis-Courmont * This program is distributed in the hope that it will be useful, but 159641458dSRémi Denis-Courmont * WITHOUT ANY WARRANTY; without even the implied warranty of 169641458dSRémi Denis-Courmont * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 179641458dSRémi Denis-Courmont * General Public License for more details. 189641458dSRémi Denis-Courmont * 199641458dSRémi Denis-Courmont * You should have received a copy of the GNU General Public License 209641458dSRémi Denis-Courmont * along with this program; if not, write to the Free Software 219641458dSRémi Denis-Courmont * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 229641458dSRémi Denis-Courmont * 02110-1301 USA 239641458dSRémi Denis-Courmont */ 249641458dSRémi Denis-Courmont 259641458dSRémi Denis-Courmont #include <linux/kernel.h> 265a0e3ad6STejun Heo #include <linux/slab.h> 279641458dSRémi Denis-Courmont #include <linux/socket.h> 289641458dSRémi Denis-Courmont #include <net/sock.h> 299641458dSRémi Denis-Courmont #include <net/tcp_states.h> 309641458dSRémi Denis-Courmont #include <asm/ioctls.h> 319641458dSRémi Denis-Courmont 329641458dSRémi Denis-Courmont #include <linux/phonet.h> 333a9a231dSPaul Gortmaker #include <linux/module.h> 349641458dSRémi Denis-Courmont #include <net/phonet/phonet.h> 359641458dSRémi Denis-Courmont #include <net/phonet/pep.h> 3602a47617SRémi Denis-Courmont #include <net/phonet/gprs.h> 379641458dSRémi Denis-Courmont 389641458dSRémi Denis-Courmont /* sk_state values: 399641458dSRémi Denis-Courmont * TCP_CLOSE sock not in use yet 409641458dSRémi Denis-Courmont * TCP_CLOSE_WAIT disconnected pipe 419641458dSRémi Denis-Courmont * TCP_LISTEN listening pipe endpoint 429641458dSRémi Denis-Courmont * TCP_SYN_RECV connected pipe in disabled state 439641458dSRémi Denis-Courmont * TCP_ESTABLISHED connected pipe in enabled state 449641458dSRémi Denis-Courmont * 459641458dSRémi Denis-Courmont * pep_sock locking: 46f7ae8d59SRémi Denis-Courmont * - sk_state, hlist: sock lock needed 479641458dSRémi Denis-Courmont * - listener: read only 489641458dSRémi Denis-Courmont * - pipe_handle: read only 499641458dSRémi Denis-Courmont */ 509641458dSRémi Denis-Courmont 519641458dSRémi Denis-Courmont #define CREDITS_MAX 10 529641458dSRémi Denis-Courmont #define CREDITS_THR 7 539641458dSRémi Denis-Courmont 549641458dSRémi Denis-Courmont #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ 559641458dSRémi Denis-Courmont 569641458dSRémi Denis-Courmont /* Get the next TLV sub-block. */ 579641458dSRémi Denis-Courmont static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, 589641458dSRémi Denis-Courmont void *buf) 599641458dSRémi Denis-Courmont { 609641458dSRémi Denis-Courmont void *data = NULL; 619641458dSRémi Denis-Courmont struct { 629641458dSRémi Denis-Courmont u8 sb_type; 639641458dSRémi Denis-Courmont u8 sb_len; 649641458dSRémi Denis-Courmont } *ph, h; 659641458dSRémi Denis-Courmont int buflen = *plen; 669641458dSRémi Denis-Courmont 679641458dSRémi Denis-Courmont ph = skb_header_pointer(skb, 0, 2, &h); 689641458dSRémi Denis-Courmont if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) 699641458dSRémi Denis-Courmont return NULL; 709641458dSRémi Denis-Courmont ph->sb_len -= 2; 719641458dSRémi Denis-Courmont *ptype = ph->sb_type; 729641458dSRémi Denis-Courmont *plen = ph->sb_len; 739641458dSRémi Denis-Courmont 749641458dSRémi Denis-Courmont if (buflen > ph->sb_len) 759641458dSRémi Denis-Courmont buflen = ph->sb_len; 769641458dSRémi Denis-Courmont data = skb_header_pointer(skb, 2, buflen, buf); 779641458dSRémi Denis-Courmont __skb_pull(skb, 2 + ph->sb_len); 789641458dSRémi Denis-Courmont return data; 799641458dSRémi Denis-Courmont } 809641458dSRémi Denis-Courmont 8144c9ab16SRémi Denis-Courmont static struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload, 8244c9ab16SRémi Denis-Courmont int len, gfp_t priority) 8344c9ab16SRémi Denis-Courmont { 8444c9ab16SRémi Denis-Courmont struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); 8544c9ab16SRémi Denis-Courmont if (!skb) 8644c9ab16SRémi Denis-Courmont return NULL; 8744c9ab16SRémi Denis-Courmont skb_set_owner_w(skb, sk); 8844c9ab16SRémi Denis-Courmont 8944c9ab16SRémi Denis-Courmont skb_reserve(skb, MAX_PNPIPE_HEADER); 9044c9ab16SRémi Denis-Courmont __skb_put(skb, len); 9144c9ab16SRémi Denis-Courmont skb_copy_to_linear_data(skb, payload, len); 9244c9ab16SRémi Denis-Courmont __skb_push(skb, sizeof(struct pnpipehdr)); 9344c9ab16SRémi Denis-Courmont skb_reset_transport_header(skb); 9444c9ab16SRémi Denis-Courmont return skb; 9544c9ab16SRémi Denis-Courmont } 9644c9ab16SRémi Denis-Courmont 9744c9ab16SRémi Denis-Courmont static int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code, 9844c9ab16SRémi Denis-Courmont const void *data, int len, gfp_t priority) 999641458dSRémi Denis-Courmont { 1009641458dSRémi Denis-Courmont const struct pnpipehdr *oph = pnp_hdr(oskb); 1019641458dSRémi Denis-Courmont struct pnpipehdr *ph; 1029641458dSRémi Denis-Courmont struct sk_buff *skb; 10314ba8faeSRémi Denis-Courmont struct sockaddr_pn peer; 1049641458dSRémi Denis-Courmont 10544c9ab16SRémi Denis-Courmont skb = pep_alloc_skb(sk, data, len, priority); 1069641458dSRémi Denis-Courmont if (!skb) 1079641458dSRémi Denis-Courmont return -ENOMEM; 1089641458dSRémi Denis-Courmont 1099641458dSRémi Denis-Courmont ph = pnp_hdr(skb); 1109641458dSRémi Denis-Courmont ph->utid = oph->utid; 1119641458dSRémi Denis-Courmont ph->message_id = oph->message_id + 1; /* REQ -> RESP */ 1129641458dSRémi Denis-Courmont ph->pipe_handle = oph->pipe_handle; 1139641458dSRémi Denis-Courmont ph->error_code = code; 1149641458dSRémi Denis-Courmont 11514ba8faeSRémi Denis-Courmont pn_skb_get_src_sockaddr(oskb, &peer); 11614ba8faeSRémi Denis-Courmont return pn_skb_send(sk, skb, &peer); 1179641458dSRémi Denis-Courmont } 1189641458dSRémi Denis-Courmont 11944c9ab16SRémi Denis-Courmont static int pep_indicate(struct sock *sk, u8 id, u8 code, 12044c9ab16SRémi Denis-Courmont const void *data, int len, gfp_t priority) 12144c9ab16SRémi Denis-Courmont { 12244c9ab16SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 12344c9ab16SRémi Denis-Courmont struct pnpipehdr *ph; 12444c9ab16SRémi Denis-Courmont struct sk_buff *skb; 12544c9ab16SRémi Denis-Courmont 12644c9ab16SRémi Denis-Courmont skb = pep_alloc_skb(sk, data, len, priority); 12744c9ab16SRémi Denis-Courmont if (!skb) 12844c9ab16SRémi Denis-Courmont return -ENOMEM; 12944c9ab16SRémi Denis-Courmont 13044c9ab16SRémi Denis-Courmont ph = pnp_hdr(skb); 13144c9ab16SRémi Denis-Courmont ph->utid = 0; 13244c9ab16SRémi Denis-Courmont ph->message_id = id; 13344c9ab16SRémi Denis-Courmont ph->pipe_handle = pn->pipe_handle; 13444c9ab16SRémi Denis-Courmont ph->data[0] = code; 13544c9ab16SRémi Denis-Courmont return pn_skb_send(sk, skb, NULL); 13644c9ab16SRémi Denis-Courmont } 13744c9ab16SRémi Denis-Courmont 1389641458dSRémi Denis-Courmont #define PAD 0x00 1398d98efa8SKumar Sanghvi 14044c9ab16SRémi Denis-Courmont static int pipe_handler_request(struct sock *sk, u8 id, u8 code, 14144c9ab16SRémi Denis-Courmont const void *data, int len) 1428d98efa8SKumar Sanghvi { 14344c9ab16SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 1448d98efa8SKumar Sanghvi struct pnpipehdr *ph; 1458d98efa8SKumar Sanghvi struct sk_buff *skb; 1468d98efa8SKumar Sanghvi 14744c9ab16SRémi Denis-Courmont skb = pep_alloc_skb(sk, data, len, GFP_KERNEL); 14844c9ab16SRémi Denis-Courmont if (!skb) 14944c9ab16SRémi Denis-Courmont return -ENOMEM; 15044c9ab16SRémi Denis-Courmont 15144c9ab16SRémi Denis-Courmont ph = pnp_hdr(skb); 15244c9ab16SRémi Denis-Courmont ph->utid = id; /* whatever */ 15344c9ab16SRémi Denis-Courmont ph->message_id = id; 15444c9ab16SRémi Denis-Courmont ph->pipe_handle = pn->pipe_handle; 15544c9ab16SRémi Denis-Courmont ph->data[0] = code; 15644c9ab16SRémi Denis-Courmont return pn_skb_send(sk, skb, NULL); 15744c9ab16SRémi Denis-Courmont } 15844c9ab16SRémi Denis-Courmont 15944c9ab16SRémi Denis-Courmont static int pipe_handler_send_created_ind(struct sock *sk) 16044c9ab16SRémi Denis-Courmont { 16144c9ab16SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 16244c9ab16SRémi Denis-Courmont u8 data[4] = { 16344c9ab16SRémi Denis-Courmont PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2), 16444c9ab16SRémi Denis-Courmont pn->tx_fc, pn->rx_fc, 1658d98efa8SKumar Sanghvi }; 1668d98efa8SKumar Sanghvi 16744c9ab16SRémi Denis-Courmont return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */, 16844c9ab16SRémi Denis-Courmont data, 4, GFP_ATOMIC); 1698d98efa8SKumar Sanghvi } 1708d98efa8SKumar Sanghvi 1719641458dSRémi Denis-Courmont static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) 1729641458dSRémi Denis-Courmont { 1739641458dSRémi Denis-Courmont static const u8 data[20] = { 1749641458dSRémi Denis-Courmont PAD, PAD, PAD, 2 /* sub-blocks */, 1759641458dSRémi Denis-Courmont PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD, 1769641458dSRémi Denis-Courmont PN_MULTI_CREDIT_FLOW_CONTROL, 1779641458dSRémi Denis-Courmont PN_ONE_CREDIT_FLOW_CONTROL, 1789641458dSRémi Denis-Courmont PN_LEGACY_FLOW_CONTROL, 1799641458dSRémi Denis-Courmont PAD, 1809641458dSRémi Denis-Courmont PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD, 1819641458dSRémi Denis-Courmont PN_MULTI_CREDIT_FLOW_CONTROL, 1829641458dSRémi Denis-Courmont PN_ONE_CREDIT_FLOW_CONTROL, 1839641458dSRémi Denis-Courmont PN_LEGACY_FLOW_CONTROL, 1849641458dSRémi Denis-Courmont PAD, 1859641458dSRémi Denis-Courmont }; 1869641458dSRémi Denis-Courmont 1879641458dSRémi Denis-Courmont might_sleep(); 1889641458dSRémi Denis-Courmont return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), 1899641458dSRémi Denis-Courmont GFP_KERNEL); 1909641458dSRémi Denis-Courmont } 1919641458dSRémi Denis-Courmont 192f7ae8d59SRémi Denis-Courmont static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code, 193f7ae8d59SRémi Denis-Courmont gfp_t priority) 1949641458dSRémi Denis-Courmont { 1959641458dSRémi Denis-Courmont static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; 1969641458dSRémi Denis-Courmont WARN_ON(code == PN_PIPE_NO_ERROR); 197f7ae8d59SRémi Denis-Courmont return pep_reply(sk, skb, code, data, sizeof(data), priority); 1989641458dSRémi Denis-Courmont } 1999641458dSRémi Denis-Courmont 2009641458dSRémi Denis-Courmont /* Control requests are not sent by the pipe service and have a specific 2019641458dSRémi Denis-Courmont * message format. */ 202c41bd97fSRémi Denis-Courmont static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code, 203c41bd97fSRémi Denis-Courmont gfp_t priority) 2049641458dSRémi Denis-Courmont { 2059641458dSRémi Denis-Courmont const struct pnpipehdr *oph = pnp_hdr(oskb); 2069641458dSRémi Denis-Courmont struct sk_buff *skb; 2079641458dSRémi Denis-Courmont struct pnpipehdr *ph; 2089641458dSRémi Denis-Courmont struct sockaddr_pn dst; 20944c9ab16SRémi Denis-Courmont u8 data[4] = { 21044c9ab16SRémi Denis-Courmont oph->data[0], /* PEP type */ 21144c9ab16SRémi Denis-Courmont code, /* error code, at an unusual offset */ 21244c9ab16SRémi Denis-Courmont PAD, PAD, 21344c9ab16SRémi Denis-Courmont }; 2149641458dSRémi Denis-Courmont 21544c9ab16SRémi Denis-Courmont skb = pep_alloc_skb(sk, data, 4, priority); 2169641458dSRémi Denis-Courmont if (!skb) 2179641458dSRémi Denis-Courmont return -ENOMEM; 2189641458dSRémi Denis-Courmont 21944c9ab16SRémi Denis-Courmont ph = pnp_hdr(skb); 2209641458dSRémi Denis-Courmont ph->utid = oph->utid; 2219641458dSRémi Denis-Courmont ph->message_id = PNS_PEP_CTRL_RESP; 2229641458dSRémi Denis-Courmont ph->pipe_handle = oph->pipe_handle; 2239641458dSRémi Denis-Courmont ph->data[0] = oph->data[1]; /* CTRL id */ 2249641458dSRémi Denis-Courmont 2259641458dSRémi Denis-Courmont pn_skb_get_src_sockaddr(oskb, &dst); 2269641458dSRémi Denis-Courmont return pn_skb_send(sk, skb, &dst); 2279641458dSRémi Denis-Courmont } 2289641458dSRémi Denis-Courmont 2299641458dSRémi Denis-Courmont static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) 2309641458dSRémi Denis-Courmont { 23144c9ab16SRémi Denis-Courmont u8 data[4] = { type, PAD, PAD, status }; 2329641458dSRémi Denis-Courmont 23344c9ab16SRémi Denis-Courmont return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON, 23444c9ab16SRémi Denis-Courmont data, 4, priority); 2359641458dSRémi Denis-Courmont } 2369641458dSRémi Denis-Courmont 2379641458dSRémi Denis-Courmont /* Send our RX flow control information to the sender. 2389641458dSRémi Denis-Courmont * Socket must be locked. */ 23944c9ab16SRémi Denis-Courmont static void pipe_grant_credits(struct sock *sk, gfp_t priority) 2409641458dSRémi Denis-Courmont { 2419641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 2429641458dSRémi Denis-Courmont 2439641458dSRémi Denis-Courmont BUG_ON(sk->sk_state != TCP_ESTABLISHED); 2449641458dSRémi Denis-Courmont 2459641458dSRémi Denis-Courmont switch (pn->rx_fc) { 2469641458dSRémi Denis-Courmont case PN_LEGACY_FLOW_CONTROL: /* TODO */ 2479641458dSRémi Denis-Courmont break; 2489641458dSRémi Denis-Courmont case PN_ONE_CREDIT_FLOW_CONTROL: 24944c9ab16SRémi Denis-Courmont if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, 25044c9ab16SRémi Denis-Courmont PEP_IND_READY, priority) == 0) 2519641458dSRémi Denis-Courmont pn->rx_credits = 1; 2529641458dSRémi Denis-Courmont break; 2539641458dSRémi Denis-Courmont case PN_MULTI_CREDIT_FLOW_CONTROL: 2549641458dSRémi Denis-Courmont if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX) 2559641458dSRémi Denis-Courmont break; 2569641458dSRémi Denis-Courmont if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS, 2579641458dSRémi Denis-Courmont CREDITS_MAX - pn->rx_credits, 25844c9ab16SRémi Denis-Courmont priority) == 0) 2599641458dSRémi Denis-Courmont pn->rx_credits = CREDITS_MAX; 2609641458dSRémi Denis-Courmont break; 2619641458dSRémi Denis-Courmont } 2629641458dSRémi Denis-Courmont } 2639641458dSRémi Denis-Courmont 2649641458dSRémi Denis-Courmont static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) 2659641458dSRémi Denis-Courmont { 2669641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 267a91e7d47SKumar Sanghvi struct pnpipehdr *hdr; 268be677730SRémi Denis-Courmont int wake = 0; 2699641458dSRémi Denis-Courmont 2709641458dSRémi Denis-Courmont if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) 2719641458dSRémi Denis-Courmont return -EINVAL; 2729641458dSRémi Denis-Courmont 273a91e7d47SKumar Sanghvi hdr = pnp_hdr(skb); 2749641458dSRémi Denis-Courmont if (hdr->data[0] != PN_PEP_TYPE_COMMON) { 275ba7a46f1SJoe Perches net_dbg_ratelimited("Phonet unknown PEP type: %u\n", 27695c96174SEric Dumazet (unsigned int)hdr->data[0]); 2779641458dSRémi Denis-Courmont return -EOPNOTSUPP; 2789641458dSRémi Denis-Courmont } 2799641458dSRémi Denis-Courmont 2809641458dSRémi Denis-Courmont switch (hdr->data[1]) { 2819641458dSRémi Denis-Courmont case PN_PEP_IND_FLOW_CONTROL: 2829641458dSRémi Denis-Courmont switch (pn->tx_fc) { 2839641458dSRémi Denis-Courmont case PN_LEGACY_FLOW_CONTROL: 2849641458dSRémi Denis-Courmont switch (hdr->data[4]) { 2859641458dSRémi Denis-Courmont case PEP_IND_BUSY: 286be677730SRémi Denis-Courmont atomic_set(&pn->tx_credits, 0); 2879641458dSRémi Denis-Courmont break; 2889641458dSRémi Denis-Courmont case PEP_IND_READY: 289be677730SRémi Denis-Courmont atomic_set(&pn->tx_credits, wake = 1); 2909641458dSRémi Denis-Courmont break; 2919641458dSRémi Denis-Courmont } 2929641458dSRémi Denis-Courmont break; 2939641458dSRémi Denis-Courmont case PN_ONE_CREDIT_FLOW_CONTROL: 2949641458dSRémi Denis-Courmont if (hdr->data[4] == PEP_IND_READY) 295be677730SRémi Denis-Courmont atomic_set(&pn->tx_credits, wake = 1); 2969641458dSRémi Denis-Courmont break; 2979641458dSRémi Denis-Courmont } 2989641458dSRémi Denis-Courmont break; 2999641458dSRémi Denis-Courmont 3009641458dSRémi Denis-Courmont case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: 3019641458dSRémi Denis-Courmont if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) 3029641458dSRémi Denis-Courmont break; 303be677730SRémi Denis-Courmont atomic_add(wake = hdr->data[4], &pn->tx_credits); 3049641458dSRémi Denis-Courmont break; 3059641458dSRémi Denis-Courmont 3069641458dSRémi Denis-Courmont default: 307ba7a46f1SJoe Perches net_dbg_ratelimited("Phonet unknown PEP indication: %u\n", 30895c96174SEric Dumazet (unsigned int)hdr->data[1]); 3099641458dSRémi Denis-Courmont return -EOPNOTSUPP; 3109641458dSRémi Denis-Courmont } 311be677730SRémi Denis-Courmont if (wake) 3129641458dSRémi Denis-Courmont sk->sk_write_space(sk); 3139641458dSRémi Denis-Courmont return 0; 3149641458dSRémi Denis-Courmont } 3159641458dSRémi Denis-Courmont 3169641458dSRémi Denis-Courmont static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb) 3179641458dSRémi Denis-Courmont { 3189641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 3199641458dSRémi Denis-Courmont struct pnpipehdr *hdr = pnp_hdr(skb); 3209641458dSRémi Denis-Courmont u8 n_sb = hdr->data[0]; 3219641458dSRémi Denis-Courmont 3229641458dSRémi Denis-Courmont pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; 3239641458dSRémi Denis-Courmont __skb_pull(skb, sizeof(*hdr)); 3249641458dSRémi Denis-Courmont while (n_sb > 0) { 3259641458dSRémi Denis-Courmont u8 type, buf[2], len = sizeof(buf); 3269641458dSRémi Denis-Courmont u8 *data = pep_get_sb(skb, &type, &len, buf); 3279641458dSRémi Denis-Courmont 3289641458dSRémi Denis-Courmont if (data == NULL) 3299641458dSRémi Denis-Courmont return -EINVAL; 3309641458dSRémi Denis-Courmont switch (type) { 3319641458dSRémi Denis-Courmont case PN_PIPE_SB_NEGOTIATED_FC: 3329641458dSRémi Denis-Courmont if (len < 2 || (data[0] | data[1]) > 3) 3339641458dSRémi Denis-Courmont break; 3349641458dSRémi Denis-Courmont pn->tx_fc = data[0] & 3; 3359641458dSRémi Denis-Courmont pn->rx_fc = data[1] & 3; 3369641458dSRémi Denis-Courmont break; 3379641458dSRémi Denis-Courmont } 3389641458dSRémi Denis-Courmont n_sb--; 3399641458dSRémi Denis-Courmont } 3409641458dSRémi Denis-Courmont return 0; 3419641458dSRémi Denis-Courmont } 3429641458dSRémi Denis-Courmont 3439641458dSRémi Denis-Courmont /* Queue an skb to a connected sock. 3449641458dSRémi Denis-Courmont * Socket lock must be held. */ 3459641458dSRémi Denis-Courmont static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) 3469641458dSRémi Denis-Courmont { 3479641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 3489641458dSRémi Denis-Courmont struct pnpipehdr *hdr = pnp_hdr(skb); 349c41bd97fSRémi Denis-Courmont struct sk_buff_head *queue; 3509641458dSRémi Denis-Courmont int err = 0; 3519641458dSRémi Denis-Courmont 3529641458dSRémi Denis-Courmont BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); 3539641458dSRémi Denis-Courmont 3549641458dSRémi Denis-Courmont switch (hdr->message_id) { 3559641458dSRémi Denis-Courmont case PNS_PEP_CONNECT_REQ: 356f7ae8d59SRémi Denis-Courmont pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); 3579641458dSRémi Denis-Courmont break; 3589641458dSRémi Denis-Courmont 3599641458dSRémi Denis-Courmont case PNS_PEP_DISCONNECT_REQ: 3609641458dSRémi Denis-Courmont pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 3619641458dSRémi Denis-Courmont sk->sk_state = TCP_CLOSE_WAIT; 3629641458dSRémi Denis-Courmont if (!sock_flag(sk, SOCK_DEAD)) 3639641458dSRémi Denis-Courmont sk->sk_state_change(sk); 3649641458dSRémi Denis-Courmont break; 3659641458dSRémi Denis-Courmont 3669641458dSRémi Denis-Courmont case PNS_PEP_ENABLE_REQ: 3679641458dSRémi Denis-Courmont /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ 3689641458dSRémi Denis-Courmont pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 3699641458dSRémi Denis-Courmont break; 3709641458dSRémi Denis-Courmont 3719641458dSRémi Denis-Courmont case PNS_PEP_RESET_REQ: 3729641458dSRémi Denis-Courmont switch (hdr->state_after_reset) { 3739641458dSRémi Denis-Courmont case PN_PIPE_DISABLE: 3749641458dSRémi Denis-Courmont pn->init_enable = 0; 3759641458dSRémi Denis-Courmont break; 3769641458dSRémi Denis-Courmont case PN_PIPE_ENABLE: 3779641458dSRémi Denis-Courmont pn->init_enable = 1; 3789641458dSRémi Denis-Courmont break; 3799641458dSRémi Denis-Courmont default: /* not allowed to send an error here!? */ 3809641458dSRémi Denis-Courmont err = -EINVAL; 3819641458dSRémi Denis-Courmont goto out; 3829641458dSRémi Denis-Courmont } 3839641458dSRémi Denis-Courmont /* fall through */ 3849641458dSRémi Denis-Courmont case PNS_PEP_DISABLE_REQ: 385be677730SRémi Denis-Courmont atomic_set(&pn->tx_credits, 0); 3869641458dSRémi Denis-Courmont pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 3879641458dSRémi Denis-Courmont break; 3889641458dSRémi Denis-Courmont 3899641458dSRémi Denis-Courmont case PNS_PEP_CTRL_REQ: 3902e2fb4b3SRémi Denis-Courmont if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { 3912e2fb4b3SRémi Denis-Courmont atomic_inc(&sk->sk_drops); 3929641458dSRémi Denis-Courmont break; 3932e2fb4b3SRémi Denis-Courmont } 394c41bd97fSRémi Denis-Courmont __skb_pull(skb, 4); 395c41bd97fSRémi Denis-Courmont queue = &pn->ctrlreq_queue; 396c41bd97fSRémi Denis-Courmont goto queue; 3979641458dSRémi Denis-Courmont 398fc6a1107SRémi Denis-Courmont case PNS_PIPE_ALIGNED_DATA: 399fc6a1107SRémi Denis-Courmont __skb_pull(skb, 1); 400fc6a1107SRémi Denis-Courmont /* fall through */ 4019641458dSRémi Denis-Courmont case PNS_PIPE_DATA: 4029641458dSRémi Denis-Courmont __skb_pull(skb, 3); /* Pipe data header */ 4039641458dSRémi Denis-Courmont if (!pn_flow_safe(pn->rx_fc)) { 4049641458dSRémi Denis-Courmont err = sock_queue_rcv_skb(sk, skb); 4059641458dSRémi Denis-Courmont if (!err) 4060ebbf318SRémi Denis-Courmont return NET_RX_SUCCESS; 4070ebbf318SRémi Denis-Courmont err = -ENOBUFS; 4089641458dSRémi Denis-Courmont break; 4099641458dSRémi Denis-Courmont } 4109641458dSRémi Denis-Courmont 4119641458dSRémi Denis-Courmont if (pn->rx_credits == 0) { 4122e2fb4b3SRémi Denis-Courmont atomic_inc(&sk->sk_drops); 4139641458dSRémi Denis-Courmont err = -ENOBUFS; 4149641458dSRémi Denis-Courmont break; 4159641458dSRémi Denis-Courmont } 4169641458dSRémi Denis-Courmont pn->rx_credits--; 417c41bd97fSRémi Denis-Courmont queue = &sk->sk_receive_queue; 418c41bd97fSRémi Denis-Courmont goto queue; 4199641458dSRémi Denis-Courmont 4209641458dSRémi Denis-Courmont case PNS_PEP_STATUS_IND: 4219641458dSRémi Denis-Courmont pipe_rcv_status(sk, skb); 4229641458dSRémi Denis-Courmont break; 4239641458dSRémi Denis-Courmont 4249641458dSRémi Denis-Courmont case PNS_PIPE_REDIRECTED_IND: 4259641458dSRémi Denis-Courmont err = pipe_rcv_created(sk, skb); 4269641458dSRémi Denis-Courmont break; 4279641458dSRémi Denis-Courmont 4289641458dSRémi Denis-Courmont case PNS_PIPE_CREATED_IND: 4299641458dSRémi Denis-Courmont err = pipe_rcv_created(sk, skb); 4309641458dSRémi Denis-Courmont if (err) 4319641458dSRémi Denis-Courmont break; 4329641458dSRémi Denis-Courmont /* fall through */ 4339641458dSRémi Denis-Courmont case PNS_PIPE_RESET_IND: 4349641458dSRémi Denis-Courmont if (!pn->init_enable) 4359641458dSRémi Denis-Courmont break; 4369641458dSRémi Denis-Courmont /* fall through */ 4379641458dSRémi Denis-Courmont case PNS_PIPE_ENABLED_IND: 4389641458dSRémi Denis-Courmont if (!pn_flow_safe(pn->tx_fc)) { 439be677730SRémi Denis-Courmont atomic_set(&pn->tx_credits, 1); 4409641458dSRémi Denis-Courmont sk->sk_write_space(sk); 4419641458dSRémi Denis-Courmont } 4429641458dSRémi Denis-Courmont if (sk->sk_state == TCP_ESTABLISHED) 4439641458dSRémi Denis-Courmont break; /* Nothing to do */ 4449641458dSRémi Denis-Courmont sk->sk_state = TCP_ESTABLISHED; 44544c9ab16SRémi Denis-Courmont pipe_grant_credits(sk, GFP_ATOMIC); 4469641458dSRémi Denis-Courmont break; 4479641458dSRémi Denis-Courmont 4489641458dSRémi Denis-Courmont case PNS_PIPE_DISABLED_IND: 4499641458dSRémi Denis-Courmont sk->sk_state = TCP_SYN_RECV; 4509641458dSRémi Denis-Courmont pn->rx_credits = 0; 4519641458dSRémi Denis-Courmont break; 4529641458dSRémi Denis-Courmont 4539641458dSRémi Denis-Courmont default: 454ba7a46f1SJoe Perches net_dbg_ratelimited("Phonet unknown PEP message: %u\n", 4559641458dSRémi Denis-Courmont hdr->message_id); 4569641458dSRémi Denis-Courmont err = -EINVAL; 4579641458dSRémi Denis-Courmont } 4589641458dSRémi Denis-Courmont out: 4599641458dSRémi Denis-Courmont kfree_skb(skb); 4600ebbf318SRémi Denis-Courmont return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS; 461c41bd97fSRémi Denis-Courmont 462c41bd97fSRémi Denis-Courmont queue: 463c41bd97fSRémi Denis-Courmont skb->dev = NULL; 464c41bd97fSRémi Denis-Courmont skb_set_owner_r(skb, sk); 465c41bd97fSRémi Denis-Courmont skb_queue_tail(queue, skb); 466c41bd97fSRémi Denis-Courmont if (!sock_flag(sk, SOCK_DEAD)) 467676d2369SDavid S. Miller sk->sk_data_ready(sk); 4680ebbf318SRémi Denis-Courmont return NET_RX_SUCCESS; 4699641458dSRémi Denis-Courmont } 4709641458dSRémi Denis-Courmont 4719641458dSRémi Denis-Courmont /* Destroy connected sock. */ 4729641458dSRémi Denis-Courmont static void pipe_destruct(struct sock *sk) 4739641458dSRémi Denis-Courmont { 474c41bd97fSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 475c41bd97fSRémi Denis-Courmont 4769641458dSRémi Denis-Courmont skb_queue_purge(&sk->sk_receive_queue); 477c41bd97fSRémi Denis-Courmont skb_queue_purge(&pn->ctrlreq_queue); 4789641458dSRémi Denis-Courmont } 4799641458dSRémi Denis-Courmont 48095c96174SEric Dumazet static u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n) 4818f44fcc7SRémi Denis-Courmont { 48295c96174SEric Dumazet unsigned int i; 4838f44fcc7SRémi Denis-Courmont u8 final_fc = PN_NO_FLOW_CONTROL; 4848f44fcc7SRémi Denis-Courmont 4858f44fcc7SRémi Denis-Courmont for (i = 0; i < n; i++) { 4868f44fcc7SRémi Denis-Courmont u8 fc = fcs[i]; 4878f44fcc7SRémi Denis-Courmont 4888f44fcc7SRémi Denis-Courmont if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL) 4898f44fcc7SRémi Denis-Courmont final_fc = fc; 4908f44fcc7SRémi Denis-Courmont } 4918f44fcc7SRémi Denis-Courmont return final_fc; 4928f44fcc7SRémi Denis-Courmont } 4938f44fcc7SRémi Denis-Courmont 494b3d62553SKumar Sanghvi static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) 495b3d62553SKumar Sanghvi { 496b3d62553SKumar Sanghvi struct pep_sock *pn = pep_sk(sk); 4978f44fcc7SRémi Denis-Courmont struct pnpipehdr *hdr; 4988f44fcc7SRémi Denis-Courmont u8 n_sb; 499b3d62553SKumar Sanghvi 5008f44fcc7SRémi Denis-Courmont if (!pskb_pull(skb, sizeof(*hdr) + 4)) 5018f44fcc7SRémi Denis-Courmont return -EINVAL; 5028f44fcc7SRémi Denis-Courmont 5038f44fcc7SRémi Denis-Courmont hdr = pnp_hdr(skb); 504297edb60SRémi Denis-Courmont if (hdr->error_code != PN_PIPE_NO_ERROR) 505297edb60SRémi Denis-Courmont return -ECONNREFUSED; 5068f44fcc7SRémi Denis-Courmont 5078f44fcc7SRémi Denis-Courmont /* Parse sub-blocks */ 5088f44fcc7SRémi Denis-Courmont n_sb = hdr->data[4]; 5098f44fcc7SRémi Denis-Courmont while (n_sb > 0) { 5108f44fcc7SRémi Denis-Courmont u8 type, buf[6], len = sizeof(buf); 5118f44fcc7SRémi Denis-Courmont const u8 *data = pep_get_sb(skb, &type, &len, buf); 5128f44fcc7SRémi Denis-Courmont 5138f44fcc7SRémi Denis-Courmont if (data == NULL) 5148f44fcc7SRémi Denis-Courmont return -EINVAL; 5158f44fcc7SRémi Denis-Courmont 5168f44fcc7SRémi Denis-Courmont switch (type) { 5178f44fcc7SRémi Denis-Courmont case PN_PIPE_SB_REQUIRED_FC_TX: 5188f44fcc7SRémi Denis-Courmont if (len < 2 || len < data[0]) 5198f44fcc7SRémi Denis-Courmont break; 5208f44fcc7SRémi Denis-Courmont pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2); 5218f44fcc7SRémi Denis-Courmont break; 5228f44fcc7SRémi Denis-Courmont 5238f44fcc7SRémi Denis-Courmont case PN_PIPE_SB_PREFERRED_FC_RX: 5248f44fcc7SRémi Denis-Courmont if (len < 2 || len < data[0]) 5258f44fcc7SRémi Denis-Courmont break; 5268f44fcc7SRémi Denis-Courmont pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2); 5278f44fcc7SRémi Denis-Courmont break; 5288f44fcc7SRémi Denis-Courmont 5298f44fcc7SRémi Denis-Courmont } 5308f44fcc7SRémi Denis-Courmont n_sb--; 5318f44fcc7SRémi Denis-Courmont } 532b3d62553SKumar Sanghvi 53344c9ab16SRémi Denis-Courmont return pipe_handler_send_created_ind(sk); 534b3d62553SKumar Sanghvi } 535297edb60SRémi Denis-Courmont 536bdb6e697SDinesh Kumar Sharma static int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb) 537bdb6e697SDinesh Kumar Sharma { 538bdb6e697SDinesh Kumar Sharma struct pnpipehdr *hdr = pnp_hdr(skb); 539bdb6e697SDinesh Kumar Sharma 540bdb6e697SDinesh Kumar Sharma if (hdr->error_code != PN_PIPE_NO_ERROR) 541bdb6e697SDinesh Kumar Sharma return -ECONNREFUSED; 542bdb6e697SDinesh Kumar Sharma 543bdb6e697SDinesh Kumar Sharma return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */, 544bdb6e697SDinesh Kumar Sharma NULL, 0, GFP_ATOMIC); 545bdb6e697SDinesh Kumar Sharma 546bdb6e697SDinesh Kumar Sharma } 547bdb6e697SDinesh Kumar Sharma 548bdb6e697SDinesh Kumar Sharma static void pipe_start_flow_control(struct sock *sk) 549bdb6e697SDinesh Kumar Sharma { 550bdb6e697SDinesh Kumar Sharma struct pep_sock *pn = pep_sk(sk); 551bdb6e697SDinesh Kumar Sharma 552bdb6e697SDinesh Kumar Sharma if (!pn_flow_safe(pn->tx_fc)) { 553bdb6e697SDinesh Kumar Sharma atomic_set(&pn->tx_credits, 1); 554bdb6e697SDinesh Kumar Sharma sk->sk_write_space(sk); 555bdb6e697SDinesh Kumar Sharma } 556bdb6e697SDinesh Kumar Sharma pipe_grant_credits(sk, GFP_ATOMIC); 557bdb6e697SDinesh Kumar Sharma } 558bdb6e697SDinesh Kumar Sharma 559297edb60SRémi Denis-Courmont /* Queue an skb to an actively connected sock. 560297edb60SRémi Denis-Courmont * Socket lock must be held. */ 561297edb60SRémi Denis-Courmont static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb) 562297edb60SRémi Denis-Courmont { 563297edb60SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 564297edb60SRémi Denis-Courmont struct pnpipehdr *hdr = pnp_hdr(skb); 565297edb60SRémi Denis-Courmont int err = NET_RX_SUCCESS; 566297edb60SRémi Denis-Courmont 567297edb60SRémi Denis-Courmont switch (hdr->message_id) { 568297edb60SRémi Denis-Courmont case PNS_PIPE_ALIGNED_DATA: 569297edb60SRémi Denis-Courmont __skb_pull(skb, 1); 570297edb60SRémi Denis-Courmont /* fall through */ 571297edb60SRémi Denis-Courmont case PNS_PIPE_DATA: 572297edb60SRémi Denis-Courmont __skb_pull(skb, 3); /* Pipe data header */ 573297edb60SRémi Denis-Courmont if (!pn_flow_safe(pn->rx_fc)) { 574297edb60SRémi Denis-Courmont err = sock_queue_rcv_skb(sk, skb); 575297edb60SRémi Denis-Courmont if (!err) 576297edb60SRémi Denis-Courmont return NET_RX_SUCCESS; 577297edb60SRémi Denis-Courmont err = NET_RX_DROP; 578297edb60SRémi Denis-Courmont break; 579297edb60SRémi Denis-Courmont } 580297edb60SRémi Denis-Courmont 581297edb60SRémi Denis-Courmont if (pn->rx_credits == 0) { 582297edb60SRémi Denis-Courmont atomic_inc(&sk->sk_drops); 583297edb60SRémi Denis-Courmont err = NET_RX_DROP; 584297edb60SRémi Denis-Courmont break; 585297edb60SRémi Denis-Courmont } 586297edb60SRémi Denis-Courmont pn->rx_credits--; 587297edb60SRémi Denis-Courmont skb->dev = NULL; 588297edb60SRémi Denis-Courmont skb_set_owner_r(skb, sk); 589297edb60SRémi Denis-Courmont skb_queue_tail(&sk->sk_receive_queue, skb); 590297edb60SRémi Denis-Courmont if (!sock_flag(sk, SOCK_DEAD)) 591676d2369SDavid S. Miller sk->sk_data_ready(sk); 592297edb60SRémi Denis-Courmont return NET_RX_SUCCESS; 593297edb60SRémi Denis-Courmont 594297edb60SRémi Denis-Courmont case PNS_PEP_CONNECT_RESP: 595297edb60SRémi Denis-Courmont if (sk->sk_state != TCP_SYN_SENT) 596297edb60SRémi Denis-Courmont break; 597297edb60SRémi Denis-Courmont if (!sock_flag(sk, SOCK_DEAD)) 598297edb60SRémi Denis-Courmont sk->sk_state_change(sk); 599297edb60SRémi Denis-Courmont if (pep_connresp_rcv(sk, skb)) { 600297edb60SRémi Denis-Courmont sk->sk_state = TCP_CLOSE_WAIT; 601297edb60SRémi Denis-Courmont break; 602297edb60SRémi Denis-Courmont } 603bdb6e697SDinesh Kumar Sharma if (pn->init_enable == PN_PIPE_DISABLE) 604bdb6e697SDinesh Kumar Sharma sk->sk_state = TCP_SYN_RECV; 605bdb6e697SDinesh Kumar Sharma else { 606bdb6e697SDinesh Kumar Sharma sk->sk_state = TCP_ESTABLISHED; 607bdb6e697SDinesh Kumar Sharma pipe_start_flow_control(sk); 608bdb6e697SDinesh Kumar Sharma } 609bdb6e697SDinesh Kumar Sharma break; 610bdb6e697SDinesh Kumar Sharma 611bdb6e697SDinesh Kumar Sharma case PNS_PEP_ENABLE_RESP: 612bdb6e697SDinesh Kumar Sharma if (sk->sk_state != TCP_SYN_SENT) 613bdb6e697SDinesh Kumar Sharma break; 614bdb6e697SDinesh Kumar Sharma 615bdb6e697SDinesh Kumar Sharma if (pep_enableresp_rcv(sk, skb)) { 616bdb6e697SDinesh Kumar Sharma sk->sk_state = TCP_CLOSE_WAIT; 617bdb6e697SDinesh Kumar Sharma break; 618bdb6e697SDinesh Kumar Sharma } 619297edb60SRémi Denis-Courmont 620297edb60SRémi Denis-Courmont sk->sk_state = TCP_ESTABLISHED; 621bdb6e697SDinesh Kumar Sharma pipe_start_flow_control(sk); 622297edb60SRémi Denis-Courmont break; 623297edb60SRémi Denis-Courmont 624297edb60SRémi Denis-Courmont case PNS_PEP_DISCONNECT_RESP: 625297edb60SRémi Denis-Courmont /* sock should already be dead, nothing to do */ 626297edb60SRémi Denis-Courmont break; 627297edb60SRémi Denis-Courmont 628297edb60SRémi Denis-Courmont case PNS_PEP_STATUS_IND: 629297edb60SRémi Denis-Courmont pipe_rcv_status(sk, skb); 630297edb60SRémi Denis-Courmont break; 631297edb60SRémi Denis-Courmont } 632297edb60SRémi Denis-Courmont kfree_skb(skb); 633297edb60SRémi Denis-Courmont return err; 634297edb60SRémi Denis-Courmont } 635b3d62553SKumar Sanghvi 6369641458dSRémi Denis-Courmont /* Listening sock must be locked */ 6379641458dSRémi Denis-Courmont static struct sock *pep_find_pipe(const struct hlist_head *hlist, 6389641458dSRémi Denis-Courmont const struct sockaddr_pn *dst, 6399641458dSRémi Denis-Courmont u8 pipe_handle) 6409641458dSRémi Denis-Courmont { 6419641458dSRémi Denis-Courmont struct sock *sknode; 6429641458dSRémi Denis-Courmont u16 dobj = pn_sockaddr_get_object(dst); 6439641458dSRémi Denis-Courmont 644b67bfe0dSSasha Levin sk_for_each(sknode, hlist) { 6459641458dSRémi Denis-Courmont struct pep_sock *pnnode = pep_sk(sknode); 6469641458dSRémi Denis-Courmont 6479641458dSRémi Denis-Courmont /* Ports match, but addresses might not: */ 6489641458dSRémi Denis-Courmont if (pnnode->pn_sk.sobject != dobj) 6499641458dSRémi Denis-Courmont continue; 6509641458dSRémi Denis-Courmont if (pnnode->pipe_handle != pipe_handle) 6519641458dSRémi Denis-Courmont continue; 6529641458dSRémi Denis-Courmont if (sknode->sk_state == TCP_CLOSE_WAIT) 6539641458dSRémi Denis-Courmont continue; 6549641458dSRémi Denis-Courmont 6559641458dSRémi Denis-Courmont sock_hold(sknode); 6569641458dSRémi Denis-Courmont return sknode; 6579641458dSRémi Denis-Courmont } 6589641458dSRémi Denis-Courmont return NULL; 6599641458dSRémi Denis-Courmont } 6609641458dSRémi Denis-Courmont 6619641458dSRémi Denis-Courmont /* 6629641458dSRémi Denis-Courmont * Deliver an skb to a listening sock. 6639641458dSRémi Denis-Courmont * Socket lock must be held. 6649641458dSRémi Denis-Courmont * We then queue the skb to the right connected sock (if any). 6659641458dSRémi Denis-Courmont */ 6669641458dSRémi Denis-Courmont static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) 6679641458dSRémi Denis-Courmont { 6689641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 6699641458dSRémi Denis-Courmont struct sock *sknode; 6702ddc1ac1SRémi Denis-Courmont struct pnpipehdr *hdr; 6719641458dSRémi Denis-Courmont struct sockaddr_pn dst; 6729641458dSRémi Denis-Courmont u8 pipe_handle; 6739641458dSRémi Denis-Courmont 6749641458dSRémi Denis-Courmont if (!pskb_may_pull(skb, sizeof(*hdr))) 6759641458dSRémi Denis-Courmont goto drop; 6769641458dSRémi Denis-Courmont 6779641458dSRémi Denis-Courmont hdr = pnp_hdr(skb); 6789641458dSRémi Denis-Courmont pipe_handle = hdr->pipe_handle; 6799641458dSRémi Denis-Courmont if (pipe_handle == PN_PIPE_INVALID_HANDLE) 6809641458dSRémi Denis-Courmont goto drop; 6819641458dSRémi Denis-Courmont 6829641458dSRémi Denis-Courmont pn_skb_get_dst_sockaddr(skb, &dst); 6839641458dSRémi Denis-Courmont 6849641458dSRémi Denis-Courmont /* Look for an existing pipe handle */ 6859641458dSRémi Denis-Courmont sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); 6869641458dSRémi Denis-Courmont if (sknode) 6879641458dSRémi Denis-Courmont return sk_receive_skb(sknode, skb, 1); 6889641458dSRémi Denis-Courmont 6899641458dSRémi Denis-Courmont switch (hdr->message_id) { 6909641458dSRémi Denis-Courmont case PNS_PEP_CONNECT_REQ: 691f7ae8d59SRémi Denis-Courmont if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { 692f7ae8d59SRémi Denis-Courmont pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, 693f7ae8d59SRémi Denis-Courmont GFP_ATOMIC); 6949641458dSRémi Denis-Courmont break; 695f7ae8d59SRémi Denis-Courmont } 696f7ae8d59SRémi Denis-Courmont skb_queue_head(&sk->sk_receive_queue, skb); 697f7ae8d59SRémi Denis-Courmont sk_acceptq_added(sk); 698f7ae8d59SRémi Denis-Courmont if (!sock_flag(sk, SOCK_DEAD)) 699676d2369SDavid S. Miller sk->sk_data_ready(sk); 700f7ae8d59SRémi Denis-Courmont return NET_RX_SUCCESS; 7019641458dSRémi Denis-Courmont 7029641458dSRémi Denis-Courmont case PNS_PEP_DISCONNECT_REQ: 7039641458dSRémi Denis-Courmont pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 7049641458dSRémi Denis-Courmont break; 7059641458dSRémi Denis-Courmont 7069641458dSRémi Denis-Courmont case PNS_PEP_CTRL_REQ: 707c41bd97fSRémi Denis-Courmont pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC); 7089641458dSRémi Denis-Courmont break; 7099641458dSRémi Denis-Courmont 7109641458dSRémi Denis-Courmont case PNS_PEP_RESET_REQ: 7119641458dSRémi Denis-Courmont case PNS_PEP_ENABLE_REQ: 7129641458dSRémi Denis-Courmont case PNS_PEP_DISABLE_REQ: 7139641458dSRémi Denis-Courmont /* invalid handle is not even allowed here! */ 7140ebbf318SRémi Denis-Courmont break; 715297edb60SRémi Denis-Courmont 716297edb60SRémi Denis-Courmont default: 717297edb60SRémi Denis-Courmont if ((1 << sk->sk_state) 718297edb60SRémi Denis-Courmont & ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT)) 719297edb60SRémi Denis-Courmont /* actively connected socket */ 720297edb60SRémi Denis-Courmont return pipe_handler_do_rcv(sk, skb); 7219641458dSRémi Denis-Courmont } 7229641458dSRémi Denis-Courmont drop: 7239641458dSRémi Denis-Courmont kfree_skb(skb); 7240ebbf318SRémi Denis-Courmont return NET_RX_SUCCESS; 7259641458dSRémi Denis-Courmont } 7269641458dSRémi Denis-Courmont 7276482f554SRémi Denis-Courmont static int pipe_do_remove(struct sock *sk) 7286482f554SRémi Denis-Courmont { 7296482f554SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 7306482f554SRémi Denis-Courmont struct pnpipehdr *ph; 7316482f554SRémi Denis-Courmont struct sk_buff *skb; 7326482f554SRémi Denis-Courmont 73344c9ab16SRémi Denis-Courmont skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL); 7346482f554SRémi Denis-Courmont if (!skb) 7356482f554SRémi Denis-Courmont return -ENOMEM; 7366482f554SRémi Denis-Courmont 7376482f554SRémi Denis-Courmont ph = pnp_hdr(skb); 7386482f554SRémi Denis-Courmont ph->utid = 0; 7396482f554SRémi Denis-Courmont ph->message_id = PNS_PIPE_REMOVE_REQ; 7406482f554SRémi Denis-Courmont ph->pipe_handle = pn->pipe_handle; 7416482f554SRémi Denis-Courmont ph->data[0] = PAD; 74214ba8faeSRémi Denis-Courmont return pn_skb_send(sk, skb, NULL); 7436482f554SRémi Denis-Courmont } 7446482f554SRémi Denis-Courmont 7459641458dSRémi Denis-Courmont /* associated socket ceases to exist */ 7469641458dSRémi Denis-Courmont static void pep_sock_close(struct sock *sk, long timeout) 7479641458dSRémi Denis-Courmont { 7489641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 74902a47617SRémi Denis-Courmont int ifindex = 0; 7509641458dSRémi Denis-Courmont 751e513480eSRémi Denis-Courmont sock_hold(sk); /* keep a reference after sk_common_release() */ 7529641458dSRémi Denis-Courmont sk_common_release(sk); 7539641458dSRémi Denis-Courmont 7549641458dSRémi Denis-Courmont lock_sock(sk); 755f7ae8d59SRémi Denis-Courmont if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { 756297edb60SRémi Denis-Courmont if (sk->sk_backlog_rcv == pipe_do_rcv) 7576482f554SRémi Denis-Courmont /* Forcefully remove dangling Phonet pipe */ 7586482f554SRémi Denis-Courmont pipe_do_remove(sk); 759297edb60SRémi Denis-Courmont else 760297edb60SRémi Denis-Courmont pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, 761297edb60SRémi Denis-Courmont NULL, 0); 7622feb6181SRémi Denis-Courmont } 763f7ae8d59SRémi Denis-Courmont sk->sk_state = TCP_CLOSE; 764b3d62553SKumar Sanghvi 76502a47617SRémi Denis-Courmont ifindex = pn->ifindex; 76602a47617SRémi Denis-Courmont pn->ifindex = 0; 7679641458dSRémi Denis-Courmont release_sock(sk); 76802a47617SRémi Denis-Courmont 76902a47617SRémi Denis-Courmont if (ifindex) 77002a47617SRémi Denis-Courmont gprs_detach(sk); 771e513480eSRémi Denis-Courmont sock_put(sk); 7729641458dSRémi Denis-Courmont } 7739641458dSRémi Denis-Courmont 7749641458dSRémi Denis-Courmont static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) 7759641458dSRémi Denis-Courmont { 776f7ae8d59SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk), *newpn; 7779641458dSRémi Denis-Courmont struct sock *newsk = NULL; 778f7ae8d59SRémi Denis-Courmont struct sk_buff *skb; 779f7ae8d59SRémi Denis-Courmont struct pnpipehdr *hdr; 780f7ae8d59SRémi Denis-Courmont struct sockaddr_pn dst, src; 7819641458dSRémi Denis-Courmont int err; 782f7ae8d59SRémi Denis-Courmont u16 peer_type; 783f7ae8d59SRémi Denis-Courmont u8 pipe_handle, enabled, n_sb; 784f7ae8d59SRémi Denis-Courmont u8 aligned = 0; 785f7ae8d59SRémi Denis-Courmont 786f7ae8d59SRémi Denis-Courmont skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp); 787f7ae8d59SRémi Denis-Courmont if (!skb) 788f7ae8d59SRémi Denis-Courmont return NULL; 7899641458dSRémi Denis-Courmont 7909641458dSRémi Denis-Courmont lock_sock(sk); 791f7ae8d59SRémi Denis-Courmont if (sk->sk_state != TCP_LISTEN) { 792f7ae8d59SRémi Denis-Courmont err = -EINVAL; 793f7ae8d59SRémi Denis-Courmont goto drop; 7949641458dSRémi Denis-Courmont } 7959641458dSRémi Denis-Courmont sk_acceptq_removed(sk); 7969641458dSRémi Denis-Courmont 797f7ae8d59SRémi Denis-Courmont err = -EPROTO; 798f7ae8d59SRémi Denis-Courmont if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) 799f7ae8d59SRémi Denis-Courmont goto drop; 800f7ae8d59SRémi Denis-Courmont 801f7ae8d59SRémi Denis-Courmont hdr = pnp_hdr(skb); 802f7ae8d59SRémi Denis-Courmont pipe_handle = hdr->pipe_handle; 803f7ae8d59SRémi Denis-Courmont switch (hdr->state_after_connect) { 804f7ae8d59SRémi Denis-Courmont case PN_PIPE_DISABLE: 805f7ae8d59SRémi Denis-Courmont enabled = 0; 806f7ae8d59SRémi Denis-Courmont break; 807f7ae8d59SRémi Denis-Courmont case PN_PIPE_ENABLE: 808f7ae8d59SRémi Denis-Courmont enabled = 1; 809f7ae8d59SRémi Denis-Courmont break; 810f7ae8d59SRémi Denis-Courmont default: 811f7ae8d59SRémi Denis-Courmont pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, 812f7ae8d59SRémi Denis-Courmont GFP_KERNEL); 813f7ae8d59SRémi Denis-Courmont goto drop; 814f7ae8d59SRémi Denis-Courmont } 815f7ae8d59SRémi Denis-Courmont peer_type = hdr->other_pep_type << 8; 816f7ae8d59SRémi Denis-Courmont 817f7ae8d59SRémi Denis-Courmont /* Parse sub-blocks (options) */ 818f7ae8d59SRémi Denis-Courmont n_sb = hdr->data[4]; 819f7ae8d59SRémi Denis-Courmont while (n_sb > 0) { 820f7ae8d59SRémi Denis-Courmont u8 type, buf[1], len = sizeof(buf); 821f7ae8d59SRémi Denis-Courmont const u8 *data = pep_get_sb(skb, &type, &len, buf); 822f7ae8d59SRémi Denis-Courmont 823f7ae8d59SRémi Denis-Courmont if (data == NULL) 824f7ae8d59SRémi Denis-Courmont goto drop; 825f7ae8d59SRémi Denis-Courmont switch (type) { 826f7ae8d59SRémi Denis-Courmont case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: 827f7ae8d59SRémi Denis-Courmont if (len < 1) 828f7ae8d59SRémi Denis-Courmont goto drop; 829f7ae8d59SRémi Denis-Courmont peer_type = (peer_type & 0xff00) | data[0]; 830f7ae8d59SRémi Denis-Courmont break; 831f7ae8d59SRémi Denis-Courmont case PN_PIPE_SB_ALIGNED_DATA: 832f7ae8d59SRémi Denis-Courmont aligned = data[0] != 0; 833f7ae8d59SRémi Denis-Courmont break; 834f7ae8d59SRémi Denis-Courmont } 835f7ae8d59SRémi Denis-Courmont n_sb--; 836f7ae8d59SRémi Denis-Courmont } 837f7ae8d59SRémi Denis-Courmont 838f7ae8d59SRémi Denis-Courmont /* Check for duplicate pipe handle */ 839f7ae8d59SRémi Denis-Courmont newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); 840f7ae8d59SRémi Denis-Courmont if (unlikely(newsk)) { 841f7ae8d59SRémi Denis-Courmont __sock_put(newsk); 842f7ae8d59SRémi Denis-Courmont newsk = NULL; 843f7ae8d59SRémi Denis-Courmont pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); 844f7ae8d59SRémi Denis-Courmont goto drop; 845f7ae8d59SRémi Denis-Courmont } 846f7ae8d59SRémi Denis-Courmont 847f7ae8d59SRémi Denis-Courmont /* Create a new to-be-accepted sock */ 84811aa9c28SEric W. Biederman newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot, 0); 849f7ae8d59SRémi Denis-Courmont if (!newsk) { 850f7ae8d59SRémi Denis-Courmont pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); 851f7ae8d59SRémi Denis-Courmont err = -ENOBUFS; 852f7ae8d59SRémi Denis-Courmont goto drop; 853f7ae8d59SRémi Denis-Courmont } 854f7ae8d59SRémi Denis-Courmont 855f7ae8d59SRémi Denis-Courmont sock_init_data(NULL, newsk); 856f7ae8d59SRémi Denis-Courmont newsk->sk_state = TCP_SYN_RECV; 857f7ae8d59SRémi Denis-Courmont newsk->sk_backlog_rcv = pipe_do_rcv; 858f7ae8d59SRémi Denis-Courmont newsk->sk_protocol = sk->sk_protocol; 859f7ae8d59SRémi Denis-Courmont newsk->sk_destruct = pipe_destruct; 860f7ae8d59SRémi Denis-Courmont 861f7ae8d59SRémi Denis-Courmont newpn = pep_sk(newsk); 862f7ae8d59SRémi Denis-Courmont pn_skb_get_dst_sockaddr(skb, &dst); 863f7ae8d59SRémi Denis-Courmont pn_skb_get_src_sockaddr(skb, &src); 864f7ae8d59SRémi Denis-Courmont newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); 865f7ae8d59SRémi Denis-Courmont newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); 866f7ae8d59SRémi Denis-Courmont newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); 867f7ae8d59SRémi Denis-Courmont sock_hold(sk); 868f7ae8d59SRémi Denis-Courmont newpn->listener = sk; 869f7ae8d59SRémi Denis-Courmont skb_queue_head_init(&newpn->ctrlreq_queue); 870f7ae8d59SRémi Denis-Courmont newpn->pipe_handle = pipe_handle; 871f7ae8d59SRémi Denis-Courmont atomic_set(&newpn->tx_credits, 0); 872f7ae8d59SRémi Denis-Courmont newpn->ifindex = 0; 873f7ae8d59SRémi Denis-Courmont newpn->peer_type = peer_type; 874f7ae8d59SRémi Denis-Courmont newpn->rx_credits = 0; 875f7ae8d59SRémi Denis-Courmont newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; 876f7ae8d59SRémi Denis-Courmont newpn->init_enable = enabled; 877f7ae8d59SRémi Denis-Courmont newpn->aligned = aligned; 878f7ae8d59SRémi Denis-Courmont 879f7ae8d59SRémi Denis-Courmont err = pep_accept_conn(newsk, skb); 880f7ae8d59SRémi Denis-Courmont if (err) { 881f7ae8d59SRémi Denis-Courmont sock_put(newsk); 882f7ae8d59SRémi Denis-Courmont newsk = NULL; 883f7ae8d59SRémi Denis-Courmont goto drop; 884f7ae8d59SRémi Denis-Courmont } 885f7ae8d59SRémi Denis-Courmont sk_add_node(newsk, &pn->hlist); 886f7ae8d59SRémi Denis-Courmont drop: 8879641458dSRémi Denis-Courmont release_sock(sk); 888f7ae8d59SRémi Denis-Courmont kfree_skb(skb); 8899641458dSRémi Denis-Courmont *errp = err; 8909641458dSRémi Denis-Courmont return newsk; 8919641458dSRémi Denis-Courmont } 8929641458dSRémi Denis-Courmont 893b3d62553SKumar Sanghvi static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) 894b3d62553SKumar Sanghvi { 895b3d62553SKumar Sanghvi struct pep_sock *pn = pep_sk(sk); 896297edb60SRémi Denis-Courmont int err; 89744c9ab16SRémi Denis-Courmont u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; 898b3d62553SKumar Sanghvi 899bdb6e697SDinesh Kumar Sharma if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE) 900acaf7df6SRémi Denis-Courmont pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ 901bdb6e697SDinesh Kumar Sharma 902297edb60SRémi Denis-Courmont err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, 903bdb6e697SDinesh Kumar Sharma pn->init_enable, data, 4); 904297edb60SRémi Denis-Courmont if (err) { 905297edb60SRémi Denis-Courmont pn->pipe_handle = PN_PIPE_INVALID_HANDLE; 906297edb60SRémi Denis-Courmont return err; 907b3d62553SKumar Sanghvi } 908bdb6e697SDinesh Kumar Sharma 909297edb60SRémi Denis-Courmont sk->sk_state = TCP_SYN_SENT; 910bdb6e697SDinesh Kumar Sharma 911bdb6e697SDinesh Kumar Sharma return 0; 912bdb6e697SDinesh Kumar Sharma } 913bdb6e697SDinesh Kumar Sharma 914bdb6e697SDinesh Kumar Sharma static int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len) 915bdb6e697SDinesh Kumar Sharma { 916bdb6e697SDinesh Kumar Sharma int err; 917bdb6e697SDinesh Kumar Sharma 918bdb6e697SDinesh Kumar Sharma err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD, 919bdb6e697SDinesh Kumar Sharma NULL, 0); 920bdb6e697SDinesh Kumar Sharma if (err) 921bdb6e697SDinesh Kumar Sharma return err; 922bdb6e697SDinesh Kumar Sharma 923bdb6e697SDinesh Kumar Sharma sk->sk_state = TCP_SYN_SENT; 924bdb6e697SDinesh Kumar Sharma 925297edb60SRémi Denis-Courmont return 0; 926297edb60SRémi Denis-Courmont } 927b3d62553SKumar Sanghvi 9289641458dSRémi Denis-Courmont static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) 9299641458dSRémi Denis-Courmont { 930c41bd97fSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 9319641458dSRémi Denis-Courmont int answ; 932bdb6e697SDinesh Kumar Sharma int ret = -ENOIOCTLCMD; 9339641458dSRémi Denis-Courmont 9349641458dSRémi Denis-Courmont switch (cmd) { 9359641458dSRémi Denis-Courmont case SIOCINQ: 936bdb6e697SDinesh Kumar Sharma if (sk->sk_state == TCP_LISTEN) { 937bdb6e697SDinesh Kumar Sharma ret = -EINVAL; 938bdb6e697SDinesh Kumar Sharma break; 939bdb6e697SDinesh Kumar Sharma } 9409641458dSRémi Denis-Courmont 9419641458dSRémi Denis-Courmont lock_sock(sk); 942f64f9e71SJoe Perches if (sock_flag(sk, SOCK_URGINLINE) && 943f64f9e71SJoe Perches !skb_queue_empty(&pn->ctrlreq_queue)) 944c41bd97fSRémi Denis-Courmont answ = skb_peek(&pn->ctrlreq_queue)->len; 945c41bd97fSRémi Denis-Courmont else if (!skb_queue_empty(&sk->sk_receive_queue)) 9469641458dSRémi Denis-Courmont answ = skb_peek(&sk->sk_receive_queue)->len; 9479641458dSRémi Denis-Courmont else 9489641458dSRémi Denis-Courmont answ = 0; 9499641458dSRémi Denis-Courmont release_sock(sk); 950bdb6e697SDinesh Kumar Sharma ret = put_user(answ, (int __user *)arg); 951bdb6e697SDinesh Kumar Sharma break; 952bdb6e697SDinesh Kumar Sharma 953bdb6e697SDinesh Kumar Sharma case SIOCPNENABLEPIPE: 954bdb6e697SDinesh Kumar Sharma lock_sock(sk); 955bdb6e697SDinesh Kumar Sharma if (sk->sk_state == TCP_SYN_SENT) 956bdb6e697SDinesh Kumar Sharma ret = -EBUSY; 957bdb6e697SDinesh Kumar Sharma else if (sk->sk_state == TCP_ESTABLISHED) 958bdb6e697SDinesh Kumar Sharma ret = -EISCONN; 959bdb6e697SDinesh Kumar Sharma else 960bdb6e697SDinesh Kumar Sharma ret = pep_sock_enable(sk, NULL, 0); 961bdb6e697SDinesh Kumar Sharma release_sock(sk); 962bdb6e697SDinesh Kumar Sharma break; 9639641458dSRémi Denis-Courmont } 9649641458dSRémi Denis-Courmont 965bdb6e697SDinesh Kumar Sharma return ret; 9669641458dSRémi Denis-Courmont } 9679641458dSRémi Denis-Courmont 9689641458dSRémi Denis-Courmont static int pep_init(struct sock *sk) 9699641458dSRémi Denis-Courmont { 9709641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 9719641458dSRémi Denis-Courmont 972f7ae8d59SRémi Denis-Courmont sk->sk_destruct = pipe_destruct; 9739641458dSRémi Denis-Courmont INIT_HLIST_HEAD(&pn->hlist); 974297edb60SRémi Denis-Courmont pn->listener = NULL; 975c41bd97fSRémi Denis-Courmont skb_queue_head_init(&pn->ctrlreq_queue); 976297edb60SRémi Denis-Courmont atomic_set(&pn->tx_credits, 0); 977297edb60SRémi Denis-Courmont pn->ifindex = 0; 978297edb60SRémi Denis-Courmont pn->peer_type = 0; 9799641458dSRémi Denis-Courmont pn->pipe_handle = PN_PIPE_INVALID_HANDLE; 980297edb60SRémi Denis-Courmont pn->rx_credits = 0; 981297edb60SRémi Denis-Courmont pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; 982297edb60SRémi Denis-Courmont pn->init_enable = 1; 983297edb60SRémi Denis-Courmont pn->aligned = 0; 9849641458dSRémi Denis-Courmont return 0; 9859641458dSRémi Denis-Courmont } 9869641458dSRémi Denis-Courmont 98702a47617SRémi Denis-Courmont static int pep_setsockopt(struct sock *sk, int level, int optname, 988b7058842SDavid S. Miller char __user *optval, unsigned int optlen) 98902a47617SRémi Denis-Courmont { 99002a47617SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 99102a47617SRémi Denis-Courmont int val = 0, err = 0; 99202a47617SRémi Denis-Courmont 99302a47617SRémi Denis-Courmont if (level != SOL_PNPIPE) 99402a47617SRémi Denis-Courmont return -ENOPROTOOPT; 99502a47617SRémi Denis-Courmont if (optlen >= sizeof(int)) { 99602a47617SRémi Denis-Courmont if (get_user(val, (int __user *) optval)) 99702a47617SRémi Denis-Courmont return -EFAULT; 99802a47617SRémi Denis-Courmont } 99902a47617SRémi Denis-Courmont 100002a47617SRémi Denis-Courmont lock_sock(sk); 100102a47617SRémi Denis-Courmont switch (optname) { 100202a47617SRémi Denis-Courmont case PNPIPE_ENCAP: 100302a47617SRémi Denis-Courmont if (val && val != PNPIPE_ENCAP_IP) { 100402a47617SRémi Denis-Courmont err = -EINVAL; 100502a47617SRémi Denis-Courmont break; 100602a47617SRémi Denis-Courmont } 100702a47617SRémi Denis-Courmont if (!pn->ifindex == !val) 100802a47617SRémi Denis-Courmont break; /* Nothing to do! */ 100902a47617SRémi Denis-Courmont if (!capable(CAP_NET_ADMIN)) { 101002a47617SRémi Denis-Courmont err = -EPERM; 101102a47617SRémi Denis-Courmont break; 101202a47617SRémi Denis-Courmont } 101302a47617SRémi Denis-Courmont if (val) { 101402a47617SRémi Denis-Courmont release_sock(sk); 101502a47617SRémi Denis-Courmont err = gprs_attach(sk); 101602a47617SRémi Denis-Courmont if (err > 0) { 101702a47617SRémi Denis-Courmont pn->ifindex = err; 101802a47617SRémi Denis-Courmont err = 0; 101902a47617SRémi Denis-Courmont } 102002a47617SRémi Denis-Courmont } else { 102102a47617SRémi Denis-Courmont pn->ifindex = 0; 102202a47617SRémi Denis-Courmont release_sock(sk); 102302a47617SRémi Denis-Courmont gprs_detach(sk); 102402a47617SRémi Denis-Courmont err = 0; 102502a47617SRémi Denis-Courmont } 102602a47617SRémi Denis-Courmont goto out_norel; 102703789f26SRémi Denis-Courmont 1028bdb6e697SDinesh Kumar Sharma case PNPIPE_HANDLE: 1029bdb6e697SDinesh Kumar Sharma if ((sk->sk_state == TCP_CLOSE) && 1030bdb6e697SDinesh Kumar Sharma (val >= 0) && (val < PN_PIPE_INVALID_HANDLE)) 1031bdb6e697SDinesh Kumar Sharma pn->pipe_handle = val; 1032bdb6e697SDinesh Kumar Sharma else 1033bdb6e697SDinesh Kumar Sharma err = -EINVAL; 1034bdb6e697SDinesh Kumar Sharma break; 1035bdb6e697SDinesh Kumar Sharma 1036bdb6e697SDinesh Kumar Sharma case PNPIPE_INITSTATE: 1037bdb6e697SDinesh Kumar Sharma pn->init_enable = !!val; 1038bdb6e697SDinesh Kumar Sharma break; 1039bdb6e697SDinesh Kumar Sharma 104002a47617SRémi Denis-Courmont default: 104102a47617SRémi Denis-Courmont err = -ENOPROTOOPT; 104202a47617SRémi Denis-Courmont } 104302a47617SRémi Denis-Courmont release_sock(sk); 104402a47617SRémi Denis-Courmont 104502a47617SRémi Denis-Courmont out_norel: 104602a47617SRémi Denis-Courmont return err; 104702a47617SRémi Denis-Courmont } 104802a47617SRémi Denis-Courmont 104902a47617SRémi Denis-Courmont static int pep_getsockopt(struct sock *sk, int level, int optname, 105002a47617SRémi Denis-Courmont char __user *optval, int __user *optlen) 105102a47617SRémi Denis-Courmont { 105202a47617SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 105302a47617SRémi Denis-Courmont int len, val; 105402a47617SRémi Denis-Courmont 105502a47617SRémi Denis-Courmont if (level != SOL_PNPIPE) 105602a47617SRémi Denis-Courmont return -ENOPROTOOPT; 105702a47617SRémi Denis-Courmont if (get_user(len, optlen)) 105802a47617SRémi Denis-Courmont return -EFAULT; 105902a47617SRémi Denis-Courmont 106002a47617SRémi Denis-Courmont switch (optname) { 106102a47617SRémi Denis-Courmont case PNPIPE_ENCAP: 106202a47617SRémi Denis-Courmont val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; 106302a47617SRémi Denis-Courmont break; 10648d98efa8SKumar Sanghvi 106502a47617SRémi Denis-Courmont case PNPIPE_IFINDEX: 106602a47617SRémi Denis-Courmont val = pn->ifindex; 106702a47617SRémi Denis-Courmont break; 106803789f26SRémi Denis-Courmont 1069acaf7df6SRémi Denis-Courmont case PNPIPE_HANDLE: 1070acaf7df6SRémi Denis-Courmont val = pn->pipe_handle; 1071acaf7df6SRémi Denis-Courmont if (val == PN_PIPE_INVALID_HANDLE) 1072acaf7df6SRémi Denis-Courmont return -EINVAL; 1073acaf7df6SRémi Denis-Courmont break; 1074acaf7df6SRémi Denis-Courmont 1075bdb6e697SDinesh Kumar Sharma case PNPIPE_INITSTATE: 1076bdb6e697SDinesh Kumar Sharma val = pn->init_enable; 1077bdb6e697SDinesh Kumar Sharma break; 1078bdb6e697SDinesh Kumar Sharma 107902a47617SRémi Denis-Courmont default: 108002a47617SRémi Denis-Courmont return -ENOPROTOOPT; 108102a47617SRémi Denis-Courmont } 108202a47617SRémi Denis-Courmont 108302a47617SRémi Denis-Courmont len = min_t(unsigned int, sizeof(int), len); 108402a47617SRémi Denis-Courmont if (put_user(len, optlen)) 108502a47617SRémi Denis-Courmont return -EFAULT; 108602a47617SRémi Denis-Courmont if (put_user(val, (int __user *) optval)) 108702a47617SRémi Denis-Courmont return -EFAULT; 108802a47617SRémi Denis-Courmont return 0; 108902a47617SRémi Denis-Courmont } 109002a47617SRémi Denis-Courmont 109102a47617SRémi Denis-Courmont static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) 109202a47617SRémi Denis-Courmont { 109302a47617SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 109402a47617SRémi Denis-Courmont struct pnpipehdr *ph; 1095e1a5964fSRémi Denis-Courmont int err; 109602a47617SRémi Denis-Courmont 1097be677730SRémi Denis-Courmont if (pn_flow_safe(pn->tx_fc) && 1098be677730SRémi Denis-Courmont !atomic_add_unless(&pn->tx_credits, -1, 0)) { 1099be677730SRémi Denis-Courmont kfree_skb(skb); 1100be677730SRémi Denis-Courmont return -ENOBUFS; 1101be677730SRémi Denis-Courmont } 1102be677730SRémi Denis-Courmont 1103fea93eceSRémi Denis-Courmont skb_push(skb, 3 + pn->aligned); 110402a47617SRémi Denis-Courmont skb_reset_transport_header(skb); 110502a47617SRémi Denis-Courmont ph = pnp_hdr(skb); 110602a47617SRémi Denis-Courmont ph->utid = 0; 1107fea93eceSRémi Denis-Courmont if (pn->aligned) { 1108fea93eceSRémi Denis-Courmont ph->message_id = PNS_PIPE_ALIGNED_DATA; 1109fea93eceSRémi Denis-Courmont ph->data[0] = 0; /* padding */ 1110fea93eceSRémi Denis-Courmont } else 111102a47617SRémi Denis-Courmont ph->message_id = PNS_PIPE_DATA; 111202a47617SRémi Denis-Courmont ph->pipe_handle = pn->pipe_handle; 111314ba8faeSRémi Denis-Courmont err = pn_skb_send(sk, skb, NULL); 1114e1a5964fSRémi Denis-Courmont 1115e1a5964fSRémi Denis-Courmont if (err && pn_flow_safe(pn->tx_fc)) 1116e1a5964fSRémi Denis-Courmont atomic_inc(&pn->tx_credits); 1117e1a5964fSRémi Denis-Courmont return err; 1118e1a5964fSRémi Denis-Courmont 111902a47617SRémi Denis-Courmont } 112002a47617SRémi Denis-Courmont 11211b784140SYing Xue static int pep_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 11229641458dSRémi Denis-Courmont { 11239641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 1124b1704374SRémi Denis-Courmont struct sk_buff *skb; 11259641458dSRémi Denis-Courmont long timeo; 11269641458dSRémi Denis-Courmont int flags = msg->msg_flags; 11279641458dSRémi Denis-Courmont int err, done; 11289641458dSRémi Denis-Courmont 1129bcf1b70aSSasha Levin if (len > USHRT_MAX) 1130bcf1b70aSSasha Levin return -EMSGSIZE; 1131bcf1b70aSSasha Levin 113282ecbcb9SRémi Denis-Courmont if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL| 113382ecbcb9SRémi Denis-Courmont MSG_CMSG_COMPAT)) || 113482ecbcb9SRémi Denis-Courmont !(msg->msg_flags & MSG_EOR)) 11359641458dSRémi Denis-Courmont return -EOPNOTSUPP; 11369641458dSRémi Denis-Courmont 1137b1704374SRémi Denis-Courmont skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, 1138b1704374SRémi Denis-Courmont flags & MSG_DONTWAIT, &err); 1139b1704374SRémi Denis-Courmont if (!skb) 114002ac3268SRémi Denis-Courmont return err; 1141b1704374SRémi Denis-Courmont 1142638be344SRémi Denis-Courmont skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned); 11436ce8e9ceSAl Viro err = memcpy_from_msg(skb_put(skb, len), msg, len); 1144b1704374SRémi Denis-Courmont if (err < 0) 1145b1704374SRémi Denis-Courmont goto outfree; 1146b1704374SRémi Denis-Courmont 11479641458dSRémi Denis-Courmont lock_sock(sk); 11489641458dSRémi Denis-Courmont timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); 11499641458dSRémi Denis-Courmont if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) { 11509641458dSRémi Denis-Courmont err = -ENOTCONN; 11519641458dSRémi Denis-Courmont goto out; 11529641458dSRémi Denis-Courmont } 11539641458dSRémi Denis-Courmont if (sk->sk_state != TCP_ESTABLISHED) { 11549641458dSRémi Denis-Courmont /* Wait until the pipe gets to enabled state */ 11559641458dSRémi Denis-Courmont disabled: 11569641458dSRémi Denis-Courmont err = sk_stream_wait_connect(sk, &timeo); 11579641458dSRémi Denis-Courmont if (err) 11589641458dSRémi Denis-Courmont goto out; 11599641458dSRémi Denis-Courmont 11609641458dSRémi Denis-Courmont if (sk->sk_state == TCP_CLOSE_WAIT) { 11619641458dSRémi Denis-Courmont err = -ECONNRESET; 11629641458dSRémi Denis-Courmont goto out; 11639641458dSRémi Denis-Courmont } 11649641458dSRémi Denis-Courmont } 11659641458dSRémi Denis-Courmont BUG_ON(sk->sk_state != TCP_ESTABLISHED); 11669641458dSRémi Denis-Courmont 11679641458dSRémi Denis-Courmont /* Wait until flow control allows TX */ 1168be677730SRémi Denis-Courmont done = atomic_read(&pn->tx_credits); 11699641458dSRémi Denis-Courmont while (!done) { 11709641458dSRémi Denis-Courmont DEFINE_WAIT(wait); 11719641458dSRémi Denis-Courmont 11729641458dSRémi Denis-Courmont if (!timeo) { 11739641458dSRémi Denis-Courmont err = -EAGAIN; 11749641458dSRémi Denis-Courmont goto out; 11759641458dSRémi Denis-Courmont } 11769641458dSRémi Denis-Courmont if (signal_pending(current)) { 11779641458dSRémi Denis-Courmont err = sock_intr_errno(timeo); 11789641458dSRémi Denis-Courmont goto out; 11799641458dSRémi Denis-Courmont } 11809641458dSRémi Denis-Courmont 118143815482SEric Dumazet prepare_to_wait(sk_sleep(sk), &wait, 11829641458dSRémi Denis-Courmont TASK_INTERRUPTIBLE); 1183be677730SRémi Denis-Courmont done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits)); 118443815482SEric Dumazet finish_wait(sk_sleep(sk), &wait); 11859641458dSRémi Denis-Courmont 11869641458dSRémi Denis-Courmont if (sk->sk_state != TCP_ESTABLISHED) 11879641458dSRémi Denis-Courmont goto disabled; 11889641458dSRémi Denis-Courmont } 11899641458dSRémi Denis-Courmont 119002a47617SRémi Denis-Courmont err = pipe_skb_send(sk, skb); 11919641458dSRémi Denis-Courmont if (err >= 0) 11929641458dSRémi Denis-Courmont err = len; /* success! */ 11939641458dSRémi Denis-Courmont skb = NULL; 11949641458dSRémi Denis-Courmont out: 11959641458dSRémi Denis-Courmont release_sock(sk); 1196b1704374SRémi Denis-Courmont outfree: 11979641458dSRémi Denis-Courmont kfree_skb(skb); 11989641458dSRémi Denis-Courmont return err; 11999641458dSRémi Denis-Courmont } 12009641458dSRémi Denis-Courmont 120102a47617SRémi Denis-Courmont int pep_writeable(struct sock *sk) 120202a47617SRémi Denis-Courmont { 120302a47617SRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 120402a47617SRémi Denis-Courmont 1205be677730SRémi Denis-Courmont return atomic_read(&pn->tx_credits); 120602a47617SRémi Denis-Courmont } 120702a47617SRémi Denis-Courmont 120802a47617SRémi Denis-Courmont int pep_write(struct sock *sk, struct sk_buff *skb) 120902a47617SRémi Denis-Courmont { 121002a47617SRémi Denis-Courmont struct sk_buff *rskb, *fs; 121102a47617SRémi Denis-Courmont int flen = 0; 121202a47617SRémi Denis-Courmont 1213fea93eceSRémi Denis-Courmont if (pep_sk(sk)->aligned) 1214fea93eceSRémi Denis-Courmont return pipe_skb_send(sk, skb); 1215fea93eceSRémi Denis-Courmont 121602a47617SRémi Denis-Courmont rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); 121702a47617SRémi Denis-Courmont if (!rskb) { 121802a47617SRémi Denis-Courmont kfree_skb(skb); 121902a47617SRémi Denis-Courmont return -ENOMEM; 122002a47617SRémi Denis-Courmont } 122102a47617SRémi Denis-Courmont skb_shinfo(rskb)->frag_list = skb; 122202a47617SRémi Denis-Courmont rskb->len += skb->len; 122302a47617SRémi Denis-Courmont rskb->data_len += rskb->len; 122402a47617SRémi Denis-Courmont rskb->truesize += rskb->len; 122502a47617SRémi Denis-Courmont 122602a47617SRémi Denis-Courmont /* Avoid nested fragments */ 12275c313e9aSDavid S. Miller skb_walk_frags(skb, fs) 122802a47617SRémi Denis-Courmont flen += fs->len; 122902a47617SRémi Denis-Courmont skb->next = skb_shinfo(skb)->frag_list; 12305c313e9aSDavid S. Miller skb_frag_list_init(skb); 123102a47617SRémi Denis-Courmont skb->len -= flen; 123202a47617SRémi Denis-Courmont skb->data_len -= flen; 123302a47617SRémi Denis-Courmont skb->truesize -= flen; 123402a47617SRémi Denis-Courmont 123502a47617SRémi Denis-Courmont skb_reserve(rskb, MAX_PHONET_HEADER + 3); 123602a47617SRémi Denis-Courmont return pipe_skb_send(sk, rskb); 123702a47617SRémi Denis-Courmont } 123802a47617SRémi Denis-Courmont 123902a47617SRémi Denis-Courmont struct sk_buff *pep_read(struct sock *sk) 124002a47617SRémi Denis-Courmont { 124102a47617SRémi Denis-Courmont struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); 124202a47617SRémi Denis-Courmont 124302a47617SRémi Denis-Courmont if (sk->sk_state == TCP_ESTABLISHED) 124444c9ab16SRémi Denis-Courmont pipe_grant_credits(sk, GFP_ATOMIC); 124502a47617SRémi Denis-Courmont return skb; 124602a47617SRémi Denis-Courmont } 124702a47617SRémi Denis-Courmont 12481b784140SYing Xue static int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, 12491b784140SYing Xue int noblock, int flags, int *addr_len) 12509641458dSRémi Denis-Courmont { 12519641458dSRémi Denis-Courmont struct sk_buff *skb; 12529641458dSRémi Denis-Courmont int err; 12539641458dSRémi Denis-Courmont 125482ecbcb9SRémi Denis-Courmont if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL| 125582ecbcb9SRémi Denis-Courmont MSG_NOSIGNAL|MSG_CMSG_COMPAT)) 125682ecbcb9SRémi Denis-Courmont return -EOPNOTSUPP; 125782ecbcb9SRémi Denis-Courmont 12589641458dSRémi Denis-Courmont if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE))) 12599641458dSRémi Denis-Courmont return -ENOTCONN; 12609641458dSRémi Denis-Courmont 1261c41bd97fSRémi Denis-Courmont if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) { 1262c41bd97fSRémi Denis-Courmont /* Dequeue and acknowledge control request */ 1263c41bd97fSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 1264c41bd97fSRémi Denis-Courmont 126582ecbcb9SRémi Denis-Courmont if (flags & MSG_PEEK) 126682ecbcb9SRémi Denis-Courmont return -EOPNOTSUPP; 1267c41bd97fSRémi Denis-Courmont skb = skb_dequeue(&pn->ctrlreq_queue); 1268c41bd97fSRémi Denis-Courmont if (skb) { 1269c41bd97fSRémi Denis-Courmont pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR, 1270c41bd97fSRémi Denis-Courmont GFP_KERNEL); 1271c41bd97fSRémi Denis-Courmont msg->msg_flags |= MSG_OOB; 1272c41bd97fSRémi Denis-Courmont goto copy; 1273c41bd97fSRémi Denis-Courmont } 1274c41bd97fSRémi Denis-Courmont if (flags & MSG_OOB) 1275c41bd97fSRémi Denis-Courmont return -EINVAL; 1276c41bd97fSRémi Denis-Courmont } 1277c41bd97fSRémi Denis-Courmont 12789641458dSRémi Denis-Courmont skb = skb_recv_datagram(sk, flags, noblock, &err); 12799641458dSRémi Denis-Courmont lock_sock(sk); 12809641458dSRémi Denis-Courmont if (skb == NULL) { 12819641458dSRémi Denis-Courmont if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT) 12829641458dSRémi Denis-Courmont err = -ECONNRESET; 12839641458dSRémi Denis-Courmont release_sock(sk); 12849641458dSRémi Denis-Courmont return err; 12859641458dSRémi Denis-Courmont } 12869641458dSRémi Denis-Courmont 12879641458dSRémi Denis-Courmont if (sk->sk_state == TCP_ESTABLISHED) 128844c9ab16SRémi Denis-Courmont pipe_grant_credits(sk, GFP_KERNEL); 12899641458dSRémi Denis-Courmont release_sock(sk); 1290c41bd97fSRémi Denis-Courmont copy: 12919641458dSRémi Denis-Courmont msg->msg_flags |= MSG_EOR; 12929641458dSRémi Denis-Courmont if (skb->len > len) 12939641458dSRémi Denis-Courmont msg->msg_flags |= MSG_TRUNC; 12949641458dSRémi Denis-Courmont else 12959641458dSRémi Denis-Courmont len = skb->len; 12969641458dSRémi Denis-Courmont 129751f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, len); 12989641458dSRémi Denis-Courmont if (!err) 12999641458dSRémi Denis-Courmont err = (flags & MSG_TRUNC) ? skb->len : len; 13009641458dSRémi Denis-Courmont 13019641458dSRémi Denis-Courmont skb_free_datagram(sk, skb); 13029641458dSRémi Denis-Courmont return err; 13039641458dSRémi Denis-Courmont } 13049641458dSRémi Denis-Courmont 13059641458dSRémi Denis-Courmont static void pep_sock_unhash(struct sock *sk) 13069641458dSRémi Denis-Courmont { 13079641458dSRémi Denis-Courmont struct pep_sock *pn = pep_sk(sk); 13089641458dSRémi Denis-Courmont struct sock *skparent = NULL; 13099641458dSRémi Denis-Courmont 13109641458dSRémi Denis-Courmont lock_sock(sk); 1311b3d62553SKumar Sanghvi 1312297edb60SRémi Denis-Courmont if (pn->listener != NULL) { 13139641458dSRémi Denis-Courmont skparent = pn->listener; 1314297edb60SRémi Denis-Courmont pn->listener = NULL; 13159641458dSRémi Denis-Courmont release_sock(sk); 13169641458dSRémi Denis-Courmont 13179641458dSRémi Denis-Courmont pn = pep_sk(skparent); 13187dfde179SRémi Denis-Courmont lock_sock(skparent); 13197dfde179SRémi Denis-Courmont sk_del_node_init(sk); 13207dfde179SRémi Denis-Courmont sk = skparent; 13219641458dSRémi Denis-Courmont } 1322297edb60SRémi Denis-Courmont 13239641458dSRémi Denis-Courmont /* Unhash a listening sock only when it is closed 13249641458dSRémi Denis-Courmont * and all of its active connected pipes are closed. */ 13259641458dSRémi Denis-Courmont if (hlist_empty(&pn->hlist)) 13269641458dSRémi Denis-Courmont pn_sock_unhash(&pn->pn_sk.sk); 13279641458dSRémi Denis-Courmont release_sock(sk); 13289641458dSRémi Denis-Courmont 13299641458dSRémi Denis-Courmont if (skparent) 13309641458dSRémi Denis-Courmont sock_put(skparent); 13319641458dSRémi Denis-Courmont } 13329641458dSRémi Denis-Courmont 13339641458dSRémi Denis-Courmont static struct proto pep_proto = { 13349641458dSRémi Denis-Courmont .close = pep_sock_close, 13359641458dSRémi Denis-Courmont .accept = pep_sock_accept, 1336b3d62553SKumar Sanghvi .connect = pep_sock_connect, 13379641458dSRémi Denis-Courmont .ioctl = pep_ioctl, 13389641458dSRémi Denis-Courmont .init = pep_init, 133902a47617SRémi Denis-Courmont .setsockopt = pep_setsockopt, 134002a47617SRémi Denis-Courmont .getsockopt = pep_getsockopt, 13419641458dSRémi Denis-Courmont .sendmsg = pep_sendmsg, 13429641458dSRémi Denis-Courmont .recvmsg = pep_recvmsg, 13439641458dSRémi Denis-Courmont .backlog_rcv = pep_do_rcv, 13449641458dSRémi Denis-Courmont .hash = pn_sock_hash, 13459641458dSRémi Denis-Courmont .unhash = pep_sock_unhash, 13469641458dSRémi Denis-Courmont .get_port = pn_sock_get_port, 13479641458dSRémi Denis-Courmont .obj_size = sizeof(struct pep_sock), 13489641458dSRémi Denis-Courmont .owner = THIS_MODULE, 13499641458dSRémi Denis-Courmont .name = "PNPIPE", 13509641458dSRémi Denis-Courmont }; 13519641458dSRémi Denis-Courmont 13529641458dSRémi Denis-Courmont static struct phonet_protocol pep_pn_proto = { 13539641458dSRémi Denis-Courmont .ops = &phonet_stream_ops, 13549641458dSRémi Denis-Courmont .prot = &pep_proto, 13559641458dSRémi Denis-Courmont .sock_type = SOCK_SEQPACKET, 13569641458dSRémi Denis-Courmont }; 13579641458dSRémi Denis-Courmont 13589641458dSRémi Denis-Courmont static int __init pep_register(void) 13599641458dSRémi Denis-Courmont { 13609641458dSRémi Denis-Courmont return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); 13619641458dSRémi Denis-Courmont } 13629641458dSRémi Denis-Courmont 13639641458dSRémi Denis-Courmont static void __exit pep_unregister(void) 13649641458dSRémi Denis-Courmont { 13659641458dSRémi Denis-Courmont phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto); 13669641458dSRémi Denis-Courmont } 13679641458dSRémi Denis-Courmont 13689641458dSRémi Denis-Courmont module_init(pep_register); 13699641458dSRémi Denis-Courmont module_exit(pep_unregister); 13709641458dSRémi Denis-Courmont MODULE_AUTHOR("Remi Denis-Courmont, Nokia"); 13719641458dSRémi Denis-Courmont MODULE_DESCRIPTION("Phonet pipe protocol"); 13729641458dSRémi Denis-Courmont MODULE_LICENSE("GPL"); 13739641458dSRémi Denis-Courmont MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE); 1374