一:linux下的网络设备
linux的网络设备信息都在/proc/net/dev,从这里我们可以得到所有网卡的名字,如eth0, eth1等等
root@dlrc-desktop:/home/dlrc/dlsp-ep9302/work/mystar-v0.4# cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop
lo: 472252 1696 0 0 0 0 0 0 472252 1696 0 0 0 0 0 0
eth1:20826443 20156 0 0 0 0 0 0 926357 14613 0 0 0 0 0 0
root@dlrc-desktop:/home/dlrc/dlsp-ep9302/work/mystar-v0.4# cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop
lo: 472252 1696 0 0 0 0 0 0 472252 1696 0 0 0 0 0 0
eth1:20826443 20156 0 0 0 0 0 0 926357 14613 0 0 0 0 0 0
上面的lo和eth1便是我的网卡名字。ifconfig就是读取/proc/net/dev这个文件来取得设备名列表的。
二:读取网卡mac地址
可以通过ioctl(sock,
SIOCGIFHWADDR,
&ifr)读取mac地址,对任意类型的socket都适用,只需指定第三参数struct ifreq
ifr的ifr.ifr_name, 这个ifr_name就是网络设备的名字,如eth0, eth1,
lo等,在/proc/net/dev可找到。ioctl通过ifr_name获取设备信息。
structifreq ifr;
strncpy(ifr.ifr_name, name, 6);
if(ioctl(sock, SIOCGIFHWADDR, &ifr)
perror("get_hwaddr ioctl:");
close(sock);
return-1;
}
struct ifreq ifr;
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
}
以下是一个实现:
intget_hwaddr(char*name, unsignedchar*hwaddr)
{
structifreq ifr;
unsignedcharmemzero[6];
intsock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return-1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0,sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if(ioctl(sock, SIOCGIFHWADDR, &ifr)
perror("get_hwaddr ioctl:");
close(sock);
return-1;
}else{
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return-1;
}
close(sock);
return0;
}
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
三:发送底层网络数据包
绕过TCP,UDP等传输协议,自己维护协议首部,发送原始数据包,在自定义的协议方面很方便。
SOCK_RAW提供了一个数据包接口直接访问底层网络。使用这个socket需要root权限。
创建
socket,用于发送自定义数据报:
intsock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock
perror("sock");
return-1;
}
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
第三个参数htons(ETH_P_ALL)只对recvfrom有意义。用这个socket发送的数据,都需要自己维护数据包协议首部,包括网络数据包中的mac地址。
发送数据包:
structsockaddr_ll sll;
memset(&sll, 0,sizeof(sll));
sll.sll_ifindex = 2;// 指定网卡
if(sendto(sock, packet_start,sizeofpacket_start, 0, &sll,sizeof(sll))
perror("sendto");
return1;
}
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = 2; // 指定网卡
if (sendto(sock, packet_start, sizeof packet_start, 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
sendto发送原始数据包,只需用struct
sockaddr_ll的sll_ifindex指定网卡。
接收这类的数据包:
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
以下是一个简单的实现,运行两个实例,一个带参数,用于发送数据包。一个不带参数,用于接收数据包。完整的源码在最下面:
在wireshark可以看到,发送的数据包就是原始的网络数据包。
完整的源码:
#include
#include
#include
#include
#include
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include
#include
#else
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#define _PATH_PROCNET_DEV "/proc/net/dev"
staticchar*get_name(char*name,char*p)
{
while(isspace(*p))
p++;
while(*p) {
if(isspace(*p))
break;
if(*p ==':') {
char*dot = p, *dotname = name;
*name++ = *p++;
while(isdigit(*p))
*name++ = *p++;
if(*p !=':') {
p = dot;
name = dotname;
}
if(*p =='\0')
returnNULL;
p++;
break;
}
*name++ = *p++;
}
*name++ ='\0';
returnp;
}
staticintread_netdev_proc(void*devname,constintnum,constintlen)
{
FILE*fh;
charbuf[512];
intcnt = 0;
char*dev = (char*)devname;
if(devname == NULL || num
printf("read_netdev_proc: para error\n");
return-1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV,"r");
if(!fh) {
fprintf(stderr,"Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return-1;
}
fgets(buf,sizeofbuf, fh);
fgets(buf,sizeofbuf, fh);
cnt = 0;
while(fgets(buf,sizeofbuf, fh) && cnt
char*s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if(ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return0;
}
intget_hwaddr(char*name, unsignedchar*hwaddr)
{
structifreq ifr;
unsignedcharmemzero[6];
intsock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return-1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0,sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if(ioctl(sock, SIOCGIFHWADDR, &ifr)
perror("get_hwaddr ioctl:");
close(sock);
return-1;
}else{
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return-1;
}
close(sock);
return0;
}
unsignedcharpacket_start[]={
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,//dst mac
0x00, 0x23, 0x54, 0x0e, 0xe5, 0xd8,//src mac
0x88, 0x8e,//Type: 802.1x authentication
0x01,//Version:v1
0x01,//Type: Start (1)
0x00, 0x00//Length 0
};
voidprinthex(void*hex,intlen,char*tag)
{
inti;
unsignedchar*p = (unsignedchar*)hex;
if(len
return;
for(i = 0; i
if(*p
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
intmain(intargc,char**argv)
{
inti;
unsignedcharhwaddr[6];
chardevname[3][7];
unsignedcharbuf[1024];// for revevied packet
intret;
read_netdev_proc(devname, 3, 7);
for(i = 0; i
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6,":");
intsock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock
perror("sock");
return-1;
}
structsockaddr_ll sll;
memset(&sll, 0,sizeof(sll));
sll.sll_ifindex = 2;// It seems only need this to specify which NIC to use
memcpy(packet_start + 6, hwaddr, 6);
//memcpy(packet_start, hwaddr, 6);
while(argc == 1){
if(sendto(sock, packet_start,sizeofpacket_start, 0, &sll,sizeof(sll))
perror("sendto");
return1;
}
printf("Sendto Success!\n");
sleep(1);
}
while(argc == 2){
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
printf("recv: ");
printhex(buf, ret," ");
}
return0;
}
#include
#include
#include
#include
#include
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include
#include
#else
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#define _PATH_PROCNET_DEV "/proc/net/dev"
static char *get_name(char *name, char *p)
{
while (isspace(*p))
p++;
while (*p) {
if (isspace(*p))
break;
if (*p == ':') {
char *dot = p, *dotname = name;
*name++ = *p++;
while (isdigit(*p))
*name++ = *p++;
if (*p != ':') {
p = dot;
name = dotname;
}
if (*p == '\0')
return NULL;
p++;
break;
}
*name++ = *p++;
}
*name++ = '\0';
return p;
}
static int read_netdev_proc(void *devname, const int num, const int len)
{
FILE *fh;
char buf[512];
int cnt = 0;
char *dev = (char *)devname;
if(devname == NULL || num < 1 || len < 4){
printf("read_netdev_proc: para error\n");
return -1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return -1;
}
fgets(buf, sizeof buf, fh);
fgets(buf, sizeof buf, fh);
cnt = 0;
while (fgets(buf, sizeof buf, fh) && cnt < num) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return 0;
}
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
unsigned char packet_start[]={
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,//dst mac
0x00, 0x23, 0x54, 0x0e, 0xe5, 0xd8,//src mac
0x88, 0x8e, //Type: 802.1x authentication
0x01, //Version:v1
0x01, //Type: Start (1)
0x00, 0x00//Length 0
};
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main(int argc, char **argv)
{
int i;
unsigned char hwaddr[6];
char devname[3][7];
unsigned char buf[1024]; // for revevied packet
int ret;
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = 2; // It seems only need this to specify which NIC to use
memcpy(packet_start + 6, hwaddr, 6);
//memcpy(packet_start, hwaddr, 6);
while(argc == 1){
if (sendto(sock, packet_start, sizeof packet_start, 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
printf("Sendto Success!\n");
sleep(1);
}
while(argc == 2){
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
printf("recv: ");
printhex(buf, ret, " ");
}
return 0;
}