Blame view
net/phonet/socket.c
17.8 KB
ba113a94b
|
1 2 3 4 5 6 7 |
/* * File: socket.c * * Phonet sockets * * Copyright (C) 2008 Nokia Corporation. * |
31fdc5553
|
8 9 |
* Authors: Sakari Ailus <sakari.ailus@nokia.com> * Rémi Denis-Courmont |
ba113a94b
|
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
* * 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 */ |
5a0e3ad6a
|
25 |
#include <linux/gfp.h> |
ba113a94b
|
26 27 |
#include <linux/kernel.h> #include <linux/net.h> |
9995a32b4
|
28 |
#include <linux/poll.h> |
ba113a94b
|
29 30 31 32 |
#include <net/sock.h> #include <net/tcp_states.h> #include <linux/phonet.h> |
bc3b2d7fb
|
33 |
#include <linux/export.h> |
ba113a94b
|
34 |
#include <net/phonet/phonet.h> |
9995a32b4
|
35 |
#include <net/phonet/pep.h> |
ba113a94b
|
36 37 38 39 40 41 42 43 44 45 46 47 |
#include <net/phonet/pn_dev.h> static int pn_socket_release(struct socket *sock) { struct sock *sk = sock->sk; if (sk) { sock->sk = NULL; sk->sk_prot->close(sk, 0); } return 0; } |
6b0d07ba1
|
48 49 |
#define PN_HASHSIZE 16 #define PN_HASHMASK (PN_HASHSIZE-1) |
ba113a94b
|
50 |
static struct { |
6b0d07ba1
|
51 |
struct hlist_head hlist[PN_HASHSIZE]; |
44f4d5a27
|
52 |
struct mutex lock; |
6b0d07ba1
|
53 54 55 56 |
} pnsocks; void __init pn_sock_init(void) { |
95c961747
|
57 |
unsigned int i; |
6b0d07ba1
|
58 59 60 |
for (i = 0; i < PN_HASHSIZE; i++) INIT_HLIST_HEAD(pnsocks.hlist + i); |
44f4d5a27
|
61 |
mutex_init(&pnsocks.lock); |
6b0d07ba1
|
62 63 64 65 66 67 |
} static struct hlist_head *pn_hash_list(u16 obj) { return pnsocks.hlist + (obj & PN_HASHMASK); } |
ba113a94b
|
68 69 70 71 72 |
/* * Find address based on socket address, match only certain fields. * Also grab sock if it was found. Remember to sock_put it later. */ |
524048819
|
73 |
struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn) |
ba113a94b
|
74 |
{ |
ba113a94b
|
75 76 77 78 |
struct sock *sknode; struct sock *rval = NULL; u16 obj = pn_sockaddr_get_object(spn); u8 res = spn->spn_resource; |
6b0d07ba1
|
79 |
struct hlist_head *hlist = pn_hash_list(obj); |
ba113a94b
|
80 |
|
44f4d5a27
|
81 |
rcu_read_lock(); |
b67bfe0d4
|
82 |
sk_for_each_rcu(sknode, hlist) { |
ba113a94b
|
83 84 |
struct pn_sock *pn = pn_sk(sknode); BUG_ON(!pn->sobject); /* unbound socket */ |
524048819
|
85 86 |
if (!net_eq(sock_net(sknode), net)) continue; |
ba113a94b
|
87 88 89 90 91 92 93 94 95 |
if (pn_port(obj)) { /* Look up socket by port */ if (pn_port(pn->sobject) != pn_port(obj)) continue; } else { /* If port is zero, look up by resource */ if (pn->resource != res) continue; } |
f64f9e719
|
96 97 |
if (pn_addr(pn->sobject) && pn_addr(pn->sobject) != pn_addr(obj)) |
ba113a94b
|
98 99 100 101 102 103 |
continue; rval = sknode; sock_hold(sknode); break; } |
44f4d5a27
|
104 |
rcu_read_unlock(); |
ba113a94b
|
105 106 |
return rval; |
f14001fcd
|
107 108 109 110 111 |
} /* Deliver a broadcast packet (only in bottom-half) */ void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb) { |
6b0d07ba1
|
112 |
struct hlist_head *hlist = pnsocks.hlist; |
95c961747
|
113 |
unsigned int h; |
f14001fcd
|
114 |
|
44f4d5a27
|
115 |
rcu_read_lock(); |
6b0d07ba1
|
116 |
for (h = 0; h < PN_HASHSIZE; h++) { |
6b0d07ba1
|
117 |
struct sock *sknode; |
f14001fcd
|
118 |
|
b67bfe0d4
|
119 |
sk_for_each(sknode, hlist) { |
6b0d07ba1
|
120 |
struct sk_buff *clone; |
ba113a94b
|
121 |
|
6b0d07ba1
|
122 123 124 125 126 127 128 129 130 131 |
if (!net_eq(sock_net(sknode), net)) continue; if (!sock_flag(sknode, SOCK_BROADCAST)) continue; clone = skb_clone(skb, GFP_ATOMIC); if (clone) { sock_hold(sknode); sk_receive_skb(sknode, clone, 0); } |
21912d1ca
|
132 |
} |
6b0d07ba1
|
133 |
hlist++; |
f14001fcd
|
134 |
} |
44f4d5a27
|
135 |
rcu_read_unlock(); |
ba113a94b
|
136 137 138 139 |
} void pn_sock_hash(struct sock *sk) { |
6b0d07ba1
|
140 |
struct hlist_head *hlist = pn_hash_list(pn_sk(sk)->sobject); |
44f4d5a27
|
141 142 143 |
mutex_lock(&pnsocks.lock); sk_add_node_rcu(sk, hlist); mutex_unlock(&pnsocks.lock); |
ba113a94b
|
144 145 146 147 148 |
} EXPORT_SYMBOL(pn_sock_hash); void pn_sock_unhash(struct sock *sk) { |
44f4d5a27
|
149 150 151 |
mutex_lock(&pnsocks.lock); sk_del_node_init_rcu(sk); mutex_unlock(&pnsocks.lock); |
7417fa83c
|
152 |
pn_sock_unbind_all_res(sk); |
44f4d5a27
|
153 |
synchronize_rcu(); |
ba113a94b
|
154 155 |
} EXPORT_SYMBOL(pn_sock_unhash); |
582b0b611
|
156 |
static DEFINE_MUTEX(port_mutex); |
ba113a94b
|
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len) { struct sock *sk = sock->sk; struct pn_sock *pn = pn_sk(sk); struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; int err; u16 handle; u8 saddr; if (sk->sk_prot->bind) return sk->sk_prot->bind(sk, addr, len); if (len < sizeof(struct sockaddr_pn)) return -EINVAL; if (spn->spn_family != AF_PHONET) return -EAFNOSUPPORT; handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr); saddr = pn_addr(handle); |
524048819
|
176 |
if (saddr && phonet_address_lookup(sock_net(sk), saddr)) |
ba113a94b
|
177 178 179 180 181 182 183 |
return -EADDRNOTAVAIL; lock_sock(sk); if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) { err = -EINVAL; /* attempt to rebind */ goto out; } |
582b0b611
|
184 185 |
WARN_ON(sk_hashed(sk)); mutex_lock(&port_mutex); |
ba113a94b
|
186 187 |
err = sk->sk_prot->get_port(sk, pn_port(handle)); if (err) |
582b0b611
|
188 |
goto out_port; |
ba113a94b
|
189 190 191 192 193 194 195 |
/* get_port() sets the port, bind() sets the address if applicable */ pn->sobject = pn_object(saddr, pn_port(pn->sobject)); pn->resource = spn->spn_resource; /* Enable RX on the socket */ sk->sk_prot->hash(sk); |
582b0b611
|
196 197 |
out_port: mutex_unlock(&port_mutex); |
ba113a94b
|
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
out: release_sock(sk); return err; } static int pn_socket_autobind(struct socket *sock) { struct sockaddr_pn sa; int err; memset(&sa, 0, sizeof(sa)); sa.spn_family = AF_PHONET; err = pn_socket_bind(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr_pn)); if (err != -EINVAL) return err; BUG_ON(!pn_port(pn_sk(sock->sk)->sobject)); return 0; /* socket was already bound */ } |
b3d625538
|
217 218 219 220 |
static int pn_socket_connect(struct socket *sock, struct sockaddr *addr, int len, int flags) { struct sock *sk = sock->sk; |
297edb600
|
221 |
struct pn_sock *pn = pn_sk(sk); |
b3d625538
|
222 |
struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; |
297edb600
|
223 224 |
struct task_struct *tsk = current; long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); |
b3d625538
|
225 |
int err; |
297edb600
|
226 227 |
if (pn_socket_autobind(sock)) return -ENOBUFS; |
b3d625538
|
228 229 230 231 232 233 234 235 236 |
if (len < sizeof(struct sockaddr_pn)) return -EINVAL; if (spn->spn_family != AF_PHONET) return -EAFNOSUPPORT; lock_sock(sk); switch (sock->state) { case SS_UNCONNECTED: |
297edb600
|
237 |
if (sk->sk_state != TCP_CLOSE) { |
b3d625538
|
238 239 |
err = -EISCONN; goto out; |
b3d625538
|
240 241 |
} break; |
297edb600
|
242 243 244 245 246 247 |
case SS_CONNECTING: err = -EALREADY; goto out; default: err = -EISCONN; goto out; |
b3d625538
|
248 |
} |
b3d625538
|
249 |
|
297edb600
|
250 251 |
pn->dobject = pn_sockaddr_get_object(spn); pn->resource = pn_sockaddr_get_resource(spn); |
b3d625538
|
252 |
sock->state = SS_CONNECTING; |
297edb600
|
253 |
|
b3d625538
|
254 |
err = sk->sk_prot->connect(sk, addr, len); |
297edb600
|
255 |
if (err) { |
b3d625538
|
256 |
sock->state = SS_UNCONNECTED; |
297edb600
|
257 |
pn->dobject = 0; |
b3d625538
|
258 259 |
goto out; } |
297edb600
|
260 261 |
while (sk->sk_state == TCP_SYN_SENT) { DEFINE_WAIT(wait); |
b3d625538
|
262 |
|
297edb600
|
263 264 265 266 267 268 269 270 |
if (!timeo) { err = -EINPROGRESS; goto out; } if (signal_pending(tsk)) { err = sock_intr_errno(timeo); goto out; } |
b3d625538
|
271 |
|
297edb600
|
272 273 274 275 276 277 |
prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); finish_wait(sk_sleep(sk), &wait); |
b3d625538
|
278 |
} |
b3d625538
|
279 |
|
297edb600
|
280 281 282 283 284 285 286 |
if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) err = 0; else if (sk->sk_state == TCP_CLOSE_WAIT) err = -ECONNRESET; else err = -ECONNREFUSED; sock->state = err ? SS_UNCONNECTED : SS_CONNECTED; |
b3d625538
|
287 288 289 290 |
out: release_sock(sk); return err; } |
b3d625538
|
291 |
|
9995a32b4
|
292 293 294 295 296 297 |
static int pn_socket_accept(struct socket *sock, struct socket *newsock, int flags) { struct sock *sk = sock->sk; struct sock *newsk; int err; |
f7ae8d59f
|
298 299 |
if (unlikely(sk->sk_state != TCP_LISTEN)) return -EINVAL; |
9995a32b4
|
300 301 302 303 304 305 306 307 308 309 |
newsk = sk->sk_prot->accept(sk, flags, &err); if (!newsk) return err; lock_sock(newsk); sock_graft(newsk, newsock); newsock->state = SS_CONNECTED; release_sock(newsk); return 0; } |
ba113a94b
|
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
static int pn_socket_getname(struct socket *sock, struct sockaddr *addr, int *sockaddr_len, int peer) { struct sock *sk = sock->sk; struct pn_sock *pn = pn_sk(sk); memset(addr, 0, sizeof(struct sockaddr_pn)); addr->sa_family = AF_PHONET; if (!peer) /* Race with bind() here is userland's problem. */ pn_sockaddr_set_object((struct sockaddr_pn *)addr, pn->sobject); *sockaddr_len = sizeof(struct sockaddr_pn); return 0; } |
9995a32b4
|
325 326 327 328 329 330 |
static unsigned int pn_socket_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; struct pep_sock *pn = pep_sk(sk); unsigned int mask = 0; |
438154823
|
331 |
poll_wait(file, sk_sleep(sk), wait); |
9995a32b4
|
332 |
|
f7ae8d59f
|
333 |
if (sk->sk_state == TCP_CLOSE) |
9995a32b4
|
334 |
return POLLERR; |
9995a32b4
|
335 336 |
if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; |
c41bd97f8
|
337 338 339 |
if (!skb_queue_empty(&pn->ctrlreq_queue)) mask |= POLLPRI; if (!mask && sk->sk_state == TCP_CLOSE_WAIT) |
9995a32b4
|
340 |
return POLLHUP; |
01b38606b
|
341 342 343 |
if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf && atomic_read(&pn->tx_credits)) |
9995a32b4
|
344 345 346 347 |
mask |= POLLOUT | POLLWRNORM | POLLWRBAND; return mask; } |
ba113a94b
|
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
static int pn_socket_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; struct pn_sock *pn = pn_sk(sk); if (cmd == SIOCPNGETOBJECT) { struct net_device *dev; u16 handle; u8 saddr; if (get_user(handle, (__u16 __user *)arg)) return -EFAULT; lock_sock(sk); if (sk->sk_bound_dev_if) dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); else dev = phonet_device_get(sock_net(sk)); if (dev && (dev->flags & IFF_UP)) saddr = phonet_address_get(dev, pn_addr(handle)); else saddr = PN_NO_ADDR; release_sock(sk); if (dev) dev_put(dev); if (saddr == PN_NO_ADDR) return -EHOSTUNREACH; handle = pn_object(saddr, pn_port(pn->sobject)); return put_user(handle, (__u16 __user *)arg); } return sk->sk_prot->ioctl(sk, cmd, arg); } |
9995a32b4
|
385 386 387 388 |
static int pn_socket_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; int err = 0; |
9995a32b4
|
389 390 391 392 |
if (pn_socket_autobind(sock)) return -ENOBUFS; lock_sock(sk); |
96241544c
|
393 |
if (sock->state != SS_UNCONNECTED) { |
9995a32b4
|
394 395 396 |
err = -EINVAL; goto out; } |
96241544c
|
397 398 399 400 |
if (sk->sk_state != TCP_LISTEN) { sk->sk_state = TCP_LISTEN; sk->sk_ack_backlog = 0; } |
9995a32b4
|
401 402 403 404 405 |
sk->sk_max_ack_backlog = backlog; out: release_sock(sk); return err; } |
ba113a94b
|
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len) { struct sock *sk = sock->sk; if (pn_socket_autobind(sock)) return -EAGAIN; return sk->sk_prot->sendmsg(iocb, sk, m, total_len); } const struct proto_ops phonet_dgram_ops = { .family = AF_PHONET, .owner = THIS_MODULE, .release = pn_socket_release, .bind = pn_socket_bind, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pn_socket_getname, .poll = datagram_poll, .ioctl = pn_socket_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, #ifdef CONFIG_COMPAT .compat_setsockopt = sock_no_setsockopt, .compat_getsockopt = sock_no_getsockopt, #endif .sendmsg = pn_socket_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; |
9995a32b4
|
441 442 443 444 445 |
const struct proto_ops phonet_stream_ops = { .family = AF_PHONET, .owner = THIS_MODULE, .release = pn_socket_release, .bind = pn_socket_bind, |
b3d625538
|
446 |
.connect = pn_socket_connect, |
9995a32b4
|
447 448 449 450 451 452 453 |
.socketpair = sock_no_socketpair, .accept = pn_socket_accept, .getname = pn_socket_getname, .poll = pn_socket_poll, .ioctl = pn_socket_ioctl, .listen = pn_socket_listen, .shutdown = sock_no_shutdown, |
02a47617c
|
454 455 |
.setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, |
9995a32b4
|
456 |
#ifdef CONFIG_COMPAT |
02a47617c
|
457 458 |
.compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, |
9995a32b4
|
459 460 461 462 463 464 465 |
#endif .sendmsg = pn_socket_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; EXPORT_SYMBOL(phonet_stream_ops); |
ba113a94b
|
466 467 468 469 |
/* allocate port for a socket */ int pn_sock_get_port(struct sock *sk, unsigned short sport) { static int port_cur; |
524048819
|
470 |
struct net *net = sock_net(sk); |
ba113a94b
|
471 472 473 474 475 476 |
struct pn_sock *pn = pn_sk(sk); struct sockaddr_pn try_sa; struct sock *tmpsk; memset(&try_sa, 0, sizeof(struct sockaddr_pn)); try_sa.spn_family = AF_PHONET; |
582b0b611
|
477 |
WARN_ON(!mutex_is_locked(&port_mutex)); |
ba113a94b
|
478 479 |
if (!sport) { /* search free port */ |
87ab4e20b
|
480 |
int port, pmin, pmax; |
ba113a94b
|
481 |
|
87ab4e20b
|
482 |
phonet_get_local_port_range(&pmin, &pmax); |
ba113a94b
|
483 484 485 486 487 488 |
for (port = pmin; port <= pmax; port++) { port_cur++; if (port_cur < pmin || port_cur > pmax) port_cur = pmin; pn_sockaddr_set_port(&try_sa, port_cur); |
524048819
|
489 |
tmpsk = pn_find_sock_by_sa(net, &try_sa); |
ba113a94b
|
490 491 492 493 494 495 496 497 498 |
if (tmpsk == NULL) { sport = port_cur; goto found; } else sock_put(tmpsk); } } else { /* try to find specific port */ pn_sockaddr_set_port(&try_sa, sport); |
524048819
|
499 |
tmpsk = pn_find_sock_by_sa(net, &try_sa); |
ba113a94b
|
500 501 502 503 504 505 |
if (tmpsk == NULL) /* No sock there! We can use that port... */ goto found; else sock_put(tmpsk); } |
ba113a94b
|
506 507 508 509 |
/* the port must be in use already */ return -EADDRINUSE; found: |
ba113a94b
|
510 511 512 513 |
pn->sobject = pn_object(pn_addr(pn->sobject), sport); return 0; } EXPORT_SYMBOL(pn_sock_get_port); |
c1dc13e9d
|
514 |
|
ae6e2aef6
|
515 |
#ifdef CONFIG_PROC_FS |
c1dc13e9d
|
516 517 518 |
static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos) { struct net *net = seq_file_net(seq); |
6b0d07ba1
|
519 |
struct hlist_head *hlist = pnsocks.hlist; |
c1dc13e9d
|
520 |
struct sock *sknode; |
95c961747
|
521 |
unsigned int h; |
c1dc13e9d
|
522 |
|
6b0d07ba1
|
523 |
for (h = 0; h < PN_HASHSIZE; h++) { |
b67bfe0d4
|
524 |
sk_for_each_rcu(sknode, hlist) { |
6b0d07ba1
|
525 526 527 528 529 530 531 |
if (!net_eq(net, sock_net(sknode))) continue; if (!pos) return sknode; pos--; } hlist++; |
c1dc13e9d
|
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
} return NULL; } static struct sock *pn_sock_get_next(struct seq_file *seq, struct sock *sk) { struct net *net = seq_file_net(seq); do sk = sk_next(sk); while (sk && !net_eq(net, sock_net(sk))); return sk; } static void *pn_sock_seq_start(struct seq_file *seq, loff_t *pos) |
44f4d5a27
|
548 |
__acquires(rcu) |
c1dc13e9d
|
549 |
{ |
44f4d5a27
|
550 |
rcu_read_lock(); |
c1dc13e9d
|
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
return *pos ? pn_sock_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; } static void *pn_sock_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct sock *sk; if (v == SEQ_START_TOKEN) sk = pn_sock_get_idx(seq, 0); else sk = pn_sock_get_next(seq, v); (*pos)++; return sk; } static void pn_sock_seq_stop(struct seq_file *seq, void *v) |
44f4d5a27
|
567 |
__releases(rcu) |
c1dc13e9d
|
568 |
{ |
44f4d5a27
|
569 |
rcu_read_unlock(); |
c1dc13e9d
|
570 571 572 573 |
} static int pn_sock_seq_show(struct seq_file *seq, void *v) { |
652586df9
|
574 |
seq_setwidth(seq, 127); |
c1dc13e9d
|
575 |
if (v == SEQ_START_TOKEN) |
652586df9
|
576 577 |
seq_puts(seq, "pt loc rem rs st tx_queue rx_queue " " uid inode ref pointer drops"); |
c1dc13e9d
|
578 579 580 581 582 |
else { struct sock *sk = v; struct pn_sock *pn = pn_sk(sk); seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu " |
652586df9
|
583 |
"%d %pK %d", |
a8059512b
|
584 585 |
sk->sk_protocol, pn->sobject, pn->dobject, pn->resource, sk->sk_state, |
c1dc13e9d
|
586 |
sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), |
a7cb5a49b
|
587 588 |
from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), sock_i_ino(sk), |
c1dc13e9d
|
589 |
atomic_read(&sk->sk_refcnt), sk, |
652586df9
|
590 |
atomic_read(&sk->sk_drops)); |
c1dc13e9d
|
591 |
} |
652586df9
|
592 593 |
seq_pad(seq, ' '); |
c1dc13e9d
|
594 595 596 597 598 599 600 601 602 603 604 605 |
return 0; } static const struct seq_operations pn_sock_seq_ops = { .start = pn_sock_seq_start, .next = pn_sock_seq_next, .stop = pn_sock_seq_stop, .show = pn_sock_seq_show, }; static int pn_sock_open(struct inode *inode, struct file *file) { |
cb7d9e7f9
|
606 607 |
return seq_open_net(inode, file, &pn_sock_seq_ops, sizeof(struct seq_net_private)); |
c1dc13e9d
|
608 609 610 611 612 613 614 |
} const struct file_operations pn_sock_seq_fops = { .owner = THIS_MODULE, .open = pn_sock_open, .read = seq_read, .llseek = seq_lseek, |
cb7d9e7f9
|
615 |
.release = seq_release_net, |
c1dc13e9d
|
616 |
}; |
ae6e2aef6
|
617 |
#endif |
4e3d16ce5
|
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
static struct { struct sock *sk[256]; } pnres; /* * Find and hold socket based on resource. */ struct sock *pn_find_sock_by_res(struct net *net, u8 res) { struct sock *sk; if (!net_eq(net, &init_net)) return NULL; rcu_read_lock(); sk = rcu_dereference(pnres.sk[res]); if (sk) sock_hold(sk); rcu_read_unlock(); return sk; } static DEFINE_MUTEX(resource_mutex); int pn_sock_bind_res(struct sock *sk, u8 res) { int ret = -EADDRINUSE; if (!net_eq(sock_net(sk), &init_net)) return -ENOIOCTLCMD; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (pn_socket_autobind(sk->sk_socket)) return -EAGAIN; mutex_lock(&resource_mutex); if (pnres.sk[res] == NULL) { sock_hold(sk); |
cf778b00e
|
657 |
rcu_assign_pointer(pnres.sk[res], sk); |
4e3d16ce5
|
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
ret = 0; } mutex_unlock(&resource_mutex); return ret; } int pn_sock_unbind_res(struct sock *sk, u8 res) { int ret = -ENOENT; if (!capable(CAP_SYS_ADMIN)) return -EPERM; mutex_lock(&resource_mutex); if (pnres.sk[res] == sk) { |
a9b3cd7f3
|
673 |
RCU_INIT_POINTER(pnres.sk[res], NULL); |
4e3d16ce5
|
674 675 676 677 678 679 680 681 682 683 684 685 686 |
ret = 0; } mutex_unlock(&resource_mutex); if (ret == 0) { synchronize_rcu(); sock_put(sk); } return ret; } void pn_sock_unbind_all_res(struct sock *sk) { |
95c961747
|
687 |
unsigned int res, match = 0; |
4e3d16ce5
|
688 689 690 691 |
mutex_lock(&resource_mutex); for (res = 0; res < 256; res++) { if (pnres.sk[res] == sk) { |
a9b3cd7f3
|
692 |
RCU_INIT_POINTER(pnres.sk[res], NULL); |
4e3d16ce5
|
693 694 695 696 |
match++; } } mutex_unlock(&resource_mutex); |
4e3d16ce5
|
697 |
while (match > 0) { |
44f4d5a27
|
698 |
__sock_put(sk); |
4e3d16ce5
|
699 700 |
match--; } |
44f4d5a27
|
701 |
/* Caller is responsible for RCU sync before final sock_put() */ |
4e3d16ce5
|
702 |
} |
507215f8d
|
703 704 705 706 707 |
#ifdef CONFIG_PROC_FS static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) { struct net *net = seq_file_net(seq); |
95c961747
|
708 |
unsigned int i; |
507215f8d
|
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
if (!net_eq(net, &init_net)) return NULL; for (i = 0; i < 256; i++) { if (pnres.sk[i] == NULL) continue; if (!pos) return pnres.sk + i; pos--; } return NULL; } static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk) { struct net *net = seq_file_net(seq); |
95c961747
|
726 |
unsigned int i; |
507215f8d
|
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
BUG_ON(!net_eq(net, &init_net)); for (i = (sk - pnres.sk) + 1; i < 256; i++) if (pnres.sk[i]) return pnres.sk + i; return NULL; } static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos) __acquires(resource_mutex) { mutex_lock(&resource_mutex); return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; } static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct sock **sk; if (v == SEQ_START_TOKEN) sk = pn_res_get_idx(seq, 0); else sk = pn_res_get_next(seq, v); (*pos)++; return sk; } static void pn_res_seq_stop(struct seq_file *seq, void *v) __releases(resource_mutex) { mutex_unlock(&resource_mutex); } static int pn_res_seq_show(struct seq_file *seq, void *v) { |
652586df9
|
763 |
seq_setwidth(seq, 63); |
507215f8d
|
764 |
if (v == SEQ_START_TOKEN) |
652586df9
|
765 |
seq_puts(seq, "rs uid inode"); |
507215f8d
|
766 767 768 |
else { struct sock **psk = v; struct sock *sk = *psk; |
652586df9
|
769 |
seq_printf(seq, "%02X %5u %lu", |
a7cb5a49b
|
770 771 |
(int) (psk - pnres.sk), from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), |
652586df9
|
772 |
sock_i_ino(sk)); |
507215f8d
|
773 |
} |
652586df9
|
774 775 |
seq_pad(seq, ' '); |
507215f8d
|
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
return 0; } static const struct seq_operations pn_res_seq_ops = { .start = pn_res_seq_start, .next = pn_res_seq_next, .stop = pn_res_seq_stop, .show = pn_res_seq_show, }; static int pn_res_open(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &pn_res_seq_ops, sizeof(struct seq_net_private)); } const struct file_operations pn_res_seq_fops = { .owner = THIS_MODULE, .open = pn_res_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_net, }; #endif |