int virtio_transport_shutdown(struct vsock_sock *vsk, int mode) { struct virtio_vsock_pkt_info info = { .op = VIRTIO_VSOCK_OP_SHUTDOWN, .type = VIRTIO_VSOCK_TYPE_STREAM, .flags = (mode & RCV_SHUTDOWN ? VIRTIO_VSOCK_SHUTDOWN_RCV : 0) | (mode & SEND_SHUTDOWN ? VIRTIO_VSOCK_SHUTDOWN_SEND : 0), .vsk = vsk, }; return virtio_transport_send_pkt_info(vsk, &info); } EXPORT_SYMBOL_GPL(virtio_transport_shutdown); int virtio_transport_dgram_enqueue(struct vsock_sock *vsk, struct sockaddr_vm *remote_addr, struct msghdr *msg, size_t dgram_len) { return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(virtio_transport_dgram_enqueue); ssize_t virtio_transport_stream_enqueue(struct vsock_sock *vsk, struct msghdr *msg, size_t len) { struct virtio_vsock_pkt_info info = { .op = VIRTIO_VSOCK_OP_RW, .type = VIRTIO_VSOCK_TYPE_STREAM, .msg = msg, .pkt_len = len, .vsk = vsk, }; return virtio_transport_send_pkt_info(vsk, &info); } EXPORT_SYMBOL_GPL(virtio_transport_stream_enqueue); void virtio_transport_destruct(struct vsock_sock *vsk) { struct virtio_vsock_sock *vvs = vsk->trans; kfree(vvs); } EXPORT_SYMBOL_GPL(virtio_transport_destruct); static int virtio_transport_reset(struct vsock_sock *vsk, struct virtio_vsock_pkt *pkt) { struct virtio_vsock_pkt_info info = { .op = VIRTIO_VSOCK_OP_RST, .type = VIRTIO_VSOCK_TYPE_STREAM, .reply = !!pkt, .vsk = vsk, }; /* Send RST only if the original pkt is not a RST pkt */ if (pkt && le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST) return 0; return virtio_transport_send_pkt_info(vsk, &info); } /* Normally packets are associated with a socket. There may be no socket if an * attempt was made to connect to a socket that does not exist. */ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt) { struct virtio_vsock_pkt_info info = { .op = VIRTIO_VSOCK_OP_RST, .type = le16_to_cpu(pkt->hdr.type), .reply = true, }; /* Send RST only if the original pkt is not a RST pkt */ if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST) return 0; pkt = virtio_transport_alloc_pkt(&info, 0, le64_to_cpu(pkt->hdr.dst_cid), le32_to_cpu(pkt->hdr.dst_port), le64_to_cpu(pkt->hdr.src_cid), le32_to_cpu(pkt->hdr.src_port)); if (!pkt) return -ENOMEM; return virtio_transport_get_ops()->send_pkt(pkt); } static void virtio_transport_wait_close(struct sock *sk, long timeout) { if (timeout) { DEFINE_WAIT_FUNC(wait, woken_wake_function); add_wait_queue(sk_sleep(sk), &wait); do { if (sk_wait_event(sk, &timeout, sock_flag(sk, SOCK_DONE), &wait)) break; } while (!signal_pending(current) && timeout); remove_wait_queue(sk_sleep(sk), &wait); } } static void virtio_transport_do_close(struct vsock_sock *vsk, bool cancel_timeout) { struct sock *sk = sk_vsock(vsk); sock_set_flag(sk, SOCK_DONE); vsk->peer_shutdown = SHUTDOWN_MASK; if (vsock_stream_has_data(vsk) <= 0) sk->sk_state = SS_DISCONNECTING; sk->sk_state_change(sk); if (vsk->close_work_scheduled && (!cancel_timeout || cancel_delayed_work(&vsk->close_work))) { vsk->close_work_scheduled = false; vsock_remove_sock(vsk); /* Release refcnt obtained when we scheduled the timeout */ sock_put(sk); } } static void virtio_transport_close_timeout(struct work_struct *work) { struct vsock_sock *vsk = container_of(work, struct vsock_sock, close_work.work); struct sock *sk = sk_vsock(vsk); sock_hold(sk); lock_sock(sk); if (!sock_flag(sk, SOCK_DONE)) { (void)virtio_transport_reset(vsk, NULL); virtio_transport_do_close(vsk, false); } vsk->close_work_scheduled = false; release_sock(sk); sock_put(sk); } /* User context, vsk->sk is locked */ static bool virtio_transport_close(struct vsock_sock *vsk) { struct sock *sk = &vsk->sk; if (!(sk->sk_state == SS_CONNECTED || sk->sk_state == SS_DISCONNECTING)) return true; /* Already received SHUTDOWN from peer, reply with RST */ if ((vsk->peer_shutdown & SHUTDOWN_MASK) == SHUTDOWN_MASK) { (void)virtio_transport_reset(vsk, NULL); return true; } if ((sk->sk_shutdown & SHUTDOWN_MASK) != SHUTDOWN_MASK) (void)virtio_transport_shutdown(vsk, SHUTDOWN_MASK); if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING)) virtio_transport_wait_close(sk, sk->sk_lingertime); if (sock_flag(sk, SOCK_DONE)) { return true; } sock_hold(sk); INIT_DELAYED_WORK(&vsk->close_work, virtio_transport_close_timeout); vsk->close_work_scheduled = true; schedule_delayed_work(&vsk->close_work, VSOCK_CLOSE_TIMEOUT); return false; } void virtio_transport_release(struct vsock_sock *vsk) { struct sock *sk = &vsk->sk; bool remove_sock = true; lock_sock(sk); if (sk->sk_type == SOCK_STREAM) remove_sock = virtio_transport_close(vsk); release_sock(sk); if (remove_sock) vsock_remove_sock(vsk); } EXPORT_SYMBOL_GPL(virtio_transport_release); static int virtio_transport_recv_connecting(struct sock *sk, struct virtio_vsock_pkt *pkt) { struct vsock_sock *vsk = vsock_sk(sk); int err; int skerr; switch (le16_to_cpu(pkt->hdr.op)) { case VIRTIO_VSOCK_OP_RESPONSE: sk->sk_state = SS_CONNECTED; sk->sk_socket->state = SS_CONNECTED; vsock_insert_connected(vsk); sk->sk_state_change(sk); break; case VIRTIO_VSOCK_OP_INVALID: break; case VIRTIO_VSOCK_OP_RST: skerr = ECONNRESET; err = 0; goto destroy; default: skerr = EPROTO; err = -EINVAL; goto destroy; } return 0; destroy: virtio_transport_reset(vsk, pkt); sk->sk_state = SS_UNCONNECTED; sk->sk_err = skerr; sk->sk_error_report(sk); return err; } static int virtio_transport_recv_connected(struct sock *sk, struct virtio_vsock_pkt *pkt) { struct vsock_sock *vsk = vsock_sk(sk); struct virtio_vsock_sock *vvs = vsk->trans; int err = 0; switch (le16_to_cpu(pkt->hdr.op)) { case VIRTIO_VSOCK_OP_RW: pkt->len = le32_to_cpu(pkt->hdr.len); pkt->off = 0; spin_lock_bh(&vvs->rx_lock); virtio_transport_inc_rx_pkt(vvs, pkt); list_add_tail(&pkt->list, &vvs->rx_queue); spin_unlock_bh(&vvs->rx_lock); sk->sk_data_ready(sk); return err; case VIRTIO_VSOCK_OP_CREDIT_UPDATE: sk->sk_write_space(sk); break; case VIRTIO_VSOCK_OP_SHUTDOWN: if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_RCV) vsk->peer_shutdown |= RCV_SHUTDOWN; if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_SEND) vsk->peer_shutdown |= SEND_SHUTDOWN; if (vsk->peer_shutdown == SHUTDOWN_MASK && vsock_stream_has_data(vsk) <= 0) sk->sk_state = SS_DISCONNECTING; if (le32_to_cpu(pkt->hdr.flags)) sk->sk_state_change(sk); break; case VIRTIO_VSOCK_OP_RST: virtio_transport_do_close(vsk, true); break; default: err = -EINVAL; break; } virtio_transport_free_pkt(pkt); return err; } static void virtio_transport_recv_disconnecting(struct sock *sk, struct virtio_vsock_pkt *pkt) { struct vsock_sock *vsk = vsock_sk(sk); if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST) virtio_transport_do_close(vsk, true); } static int virtio_transport_send_response(struct vsock_sock *vsk, struct virtio_vsock_pkt *pkt) { struct virtio_vsock_pkt_info info = { .op = VIRTIO_VSOCK_OP_RESPONSE, .type = VIRTIO_VSOCK_TYPE_STREAM, .remote_cid = le64_to_cpu(pkt->hdr.src_cid), .remote_port = le32_to_cpu(pkt->hdr.src_port), .reply = true, .vsk = vsk, }; return virtio_transport_send_pkt_info(vsk, &info); } /* Handle server socket */ static int virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt) { struct vsock_sock *vsk = vsock_sk(sk); struct vsock_sock *vchild; struct sock *child; if (le16_to_cpu(pkt->hdr.op) != VIRTIO_VSOCK_OP_REQUEST) { virtio_transport_reset(vsk, pkt); return -EINVAL; } if (sk_acceptq_is_full(sk)) { virtio_transport_reset(vsk, pkt); return -ENOMEM; } child = __vsock_create(sock_net(sk), NULL, sk, GFP_KERNEL, sk->sk_type, 0); if (!child) { virtio_transport_reset(vsk, pkt); return -ENOMEM; } sk->sk_ack_backlog++; lock_sock_nested(child, SINGLE_DEPTH_NESTING); child->sk_state = SS_CONNECTED; vchild = vsock_sk(child); vsock_addr_init(&vchild->local_addr, le64_to_cpu(pkt->hdr.dst_cid), le32_to_cpu(pkt->hdr.dst_port)); vsock_addr_init(&vchild->remote_addr, le64_to_cpu(pkt->hdr.src_cid), le32_to_cpu(pkt->hdr.src_port)); vsock_insert_connected(vchild); vsock_enqueue_accept(sk, child); virtio_transport_send_response(vchild, pkt); release_sock(child); sk->sk_data_ready(sk); return 0; } static bool virtio_transport_space_update(struct sock *sk, struct virtio_vsock_pkt *pkt) { struct vsock_sock *vsk = vsock_sk(sk); struct virtio_vsock_sock *vvs = vsk->trans; bool space_available; /* buf_alloc and fwd_cnt is always included in the hdr */ spin_lock_bh(&vvs->tx_lock); vvs->peer_buf_alloc = le32_to_cpu(pkt->hdr.buf_alloc); vvs->peer_fwd_cnt = le32_to_cpu(pkt->hdr.fwd_cnt); space_available = virtio_transport_has_space(vsk); spin_unlock_bh(&vvs->tx_lock); return space_available; } /* We are under the virtio-vsock's vsock->rx_lock or vhost-vsock's vq->mutex * lock. */ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) { struct sockaddr_vm src, dst; struct vsock_sock *vsk; struct sock *sk; bool space_available; vsock_addr_init(&src, le64_to_cpu(pkt->hdr.src_cid), le32_to_cpu(pkt->hdr.src_port)); vsock_addr_init(&dst, le64_to_cpu(pkt->hdr.dst_cid), le32_to_cpu(pkt->hdr.dst_port)); trace_virtio_transport_recv_pkt(src.svm_cid, src.svm_port, dst.svm_cid, dst.svm_port, le32_to_cpu(pkt->hdr.len), le16_to_cpu(pkt->hdr.type), le16_to_cpu(pkt->hdr.op), le32_to_cpu(pkt->hdr.flags), le32_to_cpu(pkt->hdr.buf_alloc), le32_to_cpu(pkt->hdr.fwd_cnt)); if (le16_to_cpu(pkt->hdr.type) != VIRTIO_VSOCK_TYPE_STREAM) { (void)virtio_transport_reset_no_sock(pkt); goto free_pkt; } /* The socket must be in connected or bound table * otherwise send reset back */ sk = vsock_find_connected_socket(&src, &dst); if (!sk) { sk = vsock_find_bound_socket(&dst); if (!sk) { (void)virtio_transport_reset_no_sock(pkt); goto free_pkt; } } vsk = vsock_sk(sk); space_available = virtio_transport_space_update(sk, pkt); lock_sock(sk); /* Update CID in case it has changed after a transport reset event */ vsk->local_addr.svm_cid = dst.svm_cid; if (space_available) sk->sk_write_space(sk); switch (sk->sk_state) { case VSOCK_SS_LISTEN: virtio_transport_recv_listen(sk, pkt); virtio_transport_free_pkt(pkt); break; case SS_CONNECTING: virtio_transport_recv_connecting(sk, pkt); virtio_transport_free_pkt(pkt); break; case SS_CONNECTED: virtio_transport_recv_connected(sk, pkt); break; case SS_DISCONNECTING: virtio_transport_recv_disconnecting(sk, pkt); virtio_transport_free_pkt(pkt); break; default: virtio_transport_free_pkt(pkt); break; } release_sock(sk); /* Release refcnt obtained when we fetched this socket out of the * bound or connected list. */ sock_put(sk); return; free_pkt: virtio_transport_free_pkt(pkt); } EXPORT_SYMBOL_GPL(virtio_transport_recv_pkt); void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt) { kfree(pkt->buf); kfree(pkt); } EXPORT_SYMBOL_GPL(virtio_transport_free_pkt); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Asias He"); MODULE_DESCRIPTION("common code for virtio vsock");
int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) { int rc; struct llc_sock *llc = llc_sk(skb->sk); struct llc_conn_state_ev *ev = llc_conn_ev(skb); skb_get(skb); ev->ind_prim = ev->cfm_prim = 0; rc = llc_conn_service(skb->sk, skb); if (unlikely(rc != 0)) { printk(KERN_ERR "%s: llc_conn_service failed\n", __func__); goto out_kfree_skb; } if (unlikely(!ev->ind_prim && !ev->cfm_prim)) { if (!skb->next) goto out_kfree_skb; goto out_skb_put; } if (unlikely(ev->ind_prim && ev->cfm_prim)) skb_get(skb); switch (ev->ind_prim) { case LLC_DATA_PRIM: llc_save_primitive(sk, skb, LLC_DATA_PRIM); if (unlikely(sock_queue_rcv_skb(sk, skb))) { printk(KERN_ERR "%s: sock_queue_rcv_skb failed!\n", __func__); kfree_skb(skb); } break; case LLC_CONN_PRIM: skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_state_change(sk); break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_ESTABLISHED) { sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; if (!sock_flag(sk, SOCK_DEAD)) { sock_set_flag(sk, SOCK_DEAD); sk->sk_state_change(sk); } } kfree_skb(skb); sock_put(sk); break; case LLC_RESET_PRIM: printk(KERN_INFO "%s: received a reset ind!\n", __func__); kfree_skb(skb); break; default: if (ev->ind_prim) { printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->ind_prim); kfree_skb(skb); } break; } switch (ev->cfm_prim) { case LLC_DATA_PRIM: if (!llc_data_accept_state(llc->state)) sk->sk_write_space(sk); else rc = llc->failed_data_req = 1; break; case LLC_CONN_PRIM: if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_SYN_SENT) { if (ev->status) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; } else { sk->sk_socket->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; } sk->sk_state_change(sk); } break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; sk->sk_state_change(sk); } sock_put(sk); break; case LLC_RESET_PRIM: printk(KERN_INFO "%s: received a reset conf!\n", __func__); break; default: if (ev->cfm_prim) { printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->cfm_prim); break; } goto out_skb_put; } out_kfree_skb: kfree_skb(skb); out_skb_put: kfree_skb(skb); return rc; }
int sc_capwap_send(struct sc_capwap_session *session, uint8_t* buffer, int length) { struct kvec vec = { .iov_base = buffer, .iov_len = length, }; struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL, }; TRACEKMOD("### sc_capwap_send\n"); return kernel_sendmsg(session->socket, &msg, &vec, 1, vec.iov_len); } int sc_capwap_recvpacket(struct sock *sk, struct sk_buff* skb) { struct sc_capwap_session* session; TRACEKMOD("### sc_capwap_recvpacket\n"); CAPWAP_SKB_CB(skb)->flags = SKB_CAPWAP_FLAG_FROM_DATA_CHANNEL; sock_hold(sk); /* Get session */ session = (struct sc_capwap_session *)sk->sk_user_data; if (!session) { TRACEKMOD("*** Session not found\n"); goto drop; } /* Remove UDP header */ if (!skb_pull(skb, sizeof(struct udphdr))) { TRACEKMOD("*** Invalid packet\n"); goto drop; } /* Parsing packet */ if (sc_capwap_parsingpacket(session, skb)) { TRACEKMOD("*** Parsing error\n"); goto drop; } sock_put(sk); return 0; drop: sock_put(sk); kfree_skb(skb); return 0; } /* */ struct sc_capwap_session* sc_capwap_recvunknownkeepalive(struct sc_capwap_session* session, const struct sc_capwap_sessionid_element* sessionid) { TRACEKMOD("### sc_capwap_recvunknownkeepalive\n"); return NULL; }
void dccp_close(struct sock *sk, long timeout) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; int state; lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; if (sk->sk_state == DCCP_LISTEN) { dccp_set_state(sk, DCCP_CLOSED); /* Special case. */ inet_csk_listen_stop(sk); goto adjudge_to_death; } sk_stop_timer(sk, &dp->dccps_xmit_timer); /* * We need to flush the recv. buffs. We do this only on the * descriptor close, not protocol-sourced closes, because the *reader process may not have drained the data yet! */ /* FIXME: check for unread data */ while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { __kfree_skb(skb); } if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); } else if (dccp_close_state(sk)) { dccp_send_close(sk, 1); } sk_stream_wait_close(sk, timeout); adjudge_to_death: state = sk->sk_state; sock_hold(sk); sock_orphan(sk); atomic_inc(sk->sk_prot->orphan_count); /* * It is the last release_sock in its life. It will remove backlog. */ release_sock(sk); /* * Now socket is owned by kernel and we acquire BH lock * to finish close. No need to check for user refs. */ local_bh_disable(); bh_lock_sock(sk); BUG_TRAP(!sock_owned_by_user(sk)); /* Have we already been destroyed by a softirq or backlog? */ if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) goto out; /* * The last release_sock may have processed the CLOSE or RESET * packet moving sock to CLOSED state, if not we have to fire * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination" * in draft-ietf-dccp-spec-11. -acme */ if (sk->sk_state == DCCP_CLOSING) { /* FIXME: should start at 2 * RTT */ /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, DCCP_RTO_MAX); #if 0 /* Yeah, we should use sk->sk_prot->orphan_count, etc */ dccp_set_state(sk, DCCP_CLOSED); #endif } if (sk->sk_state == DCCP_CLOSED) inet_csk_destroy_sock(sk); /* Otherwise, socket is reprieved until protocol close. */ out: bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); }
static int udp_v4_get_port(struct sock *sk, unsigned short snum) { write_lock_bh(&udp_hash_lock); if (snum == 0) { int best_size_so_far, best, result, i; if (udp_port_rover > sysctl_local_port_range[1] || udp_port_rover < sysctl_local_port_range[0]) udp_port_rover = sysctl_local_port_range[0]; best_size_so_far = 32767; best = result = udp_port_rover; for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { struct sock *sk; int size; sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; if (!sk) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); goto gotit; } size = 0; do { if (++size >= best_size_so_far) goto next; } while ((sk = sk->next) != NULL); best_size_so_far = size; best = result; next:; } result = best; for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); if (!udp_lport_inuse(result)) break; } if (i >= (1 << 16) / UDP_HTABLE_SIZE) goto fail; gotit: udp_port_rover = snum = result; } else { struct sock *sk2; for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { if (sk2->num == snum && sk2 != sk && !ipv6_only_sock(sk2) && sk2->bound_dev_if == sk->bound_dev_if && (!sk2->rcv_saddr || !sk->rcv_saddr || sk2->rcv_saddr == sk->rcv_saddr) && (!sk2->reuse || !sk->reuse)) goto fail; } } sk->num = snum; if (sk->pprev == NULL) { struct sock **skp = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; sock_prot_inc_use(sk->prot); sock_hold(sk); } write_unlock_bh(&udp_hash_lock); return 0; fail: write_unlock_bh(&udp_hash_lock); return 1; }
static int packet_create(struct socket *sock, int protocol) { struct sock *sk; struct packet_opt *po; int err; if (!capable(CAP_NET_RAW)) return -EPERM; if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW #ifdef CONFIG_SOCK_PACKET && sock->type != SOCK_PACKET #endif ) return -ESOCKTNOSUPPORT; sock->state = SS_UNCONNECTED; err = -ENOBUFS; sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1, NULL); if (sk == NULL) goto out; sock->ops = &packet_ops; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) sock->ops = &packet_ops_spkt; #endif sock_init_data(sock,sk); sk_set_owner(sk, THIS_MODULE); po = sk->sk_protinfo = kmalloc(sizeof(*po), GFP_KERNEL); if (!po) goto out_free; memset(po, 0, sizeof(*po)); sk->sk_family = PF_PACKET; po->num = protocol; sk->sk_destruct = packet_sock_destruct; atomic_inc(&packet_socks_nr); /* * Attach a protocol block */ spin_lock_init(&po->bind_lock); po->prot_hook.func = packet_rcv; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) po->prot_hook.func = packet_rcv_spkt; #endif po->prot_hook.af_packet_priv = sk; if (protocol) { po->prot_hook.type = protocol; dev_add_pack(&po->prot_hook); sock_hold(sk); po->running = 1; } write_lock_bh(&packet_sklist_lock); sk_add_node(sk, &packet_sklist); write_unlock_bh(&packet_sklist_lock); return(0); out_free: sk_free(sk); out: return err; }
static int packet_create(struct socket *sock, int protocol) { struct sock *sk; int err; if (!capable(CAP_NET_RAW)) return -EPERM; if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW #ifdef CONFIG_SOCK_PACKET && sock->type != SOCK_PACKET #endif ) return -ESOCKTNOSUPPORT; sock->state = SS_UNCONNECTED; MOD_INC_USE_COUNT; err = -ENOBUFS; sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1); if (sk == NULL) goto out; sock->ops = &packet_ops; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) sock->ops = &packet_ops_spkt; #endif sock_init_data(sock,sk); sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt), GFP_KERNEL); if (sk->protinfo.af_packet == NULL) goto out_free; memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt)); sk->family = PF_PACKET; sk->num = protocol; sk->destruct = packet_sock_destruct; atomic_inc(&packet_socks_nr); /* * Attach a protocol block */ spin_lock_init(&sk->protinfo.af_packet->bind_lock); sk->protinfo.af_packet->prot_hook.func = packet_rcv; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt; #endif sk->protinfo.af_packet->prot_hook.data = (void *)sk; if (protocol) { sk->protinfo.af_packet->prot_hook.type = protocol; dev_add_pack(&sk->protinfo.af_packet->prot_hook); sock_hold(sk); sk->protinfo.af_packet->running = 1; } write_lock_bh(&packet_sklist_lock); sk->next = packet_sklist; packet_sklist = sk; sock_hold(sk); write_unlock_bh(&packet_sklist_lock); return(0); out_free: sk_free(sk); out: MOD_DEC_USE_COUNT; return err; }
static void __vsock_insert_bound(struct list_head *list, struct vsock_sock *vsk) { sock_hold(&vsk->sk); list_add(&vsk->bound_table, list); }
static void __vsock_insert_connected(struct list_head *list, struct vsock_sock *vsk) { sock_hold(&vsk->sk); list_add(&vsk->connected_table, list); }
static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, struct sock *sk, gfp_t gfp) { struct sctp_hmac_algo_param *auth_hmacs = NULL; struct sctp_chunks_param *auth_chunks = NULL; struct sctp_shared_key *null_key; int err; ep->digest = kzalloc(SCTP_SIGNATURE_SIZE, gfp); if (!ep->digest) return NULL; if (sctp_auth_enable) { /* */ auth_hmacs = kzalloc(sizeof(sctp_hmac_algo_param_t) + sizeof(__u16) * SCTP_AUTH_NUM_HMACS, gfp); if (!auth_hmacs) goto nomem; auth_chunks = kzalloc(sizeof(sctp_chunks_param_t) + SCTP_NUM_CHUNK_TYPES, gfp); if (!auth_chunks) goto nomem; /* */ auth_hmacs->param_hdr.type = SCTP_PARAM_HMAC_ALGO; auth_hmacs->param_hdr.length = htons(sizeof(sctp_paramhdr_t) + 2); auth_hmacs->hmac_ids[0] = htons(SCTP_AUTH_HMAC_ID_SHA1); /* */ auth_chunks->param_hdr.type = SCTP_PARAM_CHUNKS; auth_chunks->param_hdr.length = htons(sizeof(sctp_paramhdr_t)); /* */ if (sctp_addip_enable) { auth_chunks->chunks[0] = SCTP_CID_ASCONF; auth_chunks->chunks[1] = SCTP_CID_ASCONF_ACK; auth_chunks->param_hdr.length = htons(sizeof(sctp_paramhdr_t) + 2); } } /* */ /* */ ep->base.type = SCTP_EP_TYPE_SOCKET; /* */ atomic_set(&ep->base.refcnt, 1); ep->base.dead = 0; ep->base.malloced = 1; /* */ sctp_inq_init(&ep->base.inqueue); /* */ sctp_inq_set_th_handler(&ep->base.inqueue, sctp_endpoint_bh_rcv); /* */ sctp_bind_addr_init(&ep->base.bind_addr, 0); /* */ ep->base.sk = sk; sock_hold(ep->base.sk); /* */ INIT_LIST_HEAD(&ep->asocs); /* */ ep->sndbuf_policy = sctp_sndbuf_policy; sk->sk_data_ready = sctp_data_ready; sk->sk_write_space = sctp_write_space; sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); /* */ ep->rcvbuf_policy = sctp_rcvbuf_policy; /* */ get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE); ep->last_key = ep->current_key = 0; ep->key_changed_at = jiffies; /* */ INIT_LIST_HEAD(&ep->endpoint_shared_keys); null_key = sctp_auth_shkey_create(0, GFP_KERNEL); if (!null_key) goto nomem; list_add(&null_key->key_list, &ep->endpoint_shared_keys); /* */ err = sctp_auth_init_hmacs(ep, gfp); if (err) goto nomem_hmacs; /* */ ep->auth_hmacs_list = auth_hmacs; ep->auth_chunk_list = auth_chunks; return ep; nomem_hmacs: sctp_auth_destroy_keys(&ep->endpoint_shared_keys); nomem: /* */ kfree(auth_hmacs); kfree(auth_chunks); kfree(ep->digest); return NULL; }
/* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. */ static int udp_v6_get_port(struct sock *sk, unsigned short snum) { write_lock_bh(&udp_hash_lock); if (snum == 0) { int best_size_so_far, best, result, i; if (udp_port_rover > sysctl_local_port_range[1] || udp_port_rover < sysctl_local_port_range[0]) udp_port_rover = sysctl_local_port_range[0]; best_size_so_far = 32767; best = result = udp_port_rover; for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { struct sock *sk; int size; sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; if (!sk) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); goto gotit; } size = 0; do { if (++size >= best_size_so_far) goto next; } while ((sk = sk->next) != NULL); best_size_so_far = size; best = result; next:; } result = best; for(;; result += UDP_HTABLE_SIZE) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); if (!udp_lport_inuse(result)) break; } gotit: udp_port_rover = snum = result; } else { struct sock *sk2; int sk_reuse, sk2_reuse; int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr), addr_type2; #if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND) uid_t sk_uid = sock_i_uid_t(sk), sk2_uid; #endif sk_reuse = 0; if (sk->reuse) sk_reuse |= 1; #ifdef SO_REUSEPORT if (sk->reuseport) sk_reuse |= 2; #endif if (sk_reuse && (addr_type != IPV6_ADDR_MAPPED ? (addr_type & IPV6_ADDR_MULTICAST) : MULTICAST(sk->rcv_saddr))) sk_reuse |= 4; for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { #if 1 /* XXX: should be recoded like 2.4.21 */ #if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND) int uid_ok; #endif int both_specified = 0; if (sk2->num != snum || sk2 == sk || (sk2->bound_dev_if && sk->bound_dev_if && sk2->bound_dev_if != sk->bound_dev_if)) continue; #if 0 if (sk2->family != AF_INET6 && sk2->family != AF_INET) continue; #endif addr_type2 = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) : IPV6_ADDR_MAPPED; #if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND) sk2_uid = sock_i_uid_t(sk2); #endif if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) && (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr)) { if (addr_type2 == IPV6_ADDR_MAPPED || addr_type == IPV6_ADDR_MAPPED) { if (addr_type2 != addr_type || sk2->rcv_saddr != sk->rcv_saddr) continue; } else { if (ipv6_addr_cmp(&sk2->net_pinfo.af_inet6.rcv_saddr, &sk->net_pinfo.af_inet6.rcv_saddr)) continue; } both_specified = 1; } #if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND) uid_ok = sk2_uid == (uid_t) -1 || sk_uid == sk2_uid; #endif if ((addr_type2 == IPV6_ADDR_MAPPED && addr_type != IPV6_ADDR_MAPPED && sk->net_pinfo.af_inet6.ipv6only) || (addr_type == IPV6_ADDR_MAPPED && addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only)) { #ifdef CONFIG_IPV6_RESTRICTED_DOUBLE_BIND if (sysctl_ipv6_bindv6only_restriction == 0 || uid_ok) continue; #else continue; #endif } sk2_reuse = 0; if (sk2->reuse) sk2_reuse |= 1; #ifdef SO_REUSEPORT if (sk2->reuseport) sk2_reuse |= 2; #endif if (sk2_reuse && (addr_type2 != IPV6_ADDR_MAPPED ? (addr_type2 & IPV6_ADDR_MULTICAST) : MULTICAST(sk2->rcv_saddr))) sk2_reuse |= 4; if (sk2_reuse & sk_reuse & 3) { /* NOT && */ if (sk2_reuse & sk_reuse & 4) continue; #ifdef CONFIG_NET_RESTRICTED_REUSE if (!uid_ok) goto fail; #endif #ifdef SO_REUSEPORT if (sk2_reuse & sk_reuse & 2) continue; #endif if (both_specified) { int addr_type2d = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.daddr) : IPV6_ADDR_MAPPED; if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr) continue; } else { if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) || (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr)) continue; } } goto fail; #else /* XXX: should be recoded like 2.4.21 */ if (sk2->num == snum && sk2 != sk && (!sk2->bound_dev_if || !sk->bound_dev_if || sk2->bound_dev_if == sk->bound_dev_if) && ((!sk2->rcv_saddr && !ipv6_only_sock(sk)) || (sk2->family == AF_INET6 && ipv6_addr_any(&sk2->net_pinfo.af_inet6.rcv_saddr) && !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) || (addr_type == IPV6_ADDR_ANY && (!ipv6_only_sock(sk) || !(sk2->family == AF_INET6 ? (ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_MAPPED) : 1))) || (sk2->family == AF_INET6 && !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, &sk2->net_pinfo.af_inet6.rcv_saddr)) || (addr_type == IPV6_ADDR_MAPPED && !ipv6_only_sock(sk2) && (!sk2->rcv_saddr || !sk->rcv_saddr || sk->rcv_saddr == sk2->rcv_saddr))) && (!sk2->reuse || !sk->reuse)) goto fail; #endif /* XXX: should be recoded like 2.4.21 */ } } sk->num = snum; if (sk->pprev == NULL) { struct sock **skp = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; sock_prot_inc_use(sk->prot); sock_hold(sk); } write_unlock_bh(&udp_hash_lock); return 0; fail: write_unlock_bh(&udp_hash_lock); return 1; }
static bool tcp_fastopen_create_child(struct sock *sk, struct sk_buff *skb, struct dst_entry *dst, struct request_sock *req) { struct tcp_sock *tp = tcp_sk(sk); struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; struct sock *child; req->num_retrans = 0; req->num_timeout = 0; req->sk = NULL; child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); if (child == NULL) return false; spin_lock(&queue->fastopenq->lock); queue->fastopenq->qlen++; spin_unlock(&queue->fastopenq->lock); /* Initialize the child socket. Have to fix some values to take * into account the child is a Fast Open socket and is created * only out of the bits carried in the SYN packet. */ tp = tcp_sk(child); tp->fastopen_rsk = req; /* Do a hold on the listner sk so that if the listener is being * closed, the child that has been accepted can live on and still * access listen_lock. */ sock_hold(sk); tcp_rsk(req)->listener = sk; /* RFC1323: The window in SYN & SYN/ACK segments is never * scaled. So correct it appropriately. */ tp->snd_wnd = ntohs(tcp_hdr(skb)->window); /* Activate the retrans timer so that SYNACK can be retransmitted. * The request socket is not added to the SYN table of the parent * because it's been added to the accept queue directly. */ inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS, TCP_TIMEOUT_INIT, TCP_RTO_MAX); /* Add the child socket directly into the accept queue */ inet_csk_reqsk_queue_add(sk, req, child); /* Now finish processing the fastopen child socket. */ inet_csk(child)->icsk_af_ops->rebuild_header(child); tcp_init_congestion_control(child); tcp_mtup_init(child); tcp_init_metrics(child); tcp_init_buffer_space(child); /* Queue the data carried in the SYN packet. We need to first * bump skb's refcnt because the caller will attempt to free it. * * XXX (TFO) - we honor a zero-payload TFO request for now, * (any reason not to?) but no need to queue the skb since * there is no data. How about SYN+FIN? */ if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) { skb = skb_get(skb); skb_dst_drop(skb); __skb_pull(skb, tcp_hdr(skb)->doff * 4); skb_set_owner_r(skb, child); __skb_queue_tail(&child->sk_receive_queue, skb); tp->syn_data_acked = 1; } tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; sk->sk_data_ready(sk, 0); bh_unlock_sock(child); sock_put(child); WARN_ON(req->sk == NULL); return true; }
void dccp_close(struct sock *sk, long timeout) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; u32 data_was_unread = 0; int state; lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; if (sk->sk_state == DCCP_LISTEN) { dccp_set_state(sk, DCCP_CLOSED); inet_csk_listen_stop(sk); goto adjudge_to_death; } sk_stop_timer(sk, &dp->dccps_xmit_timer); while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { data_was_unread += skb->len; __kfree_skb(skb); } if (data_was_unread) { DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread); dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); dccp_set_state(sk, DCCP_CLOSED); } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { sk->sk_prot->disconnect(sk, 0); } else if (sk->sk_state != DCCP_CLOSED) { dccp_terminate_connection(sk); } sk_stream_wait_close(sk, timeout); adjudge_to_death: state = sk->sk_state; sock_hold(sk); sock_orphan(sk); release_sock(sk); local_bh_disable(); bh_lock_sock(sk); WARN_ON(sock_owned_by_user(sk)); percpu_counter_inc(sk->sk_prot->orphan_count); if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) goto out; if (sk->sk_state == DCCP_CLOSED) inet_csk_destroy_sock(sk); out: bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); }
static int serval_inet_tcp_init_sock(struct sock *sk) { struct socket *sock = sk->sk_socket; struct sock *old_sk = sk; const struct proto_ops *old_ops = sock->ops; struct inet_sock *inet; int err = 0; LOG_DBG("init sock\n"); /* First fully initialize the old sock. Otherwise, the release function will fail. */ err = old_tcp_prot.init(sk); if (err) return err; sock->ops = &serval_inet_stream_ops; sk = serval_sk_alloc(sock_net(sk), sock, GFP_KERNEL, SERVAL_PROTO_TCP, &serval_tcp_proto); if (!sk) { LOG_ERR("Fail alloc\n"); goto out_fail_alloc; } /* Initialize serval sock part of socket */ serval_sock_init(sk); /* Initialize inet part */ inet = inet_sk(sk); inet->uc_ttl = -1; /* Let IP decide TTL */ inet->mc_loop = 1; inet->mc_ttl = 1; inet->mc_index = 0; inet->mc_list = NULL; if (sk->sk_prot->init) { /* Call protocol specific init */ err = sk->sk_prot->init(sk); if (err < 0) { LOG_ERR("Fail init\n"); goto out_fail_init; } } sock_hold(old_sk); serval_sk(sk)->old_sk = old_sk; LOG_DBG("Successfully hijacked sock\n"); return 0; out_fail_init: sk_common_release(sk); out_fail_alloc: sock->ops = old_ops; sock->sk = old_sk; return 0; }
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { struct net_device *dev; struct sk_buff *frag; struct rt6_info *rt = (struct rt6_info*)skb->dst; struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL; struct ipv6hdr *tmp_hdr; struct frag_hdr *fh; unsigned int mtu, hlen, left, len; __be32 frag_id = 0; int ptr, offset = 0, err=0; u8 *prevhdr, nexthdr = 0; dev = rt->u.dst.dev; hlen = ip6_find_1stfragopt(skb, &prevhdr); nexthdr = *prevhdr; mtu = ip6_skb_dst_mtu(skb); /* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. (This last * check should be redundant, but it's free.) */ if (!skb->local_df) { skb->dev = skb->dst->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); kfree_skb(skb); return -EMSGSIZE; } if (np && np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; } mtu -= hlen + sizeof(struct frag_hdr); if (skb_shinfo(skb)->frag_list) { int first_len = skb_pagelen(skb); int truesizes = 0; if (first_len - hlen > mtu || ((first_len - hlen) & 7) || skb_cloned(skb)) goto slow_path; for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { /* Correct geometry. */ if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < hlen) goto slow_path; /* Partially cloned skb? */ if (skb_shared(frag)) goto slow_path; BUG_ON(frag->sk); if (skb->sk) { sock_hold(skb->sk); frag->sk = skb->sk; frag->destructor = sock_wfree; truesizes += frag->truesize; } } err = 0; offset = 0; frag = skb_shinfo(skb)->frag_list; skb_shinfo(skb)->frag_list = NULL; /* BUILD HEADER */ *prevhdr = NEXTHDR_FRAGMENT; tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC); if (!tmp_hdr) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); return -ENOMEM; } __skb_pull(skb, hlen); fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr)); __skb_push(skb, hlen); skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); ipv6_select_ident(skb, fh); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); frag_id = fh->identification; first_len = skb_pagelen(skb); skb->data_len = first_len - skb_headlen(skb); skb->truesize -= truesizes; skb->len = first_len; ipv6_hdr(skb)->payload_len = htons(first_len - sizeof(struct ipv6hdr)); dst_hold(&rt->u.dst); for (;;) { /* Prepare header of the next frame, * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; skb_reset_transport_header(frag); fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr)); __skb_push(frag, hlen); skb_reset_network_header(frag); memcpy(skb_network_header(frag), tmp_hdr, hlen); offset += skb->len - hlen - sizeof(struct frag_hdr); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(offset); if (frag->next != NULL) fh->frag_off |= htons(IP6_MF); fh->identification = frag_id; ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ip6_copy_metadata(frag, skb); } err = output(skb); if(!err) IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGCREATES); if (err || !frag) break; skb = frag; frag = skb->next; skb->next = NULL; } kfree(tmp_hdr); if (err == 0) { IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGOKS); dst_release(&rt->u.dst); return 0; } while (frag) { skb = frag->next; kfree_skb(frag); frag = skb; } IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGFAILS); dst_release(&rt->u.dst); return err; } slow_path: left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ /* * Fragment the datagram. */ *prevhdr = NEXTHDR_FRAGMENT; /* * Keep copying data until we run out. */ while(left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) len = mtu; /* IF: we are not sending upto and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len &= ~7; } /* * Allocate buffer. */ if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_ALLOCATED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) { NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n"); IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); err = -ENOMEM; goto fail; } /* * Set up data on packet */ ip6_copy_metadata(frag, skb); skb_reserve(frag, LL_RESERVED_SPACE(rt->u.dst.dev)); skb_put(frag, len + hlen + sizeof(struct frag_hdr)); skb_reset_network_header(frag); fh = (struct frag_hdr *)(skb_network_header(frag) + hlen); frag->transport_header = (frag->network_header + hlen + sizeof(struct frag_hdr)); /* * Charge the memory for the fragment to any owner * it might possess */ if (skb->sk) skb_set_owner_w(frag, skb->sk); /* * Copy the packet header into the new buffer. */ skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); /* * Build fragment header. */ fh->nexthdr = nexthdr; fh->reserved = 0; if (!frag_id) { ipv6_select_ident(skb, fh); frag_id = fh->identification; } else fh->identification = frag_id; /* * Copy a block of the IP datagram. */ if (skb_copy_bits(skb, ptr, skb_transport_header(frag), len)) BUG(); left -= len; fh->frag_off = htons(offset); if (left > 0) fh->frag_off |= htons(IP6_MF); ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ptr += len; offset += len; /* * Put this fragment into the sending queue. */ err = output(frag); if (err) goto fail; IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGCREATES); } IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGOKS); kfree_skb(skb); return err; fail: IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS); kfree_skb(skb); return err; }
/** * iscsi_sw_tcp_xmit_segment - transmit segment * @tcp_conn: the iSCSI TCP connection * @segment: the buffer to transmnit * * This function transmits as much of the buffer as * the network layer will accept, and returns the number of * bytes transmitted. * * If CRC hashing is enabled, the function will compute the * hash as it goes. When the entire segment has been transmitted, * it will retrieve the hash value and send it as well. */ static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct socket *sk = tcp_sw_conn->sock; unsigned int copied = 0; int r = 0; while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) { struct scatterlist *sg; unsigned int offset, copy; int flags = 0; r = 0; offset = segment->copied; copy = segment->size - offset; if (segment->total_copied + segment->size < segment->total_size) flags |= MSG_MORE; /* Use sendpage if we can; else fall back to sendmsg */ if (!segment->data) { sg = segment->sg; offset += segment->sg_offset + sg->offset; r = tcp_sw_conn->sendpage(sk, sg_page(sg), offset, copy, flags); } else { struct msghdr msg = { .msg_flags = flags }; struct kvec iov = { .iov_base = segment->data + offset, .iov_len = copy }; r = kernel_sendmsg(sk, &msg, &iov, 1, copy); } if (r < 0) { iscsi_tcp_segment_unmap(segment); return r; } copied += r; } return copied; } /** * iscsi_sw_tcp_xmit - TCP transmit **/ static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct iscsi_segment *segment = &tcp_sw_conn->out.segment; unsigned int consumed = 0; int rc = 0; while (1) { rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment); /* * We may not have been able to send data because the conn * is getting stopped. libiscsi will know so propagate err * for it to do the right thing. */ if (rc == -EAGAIN) return rc; else if (rc < 0) { rc = ISCSI_ERR_XMIT_FAILED; goto error; } else if (rc == 0) break; consumed += rc; if (segment->total_copied >= segment->total_size) { if (segment->done != NULL) { rc = segment->done(tcp_conn, segment); if (rc != 0) goto error; } } } ISCSI_SW_TCP_DBG(conn, "xmit %d bytes\n", consumed); conn->txdata_octets += consumed; return consumed; error: /* Transmit error. We could initiate error recovery * here. */ ISCSI_SW_TCP_DBG(conn, "Error sending PDU, errno=%d\n", rc); iscsi_conn_failure(conn, rc); return -EIO; } /** * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit */ static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct iscsi_segment *segment = &tcp_sw_conn->out.segment; return segment->total_copied - segment->total_size; } static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task) { struct iscsi_conn *conn = task->conn; int rc; while (iscsi_sw_tcp_xmit_qlen(conn)) { rc = iscsi_sw_tcp_xmit(conn); if (rc == 0) return -EAGAIN; if (rc < 0) return rc; } return 0; } /* * This is called when we're done sending the header. * Simply copy the data_segment to the send segment, and return. */ static int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment; ISCSI_SW_TCP_DBG(tcp_conn->iscsi_conn, "Header done. Next segment size %u total_size %u\n", tcp_sw_conn->out.segment.size, tcp_sw_conn->out.segment.total_size); return 0; } static void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; ISCSI_SW_TCP_DBG(conn, "%s\n", conn->hdrdgst_en ? "digest enabled" : "digest disabled"); /* Clear the data segment - needs to be filled in by the * caller using iscsi_tcp_send_data_prep() */ memset(&tcp_sw_conn->out.data_segment, 0, sizeof(struct iscsi_segment)); /* If header digest is enabled, compute the CRC and * place the digest into the same buffer. We make * sure that both iscsi_tcp_task and mtask have * sufficient room. */ if (conn->hdrdgst_en) { iscsi_tcp_dgst_header(&tcp_sw_conn->tx_hash, hdr, hdrlen, hdr + hdrlen); hdrlen += ISCSI_DIGEST_SIZE; } /* Remember header pointer for later, when we need * to decide whether there's a payload to go along * with the header. */ tcp_sw_conn->out.hdr = hdr; iscsi_segment_init_linear(&tcp_sw_conn->out.segment, hdr, hdrlen, iscsi_sw_tcp_send_hdr_done, NULL); } /* * Prepare the send buffer for the payload data. * Padding and checksumming will all be taken care * of by the iscsi_segment routines. */ static int iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg, unsigned int count, unsigned int offset, unsigned int len) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct hash_desc *tx_hash = NULL; unsigned int hdr_spec_len; ISCSI_SW_TCP_DBG(conn, "offset=%d, datalen=%d %s\n", offset, len, conn->datadgst_en ? "digest enabled" : "digest disabled"); /* Make sure the datalen matches what the caller said he would send. */ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength); WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); if (conn->datadgst_en) tx_hash = &tcp_sw_conn->tx_hash; return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment, sg, count, offset, len, NULL, tx_hash); } static void iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data, size_t len) { struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct hash_desc *tx_hash = NULL; unsigned int hdr_spec_len; ISCSI_SW_TCP_DBG(conn, "datalen=%zd %s\n", len, conn->datadgst_en ? "digest enabled" : "digest disabled"); /* Make sure the datalen matches what the caller said he would send. */ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength); WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); if (conn->datadgst_en) tx_hash = &tcp_sw_conn->tx_hash; iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment, data, len, NULL, tx_hash); } static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task, unsigned int offset, unsigned int count) { struct iscsi_conn *conn = task->conn; int err = 0; iscsi_sw_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len); if (!count) return 0; if (!task->sc) iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count); else { struct scsi_data_buffer *sdb = scsi_out(task->sc); err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl, sdb->table.nents, offset, count); } if (err) { /* got invalid offset/len */ return -EIO; } return 0; } static int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode) { struct iscsi_tcp_task *tcp_task = task->dd_data; task->hdr = task->dd_data + sizeof(*tcp_task); task->hdr_max = sizeof(struct iscsi_sw_tcp_hdrbuf) - ISCSI_DIGEST_SIZE; return 0; } static struct iscsi_cls_conn * iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) { struct iscsi_conn *conn; struct iscsi_cls_conn *cls_conn; struct iscsi_tcp_conn *tcp_conn; struct iscsi_sw_tcp_conn *tcp_sw_conn; cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*tcp_sw_conn), conn_idx); if (!cls_conn) return NULL; conn = cls_conn->dd_data; tcp_conn = conn->dd_data; tcp_sw_conn = tcp_conn->dd_data; tcp_sw_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); tcp_sw_conn->tx_hash.flags = 0; if (IS_ERR(tcp_sw_conn->tx_hash.tfm)) goto free_conn; tcp_sw_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); tcp_sw_conn->rx_hash.flags = 0; if (IS_ERR(tcp_sw_conn->rx_hash.tfm)) goto free_tx_tfm; tcp_conn->rx_hash = &tcp_sw_conn->rx_hash; return cls_conn; free_tx_tfm: crypto_free_hash(tcp_sw_conn->tx_hash.tfm); free_conn: iscsi_conn_printk(KERN_ERR, conn, "Could not create connection due to crc32c " "loading error. Make sure the crc32c " "module is built as a module or into the " "kernel\n"); iscsi_tcp_conn_teardown(cls_conn); return NULL; } static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct socket *sock = tcp_sw_conn->sock; if (!sock) return; sock_hold(sock->sk); iscsi_sw_tcp_conn_restore_callbacks(conn); sock_put(sock->sk); spin_lock_bh(&session->lock); tcp_sw_conn->sock = NULL; spin_unlock_bh(&session->lock); sockfd_put(sock); } static void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; iscsi_sw_tcp_release_conn(conn); if (tcp_sw_conn->tx_hash.tfm) crypto_free_hash(tcp_sw_conn->tx_hash.tfm); if (tcp_sw_conn->rx_hash.tfm) crypto_free_hash(tcp_sw_conn->rx_hash.tfm); iscsi_tcp_conn_teardown(cls_conn); } static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct socket *sock = tcp_sw_conn->sock; /* userspace may have goofed up and not bound us */ if (!sock) return; sock->sk->sk_err = EIO; wake_up_interruptible(sk_sleep(sock->sk)); /* stop xmit side */ iscsi_suspend_tx(conn); /* stop recv side and release socket */ iscsi_sw_tcp_release_conn(conn); iscsi_conn_stop(cls_conn, flag); } static int iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, int is_leading) { struct iscsi_session *session = cls_session->dd_data; struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct sock *sk; struct socket *sock; int err; /* lookup for existing socket */ sock = sockfd_lookup((int)transport_eph, &err); if (!sock) { iscsi_conn_printk(KERN_ERR, conn, "sockfd_lookup failed %d\n", err); return -EEXIST; } err = iscsi_conn_bind(cls_session, cls_conn, is_leading); if (err) goto free_socket; spin_lock_bh(&session->lock); /* bind iSCSI connection and socket */ tcp_sw_conn->sock = sock; spin_unlock_bh(&session->lock); /* setup Socket parameters */ sk = sock->sk; sk->sk_reuse = 1; sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ sk->sk_allocation = GFP_ATOMIC; iscsi_sw_tcp_conn_set_callbacks(conn); tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage; /* * set receive state machine into initial state */ iscsi_tcp_hdr_recv_prep(tcp_conn); return 0; free_socket: sockfd_put(sock); return err; } static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; int value; switch(param) { case ISCSI_PARAM_HDRDGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); break; case ISCSI_PARAM_DATADGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); tcp_sw_conn->sendpage = conn->datadgst_en ? sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage; break; case ISCSI_PARAM_MAX_R2T: sscanf(buf, "%d", &value); if (value <= 0 || !is_power_of_2(value)) return -EINVAL; if (session->max_r2t == value) break; iscsi_tcp_r2tpool_free(session); iscsi_set_param(cls_conn, param, buf, buflen); if (iscsi_tcp_r2tpool_alloc(session)) return -ENOMEM; break; default: return iscsi_set_param(cls_conn, param, buf, buflen); } return 0; } static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct sockaddr_in6 addr; int rc, len; switch(param) { case ISCSI_PARAM_CONN_PORT: case ISCSI_PARAM_CONN_ADDRESS: spin_lock_bh(&conn->session->lock); if (!tcp_sw_conn || !tcp_sw_conn->sock) { spin_unlock_bh(&conn->session->lock); return -ENOTCONN; } rc = kernel_getpeername(tcp_sw_conn->sock, (struct sockaddr *)&addr, &len); spin_unlock_bh(&conn->session->lock); if (rc) return rc; return iscsi_conn_get_addr_param((struct sockaddr_storage *) &addr, param, buf); default: return iscsi_conn_get_param(cls_conn, param, buf); } return 0; } static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf) { struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost); struct iscsi_session *session = tcp_sw_host->session; struct iscsi_conn *conn; struct iscsi_tcp_conn *tcp_conn; struct iscsi_sw_tcp_conn *tcp_sw_conn; struct sockaddr_in6 addr; int rc, len; switch (param) { case ISCSI_HOST_PARAM_IPADDRESS: spin_lock_bh(&session->lock); conn = session->leadconn; if (!conn) { spin_unlock_bh(&session->lock); return -ENOTCONN; } tcp_conn = conn->dd_data; tcp_sw_conn = tcp_conn->dd_data; if (!tcp_sw_conn->sock) { spin_unlock_bh(&session->lock); return -ENOTCONN; } rc = kernel_getsockname(tcp_sw_conn->sock, (struct sockaddr *)&addr, &len); spin_unlock_bh(&session->lock); if (rc) return rc; return iscsi_conn_get_addr_param((struct sockaddr_storage *) &addr, param, buf); default: return iscsi_host_get_param(shost, param, buf); } return 0; } static void iscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; stats->custom_length = 3; strcpy(stats->custom[0].desc, "tx_sendpage_failures"); stats->custom[0].value = tcp_sw_conn->sendpage_failures_cnt; strcpy(stats->custom[1].desc, "rx_discontiguous_hdr"); stats->custom[1].value = tcp_sw_conn->discontiguous_hdr_cnt; strcpy(stats->custom[2].desc, "eh_abort_cnt"); stats->custom[2].value = conn->eh_abort_cnt; iscsi_tcp_conn_get_stats(cls_conn, stats); } static struct iscsi_cls_session * iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, uint16_t qdepth, uint32_t initial_cmdsn) { struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_sw_tcp_host *tcp_sw_host; struct Scsi_Host *shost; if (ep) { printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep); return NULL; } shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, sizeof(struct iscsi_sw_tcp_host), 1); if (!shost) return NULL; shost->transportt = iscsi_sw_tcp_scsi_transport; shost->cmd_per_lun = qdepth; shost->max_lun = iscsi_max_lun; shost->max_id = 0; shost->max_channel = 0; shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE; if (iscsi_host_add(shost, NULL)) goto free_host; cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost, cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct iscsi_sw_tcp_hdrbuf), initial_cmdsn, 0); if (!cls_session) goto remove_host; session = cls_session->dd_data; tcp_sw_host = iscsi_host_priv(shost); tcp_sw_host->session = session; shost->can_queue = session->scsi_cmds_max; if (iscsi_tcp_r2tpool_alloc(session)) goto remove_session; return cls_session; remove_session: iscsi_session_teardown(cls_session); remove_host: iscsi_host_remove(shost); free_host: iscsi_host_free(shost); return NULL; } static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) { struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); iscsi_tcp_r2tpool_free(cls_session->dd_data); iscsi_session_teardown(cls_session); iscsi_host_remove(shost); iscsi_host_free(shost); } static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev) { set_bit(QUEUE_FLAG_BIDI, &sdev->request_queue->queue_flags); return 0; }
static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) { struct pep_sock *pn = pep_sk(sk), *newpn; struct sock *newsk = NULL; struct sk_buff *skb; struct pnpipehdr *hdr; struct sockaddr_pn dst, src; int err; u16 peer_type; u8 pipe_handle, enabled, n_sb; u8 aligned = 0; skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp); if (!skb) return NULL; lock_sock(sk); if (sk->sk_state != TCP_LISTEN) { err = -EINVAL; goto drop; } sk_acceptq_removed(sk); err = -EPROTO; if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) goto drop; hdr = pnp_hdr(skb); pipe_handle = hdr->pipe_handle; switch (hdr->state_after_connect) { case PN_PIPE_DISABLE: enabled = 0; break; case PN_PIPE_ENABLE: enabled = 1; break; default: pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, GFP_KERNEL); goto drop; } peer_type = hdr->other_pep_type << 8; /* Parse sub-blocks (options) */ n_sb = hdr->data[4]; while (n_sb > 0) { u8 type, buf[1], len = sizeof(buf); const u8 *data = pep_get_sb(skb, &type, &len, buf); if (data == NULL) goto drop; switch (type) { case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: if (len < 1) goto drop; peer_type = (peer_type & 0xff00) | data[0]; break; case PN_PIPE_SB_ALIGNED_DATA: aligned = data[0] != 0; break; } n_sb--; } /* Check for duplicate pipe handle */ newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); if (unlikely(newsk)) { __sock_put(newsk); newsk = NULL; pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); goto drop; } /* Create a new to-be-accepted sock */ newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot, 0); if (!newsk) { pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); err = -ENOBUFS; goto drop; } sock_init_data(NULL, newsk); newsk->sk_state = TCP_SYN_RECV; newsk->sk_backlog_rcv = pipe_do_rcv; newsk->sk_protocol = sk->sk_protocol; newsk->sk_destruct = pipe_destruct; newpn = pep_sk(newsk); pn_skb_get_dst_sockaddr(skb, &dst); pn_skb_get_src_sockaddr(skb, &src); newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); sock_hold(sk); newpn->listener = sk; skb_queue_head_init(&newpn->ctrlreq_queue); newpn->pipe_handle = pipe_handle; atomic_set(&newpn->tx_credits, 0); newpn->ifindex = 0; newpn->peer_type = peer_type; newpn->rx_credits = 0; newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; newpn->init_enable = enabled; newpn->aligned = aligned; err = pep_accept_conn(newsk, skb); if (err) { sock_put(newsk); newsk = NULL; goto drop; } sk_add_node(newsk, &pn->hlist); drop: release_sock(sk); kfree_skb(skb); *errp = err; return newsk; }
/* * Initialize the base fields of the endpoint structure. */ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, struct sock *sk, gfp_t gfp) { struct sctp_hmac_algo_param *auth_hmacs = NULL; struct sctp_chunks_param *auth_chunks = NULL; struct sctp_shared_key *null_key; int err; memset(ep, 0, sizeof(struct sctp_endpoint)); ep->digest = kzalloc(SCTP_SIGNATURE_SIZE, gfp); if (!ep->digest) return NULL; if (sctp_auth_enable) { /* Allocate space for HMACS and CHUNKS authentication * variables. There are arrays that we encode directly * into parameters to make the rest of the operations easier. */ auth_hmacs = kzalloc(sizeof(sctp_hmac_algo_param_t) + sizeof(__u16) * SCTP_AUTH_NUM_HMACS, gfp); if (!auth_hmacs) goto nomem; auth_chunks = kzalloc(sizeof(sctp_chunks_param_t) + SCTP_NUM_CHUNK_TYPES, gfp); if (!auth_chunks) goto nomem; /* Initialize the HMACS parameter. * SCTP-AUTH: Section 3.3 * Every endpoint supporting SCTP chunk authentication MUST * support the HMAC based on the SHA-1 algorithm. */ auth_hmacs->param_hdr.type = SCTP_PARAM_HMAC_ALGO; auth_hmacs->param_hdr.length = htons(sizeof(sctp_paramhdr_t) + 2); auth_hmacs->hmac_ids[0] = htons(SCTP_AUTH_HMAC_ID_SHA1); /* Initialize the CHUNKS parameter */ auth_chunks->param_hdr.type = SCTP_PARAM_CHUNKS; /* If the Add-IP functionality is enabled, we must * authenticate, ASCONF and ASCONF-ACK chunks */ if (sctp_addip_enable) { auth_chunks->chunks[0] = SCTP_CID_ASCONF; auth_chunks->chunks[1] = SCTP_CID_ASCONF_ACK; auth_chunks->param_hdr.length = htons(sizeof(sctp_paramhdr_t) + 2); } } /* Initialize the base structure. */ /* What type of endpoint are we? */ ep->base.type = SCTP_EP_TYPE_SOCKET; /* Initialize the basic object fields. */ atomic_set(&ep->base.refcnt, 1); ep->base.dead = 0; ep->base.malloced = 1; /* Create an input queue. */ sctp_inq_init(&ep->base.inqueue); /* Set its top-half handler */ sctp_inq_set_th_handler(&ep->base.inqueue, sctp_endpoint_bh_rcv); /* Initialize the bind addr area */ sctp_bind_addr_init(&ep->base.bind_addr, 0); /* Remember who we are attached to. */ ep->base.sk = sk; sock_hold(ep->base.sk); /* Create the lists of associations. */ INIT_LIST_HEAD(&ep->asocs); /* Use SCTP specific send buffer space queues. */ ep->sndbuf_policy = sctp_sndbuf_policy; sk->sk_write_space = sctp_write_space; sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); /* Get the receive buffer policy for this endpoint */ ep->rcvbuf_policy = sctp_rcvbuf_policy; /* Initialize the secret key used with cookie. */ get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE); ep->last_key = ep->current_key = 0; ep->key_changed_at = jiffies; /* SCTP-AUTH extensions*/ INIT_LIST_HEAD(&ep->endpoint_shared_keys); null_key = sctp_auth_shkey_create(0, GFP_KERNEL); if (!null_key) goto nomem; list_add(&null_key->key_list, &ep->endpoint_shared_keys); /* Allocate and initialize transorms arrays for suported HMACs. */ err = sctp_auth_init_hmacs(ep, gfp); if (err) goto nomem_hmacs; /* Add the null key to the endpoint shared keys list and * set the hmcas and chunks pointers. */ ep->auth_hmacs_list = auth_hmacs; ep->auth_chunk_list = auth_chunks; return ep; nomem_hmacs: sctp_auth_destroy_keys(&ep->endpoint_shared_keys); nomem: /* Free all allocations */ kfree(auth_hmacs); kfree(auth_chunks); kfree(ep->digest); return NULL; }
void dccp_close(struct sock *sk, long timeout) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; u32 data_was_unread = 0; int state; lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; if (sk->sk_state == DCCP_LISTEN) { dccp_set_state(sk, DCCP_CLOSED); /* Special case. */ inet_csk_listen_stop(sk); goto adjudge_to_death; } sk_stop_timer(sk, &dp->dccps_xmit_timer); /* * We need to flush the recv. buffs. We do this only on the * descriptor close, not protocol-sourced closes, because the *reader process may not have drained the data yet! */ while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { data_was_unread += skb->len; __kfree_skb(skb); } /* If socket has been already reset kill it. */ if (sk->sk_state == DCCP_CLOSED) goto adjudge_to_death; if (data_was_unread) { /* Unread data was tossed, send an appropriate Reset Code */ DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread); dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); dccp_set_state(sk, DCCP_CLOSED); } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); } else if (sk->sk_state != DCCP_CLOSED) { /* * Normal connection termination. May need to wait if there are * still packets in the TX queue that are delayed by the CCID. */ dccp_flush_write_queue(sk, &timeout); dccp_terminate_connection(sk); } /* * Flush write queue. This may be necessary in several cases: * - we have been closed by the peer but still have application data; * - abortive termination (unread data or zero linger time), * - normal termination but queue could not be flushed within time limit */ __skb_queue_purge(&sk->sk_write_queue); sk_stream_wait_close(sk, timeout); adjudge_to_death: state = sk->sk_state; sock_hold(sk); sock_orphan(sk); /* * It is the last release_sock in its life. It will remove backlog. */ release_sock(sk); /* * Now socket is owned by kernel and we acquire BH lock * to finish close. No need to check for user refs. */ local_bh_disable(); bh_lock_sock(sk); WARN_ON(sock_owned_by_user(sk)); percpu_counter_inc(sk->sk_prot->orphan_count); /* Have we already been destroyed by a softirq or backlog? */ if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) goto out; if (sk->sk_state == DCCP_CLOSED) inet_csk_destroy_sock(sk); /* Otherwise, socket is reprieved until protocol close. */ out: bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); }
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { struct net_device *dev; struct sk_buff *frag; struct rt6_info *rt = (struct rt6_info*)skb->dst; struct ipv6hdr *tmp_hdr; struct frag_hdr *fh; unsigned int mtu, hlen, left, len; u32 frag_id = 0; int ptr, offset = 0, err=0; u8 *prevhdr, nexthdr = 0; dev = rt->u.dst.dev; hlen = ip6_find_1stfragopt(skb, &prevhdr); nexthdr = *prevhdr; mtu = dst_mtu(&rt->u.dst) - hlen - sizeof(struct frag_hdr); if (skb_shinfo(skb)->frag_list) { int first_len = skb_pagelen(skb); if (first_len - hlen > mtu || ((first_len - hlen) & 7) || skb_cloned(skb)) goto slow_path; for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { /* Correct geometry. */ if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < hlen) goto slow_path; /* Partially cloned skb? */ if (skb_shared(frag)) goto slow_path; BUG_ON(frag->sk); if (skb->sk) { sock_hold(skb->sk); frag->sk = skb->sk; frag->destructor = sock_wfree; skb->truesize -= frag->truesize; } } err = 0; offset = 0; frag = skb_shinfo(skb)->frag_list; skb_shinfo(skb)->frag_list = NULL; /* BUILD HEADER */ tmp_hdr = kmalloc(hlen, GFP_ATOMIC); if (!tmp_hdr) { IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS); return -ENOMEM; } *prevhdr = NEXTHDR_FRAGMENT; memcpy(tmp_hdr, skb->nh.raw, hlen); __skb_pull(skb, hlen); fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr)); skb->nh.raw = __skb_push(skb, hlen); memcpy(skb->nh.raw, tmp_hdr, hlen); ipv6_select_ident(skb, fh); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); frag_id = fh->identification; first_len = skb_pagelen(skb); skb->data_len = first_len - skb_headlen(skb); skb->len = first_len; skb->nh.ipv6h->payload_len = htons(first_len - sizeof(struct ipv6hdr)); for (;;) { /* Prepare header of the next frame, * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; frag->h.raw = frag->data; fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr)); frag->nh.raw = __skb_push(frag, hlen); memcpy(frag->nh.raw, tmp_hdr, hlen); offset += skb->len - hlen - sizeof(struct frag_hdr); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(offset); if (frag->next != NULL) fh->frag_off |= htons(IP6_MF); fh->identification = frag_id; frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ip6_copy_metadata(frag, skb); } err = output(skb); if (err || !frag) break; skb = frag; frag = skb->next; skb->next = NULL; } if (tmp_hdr) kfree(tmp_hdr); if (err == 0) { IP6_INC_STATS(IPSTATS_MIB_FRAGOKS); return 0; } while (frag) { skb = frag->next; kfree_skb(frag); frag = skb; } IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS); return err; } slow_path: left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ /* * Fragment the datagram. */ *prevhdr = NEXTHDR_FRAGMENT; /* * Keep copying data until we run out. */ while(left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) len = mtu; /* IF: we are not sending upto and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len &= ~7; } /* * Allocate buffer. */ if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_RESERVED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) { NETDEBUG(printk(KERN_INFO "IPv6: frag: no memory for new fragment!\n")); IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS); err = -ENOMEM; goto fail; } /* * Set up data on packet */ ip6_copy_metadata(frag, skb); skb_reserve(frag, LL_RESERVED_SPACE(rt->u.dst.dev)); skb_put(frag, len + hlen + sizeof(struct frag_hdr)); frag->nh.raw = frag->data; fh = (struct frag_hdr*)(frag->data + hlen); frag->h.raw = frag->data + hlen + sizeof(struct frag_hdr); /* * Charge the memory for the fragment to any owner * it might possess */ if (skb->sk) skb_set_owner_w(frag, skb->sk); /* * Copy the packet header into the new buffer. */ memcpy(frag->nh.raw, skb->data, hlen); /* * Build fragment header. */ fh->nexthdr = nexthdr; fh->reserved = 0; if (frag_id) { ipv6_select_ident(skb, fh); frag_id = fh->identification; } else fh->identification = frag_id; /* * Copy a block of the IP datagram. */ if (skb_copy_bits(skb, ptr, frag->h.raw, len)) BUG(); left -= len; fh->frag_off = htons(offset); if (left > 0) fh->frag_off |= htons(IP6_MF); frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ptr += len; offset += len; /* * Put this fragment into the sending queue. */ IP6_INC_STATS(IPSTATS_MIB_FRAGCREATES); err = output(frag); if (err) goto fail; } kfree_skb(skb); IP6_INC_STATS(IPSTATS_MIB_FRAGOKS); return err; fail: kfree_skb(skb); IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS); return err; }
static int dccp_v6_rcv(struct sk_buff *skb) { const struct dccp_hdr *dh; bool refcounted; struct sock *sk; int min_cov; /* Step 1: Check header basics */ if (dccp_invalid_packet(skb)) goto discard_it; /* Step 1: If header checksum is incorrect, drop packet and return. */ if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr)) { DCCP_WARN("dropped packet with invalid checksum\n"); goto discard_it; } dh = dccp_hdr(skb); DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(dh); DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type; if (dccp_packet_without_ack(skb)) DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ; else DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb); lookup: sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), dh->dccph_sport, dh->dccph_dport, inet6_iif(skb), 0, &refcounted); if (!sk) { dccp_pr_debug("failed to look up flow ID in table and " "get corresponding socket\n"); goto no_dccp_socket; } /* * Step 2: * ... or S.state == TIMEWAIT, * Generate Reset(No Connection) unless P.type == Reset * Drop packet and return */ if (sk->sk_state == DCCP_TIME_WAIT) { dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n"); inet_twsk_put(inet_twsk(sk)); goto no_dccp_socket; } if (sk->sk_state == DCCP_NEW_SYN_RECV) { struct request_sock *req = inet_reqsk(sk); struct sock *nsk; sk = req->rsk_listener; if (unlikely(sk->sk_state != DCCP_LISTEN)) { inet_csk_reqsk_queue_drop_and_put(sk, req); goto lookup; } sock_hold(sk); refcounted = true; nsk = dccp_check_req(sk, skb, req); if (!nsk) { reqsk_put(req); goto discard_and_relse; } if (nsk == sk) { reqsk_put(req); } else if (dccp_child_process(sk, nsk, skb)) { dccp_v6_ctl_send_reset(sk, skb); goto discard_and_relse; } else { sock_put(sk); return 0; } } /* * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage * o if MinCsCov = 0, only packets with CsCov = 0 are accepted * o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov */ min_cov = dccp_sk(sk)->dccps_pcrlen; if (dh->dccph_cscov && (min_cov == 0 || dh->dccph_cscov < min_cov)) { dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n", dh->dccph_cscov, min_cov); /* FIXME: send Data Dropped option (see also dccp_v4_rcv) */ goto discard_and_relse; } if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4, refcounted) ? -1 : 0; no_dccp_socket: if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_it; /* * Step 2: * If no socket ... * Generate Reset(No Connection) unless P.type == Reset * Drop packet and return */ if (dh->dccph_type != DCCP_PKT_RESET) { DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; dccp_v6_ctl_send_reset(sk, skb); } discard_it: kfree_skb(skb); return 0; discard_and_relse: if (refcounted) sock_put(sk); goto discard_it; }
static int open_socket(cpt_object_t *obj, struct cpt_sock_image *si, struct cpt_context *ctx) { int err; struct socket *sock; struct socket *sock2 = NULL; struct file *file; cpt_object_t *fobj; cpt_object_t *pobj = NULL; err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol, &sock); if (err) return err; if (si->cpt_socketpair) { err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol, &sock2); if (err) goto err_out; err = sock->ops->socketpair(sock, sock2); if (err < 0) goto err_out; /* Socketpair with a peer outside our environment. * So, we create real half-open pipe and do not worry * about dead end anymore. */ if (si->cpt_peer == -1) { sock_release(sock2); sock2 = NULL; } } cpt_obj_setobj(obj, sock->sk, ctx); if (si->cpt_file != CPT_NULL) { file = sock_mapfile(sock); err = PTR_ERR(file); if (IS_ERR(file)) goto err_out; err = -ENOMEM; obj->o_parent = file; if ((fobj = cpt_object_add(CPT_OBJ_FILE, file, ctx)) == NULL) goto err_out; cpt_obj_setpos(fobj, si->cpt_file, ctx); cpt_obj_setindex(fobj, si->cpt_index, ctx); } if (sock2) { struct file *file2; pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_peer, ctx); if (!pobj) BUG(); if (pobj->o_obj) BUG(); cpt_obj_setobj(pobj, sock2->sk, ctx); if (pobj->o_ppos != CPT_NULL) { file2 = sock_mapfile(sock2); err = PTR_ERR(file2); if (IS_ERR(file2)) goto err_out; err = -ENOMEM; if ((fobj = cpt_object_add(CPT_OBJ_FILE, file2, ctx)) == NULL) goto err_out; cpt_obj_setpos(fobj, pobj->o_ppos, ctx); cpt_obj_setindex(fobj, si->cpt_peer, ctx); pobj->o_parent = file2; } } setup_sock_common(sock->sk, si, obj->o_pos, ctx); if (sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6) { int saved_reuse = sock->sk->sk_reuse; inet_sk(sock->sk)->freebind = 1; sock->sk->sk_reuse = 2; if (si->cpt_laddrlen) { err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen); if (err) { dprintk_ctx("binding failed: %d, do not worry\n", err); } } sock->sk->sk_reuse = saved_reuse; rst_socket_in(si, obj->o_pos, sock->sk, ctx); } else if (sock->sk->sk_family == AF_NETLINK) { struct sockaddr_nl *nl = (struct sockaddr_nl *)&si->cpt_laddr; if (nl->nl_pid) { err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen); if (err) { eprintk_ctx("AF_NETLINK binding failed: %d\n", err); } } if (si->cpt_raddrlen && nl->nl_pid) { err = sock->ops->connect(sock, (struct sockaddr *)&si->cpt_raddr, si->cpt_raddrlen, O_NONBLOCK); if (err) { eprintk_ctx("oops, AF_NETLINK connect failed: %d\n", err); } } generic_restore_queues(sock->sk, si, obj->o_pos, ctx); } else if (sock->sk->sk_family == PF_PACKET) { struct sockaddr_ll *ll = (struct sockaddr_ll *)&si->cpt_laddr; if (ll->sll_protocol || ll->sll_ifindex) { int alen = si->cpt_laddrlen; if (alen < sizeof(struct sockaddr_ll)) alen = sizeof(struct sockaddr_ll); err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, alen); if (err) { eprintk_ctx("AF_PACKET binding failed: %d\n", err); } } generic_restore_queues(sock->sk, si, obj->o_pos, ctx); } fixup_unix_address(sock, si, ctx); if (sock2) { err = rst_get_object(CPT_OBJ_SOCKET, pobj->o_pos, si, ctx); if (err) return err; setup_sock_common(sock2->sk, si, pobj->o_pos, ctx); fixup_unix_address(sock2, si, ctx); } if ((sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6) && (int)si->cpt_parent != -1) { cpt_object_t *lobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx); if (lobj && cpt_attach_accept(lobj->o_obj, sock->sk, ctx) == 0) sock->sk = NULL; } if (si->cpt_file == CPT_NULL && sock->sk && sock->sk->sk_family == AF_INET) { struct sock *sk = sock->sk; if (sk) { sock->sk = NULL; local_bh_disable(); bh_lock_sock(sk); if (sock_owned_by_user(sk)) eprintk_ctx("oops, sock is locked by user\n"); sock_hold(sk); sock_orphan(sk); ub_inc_orphan_count(sk); bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); dprintk_ctx("orphaning socket %p\n", sk); } } if (si->cpt_file == CPT_NULL && sock->sk == NULL) sock_release(sock); return 0; err_out: if (sock2) sock_release(sock2); sock_release(sock); return err; }
static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, int addr_len, int flags) { int err; struct sock *sk; struct vsock_sock *vsk; struct sockaddr_vm *remote_addr; long timeout; DEFINE_WAIT(wait); err = 0; sk = sock->sk; vsk = vsock_sk(sk); lock_sock(sk); /* XXX AF_UNSPEC should make us disconnect like AF_INET. */ switch (sock->state) { case SS_CONNECTED: err = -EISCONN; goto out; case SS_DISCONNECTING: err = -EINVAL; goto out; case SS_CONNECTING: /* This continues on so we can move sock into the SS_CONNECTED * state once the connection has completed (at which point err * will be set to zero also). Otherwise, we will either wait * for the connection or return -EALREADY should this be a * non-blocking call. */ err = -EALREADY; break; default: if ((sk->sk_state == SS_LISTEN) || vsock_addr_cast(addr, addr_len, &remote_addr) != 0) { err = -EINVAL; goto out; } /* The hypervisor and well-known contexts do not have socket * endpoints. */ if (!transport->stream_allow(remote_addr->svm_cid, remote_addr->svm_port)) { err = -ENETUNREACH; goto out; } /* Set the remote address that we are connecting to. */ memcpy(&vsk->remote_addr, remote_addr, sizeof(vsk->remote_addr)); err = vsock_auto_bind(vsk); if (err) goto out; sk->sk_state = SS_CONNECTING; err = transport->connect(vsk); if (err < 0) goto out; /* Mark sock as connecting and set the error code to in * progress in case this is a non-blocking connect. */ sock->state = SS_CONNECTING; err = -EINPROGRESS; } /* The receive path will handle all communication until we are able to * enter the connected state. Here we wait for the connection to be * completed or a notification of an error. */ timeout = vsk->connect_timeout; prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); while (sk->sk_state != SS_CONNECTED && sk->sk_err == 0) { if (flags & O_NONBLOCK) { /* If we're not going to block, we schedule a timeout * function to generate a timeout on the connection * attempt, in case the peer doesn't respond in a * timely manner. We hold on to the socket until the * timeout fires. */ sock_hold(sk); INIT_DELAYED_WORK(&vsk->dwork, vsock_connect_timeout); schedule_delayed_work(&vsk->dwork, timeout); /* Skip ahead to preserve error code set above. */ goto out_wait; } release_sock(sk); timeout = schedule_timeout(timeout); lock_sock(sk); if (signal_pending(current)) { err = sock_intr_errno(timeout); goto out_wait_error; } else if (timeout == 0) { err = -ETIMEDOUT; goto out_wait_error; } prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); } if (sk->sk_err) { err = -sk->sk_err; goto out_wait_error; } else err = 0; out_wait: finish_wait(sk_sleep(sk), &wait); out: release_sock(sk); return err; out_wait_error: sk->sk_state = SS_UNCONNECTED; sock->state = SS_UNCONNECTED; goto out_wait; }
/* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. */ static int udp_v6_get_port(struct sock *sk, unsigned short snum) { write_lock_bh(&udp_hash_lock); if (snum == 0) { int best_size_so_far, best, result, i; if (udp_port_rover > sysctl_local_port_range[1] || udp_port_rover < sysctl_local_port_range[0]) udp_port_rover = sysctl_local_port_range[0]; best_size_so_far = 32767; best = result = udp_port_rover; for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { struct sock *sk; int size; sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; if (!sk) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); goto gotit; } size = 0; do { if (++size >= best_size_so_far) goto next; } while ((sk = sk->next) != NULL); best_size_so_far = size; best = result; next:; } result = best; for(;; result += UDP_HTABLE_SIZE) { if (result > sysctl_local_port_range[1]) result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); if (!udp_lport_inuse(result)) break; } gotit: udp_port_rover = snum = result; } else { struct sock *sk2; int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { if (sk2->num == snum && sk2 != sk && sk2->bound_dev_if == sk->bound_dev_if && (!sk2->rcv_saddr || addr_type == IPV6_ADDR_ANY || !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, &sk2->net_pinfo.af_inet6.rcv_saddr) || (addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET && sk->rcv_saddr == sk2->rcv_saddr)) && (!sk2->reuse || !sk->reuse)) goto fail; } } sk->num = snum; if (sk->pprev == NULL) { struct sock **skp = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; sock_prot_inc_use(sk->prot); sock_hold(sk); } write_unlock_bh(&udp_hash_lock); return 0; fail: write_unlock_bh(&udp_hash_lock); return 1; }