mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
Phonet: implement GPRS virtual interface over PEP socket
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c41bd97f81
commit
02a47617cd
8 changed files with 550 additions and 16 deletions
|
@ -31,9 +31,17 @@
|
||||||
#define PN_PROTO_PIPE 2
|
#define PN_PROTO_PIPE 2
|
||||||
#define PHONET_NPROTO 3
|
#define PHONET_NPROTO 3
|
||||||
|
|
||||||
|
/* Socket options for SOL_PNPIPE level */
|
||||||
|
#define PNPIPE_ENCAP 1
|
||||||
|
#define PNPIPE_IFINDEX 2
|
||||||
|
|
||||||
#define PNADDR_ANY 0
|
#define PNADDR_ANY 0
|
||||||
#define PNPORT_RESOURCE_ROUTING 0
|
#define PNPORT_RESOURCE_ROUTING 0
|
||||||
|
|
||||||
|
/* Values for PNPIPE_ENCAP option */
|
||||||
|
#define PNPIPE_ENCAP_NONE 0
|
||||||
|
#define PNPIPE_ENCAP_IP 1
|
||||||
|
|
||||||
/* ioctls */
|
/* ioctls */
|
||||||
#define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0)
|
#define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0)
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,7 @@ struct ucred {
|
||||||
#define SOL_RXRPC 272
|
#define SOL_RXRPC 272
|
||||||
#define SOL_PPPOL2TP 273
|
#define SOL_PPPOL2TP 273
|
||||||
#define SOL_BLUETOOTH 274
|
#define SOL_BLUETOOTH 274
|
||||||
|
#define SOL_PNPIPE 275
|
||||||
|
|
||||||
/* IPX options */
|
/* IPX options */
|
||||||
#define IPX_TYPE 1
|
#define IPX_TYPE 1
|
||||||
|
|
38
include/net/phonet/gprs.h
Normal file
38
include/net/phonet/gprs.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* File: pep_gprs.h
|
||||||
|
*
|
||||||
|
* GPRS over Phonet pipe end point socket
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Nokia Corporation.
|
||||||
|
*
|
||||||
|
* Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NET_PHONET_GPRS_H
|
||||||
|
#define NET_PHONET_GPRS_H
|
||||||
|
|
||||||
|
struct sock;
|
||||||
|
struct sk_buff;
|
||||||
|
|
||||||
|
int pep_writeable(struct sock *sk);
|
||||||
|
int pep_write(struct sock *sk, struct sk_buff *skb);
|
||||||
|
struct sk_buff *pep_read(struct sock *sk);
|
||||||
|
|
||||||
|
int gprs_attach(struct sock *sk);
|
||||||
|
void gprs_detach(struct sock *sk);
|
||||||
|
|
||||||
|
#endif
|
|
@ -35,6 +35,7 @@ struct pep_sock {
|
||||||
struct sock *listener;
|
struct sock *listener;
|
||||||
struct sk_buff_head ctrlreq_queue;
|
struct sk_buff_head ctrlreq_queue;
|
||||||
#define PNPIPE_CTRLREQ_MAX 10
|
#define PNPIPE_CTRLREQ_MAX 10
|
||||||
|
int ifindex;
|
||||||
u16 peer_type; /* peer type/subtype */
|
u16 peer_type; /* peer type/subtype */
|
||||||
u8 pipe_handle;
|
u8 pipe_handle;
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ phonet-objs := \
|
||||||
sysctl.o \
|
sysctl.o \
|
||||||
af_phonet.o
|
af_phonet.o
|
||||||
|
|
||||||
pn_pep-objs := pep.o
|
pn_pep-objs := pep.o pep-gprs.o
|
||||||
|
|
347
net/phonet/pep-gprs.c
Normal file
347
net/phonet/pep-gprs.c
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
/*
|
||||||
|
* File: pep-gprs.c
|
||||||
|
*
|
||||||
|
* GPRS over Phonet pipe end point socket
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Nokia Corporation.
|
||||||
|
*
|
||||||
|
* Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/if_arp.h>
|
||||||
|
#include <net/sock.h>
|
||||||
|
|
||||||
|
#include <linux/if_phonet.h>
|
||||||
|
#include <net/tcp_states.h>
|
||||||
|
#include <net/phonet/gprs.h>
|
||||||
|
|
||||||
|
#define GPRS_DEFAULT_MTU 1400
|
||||||
|
|
||||||
|
struct gprs_dev {
|
||||||
|
struct sock *sk;
|
||||||
|
void (*old_state_change)(struct sock *);
|
||||||
|
void (*old_data_ready)(struct sock *, int);
|
||||||
|
void (*old_write_space)(struct sock *);
|
||||||
|
|
||||||
|
struct net_device *net;
|
||||||
|
struct net_device_stats stats;
|
||||||
|
|
||||||
|
struct sk_buff_head tx_queue;
|
||||||
|
struct work_struct tx_work;
|
||||||
|
spinlock_t tx_lock;
|
||||||
|
unsigned tx_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gprs_type_trans(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
const u8 *pvfc;
|
||||||
|
u8 buf;
|
||||||
|
|
||||||
|
pvfc = skb_header_pointer(skb, 0, 1, &buf);
|
||||||
|
if (!pvfc)
|
||||||
|
return 0;
|
||||||
|
/* Look at IP version field */
|
||||||
|
switch (*pvfc >> 4) {
|
||||||
|
case 4:
|
||||||
|
return htons(ETH_P_IP);
|
||||||
|
case 6:
|
||||||
|
return htons(ETH_P_IPV6);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Socket callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void gprs_state_change(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = sk->sk_user_data;
|
||||||
|
|
||||||
|
if (sk->sk_state == TCP_CLOSE_WAIT) {
|
||||||
|
netif_stop_queue(dev->net);
|
||||||
|
netif_carrier_off(dev->net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
u16 protocol = gprs_type_trans(skb);
|
||||||
|
|
||||||
|
if (!protocol) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(skb_headroom(skb) & 3)) {
|
||||||
|
struct sk_buff *rskb, *fs;
|
||||||
|
int flen = 0;
|
||||||
|
|
||||||
|
/* Phonet Pipe data header is misaligned (3 bytes),
|
||||||
|
* so wrap the IP packet as a single fragment of an head-less
|
||||||
|
* socket buffer. The network stack will pull what it needs,
|
||||||
|
* but at least, the whole IP payload is not memcpy'd. */
|
||||||
|
rskb = netdev_alloc_skb(dev->net, 0);
|
||||||
|
if (!rskb) {
|
||||||
|
err = -ENOBUFS;
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
skb_shinfo(rskb)->frag_list = skb;
|
||||||
|
rskb->len += skb->len;
|
||||||
|
rskb->data_len += rskb->len;
|
||||||
|
rskb->truesize += rskb->len;
|
||||||
|
|
||||||
|
/* Avoid nested fragments */
|
||||||
|
for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
|
||||||
|
flen += fs->len;
|
||||||
|
skb->next = skb_shinfo(skb)->frag_list;
|
||||||
|
skb_shinfo(skb)->frag_list = NULL;
|
||||||
|
skb->len -= flen;
|
||||||
|
skb->data_len -= flen;
|
||||||
|
skb->truesize -= flen;
|
||||||
|
|
||||||
|
skb = rskb;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb->protocol = protocol;
|
||||||
|
skb_reset_mac_header(skb);
|
||||||
|
skb->dev = dev->net;
|
||||||
|
|
||||||
|
if (likely(dev->net->flags & IFF_UP)) {
|
||||||
|
dev->stats.rx_packets++;
|
||||||
|
dev->stats.rx_bytes += skb->len;
|
||||||
|
netif_rx(skb);
|
||||||
|
skb = NULL;
|
||||||
|
} else
|
||||||
|
err = -ENODEV;
|
||||||
|
|
||||||
|
drop:
|
||||||
|
if (skb) {
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
dev->stats.rx_dropped++;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gprs_data_ready(struct sock *sk, int len)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = sk->sk_user_data;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
while ((skb = pep_read(sk)) != NULL) {
|
||||||
|
skb_orphan(skb);
|
||||||
|
gprs_recv(dev, skb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gprs_write_space(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = sk->sk_user_data;
|
||||||
|
unsigned credits = pep_writeable(sk);
|
||||||
|
|
||||||
|
spin_lock_bh(&dev->tx_lock);
|
||||||
|
dev->tx_max = credits;
|
||||||
|
if (credits > skb_queue_len(&dev->tx_queue))
|
||||||
|
netif_wake_queue(dev->net);
|
||||||
|
spin_unlock_bh(&dev->tx_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Network device callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int gprs_xmit(struct sk_buff *skb, struct net_device *net)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = netdev_priv(net);
|
||||||
|
|
||||||
|
switch (skb->protocol) {
|
||||||
|
case htons(ETH_P_IP):
|
||||||
|
case htons(ETH_P_IPV6):
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&dev->tx_lock);
|
||||||
|
if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) {
|
||||||
|
skb_queue_tail(&dev->tx_queue, skb);
|
||||||
|
skb = NULL;
|
||||||
|
}
|
||||||
|
if (skb_queue_len(&dev->tx_queue) >= dev->tx_max)
|
||||||
|
netif_stop_queue(net);
|
||||||
|
spin_unlock(&dev->tx_lock);
|
||||||
|
|
||||||
|
schedule_work(&dev->tx_work);
|
||||||
|
if (unlikely(skb))
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gprs_tx(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work);
|
||||||
|
struct sock *sk = dev->sk;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dev->stats.tx_bytes += skb->len;
|
||||||
|
dev->stats.tx_packets++;
|
||||||
|
|
||||||
|
skb_orphan(skb);
|
||||||
|
skb_set_owner_w(skb, sk);
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
err = pep_write(sk, skb);
|
||||||
|
if (err) {
|
||||||
|
LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n",
|
||||||
|
dev->net->name, err);
|
||||||
|
dev->stats.tx_aborted_errors++;
|
||||||
|
dev->stats.tx_errors++;
|
||||||
|
}
|
||||||
|
release_sock(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
gprs_write_space(sk);
|
||||||
|
release_sock(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gprs_set_mtu(struct net_device *net, int new_mtu)
|
||||||
|
{
|
||||||
|
if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
net->mtu = new_mtu;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct net_device_stats *gprs_get_stats(struct net_device *net)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = netdev_priv(net);
|
||||||
|
|
||||||
|
return &dev->stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gprs_setup(struct net_device *net)
|
||||||
|
{
|
||||||
|
net->features = NETIF_F_FRAGLIST;
|
||||||
|
net->type = ARPHRD_NONE;
|
||||||
|
net->flags = IFF_POINTOPOINT | IFF_NOARP;
|
||||||
|
net->mtu = GPRS_DEFAULT_MTU;
|
||||||
|
net->hard_header_len = 0;
|
||||||
|
net->addr_len = 0;
|
||||||
|
net->tx_queue_len = 10;
|
||||||
|
|
||||||
|
net->destructor = free_netdev;
|
||||||
|
net->hard_start_xmit = gprs_xmit; /* mandatory */
|
||||||
|
net->change_mtu = gprs_set_mtu;
|
||||||
|
net->get_stats = gprs_get_stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* External interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach a GPRS interface to a datagram socket.
|
||||||
|
* Returns the interface index on success, negative error code on error.
|
||||||
|
*/
|
||||||
|
int gprs_attach(struct sock *sk)
|
||||||
|
{
|
||||||
|
static const char ifname[] = "gprs%d";
|
||||||
|
struct gprs_dev *dev;
|
||||||
|
struct net_device *net;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (unlikely(sk->sk_type == SOCK_STREAM))
|
||||||
|
return -EINVAL; /* need packet boundaries */
|
||||||
|
|
||||||
|
/* Create net device */
|
||||||
|
net = alloc_netdev(sizeof(*dev), ifname, gprs_setup);
|
||||||
|
if (!net)
|
||||||
|
return -ENOMEM;
|
||||||
|
dev = netdev_priv(net);
|
||||||
|
dev->net = net;
|
||||||
|
dev->tx_max = 0;
|
||||||
|
spin_lock_init(&dev->tx_lock);
|
||||||
|
skb_queue_head_init(&dev->tx_queue);
|
||||||
|
INIT_WORK(&dev->tx_work, gprs_tx);
|
||||||
|
|
||||||
|
netif_stop_queue(net);
|
||||||
|
err = register_netdev(net);
|
||||||
|
if (err) {
|
||||||
|
free_netdev(net);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
if (unlikely(sk->sk_user_data)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto out_rel;
|
||||||
|
}
|
||||||
|
if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) ||
|
||||||
|
sock_flag(sk, SOCK_DEAD))) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out_rel;
|
||||||
|
}
|
||||||
|
sk->sk_user_data = dev;
|
||||||
|
dev->old_state_change = sk->sk_state_change;
|
||||||
|
dev->old_data_ready = sk->sk_data_ready;
|
||||||
|
dev->old_write_space = sk->sk_write_space;
|
||||||
|
sk->sk_state_change = gprs_state_change;
|
||||||
|
sk->sk_data_ready = gprs_data_ready;
|
||||||
|
sk->sk_write_space = gprs_write_space;
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
sock_hold(sk);
|
||||||
|
dev->sk = sk;
|
||||||
|
|
||||||
|
printk(KERN_DEBUG"%s: attached\n", net->name);
|
||||||
|
gprs_write_space(sk); /* kick off TX */
|
||||||
|
return net->ifindex;
|
||||||
|
|
||||||
|
out_rel:
|
||||||
|
release_sock(sk);
|
||||||
|
unregister_netdev(net);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_detach(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct gprs_dev *dev = sk->sk_user_data;
|
||||||
|
struct net_device *net = dev->net;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
sk->sk_user_data = NULL;
|
||||||
|
sk->sk_state_change = dev->old_state_change;
|
||||||
|
sk->sk_data_ready = dev->old_data_ready;
|
||||||
|
sk->sk_write_space = dev->old_write_space;
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
printk(KERN_DEBUG"%s: detached\n", net->name);
|
||||||
|
unregister_netdev(net);
|
||||||
|
flush_scheduled_work();
|
||||||
|
sock_put(sk);
|
||||||
|
skb_queue_purge(&dev->tx_queue);
|
||||||
|
}
|
161
net/phonet/pep.c
161
net/phonet/pep.c
|
@ -31,6 +31,7 @@
|
||||||
#include <linux/phonet.h>
|
#include <linux/phonet.h>
|
||||||
#include <net/phonet/phonet.h>
|
#include <net/phonet/phonet.h>
|
||||||
#include <net/phonet/pep.h>
|
#include <net/phonet/pep.h>
|
||||||
|
#include <net/phonet/gprs.h>
|
||||||
|
|
||||||
/* sk_state values:
|
/* sk_state values:
|
||||||
* TCP_CLOSE sock not in use yet
|
* TCP_CLOSE sock not in use yet
|
||||||
|
@ -612,6 +613,7 @@ drop:
|
||||||
static void pep_sock_close(struct sock *sk, long timeout)
|
static void pep_sock_close(struct sock *sk, long timeout)
|
||||||
{
|
{
|
||||||
struct pep_sock *pn = pep_sk(sk);
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
|
int ifindex = 0;
|
||||||
|
|
||||||
sk_common_release(sk);
|
sk_common_release(sk);
|
||||||
|
|
||||||
|
@ -625,7 +627,12 @@ static void pep_sock_close(struct sock *sk, long timeout)
|
||||||
sk_del_node_init(sknode);
|
sk_del_node_init(sknode);
|
||||||
sk->sk_state = TCP_CLOSE;
|
sk->sk_state = TCP_CLOSE;
|
||||||
}
|
}
|
||||||
|
ifindex = pn->ifindex;
|
||||||
|
pn->ifindex = 0;
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
|
|
||||||
|
if (ifindex)
|
||||||
|
gprs_detach(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pep_wait_connreq(struct sock *sk, int noblock)
|
static int pep_wait_connreq(struct sock *sk, int noblock)
|
||||||
|
@ -730,12 +737,107 @@ static int pep_init(struct sock *sk)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pep_setsockopt(struct sock *sk, int level, int optname,
|
||||||
|
char __user *optval, int optlen)
|
||||||
|
{
|
||||||
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
|
int val = 0, err = 0;
|
||||||
|
|
||||||
|
if (level != SOL_PNPIPE)
|
||||||
|
return -ENOPROTOOPT;
|
||||||
|
if (optlen >= sizeof(int)) {
|
||||||
|
if (get_user(val, (int __user *) optval))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
switch (optname) {
|
||||||
|
case PNPIPE_ENCAP:
|
||||||
|
if (val && val != PNPIPE_ENCAP_IP) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!pn->ifindex == !val)
|
||||||
|
break; /* Nothing to do! */
|
||||||
|
if (!capable(CAP_NET_ADMIN)) {
|
||||||
|
err = -EPERM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
release_sock(sk);
|
||||||
|
err = gprs_attach(sk);
|
||||||
|
if (err > 0) {
|
||||||
|
pn->ifindex = err;
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pn->ifindex = 0;
|
||||||
|
release_sock(sk);
|
||||||
|
gprs_detach(sk);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
goto out_norel;
|
||||||
|
default:
|
||||||
|
err = -ENOPROTOOPT;
|
||||||
|
}
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
out_norel:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pep_getsockopt(struct sock *sk, int level, int optname,
|
||||||
|
char __user *optval, int __user *optlen)
|
||||||
|
{
|
||||||
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
|
int len, val;
|
||||||
|
|
||||||
|
if (level != SOL_PNPIPE)
|
||||||
|
return -ENOPROTOOPT;
|
||||||
|
if (get_user(len, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
switch (optname) {
|
||||||
|
case PNPIPE_ENCAP:
|
||||||
|
val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
|
||||||
|
break;
|
||||||
|
case PNPIPE_IFINDEX:
|
||||||
|
val = pn->ifindex;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENOPROTOOPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = min_t(unsigned int, sizeof(int), len);
|
||||||
|
if (put_user(len, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
if (put_user(val, (int __user *) optval))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
|
struct pnpipehdr *ph;
|
||||||
|
|
||||||
|
skb_push(skb, 3);
|
||||||
|
skb_reset_transport_header(skb);
|
||||||
|
ph = pnp_hdr(skb);
|
||||||
|
ph->utid = 0;
|
||||||
|
ph->message_id = PNS_PIPE_DATA;
|
||||||
|
ph->pipe_handle = pn->pipe_handle;
|
||||||
|
if (pn_flow_safe(pn->tx_fc) && pn->tx_credits)
|
||||||
|
pn->tx_credits--;
|
||||||
|
|
||||||
|
return pn_skb_send(sk, skb, &pipe_srv);
|
||||||
|
}
|
||||||
|
|
||||||
static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
|
static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
|
||||||
struct msghdr *msg, size_t len)
|
struct msghdr *msg, size_t len)
|
||||||
{
|
{
|
||||||
struct pep_sock *pn = pep_sk(sk);
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
||||||
struct pnpipehdr *ph;
|
|
||||||
long timeo;
|
long timeo;
|
||||||
int flags = msg->msg_flags;
|
int flags = msg->msg_flags;
|
||||||
int err, done;
|
int err, done;
|
||||||
|
@ -801,16 +903,7 @@ disabled:
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
__skb_push(skb, 3);
|
err = pipe_skb_send(sk, skb);
|
||||||
skb_reset_transport_header(skb);
|
|
||||||
ph = pnp_hdr(skb);
|
|
||||||
ph->utid = 0;
|
|
||||||
ph->message_id = PNS_PIPE_DATA;
|
|
||||||
ph->pipe_handle = pn->pipe_handle;
|
|
||||||
if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
|
|
||||||
pn->tx_credits--;
|
|
||||||
|
|
||||||
err = pn_skb_send(sk, skb, &pipe_srv);
|
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
err = len; /* success! */
|
err = len; /* success! */
|
||||||
skb = NULL;
|
skb = NULL;
|
||||||
|
@ -820,6 +913,50 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pep_writeable(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct pep_sock *pn = pep_sk(sk);
|
||||||
|
|
||||||
|
return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pep_write(struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct sk_buff *rskb, *fs;
|
||||||
|
int flen = 0;
|
||||||
|
|
||||||
|
rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
|
||||||
|
if (!rskb) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
skb_shinfo(rskb)->frag_list = skb;
|
||||||
|
rskb->len += skb->len;
|
||||||
|
rskb->data_len += rskb->len;
|
||||||
|
rskb->truesize += rskb->len;
|
||||||
|
|
||||||
|
/* Avoid nested fragments */
|
||||||
|
for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
|
||||||
|
flen += fs->len;
|
||||||
|
skb->next = skb_shinfo(skb)->frag_list;
|
||||||
|
skb_shinfo(skb)->frag_list = NULL;
|
||||||
|
skb->len -= flen;
|
||||||
|
skb->data_len -= flen;
|
||||||
|
skb->truesize -= flen;
|
||||||
|
|
||||||
|
skb_reserve(rskb, MAX_PHONET_HEADER + 3);
|
||||||
|
return pipe_skb_send(sk, rskb);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sk_buff *pep_read(struct sock *sk)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
|
||||||
|
|
||||||
|
if (sk->sk_state == TCP_ESTABLISHED)
|
||||||
|
pipe_grant_credits(sk);
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
|
static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||||
struct msghdr *msg, size_t len, int noblock,
|
struct msghdr *msg, size_t len, int noblock,
|
||||||
int flags, int *addr_len)
|
int flags, int *addr_len)
|
||||||
|
@ -902,6 +1039,8 @@ static struct proto pep_proto = {
|
||||||
.accept = pep_sock_accept,
|
.accept = pep_sock_accept,
|
||||||
.ioctl = pep_ioctl,
|
.ioctl = pep_ioctl,
|
||||||
.init = pep_init,
|
.init = pep_init,
|
||||||
|
.setsockopt = pep_setsockopt,
|
||||||
|
.getsockopt = pep_getsockopt,
|
||||||
.sendmsg = pep_sendmsg,
|
.sendmsg = pep_sendmsg,
|
||||||
.recvmsg = pep_recvmsg,
|
.recvmsg = pep_recvmsg,
|
||||||
.backlog_rcv = pep_do_rcv,
|
.backlog_rcv = pep_do_rcv,
|
||||||
|
|
|
@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = {
|
||||||
.ioctl = pn_socket_ioctl,
|
.ioctl = pn_socket_ioctl,
|
||||||
.listen = pn_socket_listen,
|
.listen = pn_socket_listen,
|
||||||
.shutdown = sock_no_shutdown,
|
.shutdown = sock_no_shutdown,
|
||||||
.setsockopt = sock_no_setsockopt,
|
.setsockopt = sock_common_setsockopt,
|
||||||
.getsockopt = sock_no_getsockopt,
|
.getsockopt = sock_common_getsockopt,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_setsockopt = sock_no_setsockopt,
|
.compat_setsockopt = compat_sock_common_setsockopt,
|
||||||
.compat_getsockopt = compat_sock_no_getsockopt,
|
.compat_getsockopt = compat_sock_common_getsockopt,
|
||||||
#endif
|
#endif
|
||||||
.sendmsg = pn_socket_sendmsg,
|
.sendmsg = pn_socket_sendmsg,
|
||||||
.recvmsg = sock_common_recvmsg,
|
.recvmsg = sock_common_recvmsg,
|
||||||
|
|
Loading…
Reference in a new issue