免费织梦导航网站模板下载,ai室内设计生成软件,工业设计优秀作品,网站二级目录建站在Linux系统中#xff0c;使用非原始的socket#xff0c;可以收发TCP或者UDP等网络层数据包。如果要处理网络层以下的数据包#xff0c;比如ICMP、ARP等#xff0c;或者更底层#xff0c;比如链路层数据包#xff0c;就得使用原始socket了。
创建socket
创建socket要使用…在Linux系统中使用非原始的socket可以收发TCP或者UDP等网络层数据包。如果要处理网络层以下的数据包比如ICMP、ARP等或者更底层比如链路层数据包就得使用原始socket了。
创建socket
创建socket要使用socket函数socket函数的原型为
#include sys/types.h
#include sys/socket.h int socket(int domain, int type, int protocol);其中domain比较常用的值有
AF_UNIX Unix本地通讯AF_INET IPv4网络协议AF_INET6 IPv6网络协议AF_NETLINK 内核用户界面设备AF_PACKET 底层包设备
我们要处理网络层以下的数据包就得使用AF_PACKET。
type比较常用的值有
SOCK_STREAM 用于面向字节流的协议如TCPSOCK_DGRAM 用于面向数据报的协议如UDPSOCK_RAW 用于非处理的原始数据包
我们这里要使用SOCK_RAW。
protocol是要使用的网络协议。
如下代码创建一个原始socket
#include sys/types.h
#include sys/socket.hint raw_socket_new () {int fd;fd socket (PF_PACKET, SOCK_RAW, IPPROTO_RAW);if (fd 0){fprintf (stderr, socket error: %s\n, strerror (errno));}return fd;绑定网口
如果我们处理非原始的数据包可以使用网络层绑定IP或者连接IP其实socket也会自动绑定IP协议栈会自动根据IP把数据包发往特定的网络接口不需要绑定网口。
我们通过原始socket发送底层数据包就需要绑定网口了。
绑定网口的方法很简单先使用if_nametoindex函数找到对应网口名的编号之后设置到struct sockaddr_ll结构中调用bind函数就行了。
if_nametoindex的原型为
#include net/if.hunsigned int if_nametoindex(const char *ifname);struct sockaddr_ll的定义为
#include linux/if_packet.hstruct sockaddr_ll { unsigned short sll_family; __be16 sll_protocol; int sll_ifindex; unsigned short sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8];
};以下函数就把给定的网络接口绑定到了给定的socket上
#include net/if.h
#include linux/if_packet.hint raw_socket_bind(int fd, const char *eth) {struct sockaddr_ll sa;memset (sa, 0, sizeof (struct sockaddr_ll));// 原始数据包sa.sll_family AF_PACKET;// 自定义二层协议sa.sll_protocol htons (CUSTOM_TYPE);sa.sll_ifindex if_nametoindex (eth);if (bind (jdpdk-fd, (struct sockaddr *)sa, sizeof (struct sockaddr_ll))! 0){fprintf (stderr, bind error: %s\n, strerror (errno));return -1;}return 0;
}二层包结构
socket创建成功绑定了网口就可以发送自定义的二层数据包了。
根据TCP/IP标准二层的数据包包头为14个字节即6字节的目标MAC地址、6字节的源MAC地址再加2字节的包类型。
在net/ethernet.h中结构定义为
struct ether_header
{uint8_t ether_dhost[ETH_ALEN]; uint8_t ether_shost[ETH_ALEN];uint16_t ether_type;
} __attribute__ ((__packed__));在我们使用网口发送的时候源MAC地址可以设成全0但是目标MAC地址必须设置。
我们可以实现一个组包函数把给定的数据设上预订的目标地址
int raw_encode (const char *dst_mac, unsigned short type, const char *data, size_t data_len, char *packet, size_t packet_len) {assert (14 data_len packet_len);memcpy (packet, dst_mac, 6);memset (packet 6, 0, 6);* (unsigned short *) (packet 12) htons (type);memcpy (packet 14, data, data_len);return 14 data_len;
}发送
发送函数就比较简单跟网络层的发送没有区别。
int raw_send (int fd, const char *data, int data_len) {int ret;ret send (fd, data, data_len, 0); if (ret ! data_len) { fprintf (stderr, send error: %s\n, strerror (errno)); }return ret;
}接收
接收函数也同样只是记得接收成功以后偏移14字节即二层包头以后才是我们的应用层数据
int raw_recv (int fd, char *packet, size_t packet_len) {int ret;ret recv (fd, packet, packet_len, 0);return ret;
}原始socket示例arp组包
根据TCP/IP协议当我们网络层使用TCP、UDP或者ICMP等使用IP地址作为目标的数据包时协议栈要查询本机的arp表找到IP地址对应的MAC地址。
如果查询失败就会发送arp请求通过arp响应来得到目标MAC地址。
所以arp协议有两种数据包一种是arp请求一种是arp响应。对应到arp结构中就是操作码的值为1或者2。
在linux/if_arp.h中有arp包的包头结构
struct arphdr { __be16 ar_hrd;__be16 ar_pro;unsigned char ar_hln;unsigned char ar_pln;__be16 ar_op;
};在arp的包体中分别是发送MAC、发送IP、目标MAC与目标IP。
当arp请求的时候设置arp包头中的操作码为1另外设置发送MAC即本机MAC与目标IP发送IP与目标MAC置空。
当arp响应的时候设置arp包头中的操作码为2另外设置发送MAC即本机MAC、发送IP即本机IP、目标MAC即请求时的发送MAC目标IP置空。
我们写一个组包函数
#include net/ethernet.h
#include arpa/inet.h
#include netinet/if_ether.h
#include memory.hvoid
buf_encode_arp (uint8_t *buf, uint16_t opcode, const uint8_t *source_mac, const uint8_t *dst_mac, unsigned long sip, unsigned long dip)
{struct ether_header *hdr; hdr (struct ether_header *)buf; if (source_mac) { memcpy (hdr-ether_shost, source_mac, sizeof hdr-ether_shost);}// arp请求的时候二层目标MAC地址填全0xFF否则填全0memset (hdr-ether_dhost, opcode 1 ? 0xFF : 0, sizeof hdr-ether_dhost);if (dst_mac) { memcpy (hdr-ether_dhost, dst_mac, sizeof hdr-ether_dhost);}hdr-ether_type htons (ETHERTYPE_ARP);struct ether_arp *arp (struct ether_arp *)(hdr 1);// 设置arp包头arp-ea_hdr.ar_hrd htons (1);arp-ea_hdr.ar_pro htons (0x800);arp-ea_hdr.ar_hln 6;arp-ea_hdr.ar_pln sizeof (uint32_t);arp-ea_hdr.ar_op htons (opcode);// 根据输入参数设置arp包体memset (arp-arp_sha, 0, sizeof arp-arp_sha);if (source_mac) { memcpy (arp-arp_sha, source_mac, sizeof arp-arp_sha);} memset (arp-arp_tha, 0, sizeof arp-arp_tha);if (dst_mac) { memcpy (arp-arp_tha, dst_mac, sizeof arp-arp_tha);}memcpy (arp-arp_spa, sip, 4);memcpy (arp-arp_tpa, dip, 4);
}