diff mbox

[5/6] Phonet: implement GPRS virtual interface over PEP socket

Message ID 1222855985-22859-5-git-send-email-remi.denis-courmont@nokia.com
State Superseded, archived
Delegated to: David Miller
Headers show

Commit Message

Rémi Denis-Courmont Oct. 1, 2008, 10:13 a.m. UTC
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>

Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
 include/linux/phonet.h    |    8 +
 include/linux/socket.h    |    1 +
 include/net/phonet/gprs.h |   38 +++++
 include/net/phonet/pep.h  |    1 +
 net/phonet/Makefile       |    2 +-
 net/phonet/pep-gprs.c     |  341 +++++++++++++++++++++++++++++++++++++++++++++
 net/phonet/pep.c          |  157 ++++++++++++++++++++--
 net/phonet/socket.c       |    8 +-
 8 files changed, 542 insertions(+), 14 deletions(-)
 create mode 100644 include/net/phonet/gprs.h
 create mode 100644 net/phonet/pep-gprs.c

Comments

Arnaldo Carvalho de Melo Oct. 1, 2008, 1:32 p.m. UTC | #1
Em Wed, Oct 01, 2008 at 01:13:04PM +0300, Remi Denis-Courmont escreveu:
> From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> 
> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> ---
>  include/linux/phonet.h    |    8 +
>  include/linux/socket.h    |    1 +
>  include/net/phonet/gprs.h |   38 +++++
>  include/net/phonet/pep.h  |    1 +
>  net/phonet/Makefile       |    2 +-
>  net/phonet/pep-gprs.c     |  341 +++++++++++++++++++++++++++++++++++++++++++++
>  net/phonet/pep.c          |  157 ++++++++++++++++++++--
>  net/phonet/socket.c       |    8 +-
>  8 files changed, 542 insertions(+), 14 deletions(-)
>  create mode 100644 include/net/phonet/gprs.h
>  create mode 100644 net/phonet/pep-gprs.c
> 
> diff --git a/include/linux/phonet.h b/include/linux/phonet.h
> index f921852..c9609f9 100644
> --- a/include/linux/phonet.h
> +++ b/include/linux/phonet.h
> @@ -31,9 +31,17 @@
>  #define PN_PROTO_PIPE		2
>  #define PHONET_NPROTO		3
>  
> +/* Socket options for SOL_PNPIPE level */
> +#define PNPIPE_ENCAP		1
> +#define PNPIPE_IFINDEX		2
> +
>  #define PNADDR_ANY		0
>  #define PNPORT_RESOURCE_ROUTING	0
>  
> +/* Values for PNPIPE_ENCAP option */
> +#define PNPIPE_ENCAP_NONE	0
> +#define PNPIPE_ENCAP_IP		1
> +
>  /* ioctls */
>  #define SIOCPNGETOBJECT		(SIOCPROTOPRIVATE + 0)
>  
> diff --git a/include/linux/socket.h b/include/linux/socket.h
> index 818ca33..20fc4bb 100644
> --- a/include/linux/socket.h
> +++ b/include/linux/socket.h
> @@ -297,6 +297,7 @@ struct ucred {
>  #define SOL_RXRPC	272
>  #define SOL_PPPOL2TP	273
>  #define SOL_BLUETOOTH	274
> +#define SOL_PNPIPE	275
>  
>  /* IPX options */
>  #define IPX_TYPE	1
> diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h
> new file mode 100644
> index 0000000..928daf5
> --- /dev/null
> +++ b/include/net/phonet/gprs.h
> @@ -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
> diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
> index b06852b..a202adc 100644
> --- a/include/net/phonet/pep.h
> +++ b/include/net/phonet/pep.h
> @@ -35,6 +35,7 @@ struct pep_sock {
>  	struct sock		*listener;
>  	struct sk_buff_head	ctrlreq_queue;
>  #define PNPIPE_CTRLREQ_MAX	10
> +	int			ifindex;
>  	u16			peer_type;	/* peer type/subtype */
>  	u8			pipe_handle;
>  
> diff --git a/net/phonet/Makefile b/net/phonet/Makefile
> index 505df2a..d62bbba 100644
> --- a/net/phonet/Makefile
> +++ b/net/phonet/Makefile
> @@ -8,4 +8,4 @@ phonet-objs := \
>  	sysctl.o \
>  	af_phonet.o
>  
> -pn_pep-objs := pep.o
> +pn_pep-objs := pep.o pep-gprs.o
> diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
> new file mode 100644
> index 0000000..3341346
> --- /dev/null
> +++ b/net/phonet/pep-gprs.c
> @@ -0,0 +1,341 @@
> +/*
> + * 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;
> +};
> +
> +/*
> + * 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;
> +
> +	if (!pskb_may_pull(skb, 1))
> +		goto drop;
> +
> +	/* Look at IP version field */
> +	switch (skb->data[0] >> 4) {
> +	case 4:
> +		protocol = htons(ETH_P_IP);
> +		break;
> +	case 6:
> +		protocol = htons(ETH_P_IPV6);
> +		break;
> +	default:
> +		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;

Perhaps introduce gprs_type_trans() and do:

	skb->protocol = gprs_type_trans() ?

Look at:

[acme@doppio linux-2.6]$ grep _type_trans tags | cut -f  1 | sort -u
ax25_type_trans
cisco_type_trans
dvb_net_eth_type_trans
ether1394_type_trans
eth_type_trans
farsync_type_trans
fddi_type_trans
hdlc_type_trans
hippi_type_trans
isdn_net_type_trans
lan_type_trans
mpt_lan_type_trans
myri_type_trans
plip_type_trans
ppp_type_trans
raw_type_trans
tr_type_trans
x25_type_trans
[acme@doppio linux-2.6]$

> +
> +	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) {
> +			if (net_ratelimit())
> +				printk(KERN_WARNING"%s: TX error (%d)\n",
> +					dev->net->name, err);

LIMIT_NETDEBUG()

> +			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);
> +}
> diff --git a/net/phonet/pep.c b/net/phonet/pep.c
> index 9a2ed45..f0c212f 100644
> --- a/net/phonet/pep.c
> +++ b/net/phonet/pep.c
> @@ -31,6 +31,7 @@
>  #include <linux/phonet.h>
>  #include <net/phonet/phonet.h>
>  #include <net/phonet/pep.h>
> +#include <net/phonet/gprs.h>
>  
>  /* sk_state values:
>   * TCP_CLOSE		sock not in use yet
> @@ -610,6 +611,7 @@ drop:
>  static void pep_sock_close(struct sock *sk, long timeout)
>  {
>  	struct pep_sock *pn = pep_sk(sk);
> +	int ifindex = 0;
>  
>  	sk_common_release(sk);
>  
> @@ -623,7 +625,12 @@ static void pep_sock_close(struct sock *sk, long timeout)
>  			sk_del_node_init(sknode);
>  		sk->sk_state = TCP_CLOSE;
>  	}
> +	ifindex = pn->ifindex;
> +	pn->ifindex = 0;
>  	release_sock(sk);
> +
> +	if (ifindex)
> +		gprs_detach(sk);
>  }
>  
>  static int pep_wait_connreq(struct sock *sk, int noblock)
> @@ -728,12 +735,105 @@ static int pep_init(struct sock *sk)
>  	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;
> +
> +	ph = (struct pnpipehdr *)skb_push(skb, 3);

skb_reset_transport_header + pnp_hdr()?

> +	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,
>  			struct msghdr *msg, size_t len)
>  {
>  	struct pep_sock *pn = pep_sk(sk);
>  	struct sk_buff *skb = NULL;
> -	struct pnpipehdr *ph;
>  	long timeo;
>  	int flags = msg->msg_flags;
>  	int err, done;
> @@ -799,14 +899,7 @@ disabled:
>  	if (err < 0)
>  		goto out;
>  
> -	ph = (struct pnpipehdr *)skb_push(skb, 3);
> -	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);
> +	err = pipe_skb_send(sk, skb);
>  	if (err >= 0)
>  		err = len; /* success! */
>  	skb = NULL;
> @@ -817,6 +910,50 @@ out:
>  	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,
>  			struct msghdr *msg, size_t len, int noblock,
>  			int flags, int *addr_len)
> @@ -899,6 +1036,8 @@ static struct proto pep_proto = {
>  	.accept		= pep_sock_accept,
>  	.ioctl		= pep_ioctl,
>  	.init		= pep_init,
> +	.setsockopt	= pep_setsockopt,
> +	.getsockopt	= pep_getsockopt,
>  	.sendmsg	= pep_sendmsg,
>  	.recvmsg	= pep_recvmsg,
>  	.backlog_rcv	= pep_do_rcv,
> diff --git a/net/phonet/socket.c b/net/phonet/socket.c
> index a9c3d1f..d817401 100644
> --- a/net/phonet/socket.c
> +++ b/net/phonet/socket.c
> @@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = {
>  	.ioctl		= pn_socket_ioctl,
>  	.listen		= pn_socket_listen,
>  	.shutdown	= sock_no_shutdown,
> -	.setsockopt	= sock_no_setsockopt,
> -	.getsockopt	= sock_no_getsockopt,
> +	.setsockopt	= sock_common_setsockopt,
> +	.getsockopt	= sock_common_getsockopt,
>  #ifdef CONFIG_COMPAT
> -	.compat_setsockopt = sock_no_setsockopt,
> -	.compat_getsockopt = compat_sock_no_getsockopt,
> +	.compat_setsockopt = compat_sock_common_setsockopt,
> +	.compat_getsockopt = compat_sock_common_getsockopt,
>  #endif
>  	.sendmsg	= pn_socket_sendmsg,
>  	.recvmsg	= sock_common_recvmsg,
> -- 
> 1.5.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/linux/phonet.h b/include/linux/phonet.h
index f921852..c9609f9 100644
--- a/include/linux/phonet.h
+++ b/include/linux/phonet.h
@@ -31,9 +31,17 @@ 
 #define PN_PROTO_PIPE		2
 #define PHONET_NPROTO		3
 
+/* Socket options for SOL_PNPIPE level */
+#define PNPIPE_ENCAP		1
+#define PNPIPE_IFINDEX		2
+
 #define PNADDR_ANY		0
 #define PNPORT_RESOURCE_ROUTING	0
 
+/* Values for PNPIPE_ENCAP option */
+#define PNPIPE_ENCAP_NONE	0
+#define PNPIPE_ENCAP_IP		1
+
 /* ioctls */
 #define SIOCPNGETOBJECT		(SIOCPROTOPRIVATE + 0)
 
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 818ca33..20fc4bb 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -297,6 +297,7 @@  struct ucred {
 #define SOL_RXRPC	272
 #define SOL_PPPOL2TP	273
 #define SOL_BLUETOOTH	274
+#define SOL_PNPIPE	275
 
 /* IPX options */
 #define IPX_TYPE	1
diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h
new file mode 100644
index 0000000..928daf5
--- /dev/null
+++ b/include/net/phonet/gprs.h
@@ -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
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index b06852b..a202adc 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -35,6 +35,7 @@  struct pep_sock {
 	struct sock		*listener;
 	struct sk_buff_head	ctrlreq_queue;
 #define PNPIPE_CTRLREQ_MAX	10
+	int			ifindex;
 	u16			peer_type;	/* peer type/subtype */
 	u8			pipe_handle;
 
diff --git a/net/phonet/Makefile b/net/phonet/Makefile
index 505df2a..d62bbba 100644
--- a/net/phonet/Makefile
+++ b/net/phonet/Makefile
@@ -8,4 +8,4 @@  phonet-objs := \
 	sysctl.o \
 	af_phonet.o
 
-pn_pep-objs := pep.o
+pn_pep-objs := pep.o pep-gprs.o
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
new file mode 100644
index 0000000..3341346
--- /dev/null
+++ b/net/phonet/pep-gprs.c
@@ -0,0 +1,341 @@ 
+/*
+ * 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;
+};
+
+/*
+ * 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;
+
+	if (!pskb_may_pull(skb, 1))
+		goto drop;
+
+	/* Look at IP version field */
+	switch (skb->data[0] >> 4) {
+	case 4:
+		protocol = htons(ETH_P_IP);
+		break;
+	case 6:
+		protocol = htons(ETH_P_IPV6);
+		break;
+	default:
+		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) {
+			if (net_ratelimit())
+				printk(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);
+}
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 9a2ed45..f0c212f 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -31,6 +31,7 @@ 
 #include <linux/phonet.h>
 #include <net/phonet/phonet.h>
 #include <net/phonet/pep.h>
+#include <net/phonet/gprs.h>
 
 /* sk_state values:
  * TCP_CLOSE		sock not in use yet
@@ -610,6 +611,7 @@  drop:
 static void pep_sock_close(struct sock *sk, long timeout)
 {
 	struct pep_sock *pn = pep_sk(sk);
+	int ifindex = 0;
 
 	sk_common_release(sk);
 
@@ -623,7 +625,12 @@  static void pep_sock_close(struct sock *sk, long timeout)
 			sk_del_node_init(sknode);
 		sk->sk_state = TCP_CLOSE;
 	}
+	ifindex = pn->ifindex;
+	pn->ifindex = 0;
 	release_sock(sk);
+
+	if (ifindex)
+		gprs_detach(sk);
 }
 
 static int pep_wait_connreq(struct sock *sk, int noblock)
@@ -728,12 +735,105 @@  static int pep_init(struct sock *sk)
 	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;
+
+	ph = (struct pnpipehdr *)skb_push(skb, 3);
+	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,
 			struct msghdr *msg, size_t len)
 {
 	struct pep_sock *pn = pep_sk(sk);
 	struct sk_buff *skb = NULL;
-	struct pnpipehdr *ph;
 	long timeo;
 	int flags = msg->msg_flags;
 	int err, done;
@@ -799,14 +899,7 @@  disabled:
 	if (err < 0)
 		goto out;
 
-	ph = (struct pnpipehdr *)skb_push(skb, 3);
-	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);
+	err = pipe_skb_send(sk, skb);
 	if (err >= 0)
 		err = len; /* success! */
 	skb = NULL;
@@ -817,6 +910,50 @@  out:
 	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,
 			struct msghdr *msg, size_t len, int noblock,
 			int flags, int *addr_len)
@@ -899,6 +1036,8 @@  static struct proto pep_proto = {
 	.accept		= pep_sock_accept,
 	.ioctl		= pep_ioctl,
 	.init		= pep_init,
+	.setsockopt	= pep_setsockopt,
+	.getsockopt	= pep_getsockopt,
 	.sendmsg	= pep_sendmsg,
 	.recvmsg	= pep_recvmsg,
 	.backlog_rcv	= pep_do_rcv,
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index a9c3d1f..d817401 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -342,11 +342,11 @@  const struct proto_ops phonet_stream_ops = {
 	.ioctl		= pn_socket_ioctl,
 	.listen		= pn_socket_listen,
 	.shutdown	= sock_no_shutdown,
-	.setsockopt	= sock_no_setsockopt,
-	.getsockopt	= sock_no_getsockopt,
+	.setsockopt	= sock_common_setsockopt,
+	.getsockopt	= sock_common_getsockopt,
 #ifdef CONFIG_COMPAT
-	.compat_setsockopt = sock_no_setsockopt,
-	.compat_getsockopt = compat_sock_no_getsockopt,
+	.compat_setsockopt = compat_sock_common_setsockopt,
+	.compat_getsockopt = compat_sock_common_getsockopt,
 #endif
 	.sendmsg	= pn_socket_sendmsg,
 	.recvmsg	= sock_common_recvmsg,