转自:http://blog.csdn.net/crazyleen/article/details/7014978
一: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
上面的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获取设备信息。
- struct ifreq ifr;
- strncpy(ifr.ifr_name, name, 6);
- if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
- perror("get_hwaddr ioctl:");
- close(sock);
- return -1;
- }
以下是一个实现:
- /**
- * get_hwaddr - get netdevice mac addr
- * @name: device name, e.g: eth0
- * @hwaddr: where to save mac, 6 byte hwaddr[6]
- * @return: 0 on success, -1 on failure
- */
- 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,用于发送自定义数据报:
- 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地址。
发送数据包:
- 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);
以下是一个简单的实现,运行两个实例,一个带参数,用于发送数据包。一个不带参数,用于接收数据包。完整的源码在最下面:
在wireshark可以看到,发送的数据包就是原始的网络数据包。
完整的源码:
- /*
- * socket.c
- *
- * Copyright (C) 2011 crazyleen <ruishenglin@126.com>
- *
- */
- #include <sys/socket.h>
- #include <string.h>
- #include <sys/types.h>
- #include <arpa/inet.h>
- #include <features.h> /* for the glibc version number */
- #if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
- #include <netpacket/packet.h>
- #include <net/ethernet.h> /* the L2 protocols */
- #else
- #include <asm/types.h>
- #include <linux/if_packet.h>
- #include <linux/if_ether.h> /* The L2 protocols */
- #endif
- #include <stdio.h>
- #include <netinet/in.h>
- #include <net/if.h>
- #include <sys/ioctl.h>
- #include <errno.h>
- #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 == ':') { /* could be an alias */
- char *dot = p, *dotname = name;
- *name++ = *p++;
- while (isdigit(*p))
- *name++ = *p++;
- if (*p != ':') { /* it wasn't, backup */
- p = dot;
- name = dotname;
- }
- if (*p == '\0')
- return NULL;
- p++;
- break;
- }
- *name++ = *p++;
- }
- *name++ = '\0';
- return p;
- }
- /**
- * read_netdev_proc - read net dev names form proc/net/dev
- * @devname: where to store dev names, devname[num][len]
- */
- 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); /* eat two line */
- 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;
- }
- /**
- * get_hwaddr - get netdevice mac addr
- * @name: device name, e.g: eth0
- * @hwaddr: where to save mac, 6 byte hwaddr[6]
- * @return: 0 on success, -1 on failure
- */
- 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;
- }