网络编程之-原始套接字

原始套接字可以访问ICMP和ICMP等协议包,可以读写内核不处理的IP数据包。可以创建自定义的IP数据包首部。一句话,使用原始套接字可以编写基于IP协议的通讯程序。

    1.创建原始套接字具体格式如下:

int sockfd;sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);

第一个参数:协议族 AF_INET 代表TCP/IP协议

第二个参数:SOCKET类型

第三个参数:协议类型

注意:@如果指定协议为0时,原始套接字可以接收内核传递给原始套接字的任何IP数据包,且只有超级用户才可以创建原始套接字

    @当需要编写自己的IP数据包首部时,可以在原始套接字上设置套接字选项IP_HDRINCL.在不设置这个选项的情况下,IP协议自动填充IP数据包的首部。

    int on = 1;

   if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)

    { fprintf(stderr, "setsockopt IP_HDRINCL ERROR! /n");exit(1);}

    原始套接字直接使用IP协议的套接字,所以是非面向连接的。在这个套接字上可以调用connect和bind函数,分别执行绑定对方和本地地址。

    说明:

    bind函数:调用bind函数后,发送数据包的源IP地址将是bind函数指定的地址。如是不调用bind,则内核将以发接口的主IP地址填充。如果设置了IP_HDRINCL,那么必须手工填充每个发送数据包的源IP地址。

    connetc函数:调用connect函数后,可以用write和send发送数据包。内核将用这个绑定的地址填充IP数据包的目的IP地址。

    2.发送数据包

使用原始套接字发送数据包必须遵循以下规则:

1.如果没有用connect函数绑定对方地址时,则应使用sendto或sendmsg函数发送数据包,在函数参数中指定对方地址。如果调用了connect函数,则可以直接使用send,write或writev来发送数据包。

2.如果没有设置IP_HDRINCL选项时,包内可写的内容为数据部分,内核将自动创建IP首部。如果设置了IP_HDRINCL选项,则包内要填充的内容为IP数据包和首部。内核只负责填充下面两个域:。如果将IP数据包的标识域设置为0,内核将设置这个域。内核总是计算和填充IP数据包首部的校验和。

注意:IP数据包首部各个域的内容都是网络字节顺序。

    3.接收数据包

    内核遵循以下规则接收数据包:

1.UDP和TCP数据包从不传送给一个原始套接字。如果要查看这两类数据包,只能通过直接访问数据链路层来实现。

2.大多数ICMP数据包的一个拷贝传送给匹配的原始套接字。

3.内核处理的所有其它类型的数据包的一个拷贝都传给匹配的原始套接字。

4.所有内核不能识别的协议类型的IP数据包都传送给匹配的原始套接字。对于这些IP数据包,内核只做必要的检验工作。

    在将一个IP数据包传送给原始套接字之前,内核需要选择匹配的原始套接字

1.数据包的协议域必须与接收原始套接字的协议类型匹配。

2.如果原始套接字调用了bind函数绑定了本地IP地址,那么到达的IP数据包的源IP地址必须和对方的IP相匹配。

3.如果原始套接字调用connect函数指定了对方的IP地址,则到达的IP数据包的源IP地址秘须与这它相同。

    4.更加强大的SOCK_RAM

socket(PF_PACKET, SOCK_RAW, htons(x));

这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来 的,因为内核已经判断过了,对程序来说没有任何意义了.

能: 接收发往本地mac的数据帧

能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)

能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)

协议类型一共有四个

ETH_P_IP 0x800      只接收发往本机mac的ip类型的数据帧

ETH_P_ARP 0x806      只接受发往本机mac的arp类型的数据帧

ETH_P_RARP 0x8035     只接受发往本机mac的rarp类型的数据帧

ETH_P_ALL 0x3         接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)

发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll.

这种socket大小通吃,强悍

相关的代码:

...
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
struct sockaddr_ll sll;
     memset( &sll, 0, sizeof(sll) );
     sll.sll_family = PF_PACKET;
struct ifreq ifstruct;
   strcpy(ifstruct.ifr_name, "eth0");
   ioctl(sockfd, SIOCGIFINDEX, &ifstruct);
    sll.sll_ifindex = ifstruct.ifr_ifindex;
     sll.sll_protocol = htons(ETH_P_ALL);
if(bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1 ) {
   perror("bind()");
...

int set_promisc(char *interface, int fd) {
struct ifreq ifr;
         strcpy(ifr.ifr_name, interface);
if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         ifr.ifr_flags |= IFF_PROMISC;
if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         return 0;
}
int unset_promisc(char *interface, int fd) {
struct ifreq ifr;
         strcpy(ifr.ifr_name, interface);
if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         ifr.ifr_flags &= ~IFF_PROMISC;
if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值