本章节讲述了IPv4及IPv6选项,由于IPv6选项的运用并不鲜见,此处只重点介绍IPv4选项的用法,也只是重点关注源路径选项。
源路径选项的运用,先是通过往给定的44字节缓冲区中填写路径IP地址及目的IP地址,通过setsockopt()函数给相应套接字设置IPv4选项,接收端通过getsockopt()函数读取IP报文首部中选项部分。
1、IPv4选项
共有10个,如NOP(no-operation,单字节选项)、EOL(end-of-list,单字节选项)、LSRR(loose source and record route)、SSRR(strict source and record route)、Timestamp、Record route、Basic security(已作废)、Extended security(已作废)、Stream identifier(已作废)、Router alert。
源路径是由IP数据报的发送者指定的一个IP地址列表,分成LSRR和SSRR。
IPv4源路径称为源和记录路径,因为随着数据报逐一经过所列的节点,每个节点都把列在源路径中的自已的地址替换为外出接口的地址。SRR允许接收者逆转新的列表的顺序,得到沿相反方向回到发送者的路径(源路径是被tcp自动逆转顺序哈)。
将源路径指定一个IPv4地址数组,并冠以3个单字节字段,于是传递给setsockopt的缓冲区的格式如下所示,
2、三个被开发的函数,分别用于初始化、创建和处理一个源路径选项
/* include inet_srcrt_init */ #include "unp.h" #include <netinet/in_systm.h> #include <netinet/ip.h> static u_char *optr; /* pointer into options being formed */ static u_char *lenptr; /* pointer to length byte in SRR option */ static int ocnt; /* count of # addresses */ u_char * inet_srcrt_init(int type) { optr = Malloc(44); /* NOP, code, len, ptr, up to 10 addresses */ bzero(optr, 44); /* guarantees EOLs at end */ ocnt = 0; *optr++ = IPOPT_NOP; /* NOP for alignment */ *optr++ = type ? IPOPT_SSRR : IPOPT_LSRR; lenptr = optr++; /* we fill in length later *///长度将随着后续插入的IP地址而改变 *optr++ = 4; /* offset to first address */ return(optr - 4); /* pointer for setsockopt() *///返回指向选项缓冲区的指针 } /* end inet_srcrt_init */ /* include inet_srcrt_add */ int inet_srcrt_add(char *hostptr) { int len; struct addrinfo *ai;//结构struct addrinfo的具体信息见书P246 struct sockaddr_in *sin; if (ocnt > 9) err_quit("too many source routes with: %s", hostptr); ai = Host_serv(hostptr, NULL, AF_INET, 0);//获取IPv4相关信息 sin = (struct sockaddr_in *) ai->ai_addr; memcpy(optr, &sin->sin_addr, sizeof(struct in_addr));//添加节点地址信息 freeaddrinfo(ai); optr += sizeof(struct in_addr); ocnt++; len = 3 + (ocnt * sizeof(struct in_addr));//此处+3是不包括NOP单字节 *lenptr = len; return(len + 1); /* size for setsockopt() *///返回IP选项缓冲区的总长度(包括NOP) } /* end inet_srcrt_add */ /* include inet_srcrt_print */ void inet_srcrt_print(u_char *ptr, int len) { u_char c; char str[INET_ADDRSTRLEN];//#define INET_ADDRSTRLEN 16; struct in_addr hop1; memcpy(&hop1, ptr, sizeof(struct in_addr)); //保存缓冲区的第一个IP地址 ptr += sizeof(struct in_addr); while ((c = *ptr++) == IPOPT_NOP) ; /* skip any leading NOPs */ if (c == IPOPT_LSRR) printf("received LSRR: "); else if (c == IPOPT_SSRR) printf("received SSRR: "); else { printf("received option type %d\n", c); return; } printf("%s ", Inet_ntop(AF_INET, &hop1, str, sizeof(str))); len = *ptr++ - sizeof(struct in_addr); /* subtract dest IP addr */ ptr++; /* skip over pointer */ while (len > 0) { printf("%s ", Inet_ntop(AF_INET, ptr, str, sizeof(str))); ptr += sizeof(struct in_addr); len -= sizeof(struct in_addr); } printf("\n"); } /* end inet_srcrt_print */
需要注意一点是,当getsockopt返回的源路径选项与上图有少许不同,见下图所示,
上图的格式代码在<netinet/ip_var.h>(本人机器上没有哈)中定义,如下,
struct ipoption { struct in_addr ipopt_dst;//first_hop dst if source routed char ipopt_list[MAX_IPOPTLEN];//options proper };
3、使用了源路径的tcp回射客户程序
客户端:
#include "unp.h" int main(int argc, char **argv) { int c, sockfd, len = 0; u_char *ptr = NULL; struct addrinfo *ai; if (argc < 2) err_quit("usage: tcpcli01 [ -[gG] <hostname> ... ] <hostname>"); opterr = 0; /* don't want getopt() writing to stderr */ while ((c = getopt(argc, argv, "gG")) != -1) { switch (c) { case 'g': /* loose source route */ if (ptr) err_quit("can't use both -g and -G"); ptr = inet_srcrt_init(0); break; case 'G': /* strict source route */ if (ptr) err_quit("can't use both -g and -G"); ptr = inet_srcrt_init(1); break; case '?': err_quit("unrecognized option: %c", c); } } if (ptr)//若ip选项缓冲区初始化成功,则ptr指向IP选项缓冲区 while (optind < argc - 1) len = inet_srcrt_add(argv[optind++]); else if (optind < argc - 1) err_quit("need -g or -G to specify route"); if (optind != argc - 1)//不太熟悉optind的用法 err_quit("missing <hostname>"); ai = Host_serv(argv[optind], SERV_PORT_STR, AF_INET, SOCK_STREAM); sockfd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (ptr) { len = inet_srcrt_add(argv[optind]); /* dest at end */ Setsockopt(sockfd, IPPROTO_IP, IP_OPTIONS, ptr, len); free(ptr); } Connect(sockfd, ai->ai_addr, ai->ai_addrlen); str_cli(stdin, sockfd); /* do it all */ exit(0); }服务器:
#include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; u_char *opts; pid_t childpid; socklen_t clilen, len; struct sockaddr_in cliaddr, servaddr; void sig_chld(int); opts = Malloc(44); listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); Signal(SIGCHLD, sig_chld); for ( ; ; ) { clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) { if (errno == EINTR) continue; /* back to for() */ else err_sys("accept error"); } len = 44; Getsockopt(connfd, IPPROTO_IP, IP_OPTIONS, opts, &len);//获取IP首部option部分 if (len > 0) { printf("received IP options, len = %d\n", len); inet_srcrt_print(opts, len); } if ( (childpid = Fork()) == 0) { /* child process */ Close(listenfd); /* close listening socket */ str_echo(connfd); /* process the request */ exit(0); } Close(connfd); /* parent closes connected socket */ } }以上知识点来均来自steven先生所著UNP卷一(version3),刚开始学习网络编程,如有不正确之处请大家多多指正。