162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * File: pep.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Phonet pipe protocol end point socket 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author: Rémi Denis-Courmont 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/socket.h> 1662306a36Sopenharmony_ci#include <net/sock.h> 1762306a36Sopenharmony_ci#include <net/tcp_states.h> 1862306a36Sopenharmony_ci#include <asm/ioctls.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/phonet.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <net/phonet/phonet.h> 2362306a36Sopenharmony_ci#include <net/phonet/pep.h> 2462306a36Sopenharmony_ci#include <net/phonet/gprs.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* sk_state values: 2762306a36Sopenharmony_ci * TCP_CLOSE sock not in use yet 2862306a36Sopenharmony_ci * TCP_CLOSE_WAIT disconnected pipe 2962306a36Sopenharmony_ci * TCP_LISTEN listening pipe endpoint 3062306a36Sopenharmony_ci * TCP_SYN_RECV connected pipe in disabled state 3162306a36Sopenharmony_ci * TCP_ESTABLISHED connected pipe in enabled state 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * pep_sock locking: 3462306a36Sopenharmony_ci * - sk_state, hlist: sock lock needed 3562306a36Sopenharmony_ci * - listener: read only 3662306a36Sopenharmony_ci * - pipe_handle: read only 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CREDITS_MAX 10 4062306a36Sopenharmony_ci#define CREDITS_THR 7 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Get the next TLV sub-block. */ 4562306a36Sopenharmony_cistatic unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, 4662306a36Sopenharmony_ci void *buf) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci void *data = NULL; 4962306a36Sopenharmony_ci struct { 5062306a36Sopenharmony_ci u8 sb_type; 5162306a36Sopenharmony_ci u8 sb_len; 5262306a36Sopenharmony_ci } *ph, h; 5362306a36Sopenharmony_ci int buflen = *plen; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ph = skb_header_pointer(skb, 0, 2, &h); 5662306a36Sopenharmony_ci if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) 5762306a36Sopenharmony_ci return NULL; 5862306a36Sopenharmony_ci ph->sb_len -= 2; 5962306a36Sopenharmony_ci *ptype = ph->sb_type; 6062306a36Sopenharmony_ci *plen = ph->sb_len; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (buflen > ph->sb_len) 6362306a36Sopenharmony_ci buflen = ph->sb_len; 6462306a36Sopenharmony_ci data = skb_header_pointer(skb, 2, buflen, buf); 6562306a36Sopenharmony_ci __skb_pull(skb, 2 + ph->sb_len); 6662306a36Sopenharmony_ci return data; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload, 7062306a36Sopenharmony_ci int len, gfp_t priority) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); 7362306a36Sopenharmony_ci if (!skb) 7462306a36Sopenharmony_ci return NULL; 7562306a36Sopenharmony_ci skb_set_owner_w(skb, sk); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci skb_reserve(skb, MAX_PNPIPE_HEADER); 7862306a36Sopenharmony_ci __skb_put(skb, len); 7962306a36Sopenharmony_ci skb_copy_to_linear_data(skb, payload, len); 8062306a36Sopenharmony_ci __skb_push(skb, sizeof(struct pnpipehdr)); 8162306a36Sopenharmony_ci skb_reset_transport_header(skb); 8262306a36Sopenharmony_ci return skb; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code, 8662306a36Sopenharmony_ci const void *data, int len, gfp_t priority) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci const struct pnpipehdr *oph = pnp_hdr(oskb); 8962306a36Sopenharmony_ci struct pnpipehdr *ph; 9062306a36Sopenharmony_ci struct sk_buff *skb; 9162306a36Sopenharmony_ci struct sockaddr_pn peer; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci skb = pep_alloc_skb(sk, data, len, priority); 9462306a36Sopenharmony_ci if (!skb) 9562306a36Sopenharmony_ci return -ENOMEM; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ph = pnp_hdr(skb); 9862306a36Sopenharmony_ci ph->utid = oph->utid; 9962306a36Sopenharmony_ci ph->message_id = oph->message_id + 1; /* REQ -> RESP */ 10062306a36Sopenharmony_ci ph->pipe_handle = oph->pipe_handle; 10162306a36Sopenharmony_ci ph->error_code = code; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pn_skb_get_src_sockaddr(oskb, &peer); 10462306a36Sopenharmony_ci return pn_skb_send(sk, skb, &peer); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int pep_indicate(struct sock *sk, u8 id, u8 code, 10862306a36Sopenharmony_ci const void *data, int len, gfp_t priority) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 11162306a36Sopenharmony_ci struct pnpipehdr *ph; 11262306a36Sopenharmony_ci struct sk_buff *skb; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci skb = pep_alloc_skb(sk, data, len, priority); 11562306a36Sopenharmony_ci if (!skb) 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ph = pnp_hdr(skb); 11962306a36Sopenharmony_ci ph->utid = 0; 12062306a36Sopenharmony_ci ph->message_id = id; 12162306a36Sopenharmony_ci ph->pipe_handle = pn->pipe_handle; 12262306a36Sopenharmony_ci ph->error_code = code; 12362306a36Sopenharmony_ci return pn_skb_send(sk, skb, NULL); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define PAD 0x00 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int pipe_handler_request(struct sock *sk, u8 id, u8 code, 12962306a36Sopenharmony_ci const void *data, int len) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 13262306a36Sopenharmony_ci struct pnpipehdr *ph; 13362306a36Sopenharmony_ci struct sk_buff *skb; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci skb = pep_alloc_skb(sk, data, len, GFP_KERNEL); 13662306a36Sopenharmony_ci if (!skb) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ph = pnp_hdr(skb); 14062306a36Sopenharmony_ci ph->utid = id; /* whatever */ 14162306a36Sopenharmony_ci ph->message_id = id; 14262306a36Sopenharmony_ci ph->pipe_handle = pn->pipe_handle; 14362306a36Sopenharmony_ci ph->error_code = code; 14462306a36Sopenharmony_ci return pn_skb_send(sk, skb, NULL); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int pipe_handler_send_created_ind(struct sock *sk) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 15062306a36Sopenharmony_ci u8 data[4] = { 15162306a36Sopenharmony_ci PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2), 15262306a36Sopenharmony_ci pn->tx_fc, pn->rx_fc, 15362306a36Sopenharmony_ci }; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */, 15662306a36Sopenharmony_ci data, 4, GFP_ATOMIC); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int pep_accept_conn(struct sock *sk, struct sk_buff *skb) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci static const u8 data[20] = { 16262306a36Sopenharmony_ci PAD, PAD, PAD, 2 /* sub-blocks */, 16362306a36Sopenharmony_ci PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD, 16462306a36Sopenharmony_ci PN_MULTI_CREDIT_FLOW_CONTROL, 16562306a36Sopenharmony_ci PN_ONE_CREDIT_FLOW_CONTROL, 16662306a36Sopenharmony_ci PN_LEGACY_FLOW_CONTROL, 16762306a36Sopenharmony_ci PAD, 16862306a36Sopenharmony_ci PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD, 16962306a36Sopenharmony_ci PN_MULTI_CREDIT_FLOW_CONTROL, 17062306a36Sopenharmony_ci PN_ONE_CREDIT_FLOW_CONTROL, 17162306a36Sopenharmony_ci PN_LEGACY_FLOW_CONTROL, 17262306a36Sopenharmony_ci PAD, 17362306a36Sopenharmony_ci }; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci might_sleep(); 17662306a36Sopenharmony_ci return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), 17762306a36Sopenharmony_ci GFP_KERNEL); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code, 18162306a36Sopenharmony_ci gfp_t priority) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; 18462306a36Sopenharmony_ci WARN_ON(code == PN_PIPE_NO_ERROR); 18562306a36Sopenharmony_ci return pep_reply(sk, skb, code, data, sizeof(data), priority); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Control requests are not sent by the pipe service and have a specific 18962306a36Sopenharmony_ci * message format. */ 19062306a36Sopenharmony_cistatic int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code, 19162306a36Sopenharmony_ci gfp_t priority) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci const struct pnpipehdr *oph = pnp_hdr(oskb); 19462306a36Sopenharmony_ci struct sk_buff *skb; 19562306a36Sopenharmony_ci struct pnpipehdr *ph; 19662306a36Sopenharmony_ci struct sockaddr_pn dst; 19762306a36Sopenharmony_ci u8 data[4] = { 19862306a36Sopenharmony_ci oph->pep_type, /* PEP type */ 19962306a36Sopenharmony_ci code, /* error code, at an unusual offset */ 20062306a36Sopenharmony_ci PAD, PAD, 20162306a36Sopenharmony_ci }; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci skb = pep_alloc_skb(sk, data, 4, priority); 20462306a36Sopenharmony_ci if (!skb) 20562306a36Sopenharmony_ci return -ENOMEM; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ph = pnp_hdr(skb); 20862306a36Sopenharmony_ci ph->utid = oph->utid; 20962306a36Sopenharmony_ci ph->message_id = PNS_PEP_CTRL_RESP; 21062306a36Sopenharmony_ci ph->pipe_handle = oph->pipe_handle; 21162306a36Sopenharmony_ci ph->data0 = oph->data[0]; /* CTRL id */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pn_skb_get_src_sockaddr(oskb, &dst); 21462306a36Sopenharmony_ci return pn_skb_send(sk, skb, &dst); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci u8 data[4] = { type, PAD, PAD, status }; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON, 22262306a36Sopenharmony_ci data, 4, priority); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* Send our RX flow control information to the sender. 22662306a36Sopenharmony_ci * Socket must be locked. */ 22762306a36Sopenharmony_cistatic void pipe_grant_credits(struct sock *sk, gfp_t priority) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci BUG_ON(sk->sk_state != TCP_ESTABLISHED); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci switch (pn->rx_fc) { 23462306a36Sopenharmony_ci case PN_LEGACY_FLOW_CONTROL: /* TODO */ 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case PN_ONE_CREDIT_FLOW_CONTROL: 23762306a36Sopenharmony_ci if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, 23862306a36Sopenharmony_ci PEP_IND_READY, priority) == 0) 23962306a36Sopenharmony_ci pn->rx_credits = 1; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case PN_MULTI_CREDIT_FLOW_CONTROL: 24262306a36Sopenharmony_ci if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX) 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS, 24562306a36Sopenharmony_ci CREDITS_MAX - pn->rx_credits, 24662306a36Sopenharmony_ci priority) == 0) 24762306a36Sopenharmony_ci pn->rx_credits = CREDITS_MAX; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 25562306a36Sopenharmony_ci struct pnpipehdr *hdr; 25662306a36Sopenharmony_ci int wake = 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci hdr = pnp_hdr(skb); 26262306a36Sopenharmony_ci if (hdr->pep_type != PN_PEP_TYPE_COMMON) { 26362306a36Sopenharmony_ci net_dbg_ratelimited("Phonet unknown PEP type: %u\n", 26462306a36Sopenharmony_ci (unsigned int)hdr->pep_type); 26562306a36Sopenharmony_ci return -EOPNOTSUPP; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci switch (hdr->data[0]) { 26962306a36Sopenharmony_ci case PN_PEP_IND_FLOW_CONTROL: 27062306a36Sopenharmony_ci switch (pn->tx_fc) { 27162306a36Sopenharmony_ci case PN_LEGACY_FLOW_CONTROL: 27262306a36Sopenharmony_ci switch (hdr->data[3]) { 27362306a36Sopenharmony_ci case PEP_IND_BUSY: 27462306a36Sopenharmony_ci atomic_set(&pn->tx_credits, 0); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case PEP_IND_READY: 27762306a36Sopenharmony_ci atomic_set(&pn->tx_credits, wake = 1); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case PN_ONE_CREDIT_FLOW_CONTROL: 28262306a36Sopenharmony_ci if (hdr->data[3] == PEP_IND_READY) 28362306a36Sopenharmony_ci atomic_set(&pn->tx_credits, wake = 1); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: 28962306a36Sopenharmony_ci if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci atomic_add(wake = hdr->data[3], &pn->tx_credits); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci default: 29562306a36Sopenharmony_ci net_dbg_ratelimited("Phonet unknown PEP indication: %u\n", 29662306a36Sopenharmony_ci (unsigned int)hdr->data[0]); 29762306a36Sopenharmony_ci return -EOPNOTSUPP; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci if (wake) 30062306a36Sopenharmony_ci sk->sk_write_space(sk); 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int pipe_rcv_created(struct sock *sk, struct sk_buff *skb) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 30762306a36Sopenharmony_ci struct pnpipehdr *hdr = pnp_hdr(skb); 30862306a36Sopenharmony_ci u8 n_sb = hdr->data0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; 31162306a36Sopenharmony_ci __skb_pull(skb, sizeof(*hdr)); 31262306a36Sopenharmony_ci while (n_sb > 0) { 31362306a36Sopenharmony_ci u8 type, buf[2], len = sizeof(buf); 31462306a36Sopenharmony_ci u8 *data = pep_get_sb(skb, &type, &len, buf); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (data == NULL) 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci switch (type) { 31962306a36Sopenharmony_ci case PN_PIPE_SB_NEGOTIATED_FC: 32062306a36Sopenharmony_ci if (len < 2 || (data[0] | data[1]) > 3) 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci pn->tx_fc = data[0] & 3; 32362306a36Sopenharmony_ci pn->rx_fc = data[1] & 3; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci n_sb--; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* Queue an skb to a connected sock. 33262306a36Sopenharmony_ci * Socket lock must be held. */ 33362306a36Sopenharmony_cistatic int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 33662306a36Sopenharmony_ci struct pnpipehdr *hdr = pnp_hdr(skb); 33762306a36Sopenharmony_ci struct sk_buff_head *queue; 33862306a36Sopenharmony_ci int err = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci switch (hdr->message_id) { 34362306a36Sopenharmony_ci case PNS_PEP_CONNECT_REQ: 34462306a36Sopenharmony_ci pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci case PNS_PEP_DISCONNECT_REQ: 34862306a36Sopenharmony_ci pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 34962306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE_WAIT; 35062306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 35162306a36Sopenharmony_ci sk->sk_state_change(sk); 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci case PNS_PEP_ENABLE_REQ: 35562306a36Sopenharmony_ci /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ 35662306a36Sopenharmony_ci pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci case PNS_PEP_RESET_REQ: 36062306a36Sopenharmony_ci switch (hdr->state_after_reset) { 36162306a36Sopenharmony_ci case PN_PIPE_DISABLE: 36262306a36Sopenharmony_ci pn->init_enable = 0; 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case PN_PIPE_ENABLE: 36562306a36Sopenharmony_ci pn->init_enable = 1; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci default: /* not allowed to send an error here!? */ 36862306a36Sopenharmony_ci err = -EINVAL; 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci fallthrough; 37262306a36Sopenharmony_ci case PNS_PEP_DISABLE_REQ: 37362306a36Sopenharmony_ci atomic_set(&pn->tx_credits, 0); 37462306a36Sopenharmony_ci pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci case PNS_PEP_CTRL_REQ: 37862306a36Sopenharmony_ci if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { 37962306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci __skb_pull(skb, 4); 38362306a36Sopenharmony_ci queue = &pn->ctrlreq_queue; 38462306a36Sopenharmony_ci goto queue; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci case PNS_PIPE_ALIGNED_DATA: 38762306a36Sopenharmony_ci __skb_pull(skb, 1); 38862306a36Sopenharmony_ci fallthrough; 38962306a36Sopenharmony_ci case PNS_PIPE_DATA: 39062306a36Sopenharmony_ci __skb_pull(skb, 3); /* Pipe data header */ 39162306a36Sopenharmony_ci if (!pn_flow_safe(pn->rx_fc)) { 39262306a36Sopenharmony_ci err = sock_queue_rcv_skb(sk, skb); 39362306a36Sopenharmony_ci if (!err) 39462306a36Sopenharmony_ci return NET_RX_SUCCESS; 39562306a36Sopenharmony_ci err = -ENOBUFS; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (pn->rx_credits == 0) { 40062306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 40162306a36Sopenharmony_ci err = -ENOBUFS; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci pn->rx_credits--; 40562306a36Sopenharmony_ci queue = &sk->sk_receive_queue; 40662306a36Sopenharmony_ci goto queue; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci case PNS_PEP_STATUS_IND: 40962306a36Sopenharmony_ci pipe_rcv_status(sk, skb); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci case PNS_PIPE_REDIRECTED_IND: 41362306a36Sopenharmony_ci err = pipe_rcv_created(sk, skb); 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci case PNS_PIPE_CREATED_IND: 41762306a36Sopenharmony_ci err = pipe_rcv_created(sk, skb); 41862306a36Sopenharmony_ci if (err) 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci fallthrough; 42162306a36Sopenharmony_ci case PNS_PIPE_RESET_IND: 42262306a36Sopenharmony_ci if (!pn->init_enable) 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci fallthrough; 42562306a36Sopenharmony_ci case PNS_PIPE_ENABLED_IND: 42662306a36Sopenharmony_ci if (!pn_flow_safe(pn->tx_fc)) { 42762306a36Sopenharmony_ci atomic_set(&pn->tx_credits, 1); 42862306a36Sopenharmony_ci sk->sk_write_space(sk); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED) 43162306a36Sopenharmony_ci break; /* Nothing to do */ 43262306a36Sopenharmony_ci sk->sk_state = TCP_ESTABLISHED; 43362306a36Sopenharmony_ci pipe_grant_credits(sk, GFP_ATOMIC); 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci case PNS_PIPE_DISABLED_IND: 43762306a36Sopenharmony_ci sk->sk_state = TCP_SYN_RECV; 43862306a36Sopenharmony_ci pn->rx_credits = 0; 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci default: 44262306a36Sopenharmony_ci net_dbg_ratelimited("Phonet unknown PEP message: %u\n", 44362306a36Sopenharmony_ci hdr->message_id); 44462306a36Sopenharmony_ci err = -EINVAL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ciout: 44762306a36Sopenharmony_ci kfree_skb(skb); 44862306a36Sopenharmony_ci return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciqueue: 45162306a36Sopenharmony_ci skb->dev = NULL; 45262306a36Sopenharmony_ci skb_set_owner_r(skb, sk); 45362306a36Sopenharmony_ci skb_queue_tail(queue, skb); 45462306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 45562306a36Sopenharmony_ci sk->sk_data_ready(sk); 45662306a36Sopenharmony_ci return NET_RX_SUCCESS; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* Destroy connected sock. */ 46062306a36Sopenharmony_cistatic void pipe_destruct(struct sock *sk) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci skb_queue_purge(&sk->sk_receive_queue); 46562306a36Sopenharmony_ci skb_queue_purge(&pn->ctrlreq_queue); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci unsigned int i; 47162306a36Sopenharmony_ci u8 final_fc = PN_NO_FLOW_CONTROL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci for (i = 0; i < n; i++) { 47462306a36Sopenharmony_ci u8 fc = fcs[i]; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL) 47762306a36Sopenharmony_ci final_fc = fc; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci return final_fc; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 48562306a36Sopenharmony_ci struct pnpipehdr *hdr; 48662306a36Sopenharmony_ci u8 n_sb; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!pskb_pull(skb, sizeof(*hdr) + 4)) 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci hdr = pnp_hdr(skb); 49262306a36Sopenharmony_ci if (hdr->error_code != PN_PIPE_NO_ERROR) 49362306a36Sopenharmony_ci return -ECONNREFUSED; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Parse sub-blocks */ 49662306a36Sopenharmony_ci n_sb = hdr->data[3]; 49762306a36Sopenharmony_ci while (n_sb > 0) { 49862306a36Sopenharmony_ci u8 type, buf[6], len = sizeof(buf); 49962306a36Sopenharmony_ci const u8 *data = pep_get_sb(skb, &type, &len, buf); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (data == NULL) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci switch (type) { 50562306a36Sopenharmony_ci case PN_PIPE_SB_REQUIRED_FC_TX: 50662306a36Sopenharmony_ci if (len < 2 || len < data[0]) 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci case PN_PIPE_SB_PREFERRED_FC_RX: 51262306a36Sopenharmony_ci if (len < 2 || len < data[0]) 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci n_sb--; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return pipe_handler_send_created_ind(sk); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct pnpipehdr *hdr = pnp_hdr(skb); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (hdr->error_code != PN_PIPE_NO_ERROR) 52962306a36Sopenharmony_ci return -ECONNREFUSED; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */, 53262306a36Sopenharmony_ci NULL, 0, GFP_ATOMIC); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void pipe_start_flow_control(struct sock *sk) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!pn_flow_safe(pn->tx_fc)) { 54162306a36Sopenharmony_ci atomic_set(&pn->tx_credits, 1); 54262306a36Sopenharmony_ci sk->sk_write_space(sk); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci pipe_grant_credits(sk, GFP_ATOMIC); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* Queue an skb to an actively connected sock. 54862306a36Sopenharmony_ci * Socket lock must be held. */ 54962306a36Sopenharmony_cistatic int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 55262306a36Sopenharmony_ci struct pnpipehdr *hdr = pnp_hdr(skb); 55362306a36Sopenharmony_ci int err = NET_RX_SUCCESS; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci switch (hdr->message_id) { 55662306a36Sopenharmony_ci case PNS_PIPE_ALIGNED_DATA: 55762306a36Sopenharmony_ci __skb_pull(skb, 1); 55862306a36Sopenharmony_ci fallthrough; 55962306a36Sopenharmony_ci case PNS_PIPE_DATA: 56062306a36Sopenharmony_ci __skb_pull(skb, 3); /* Pipe data header */ 56162306a36Sopenharmony_ci if (!pn_flow_safe(pn->rx_fc)) { 56262306a36Sopenharmony_ci err = sock_queue_rcv_skb(sk, skb); 56362306a36Sopenharmony_ci if (!err) 56462306a36Sopenharmony_ci return NET_RX_SUCCESS; 56562306a36Sopenharmony_ci err = NET_RX_DROP; 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (pn->rx_credits == 0) { 57062306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 57162306a36Sopenharmony_ci err = NET_RX_DROP; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci pn->rx_credits--; 57562306a36Sopenharmony_ci skb->dev = NULL; 57662306a36Sopenharmony_ci skb_set_owner_r(skb, sk); 57762306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 57862306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 57962306a36Sopenharmony_ci sk->sk_data_ready(sk); 58062306a36Sopenharmony_ci return NET_RX_SUCCESS; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci case PNS_PEP_CONNECT_RESP: 58362306a36Sopenharmony_ci if (sk->sk_state != TCP_SYN_SENT) 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 58662306a36Sopenharmony_ci sk->sk_state_change(sk); 58762306a36Sopenharmony_ci if (pep_connresp_rcv(sk, skb)) { 58862306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE_WAIT; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci if (pn->init_enable == PN_PIPE_DISABLE) 59262306a36Sopenharmony_ci sk->sk_state = TCP_SYN_RECV; 59362306a36Sopenharmony_ci else { 59462306a36Sopenharmony_ci sk->sk_state = TCP_ESTABLISHED; 59562306a36Sopenharmony_ci pipe_start_flow_control(sk); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci case PNS_PEP_ENABLE_RESP: 60062306a36Sopenharmony_ci if (sk->sk_state != TCP_SYN_SENT) 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (pep_enableresp_rcv(sk, skb)) { 60462306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE_WAIT; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci sk->sk_state = TCP_ESTABLISHED; 60962306a36Sopenharmony_ci pipe_start_flow_control(sk); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case PNS_PEP_DISCONNECT_RESP: 61362306a36Sopenharmony_ci /* sock should already be dead, nothing to do */ 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci case PNS_PEP_STATUS_IND: 61762306a36Sopenharmony_ci pipe_rcv_status(sk, skb); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci kfree_skb(skb); 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* Listening sock must be locked */ 62562306a36Sopenharmony_cistatic struct sock *pep_find_pipe(const struct hlist_head *hlist, 62662306a36Sopenharmony_ci const struct sockaddr_pn *dst, 62762306a36Sopenharmony_ci u8 pipe_handle) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct sock *sknode; 63062306a36Sopenharmony_ci u16 dobj = pn_sockaddr_get_object(dst); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci sk_for_each(sknode, hlist) { 63362306a36Sopenharmony_ci struct pep_sock *pnnode = pep_sk(sknode); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Ports match, but addresses might not: */ 63662306a36Sopenharmony_ci if (pnnode->pn_sk.sobject != dobj) 63762306a36Sopenharmony_ci continue; 63862306a36Sopenharmony_ci if (pnnode->pipe_handle != pipe_handle) 63962306a36Sopenharmony_ci continue; 64062306a36Sopenharmony_ci if (sknode->sk_state == TCP_CLOSE_WAIT) 64162306a36Sopenharmony_ci continue; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci sock_hold(sknode); 64462306a36Sopenharmony_ci return sknode; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci return NULL; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/* 65062306a36Sopenharmony_ci * Deliver an skb to a listening sock. 65162306a36Sopenharmony_ci * Socket lock must be held. 65262306a36Sopenharmony_ci * We then queue the skb to the right connected sock (if any). 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic int pep_do_rcv(struct sock *sk, struct sk_buff *skb) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 65762306a36Sopenharmony_ci struct sock *sknode; 65862306a36Sopenharmony_ci struct pnpipehdr *hdr; 65962306a36Sopenharmony_ci struct sockaddr_pn dst; 66062306a36Sopenharmony_ci u8 pipe_handle; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr))) 66362306a36Sopenharmony_ci goto drop; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci hdr = pnp_hdr(skb); 66662306a36Sopenharmony_ci pipe_handle = hdr->pipe_handle; 66762306a36Sopenharmony_ci if (pipe_handle == PN_PIPE_INVALID_HANDLE) 66862306a36Sopenharmony_ci goto drop; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci pn_skb_get_dst_sockaddr(skb, &dst); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Look for an existing pipe handle */ 67362306a36Sopenharmony_ci sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); 67462306a36Sopenharmony_ci if (sknode) 67562306a36Sopenharmony_ci return sk_receive_skb(sknode, skb, 1); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci switch (hdr->message_id) { 67862306a36Sopenharmony_ci case PNS_PEP_CONNECT_REQ: 67962306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { 68062306a36Sopenharmony_ci pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, 68162306a36Sopenharmony_ci GFP_ATOMIC); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci skb_queue_head(&sk->sk_receive_queue, skb); 68562306a36Sopenharmony_ci sk_acceptq_added(sk); 68662306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 68762306a36Sopenharmony_ci sk->sk_data_ready(sk); 68862306a36Sopenharmony_ci return NET_RX_SUCCESS; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci case PNS_PEP_DISCONNECT_REQ: 69162306a36Sopenharmony_ci pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci case PNS_PEP_CTRL_REQ: 69562306a36Sopenharmony_ci pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC); 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci case PNS_PEP_RESET_REQ: 69962306a36Sopenharmony_ci case PNS_PEP_ENABLE_REQ: 70062306a36Sopenharmony_ci case PNS_PEP_DISABLE_REQ: 70162306a36Sopenharmony_ci /* invalid handle is not even allowed here! */ 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci default: 70562306a36Sopenharmony_ci if ((1 << sk->sk_state) 70662306a36Sopenharmony_ci & ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT)) 70762306a36Sopenharmony_ci /* actively connected socket */ 70862306a36Sopenharmony_ci return pipe_handler_do_rcv(sk, skb); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_cidrop: 71162306a36Sopenharmony_ci kfree_skb(skb); 71262306a36Sopenharmony_ci return NET_RX_SUCCESS; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int pipe_do_remove(struct sock *sk) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 71862306a36Sopenharmony_ci struct pnpipehdr *ph; 71962306a36Sopenharmony_ci struct sk_buff *skb; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL); 72262306a36Sopenharmony_ci if (!skb) 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ph = pnp_hdr(skb); 72662306a36Sopenharmony_ci ph->utid = 0; 72762306a36Sopenharmony_ci ph->message_id = PNS_PIPE_REMOVE_REQ; 72862306a36Sopenharmony_ci ph->pipe_handle = pn->pipe_handle; 72962306a36Sopenharmony_ci ph->data0 = PAD; 73062306a36Sopenharmony_ci return pn_skb_send(sk, skb, NULL); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/* associated socket ceases to exist */ 73462306a36Sopenharmony_cistatic void pep_sock_close(struct sock *sk, long timeout) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 73762306a36Sopenharmony_ci int ifindex = 0; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci sock_hold(sk); /* keep a reference after sk_common_release() */ 74062306a36Sopenharmony_ci sk_common_release(sk); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci lock_sock(sk); 74362306a36Sopenharmony_ci if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { 74462306a36Sopenharmony_ci if (sk->sk_backlog_rcv == pipe_do_rcv) 74562306a36Sopenharmony_ci /* Forcefully remove dangling Phonet pipe */ 74662306a36Sopenharmony_ci pipe_do_remove(sk); 74762306a36Sopenharmony_ci else 74862306a36Sopenharmony_ci pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, 74962306a36Sopenharmony_ci NULL, 0); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ifindex = pn->ifindex; 75462306a36Sopenharmony_ci pn->ifindex = 0; 75562306a36Sopenharmony_ci release_sock(sk); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (ifindex) 75862306a36Sopenharmony_ci gprs_detach(sk); 75962306a36Sopenharmony_ci sock_put(sk); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp, 76362306a36Sopenharmony_ci bool kern) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk), *newpn; 76662306a36Sopenharmony_ci struct sock *newsk = NULL; 76762306a36Sopenharmony_ci struct sk_buff *skb; 76862306a36Sopenharmony_ci struct pnpipehdr *hdr; 76962306a36Sopenharmony_ci struct sockaddr_pn dst, src; 77062306a36Sopenharmony_ci int err; 77162306a36Sopenharmony_ci u16 peer_type; 77262306a36Sopenharmony_ci u8 pipe_handle, enabled, n_sb; 77362306a36Sopenharmony_ci u8 aligned = 0; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci skb = skb_recv_datagram(sk, (flags & O_NONBLOCK) ? MSG_DONTWAIT : 0, 77662306a36Sopenharmony_ci errp); 77762306a36Sopenharmony_ci if (!skb) 77862306a36Sopenharmony_ci return NULL; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci lock_sock(sk); 78162306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN) { 78262306a36Sopenharmony_ci err = -EINVAL; 78362306a36Sopenharmony_ci goto drop; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci sk_acceptq_removed(sk); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci err = -EPROTO; 78862306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) 78962306a36Sopenharmony_ci goto drop; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci hdr = pnp_hdr(skb); 79262306a36Sopenharmony_ci pipe_handle = hdr->pipe_handle; 79362306a36Sopenharmony_ci switch (hdr->state_after_connect) { 79462306a36Sopenharmony_ci case PN_PIPE_DISABLE: 79562306a36Sopenharmony_ci enabled = 0; 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case PN_PIPE_ENABLE: 79862306a36Sopenharmony_ci enabled = 1; 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci default: 80162306a36Sopenharmony_ci pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, 80262306a36Sopenharmony_ci GFP_KERNEL); 80362306a36Sopenharmony_ci goto drop; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci peer_type = hdr->other_pep_type << 8; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Parse sub-blocks (options) */ 80862306a36Sopenharmony_ci n_sb = hdr->data[3]; 80962306a36Sopenharmony_ci while (n_sb > 0) { 81062306a36Sopenharmony_ci u8 type, buf[1], len = sizeof(buf); 81162306a36Sopenharmony_ci const u8 *data = pep_get_sb(skb, &type, &len, buf); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (data == NULL) 81462306a36Sopenharmony_ci goto drop; 81562306a36Sopenharmony_ci switch (type) { 81662306a36Sopenharmony_ci case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: 81762306a36Sopenharmony_ci if (len < 1) 81862306a36Sopenharmony_ci goto drop; 81962306a36Sopenharmony_ci peer_type = (peer_type & 0xff00) | data[0]; 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci case PN_PIPE_SB_ALIGNED_DATA: 82262306a36Sopenharmony_ci aligned = data[0] != 0; 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci n_sb--; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Check for duplicate pipe handle */ 82962306a36Sopenharmony_ci newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); 83062306a36Sopenharmony_ci if (unlikely(newsk)) { 83162306a36Sopenharmony_ci __sock_put(newsk); 83262306a36Sopenharmony_ci newsk = NULL; 83362306a36Sopenharmony_ci pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); 83462306a36Sopenharmony_ci goto drop; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Create a new to-be-accepted sock */ 83862306a36Sopenharmony_ci newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot, 83962306a36Sopenharmony_ci kern); 84062306a36Sopenharmony_ci if (!newsk) { 84162306a36Sopenharmony_ci pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); 84262306a36Sopenharmony_ci err = -ENOBUFS; 84362306a36Sopenharmony_ci goto drop; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci sock_init_data(NULL, newsk); 84762306a36Sopenharmony_ci newsk->sk_state = TCP_SYN_RECV; 84862306a36Sopenharmony_ci newsk->sk_backlog_rcv = pipe_do_rcv; 84962306a36Sopenharmony_ci newsk->sk_protocol = sk->sk_protocol; 85062306a36Sopenharmony_ci newsk->sk_destruct = pipe_destruct; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci newpn = pep_sk(newsk); 85362306a36Sopenharmony_ci pn_skb_get_dst_sockaddr(skb, &dst); 85462306a36Sopenharmony_ci pn_skb_get_src_sockaddr(skb, &src); 85562306a36Sopenharmony_ci newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); 85662306a36Sopenharmony_ci newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); 85762306a36Sopenharmony_ci newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); 85862306a36Sopenharmony_ci sock_hold(sk); 85962306a36Sopenharmony_ci newpn->listener = sk; 86062306a36Sopenharmony_ci skb_queue_head_init(&newpn->ctrlreq_queue); 86162306a36Sopenharmony_ci newpn->pipe_handle = pipe_handle; 86262306a36Sopenharmony_ci atomic_set(&newpn->tx_credits, 0); 86362306a36Sopenharmony_ci newpn->ifindex = 0; 86462306a36Sopenharmony_ci newpn->peer_type = peer_type; 86562306a36Sopenharmony_ci newpn->rx_credits = 0; 86662306a36Sopenharmony_ci newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; 86762306a36Sopenharmony_ci newpn->init_enable = enabled; 86862306a36Sopenharmony_ci newpn->aligned = aligned; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci err = pep_accept_conn(newsk, skb); 87162306a36Sopenharmony_ci if (err) { 87262306a36Sopenharmony_ci __sock_put(sk); 87362306a36Sopenharmony_ci sock_put(newsk); 87462306a36Sopenharmony_ci newsk = NULL; 87562306a36Sopenharmony_ci goto drop; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci sk_add_node(newsk, &pn->hlist); 87862306a36Sopenharmony_cidrop: 87962306a36Sopenharmony_ci release_sock(sk); 88062306a36Sopenharmony_ci kfree_skb(skb); 88162306a36Sopenharmony_ci *errp = err; 88262306a36Sopenharmony_ci return newsk; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 88862306a36Sopenharmony_ci int err; 88962306a36Sopenharmony_ci u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE) 89262306a36Sopenharmony_ci pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, 89562306a36Sopenharmony_ci pn->init_enable, data, 4); 89662306a36Sopenharmony_ci if (err) { 89762306a36Sopenharmony_ci pn->pipe_handle = PN_PIPE_INVALID_HANDLE; 89862306a36Sopenharmony_ci return err; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci sk->sk_state = TCP_SYN_SENT; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci int err; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD, 91162306a36Sopenharmony_ci NULL, 0); 91262306a36Sopenharmony_ci if (err) 91362306a36Sopenharmony_ci return err; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci sk->sk_state = TCP_SYN_SENT; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic unsigned int pep_first_packet_length(struct sock *sk) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 92362306a36Sopenharmony_ci struct sk_buff_head *q; 92462306a36Sopenharmony_ci struct sk_buff *skb; 92562306a36Sopenharmony_ci unsigned int len = 0; 92662306a36Sopenharmony_ci bool found = false; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (sock_flag(sk, SOCK_URGINLINE)) { 92962306a36Sopenharmony_ci q = &pn->ctrlreq_queue; 93062306a36Sopenharmony_ci spin_lock_bh(&q->lock); 93162306a36Sopenharmony_ci skb = skb_peek(q); 93262306a36Sopenharmony_ci if (skb) { 93362306a36Sopenharmony_ci len = skb->len; 93462306a36Sopenharmony_ci found = true; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci spin_unlock_bh(&q->lock); 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (likely(!found)) { 94062306a36Sopenharmony_ci q = &sk->sk_receive_queue; 94162306a36Sopenharmony_ci spin_lock_bh(&q->lock); 94262306a36Sopenharmony_ci skb = skb_peek(q); 94362306a36Sopenharmony_ci if (skb) 94462306a36Sopenharmony_ci len = skb->len; 94562306a36Sopenharmony_ci spin_unlock_bh(&q->lock); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return len; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int pep_ioctl(struct sock *sk, int cmd, int *karg) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 95462306a36Sopenharmony_ci int ret = -ENOIOCTLCMD; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci switch (cmd) { 95762306a36Sopenharmony_ci case SIOCINQ: 95862306a36Sopenharmony_ci if (sk->sk_state == TCP_LISTEN) { 95962306a36Sopenharmony_ci ret = -EINVAL; 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci *karg = pep_first_packet_length(sk); 96462306a36Sopenharmony_ci ret = 0; 96562306a36Sopenharmony_ci break; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci case SIOCPNENABLEPIPE: 96862306a36Sopenharmony_ci lock_sock(sk); 96962306a36Sopenharmony_ci if (sk->sk_state == TCP_SYN_SENT) 97062306a36Sopenharmony_ci ret = -EBUSY; 97162306a36Sopenharmony_ci else if (sk->sk_state == TCP_ESTABLISHED) 97262306a36Sopenharmony_ci ret = -EISCONN; 97362306a36Sopenharmony_ci else if (!pn->pn_sk.sobject) 97462306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 97562306a36Sopenharmony_ci else 97662306a36Sopenharmony_ci ret = pep_sock_enable(sk, NULL, 0); 97762306a36Sopenharmony_ci release_sock(sk); 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return ret; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int pep_init(struct sock *sk) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci sk->sk_destruct = pipe_destruct; 98962306a36Sopenharmony_ci INIT_HLIST_HEAD(&pn->hlist); 99062306a36Sopenharmony_ci pn->listener = NULL; 99162306a36Sopenharmony_ci skb_queue_head_init(&pn->ctrlreq_queue); 99262306a36Sopenharmony_ci atomic_set(&pn->tx_credits, 0); 99362306a36Sopenharmony_ci pn->ifindex = 0; 99462306a36Sopenharmony_ci pn->peer_type = 0; 99562306a36Sopenharmony_ci pn->pipe_handle = PN_PIPE_INVALID_HANDLE; 99662306a36Sopenharmony_ci pn->rx_credits = 0; 99762306a36Sopenharmony_ci pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; 99862306a36Sopenharmony_ci pn->init_enable = 1; 99962306a36Sopenharmony_ci pn->aligned = 0; 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int pep_setsockopt(struct sock *sk, int level, int optname, 100462306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 100762306a36Sopenharmony_ci int val = 0, err = 0; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (level != SOL_PNPIPE) 101062306a36Sopenharmony_ci return -ENOPROTOOPT; 101162306a36Sopenharmony_ci if (optlen >= sizeof(int)) { 101262306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) 101362306a36Sopenharmony_ci return -EFAULT; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci lock_sock(sk); 101762306a36Sopenharmony_ci switch (optname) { 101862306a36Sopenharmony_ci case PNPIPE_ENCAP: 101962306a36Sopenharmony_ci if (val && val != PNPIPE_ENCAP_IP) { 102062306a36Sopenharmony_ci err = -EINVAL; 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci if (!pn->ifindex == !val) 102462306a36Sopenharmony_ci break; /* Nothing to do! */ 102562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 102662306a36Sopenharmony_ci err = -EPERM; 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci if (val) { 103062306a36Sopenharmony_ci release_sock(sk); 103162306a36Sopenharmony_ci err = gprs_attach(sk); 103262306a36Sopenharmony_ci if (err > 0) { 103362306a36Sopenharmony_ci pn->ifindex = err; 103462306a36Sopenharmony_ci err = 0; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } else { 103762306a36Sopenharmony_ci pn->ifindex = 0; 103862306a36Sopenharmony_ci release_sock(sk); 103962306a36Sopenharmony_ci gprs_detach(sk); 104062306a36Sopenharmony_ci err = 0; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci goto out_norel; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci case PNPIPE_HANDLE: 104562306a36Sopenharmony_ci if ((sk->sk_state == TCP_CLOSE) && 104662306a36Sopenharmony_ci (val >= 0) && (val < PN_PIPE_INVALID_HANDLE)) 104762306a36Sopenharmony_ci pn->pipe_handle = val; 104862306a36Sopenharmony_ci else 104962306a36Sopenharmony_ci err = -EINVAL; 105062306a36Sopenharmony_ci break; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci case PNPIPE_INITSTATE: 105362306a36Sopenharmony_ci pn->init_enable = !!val; 105462306a36Sopenharmony_ci break; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci err = -ENOPROTOOPT; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci release_sock(sk); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ciout_norel: 106262306a36Sopenharmony_ci return err; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic int pep_getsockopt(struct sock *sk, int level, int optname, 106662306a36Sopenharmony_ci char __user *optval, int __user *optlen) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 106962306a36Sopenharmony_ci int len, val; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (level != SOL_PNPIPE) 107262306a36Sopenharmony_ci return -ENOPROTOOPT; 107362306a36Sopenharmony_ci if (get_user(len, optlen)) 107462306a36Sopenharmony_ci return -EFAULT; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci switch (optname) { 107762306a36Sopenharmony_ci case PNPIPE_ENCAP: 107862306a36Sopenharmony_ci val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; 107962306a36Sopenharmony_ci break; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci case PNPIPE_IFINDEX: 108262306a36Sopenharmony_ci val = pn->ifindex; 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci case PNPIPE_HANDLE: 108662306a36Sopenharmony_ci val = pn->pipe_handle; 108762306a36Sopenharmony_ci if (val == PN_PIPE_INVALID_HANDLE) 108862306a36Sopenharmony_ci return -EINVAL; 108962306a36Sopenharmony_ci break; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci case PNPIPE_INITSTATE: 109262306a36Sopenharmony_ci val = pn->init_enable; 109362306a36Sopenharmony_ci break; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci default: 109662306a36Sopenharmony_ci return -ENOPROTOOPT; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci len = min_t(unsigned int, sizeof(int), len); 110062306a36Sopenharmony_ci if (put_user(len, optlen)) 110162306a36Sopenharmony_ci return -EFAULT; 110262306a36Sopenharmony_ci if (put_user(val, (int __user *) optval)) 110362306a36Sopenharmony_ci return -EFAULT; 110462306a36Sopenharmony_ci return 0; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic int pipe_skb_send(struct sock *sk, struct sk_buff *skb) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 111062306a36Sopenharmony_ci struct pnpipehdr *ph; 111162306a36Sopenharmony_ci int err; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (pn_flow_safe(pn->tx_fc) && 111462306a36Sopenharmony_ci !atomic_add_unless(&pn->tx_credits, -1, 0)) { 111562306a36Sopenharmony_ci kfree_skb(skb); 111662306a36Sopenharmony_ci return -ENOBUFS; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci skb_push(skb, 3 + pn->aligned); 112062306a36Sopenharmony_ci skb_reset_transport_header(skb); 112162306a36Sopenharmony_ci ph = pnp_hdr(skb); 112262306a36Sopenharmony_ci ph->utid = 0; 112362306a36Sopenharmony_ci if (pn->aligned) { 112462306a36Sopenharmony_ci ph->message_id = PNS_PIPE_ALIGNED_DATA; 112562306a36Sopenharmony_ci ph->data0 = 0; /* padding */ 112662306a36Sopenharmony_ci } else 112762306a36Sopenharmony_ci ph->message_id = PNS_PIPE_DATA; 112862306a36Sopenharmony_ci ph->pipe_handle = pn->pipe_handle; 112962306a36Sopenharmony_ci err = pn_skb_send(sk, skb, NULL); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (err && pn_flow_safe(pn->tx_fc)) 113262306a36Sopenharmony_ci atomic_inc(&pn->tx_credits); 113362306a36Sopenharmony_ci return err; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic int pep_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 114062306a36Sopenharmony_ci struct sk_buff *skb; 114162306a36Sopenharmony_ci long timeo; 114262306a36Sopenharmony_ci int flags = msg->msg_flags; 114362306a36Sopenharmony_ci int err, done; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (len > USHRT_MAX) 114662306a36Sopenharmony_ci return -EMSGSIZE; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL| 114962306a36Sopenharmony_ci MSG_CMSG_COMPAT)) || 115062306a36Sopenharmony_ci !(msg->msg_flags & MSG_EOR)) 115162306a36Sopenharmony_ci return -EOPNOTSUPP; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, 115462306a36Sopenharmony_ci flags & MSG_DONTWAIT, &err); 115562306a36Sopenharmony_ci if (!skb) 115662306a36Sopenharmony_ci return err; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned); 115962306a36Sopenharmony_ci err = memcpy_from_msg(skb_put(skb, len), msg, len); 116062306a36Sopenharmony_ci if (err < 0) 116162306a36Sopenharmony_ci goto outfree; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci lock_sock(sk); 116462306a36Sopenharmony_ci timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); 116562306a36Sopenharmony_ci if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) { 116662306a36Sopenharmony_ci err = -ENOTCONN; 116762306a36Sopenharmony_ci goto out; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) { 117062306a36Sopenharmony_ci /* Wait until the pipe gets to enabled state */ 117162306a36Sopenharmony_cidisabled: 117262306a36Sopenharmony_ci err = sk_stream_wait_connect(sk, &timeo); 117362306a36Sopenharmony_ci if (err) 117462306a36Sopenharmony_ci goto out; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (sk->sk_state == TCP_CLOSE_WAIT) { 117762306a36Sopenharmony_ci err = -ECONNRESET; 117862306a36Sopenharmony_ci goto out; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci BUG_ON(sk->sk_state != TCP_ESTABLISHED); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Wait until flow control allows TX */ 118462306a36Sopenharmony_ci done = atomic_read(&pn->tx_credits); 118562306a36Sopenharmony_ci while (!done) { 118662306a36Sopenharmony_ci DEFINE_WAIT_FUNC(wait, woken_wake_function); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (!timeo) { 118962306a36Sopenharmony_ci err = -EAGAIN; 119062306a36Sopenharmony_ci goto out; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci if (signal_pending(current)) { 119362306a36Sopenharmony_ci err = sock_intr_errno(timeo); 119462306a36Sopenharmony_ci goto out; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci add_wait_queue(sk_sleep(sk), &wait); 119862306a36Sopenharmony_ci done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits), &wait); 119962306a36Sopenharmony_ci remove_wait_queue(sk_sleep(sk), &wait); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 120262306a36Sopenharmony_ci goto disabled; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci err = pipe_skb_send(sk, skb); 120662306a36Sopenharmony_ci if (err >= 0) 120762306a36Sopenharmony_ci err = len; /* success! */ 120862306a36Sopenharmony_ci skb = NULL; 120962306a36Sopenharmony_ciout: 121062306a36Sopenharmony_ci release_sock(sk); 121162306a36Sopenharmony_cioutfree: 121262306a36Sopenharmony_ci kfree_skb(skb); 121362306a36Sopenharmony_ci return err; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ciint pep_writeable(struct sock *sk) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci return atomic_read(&pn->tx_credits); 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ciint pep_write(struct sock *sk, struct sk_buff *skb) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct sk_buff *rskb, *fs; 122662306a36Sopenharmony_ci int flen = 0; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (pep_sk(sk)->aligned) 122962306a36Sopenharmony_ci return pipe_skb_send(sk, skb); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); 123262306a36Sopenharmony_ci if (!rskb) { 123362306a36Sopenharmony_ci kfree_skb(skb); 123462306a36Sopenharmony_ci return -ENOMEM; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci skb_shinfo(rskb)->frag_list = skb; 123762306a36Sopenharmony_ci rskb->len += skb->len; 123862306a36Sopenharmony_ci rskb->data_len += rskb->len; 123962306a36Sopenharmony_ci rskb->truesize += rskb->len; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci /* Avoid nested fragments */ 124262306a36Sopenharmony_ci skb_walk_frags(skb, fs) 124362306a36Sopenharmony_ci flen += fs->len; 124462306a36Sopenharmony_ci skb->next = skb_shinfo(skb)->frag_list; 124562306a36Sopenharmony_ci skb_frag_list_init(skb); 124662306a36Sopenharmony_ci skb->len -= flen; 124762306a36Sopenharmony_ci skb->data_len -= flen; 124862306a36Sopenharmony_ci skb->truesize -= flen; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci skb_reserve(rskb, MAX_PHONET_HEADER + 3); 125162306a36Sopenharmony_ci return pipe_skb_send(sk, rskb); 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistruct sk_buff *pep_read(struct sock *sk) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED) 125962306a36Sopenharmony_ci pipe_grant_credits(sk, GFP_ATOMIC); 126062306a36Sopenharmony_ci return skb; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, 126462306a36Sopenharmony_ci int flags, int *addr_len) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci struct sk_buff *skb; 126762306a36Sopenharmony_ci int err; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL| 127062306a36Sopenharmony_ci MSG_NOSIGNAL|MSG_CMSG_COMPAT)) 127162306a36Sopenharmony_ci return -EOPNOTSUPP; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE))) 127462306a36Sopenharmony_ci return -ENOTCONN; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) { 127762306a36Sopenharmony_ci /* Dequeue and acknowledge control request */ 127862306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (flags & MSG_PEEK) 128162306a36Sopenharmony_ci return -EOPNOTSUPP; 128262306a36Sopenharmony_ci skb = skb_dequeue(&pn->ctrlreq_queue); 128362306a36Sopenharmony_ci if (skb) { 128462306a36Sopenharmony_ci pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR, 128562306a36Sopenharmony_ci GFP_KERNEL); 128662306a36Sopenharmony_ci msg->msg_flags |= MSG_OOB; 128762306a36Sopenharmony_ci goto copy; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci if (flags & MSG_OOB) 129062306a36Sopenharmony_ci return -EINVAL; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &err); 129462306a36Sopenharmony_ci lock_sock(sk); 129562306a36Sopenharmony_ci if (skb == NULL) { 129662306a36Sopenharmony_ci if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT) 129762306a36Sopenharmony_ci err = -ECONNRESET; 129862306a36Sopenharmony_ci release_sock(sk); 129962306a36Sopenharmony_ci return err; 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED) 130362306a36Sopenharmony_ci pipe_grant_credits(sk, GFP_KERNEL); 130462306a36Sopenharmony_ci release_sock(sk); 130562306a36Sopenharmony_cicopy: 130662306a36Sopenharmony_ci msg->msg_flags |= MSG_EOR; 130762306a36Sopenharmony_ci if (skb->len > len) 130862306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 130962306a36Sopenharmony_ci else 131062306a36Sopenharmony_ci len = skb->len; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, len); 131362306a36Sopenharmony_ci if (!err) 131462306a36Sopenharmony_ci err = (flags & MSG_TRUNC) ? skb->len : len; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci skb_free_datagram(sk, skb); 131762306a36Sopenharmony_ci return err; 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic void pep_sock_unhash(struct sock *sk) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct pep_sock *pn = pep_sk(sk); 132362306a36Sopenharmony_ci struct sock *skparent = NULL; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci lock_sock(sk); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci if (pn->listener != NULL) { 132862306a36Sopenharmony_ci skparent = pn->listener; 132962306a36Sopenharmony_ci pn->listener = NULL; 133062306a36Sopenharmony_ci release_sock(sk); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci pn = pep_sk(skparent); 133362306a36Sopenharmony_ci lock_sock(skparent); 133462306a36Sopenharmony_ci sk_del_node_init(sk); 133562306a36Sopenharmony_ci sk = skparent; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Unhash a listening sock only when it is closed 133962306a36Sopenharmony_ci * and all of its active connected pipes are closed. */ 134062306a36Sopenharmony_ci if (hlist_empty(&pn->hlist)) 134162306a36Sopenharmony_ci pn_sock_unhash(&pn->pn_sk.sk); 134262306a36Sopenharmony_ci release_sock(sk); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci if (skparent) 134562306a36Sopenharmony_ci sock_put(skparent); 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic struct proto pep_proto = { 134962306a36Sopenharmony_ci .close = pep_sock_close, 135062306a36Sopenharmony_ci .accept = pep_sock_accept, 135162306a36Sopenharmony_ci .connect = pep_sock_connect, 135262306a36Sopenharmony_ci .ioctl = pep_ioctl, 135362306a36Sopenharmony_ci .init = pep_init, 135462306a36Sopenharmony_ci .setsockopt = pep_setsockopt, 135562306a36Sopenharmony_ci .getsockopt = pep_getsockopt, 135662306a36Sopenharmony_ci .sendmsg = pep_sendmsg, 135762306a36Sopenharmony_ci .recvmsg = pep_recvmsg, 135862306a36Sopenharmony_ci .backlog_rcv = pep_do_rcv, 135962306a36Sopenharmony_ci .hash = pn_sock_hash, 136062306a36Sopenharmony_ci .unhash = pep_sock_unhash, 136162306a36Sopenharmony_ci .get_port = pn_sock_get_port, 136262306a36Sopenharmony_ci .obj_size = sizeof(struct pep_sock), 136362306a36Sopenharmony_ci .owner = THIS_MODULE, 136462306a36Sopenharmony_ci .name = "PNPIPE", 136562306a36Sopenharmony_ci}; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic const struct phonet_protocol pep_pn_proto = { 136862306a36Sopenharmony_ci .ops = &phonet_stream_ops, 136962306a36Sopenharmony_ci .prot = &pep_proto, 137062306a36Sopenharmony_ci .sock_type = SOCK_SEQPACKET, 137162306a36Sopenharmony_ci}; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic int __init pep_register(void) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic void __exit pep_unregister(void) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto); 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cimodule_init(pep_register); 138462306a36Sopenharmony_cimodule_exit(pep_unregister); 138562306a36Sopenharmony_ciMODULE_AUTHOR("Remi Denis-Courmont, Nokia"); 138662306a36Sopenharmony_ciMODULE_DESCRIPTION("Phonet pipe protocol"); 138762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 138862306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE); 1389