1
/* ----------------------------------------------------------------------- *
3
* repl_list.h - routines for replicated mount server selection
5
* Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
6
* Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
11
* USA; either version 2 of the License, or (at your option) any later
12
* version; incorporated herein by reference.
14
* A priority ordered list of hosts is created by using the following
17
* 1) Highest priority in selection is proximity.
18
* Proximity, in order of precedence is:
19
* - PROXIMITY_LOCAL, host corresponds to a local interface.
20
* - PROXIMITY_SUBNET, host is located in a subnet reachable
21
* through a local interface.
22
* - PROXIMITY_NETWORK, host is located in a network reachable
23
* through a local interface.
24
* - PROXIMITY_OTHER, host is on a network not directlty
25
* reachable through a local interface.
27
* 2) NFS version and protocol is selected by caclculating the largest
28
* number of hosts supporting an NFS version and protocol that
29
* have the closest proximity. These hosts are added to the list
30
* in response time order. Hosts may have a corresponding weight
31
* which essentially increaes response time and so influences the
34
* 3) Hosts at further proximity that support the selected NFS version
35
* and protocol are also added to the list in response time order as
38
* ----------------------------------------------------------------------- */
46
#include <sys/errno.h>
47
#include <sys/types.h>
49
#include <sys/ioctl.h>
50
#include <sys/socket.h>
51
#include <arpa/inet.h>
53
#include <netinet/in.h>
57
#include "replicated.h"
58
#include "automount.h"
61
#define MAX_ERR_BUF 512
64
#define MAX_IFC_BUF 2048
65
static int volatile ifc_buf_len = MAX_IFC_BUF;
66
static int volatile ifc_last_len = 0;
68
#define MASK_A 0x7F000000
69
#define MASK_B 0xBFFF0000
70
#define MASK_C 0xDFFFFF00
72
/* Get numeric value of the n bits starting at position p */
73
#define getbits(x, p, n) ((x >> (p + 1 - n)) & ~(~0 << n))
75
#define max(x, y) (x >= y ? x : y)
76
#define mmax(x, y, z) (max(x, y) == x ? max(x, z) : max(y, z))
78
unsigned int ipv6_mask_cmp(uint32_t *host, uint32_t *iface, uint32_t *mask)
83
for (i = 0; i < 4; i++) {
84
if ((host[i] & mask[i]) != (iface[i] & mask[i])) {
92
void seed_random(void)
97
fd = open_fd("/dev/urandom", O_RDONLY);
103
if (read(fd, &seed, sizeof(seed)) != -1)
113
static int alloc_ifreq(struct ifconf *ifc, int sock)
115
int ret, lastlen = ifc_last_len, len = ifc_buf_len;
116
char err_buf[MAX_ERR_BUF], *buf;
121
char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
122
logerr("malloc: %s", estr);
127
ifc->ifc_req = (struct ifreq *) buf;
129
ret = ioctl(sock, SIOCGIFCONF, ifc);
131
char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
132
logerr("ioctl: %s", estr);
137
if (ifc->ifc_len <= lastlen)
140
lastlen = ifc->ifc_len;
145
if (lastlen != ifc_last_len) {
146
ifc_last_len = lastlen;
153
static unsigned int get_proximity(struct sockaddr *host_addr)
155
struct sockaddr_in *addr, *msk_addr, *if_addr;
156
struct sockaddr_in6 *addr6, *msk6_addr, *if6_addr;
157
struct in_addr *hst_addr;
158
struct in6_addr *hst6_addr;
160
char buf[MAX_ERR_BUF], *ptr;
162
struct ifreq *ifr, nmptr;
164
uint32_t mask, ha, ia, *mask6, *ha6, *ia6;
174
switch (host_addr->sa_family) {
176
addr = (struct sockaddr_in *) host_addr;
177
hst_addr = (struct in_addr *) &addr->sin_addr;
178
ha = ntohl((uint32_t) hst_addr->s_addr);
179
addr_len = sizeof(hst_addr);
183
#ifndef WITH_LIBTIRPC
184
return PROXIMITY_UNSUPPORTED;
186
addr6 = (struct sockaddr_in6 *) host_addr;
187
hst6_addr = (struct in6_addr *) &addr6->sin6_addr;
188
ha6 = &hst6_addr->s6_addr32[0];
189
addr_len = sizeof(hst6_addr);
194
return PROXIMITY_ERROR;
197
sock = open_sock(AF_INET, SOCK_DGRAM, 0);
199
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
200
logerr("socket creation failed: %s", estr);
201
return PROXIMITY_ERROR;
204
if (!alloc_ifreq(&ifc, sock)) {
206
return PROXIMITY_ERROR;
209
/* For each interface */
211
/* Is the address a local interface */
213
ptr = (char *) &ifc.ifc_buf[0];
215
while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
216
ifr = (struct ifreq *) ptr;
218
switch (ifr->ifr_addr.sa_family) {
220
if (host_addr->sa_family == AF_INET6)
222
if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
223
ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len);
227
return PROXIMITY_LOCAL;
232
#ifndef WITH_LIBTIRPC
233
return PROXIMITY_UNSUPPORTED;
235
if (host_addr->sa_family == AF_INET)
238
if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
239
ret = memcmp(&if6_addr->sin6_addr, hst6_addr, addr_len);
243
return PROXIMITY_LOCAL;
252
ptr = (char *) &ifc.ifc_req[i];
256
ptr = (char *) &ifc.ifc_buf[0];
258
while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
259
ifr = (struct ifreq *) ptr;
262
ret = ioctl(sock, SIOCGIFNETMASK, &nmptr);
264
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
265
logerr("ioctl: %s", estr);
268
return PROXIMITY_ERROR;
271
switch (ifr->ifr_addr.sa_family) {
273
if (host_addr->sa_family == AF_INET6)
275
if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
276
ia = ntohl((uint32_t) if_addr->sin_addr.s_addr);
278
/* Is the address within a localiy attached subnet */
280
msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask;
281
mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr);
283
if ((ia & mask) == (ha & mask)) {
286
return PROXIMITY_SUBNET;
290
* Is the address within a local ipv4 network.
292
* Bit position 31 == 0 => class A.
293
* Bit position 30 == 0 => class B.
294
* Bit position 29 == 0 => class C.
297
if (!getbits(ia, 31, 1))
299
else if (!getbits(ia, 30, 1))
301
else if (!getbits(ia, 29, 1))
306
if ((ia & mask) == (ha & mask)) {
309
return PROXIMITY_NET;
314
#ifndef WITH_LIBTIRPC
315
return PROXIMITY_UNSUPPORTED;
317
if (host_addr->sa_family == AF_INET)
320
if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
321
ia6 = &if6_addr->sin6_addr.s6_addr32[0];
323
/* Is the address within the network of the interface */
325
msk6_addr = (struct sockaddr_in6 *) &nmptr.ifr_netmask;
326
mask6 = &msk6_addr->sin6_addr.s6_addr32[0];
328
if (ipv6_mask_cmp(ha6, ia6, mask6)) {
331
return PROXIMITY_SUBNET;
334
/* How do we define "local network" in ipv6? */
343
ptr = (char *) &ifc.ifc_req[i];
349
return PROXIMITY_OTHER;
352
static struct host *new_host(const char *name,
353
struct sockaddr *addr, size_t addr_len,
354
unsigned int proximity, unsigned int weight,
355
unsigned int options)
358
struct sockaddr *tmp2;
368
tmp2 = malloc(addr_len);
373
memcpy(tmp2, addr, addr_len);
375
new = malloc(sizeof(struct host));
382
memset(new, 0, sizeof(struct host));
385
new->addr_len = addr_len;
387
new->proximity = proximity;
388
new->weight = weight;
389
new->options = options;
394
static int add_host(struct host **list, struct host *host)
396
struct host *this, *last;
406
if (this->proximity >= host->proximity)
414
if (this->proximity != host->proximity)
416
if (this->cost >= host->cost)
435
static void free_host(struct host *host)
443
static void remove_host(struct host **hosts, struct host *host)
445
struct host *last, *this;
447
if (host == *hosts) {
448
*hosts = (*hosts)->next;
465
last->next = this->next;
471
static void delete_host(struct host **hosts, struct host *host)
473
remove_host(hosts, host);
478
void free_host_list(struct host **list)
484
struct host *next = this->next;
491
static unsigned short get_port_option(const char *options)
499
start = strstr(options, "port=");
503
char optport[30], *opteq, *end;
506
end = strchr(start, ',');
507
len = end ? end - start : strlen(start);
508
strncpy(optport, start, len);
510
opteq = strchr(optport, '=');
512
port = atoi(opteq + 1);
518
return (unsigned short) port;
521
static unsigned int get_nfs_info(unsigned logopt, struct host *host,
522
struct conn_info *pm_info, struct conn_info *rpc_info,
523
const char *proto, unsigned int version,
526
char *have_port_opt = options ? strstr(options, "port=") : NULL;
527
unsigned int random_selection = host->options & MOUNT_FLAG_RANDOM_SELECT;
528
unsigned int use_weight_only = host->options & MOUNT_FLAG_USE_WEIGHT_ONLY;
529
socklen_t len = INET6_ADDRSTRLEN;
532
struct timeval start, end;
534
unsigned int supported = 0;
536
int status, count = 0;
539
debug(logopt, "called with host %s(%s) proto %s version 0x%x",
540
host->name, get_addr_string(host->addr, buf, len),
544
"called for host %s proto %s version 0x%x",
545
host->name, proto, version);
547
memset(&parms, 0, sizeof(struct pmap));
549
parms.pm_prog = NFS_PROGRAM;
551
/* Try to prode UDP first to conserve socket space */
552
rpc_info->proto = getprotobyname(proto);
553
if (!rpc_info->proto)
556
if (!(version & NFS4_REQUESTED))
559
if (!(rpc_info->port = get_port_option(options)))
562
if (rpc_info->proto->p_proto == IPPROTO_UDP)
563
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
565
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
566
if (status == -EHOSTUNREACH)
567
return (unsigned int) status;
569
gettimeofday(&start, &tz);
570
status = rpc_ping_proto(rpc_info);
571
gettimeofday(&end, &tz);
572
if (status == -ETIMEDOUT)
573
return (unsigned int) status;
574
else if (status > 0) {
576
if (random_selection) {
577
/* Random value between 0 and 1 */
578
reply = ((float) random())/((float) RAND_MAX+1);
580
"nfs v4 random selection time: %f", reply);
582
reply = elapsed(start, end);
583
debug(logopt, "nfs v4 rpc ping time: %f", reply);
587
supported = NFS4_SUPPORTED;
592
if (!have_port_opt) {
593
status = rpc_portmap_getclient(pm_info,
594
host->name, host->addr, host->addr_len,
595
proto, RPC_CLOSE_DEFAULT);
596
if (status == -EHOSTUNREACH) {
603
if (!(version & NFS3_REQUESTED))
607
if (!(rpc_info->port = get_port_option(options)))
610
parms.pm_prot = rpc_info->proto->p_proto;
611
parms.pm_vers = NFS3_VERSION;
612
status = rpc_portmap_getport(pm_info, &parms, &rpc_info->port);
613
if (status == -EHOSTUNREACH || status == -ETIMEDOUT) {
616
} else if (status < 0)
620
if (rpc_info->proto->p_proto == IPPROTO_UDP)
621
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
623
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
624
if (status == -EHOSTUNREACH) {
627
} else if (!status) {
628
gettimeofday(&start, &tz);
629
status = rpc_ping_proto(rpc_info);
630
gettimeofday(&end, &tz);
631
if (status == -ETIMEDOUT) {
634
} else if (status > 0) {
636
if (random_selection) {
637
/* Random value between 0 and 1 */
638
reply = ((float) random())/((float) RAND_MAX+1);
640
"nfs v3 random selection time: %f", reply);
642
reply = elapsed(start, end);
643
debug(logopt, "nfs v3 rpc ping time: %f", reply);
647
supported |= NFS3_SUPPORTED;
652
if (!(version & NFS2_REQUESTED))
656
if (!(rpc_info->port = get_port_option(options)))
659
parms.pm_prot = rpc_info->proto->p_proto;
660
parms.pm_vers = NFS2_VERSION;
661
status = rpc_portmap_getport(pm_info, &parms, &rpc_info->port);
662
if (status == -EHOSTUNREACH || status == -ETIMEDOUT) {
665
} else if (status < 0)
669
if (rpc_info->proto->p_proto == IPPROTO_UDP)
670
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
672
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
673
if (status == -EHOSTUNREACH) {
676
} else if (!status) {
677
gettimeofday(&start, &tz);
678
status = rpc_ping_proto(rpc_info);
679
gettimeofday(&end, &tz);
680
if (status == -ETIMEDOUT)
682
else if (status > 0) {
684
if (random_selection) {
685
/* Random value between 0 and 1 */
686
reply = ((float) random())/((float) RAND_MAX+1);
688
"nfs v2 random selection time: %f", reply);
690
reply = elapsed(start, end);;
691
debug(logopt, "nfs v2 rpc ping time: %f", reply);
695
supported |= NFS2_SUPPORTED;
700
if (rpc_info->proto->p_proto == IPPROTO_UDP) {
701
rpc_destroy_udp_client(rpc_info);
702
rpc_destroy_udp_client(pm_info);
704
rpc_destroy_tcp_client(rpc_info);
705
rpc_destroy_tcp_client(pm_info);
710
* Average response time to 7 significant places as
716
host->cost = (unsigned long) ((taken * 1000000) / count);
718
/* Allow for user bias */
720
host->cost *= (host->weight + 1);
722
debug(logopt, "host %s cost %ld weight %d",
723
host->name, host->cost, host->weight);
729
static int get_vers_and_cost(unsigned logopt, struct host *host,
730
unsigned int version, const char *options)
732
struct conn_info pm_info, rpc_info;
733
time_t timeout = RPC_TIMEOUT;
734
unsigned int supported, vers = (NFS_VERS_MASK | NFS4_VERS_MASK);
737
memset(&pm_info, 0, sizeof(struct conn_info));
738
memset(&rpc_info, 0, sizeof(struct conn_info));
740
if (host->proximity == PROXIMITY_NET)
741
timeout = RPC_TIMEOUT * 2;
742
else if (host->proximity == PROXIMITY_OTHER)
743
timeout = RPC_TIMEOUT * 8;
745
rpc_info.host = host->name;
746
rpc_info.addr = host->addr;
747
rpc_info.addr_len = host->addr_len;
748
rpc_info.program = NFS_PROGRAM;
749
rpc_info.timeout.tv_sec = timeout;
750
rpc_info.close_option = RPC_CLOSE_DEFAULT;
751
rpc_info.client = NULL;
755
if (version & TCP_REQUESTED) {
756
supported = get_nfs_info(logopt, host,
757
&pm_info, &rpc_info, "tcp", vers, options);
758
if (IS_ERR(supported)) {
759
if (ERR(supported) == EHOSTUNREACH ||
760
ERR(supported) == ETIMEDOUT)
762
} else if (supported) {
764
host->version |= supported;
768
if (version & UDP_REQUESTED) {
769
supported = get_nfs_info(logopt, host,
770
&pm_info, &rpc_info, "udp", vers, options);
771
if (IS_ERR(supported)) {
772
if (ERR(supported) == ETIMEDOUT)
774
} else if (supported) {
776
host->version |= (supported << 8);
783
static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
784
unsigned int version, const char *options)
786
char *have_port_opt = options ? strstr(options, "port=") : NULL;
787
unsigned int random_selection = host->options & MOUNT_FLAG_RANDOM_SELECT;
788
unsigned int use_weight_only = host->options & MOUNT_FLAG_USE_WEIGHT_ONLY;
789
socklen_t len = INET6_ADDRSTRLEN;
791
struct conn_info pm_info, rpc_info;
795
struct timeval start, end;
798
time_t timeout = RPC_TIMEOUT;
802
debug(logopt, "called with host %s(%s) version 0x%x",
803
host->name, get_addr_string(host->addr, buf, len),
806
debug(logopt, "called with host %s version 0x%x",
807
host->name, version);
809
memset(&pm_info, 0, sizeof(struct conn_info));
810
memset(&rpc_info, 0, sizeof(struct conn_info));
811
memset(&parms, 0, sizeof(struct pmap));
813
if (host->proximity == PROXIMITY_NET)
814
timeout = RPC_TIMEOUT * 2;
815
else if (host->proximity == PROXIMITY_OTHER)
816
timeout = RPC_TIMEOUT * 8;
818
rpc_info.host = host->name;
819
rpc_info.addr = host->addr;
820
rpc_info.addr_len = host->addr_len;
821
rpc_info.program = NFS_PROGRAM;
822
rpc_info.timeout.tv_sec = timeout;
823
rpc_info.close_option = RPC_CLOSE_DEFAULT;
824
rpc_info.client = NULL;
826
parms.pm_prog = NFS_PROGRAM;
829
* The version passed in is the version as defined in
830
* include/replicated.h. However, the version we want to send
831
* off to the rpc calls should match the program version of NFS.
832
* So, we do the conversion here.
834
if (version & UDP_SELECTED_MASK) {
851
crit(logopt, "called with invalid version: 0x%x\n", version);
855
rpc_info.proto = getprotobyname(proto);
861
parms.pm_vers = vers;
862
if (have_port_opt || (vers & NFS4_VERSION)) {
863
if (!(rpc_info.port = get_port_option(options)))
866
int ret = rpc_portmap_getclient(&pm_info,
867
host->name, host->addr, host->addr_len,
868
proto, RPC_CLOSE_DEFAULT);
872
parms.pm_prot = rpc_info.proto->p_proto;
873
ret = rpc_portmap_getport(&pm_info, &parms, &rpc_info.port);
878
if (rpc_info.proto->p_proto == IPPROTO_UDP)
879
status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
881
status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
882
if (status == -EHOSTUNREACH)
885
gettimeofday(&start, &tz);
886
status = rpc_ping_proto(&rpc_info);
887
gettimeofday(&end, &tz);
889
if (random_selection) {
890
/* Random value between 0 and 1 */
891
taken = ((float) random())/((float) RAND_MAX+1);
892
debug(logopt, "random selection time %f", taken);
894
taken = elapsed(start, end);
895
debug(logopt, "rpc ping time %f", taken);
900
if (rpc_info.proto->p_proto == IPPROTO_UDP) {
901
rpc_destroy_udp_client(&rpc_info);
902
rpc_destroy_udp_client(&pm_info);
904
rpc_destroy_tcp_client(&rpc_info);
905
rpc_destroy_tcp_client(&pm_info);
909
/* Response time to 7 significant places as integral type. */
913
host->cost = (unsigned long) (taken * 1000000);
915
/* Allow for user bias */
917
host->cost *= (host->weight + 1);
919
debug(logopt, "cost %ld weight %d", host->cost, host->weight);
927
int prune_host_list(unsigned logopt, struct host **list,
928
unsigned int vers, const char *options)
930
struct host *this, *last, *first;
931
struct host *new = NULL;
932
unsigned int proximity, selected_version = 0;
933
unsigned int v2_tcp_count, v3_tcp_count, v4_tcp_count;
934
unsigned int v2_udp_count, v3_udp_count, v4_udp_count;
935
unsigned int max_udp_count, max_tcp_count, max_count;
942
/* Use closest hosts to choose NFS version */
946
/* Get proximity of first entry after local entries */
948
while (this && this->proximity == PROXIMITY_LOCAL)
953
* Check for either a list containing only proximity local hosts
954
* or a single host entry whose proximity isn't local. If so
955
* return immediately as we don't want to add probe latency for
956
* the common case of a single filesystem mount request.
958
* But, if the kernel understands text nfs mount options then
959
* mount.nfs most likely bypasses its probing and lets the kernel
960
* do all the work. This can lead to long timeouts for hosts that
961
* are not available so check the kernel version and mount.nfs
962
* version and probe singleton mounts if the kernel version is
963
* greater than 2.6.22 and mount.nfs version is greater than 1.1.1.
964
* But also allow the MOUNT_WAIT configuration parameter to override
967
if (nfs_mount_uses_string_options &&
968
defaults_get_mount_wait() == -1 &&
969
(kern_vers = linux_version_code()) > KERNEL_VERSION(2, 6, 22)) {
973
if (!this || !this->next)
977
proximity = this->proximity;
979
struct host *next = this->next;
981
if (this->proximity != proximity)
985
status = get_vers_and_cost(logopt, this, vers, options);
990
proximity = next->proximity;
992
delete_host(list, this);
999
* The list of hosts that aren't proximity local may now
1000
* be empty if we haven't been able probe any so we need
1001
* to check again for a list containing only proximity
1009
/* Select NFS version of highest number of closest servers */
1011
v4_tcp_count = v3_tcp_count = v2_tcp_count = 0;
1012
v4_udp_count = v3_udp_count = v2_udp_count = 0;
1016
if (this->version & NFS4_TCP_SUPPORTED)
1019
if (this->version & NFS3_TCP_SUPPORTED)
1022
if (this->version & NFS2_TCP_SUPPORTED)
1025
if (this->version & NFS4_UDP_SUPPORTED)
1028
if (this->version & NFS3_UDP_SUPPORTED)
1031
if (this->version & NFS2_UDP_SUPPORTED)
1035
} while (this && this != last);
1037
max_tcp_count = mmax(v4_tcp_count, v3_tcp_count, v2_tcp_count);
1038
max_udp_count = mmax(v4_udp_count, v3_udp_count, v2_udp_count);
1039
max_count = max(max_tcp_count, max_udp_count);
1041
if (max_count == v4_tcp_count) {
1042
selected_version = NFS4_TCP_SUPPORTED;
1044
"selected subset of hosts that support NFS4 over TCP");
1045
} else if (max_count == v3_tcp_count) {
1046
selected_version = NFS3_TCP_SUPPORTED;
1048
"selected subset of hosts that support NFS3 over TCP");
1049
} else if (max_count == v2_tcp_count) {
1050
selected_version = NFS2_TCP_SUPPORTED;
1052
"selected subset of hosts that support NFS2 over TCP");
1053
} else if (max_count == v4_udp_count) {
1054
selected_version = NFS4_UDP_SUPPORTED;
1056
"selected subset of hosts that support NFS4 over UDP");
1057
} else if (max_count == v3_udp_count) {
1058
selected_version = NFS3_UDP_SUPPORTED;
1060
"selected subset of hosts that support NFS3 over UDP");
1061
} else if (max_count == v2_udp_count) {
1062
selected_version = NFS2_UDP_SUPPORTED;
1064
"selected subset of hosts that support NFS2 over UDP");
1067
/* Add local and hosts with selected version to new list */
1070
struct host *next = this->next;
1071
if (this->version & selected_version ||
1072
this->proximity == PROXIMITY_LOCAL) {
1073
this->version = selected_version;
1074
remove_host(list, this);
1075
add_host(&new, this);
1078
} while (this && this != last);
1081
* Now go through rest of list and check for chosen version
1082
* and add to new list if selected version is supported.
1088
struct host *next = this->next;
1090
remove_host(list, this);
1091
add_host(&new, this);
1093
status = get_supported_ver_and_cost(logopt, this,
1094
selected_version, options);
1096
this->version = selected_version;
1097
remove_host(list, this);
1098
add_host(&new, this);
1104
free_host_list(list);
1110
static int add_new_host(struct host **list,
1111
const char *host, unsigned int weight,
1112
struct addrinfo *host_addr,
1113
unsigned int rr, unsigned int options)
1120
* If we are using random selection we pretend all hosts are at
1121
* the same proximity so hosts further away don't get excluded.
1122
* We can't use PROXIMITY_LOCAL or we won't perform an RPC ping
1123
* to remove hosts that may be down.
1126
prx = PROXIMITY_SUBNET;
1128
prx = get_proximity(host_addr->ai_addr);
1130
* If we want the weight to be the determining factor
1131
* when selecting a host, or we are using random selection,
1132
* then all hosts must have the same proximity. However,
1133
* if this is the local machine it should always be used
1134
* since it is certainly available.
1136
if (prx != PROXIMITY_LOCAL &&
1137
(options & (MOUNT_FLAG_USE_WEIGHT_ONLY |
1138
MOUNT_FLAG_RANDOM_SELECT)))
1139
prx = PROXIMITY_SUBNET;
1143
* If we tried to add an IPv6 address and we don't have IPv6
1144
* support return success in the hope of getting an IPv4
1147
if (prx == PROXIMITY_UNSUPPORTED)
1149
if (prx == PROXIMITY_ERROR)
1152
if (host_addr->ai_addr->sa_family == AF_INET)
1153
addr_len = INET_ADDRSTRLEN;
1154
else if (host_addr->ai_addr->sa_family == AF_INET6)
1155
addr_len = INET6_ADDRSTRLEN;
1159
new = new_host(host, host_addr->ai_addr, addr_len, prx, weight, options);
1163
if (!add_host(list, new)) {
1172
static int add_host_addrs(struct host **list, const char *host,
1173
unsigned int weight, unsigned int options)
1175
struct addrinfo hints, *ni, *this;
1177
char *name = n_ptr = strdup(host);
1179
char buf[MAX_ERR_BUF];
1180
int rr = 0, rr4 = 0, rr6 = 0;
1184
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
1185
error(LOGOPT_ANY, "strdup: %s", estr);
1186
error(LOGOPT_ANY, "failed to add host %s", host);
1191
if (name[0] == '[' && name[--len] == ']') {
1196
memset(&hints, 0, sizeof(hints));
1197
hints.ai_flags = AI_NUMERICHOST;
1198
hints.ai_family = AF_UNSPEC;
1199
hints.ai_socktype = SOCK_DGRAM;
1201
ret = getaddrinfo(name, NULL, &hints, &ni);
1207
ret = add_new_host(list, host, weight, this, 0, options);
1210
this = this->ai_next;
1216
memset(&hints, 0, sizeof(hints));
1217
hints.ai_flags = AI_ADDRCONFIG;
1218
hints.ai_family = AF_UNSPEC;
1219
hints.ai_socktype = SOCK_DGRAM;
1221
ret = getaddrinfo(name, NULL, &hints, &ni);
1223
error(LOGOPT_ANY, "hostname lookup failed: %s",
1230
while (this->ai_next) {
1231
if (this->ai_family == AF_INET) {
1232
struct sockaddr_in *addr = (struct sockaddr_in *) this->ai_addr;
1233
if (addr->sin_addr.s_addr != INADDR_LOOPBACK)
1235
} else if (this->ai_family == AF_INET6) {
1236
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) this->ai_addr;
1237
if (!IN6_IS_ADDR_LOOPBACK(addr->sin6_addr.__in6_u.__u6_addr32))
1240
this = this->ai_next;
1242
if (rr4 > 1 || rr6 > 1)
1246
ret = add_new_host(list, host, weight, this, rr, options);
1249
this = this->ai_next;
1257
static int add_path(struct host *hosts, const char *path, int len)
1262
tmp = alloca(len + 1);
1266
strncpy(tmp, path, len);
1283
static int add_local_path(struct host **hosts, const char *path)
1292
new = malloc(sizeof(struct host));
1298
memset(new, 0, sizeof(struct host));
1301
new->proximity = PROXIMITY_LOCAL;
1302
new->version = NFS_VERS_MASK;
1305
new->weight = new->cost = 0;
1307
add_host(hosts, new);
1312
static char *seek_delim(const char *s)
1317
delim = strpbrk(p, "(, \t:");
1318
if (delim && *delim != ':' && (delim == s || *(delim - 1) != '\\'))
1326
if (!strncmp(p, ":/", 2))
1334
int parse_location(unsigned logopt, struct host **hosts,
1335
const char *list, unsigned int options)
1337
char *str, *p, *delim;
1338
unsigned int empty = 1;
1353
p += strspn(p, " \t,");
1354
delim = seek_delim(p);
1357
if (*delim == '(') {
1358
char *w = delim + 1;
1362
delim = strchr(w, ')');
1368
/* syntax error - Mismatched brackets */
1369
free_host_list(hosts);
1376
if (*delim == ':') {
1382
/* Oh boy - might have spaces in the path */
1384
while (*next && strncmp(next, ":/", 2))
1387
/* No spaces in host names at least */
1390
(*next != ' ' && *next != '\t'))
1396
if (!add_host_addrs(hosts, p, weight, options)) {
1403
if (!add_path(*hosts, path, strlen(path))) {
1404
free_host_list(hosts);
1409
if (!add_local_path(hosts, path)) {
1414
} else if (*delim != '\0') {
1418
if (!add_host_addrs(hosts, p, weight, options)) {
1426
/* syntax error - no mount path */
1427
free_host_list(hosts);
1439
void dump_host_list(struct host *hosts)
1448
logmsg("name %s path %s version %x proximity %u weight %u cost %u",
1449
this->name, this->path, this->version,
1450
this->proximity, this->weight, this->cost);