[ Team LiB ] Previous Section Next Section

7.3 Checking if an Option Is Supported and Obtaining the Default

We 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 values

3–8 Our union contains one member for each possible return value from getsockopt.

Define function prototypes

9–12 We define function prototypes for four functions that are called to print the value for a given socket option.

Define structure and initialize array

13–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.

Not all implementations support all socket options. The way to determine if a given option is supported is to use an #ifdef or a #if defined, as we show for SO_REUSEPORT. For completeness, every element of the array should be compiled similarly to what we show for SO_REUSEPORT, but we omit these because the #ifdefs just lengthen the code that we show and add nothing to the discussion.

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 options

59–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 socket

63–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 getsockopt

83–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 value

88–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 ] Previous Section Next Section