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

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

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

3). 进程可以用sysctl函数得到路由表或列出所有已配置的接口。

数据链路套接字地址结构

通过路由套接字返回的一些消息中含有作为返回值给出的数据链路套接字地址结构

  1. struct sockaddr_dl {
  2. uint8_t sdl_len;
  3. sa_family_t sdl_family; /* AF_LINK */
  4. uint16_t sdl_index; /* system assigned index, if > 0 */
  5. uint8_t sdl_type; /* IFT_ETHER, etc. from */
  6. 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 */
  1. };

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消息返回相应路由表项的消息。

该程序代码如下

  1. /* include getrt1 */
  2. #include "unproute.h"
  3.  
  4. #define BUFLEN (sizeof(struct rt_msghdr) + 512)
  5. /* sizeof(struct sockaddr_in6) * 8 = 192 */
  6. #define SEQ 9999
  7.  
  8. int
  9. main(int argc, char **argv)
  10. {
  11. int sockfd;
  12. char *buf;
  13. pid_t pid;
  14. ssize_t n;
  15. struct rt_msghdr *rtm;
  16. struct sockaddr *sa, *rti_info[RTAX_MAX];
  17. struct sockaddr_in *sin;
  18.  
  19. if (argc != )
  20. err_quit("usage: getrt <IPaddress>");
  21.  
  22. sockfd = Socket(AF_ROUTE, SOCK_RAW, ); /* need superuser privileges */
  23.  
  24. buf = Calloc(, BUFLEN); /* and initialized to 0 */
  25.  
  26. rtm = (struct rt_msghdr *) buf;
  27. rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
  28. rtm->rtm_version = RTM_VERSION;
  29. rtm->rtm_type = RTM_GET;
  30. rtm->rtm_addrs = RTA_DST;
  31. rtm->rtm_pid = pid = getpid();
  32. rtm->rtm_seq = SEQ;
  33.  
  34. sin = (struct sockaddr_in *) (rtm + );
  35. sin->sin_len = sizeof(struct sockaddr_in);
  36. sin->sin_family = AF_INET;
  37. Inet_pton(AF_INET, argv[], &sin->sin_addr);
  38.  
  39. Write(sockfd, rtm, rtm->rtm_msglen);
  40.  
  41. do {
  42. n = Read(sockfd, rtm, BUFLEN);
  43. } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != SEQ ||
  44. rtm->rtm_pid != pid);
  45. /* end getrt1 */
  46.  
  47. /* include getrt2 */
  48. rtm = (struct rt_msghdr *) buf;
  49. sa = (struct sockaddr *) (rtm + );
  50. get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
  51. if ( (sa = rti_info[RTAX_DST]) != NULL)
  52. printf("dest: %s\n", Sock_ntop_host(sa, sa->sa_len));
  53.  
  54. if ( (sa = rti_info[RTAX_GATEWAY]) != NULL)
  55. printf("gateway: %s\n", Sock_ntop_host(sa, sa->sa_len));
  56.  
  57. if ( (sa = rti_info[RTAX_NETMASK]) != NULL)
  58. printf("netmask: %s\n", Sock_masktop(sa, sa->sa_len));
  59.  
  60. if ( (sa = rti_info[RTAX_GENMASK]) != NULL)
  61. printf("genmask: %s\n", Sock_masktop(sa, sa->sa_len));
  62.  
  63. exit();
  64. }
  65. /* 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函数

  1. #include "unproute.h"
  2.  
  3. /*
  4. * Round up 'a' to next multiple of 'size', which must be a power of 2
  5. */
  6. #define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
  7.  
  8. /*
  9. * Step to next socket address structure;
  10. * if sa_len is 0, assume it is sizeof(u_long).
  11. */
  12. #define NEXT_SA(ap) ap = (SA *) \
  13. ((caddr_t) ap + (ap->sa_len ? ROUNDUP(ap->sa_len, sizeof (u_long)) : \
  14. sizeof(u_long)))
  15.  
  16. void
  17. get_rtaddrs(int addrs, SA *sa, SA **rti_info)
  18. {
  19. int i;
  20.  
  21. for (i = ; i < RTAX_MAX; i++) {
  22. if (addrs & ( << i)) {
  23. rti_info[i] = sa;
  24. NEXT_SA(sa);
  25. } else
  26. rti_info[i] = NULL;
  27. }
  28. }

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

  1. #include "unproute.h"
  2.  
  3. const char *
  4. sock_masktop(SA *sa, socklen_t salen)
  5. {
  6. static char str[INET6_ADDRSTRLEN];
  7. unsigned char *ptr = &sa->sa_data[];
  8.  
  9. if (sa->sa_len == )
  10. return("0.0.0.0");
  11. else if (sa->sa_len == )
  12. snprintf(str, sizeof(str), "%d.0.0.0", *ptr);
  13. else if (sa->sa_len == )
  14. snprintf(str, sizeof(str), "%d.%d.0.0", *ptr, *(ptr+));
  15. else if (sa->sa_len == )
  16. snprintf(str, sizeof(str), "%d.%d.%d.0", *ptr, *(ptr+), *(ptr+));
  17. else if (sa->sa_len == )
  18. snprintf(str, sizeof(str), "%d.%d.%d.%d",
  19. *ptr, *(ptr+), *(ptr+), *(ptr+));
  20. else
  21. snprintf(str, sizeof(str), "(unknown mask, len = %d, family = %d)",
  22. sa->sa_len, sa->sa_family);
  23. return(str);
  24. }

sysctl操作

  1. #include <sys/param.h>
  2. #include <sys/sysctl.h>
  3.  
  4. 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校检和是否开启

  1. #include "unproute.h"
  2. #include <netinet/udp.h>
  3. #include <netinet/ip_var.h>
  4. #include <netinet/udp_var.h> /* for UDPCTL_xxx constants */
  5.  
  6. int
  7. main(int argc, char **argv)
  8. {
  9. int mib[], val;
  10. size_t len;
  11.  
  12. mib[] = CTL_NET;
  13. mib[] = AF_INET;
  14. mib[] = IPPROTO_UDP;
  15. mib[] = UDPCTL_CHECKSUM;
  16.  
  17. len = sizeof(val);
  18. Sysctl(mib, , &val, &len, NULL, );
  19. printf("udp checksum flag: %d\n", val);
  20.  
  21. exit();
  22. }

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. tail /grep/more

    1.tail -f 文件名 不断的刷新日志信息,实时的得到新追加到文件中的信息,常用来跟踪日志文件,如tail -f err.log Ctrl+C退出 2.grep '内容' 文件名 3.more 分 ...

  2. 【bzoj4896】[Thu Summer Camp2016]补退选 Trie树+STL-vector

    题目描述 X是T大的一名老师,每年他都要教授许多学生基础的C++知识.在T大,每个学生在每学期的开学前都需要选课,每次选课一共分为三个阶段:预选,正选,补退选:其中"补退选"阶段最 ...

  3. IO文本操作

    创建文件并写入内容 StreamWriter sw = new StreamWriter(url, “false 覆盖,true 追加”, Encoding.UTF8); sw.Write(“内容”) ...

  4. Fabric和Sawtooth技术分析(上)

    https://mp.weixin.qq.com/s?__biz=MjM5MDAxMTE0MA==&mid=2652049866&idx=1&sn=5b4aea961f3d64 ...

  5. python爬虫异常处理

    import urllib2 try: response = urllib2.urlopen('http://www.baidu.com') except urllib2.URLError, e: p ...

  6. 【HDOJ5956】The Elder(树形DP,斜率优化)

    题意:有一棵n个点的有根树,每条边上有一个边权.给定P,从i跳到它的祖先j的费用是距离的平方+P,问所有点中到根节点1的总花费最大值 n<=1e5,p<=1e6,w<=1e2 思路: ...

  7. awk 使用方法

    awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各 ...

  8. python的上下文管理

    说道上下文管理首先想到的就是这个: class MyResource: def __enter__(self): print("查询开始") return self def __e ...

  9. Hadoop-hdfs安装与配置

    一.安装要求   安装JDK   yum -y install jdk(或手动安装)  设置namenode节点到datanode节点的免密码登陆   a. 本地免密码登录     # ssh loc ...

  10. UVA 11059 Maximum Product【三层暴力枚举起终点】

    [题意]:乘积最大的子序列.n∈[1,10],s∈[-10,10] [代码]: #include<bits/stdc++.h> using namespace std; int a[105 ...