linux/net/phonet/datagram.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * File: datagram.c
   4 *
   5 * Datagram (ISI) Phonet sockets
   6 *
   7 * Copyright (C) 2008 Nokia Corporation.
   8 *
   9 * Authors: Sakari Ailus <sakari.ailus@nokia.com>
  10 *          RĂ©mi Denis-Courmont
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/slab.h>
  15#include <linux/socket.h>
  16#include <asm/ioctls.h>
  17#include <net/sock.h>
  18
  19#include <linux/phonet.h>
  20#include <linux/export.h>
  21#include <net/phonet/phonet.h>
  22
  23static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb);
  24
  25/* associated socket ceases to exist */
  26static void pn_sock_close(struct sock *sk, long timeout)
  27{
  28        sk_common_release(sk);
  29}
  30
  31static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg)
  32{
  33        struct sk_buff *skb;
  34        int answ;
  35
  36        switch (cmd) {
  37        case SIOCINQ:
  38                lock_sock(sk);
  39                skb = skb_peek(&sk->sk_receive_queue);
  40                answ = skb ? skb->len : 0;
  41                release_sock(sk);
  42                return put_user(answ, (int __user *)arg);
  43
  44        case SIOCPNADDRESOURCE:
  45        case SIOCPNDELRESOURCE: {
  46                        u32 res;
  47                        if (get_user(res, (u32 __user *)arg))
  48                                return -EFAULT;
  49                        if (res >= 256)
  50                                return -EINVAL;
  51                        if (cmd == SIOCPNADDRESOURCE)
  52                                return pn_sock_bind_res(sk, res);
  53                        else
  54                                return pn_sock_unbind_res(sk, res);
  55                }
  56        }
  57
  58        return -ENOIOCTLCMD;
  59}
  60
  61/* Destroy socket. All references are gone. */
  62static void pn_destruct(struct sock *sk)
  63{
  64        skb_queue_purge(&sk->sk_receive_queue);
  65}
  66
  67static int pn_init(struct sock *sk)
  68{
  69        sk->sk_destruct = pn_destruct;
  70        return 0;
  71}
  72
  73static int pn_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
  74{
  75        DECLARE_SOCKADDR(struct sockaddr_pn *, target, msg->msg_name);
  76        struct sk_buff *skb;
  77        int err;
  78
  79        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|
  80                                MSG_CMSG_COMPAT))
  81                return -EOPNOTSUPP;
  82
  83        if (target == NULL)
  84                return -EDESTADDRREQ;
  85
  86        if (msg->msg_namelen < sizeof(struct sockaddr_pn))
  87                return -EINVAL;
  88
  89        if (target->spn_family != AF_PHONET)
  90                return -EAFNOSUPPORT;
  91
  92        skb = sock_alloc_send_skb(sk, MAX_PHONET_HEADER + len,
  93                                        msg->msg_flags & MSG_DONTWAIT, &err);
  94        if (skb == NULL)
  95                return err;
  96        skb_reserve(skb, MAX_PHONET_HEADER);
  97
  98        err = memcpy_from_msg((void *)skb_put(skb, len), msg, len);
  99        if (err < 0) {
 100                kfree_skb(skb);
 101                return err;
 102        }
 103
 104        /*
 105         * Fill in the Phonet header and
 106         * finally pass the packet forwards.
 107         */
 108        err = pn_skb_send(sk, skb, target);
 109
 110        /* If ok, return len. */
 111        return (err >= 0) ? len : err;
 112}
 113
 114static int pn_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 115                      int noblock, 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_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL|
 123                        MSG_CMSG_COMPAT))
 124                goto out_nofree;
 125
 126        skb = skb_recv_datagram(sk, flags, noblock, &rval);
 127        if (skb == NULL)
 128                goto out_nofree;
 129
 130        pn_skb_get_src_sockaddr(skb, &sa);
 131
 132        copylen = skb->len;
 133        if (len < copylen) {
 134                msg->msg_flags |= MSG_TRUNC;
 135                copylen = len;
 136        }
 137
 138        rval = skb_copy_datagram_msg(skb, 0, msg, copylen);
 139        if (rval) {
 140                rval = -EFAULT;
 141                goto out;
 142        }
 143
 144        rval = (flags & MSG_TRUNC) ? skb->len : copylen;
 145
 146        if (msg->msg_name != NULL) {
 147                __sockaddr_check_size(sizeof(sa));
 148                memcpy(msg->msg_name, &sa, sizeof(sa));
 149                *addr_len = sizeof(sa);
 150        }
 151
 152out:
 153        skb_free_datagram(sk, skb);
 154
 155out_nofree:
 156        return rval;
 157}
 158
 159/* Queue an skb for a sock. */
 160static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 161{
 162        int err = sock_queue_rcv_skb(sk, skb);
 163
 164        if (err < 0)
 165                kfree_skb(skb);
 166        return err ? NET_RX_DROP : NET_RX_SUCCESS;
 167}
 168
 169/* Module registration */
 170static struct proto pn_proto = {
 171        .close          = pn_sock_close,
 172        .ioctl          = pn_ioctl,
 173        .init           = pn_init,
 174        .sendmsg        = pn_sendmsg,
 175        .recvmsg        = pn_recvmsg,
 176        .backlog_rcv    = pn_backlog_rcv,
 177        .hash           = pn_sock_hash,
 178        .unhash         = pn_sock_unhash,
 179        .get_port       = pn_sock_get_port,
 180        .obj_size       = sizeof(struct pn_sock),
 181        .owner          = THIS_MODULE,
 182        .name           = "PHONET",
 183};
 184
 185static const struct phonet_protocol pn_dgram_proto = {
 186        .ops            = &phonet_dgram_ops,
 187        .prot           = &pn_proto,
 188        .sock_type      = SOCK_DGRAM,
 189};
 190
 191int __init isi_register(void)
 192{
 193        return phonet_proto_register(PN_PROTO_PHONET, &pn_dgram_proto);
 194}
 195
 196void __exit isi_unregister(void)
 197{
 198        phonet_proto_unregister(PN_PROTO_PHONET, &pn_dgram_proto);
 199}
 200