at v3.7 270 lines 5.6 kB view raw
1/* 2 * Raw IEEE 802.15.4 sockets 3 * 4 * Copyright 2007, 2008 Siemens AG 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 8 * as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Written by: 20 * Sergey Lapin <slapin@ossfans.org> 21 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 22 */ 23 24#include <linux/net.h> 25#include <linux/module.h> 26#include <linux/if_arp.h> 27#include <linux/list.h> 28#include <linux/slab.h> 29#include <net/sock.h> 30#include <net/af_ieee802154.h> 31 32#include "af802154.h" 33 34static HLIST_HEAD(raw_head); 35static DEFINE_RWLOCK(raw_lock); 36 37static void raw_hash(struct sock *sk) 38{ 39 write_lock_bh(&raw_lock); 40 sk_add_node(sk, &raw_head); 41 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 42 write_unlock_bh(&raw_lock); 43} 44 45static void raw_unhash(struct sock *sk) 46{ 47 write_lock_bh(&raw_lock); 48 if (sk_del_node_init(sk)) 49 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 50 write_unlock_bh(&raw_lock); 51} 52 53static void raw_close(struct sock *sk, long timeout) 54{ 55 sk_common_release(sk); 56} 57 58static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len) 59{ 60 struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; 61 int err = 0; 62 struct net_device *dev = NULL; 63 64 if (len < sizeof(*addr)) 65 return -EINVAL; 66 67 if (addr->family != AF_IEEE802154) 68 return -EINVAL; 69 70 lock_sock(sk); 71 72 dev = ieee802154_get_dev(sock_net(sk), &addr->addr); 73 if (!dev) { 74 err = -ENODEV; 75 goto out; 76 } 77 78 if (dev->type != ARPHRD_IEEE802154) { 79 err = -ENODEV; 80 goto out_put; 81 } 82 83 sk->sk_bound_dev_if = dev->ifindex; 84 sk_dst_reset(sk); 85 86out_put: 87 dev_put(dev); 88out: 89 release_sock(sk); 90 91 return err; 92} 93 94static int raw_connect(struct sock *sk, struct sockaddr *uaddr, 95 int addr_len) 96{ 97 return -ENOTSUPP; 98} 99 100static int raw_disconnect(struct sock *sk, int flags) 101{ 102 return 0; 103} 104 105static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 106 size_t size) 107{ 108 struct net_device *dev; 109 unsigned int mtu; 110 struct sk_buff *skb; 111 int hlen, tlen; 112 int err; 113 114 if (msg->msg_flags & MSG_OOB) { 115 pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); 116 return -EOPNOTSUPP; 117 } 118 119 lock_sock(sk); 120 if (!sk->sk_bound_dev_if) 121 dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); 122 else 123 dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); 124 release_sock(sk); 125 126 if (!dev) { 127 pr_debug("no dev\n"); 128 err = -ENXIO; 129 goto out; 130 } 131 132 mtu = dev->mtu; 133 pr_debug("name = %s, mtu = %u\n", dev->name, mtu); 134 135 if (size > mtu) { 136 pr_debug("size = %Zu, mtu = %u\n", size, mtu); 137 err = -EINVAL; 138 goto out_dev; 139 } 140 141 hlen = LL_RESERVED_SPACE(dev); 142 tlen = dev->needed_tailroom; 143 skb = sock_alloc_send_skb(sk, hlen + tlen + size, 144 msg->msg_flags & MSG_DONTWAIT, &err); 145 if (!skb) 146 goto out_dev; 147 148 skb_reserve(skb, hlen); 149 150 skb_reset_mac_header(skb); 151 skb_reset_network_header(skb); 152 153 err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); 154 if (err < 0) 155 goto out_skb; 156 157 skb->dev = dev; 158 skb->sk = sk; 159 skb->protocol = htons(ETH_P_IEEE802154); 160 161 dev_put(dev); 162 163 err = dev_queue_xmit(skb); 164 if (err > 0) 165 err = net_xmit_errno(err); 166 167 return err ?: size; 168 169out_skb: 170 kfree_skb(skb); 171out_dev: 172 dev_put(dev); 173out: 174 return err; 175} 176 177static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 178 size_t len, int noblock, int flags, int *addr_len) 179{ 180 size_t copied = 0; 181 int err = -EOPNOTSUPP; 182 struct sk_buff *skb; 183 184 skb = skb_recv_datagram(sk, flags, noblock, &err); 185 if (!skb) 186 goto out; 187 188 copied = skb->len; 189 if (len < copied) { 190 msg->msg_flags |= MSG_TRUNC; 191 copied = len; 192 } 193 194 err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); 195 if (err) 196 goto done; 197 198 sock_recv_ts_and_drops(msg, sk, skb); 199 200 if (flags & MSG_TRUNC) 201 copied = skb->len; 202done: 203 skb_free_datagram(sk, skb); 204out: 205 if (err) 206 return err; 207 return copied; 208} 209 210static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) 211{ 212 if (sock_queue_rcv_skb(sk, skb) < 0) { 213 kfree_skb(skb); 214 return NET_RX_DROP; 215 } 216 217 return NET_RX_SUCCESS; 218} 219 220 221void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) 222{ 223 struct sock *sk; 224 struct hlist_node *node; 225 226 read_lock(&raw_lock); 227 sk_for_each(sk, node, &raw_head) { 228 bh_lock_sock(sk); 229 if (!sk->sk_bound_dev_if || 230 sk->sk_bound_dev_if == dev->ifindex) { 231 232 struct sk_buff *clone; 233 234 clone = skb_clone(skb, GFP_ATOMIC); 235 if (clone) 236 raw_rcv_skb(sk, clone); 237 } 238 bh_unlock_sock(sk); 239 } 240 read_unlock(&raw_lock); 241} 242 243static int raw_getsockopt(struct sock *sk, int level, int optname, 244 char __user *optval, int __user *optlen) 245{ 246 return -EOPNOTSUPP; 247} 248 249static int raw_setsockopt(struct sock *sk, int level, int optname, 250 char __user *optval, unsigned int optlen) 251{ 252 return -EOPNOTSUPP; 253} 254 255struct proto ieee802154_raw_prot = { 256 .name = "IEEE-802.15.4-RAW", 257 .owner = THIS_MODULE, 258 .obj_size = sizeof(struct sock), 259 .close = raw_close, 260 .bind = raw_bind, 261 .sendmsg = raw_sendmsg, 262 .recvmsg = raw_recvmsg, 263 .hash = raw_hash, 264 .unhash = raw_unhash, 265 .connect = raw_connect, 266 .disconnect = raw_disconnect, 267 .getsockopt = raw_getsockopt, 268 .setsockopt = raw_setsockopt, 269}; 270