UNP学习笔记(第十八章 路由套接字)
路由套接字上支持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学习笔记(第十八章 路由套接字)的更多相关文章
- UNP学习笔记(第七章 套接字选项)
有多种方法获取和设置影响套接字的选项: 1.getsockopt和setsockopt函数 2.fcntl函数 3.ioctl函数 getsockopt和setsockopt函数 这两个函数仅用于套接 ...
- UNP学习笔记(第八章 基本UDP套接字编程)
UDP应用程序客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数. 下图给出典型的UDP客户/服务器程序的函数调用. recvfrom和sendt ...
- 流畅python学习笔记第十八章:使用asyncio编写服务器
在这一章中,将使用asyncio写一个TCP服务器.这个服务器的作用是通过规范名称查找Unicode字符,来看下代码: import asyncio from charfinder import Un ...
- APUE 学习笔记(十一) 网络IPC:套接字
1. 网络IPC 套接字接口既可以用于计算机之间进程通信,也可以用于计算机内部进程通信 套接字描述符在Unix系统中是用文件描述符实现的 /* 创建一个套接字 */ #include < ...
- 流畅python学习笔记第十八章:使用asyncio包处理并发(二)
前面介绍了asyncio的用法.下面我们来看下如何用协程的方式来实现之前的旋转指针的方法 @asyncio.coroutine def spin(msg): write,flush=sys.stdou ...
- 流畅python学习笔记第十八章:使用asyncio包处理并发(一)
首先是线程与协程的对比.在文中作者通过一个实例分别采用线程实现和asynchio包实现来比较两者的差别.在多线程的样例中,会用到join的方法,下面来介绍下join方法的使用. 知识点一:当一个进程启 ...
- UNP学习笔记1——基本TCP套接字编程
1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...
- Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例
目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...
- Nodejs学习笔记(十六)--- Pomelo介绍&入门
目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...
随机推荐
- POJ 2976 Dropping tests(01分数规划入门)
Dropping tests Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11367 Accepted: 3962 D ...
- [NOI2009] 植物大战僵尸 [网络流]
题面: 传送门 思路: 这道题明显可以看出来有依赖关系 那么根据依赖(保护)关系建图:如果a保护b则连边(a,b) 这样,首先所有在环上的植物都吃不到,被它们间接保护的也吃不到 把这些植物去除以后,剩 ...
- ZOJ 3874 Permutation Graph ——分治 NTT
发现每一块一定是按照一定的顺序的. 然后与标号无关,并且相同大小的对答案的影响相同. 然后列出递推式,上NTT+分治就可以了. 然后就可以与输入同阶处理答案了. #include <map> ...
- javasript 按值传递
现象总结如下: 1.JS的基本类型,是按值传递的.2.对于对象而言:分两种情况(a).如果传递给函数的参数是对象,并且修改了这个对象的属性(某些字段的值),那么奇妙的问题就来了.原参数就被修改了.(b ...
- Python之文件操作:文件、目录的操作
一.创建 1.创建文件 open(path,'w') 2.创建目录 (1)os.mkdir(pt[, mode=0777]) 新建一个目录pt,参数mode表示生成的目录的权限,默认是超级权限,也就是 ...
- 微信小程序 body属性的问题
微信小程序里面没有了body这个节点了,取而代之的是page
- 使用非对称算法RSA实现加解密和使用签名算法SHA1WithRSA、MD5withRSA生成签名以及验签
不啰嗦,直接上源码 package com.hudai.platform.manager.util; import java.io.ByteArrayOutputStream; import java ...
- 创建外网 ext
虽然外部网络是已经存在的网络,但我们还是需要在 Neutron 中定义外部网络的对象,这样 router 才知道如何将租户网络和外部网络连接起来. 上一节我们已经为创建外部网络配置了ML2,本节将通过 ...
- 【CF1016D】Vasya And The Matrix(构造)
题意: 思路:构造方式见代码…… #include<cstdio> #include<cstring> #include<iostream> #include< ...
- luogu 1258 小车问题 小学奥数(?)
题目链接 题意 甲.乙两人同时从A地出发要尽快同时赶到B地.出发时A地有一辆小车,可是这辆小车除了驾驶员外只能带一人.已知甲.乙两人的步行速度一样,且小于车的速度.问:怎样利用小车才能使两人尽快同时到 ...