📄 socket.c
字号:
} } else { if (msg_mcast(msg)) { msg_dbg(msg, "dispatch filter 2\n"); return TIPC_ERR_NO_PORT; } if (sock->state == SS_CONNECTED) { if (!msg_connected(msg)) { msg_dbg(msg, "dispatch filter 3\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_CONNECTING) { if (!msg_connected(msg) && (msg_errcode(msg) == 0)) { msg_dbg(msg, "dispatch filter 4\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_LISTENING) { if (msg_connected(msg) || msg_errcode(msg)) { msg_dbg(msg, "dispatch filter 5\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_DISCONNECTING) { msg_dbg(msg, "dispatch filter 6\n"); return TIPC_ERR_NO_PORT; } else /* (sock->state == SS_UNCONNECTED) */ { if (msg_connected(msg) || msg_errcode(msg)) { msg_dbg(msg, "dispatch filter 7\n"); return TIPC_ERR_NO_PORT; } } } /* Reject message if there isn't room to queue it */ if (unlikely((u32)atomic_read(&tipc_queue_size) > OVERLOAD_LIMIT_BASE)) { if (queue_overloaded(atomic_read(&tipc_queue_size), OVERLOAD_LIMIT_BASE, msg)) return TIPC_ERR_OVERLOAD; } recv_q_len = skb_queue_len(&tsock->sk.sk_receive_queue); if (unlikely(recv_q_len > (OVERLOAD_LIMIT_BASE / 2))) { if (queue_overloaded(recv_q_len, OVERLOAD_LIMIT_BASE / 2, msg)) return TIPC_ERR_OVERLOAD; } /* Initiate connection termination for an incoming 'FIN' */ if (unlikely(msg_errcode(msg) && (sock->state == SS_CONNECTED))) { sock->state = SS_DISCONNECTING; /* Note: Use signal since port lock is already taken! */ tipc_k_signal((Handler)async_disconnect, tport->ref); } /* Enqueue message (finally!) */ msg_dbg(msg,"<DISP<: "); TIPC_SKB_CB(buf)->handle = msg_data(msg); atomic_inc(&tipc_queue_size); skb_queue_tail(&sock->sk->sk_receive_queue, buf); if (waitqueue_active(sock->sk->sk_sleep)) wake_up_interruptible(sock->sk->sk_sleep); return TIPC_OK;}/** * wakeupdispatch - wake up port after congestion * @tport: port to wakeup * * Called with port lock on. */static void wakeupdispatch(struct tipc_port *tport){ struct tipc_sock *tsock = (struct tipc_sock *)tport->usr_handle; if (waitqueue_active(tsock->sk.sk_sleep)) wake_up_interruptible(tsock->sk.sk_sleep);}/** * connect - establish a connection to another TIPC port * @sock: socket structure * @dest: socket address for destination port * @destlen: size of socket address data structure * @flags: (unused) * * Returns 0 on success, errno otherwise */static int connect(struct socket *sock, struct sockaddr *dest, int destlen, int flags){ struct tipc_sock *tsock = tipc_sk(sock->sk); struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; struct msghdr m = {NULL,}; struct sk_buff *buf; struct tipc_msg *msg; int res; /* For now, TIPC does not allow use of connect() with DGRAM or RDM types */ if (sock->state == SS_READY) return -EOPNOTSUPP; /* Issue Posix-compliant error code if socket is in the wrong state */ if (sock->state == SS_LISTENING) return -EOPNOTSUPP; if (sock->state == SS_CONNECTING) return -EALREADY; if (sock->state != SS_UNCONNECTED) return -EISCONN; /* * Reject connection attempt using multicast address * * Note: send_msg() validates the rest of the address fields, * so there's no need to do it here */ if (dst->addrtype == TIPC_ADDR_MCAST) return -EINVAL; /* Send a 'SYN-' to destination */ m.msg_name = dest; m.msg_namelen = destlen; if ((res = send_msg(NULL, sock, &m, 0)) < 0) { sock->state = SS_DISCONNECTING; return res; } if (down_interruptible(&tsock->sem)) return -ERESTARTSYS; /* Wait for destination's 'ACK' response */ res = wait_event_interruptible_timeout(*sock->sk->sk_sleep, skb_queue_len(&sock->sk->sk_receive_queue), sock->sk->sk_rcvtimeo); buf = skb_peek(&sock->sk->sk_receive_queue); if (res > 0) { msg = buf_msg(buf); res = auto_connect(sock, tsock, msg); if (!res) { if (!msg_data_sz(msg)) advance_queue(tsock); } } else { if (res == 0) { res = -ETIMEDOUT; } else { /* leave "res" unchanged */ } sock->state = SS_DISCONNECTING; } up(&tsock->sem); return res;}/** * listen - allow socket to listen for incoming connections * @sock: socket structure * @len: (unused) * * Returns 0 on success, errno otherwise */static int listen(struct socket *sock, int len){ /* REQUIRES SOCKET LOCKING OF SOME SORT? */ if (sock->state == SS_READY) return -EOPNOTSUPP; if (sock->state != SS_UNCONNECTED) return -EINVAL; sock->state = SS_LISTENING; return 0;}/** * accept - wait for connection request * @sock: listening socket * @newsock: new socket that is to be connected * @flags: file-related flags associated with socket * * Returns 0 on success, errno otherwise */static int accept(struct socket *sock, struct socket *newsock, int flags){ struct tipc_sock *tsock = tipc_sk(sock->sk); struct sk_buff *buf; int res = -EFAULT; if (sock->state == SS_READY) return -EOPNOTSUPP; if (sock->state != SS_LISTENING) return -EINVAL; if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & O_NONBLOCK))) return -EWOULDBLOCK; if (down_interruptible(&tsock->sem)) return -ERESTARTSYS; if (wait_event_interruptible(*sock->sk->sk_sleep, skb_queue_len(&sock->sk->sk_receive_queue))) { res = -ERESTARTSYS; goto exit; } buf = skb_peek(&sock->sk->sk_receive_queue); res = tipc_create(sock->sk->sk_net, newsock, 0); if (!res) { struct tipc_sock *new_tsock = tipc_sk(newsock->sk); struct tipc_portid id; struct tipc_msg *msg = buf_msg(buf); u32 new_ref = new_tsock->p->ref; id.ref = msg_origport(msg); id.node = msg_orignode(msg); tipc_connect2port(new_ref, &id); newsock->state = SS_CONNECTED; tipc_set_portimportance(new_ref, msg_importance(msg)); if (msg_named(msg)) { new_tsock->p->conn_type = msg_nametype(msg); new_tsock->p->conn_instance = msg_nameinst(msg); } /* * Respond to 'SYN-' by discarding it & returning 'ACK'-. * Respond to 'SYN+' by queuing it on new socket. */ msg_dbg(msg,"<ACC<: "); if (!msg_data_sz(msg)) { struct msghdr m = {NULL,}; send_packet(NULL, newsock, &m, 0); advance_queue(tsock); } else { sock_lock(tsock); skb_dequeue(&sock->sk->sk_receive_queue); sock_unlock(tsock); skb_queue_head(&newsock->sk->sk_receive_queue, buf); } }exit: up(&tsock->sem); return res;}/** * shutdown - shutdown socket connection * @sock: socket structure * @how: direction to close (unused; always treated as read + write) * * Terminates connection (if necessary), then purges socket's receive queue. * * Returns 0 on success, errno otherwise */static int shutdown(struct socket *sock, int how){ struct tipc_sock* tsock = tipc_sk(sock->sk); struct sk_buff *buf; int res; /* Could return -EINVAL for an invalid "how", but why bother? */ if (down_interruptible(&tsock->sem)) return -ERESTARTSYS; sock_lock(tsock); switch (sock->state) { case SS_CONNECTED: /* Send 'FIN+' or 'FIN-' message to peer */ sock_unlock(tsock);restart: if ((buf = skb_dequeue(&sock->sk->sk_receive_queue))) { atomic_dec(&tipc_queue_size); if (TIPC_SKB_CB(buf)->handle != msg_data(buf_msg(buf))) { buf_discard(buf); goto restart; } tipc_reject_msg(buf, TIPC_CONN_SHUTDOWN); } else { tipc_shutdown(tsock->p->ref); } sock_lock(tsock); /* fall through */ case SS_DISCONNECTING: /* Discard any unreceived messages */ while ((buf = skb_dequeue(&sock->sk->sk_receive_queue))) { atomic_dec(&tipc_queue_size); buf_discard(buf); } tsock->p->conn_unacked = 0; /* fall through */ case SS_CONNECTING: sock->state = SS_DISCONNECTING; res = 0; break; default: res = -ENOTCONN; } sock_unlock(tsock); up(&tsock->sem); return res;}/** * setsockopt - set socket option * @sock: socket structure * @lvl: option level * @opt: option identifier * @ov: pointer to new option value * @ol: length of option value * * For stream sockets only, accepts and ignores all IPPROTO_TCP options * (to ease compatibility). * * Returns 0 on success, errno otherwise */static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov, int ol){ struct tipc_sock *tsock = tipc_sk(sock->sk); u32 value; int res; if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) return 0; if (lvl != SOL_TIPC) return -ENOPROTOOPT; if (ol < sizeof(value)) return -EINVAL; if ((res = get_user(value, (u32 __user *)ov))) return res; if (down_interruptible(&tsock->sem)) return -ERESTARTSYS; switch (opt) { case TIPC_IMPORTANCE: res = tipc_set_portimportance(tsock->p->ref, value); break; case TIPC_SRC_DROPPABLE: if (sock->type != SOCK_STREAM) res = tipc_set_portunreliable(tsock->p->ref, value); else res = -ENOPROTOOPT; break; case TIPC_DEST_DROPPABLE: res = tipc_set_portunreturnable(tsock->p->ref, value); break; case TIPC_CONN_TIMEOUT: sock->sk->sk_rcvtimeo = (value * HZ / 1000); break; default: res = -EINVAL; } up(&tsock->sem); return res;}/** * getsockopt - get socket option * @sock: socket structure * @lvl: option level * @opt: option identifier * @ov: receptacle for option value * @ol: receptacle for length of option value * * For stream sockets only, returns 0 length result for all IPPROTO_TCP options * (to ease compatibility). * * Returns 0 on success, errno otherwise */static int getsockopt(struct socket *sock, int lvl, int opt, char __user *ov, int __user *ol){ struct tipc_sock *tsock = tipc_sk(sock->sk); int len; u32 value; int res; if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) return put_user(0, ol); if (lvl != SOL_TIPC) return -ENOPROTOOPT; if ((res = get_user(len, ol))) return res; if (down_interruptible(&tsock->sem)) return -ERESTARTSYS; switch (opt) { case TIPC_IMPORTANCE: res = tipc_portimportance(tsock->p->ref, &value); break; case TIPC_SRC_DROPPABLE: res = tipc_portunreliable(tsock->p->ref, &value); break; case TIPC_DEST_DROPPABLE: res = tipc_portunreturnable(tsock->p->ref, &value); break; case TIPC_CONN_TIMEOUT: value = (sock->sk->sk_rcvtimeo * 1000) / HZ; break; default: res = -EINVAL; } if (res) { /* "get" failed */ } else if (len < sizeof(value)) { res = -EINVAL; } else if ((res = copy_to_user(ov, &value, sizeof(value)))) { /* couldn't return value */ } else { res = put_user(sizeof(value), ol); } up(&tsock->sem); return res;}/** * Protocol switches for the various types of TIPC sockets */static struct proto_ops msg_ops = { .owner = THIS_MODULE, .family = AF_TIPC, .release = release, .bind = bind, .connect = connect, .socketpair = sock_no_socketpair, .accept = accept, .getname = get_name, .poll = poll, .ioctl = sock_no_ioctl, .listen = listen, .shutdown = shutdown, .setsockopt = setsockopt, .getsockopt = getsockopt, .sendmsg = send_msg, .recvmsg = recv_msg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage};static struct proto_ops packet_ops = { .owner = THIS_MODULE, .family = AF_TIPC, .release = release, .bind = bind, .connect = connect, .socketpair = sock_no_socketpair, .accept = accept, .getname = get_name, .poll = poll, .ioctl = sock_no_ioctl, .listen = listen, .shutdown = shutdown, .setsockopt = setsockopt, .getsockopt = getsockopt, .sendmsg = send_packet, .recvmsg = recv_msg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage};static struct proto_ops stream_ops = { .owner = THIS_MODULE, .family = AF_TIPC, .release = release, .bind = bind, .connect = connect, .socketpair = sock_no_socketpair, .accept = accept, .getname = get_name, .poll = poll, .ioctl = sock_no_ioctl, .listen = listen, .shutdown = shutdown, .setsockopt = setsockopt, .getsockopt = getsockopt, .sendmsg = send_stream, .recvmsg = recv_stream, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage};static struct net_proto_family tipc_family_ops = { .owner = THIS_MODULE, .family = AF_TIPC, .create = tipc_create};static struct proto tipc_proto = { .name = "TIPC", .owner = THIS_MODULE, .obj_size = sizeof(struct tipc_sock)};/** * tipc_socket_init - initialize TIPC socket interface * * Returns 0 on success, errno otherwise */int tipc_socket_init(void){ int res; res = proto_register(&tipc_proto, 1); if (res) { err("Failed to register TIPC protocol type\n"); goto out; } res = sock_register(&tipc_family_ops); if (res) { err("Failed to register TIPC socket type\n"); proto_unregister(&tipc_proto); goto out; } sockets_enabled = 1; out: return res;}/** * tipc_socket_stop - stop TIPC socket interface */void tipc_socket_stop(void){ if (!sockets_enabled) return; sockets_enabled = 0; sock_unregister(tipc_family_ops.family); proto_unregister(&tipc_proto);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -