2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2000-2001 Boris Popov
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/endian.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/malloc.h>
38 #include <sys/unistd.h>
40 #include <netsmb/smb.h>
41 #include <netsmb/smb_conn.h>
42 #include <netsmb/smb_rq.h>
43 #include <netsmb/smb_tran.h>
44 #include <netsmb/smb_trantcp.h>
46 #define SMBIOD_SLEEP_TIMO 2
47 #define SMBIOD_PING_TIMO 60 /* seconds */
49 #define SMB_IOD_EVLOCKPTR(iod) (&((iod)->iod_evlock))
50 #define SMB_IOD_EVLOCK(iod) smb_sl_lock(&((iod)->iod_evlock))
51 #define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&((iod)->iod_evlock))
53 #define SMB_IOD_RQLOCKPTR(iod) (&((iod)->iod_rqlock))
54 #define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock))
55 #define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&((iod)->iod_rqlock))
57 #define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags)
59 static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
61 static int smb_iod_next;
63 static int smb_iod_sendall(struct smbiod *iod);
64 static int smb_iod_disconnect(struct smbiod *iod);
65 static void smb_iod_thread(void *);
68 smb_iod_rqprocessed(struct smb_rq *rqp, int error)
71 rqp->sr_lerror = error;
73 rqp->sr_state = SMBRQ_NOTIFIED;
74 wakeup(&rqp->sr_state);
79 smb_iod_invrq(struct smbiod *iod)
84 * Invalidate all outstanding requests for this connection
87 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
88 rqp->sr_flags |= SMBR_RESTART;
89 smb_iod_rqprocessed(rqp, ENOTCONN);
91 SMB_IOD_RQUNLOCK(iod);
95 smb_iod_closetran(struct smbiod *iod)
97 struct smb_vc *vcp = iod->iod_vc;
98 struct thread *td = iod->iod_td;
100 if (vcp->vc_tdata == NULL)
102 SMB_TRAN_DISCONNECT(vcp, td);
103 SMB_TRAN_DONE(vcp, td);
104 vcp->vc_tdata = NULL;
108 smb_iod_dead(struct smbiod *iod)
110 iod->iod_state = SMBIOD_ST_DEAD;
111 smb_iod_closetran(iod);
116 smb_iod_connect(struct smbiod *iod)
118 struct smb_vc *vcp = iod->iod_vc;
119 struct thread *td = iod->iod_td;
122 SMBIODEBUG("%d\n", iod->iod_state);
123 switch(iod->iod_state) {
124 case SMBIOD_ST_VCACTIVE:
125 SMBERROR("called for already opened connection\n");
128 return ENOTCONN; /* XXX: last error code ? */
135 error = (int)SMB_TRAN_CREATE(vcp, td);
138 SMBIODEBUG("tcreate\n");
140 error = (int)SMB_TRAN_BIND(vcp, vcp->vc_laddr, td);
144 SMBIODEBUG("tbind\n");
145 error = (int)SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td);
148 SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
149 iod->iod_state = SMBIOD_ST_TRANACTIVE;
150 SMBIODEBUG("tconnect\n");
151 /* vcp->vc_mid = 0;*/
152 error = (int)smb_smb_negotiate(vcp, &iod->iod_scred);
155 SMBIODEBUG("snegotiate\n");
156 error = (int)smb_smb_ssnsetup(vcp, &iod->iod_scred);
159 iod->iod_state = SMBIOD_ST_VCACTIVE;
160 SMBIODEBUG("completed\n");
170 smb_iod_disconnect(struct smbiod *iod)
172 struct smb_vc *vcp = iod->iod_vc;
175 if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
176 smb_smb_ssnclose(vcp, &iod->iod_scred);
177 iod->iod_state = SMBIOD_ST_TRANACTIVE;
179 vcp->vc_smbuid = SMB_UID_UNKNOWN;
180 smb_iod_closetran(iod);
181 iod->iod_state = SMBIOD_ST_NOTCONN;
186 smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
190 if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
191 if (iod->iod_state != SMBIOD_ST_DEAD)
193 iod->iod_state = SMBIOD_ST_RECONNECT;
194 error = smb_iod_connect(iod);
198 SMBIODEBUG("tree reconnect\n");
200 ssp->ss_flags |= SMBS_RECONNECTING;
202 error = smb_smb_treeconnect(ssp, &iod->iod_scred);
204 ssp->ss_flags &= ~SMBS_RECONNECTING;
206 wakeup(&ssp->ss_vcgenid);
211 smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
213 struct thread *td = iod->iod_td;
214 struct smb_vc *vcp = iod->iod_vc;
215 struct smb_share *ssp = rqp->sr_share;
219 SMBIODEBUG("iod_state = %d\n", iod->iod_state);
220 switch (iod->iod_state) {
221 case SMBIOD_ST_NOTCONN:
222 smb_iod_rqprocessed(rqp, ENOTCONN);
225 iod->iod_state = SMBIOD_ST_RECONNECT;
227 case SMBIOD_ST_RECONNECT:
232 if (rqp->sr_sendcnt == 0) {
233 #ifdef movedtoanotherplace
234 if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
237 le16enc(rqp->sr_rqtid, ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
238 le16enc(rqp->sr_rquid, vcp ? vcp->vc_smbuid : 0);
239 mb_fixhdr(&rqp->sr_rq);
240 if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
243 if (rqp->sr_sendcnt++ > 5) {
244 rqp->sr_flags |= SMBR_RESTART;
245 smb_iod_rqprocessed(rqp, rqp->sr_lerror);
247 * If all attempts to send a request failed, then
248 * something is seriously hosed.
252 SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
253 m_dumpm(rqp->sr_rq.mb_top);
254 m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAITOK);
255 error = rqp->sr_lerror = SMB_TRAN_SEND(vcp, m, td);
257 getnanotime(&rqp->sr_timesent);
258 iod->iod_lastrqsent = rqp->sr_timesent;
259 rqp->sr_flags |= SMBR_SENT;
260 rqp->sr_state = SMBRQ_SENT;
264 * Check for fatal errors
266 if (SMB_TRAN_FATAL(vcp, error)) {
268 * No further attempts should be made
272 if (smb_rq_intr(rqp))
273 smb_iod_rqprocessed(rqp, EINTR);
278 * Process incoming packets
281 smb_iod_recvall(struct smbiod *iod)
283 struct smb_vc *vcp = iod->iod_vc;
284 struct thread *td = iod->iod_td;
291 switch (iod->iod_state) {
292 case SMBIOD_ST_NOTCONN:
294 case SMBIOD_ST_RECONNECT:
301 error = SMB_TRAN_RECV(vcp, &m, td);
302 if (error == EWOULDBLOCK)
304 if (SMB_TRAN_FATAL(vcp, error)) {
311 SMBERROR("tran return NULL without error\n");
315 m = m_pullup(m, SMB_HDRLEN);
317 continue; /* wait for a good packet */
319 * Now we got an entire and possibly invalid SMB packet.
320 * Be careful while parsing it.
323 hp = mtod(m, u_char*);
324 if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
328 mid = SMB_HDRMID(hp);
329 SMBSDEBUG("mid %04x\n", (u_int)mid);
331 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
332 if (rqp->sr_mid != mid)
335 if (rqp->sr_rp.md_top == NULL) {
336 md_initm(&rqp->sr_rp, m);
338 if (rqp->sr_flags & SMBR_MULTIPACKET) {
339 md_append_record(&rqp->sr_rp, m);
342 SMBERROR("duplicate response %d (ignored)\n", mid);
347 smb_iod_rqprocessed(rqp, 0);
350 SMB_IOD_RQUNLOCK(iod);
352 SMBERROR("drop resp with mid %d\n", (u_int)mid);
353 /* smb_printrqlist(vcp);*/
358 * check for interrupts
361 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
362 if (smb_td_intr(rqp->sr_cred->scr_td)) {
363 smb_iod_rqprocessed(rqp, EINTR);
366 SMB_IOD_RQUNLOCK(iod);
371 smb_iod_request(struct smbiod *iod, int event, void *ident)
373 struct smbiod_event *evp;
377 evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
378 evp->ev_type = event;
379 evp->ev_ident = ident;
381 STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
382 if ((event & SMBIOD_EV_SYNC) == 0) {
383 SMB_IOD_EVUNLOCK(iod);
388 msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
389 error = evp->ev_error;
395 * Place request in the queue.
396 * Request from smbiod have a high priority.
399 smb_iod_addrq(struct smb_rq *rqp)
401 struct smb_vc *vcp = rqp->sr_vc;
402 struct smbiod *iod = vcp->vc_iod;
406 if (rqp->sr_cred->scr_td != NULL &&
407 rqp->sr_cred->scr_td->td_proc == iod->iod_p) {
408 rqp->sr_flags |= SMBR_INTERNAL;
410 TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
411 SMB_IOD_RQUNLOCK(iod);
413 if (smb_iod_sendrq(iod, rqp) != 0) {
418 * we don't need to lock state field here
420 if (rqp->sr_state != SMBRQ_NOTSENT)
422 tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
425 smb_iod_removerq(rqp);
426 return rqp->sr_lerror;
429 switch (iod->iod_state) {
430 case SMBIOD_ST_NOTCONN:
433 error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
443 if (vcp->vc_maxmux == 0) {
444 SMBERROR("maxmux == 0\n");
447 if (iod->iod_muxcnt < vcp->vc_maxmux)
450 msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
454 TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
455 SMB_IOD_RQUNLOCK(iod);
461 smb_iod_removerq(struct smb_rq *rqp)
463 struct smb_vc *vcp = rqp->sr_vc;
464 struct smbiod *iod = vcp->vc_iod;
467 if (rqp->sr_flags & SMBR_INTERNAL) {
469 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
470 SMB_IOD_RQUNLOCK(iod);
474 while (rqp->sr_flags & SMBR_XLOCK) {
475 rqp->sr_flags |= SMBR_XLOCKWANT;
476 msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
478 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
480 if (iod->iod_muxwant) {
482 wakeup(&iod->iod_muxwant);
484 SMB_IOD_RQUNLOCK(iod);
489 smb_iod_waitrq(struct smb_rq *rqp)
491 struct smbiod *iod = rqp->sr_vc->vc_iod;
495 if (rqp->sr_flags & SMBR_INTERNAL) {
497 smb_iod_sendall(iod);
498 smb_iod_recvall(iod);
499 if (rqp->sr_rpgen != rqp->sr_rplast)
501 tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
503 smb_iod_removerq(rqp);
504 return rqp->sr_lerror;
507 if (rqp->sr_rpgen == rqp->sr_rplast)
508 msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
511 error = rqp->sr_lerror;
512 if (rqp->sr_flags & SMBR_MULTIPACKET) {
514 * If request should stay in the list, then reinsert it
515 * at the end of queue so other waiters have chance to concur
518 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
519 TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
520 SMB_IOD_RQUNLOCK(iod);
522 smb_iod_removerq(rqp);
527 smb_iod_sendall(struct smbiod *iod)
529 struct smb_vc *vcp = iod->iod_vc;
531 struct timespec ts, tstimeout;
536 * Loop through the list of requests and send them if possible
539 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
540 switch (rqp->sr_state) {
542 rqp->sr_flags |= SMBR_XLOCK;
543 SMB_IOD_RQUNLOCK(iod);
544 herror = smb_iod_sendrq(iod, rqp);
546 rqp->sr_flags &= ~SMBR_XLOCK;
547 if (rqp->sr_flags & SMBR_XLOCKWANT) {
548 rqp->sr_flags &= ~SMBR_XLOCKWANT;
553 SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
554 timespecadd(&tstimeout, &tstimeout, &tstimeout);
556 timespecsub(&ts, &tstimeout, &ts);
557 if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
558 smb_iod_rqprocessed(rqp, ETIMEDOUT);
567 SMB_IOD_RQUNLOCK(iod);
568 if (herror == ENOTCONN)
574 * "main" function for smbiod daemon
577 smb_iod_main(struct smbiod *iod)
579 /* struct smb_vc *vcp = iod->iod_vc;*/
580 struct smbiod_event *evp;
581 /* struct timespec tsnow;*/
586 * Check all interesting events
590 evp = STAILQ_FIRST(&iod->iod_evlist);
592 SMB_IOD_EVUNLOCK(iod);
595 STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
596 evp->ev_type |= SMBIOD_EV_PROCESSING;
597 SMB_IOD_EVUNLOCK(iod);
598 switch (evp->ev_type & SMBIOD_EV_MASK) {
599 case SMBIOD_EV_CONNECT:
600 iod->iod_state = SMBIOD_ST_RECONNECT;
601 evp->ev_error = smb_iod_connect(iod);
603 case SMBIOD_EV_DISCONNECT:
604 evp->ev_error = smb_iod_disconnect(iod);
606 case SMBIOD_EV_TREECONNECT:
607 evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
609 case SMBIOD_EV_SHUTDOWN:
610 iod->iod_flags |= SMBIOD_SHUTDOWN;
612 case SMBIOD_EV_NEWRQ:
615 if (evp->ev_type & SMBIOD_EV_SYNC) {
618 SMB_IOD_EVUNLOCK(iod);
623 if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
625 timespecsub(&tsnow, &iod->iod_pingtimo, &tsnow);
626 if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
627 smb_smb_echo(vcp, &iod->iod_scred);
631 smb_iod_sendall(iod);
632 smb_iod_recvall(iod);
637 smb_iod_thread(void *arg)
639 struct smbiod *iod = arg;
644 * Here we assume that the thread structure will be the same
645 * for an entire kthread (kproc, to be more precise) life.
647 iod->iod_td = curthread;
648 smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
649 while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
651 SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
652 if (iod->iod_flags & SMBIOD_SHUTDOWN)
654 tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
657 /* We can now safely destroy the mutexes and free the iod structure. */
658 smb_sl_destroy(&iod->iod_rqlock);
659 smb_sl_destroy(&iod->iod_evlock);
666 smb_iod_create(struct smb_vc *vcp)
671 iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
672 iod->iod_id = smb_iod_next++;
673 iod->iod_state = SMBIOD_ST_NOTCONN;
675 iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
676 iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
677 getnanotime(&iod->iod_lastrqsent);
679 smb_sl_init(&iod->iod_rqlock, "90rql");
680 TAILQ_INIT(&iod->iod_rqlist);
681 smb_sl_init(&iod->iod_evlock, "90evl");
682 STAILQ_INIT(&iod->iod_evlist);
683 error = kproc_create(smb_iod_thread, iod, &iod->iod_p,
684 RFNOWAIT, 0, "smbiod%d", iod->iod_id);
686 SMBERROR("can't start smbiod: %d", error);
688 smb_sl_destroy(&iod->iod_rqlock);
689 smb_sl_destroy(&iod->iod_evlock);
697 smb_iod_destroy(struct smbiod *iod)
699 smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);