路由套接字上支持3种类型的操作

1). 进程能通过写路由套接字向内核发消息。

2). 进程能通过路由套接字从内核读消息。

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 */
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[]; /* minimum work area, can be larger;
contains i/f name and link-layer address */
};

sdl_data成员含有名字和链路层地址(例如以太网接口的48位MAC地址)。

名字从sdl_data[0]开始,而且不以空字符结尾。链路层地址从sdl_data[sdl_nlen]开始

读和写

创建一个路由套接字后,进程可以通过写到该套接字向内核发送命令,通过读自该套接字从内核接收信息。

路由域套接字共有12个路由消息(还有黑体3个是新增的)

通过路由套接字交换的结构有5个类型:rt_msghdr、if_msghdr、ifa_msghdr、ifma_msghdr和if_announcemsghdr

每个结构有相同的前3个成员:本消息的长度、版本和类型。类型成员是上图第一列中的常值之一。

例子:获取并输出一个路由表项

这个程序使用一个IPv4点分十进制数地址作为命令行参数,并就这个地址向内核发送一个RTM_GET消息。

内核在它的IPv4路由表中查找这个地址,并作为一个RTM_GET消息返回相应路由表项的消息。

该程序代码如下

 /* include getrt1 */
#include "unproute.h" #define BUFLEN (sizeof(struct rt_msghdr) + 512)
/* sizeof(struct sockaddr_in6) * 8 = 192 */
#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 != )
err_quit("usage: getrt <IPaddress>"); sockfd = Socket(AF_ROUTE, SOCK_RAW, ); /* need superuser privileges */ buf = Calloc(, BUFLEN); /* and initialized to 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 + );
sin->sin_len = sizeof(struct sockaddr_in);
sin->sin_family = AF_INET;
Inet_pton(AF_INET, argv[], &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);
/* end getrt1 */ /* include getrt2 */
rtm = (struct rt_msghdr *) buf;
sa = (struct sockaddr *) (rtm + );
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
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("gateway: %s\n", Sock_ntop_host(sa, sa->sa_len)); if ( (sa = rti_info[RTAX_NETMASK]) != NULL)
printf("netmask: %s\n", Sock_masktop(sa, sa->sa_len)); if ( (sa = rti_info[RTAX_GENMASK]) != NULL)
printf("genmask: %s\n", Sock_masktop(sa, sa->sa_len)); exit();
}
/* end getrt2 */

17          创建一个AF_ROUTE域的原始套接字(SOCK_RAW)

18-25     分配一个缓冲区。在该缓冲区上构造一个rt_msghdr结构:天界我们的请求,并存放我们的进程ID和选定的序列号。

26-29     紧跟着rt_msghdr结构,我们构造一个sockaddr_in结构。

30-34     write构造号的消息到内核调用read等待应答。因为其他进程也可能打开路由套接字,而且内核给所有路由套接字都传送一个全部路由消息的副本。

于是我们必须检查消息的类型、序列号和进程ID,以确保收到的消息正是我们所等待的应答。

35-36      rtm指向rt_msghdr结构,sa指向接在其后的第一个套接字地址结构

37           rtm_addrs是一个数位掩码,指出接在rt_msghdr结构之后的是8个可能的套接字地址结构中的哪几个。

get_rtaddrs函数以该掩码和指向第一个套接字地址结构的指针为参数(sa)为参数,在rti_info数组中填入指向相应套接字地址结构的指针

下面是get_rtaddrs函数

 #include    "unproute.h"

 /*
* Round up 'a' to next multiple of 'size', which must be a power of 2
*/
#define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) /*
* Step to next socket address structure;
* if sa_len is 0, assume it is sizeof(u_long).
*/
#define NEXT_SA(ap) ap = (SA *) \
((caddr_t) ap + (ap->sa_len ? ROUNDUP(ap->sa_len, sizeof (u_long)) : \
sizeof(u_long))) void
get_rtaddrs(int addrs, SA *sa, SA **rti_info)
{
int i; for (i = ; i < RTAX_MAX; i++) {
if (addrs & ( << i)) {
rti_info[i] = sa;
NEXT_SA(sa);
} else
rti_info[i] = NULL;
}
}

下面是sock_maskop,它返回可通过路由套接字返回的那两种掩码的表达字符串。

 #include    "unproute.h"

 const char *
sock_masktop(SA *sa, socklen_t salen)
{
static char str[INET6_ADDRSTRLEN];
unsigned char *ptr = &sa->sa_data[]; if (sa->sa_len == )
return("0.0.0.0");
else if (sa->sa_len == )
snprintf(str, sizeof(str), "%d.0.0.0", *ptr);
else if (sa->sa_len == )
snprintf(str, sizeof(str), "%d.%d.0.0", *ptr, *(ptr+));
else if (sa->sa_len == )
snprintf(str, sizeof(str), "%d.%d.%d.0", *ptr, *(ptr+), *(ptr+));
else if (sa->sa_len == )
snprintf(str, sizeof(str), "%d.%d.%d.%d",
*ptr, *(ptr+), *(ptr+), *(ptr+));
else
snprintf(str, sizeof(str), "(unknown mask, len = %d, family = %d)",
sa->sa_len, sa->sa_family);
return(str);
}

sysctl操作

#include <sys/param.h>
#include <sys/sysctl.h> int sysctl(int *name,u_int namelen,void *oldp,size_t *oldlenp,void *newp,size_t newlen);

我们对路由套接字的主要兴趣点在于使用sysctl函数检查路由表和接口列表。

name参数是指定名字的一个整数数组,namelen参数指定该数组中的元素数目。该数组中的第一个元素指定本请求定向到内核的哪个子系统,第二个及其后元素逐次系话指定该子系统的某个部分。

下面展示了这样的分成排列

为了获取某个值,oldp参数指向一个供内核存放该值得缓冲区,oldlenp则是一个值-结果参数。

为了设置某个值,newp参数指向一个大小为newlen参数的缓冲区。

该函数可以获取各种系统信息,有文件系统、虚拟内存、内核限制、硬件等各方面的信息。

我们感兴趣的是网络子系统,通过吧name数组的第一个元素设置为CTL_NET来指定,第二个元素可以是以下几种:

AF_INET:       获取或设置影响网际网协议的变量。下一级为使用某个IPPROTO_xxx常值指定的具体协议

AF_LINK:       获取或设置链路层信息,比如PPP接口的数目

AF_ROUTE:   返回路由表或接口列表的信息

AF_UNSPEC:    获取或设置一些套接字层变量,比如套接字发送或接收缓冲区的最大大小

下面给出如果name数组第二个元素为AF_ROUTE时,该数组可能的取值

NET_RT_DUMP返回由name[3]指定的地址族的路由表。如果所指定的地址族为0,那么返回所有地址族的路由表。

NET_RT_FLAGS返回由name[3]指定的地址族的路由表,但是仅限于那些所带标志(若干个RTF_xxx常值的逻辑或)与由name[5]指定的标志相匹配的路由表项。

NET_RT_IFLIST返回所有已配置接口的信息。

例子:判断UDP校检和是否开启

 #include    "unproute.h"
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/udp_var.h> /* for UDPCTL_xxx constants */ int
main(int argc, char **argv)
{
int mib[], val;
size_t len; mib[] = CTL_NET;
mib[] = AF_INET;
mib[] = IPPROTO_UDP;
mib[] = UDPCTL_CHECKSUM; len = sizeof(val);
Sysctl(mib, , &val, &len, NULL, );
printf("udp checksum flag: %d\n", val); exit();
}

UNP学习笔记(第十八章 路由套接字)的更多相关文章

  1. UNP学习笔记(第七章 套接字选项)

    有多种方法获取和设置影响套接字的选项: 1.getsockopt和setsockopt函数 2.fcntl函数 3.ioctl函数 getsockopt和setsockopt函数 这两个函数仅用于套接 ...

  2. UNP学习笔记(第八章 基本UDP套接字编程)

    UDP应用程序客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数. 下图给出典型的UDP客户/服务器程序的函数调用. recvfrom和sendt ...

  3. 流畅python学习笔记第十八章:使用asyncio编写服务器

    在这一章中,将使用asyncio写一个TCP服务器.这个服务器的作用是通过规范名称查找Unicode字符,来看下代码: import asyncio from charfinder import Un ...

  4. APUE 学习笔记(十一) 网络IPC:套接字

    1. 网络IPC 套接字接口既可以用于计算机之间进程通信,也可以用于计算机内部进程通信   套接字描述符在Unix系统中是用文件描述符实现的   /* 创建一个套接字 */ #include < ...

  5. 流畅python学习笔记第十八章:使用asyncio包处理并发(二)

    前面介绍了asyncio的用法.下面我们来看下如何用协程的方式来实现之前的旋转指针的方法 @asyncio.coroutine def spin(msg): write,flush=sys.stdou ...

  6. 流畅python学习笔记第十八章:使用asyncio包处理并发(一)

    首先是线程与协程的对比.在文中作者通过一个实例分别采用线程实现和asynchio包实现来比较两者的差别.在多线程的样例中,会用到join的方法,下面来介绍下join方法的使用. 知识点一:当一个进程启 ...

  7. UNP学习笔记1——基本TCP套接字编程

    1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...

  8. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  9. Nodejs学习笔记(十六)--- Pomelo介绍&入门

    目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...

随机推荐

  1. serviceImpl中,方法加@Override注释后报错

    @Override public List<SysAdminMenu> getAdminMenusAll() { return sysAdminMenuMapper.getAdminMen ...

  2. 初始化一个Express项目

    首先新建一个目录mkdir myblog,在该目录下运行npm init 生成package.json 然后安装express,加--save写入package.json npm i express@ ...

  3. 【03】react 之 创建component

    React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归:具体的三种方式: 函数式定义的无状态组件 es5原生方式React.createClass定义的组件 es6形式的ext ...

  4. wsl折腾记

    参考1 wsl在哪 C:\Users\用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndg ...

  5. 【CF1073A】Diverse Substring(签到)

    题意:给定一个由小写字母组成的串,要求找出一个子串使得其中出现最多的字母不超过它长度的一半下取整 n<=1e3 思路: #include<cstdio> #include<cs ...

  6. VIM使用技巧2

    假如有以下程序片段: var foo = 1 var bar = 'a' var foobar = foo + bar 如果我们想在每行行尾加上分号, (1)使用$移动光标到行尾, (2)执行a;&l ...

  7. ftk学习记(label篇)【转】

    转自:http://blog.csdn.net/feixiaoxing/article/details/25000093 版权声明:本文为博主原创文章,未经博主允许不得转载. [ 声明:版权所有,欢迎 ...

  8. 【剑指offer】数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  9. LeetCode OJ-- N-Queens **

    https://oj.leetcode.com/problems/n-queens/ n皇后问题,1皇后有1个解,4皇后2个解,8皇后也有解…… 每个皇后不能在同一行上,同一列上,以及同一条45度线上 ...

  10. 用Wireshark分析Socket连接建立的过程

    0. 安装Wireshark,但是默认情况下,Wireshark无法捕获127.0.0.1的报文 解决方案:安装npcap,替换默认的winpacp,重新启动Wireshark,就可以看到一个名字中含 ...