2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
13 #include <afsconfig.h>
14 #include "afs/param.h"
18 #include "afs/sysincludes.h" /* Standard vendor system headers */
21 #if !defined(AFS_LINUX20_ENV)
24 #include <netinet/in.h>
27 #include "h/hashing.h"
29 #if !defined(AFS_HPUX110_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV)
30 #include <netinet/in_var.h>
31 #endif /* ! AFS_HPUX110_ENV */
32 #endif /* !defined(UKERNEL) */
34 #include "afsincludes.h" /* Afs-based standard headers */
35 #include "afs/afs_stats.h" /* afs statistics */
37 #if defined(AFS_SUN5_ENV)
39 #include <inet/common.h>
40 #include <netinet/ip6.h>
44 /* Exported variables */
45 afs_rwlock_t afs_xconn
; /* allocation lock for new things */
46 afs_rwlock_t afs_xinterface
; /* for multiple client address */
47 afs_int32 cryptall
= 1; /* encrypt all communications */
49 /* some connection macros */
52 #define new_conn_vector(xcv) \
54 xcv = (struct sa_conn_vector *) \
55 afs_osi_Alloc(sizeof(struct sa_conn_vector)); \
57 memset((char *)xcv, 0, sizeof(struct sa_conn_vector)); \
61 /* select a connection to return (if no connection has lower utilization
63 #define conn_vec_select_conn(xcv, bix, conn) \
65 (bix) = ((xcv)->select_index)++ % CVEC_LEN; \
66 (conn) = &((xcv)->cvec[bix]); \
69 #define struct_conn(s) ((struct afs_conn *)(s))
71 #define REPORT_CONNECTIONS_ISSUED 0 /* enable to see utilization */
74 * Find a connection with call slots available, allocating one
75 * if nothing is available and we find an allocated slot
76 * @param xcv A connection vector
77 * @param create If set, a new connection may be created
79 static struct afs_conn
*
80 find_preferred_connection(struct sa_conn_vector
*xcv
, int create
)
83 struct afs_conn
*tc
= NULL
;
86 for(cix
= 0; cix
< CVEC_LEN
; ++cix
) {
87 tc
= &(xcv
->cvec
[cix
]);
91 tc
->forceConnectFS
= 1;
97 if (tc
->refCount
< (RX_MAXCALLS
-1)) {
100 } else if (cix
== (CVEC_LEN
-1))
101 conn_vec_select_conn(xcv
, bix
, tc
);
103 } /* for cix < CVEC_LEN */
106 afs_warn("find_preferred_connection: no connection and !create\n");
115 #if REPORT_CONNECTIONS_ISSUED
116 afs_warn("Issuing conn %d refCount=%d parent refCount=%d\n", bix
,
117 tc
->refCount
, xcv
->refCount
);
123 } /* find_preferred_connection */
127 * Release all connections for unix user xu at server xs
132 release_conns_user_server(struct unixuser
*xu
, struct server
*xs
)
137 struct sa_conn_vector
*tcv
, **lcv
, *tcvn
;
138 for (sa
= (xs
)->addr
; sa
; sa
= sa
->next_sa
) {
140 for (tcv
= *lcv
; tcv
; lcv
= &tcv
->next
, tcv
= *lcv
) {
141 if (tcv
->user
== (xu
) && tcv
->refCount
== 0) {
143 /* our old friend, the GLOCK */
144 glocked
= ISAFS_GLOCK();
147 for(cix
= 0; cix
< CVEC_LEN
; ++cix
) {
148 tc
= &(tcv
->cvec
[cix
]);
150 rx_SetConnSecondsUntilNatPing(tc
->id
, 0);
151 rx_DestroyConnection(tc
->id
);
152 /* find another eligible connection */
153 if (sa
->natping
== tc
) {
155 struct afs_conn
*tcn
;
156 for (tcvn
= sa
->conns
; tcvn
; tcvn
= tcvn
->next
) {
159 for(cin
= 0; cin
< CVEC_LEN
; ++cin
) {
160 tcn
= &(tcvn
->cvec
[cin
]);
161 if (tcn
->activated
) {
162 rx_SetConnSecondsUntilNatPing(tcn
->id
, 20);
173 afs_osi_Free(tcv
, sizeof(struct sa_conn_vector
));
174 break; /* at most one instance per server */
175 } /*Found unreferenced connection for user */
177 } /*For each connection on the server */
179 } /* release_conns_user_server */
183 release_conns_vector(struct sa_conn_vector
*tcv
)
187 struct sa_conn_vector
*next
;
189 while (tcv
!= NULL
) {
192 /* you know it, you love it, the GLOCK */
193 glocked
= ISAFS_GLOCK();
196 for(cix
= 0; cix
< CVEC_LEN
; ++cix
) {
197 tc
= &(tcv
->cvec
[cix
]);
199 rx_SetConnSecondsUntilNatPing(tc
->id
, 0);
200 rx_DestroyConnection(tc
->id
);
201 if (tcv
->srvr
->natping
== tc
)
202 tcv
->srvr
->natping
= NULL
;
207 afs_osi_Free(tcv
, sizeof(struct sa_conn_vector
));
211 } /* release_conns_vector */
214 unsigned int VNOSERVERS
= 0;
217 * Pick a security object to use for a connection to a given server,
221 * The AFS connection for which the security object is required
222 * @param[out] secLevel
223 * The security level of the returned object
226 * An rx security object. This function is guaranteed to return
227 * an object, although that object may be rxnull (with a secLevel
230 static struct rx_securityClass
*
231 afs_pickSecurityObject(struct afs_conn
*conn
, int *secLevel
)
233 struct rx_securityClass
*secObj
= NULL
;
234 union tokenUnion
*token
;
236 /* Do we have tokens ? */
237 if (conn
->parent
->user
->states
& UHasTokens
) {
238 token
= afs_FindToken(conn
->parent
->user
->tokens
, RX_SECIDX_KAD
);
240 *secLevel
= RX_SECIDX_KAD
;
241 /* kerberos tickets on channel 2 */
242 secObj
= rxkad_NewClientSecurityObject(
243 cryptall
? rxkad_crypt
: rxkad_clear
,
244 (struct ktc_encryptionKey
*)
245 token
->rxkad
.clearToken
.HandShakeKey
,
246 token
->rxkad
.clearToken
.AuthHandle
,
247 token
->rxkad
.ticketLen
, token
->rxkad
.ticket
);
248 /* We're going to use this token, so populate the viced */
249 conn
->parent
->user
->viceId
= token
->rxkad
.clearToken
.ViceId
;
252 if (secObj
== NULL
) {
254 secObj
= rxnull_NewClientSecurityObject();
262 * Try setting up a connection to the server containing the specified fid.
263 * Gets the volume, checks if it's up and does the connection by server address.
266 * @param areq Request filled in by the caller.
267 * @param locktype Type of lock that will be used.
269 * @return The conn struct, or NULL.
272 afs_Conn(struct VenusFid
*afid
, struct vrequest
*areq
,
273 afs_int32 locktype
, struct rx_connection
**rxconn
)
275 u_short fsport
= AFS_FSPORT
;
277 struct afs_conn
*tconn
= NULL
;
278 struct srvAddr
*lowp
= NULL
;
282 struct srvAddr
*sa1p
;
283 afs_int32 replicated
= -1; /* a single RO will increment to 0 */
287 AFS_STATCNT(afs_Conn
);
288 /* Get fid's volume. */
289 tv
= afs_GetVolume(afid
, areq
, READ_LOCK
);
292 afs_FinalizeReq(areq
);
293 areq
->volumeError
= 1;
298 if (tv
->serverHost
[0] && tv
->serverHost
[0]->cell
) {
299 fsport
= tv
->serverHost
[0]->cell
->fsport
;
304 /* First is always lowest rank, if it's up */
305 if ((tv
->status
[0] == not_busy
) && tv
->serverHost
[0]
306 && tv
->serverHost
[0]->addr
307 && !(tv
->serverHost
[0]->addr
->sa_flags
& SRVR_ISDOWN
) &&
308 !(((areq
->idleError
> 0) || (areq
->tokenError
> 0))
309 && (areq
->skipserver
[0] == 1)))
310 lowp
= tv
->serverHost
[0]->addr
;
312 /* Otherwise we look at all of them. There are seven levels of
313 * not_busy. This means we will check a volume seven times before it
314 * is marked offline. Ideally, we only need two levels, but this
315 * serves a second purpose of waiting some number of seconds before
316 * the client decides the volume is offline (ie: a clone could finish
319 for (notbusy
= not_busy
; (!lowp
&& (notbusy
<= end_not_busy
)); notbusy
++) {
320 for (i
= 0; i
< AFS_MAXHOSTS
&& tv
->serverHost
[i
]; i
++) {
321 if (tv
->states
& VRO
)
323 if (((areq
->tokenError
> 0)||(areq
->idleError
> 0))
324 && (areq
->skipserver
[i
] == 1))
326 if (tv
->status
[i
] != notbusy
) {
327 if (tv
->status
[i
] == rd_busy
|| tv
->status
[i
] == rdwr_busy
) {
328 if (!areq
->busyCount
)
330 } else if (tv
->status
[i
] == offline
) {
331 if (!areq
->volumeError
)
332 areq
->volumeError
= VOLMISSING
;
336 for (sa1p
= tv
->serverHost
[i
]->addr
; sa1p
; sa1p
= sa1p
->next_sa
) {
337 if (sa1p
->sa_flags
& SRVR_ISDOWN
)
339 if (!lowp
|| (lowp
->sa_iprank
> sa1p
->sa_iprank
))
344 if ((replicated
== -1) && (tv
->states
& VRO
)) {
345 for (i
= 0; i
< AFS_MAXHOSTS
&& tv
->serverHost
[i
]; i
++) {
346 if (tv
->states
& VRO
)
352 afs_PutVolume(tv
, READ_LOCK
);
355 tu
= afs_GetUser(areq
->uid
, afid
->Cell
, SHARED_LOCK
);
356 tconn
= afs_ConnBySA(lowp
, fsport
, afid
->Cell
, tu
, 0 /*!force */ ,
357 1 /*create */ , locktype
, replicated
, rxconn
);
359 afs_PutUser(tu
, SHARED_LOCK
);
367 * Connects to a server by it's server address.
369 * @param sap Server address.
370 * @param aport Server port.
372 * @param tu Connect as this user.
373 * @param force_if_down
376 * @param locktype Specifies type of lock to be used for this function.
378 * @return The new connection.
381 afs_ConnBySA(struct srvAddr
*sap
, unsigned short aport
, afs_int32 acell
,
382 struct unixuser
*tu
, int force_if_down
, afs_int32 create
,
383 afs_int32 locktype
, afs_int32 replicated
,
384 struct rx_connection
**rxconn
)
386 int glocked
, foundvec
;
387 struct afs_conn
*tc
= NULL
;
388 struct sa_conn_vector
*tcv
= NULL
;
389 struct rx_securityClass
*csec
; /*Security class object */
390 int isec
; /*Security index */
392 int isrep
= (replicated
> 0)?CONN_REPLICATED
:0;
396 if (!sap
|| ((sap
->sa_flags
& SRVR_ISDOWN
) && !force_if_down
)) {
397 /* sa is known down, and we don't want to force it. */
401 /* find cached connection */
402 ObtainSharedLock(&afs_xconn
, 15);
404 for (tcv
= sap
->conns
; tcv
; tcv
= tcv
->next
) {
405 if (tcv
->user
== tu
&& tcv
->port
== aport
&&
406 (isrep
== (tcv
->flags
& CONN_REPLICATED
))) {
407 /* return most eligible conn */
410 UpgradeSToWLock(&afs_xconn
, 37);
411 tc
= find_preferred_connection(tcv
, create
);
412 ConvertWToSLock(&afs_xconn
);
417 if (!tc
&& !create
) {
418 /* Not found and can't create a new one. */
419 ReleaseSharedLock(&afs_xconn
);
423 if (AFS_IS_DISCONNECTED
&& !AFS_IN_SYNC
) {
424 afs_warnuser("afs_ConnBySA: disconnected\n");
425 ReleaseSharedLock(&afs_xconn
);
429 if (!foundvec
&& create
) {
430 /* No such connection vector exists. Create one and splice it in.
431 * Make sure the server record has been marked as used (for the purposes
432 * of calculating up & down times, it's now considered to be an
433 * ``active'' server). Also make sure the server's lastUpdateEvalTime
434 * gets set, marking the time of its ``birth''.
436 UpgradeSToWLock(&afs_xconn
, 37);
437 new_conn_vector(tcv
);
442 tcv
->next
= sap
->conns
;
444 tcv
->flags
|= CONN_REPLICATED
;
447 /* all struct afs_conn ptrs come from here */
448 tc
= find_preferred_connection(tcv
, create
);
450 afs_ActivateServer(sap
);
452 ConvertWToSLock(&afs_xconn
);
453 } /* end of if (!tcv) */
456 /* Not found and no alternatives. */
457 ReleaseSharedLock(&afs_xconn
);
461 if (tc
->refCount
> 10000) {
465 afs_warn("afs: Very high afs_conn refCount detected (conn %p, count %d)\n",
466 tc
, (int)tc
->refCount
);
467 afs_warn("afs: Trying to continue, but this may indicate an issue\n");
468 afs_warn("afs: that may eventually crash the machine. Please file\n");
469 afs_warn("afs: a bug report.\n");
473 if (tu
->states
& UTokensBad
) {
474 /* we may still have an authenticated RPC connection here,
475 * we'll have to create a new, unauthenticated, connection.
476 * Perhaps a better way to do this would be to set
477 * conn->forceConnectFS on all conns when the token first goes
478 * bad, but that's somewhat trickier, due to locking
479 * constraints (though not impossible).
481 if (tc
->id
&& (rx_SecurityClassOf(tc
->id
) != RX_SECIDX_NULL
)) {
482 tc
->forceConnectFS
= 1; /* force recreation of connection */
484 tu
->states
&= ~UHasTokens
; /* remove the authentication info */
487 glocked
= ISAFS_GLOCK();
488 if (tc
->forceConnectFS
) {
489 UpgradeSToWLock(&afs_xconn
, 38);
491 if (sap
->natping
== tc
)
495 rx_SetConnSecondsUntilNatPing(tc
->id
, 0);
496 rx_DestroyConnection(tc
->id
);
501 * Stupid hack to determine if using vldb service or file system
504 if (aport
== sap
->server
->cell
->vlport
)
510 csec
= afs_pickSecurityObject(tc
, &isec
);
514 tc
->id
= rx_NewConnection(sap
->sa_ip
, aport
, service
, csec
, isec
);
518 rx_SetConnHardDeadTime(tc
->id
, afs_rx_harddead
);
521 /* Setting idle dead time to non-zero activates idle-dead
522 * RX_CALL_TIMEOUT errors. */
524 rx_SetConnIdleDeadTime(tc
->id
, afs_rx_idledead_rep
);
526 rx_SetConnIdleDeadTime(tc
->id
, afs_rx_idledead
);
529 * Only do this for one connection
531 if ((service
!= 52) && (sap
->natping
== NULL
)) {
533 rx_SetConnSecondsUntilNatPing(tc
->id
, 20);
536 tc
->forceConnectFS
= 0; /* apparently we're appropriately connected now */
539 ConvertWToSLock(&afs_xconn
);
540 } /* end of if (tc->forceConnectFS)*/
543 rx_GetConnection(*rxconn
);
545 ReleaseSharedLock(&afs_xconn
);
550 * forceConnectFS is set whenever we must recompute the connection. UTokensBad
551 * is true only if we know that the tokens are bad. We thus clear this flag
552 * when we get a new set of tokens..
553 * Having force... true and UTokensBad true simultaneously means that the tokens
554 * went bad and we're supposed to create a new, unauthenticated, connection.
556 * @param aserver Server to connect to.
557 * @param aport Connection port.
558 * @param acell The cell where all of this happens.
559 * @param areq The request.
560 * @param aforce Force connection?
561 * @param locktype Type of lock to be used.
564 * @return The established connection.
567 afs_ConnByHost(struct server
*aserver
, unsigned short aport
, afs_int32 acell
,
568 struct vrequest
*areq
, int aforce
, afs_int32 locktype
,
569 afs_int32 replicated
, struct rx_connection
**rxconn
)
572 struct afs_conn
*tc
= NULL
;
573 struct srvAddr
*sa
= NULL
;
577 AFS_STATCNT(afs_ConnByHost
);
579 if (AFS_IS_DISCONNECTED
&& !AFS_IN_SYNC
) {
580 afs_warnuser("afs_ConnByHost: disconnected\n");
585 1. look for an existing connection
586 2. create a connection at an address believed to be up
587 (if aforce is true, create a connection at the first address)
590 tu
= afs_GetUser(areq
->uid
, acell
, SHARED_LOCK
);
592 for (sa
= aserver
->addr
; sa
; sa
= sa
->next_sa
) {
593 tc
= afs_ConnBySA(sa
, aport
, acell
, tu
, aforce
,
594 0 /*don't create one */ ,
595 locktype
, replicated
, rxconn
);
601 for (sa
= aserver
->addr
; sa
; sa
= sa
->next_sa
) {
602 tc
= afs_ConnBySA(sa
, aport
, acell
, tu
, aforce
,
604 locktype
, replicated
, rxconn
);
610 afs_PutUser(tu
, SHARED_LOCK
);
613 } /*afs_ConnByHost */
617 * Connect by multiple hosts.
618 * Try to connect to one of the hosts from the ahosts array.
620 * @param ahosts Multiple hosts to connect to.
621 * @param aport Connection port.
622 * @param acell The cell where all of this happens.
623 * @param areq The request.
624 * @param locktype Type of lock to be used.
627 * @return The established connection or NULL.
630 afs_ConnByMHosts(struct server
*ahosts
[], unsigned short aport
,
631 afs_int32 acell
, struct vrequest
*areq
,
632 afs_int32 locktype
, afs_int32 replicated
,
633 struct rx_connection
**rxconn
)
636 struct afs_conn
*tconn
;
641 /* try to find any connection from the set */
642 AFS_STATCNT(afs_ConnByMHosts
);
643 for (i
= 0; i
< AFS_MAXCELLHOSTS
; i
++) {
644 if ((ts
= ahosts
[i
]) == NULL
)
646 tconn
= afs_ConnByHost(ts
, aport
, acell
, areq
, 0, locktype
,
654 } /*afs_ConnByMHosts */
658 * Decrement reference count to this connection.
663 afs_PutConn(struct afs_conn
*ac
, struct rx_connection
*rxconn
,
666 AFS_STATCNT(afs_PutConn
);
668 if (ac
->refCount
< 0) {
669 osi_Panic("afs_PutConn: refcount imbalance 0x%lx %d",
670 (unsigned long)(uintptrsz
)ac
, (int)ac
->refCount
);
672 ac
->parent
->refCount
--;
673 rx_PutConnection(rxconn
);
678 * Free up a connection vector, allowing, eg, code in afs_user.c
679 * to ignore how connections are stored/pooled
683 afs_ReleaseConns(struct sa_conn_vector
*tcv
) {
684 release_conns_vector(tcv
);
689 * Free connection vector(s) for a user
693 afs_ReleaseConnsUser(struct unixuser
*au
) {
698 for (i
= 0; i
< NSERVERS
; i
++) {
699 for (ts
= afs_servers
[i
]; ts
; ts
= ts
->next
) {
700 release_conns_user_server(au
, ts
);
701 } /*For each server on chain */
702 } /*For each chain */
707 * For multi homed clients, a RPC may timeout because of a
708 * client network interface going down. We need to reopen new
709 * connections in this case.
711 * @param sap Server address.
714 ForceNewConnections(struct srvAddr
*sap
)
717 struct afs_conn
*tc
= NULL
;
718 struct sa_conn_vector
*tcv
= NULL
;
721 return; /* defensive check */
723 ObtainWriteLock(&afs_xconn
, 413);
724 for (tcv
= sap
->conns
; tcv
; tcv
= tcv
->next
) {
725 for(cix
= 0; cix
< CVEC_LEN
; ++cix
) {
726 tc
= &(tcv
->cvec
[cix
]);
728 tc
->forceConnectFS
= 1;
731 ReleaseWriteLock(&afs_xconn
);