路由套接字

一、概述

AF_ROUTE域对访问内核中的路由子系统的接口做了清理。在路由域中支持的唯一一种套接字是原始套接字。路由套接字支持三种类型的操作。

  1. 进程通过写到路由套接字向内核发送消息(路径的增加和删除采用这种方式实现)。
  2. 进程通过读入路由套接字接收来自内核的消息(内核采用这种操作通知进程已收到并处理一个ICMP重定向消息,或请求外部路由进程解析一个路径)。
  3. 进程调用 sysctl 函数获取路由表或列出所有已配置的接口。

前两种操作可以复合使用。内核可以通过写一个路由套接字往内核发送一个消息,请求内核关于某个特定路径的所有消息,又可以通过路由套接字接受内核的应答。

二、数据链路套接字地址结构

struct sockaddr_dl {
   
    uint8_t      sdl_len;
    sa_family_t  sdl_family;   /* AF_LINK */
    uint16_t     sdl_index;    /* system assigned index, if > 0 */
    uint8_t      sdl_type;     /* IFT_ETHER, etc. from <net/if_types.h> */
    uint8_t      sdl_nlen;     /* name length, starting in sdl_data[0] */
    uint8_t      sdl_alen;     /* link-layer address length */
    uint8_t      sdl_slen;     /* link-layer selector length */
    char         sdl_data[12]; /* minimum work area, can be larger;
                                contains i/f name and link-layer address */
};
#define LLADDR(s)   ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))


//sdl_data成员含有名字和链路层地址(例如以太网接口的48位MAC地址)。名字从sdl_data[0]开始,而且不以空字符结尾。链路层地址从sdl_data[sdl_nlen]开始。以上宏定义返回链路层地址的指针。

//链路层套接字地址结构可变。如果地址和名字总长度超出12字节,结构体将大于20字节。32位系统上通常向上舍入到下一个4字节的倍数。IF_RECVIF套接字选项返回的本结构中,所有3个长度成员都为0。

三、读和写

创建路由套接字后,与其他套接字一样,可以调用 read() 或 write() 函数进行读写,但是由于系统内核是根据应用程序写入的消息来完成对路由信息的提供和修改的,因此,与其他套接字不同,写入和读出的数据都是有固定的格式。消息定义在<net/route.h>中。
在这里插入图片描述

在这里插入图片描述

路由套接字交换的结构有5个类型:rt_msghdr、if_msghdr、ifa_msghdr、ifma_msghdr和if_announcemsghdr。每个结构有相同的前3个成员:本消息的长度、版本和类型。类型成员是上图第一列中的常值之一。

//<net/route.h>
struct rt_msghdr {
   
	u_short	rtm_msglen;	
	u_char	rtm_version;	
	u_char	rtm_type;	
	u_long	rtm_index;	
	int		rtm_flags;	
	int		rtm_addrs;	
	pid_t	rtm_pid;	
	int		rtm_seq;	
	int		rtm_errno;	
	int		rtm_use;	
	u_long	rtm_inits;
	u_long  rtm_table;     
	struct	rt_metrics rtm_rmx; 
};

//<net/if.h>
struct if_msghdr{
   
	u_short	ifm_msglen;	
	u_char	ifm_version;	
	u_char	ifm_type;
	int		ifm_addrs;
	int		ifm_flags;
	u_short ifm_index;
	struct if_data ifm_data;
};
struct ifa_msghdr{
   
	u_short	ifam_msglen;	
	u_char	ifam_version;	
	u_char	ifam_type;
	int		ifam_addrs;
	int		ifam_flags;
	u_short ifam_index;
	int 	ifam_metric;
};

struct ifma_msghdr{
   
	u_short	ifam_msglen;	
	u_char	ifmam_version;	
	u_char	ifmam_type;
	int		ifmam_addrs;
	int		ifmam_flags;
	u_short ifmam_index;

};
struct if_announcemsghdr{
   
	u_short	ifan_msglen;	
	u_char	ifan_version;	
	u_char	ifan_type;
	u_short ifan_index;
	char ifan_name[IFNAMSIZ];
	u_short ifan_what;
}	

rtm_addrs、ifm_addrs、ifam_addrs这三个成员是数位掩码,指明本消息后跟的套接字地址结构是8个可能选择中的哪几个。在<net/route.h>头文件定义。存在多个套接字地址结构时,按表中所示的顺序排列。
在这里插入图片描述

1.获取并输出一个路由表项

  1. 首先命令行取代IPv4点分十进制数地址。
  2. 将地址向内核发送RTM_GET消息。
  3. 内核在IPv4路由表查询地址,将RTM_GET消息返回相应路由表项的信息。

采用默认路径:
在这里插入图片描述
指定主机第二个以太网接口所在子网为目的的地址:

目的地址为网络本身,网关是外出接口,作为sockaddr_dl结构返回,结构索引为2.
在这里插入图片描述

如下图所示写到路由套接字的信息和由内核返回的信息。

在这里插入图片描述


#include "unproute.h"

#define BUFLEN (sizeof(struct rt_msghdr) + 512)
#define  SEQ 9999


int main(int argc,char **argv)
{
   
int sockfd; 
char* buf; pid_t pid;
ssize_t n; 

struct rt_msghdr* rtm; 
struct sockaddr* sa, *rti_info[RTAX_MAX]; 
struct sockaddr_in* sin; 
if(argc !=2)
err_quit("usage:getrt<IPaddress>");
//创建套接字
sockfd = socket(AF_ROUTE, SOCK_RAW, 0);
//填写rt_msghdr结构
buf = calloc(1, BUFLEN); //分配缓冲区初始化为0
rtm = (struct rt_msghdr*)buf; 
rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
rtm->rtm_version = RTM_VERSION; 
rtm->rtm_type = RTM_GET; 
rtm->rtm_addrs = RTA_DST;
rtm->rtm_pid = pid = getpid();
rtm->rtm_seq = SEQ;
//以目的地址填写网际网套接字地址结构
sin = (struct sockaddr_in*)(rtm + 1);
sin->sin_len = sizeof(struct sockaddr_in);
sin->sin_family = AF_INET; 
inet_pton(AF_INET, argv[1], &sin->sin_addr); 
//
write(sockfd, rtm, rtm->rtm_msglen); 

do {
   
	n = read(sockfd, rtm, BUFLEN); 
} while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != SEQ || rtm->rtm_pid != pid);

//rtm指向rt_msghdr结构,sa指向接在其后的第一个套接字地址结构
rtm = (struct rt_msghdr*)buf;
sa = (struct sockaddr*)(rtm + 1);

//一掩码和指向第一个套接字地址结构的指针为参数(sa),在rti_info数组中填入指向相应套接字地址结构的指针
get_rtaddr(rtm->rtm_addrs, sa, rti_info);

//若存在则逐个显示4个肯能的地址,sock_ntop_host:显示目的地址和网关地址;sock_masktop:显示掩码
if ((sa = rti_info[RTAX_DST]) != NULL)
printf("dest:%s\n", sock_ntop_host(sa, sa->sa_len)); 

if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
printf("dest:%s\n", sock_ntop_host(sa, sa->sa_len));

if ((sa = rti_info[RTAX_NETMASK]) != NULL)
printf("genmask:%s\n", sock_masktop(sa, sa->sa_len));


if ((sa = rti_info[RTAX_GENMASK]) != NULL)
printf("netmask:%s\n", sock_masktop(sa, sa->sa_len));


exit(0)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值