staticint
vmci_transport_send_control_pkt_bh(struct sockaddr_vm *src, struct sockaddr_vm *dst, enum vmci_transport_packet_type type,
u64 size,
u64 mode, struct vmci_transport_waiting_info *wait, struct vmci_handle handle)
{ /* Note that it is safe to use a single packet across all CPUs since *twotaskletsofthesametypeareguaranteedtonoteverrun *simultaneously.Ifthateverchanges,orVMCIstopsusingtasklets, *wecanuseper-cpupackets.
*/ staticstruct vmci_transport_packet pkt;
/* We allow two kinds of sockets to communicate with a restricted VM: 1) *trustedsockets2)socketsfromapplicationsrunningasthesameuserasthe *VM(thisisonlytrueforthehostsideandonlywhenusinghostedproducts)
*/
/* This is invoked as part of a tasklet that's scheduled when the VMCI *interruptfires.Thisisruninbottom-halfcontextandifiteverneedsto *sleepitshoulddeferthatworktoaworkqueue.
*/
/* This handler is privileged when this module is running on the host. *Wewillgetdatagramsfromallendpoints(evenVMsthatareina *restrictedcontext).Ifwegetonefromarestrictedcontextthen *thedestinationsocketmustbetrusted. * *NOTE:Weaccessthesocketstructwithoutholdingthelockhere. *Thisisokbecausethefieldweareinterestedisnevermodified *outsideofthecreateanddestructsocketfunctions.
*/
vsk = vsock_sk(sk); if (!vmci_transport_allow_dgram(vsk, dg->src.context)) return VMCI_ERROR_NO_ACCESS;
size = VMCI_DG_SIZE(dg);
/* Attach the packet to the socket's receive queue as an sk_buff. */
skb = alloc_skb(size, GFP_ATOMIC); if (!skb) return VMCI_ERROR_NO_MEM;
/* sk_receive_skb() will do a sock_put(), so hold here. */
sock_hold(sk);
skb_put(skb, size);
memcpy(skb->data, dg, size);
sk_receive_skb(sk, skb, 0);
for (i = 0; i < ARRAY_SIZE(non_socket_contexts); i++) { if (cid == non_socket_contexts[i]) returnfalse;
}
returntrue;
}
/* This is invoked as part of a tasklet that's scheduled when the VMCI *interruptfires.Thisisruninbottom-halfcontextbutitdefersmostof *itsworktothepackethandlingworkqueue.
*/
sk = NULL;
err = VMCI_SUCCESS;
bh_process_pkt = false;
/* Ignore incoming packets from contexts without sockets, or resources *thataren'tvsockimplementations.
*/
if (!vmci_transport_stream_allow(dg->src.context, -1)
|| vmci_transport_peer_rid(dg->src.context) != dg->src.resource) return VMCI_ERROR_NO_ACCESS;
if (VMCI_DG_SIZE(dg) < sizeof(*pkt)) /* Drop datagrams that do not contain full VSock packets. */ return VMCI_ERROR_INVALID_ARGS;
pkt = (struct vmci_transport_packet *)dg;
/* Find the socket that should handle this packet. First we look for a *connectedsocketandifthereisnonewelookforasocketboundto *thedestintationaddress.
*/
vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
vsock_addr_init(&dst, pkt->dg.dst.context, pkt->dst_port);
sk = vsock_find_connected_socket(&src, &dst); if (!sk) {
sk = vsock_find_bound_socket(&dst); if (!sk) { /* We could not find a socket for this specified *address.IfthispacketisaRST,wejustdropit. *Ifitisanotherpacket,wesendaRST.Notethat *wedonotsendaRSTreplytoRSTssothatwedonot *continuallysendRSTsbetweentwoendpoints. * *Notethatsincethisisareply,dstissrcandsrc *isdst.
*/ if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
pr_err("unable to send reset\n");
err = VMCI_ERROR_NOT_FOUND; goto out;
}
}
/* If the received packet type is beyond all types known to this *implementation,replywithaninvalidmessage.Hopefullythiswill *helpwhenimplementingbackwardscompatibilityinthefuture.
*/ if (pkt->type >= VMCI_TRANSPORT_PACKET_TYPE_MAX) {
vmci_transport_send_invalid_bh(&dst, &src);
err = VMCI_ERROR_INVALID_ARGS; goto out;
}
/* This handler is privileged when this module is running on the host. *Wewillgetdatagramconnectrequestsfromallendpoints(evenVMs *thatareinarestrictedcontext).Ifwegetonefromarestricted *contextthenthedestinationsocketmustbetrusted. * *NOTE:Weaccessthesocketstructwithoutholdingthelockhere. *Thisisokbecausethefieldweareinterestedisnevermodified *outsideofthecreateanddestructsocketfunctions.
*/
vsk = vsock_sk(sk); if (!vmci_transport_allow_dgram(vsk, pkt->dg.src.context)) {
err = VMCI_ERROR_NO_ACCESS; goto out;
}
/* We do most everything in a work queue, but let's fast path the *notificationofreadsandwritestohelpdatatransferperformance. *Wecanonlydothisifthereisnoprocesscontextcodeexecuting *forthissocketsincethatmaychangethestate.
*/
bh_lock_sock(sk);
if (!sock_owned_by_user(sk)) { /* The local context ID may be out of date, update it. */
vsk->local_addr.svm_cid = dst.svm_cid;
schedule_work(&recv_pkt_info->work); /* Clear sk so that the reference count incremented by one of *theFindfunctionsaboveisnotdecrementedbelow.Weneed *thatreferencecountforthepackethandlerwe'vescheduled *torun.
*/
sk = NULL;
}
vsk = vsock_sk(sk); if (!vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle)) {
sock_set_flag(sk, SOCK_DONE);
/* On a detach the peer will not be sending or receiving *anymore.
*/
vsk->peer_shutdown = SHUTDOWN_MASK;
/* We should not be sending anymore since the peer won't be *theretoreceive,butwecanstillreceiveifthereisdata *leftinourconsumequeue.Ifthelocalendpointisahost, *wecan'tcallvsock_stream_has_data,sincethatmayblock, *butahostendpointcan'treaddataoncetheVMhas *detached,sothereisnoavailabledatainthatcase.
*/ if (vsk->local_addr.svm_cid == VMADDR_CID_HOST ||
vsock_stream_has_data(vsk) <= 0) { if (sk->sk_state == TCP_SYN_SENT) { /* The peer may detach from a queue pair while *wearestillintheconnectingstate,i.e., *ifthepeerVMiskilledafterattachingto *aqueuepair,butbeforewecompletethe *handshake.Inthatcase,wetreatthedetach *eventlikeareset.
*/
/* XXX This is lame, we should provide a way to lookup sockets by *qp_handle.
*/ if (vmci_handle_is_invalid(e_payload->handle) ||
!vmci_handle_is_equal(trans->qp_handle, e_payload->handle)) return;
/* We don't ask for delayed CBs when we subscribe to this event (we *pass0asflagstovmci_event_subscribe()).VMCImakesno *guaranteesinthatcaseaboutwhatcontextwemightberunningin, *soitcouldbeBHorprocess,blockableornon-blockable.Sowe *needtoaccountforallpossiblecontextshere.
*/
spin_lock_bh(&trans->lock); if (!trans->sk) goto out;
/* Apart from here, trans->lock is only grabbed as part of sk destruct, *wheretrans->skisn'tlocked.
*/
bh_lock_sock(trans->sk);
/* The local context ID may be out of date. */
vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
switch (sk->sk_state) { case TCP_LISTEN:
vmci_transport_recv_listen(sk, pkt); break; case TCP_SYN_SENT: /* Processing of pending connections for servers goes through *thelisteningsocket,soseevmci_transport_recv_listen() *forthatpath.
*/
vmci_transport_recv_connecting_client(sk, pkt); break; case TCP_ESTABLISHED:
vmci_transport_recv_connected(sk, pkt); break; default: /* Because this function does not run in the same context as *vmci_transport_recv_stream_cbitispossiblethatthe *sockethasclosed.Weneedtolettheothersideknoworit *couldbesittinginaconnectandhangforever.Senda *resettopreventthat.
*/
vmci_transport_send_reset(sk, pkt); break;
}
release_sock(sk);
kfree(recv_pkt_info); /* Release reference obtained in the stream callback when we fetched *thissocketoutoftheboundorconnectedlist.
*/
sock_put(sk);
}
/* Because we are in the listen state, we could be receiving a packet *forourselforanypreviousconnectionrequeststhatwereceived. *Ifit'sthelatter,wetrytofindasocketinourlistofpending *connectionsand,ifwedo,calltheappropriatehandlerforthe *statethatsocketisin.Otherwisewetrytoservicethe *connectionrequest.
*/
pending = vmci_transport_get_pending(sk, pkt); if (pending) {
lock_sock(pending);
/* The local context ID may be out of date. */
vsock_sk(pending)->local_addr.svm_cid = pkt->dg.dst.context;
if (pkt->u.size == 0) {
vmci_transport_reply_reset(pkt); return -EINVAL;
}
/* If this socket can't accommodate this connection request, we send a *reset.Otherwisewecreateandinitializeachildsocketandreply *withaconnectionnegotiation.
*/ if (sk->sk_ack_backlog >= sk->sk_max_ack_backlog) {
vmci_transport_reply_reset(pkt); return -ECONNREFUSED;
}
err = vsock_assign_transport(vpending, vsock_sk(sk)); /* Transport assigned (looking at remote_addr) must be the same *wherewereceivedtherequest.
*/ if (err || !vmci_check_transport(vpending)) {
vmci_transport_send_reset(sk, pkt);
sock_put(pending); return err;
}
/* If the proposed size fits within our min/max, accept it. Otherwise *proposeourownsize.
*/ if (pkt->u.size >= vpending->buffer_min_size &&
pkt->u.size <= vpending->buffer_max_size) {
qp_size = pkt->u.size;
} else {
qp_size = vpending->buffer_size;
}
/* Figure out if we are using old or new requests based on the *overridespkttypessentbyourpeer.
*/ if (vmci_transport_old_proto_override(&old_pkt_proto)) {
old_request = old_pkt_proto;
} else { if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST)
old_request = true; elseif (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST2)
old_request = false;
}
if (old_request) { /* Handle a REQUEST (or override) */
u16 version = VSOCK_PROTO_INVALID; if (vmci_transport_proto_to_notify_struct(
pending, &version, true))
err = vmci_transport_send_negotiate(pending, qp_size); else
err = -EINVAL;
} else { /* Handle a REQUEST2 (or override) */ int proto_int = pkt->proto; int pos;
u16 active_proto_version = 0;
/* The list of possible protocols is the intersection of all *protocolstheclientsupports...plusalltheprotocolswe *support.
*/
proto_int &= vmci_transport_new_proto_supported_versions();
/* We choose the highest possible protocol version and use that *one.
*/
pos = fls(proto_int); if (pos) {
active_proto_version = (1 << (pos - 1)); if (vmci_transport_proto_to_notify_struct(
pending, &active_proto_version, false))
err = vmci_transport_send_negotiate2(pending,
qp_size,
active_proto_version); else
err = -EINVAL;
/* We might never receive another message for this socket and it's not *connectedtoanyprocess,sowehavetoensureitgetscleanedup *ourself.Ourdelayedworkfunctionwilltakecareofthat.Note *thatwedonotevercancelthisfunctionsincewehavefew *guaranteesaboutitsstatewhencallingcancel_delayed_work(). *Insteadweholdareferenceonthesocketforthatfunctionandmake *itcapableofhandlingcaseswhereitneedstodonothingbut *releasethatreference.
*/
vpending->listener = sk;
sock_hold(sk);
sock_hold(pending);
schedule_delayed_work(&vpending->pending_work, HZ);
switch (pkt->type) { case VMCI_TRANSPORT_PACKET_TYPE_OFFER: if (vmci_handle_is_invalid(pkt->u.handle)) {
vmci_transport_send_reset(pending, pkt);
skerr = EPROTO;
err = -EINVAL; goto destroy;
} break; default: /* Close and cleanup the connection. */
vmci_transport_send_reset(pending, pkt);
skerr = EPROTO;
err = pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST ? 0 : -EINVAL; goto destroy;
}
/* In order to complete the connection we need to attach to the offered *queuepairandsendanattachnotification.Wealsosubscribetothe *detacheventsoweknowwhenourpeergoesaway,andwedothat *beforeattachingsowedon'tmissanevent.Ifallthissucceeds, *weupdateourstateandwakeupanythingwaitinginaccept()fora *connection.
*/
/* We don't care about attach since we ensure the other side has *attachedbyspecifyingtheATTACH_ONLYflagbelow.
*/
err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_DETACH,
vmci_transport_peer_detach_cb,
vmci_trans(vpending), &detach_sub_id); if (err < VMCI_SUCCESS) {
vmci_transport_send_reset(pending, pkt);
err = vmci_transport_error_to_vsock_error(err);
skerr = -err; goto destroy;
}
/* Now attach to the queue pair the client created. */
handle = pkt->u.handle;
/* vpending->local_addr always has a context id so we do not need to *worryaboutVMADDR_CID_ANYinthiscase.
*/
is_local =
vpending->remote_addr.svm_cid == vpending->local_addr.svm_cid;
flags = VMCI_QPFLAG_ATTACH_ONLY;
flags |= is_local ? VMCI_QPFLAG_LOCAL : 0;
/* When we send the attach message, we must be ready to handle incoming *controlmessagesonthenewlyconnectedsocket.Sowemovethe *pendingsockettotheconnectedstatebeforesendingtheattach *message.Otherwise,anincomingpackettriggeredbytheattachbeing *receivedbythepeermaybeprocessedconcurrentlywithwhathappens *belowaftersendingtheattachmessage,andthatincomingpacket *willfindthelisteningsocketinsteadofthe(currently)pending *socket.Notethatenqueueingthesocketincrementsthereference *count,soevenifaresetcomesbeforetheconnectionisaccepted, *thesocketwillbevaliduntilitisremovedfromthequeue. * *Ifwefailsendingtheattachbelow,weremovethesocketfromthe *connectedlistandmovethesockettoTCP_CLOSEbefore *releasingthelock,soapendingslowpathprocessingofanincoming *packetwillnotseethesocketintheconnectedstateinthatcase.
*/
pending->sk_state = TCP_ESTABLISHED;
/* We have a connection. Move the now connected socket from the *listener'spendinglisttotheacceptqueuesocallersofaccept() *canfindit.
*/
vsock_remove_pending(listener, pending);
vsock_enqueue_accept(listener, pending);
/* Callers of accept() will be waiting on the listening socket, not *thependingsocket.
*/
listener->sk_data_ready(listener);
return0;
destroy:
pending->sk_err = skerr;
pending->sk_state = TCP_CLOSE; /* As long as we drop our reference, all necessary cleanup will handle *whenthecleanupfunctiondropsitsreferenceandourdestruct *implementationiscalled.Notethatsincethelistenhandlerwill *removependingfromthependinglistuponourfailure,thecleanup *functionwon'tdroptheadditionalreference,whichiswhywedoit *here.
*/
sock_put(pending);
return err;
}
staticint
vmci_transport_recv_connecting_client(struct sock *sk, struct vmci_transport_packet *pkt)
{ struct vsock_sock *vsk; int err; int skerr;
vsk = vsock_sk(sk);
switch (pkt->type) { case VMCI_TRANSPORT_PACKET_TYPE_ATTACH: if (vmci_handle_is_invalid(pkt->u.handle) ||
!vmci_handle_is_equal(pkt->u.handle,
vmci_trans(vsk)->qp_handle)) {
skerr = EPROTO;
err = -EINVAL; goto destroy;
}
/* Signify the socket is connected and wakeup the waiter in *connect().Alsoplacethesocketintheconnectedtablefor *accounting(itcanalreadybefoundsinceit'sinthebound *table).
*/
sk->sk_state = TCP_ESTABLISHED;
sk->sk_socket->state = SS_CONNECTED;
vsock_insert_connected(vsk);
sk->sk_state_change(sk);
/* If we have gotten here then we should be past the point where old *linuxvsockcouldhavesentthebogusrst.
*/
vsk->sent_request = false;
vsk->ignore_connecting_rst = false;
/* Verify that we're OK with the proposed queue pair size */ if (pkt->u.size < vsk->buffer_min_size ||
pkt->u.size > vsk->buffer_max_size) {
err = -EINVAL; goto destroy;
}
/* At this point we know the CID the peer is using to talk to us. */
if (vsk->local_addr.svm_cid == VMADDR_CID_ANY)
vsk->local_addr.svm_cid = pkt->dg.dst.context;
/* Setup the notify ops to be the highest supported version that both *theserverandtheclientsupport.
*/
/* In cases where we are closing the connection, it's sufficient to *markthestatechange(andmaybeerror)andwakeupanywaiting *threads.Sincethisisaconnectedsocket,it'sownedbyauser *processandwillbecleanedupwhenthefailureispassedbackon *thecurrentornextsystemcall.Oursystemcallimplementations *mustthereforecheckforerrorandstatechangesonentryandwhen *beingawoken.
*/ switch (pkt->type) { case VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN: if (pkt->u.mode) {
vsk = vsock_sk(sk);
case VMCI_TRANSPORT_PACKET_TYPE_RST:
vsk = vsock_sk(sk); /* It is possible that we sent our peer a message (e.g a *WAITING_READ)rightbeforewegotnotifiedthatthepeerhad *detached.IfthathappensthenwecangetaRSTpktback *fromourpeereventhoughthereisdataavailableforusto *read.Inthatcase,don'tshutdownthesocketcompletelybut *insteadallowthelocalclienttofinishreadingdataoff *thequeuepair.AlwaystreataRSTpktinconnectedmodelike *acleanshutdown.
*/
sock_set_flag(sk, SOCK_DONE);
vsk->peer_shutdown = SHUTDOWN_MASK; if (vsock_stream_has_data(vsk) <= 0)
sk->sk_state = TCP_CLOSING;
staticvoid vmci_transport_destruct(struct vsock_sock *vsk)
{ /* transport can be NULL if we hit a failure at init() time */ if (!vmci_trans(vsk)) return;
/* Ensure that the detach callback doesn't use the sk/vsk *weareabouttodestruct.
*/
spin_lock_bh(&vmci_trans(vsk)->lock);
vmci_trans(vsk)->sk = NULL;
spin_unlock_bh(&vmci_trans(vsk)->lock);
if (vmci_trans(vsk)->notify_ops)
vmci_trans(vsk)->notify_ops->socket_destruct(vsk);
/* VMCI will select a resource ID for us if we provide *VMCI_INVALID_ID.
*/
port = addr->svm_port == VMADDR_PORT_ANY ?
VMCI_INVALID_ID : addr->svm_port;
if (port <= LAST_RESERVED_PORT && !capable(CAP_NET_BIND_SERVICE)) return -EACCES;
if (old_pkt_proto) { if (*proto != VSOCK_PROTO_INVALID) {
pr_err("Can't set both an old and new protocol\n"); returnfalse;
}
vmci_trans(vsk)->notify_ops = &vmci_transport_notify_pkt_ops; gotoexit;
}
staticvoid vmci_vsock_transport_cb(bool is_host)
{ int features;
if (is_host)
features = VSOCK_TRANSPORT_F_H2G; else
features = VSOCK_TRANSPORT_F_G2H;
vsock_core_register(&vmci_transport, features);
}
staticint __init vmci_transport_init(void)
{ int err;
/* Create the datagram handle that we will use to send and receive all *VSocketcontrolmessagesforthiscontext.
*/
err = vmci_transport_datagram_create_hnd(VMCI_TRANSPORT_PACKET_RID,
VMCI_FLAG_ANYCID_DG_HND,
vmci_transport_recv_stream_cb,
NULL,
&vmci_transport_stream_handle); if (err < VMCI_SUCCESS) {
pr_err("Unable to create datagram handle. (%d)\n", err); return vmci_transport_error_to_vsock_error(err);
}
err = vmci_event_subscribe(VMCI_EVENT_QP_RESUMED,
vmci_transport_qp_resumed_cb,
NULL, &vmci_transport_qp_resumed_sub_id); if (err < VMCI_SUCCESS) {
pr_err("Unable to subscribe to resumed event. (%d)\n", err);
err = vmci_transport_error_to_vsock_error(err);
vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID; goto err_destroy_stream_handle;
}
/* Register only with dgram feature, other features (H2G, G2H) will be *registeredwhenthefirsthostorguestbecomesactive.
*/
err = vsock_core_register(&vmci_transport, VSOCK_TRANSPORT_F_DGRAM); if (err < 0) goto err_unsubscribe;
err = vmci_register_vsock_callback(vmci_vsock_transport_cb); if (err < 0) goto err_unregister;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.