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鈥?
Our union contains one member for each possible return
value from getsockopt.
Define function prototypes
9鈥?2
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鈥?2
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鈥?3
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鈥?2
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鈥?7
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鈥?9
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鈥?04
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.
|