UNP卷一chapter27 IP选项

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

猜你喜欢

转载自blog.csdn.net/tt_love9527/article/details/80686255