1.二层是指算机网络七层模型中的二层,及链路层。
2.以太帧格式如下:
Ethernet II类型以太网帧的最小长度为64字节(6+6+2+46+4),最大长度为1518字节(6+6+2+1500+4)。其中前12字节分别标识出发送数据帧的源节点MAC地址和接收数据帧的目标节点MAC地址。(注:ISL封装后可达1548字节,802.1Q封装后可达1522字节)
3.注意项:1.如果是广播,目的mac需要为全f。2.客户端和服务端不能同时绑定到同一个网卡上。3.如果客户端和服务端是经过交换器获取其他网卡转接的,需要注意是否会隔离数据传输。
4.demo代码如下:
头文件如下:
server.h
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h> // close()
#include <string.h> // strcpy, memset(), and memcpy()
#include <netdb.h> // struct addrinfo
#include <sys/types.h> // needed for socket(), uint8_t, uint16_t, uint32_t
#include <sys/socket.h> // needed for socket()
#include <netinet/in.h> // IPPROTO_TCP, INET_ADDRSTRLEN
#include <netinet/ip.h> // struct ip and IP_MAXPACKET (which is 65535)
#include <netinet/tcp.h> // struct tcphdr
#include <arpa/inet.h> // inet_pton() and inet_ntop()
#include <sys/ioctl.h> // macro ioctl is defined
#include <bits/ioctls.h> // defines values for argument "request" of ioctl.
#include <net/if.h> // struct ifreq
#include <linux/if_ether.h> // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD
#include <linux/if_packet.h> // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>
#include <linux/types.h>
#include <errno.h>
#include <sys/signal.h>
#define ETH_ALEN 6
#define ETH_ALEN 6
#define MD5_LEN 4
#define device_id_len 8
#define TRUE 1
#define FALSE 0
#define BIND_IF_NAME "eth1"
__u8 stbMac[ETH_ALEN];
#define PRO_MAX_SIZE 92
#define SERVER_SOCKET_NAME "server_socket"
#define ETH_P_DEBUG 0x9979
#define PACKET_LEN(a) (sizeof(a) - sizeof(a.stMsgBody.payload) + ntohs(a.stMsgBody.length))
typedef char __S8;
typedef short __S16;
typedef int __S32;
typedef unsigned char __U8;
typedef unsigned short __U16;
typedef unsigned int __U32;
typedef unsigned long long __U64;
typedef void __VOID;
/**消息包头*/
struct eth_header{
__S8 ether_dhost[6]; /*6*/
__S8 ether_shost[6]; /*6*/
__U16 ether_type; /*2*/
__U8 md5[4]; /**md5校验4*/
} __attribute__((packed));
/**消息体*/
typedef struct
{
__U16 action; /** PRIV_PACKET_TYPE 2*/
__U32 session; /** PPPoE session 4*/
__U8 sad[8]; /** mac-uuid(3+5) */
__U16 length; /** Payload length 2*/
__S8 payload[1480]; /* A bit of room to spare */
}__attribute__((packed))MESSAGE_BODY_ST;
/**packet = 1514bit*/
typedef struct PrivPacketStruct {
struct eth_header ethHdr; /** Ethernet header 18*/
MESSAGE_BODY_ST stMsgBody; /** Ethernet body 1486*/
}__attribute__((packed))PrivPacket;
服务端代码如下:
#include "server.h"
static __S32 ug_open_socket(__U16 u16Type, __S8 *ps8Ifname)
{
__S32 fd = socket(PF_PACKET,SOCK_RAW,htons(u16Type));
if (fd < 0) {
printf("###### open socket fail");
return -1;
}
__S32 optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) {
printf(" ###### setsockopt fail");
close (fd);
return -1;
}
__S32 nRecvBuf=64*1024; // 2KB
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,(const char*)&nRecvBuf, sizeof(__S32)) < 0) {
printf(" ###### setsockopt fail");
close (fd);
return -1;
}
struct sockaddr_ll sa;
memset(&sa,0,sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(u16Type);
sa.sll_ifindex = if_nametoindex(ps8Ifname);
if(bind(fd,(struct sockaddr *)&sa,sizeof(sa)) < 0) {
printf(" ###### bind fail");
close (fd);
return -1;
}
return fd;
}
int main(int argc, char const *argv[])
{
int fd = -1;
int s32Len =-1;
PrivPacket packet;
char mac_buff[ETH_ALEN] = {0}; /**mac buff*/
fd = ug_open_socket(ETH_P_DEBUG, BIND_IF_NAME);
if(fd < 0){
printf("fd create fail: %d",fd);
return -1;
}
printf("server: read data from client ... \n");
while (1)
{
memset(&packet,0,sizeof(packet));
s32Len = read(fd,&packet,sizeof(packet));
if (s32Len<=0) {
continue;
}
printf("read s32Len: %d, action: %d\n",s32Len,ntohs(packet.stMsgBody.action));
}
return 0;
}
客户端代码如下:
#include "server.h"
int getGwMac(char *ifname,char *gwMac)
{
struct ifreq ifr;
int sd;
printf("--------------\n");
printf("ifname: %s\n",ifname);
if ((sd = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {//第一次创建socket是为了获取本地网卡信息
printf("socket() failed to get socket descriptor for using ioctl()\n");
printf("sd: %d\n",sd);
return -1;
}
// Use ioctl() to look up interface name and get its MAC address.
memset (&ifr, 0, sizeof (ifr));
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", ifname);
if (ioctl (sd, SIOCGIFHWADDR, &ifr) < 0) {
printf("ioctl() failed to get source MAC address\n");
close (sd);
return -1;
}
// Copy source MAC address.
memcpy (gwMac, ifr.ifr_hwaddr.sa_data, ETH_ALEN * sizeof (__u8));
printf("\n ###### get gwMac = %02X:%02X:%02X:%02X:%02X:%02X \n", gwMac[0], gwMac[1], gwMac[2], gwMac[3], gwMac[4], gwMac[5]);
close (sd);
return 0;
}
int set_packet_head(PrivPacket *packet,const __u8 *src_hwaddr, const __u8 *dst_hwaddr,int len)
{
unsigned char md5_buf[4];
memset(md5_buf,0,sizeof(md5_buf));
if (NULL == src_hwaddr) {
printf("pstMsgBody fail\n");
return -1;
}
memcpy(packet->ethHdr.ether_shost, src_hwaddr, ETH_ALEN);
memcpy(packet->ethHdr.ether_dhost, dst_hwaddr, ETH_ALEN);
packet->ethHdr.ether_type = htons(ETH_P_DEBUG);
/**计算出消息体的md5*/
memcpy(packet->ethHdr.md5,md5_buf,4);
return 0;
}
/**发上报设备信息请求*/
void send_device_info(PrivPacket *packet)
{
packet->stMsgBody.action = htons(0x0101);
packet->stMsgBody.session = htonl(1);
strcpy(packet->stMsgBody.sad,"NULL");
packet->stMsgBody.length = htons(0);
return ;
}
int main(int argc, char const *argv[])
{
PrivPacket packet;
char mac_buff[ETH_ALEN] = {0}; /**mac buff*/
int ret = getGwMac(BIND_IF_NAME,mac_buff);
if (ret < 0) {
printf("getGwMac fail: %d",ret);
return ret;
}
printf("getGwMac ok!\n");
//创建socket
__S32 serverFD = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_DEBUG));
printf("client: create socket serverFD=%d\n", serverFD);
if (serverFD < 0)
{
printf("client: error: create socket failed ... \n");
return -1;
}
int optval = 1;
if (setsockopt(serverFD, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0)
{
printf("client: error: setsockopt SO_KEEPALIVE failed ... \n");
close(serverFD);
return -1;
}
int nRecvBuf = 2 * 1024; // 2KB
if (setsockopt(serverFD, SOL_SOCKET, SO_RCVBUF, (const char *)&nRecvBuf, sizeof(int)) < 0)
{
printf("client: error: setsockopt SO_RCVBUF failed ... \n");
close(serverFD);
return -1;
}
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ETH_P_DEBUG);
sa.sll_ifindex = if_nametoindex(BIND_IF_NAME);
if (bind(serverFD, (struct sockaddr *)&sa, sizeof(sa)) < 0)
{
printf("client: error: bind failed ... \n");
close(serverFD);
return -1;
}
int writeRet = 0;
memset(&packet,0,sizeof(packet));
while (1)
{
int n = 0;
printf("enter select: \n");
scanf("%d",&n);
memset(&packet,0,sizeof(packet));
int len = 0;
switch(n)
{
case 1:
send_device_info(&packet);
len = PACKET_LEN(packet) - 18;
printf("PACKET_LEN(packet): %d,len: %d\n",PACKET_LEN(packet),len);
set_packet_head(&packet,mac_buff,stbMac,len);
ret = write(serverFD,&packet,PACKET_LEN(packet));
if (ret <= 0) {
printf("\n ######## write ret= %d\n",ret);
break;
}
printf("\n ######## write ret= %d\n",ret);
break;
}
}
return 0;
}