linux/net/phonet/datagram.c
<<
>>
Prefs
   1/*
   2 * File: datagram.c
   3 *
   4 * Datagram (ISI) Phonet sockets
   5 *
   6 * Copyright (C) 2008 Nokia Corporation.
   7 *
   8 * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
   9 * Original author: Sakari Ailus <sakari.ailus@nokia.com>
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * version 2 as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  23 * 02110-1301 USA
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/socket.h>
  28#include <asm/ioctls.h>
  29#include <net/sock.h>
  30
  31#include <linux/phonet.h>
  32#include <net/phonet/phonet.h>
  33
  34static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb);
  35
  36/* associated socket ceases to exist */
  37static void pn_sock_close(struct sock *sk, long timeout)
  38{
  39        sk_common_release(sk);
  40}
  41
  42static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg)
  43{
  44        struct sk_buff *skb;
  45        int answ;
  46
  47        switch (cmd) {
  48        case SIOCINQ:
  49                lock_sock(sk);
  50                skb = skb_peek(&sk->sk_receive_queue);
  51                answ = skb ? skb->len : 0;
  52                release_sock(sk);
  53                return put_user(answ, (int __user *)arg);
  54        }
  55
  56        return -ENOIOCTLCMD;
  57}
  58
  59/* Destroy socket. All references are gone. */
  60static void pn_destruct(struct sock *sk)
  61{
  62        skb_queue_purge(&sk->sk_receive_queue);
  63}
  64
  65static int pn_init(struct sock *sk)
  66{
  67        sk->sk_destruct = pn_destruct;
  68        return 0;
  69}
  70
  71static int pn_sendmsg(struct kiocb *iocb, struct sock *sk,
  72                        struct msghdr *msg, size_t len)
  73{
  74        struct sockaddr_pn *target;
  75        struct sk_buff *skb;
  76        int err;
  77
  78        if (msg->msg_flags & MSG_OOB)
  79                return -EOPNOTSUPP;
  80
  81        if (msg->msg_name == NULL)
  82                return -EDESTADDRREQ;
  83
  84        if (msg->msg_namelen < sizeof(struct sockaddr_pn))
  85                return -EINVAL;
  86
  87        target = (struct sockaddr_pn *)msg->msg_name;
  88        if (target->spn_family != AF_PHONET)
  89                return -EAFNOSUPPORT;
  90
  91        skb = sock_alloc_send_skb(sk, MAX_PHONET_HEADER + len,
  92                                        msg->msg_flags & MSG_DONTWAIT, &err);
  93        if (skb == NULL)
  94                return err;
  95        skb_reserve(skb, MAX_PHONET_HEADER);
  96
  97        err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len);
  98        if (err < 0) {
  99                kfree_skb(skb);
 100                return err;
 101        }
 102
 103        /*
 104         * Fill in the Phonet header and
 105         * finally pass the packet forwards.
 106         */
 107        err = pn_skb_send(sk, skb, target);
 108
 109        /* If ok, return len. */
 110        return (err >= 0) ? len : err;
 111}
 112
 113static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
 114                        struct msghdr *msg, size_t len, int noblock,
 115                        int flags, int *addr_len)
 116{
 117        struct sk_buff *skb = NULL;
 118        struct sockaddr_pn sa;
 119        int rval = -EOPNOTSUPP;
 120        int copylen;
 121
 122        if (flags & MSG_OOB)
 123                goto out_nofree;
 124
 125        if (addr_len)
 126                *addr_len = sizeof(sa);
 127
 128        skb = skb_recv_datagram(sk, flags, noblock, &rval);
 129        if (skb == NULL)
 130                goto out_nofree;
 131
 132        pn_skb_get_src_sockaddr(skb, &sa);
 133
 134        copylen = skb->len;
 135        if (len < copylen) {
 136                msg->msg_flags |= MSG_TRUNC;
 137                copylen = len;
 138        }
 139
 140        rval = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copylen);
 141        if (rval) {
 142                rval = -EFAULT;
 143                goto out;
 144        }
 145
 146        rval = (flags & MSG_TRUNC) ? skb->len : copylen;
 147
 148        if (msg->msg_name != NULL)
 149                memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn));
 150
 151out:
 152        skb_free_datagram(sk, skb);
 153
 154out_nofree:
 155        return rval;
 156}
 157
 158/* Queue an skb for a sock. */
 159static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 160{
 161        int err = sock_queue_rcv_skb(sk, skb);
 162        if (err < 0) {
 163                kfree_skb(skb);
 164                if (err == -ENOMEM)
 165                        atomic_inc(&sk->sk_drops);
 166        }
 167        return err ? NET_RX_DROP : NET_RX_SUCCESS;
 168}
 169
 170/* Module registration */
 171static struct proto pn_proto = {
 172        .close          = pn_sock_close,
 173        .ioctl          = pn_ioctl,
 174        .init           = pn_init,
 175        .sendmsg        = pn_sendmsg,
 176        .recvmsg        = pn_recvmsg,
 177        .backlog_rcv    = pn_backlog_rcv,
 178        .hash           = pn_sock_hash,
 179        .unhash         = pn_sock_unhash,
 180        .get_port       = pn_sock_get_port,
 181        .obj_size       = sizeof(struct pn_sock),
 182        .owner          = THIS_MODULE,
 183        .name           = "PHONET",
 184};
 185
 186static struct phonet_protocol pn_dgram_proto = {
 187        .ops            = &phonet_dgram_ops,
 188        .prot           = &pn_proto,
 189        .sock_type      = SOCK_DGRAM,
 190};
 191
 192int __init isi_register(void)
 193{
 194        return phonet_proto_register(PN_PROTO_PHONET, &pn_dgram_proto);
 195}
 196
 197void __exit isi_unregister(void)
 198{
 199        phonet_proto_unregister(PN_PROTO_PHONET, &pn_dgram_proto);
 200}
 201