1.介绍
在linux中提供了PF_PACKET接口可以操作链路层的数据。
2.使用方法
定义一个pf_packet = socket(PF_SOCKET, SOCK_RAW, htons(ETH_P_RARP));
就可以利用函数sendto和recefrom来读取和发送链路层的数据包了(当然,发送ARP包,上面第三个参数要变为htons(ETH_P_ARP),或者IP的包为ETH_P_IP,可查看文件/usr/include/linux/if_ether.h文件看到所有支持的协议)。
3.在使用SOCK_RAW, SOCK_DGRAM和SOCK_PACKET的区别
在socket的第一个参数使用PF_PACKET的时候,上述三种socket的类型都可以使用。但是有区别。
(1)使用SOCK_RAW发送的数据必须包含链路层的协议头,接受得到的数据包,包含链路层协议头。而使用SOCK_DGRAM则都不含链路层的协议头。
(2)SOCK_PACKET也是可以使用的,但是已经废弃,以后不保证还能支持,不推荐使用。
(3)在使用SOCK_RAW或SOCK_DGRAM和SOCK_PACKET时,在sendto和recvfrom中使用的地址类型不同,前两者使用sockaddr_ll类型的地址,而后者使用sockaddr类型的地址。
(4)如socket的第一个参数使用PF_INET,第二个参数使用SOCK_RAW,则可以得到原始的IP包。
4.下面的例子是一个简单的rarp协议的server程序和client程序
server程序一开始获得除lo接口以外接口的mac地址,等待rarp request请求的到来,如果请求的是自己的mac地址,则向客户端发送rarp reply,回送自己的ip地址。应我使用的地方,一台机器的ip地址每次dhcp以后都会变。所以该程序还是有一些用处。
注意:本程序只为演示packet socket的工作原理,所以没有进行任何的错误处理,并假设工作的机器上只有ethernet接口。但是本程序有个缺点,就是两个程序工作在同一台机器上的时候,server无法接收到client的rarp request。请知道的朋友赐教,谢谢!
//File Name : rarp_server.cpp
extern "C"
{
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
}
#include
/* args: yiaddr - what IP to ping
* ip - our ip
* mac - our arp address
* interface - interface to use
* retn: 1 addr free
* 0 addr used
* -1 error
*/
/* FIXME: match response against chaddr */
struct arpMsg {
struct ethhdr ethhdr; /* Ethernet header */
u_short htype; /* hardware type (must be ARPHRD_ETHER) */
u_short ptype; /* protocol type (must be ETH_P_IP) */
u_char hlen; /* hardware address length (must be 6) */
u_char plen; /* protocol address length (must be 4) */
u_short operation; /* ARP opcode */
u_char sHaddr[6]; /* sender's hardware address */
u_char sInaddr[4]; /* sender's IP address */
u_char tHaddr[6]; /* target's hardware address */
u_char tInaddr[4]; /* target's IP address */
u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
};
/* miscellaneous defines */
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
#define OPT_CODE 0
#define OPT_LEN 1
#define OPT_DATA 2
struct interface_info
{
char ifname[64];
unsigned char ip[4];
unsigned char mac[6];
};
struct interface_info if_info[10];
int eth_num = 0;
void print_mac(unsigned char * mac_addr)
{
for (int i =0; i < 6; ++i)
{
printf("%02X", mac_addr[i]);
if (i != 5) printf(":");
}
printf("\n");
}
void print_ip(unsigned char * ip_addr)
{
for (int i =0; i < 4; ++i)
{
printf("%d", ip_addr[i]);
if (i != 3) printf(".");
}
printf("\n");
}
int get_iface_index(int fd, const char* interface_name)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strcpy (ifr.ifr_name, interface_name);
if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1)
{
return (-1);
}
return ifr.ifr_ifindex;
}
int get_interfaces()
{
int sock;
int len = 64;
int last_len = 0;
char *pBuff = NULL;
int interface_num = 0;
struct ifconf interface_conf;
struct ifreq ifreq1;
struct sockaddr_in *psockaddr_in = NULL;
if ( (sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Could not create socket for geting interface info");
exit(1);
}
while(1)
{
pBuff = (char*)malloc(len);
interface_conf.ifc_len = len;
interface_conf.ifc_buf = pBuff;
if (ioctl(sock, SIOCGIFCONF, &interface_conf) < 0)
{
perror("ioctl error");
}
else
{
if (interface_conf.ifc_len == last_len)
{
break;
}
else
{
last_len = interface_conf.ifc_len;
}
}
len += 2*sizeof(struct ifreq);
free(pBuff);
}
interface_num = last_len / sizeof(struct ifreq);
for (int i =0; i < interface_num; ++i)
{
strcpy(ifreq1.ifr_name, interface_conf.ifc_ifcu.ifcu_req[i].ifr_name);
if (strcmp(ifreq1.ifr_name, "lo") == 0)
{
continue;
}
if (ioctl(sock, SIOCGIFHWADDR, &ifreq1) < 0)
{
continue;
}
memcpy(if_info[eth_num].mac, ifreq1.ifr_hwaddr.sa_data, 6);
strcpy(if_info[eth_num].ifname, ifreq1.ifr_name);
psockaddr_in = (struct sockaddr_in*)&interface_conf.ifc_req[i].ifr_addr;
memcpy(if_info[eth_num].ip, &(psockaddr_in->sin_addr.s_addr), 4);
printf("Interface name: %s", if_info[eth_num].ifname);
printf(" ip address: ");
print_ip(if_info[eth_num].ip);
printf(" mac address:");
print_mac(if_info[eth_num].mac);
eth_num++;
}
free(pBuff);
close(sock);
}
int equal_mac(unsigned char* mac1, unsigned char* mac2)
{
for (int i =0; i < 6; ++i)
{
if (mac1[i] != mac2[i]) return 0;
}
return 1;
}
int main()
{
int timeout = 2;
int optval = 1;
int s; /* socket */
int rv = 1; /* return value */
struct sockaddr_ll addr; /* for interface name */
struct arpMsg arp;
struct arpMsg *parp;
fd_set fdset;
struct timeval tm;
time_t prevTime;
u_int32_t ip;
u_int32_t yiaddr;
struct in_addr my_ip;
struct in_addr dst_ip;
char buff[2000];
int nLen;
char szBuffer[4096];
if ((s = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_RARP))) == -1)
{
printf("Could not open raw socket\n");
return -1;
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
{
printf("Could not setsocketopt on raw socket\n");
close(s);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_ifindex = get_iface_index(s, "eth0");
addr.sll_protocol = htons(ETH_P_ARP);
get_interfaces();
memset(szBuffer, 0, sizeof(szBuffer));
while ((nLen = recvfrom(s, szBuffer, sizeof(szBuffer), MSG_TRUNC, NULL, NULL)) > 0)
{
parp = (struct arpMsg*)szBuffer;
printf("The request is from ");
print_ip(parp->sInaddr);
for (int i = 0; i < eth_num; ++i)
{
if (equal_mac(if_info[i].mac, parp->tHaddr))
{
/* send arp request */
memset(&arp, 0, sizeof(arp));
memcpy(arp.ethhdr.h_dest, parp->sHaddr, 6); // MAC DA
memcpy(arp.ethhdr.h_source, parp->tHaddr, 6); // MAC SA
arp.ethhdr.h_proto = htons(ETH_P_RARP); // protocol type (Ethernet)
arp.htype = htons(ARPHRD_ETHER); // hardware type
arp.ptype = htons(ETH_P_IP); // protocol type (ARP message)
arp.hlen = 6; // hardware address length
arp.plen = 4; // protocol address length
arp.operation = htons(4); // RARP reply code
memcpy(arp.sInaddr, if_info[i].ip, 4); // source IP address
memcpy(arp.sHaddr, parp->tHaddr, 6); // source hardware address
memcpy(arp.tInaddr, parp->sInaddr, 4); // target IP address
memcpy(arp.tHaddr, parp->sHaddr, 6);
if (sendto(s, &arp, sizeof(arp), 0, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
perror("Unabele to send arp request");
return 0;
}
else
printf("send reply\n");
}
}
}
close(s);
return 0;
}
//////////////////////////////////////////////////////////
//File Name : get_ip_by_mac.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct arpMsg {
struct ethhdr ethhdr; /* Ethernet header */
u_short htype; /* hardware type (must be ARPHRD_ETHER) */
u_short ptype; /* protocol type (must be ETH_P_IP) */
u_char hlen; /* hardware address length (must be 6) */
u_char plen; /* protocol address length (must be 4) */
u_short operation; /* ARP opcode */
u_char sHaddr[6]; /* sender's hardware address */
u_char sInaddr[4]; /* sender's IP address */
u_char tHaddr[6]; /* target's hardware address */
u_char tInaddr[4]; /* target's IP address */
u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
};
/* miscellaneous defines */
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
#define OPT_CODE 0
#define OPT_LEN 1
#define OPT_DATA 2
void print_mac(unsigned char * mac_addr)
{
for (int i =0; i < 6; ++i)
{
printf("%02X", mac_addr[i]);
if (i != 5) printf(":");
}
printf("\n");
}
void print_ip(unsigned char * ip_addr)
{
for (int i =0; i < 4; ++i)
{
printf("%d", ip_addr[i]);
if (i != 3) printf(".");
}
printf("\n");
}
void get_local_addr(unsigned char* mac, u_int32_t &ip)
{
struct ifconf interface_conf;
struct ifreq ifreq1;
int sock;
struct sockaddr_in* psockaddr_in = NULL;
if ( (sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Unable to create socket for geting the mac address");
exit(1);
}
strcpy(ifreq1.ifr_name, "eth0");
if (ioctl(sock, SIOCGIFHWADDR, &ifreq1) < 0)
{
perror("Unable to get the mac address");
exit(1);
}
memcpy(mac, ifreq1.ifr_hwaddr.sa_data, 6);
if (ioctl(sock, SIOCGIFADDR, &ifreq1) < 0)
{
perror("Unable to get the ip address");
exit(1);
}
psockaddr_in = (struct sockaddr_in*)&ifreq1.ifr_addr;
ip = psockaddr_in->sin_addr.s_addr;
//print_ip((unsigned char*)ip);
}
int main(int argc, char* argv[])
{
int timeout = 2;
int optval = 1;
int s; /* socket */
int rv = 1; /* return value */
struct sockaddr addr; /* for interface name */
struct arpMsg arp;
fd_set fdset;
struct timeval tm;
time_t prevTime;
u_int32_t ip;
struct in_addr my_ip;
struct in_addr dst_ip;
char buff[2000];
unsigned char mac[6];
unsigned char dmac[6];
char interface[] = "eth0";
if (argc != 2)
{
printf("Usage: get_ip_by_mac dst_mac\n");
printf("For example: get_ip_by_mac 00:0F:EA:40:0D:04\n");
return 0;
}
get_local_addr(mac, ip);
for (int i = 0; i < 6; ++i)
{
strncpy(buff, argv[1]+3*i, 2);
buff[3] = '\0';
dmac[i] = strtol(buff, (char**)NULL, 16);
}
if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_RARP))) == -1)
{
printf("Could not open raw socket\n");
return -1;
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
{
printf("Could not setsocketopt on raw socket\n");
close(s);
return -1;
}
memset(&addr, 0, sizeof(addr));
strcpy(addr.sa_data, interface);
/* send rarp request */
memset(&arp, 0, sizeof(arp));
memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */
arp.ethhdr.h_proto = htons(ETH_P_RARP); /* protocol type (Ethernet) */
arp.htype = htons(ARPHRD_ETHER); /* hardware type */
arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
arp.hlen = 6; /* hardware address length */
arp.plen = 4; /* protocol address length */
arp.operation = htons(3); /* RARP request code */
*((u_int *) arp.sInaddr) = ip; /* source IP address */
memcpy(arp.sHaddr, mac, 6); /* source hardware address */
memcpy(arp.tHaddr, dmac, 6);
if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
{
perror("Unabele to send arp request");
return 0;
}
rv = 0;
/* wait arp reply, and check it */
tm.tv_usec = 0;
time(&prevTime);
while (timeout > 0)
{
FD_ZERO(&fdset);
FD_SET(s, &fdset);
tm.tv_sec = timeout;
if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0)
{
printf("Error on ARPING request:");
if (errno != EINTR) rv = 0;
}
else if (FD_ISSET(s, &fdset))
{
if (recv(s, &arp, sizeof(arp), 0) < 0 )
{
perror("Unable get valid rarp response");
rv = 0;
}
if (arp.operation == htons(4) &&
bcmp(arp.tHaddr, mac, 6) == 0 )
{
printf("Valid rarp reply receved for this address\n");
//print_mac(arp.sHaddr);
print_ip(arp.sInaddr);
rv = 0;
break;
}
}
timeout -= time(NULL) - prevTime;
time(&prevTime);
}
close(s);
return 0;
}