本章节讲述了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),刚开始学习网络编程,如有不正确之处请大家多多指正。