[ Team LiB ] |
7.3 Checking if an Option Is Supported and Obtaining the DefaultWe now write a program to check whether most of the options defined in Figures 7.1 and 7.2 are supported, and if so, print their default value. Figure 7.3 contains the declarations for our program. Declare union of possible values3–8 Our union contains one member for each possible return value from getsockopt. Define function prototypes9–12 We define function prototypes for four functions that are called to print the value for a given socket option. Define structure and initialize array13–52 Our sock_opts structure contains all the information necessary to call getsockopt for each socket option and then print its current value. The final member, opt_val_str, is a pointer to one of our four functions that will print the option value. We allocate and initialize an array of these structures, one element for each socket option.
Figure 7.3 Declarations for our program to check the socket options.sockopt/checkopts.c 1 #include "unp.h" 2 #include <netinet/tcp.h> /* for TCP_xxx defines */ 3 union val { 4 int i_val; 5 long l_val; 6 struct linger linger_val; 7 struct timeval timeval_val; 8 } val; 9 static char *sock_str_flag(union val *, int); 10 static char *sock_str_int(union val *, int); 11 static char *sock_str_linger(union val *, int); 12 static char *sock_str_timeval(union val *, int); 13 struct sock_opts { 14 const char *opt_str; 15 int opt_level; 16 int opt_name; 17 char *(*opt_val_str) (union val *, int); 18 } sock_opts[] = { 19 { "SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, sock_str_flag }, 20 { "SO_DEBUG", SOL_SOCKET, SO_DEBUG, sock_str_flag }, 21 { "SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, sock_str_flag }, 22 { "SO_ERROR", SOL_SOCKET, SO_ERROR, sock_str_int }, 23 { "SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, sock_str_flag }, 24 { "SO_LINGER", SOL_SOCKET, SO_LINGER, sock_str_linger }, 25 { "SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, sock_str_flag }, 26 { "SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, sock_str_int }, 27 { "SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, sock_str_int }, 28 { "SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, sock_str_int }, 29 { "SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, sock_str_int }, 30 { "SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, sock_str_timeval }, 31 { "SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, sock_str_timeval }, 32 { "SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, sock_str_flag }, 33 #ifdef SO_REUSEPORT 34 { "SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, sock_str_flag }, 35 #else 36 { "SO_REUSEPORT", 0, 0, NULL }, 37 #endif 38 { "SO_TYPE", SOL_SOCKET, SO_TYPE, sock_str_int }, 39 { "SO_USELOOPBACK", SOL_SOCKET, SO_USELOOPBACK, sock_str_flag }, 40 { "IP_TOS", IPPROTO_IP, IP_TOS, sock_str_int }, 41 { "IP_TTL", IPPROTO_IP, IP_TTL, sock_str_int }, 42 { "IPV6_DONTFRAG", IPPROTO_IPV6,IPV6_DONTFRAG, sock_str_flag }, 43 { "IPV6_UNICAST_HOPS", IPPROTO_IPV6,IPV6_UNICAST_HOPS,sock_str_int }, 44 { "IPV6_V6ONLY", IPPROTO_IPV6,IPV6_V6ONLY, sock_str_flag }, 45 { "TCP_MAXSEG", IPPROTO_TCP,TCP_MAXSEG, sock_str_int }, 46 { "TCP_NODELAY", IPPROTO_TCP,TCP_NODELAY, sock_str_flag }, 47 { "SCTP_AUTOCLOSE", IPPROTO_SCTP,SCTP_AUTOCLOSE,sock_str_int }, 48 { "SCTP_MAXBURST", IPPROTO_SCTP,SCTP_MAXBURST, sock_str_int }, 49 { "SCTP_MAXSEG", IPPROTO_SCTP,SCTP_MAXSEG, sock_str_int }, 50 { "SCTP_NODELAY", IPPROTO_SCTP,SCTP_NODELAY, sock_str_flag }, 51 { NULL, 0, 0, NULL } 52 }; Figure 7.4 shows our main function. Figure 7.4 main function to check all socket options.sockopt/checkopts.c 53 int 54 main(int argc, char **argv) 55 { 56 int fd; 57 socklen_t len; 58 struct sock_opts *ptr; 59 for (ptr = sock_opts; ptr->opt_str != NULL; ptr++) { 60 printf("%s: ", ptr->opt_str); 61 if (ptr->opt_val_str == NULL) 62 printf("(undefined)\n"); 63 else { 64 switch (ptr->opt_level) { 65 case SOL_SOCKET: 66 case IPPROTO_IP: 67 case IPPROTO_TCP: 68 fd = Socket(AF_INET, SOCK_STREAM, 0); 69 break; 70 #ifdef IPV6 71 case IPPROTO_IPV6: 72 fd = Socket(AF_INET6, SOCK_STREAM, 0); 73 break; 74 #endif 75 #ifdef IPPROTO_SCTP 76 case IPPROTO_SCTP: 77 fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); 78 break; 79 #endif 80 default: 81 err_quit("Can't create fd for level %d\n", ptr->opt_level); 82 } 83 len = sizeof(val); 84 if (getsockopt(fd, ptr->opt_level, ptr->opt_name, 85 &val, &len) == -1) { 86 err_ret("getsockopt error"); 87 } else { 88 printf("default = %s\n", (*ptr->opt_val_str) (&val, len)); 89 } 90 close(fd); 91 } 92 } 93 exit(0); 94 } Go through all options59–63 We go through all elements in our array. If the opt_val_str pointer is null, the option is not defined by the implementation (which we showed for SO_REUSEPORT). Create socket63–82 We create a socket on which to try the option. To try socket, IPv4, and TCP layer socket options, we use an IPv4 TCP socket. To try IPv6 layer socket options, we use an IPv6 TCP socket, and to try SCTP layer socket options, we use an IPv4 SCTP socket. Call getsockopt83–87 We call getsockopt but do not terminate if an error is returned. Many implementations define some of the socket option names even though they do not support the option. Unsupported options should elicit an error of ENOPROTOOPT. Print option's default value88–89 If getsockopt returns success, we call our function to convert the option value to a string and print the string. In Figure 7.3, we showed four function prototypes, one for each type of option value that is returned. Figure 7.5 shows one of these four functions, sock_str_flag, which prints the value of a flag option. The other three functions are similar. Figure 7.5 sock_str_flag function: convert flag option to a string.sockopt/checkopts.c 95 static char strres[128]; 96 static char * 97 sock_str_flag(union val *ptr, int len) 98 { 99 if (len != sizeof(int)) 100 snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len); 101 else 102 snprintf(strres, sizeof(strres), 103 "%s", (ptr->i_val == 0) ? "off" : "on"); 104 return(strres); 105 } 99–104 Recall that the final argument to getsockopt is a value-result argument. The first check we make is that the size of the value returned by getsockopt is the expected size. The string returned is off or on, depending on whether the value of the flag option is zero or nonzero, respectively. Running this program under FreeBSD 4.8 with KAME SCTP patches gives the following output:
freebsd % checkopts
SO_BROADCAST: default = off
SO_DEBUG: default = off
SO_DONTROUTE: default = off
SO_ERROR: default = 0
SO_KEEPALIVE: default = off
SO_LINGER: default = l_onoff = 0, l_linger = 0
SO_OOBINLINE: default = off
SO_RCVBUF: default = 57344
SO_SNDBUF: default = 32768
SO_RCVLOWAT: default = 1
SO_SNDLOWAT: default = 2048
SO_RCVTIMEO: default = 0 sec, 0 usec
SO_SNDTIMEO: default = 0 sec, 0 usec
SO_REUSEADDR: default = off
SO_REUSEPORT: default = off
SO_TYPE: default = 1
SO_USELOOPBACK: default = off
IP_TOS: default = 0
IP_TTL: default = 64
IPV6_DONTFRAG: default = off
IPV6_UNICAST_HOPS: default = -1
IPV6_V6ONLY: default = off
TCP_MAXSEG: default = 512
TCP_NODELAY: default = off
SCTP_AUTOCLOSE: default = 0
SCTP_MAXBURST: default = 4
SCTP_MAXSEG: default = 1408
SCTP_NODELAY: default = off
The value of 1 returned for the SO_TYPE option corresponds to SOCK_STREAM for this implementation. |
[ Team LiB ] |