[genius_platform software platform development] lecture 37: network card hybrid mode and original socket

Hybrid mode of network card under Linux

  • Hybrid mode is to receive all packets passing through the network card, including packets not sent to the local machine, that is, the MAC address is not verified. In normal mode, the network card only receives packets sent to the local computer (including broadcast packets) and passes them to the upper layer program, and all other packets are discarded. Generally speaking, the hybrid mode will not affect the normal operation of the network card. It is mostly used on network monitoring tools.

The network card has the following working modes:

  1. Broad Cast Model: the frame whose physical address (MAC) is 0Xffffff is a broadcast frame, and the network card working in the broadcast mode receives the broadcast frame.
  2. MultiCast Model: the frame with multicast transmission address as the destination physical address can be received by other hosts in the group at the same time, but not by hosts outside the group. However, if the network card is set to multicast transmission mode, it can receive all multicast transmission frames, whether it is a member of the group or not.
  3. Direct Model: the network card working in direct mode only receives frames whose destination address is its own Mac address.
  4. Promiscuous Model: the network card working in promiscuous mode receives all frames flowing through the network card, and the packet capture program runs in this mode.

The default working mode of the network card includes broadcast mode and direct mode, that is, it only receives broadcast frames and frames sent to itself. If the hybrid mode is adopted, the network card of a site will accept the data packets sent by all sites in the same network, so as to achieve the purpose of monitoring and capturing network information.

Tips:

  • When tcpdump is used to capture packets, the network card will enter the hybrid mode, which can be seen in / var/log/messages
    As shown in the following figure, after tcpdump packet capturing is enabled, the system log can be seen in the new window clone session tail -f /var/log/messages
kernel: device eth0 entered promiscuous mode

  • In fact, whether the network card is in hybrid mode cannot be judged according to whether there is a PROMISC field in ifconfig. For example, when tcpdump packet capture is enabled, there is no PROMISC field in ifconfig. In fact, ifconfig is not the most direct basis for judging whether the network card is in the PROMISC mode. In other words, if ifconfig can see the PROMISC mark, it means that it must be in the hybrid mode, but if it is in the hybrid mode, it does not necessarily see the PROMISC mark. The kernel judges whether the network card is in hybrid mode by the value of / sys/class/net/eth0/flags. If 0x100 is set, it is in hybrid mode

    [root@CentOS_DIY ~]# cat /usr/include/linux/if.h | grep -i promisc
#define IFF_PROMISC 0x100 /* receive all packets */


Summary:

  1. The default working mode of the network card includes broadcast mode and direct mode.
  2. Promiscuous patterns do not necessarily see PROMISC markers. The kernel determines whether the network card is in hybrid mode by looking at the value of / sys/class/net/eth0/flags.
  3. ifconfig can see that the promise flag indicates that it must be in hybrid mode.
  4. When tcpdump packet capture is enabled, ifconfig does not have the PROMISC field, and it will automatically go to the kernel to set the hybrid mode.
  5. Generally speaking, the hybrid mode will not affect the normal operation of the network card. It is mostly used on network monitoring tools.

Raw socket programming

  • Similar to UDP programming, it is to create a socket and receive or send data through the socket.
    The difference is that the original socket can assemble data packets (masquerading as local IP and local MAC) and receive all data frames (data packets) on the local network card.
    In addition, you must have administrator privileges to use the original socket.

  • Creation of raw socket

int socket ( int family, int type, int protocol );
  • Parameters:
family: The protocol family is written here PF_PACKET

type:   Socket class, written here SOCK_RAW

protocol: Protocol category, which specifies the packet type that can be received or sent. It cannot write "0". The values are as follows. Note that it needs to be used when transmitting parameters htons() Byte order conversion.

ETH_P_IP: IPV4 data packet

ETH_P_ARP: ARP data packet

ETH_P_ALL: Packets of any protocol type
  • Return value:
success( >0 ): Socket, here is the socket of link layer

fail( <0 ): error
  • Examples are as follows:
// Required header file
#include <sys/socket.h>
#include <netinet/ether.h>
#include <stdio.h>  // perror
 
int main(int argc,char *argv[])
{
	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
 
	if(sock_raw_fd < 0){
		perror("socket");
		return -1;
	}
	
	return 0;
}
  • SIOCGIFFLAGS, SIOCSIFFLAGS

  • Read or set the activity flag word of the device. ifr_flags contains masked bits for the following values:
Equipment mark
IFF_UP	The interface is running.
IFF_BROADCAST	Valid broadcast address set.
IFF_DEBUG	Internal commissioning flag.
IFF_LOOPBACK	This is a self ring interface.
IFF_POINTOPOINT	This is a point-to-point link interface.
IFF_RUNNING	Resources allocated.
IFF_NOARP	nothing arp agreement, Layer 2 destination address is not set.
IFF_PROMISC	Interface is hash(promiscuous)pattern.
IFF_NOTRAILERS	Avoid using trailer .
IFF_ALLMULTI	Receive all multicast(multicast)message.
IFF_MASTER	Main load balancing group(bundle).
IFF_SLAVE	From load balancing group(bundle).
IFF_MULTICAST	Support multicast(multicast).
IFF_PORTSEL	Can pass ifmap Select media(media)type.
IFF_AUTOMEDIA	Auto select media.
IFF_DYNAMIC	Drop address when interface is closed.
  • Obtain data packets of link layer
ssize_t recvfrom(  int sockfd,  void *buf,  size_t nbytes, int flags, struct sockaddr *from,  socklen_t *addrlen );
  • Parameters:
sockfd:Raw Socket 

buf: Receive data buffer

nbytes:Size of receive data buffer

flags: Socket flag(Usually 0)

from: It's no use writing here NULL

addrlen: It's no use writing here NULL
  • Return value:
Success: number of characters received

Failed:-1
  • Examples are as follows:
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ether.h>
 
int main(int argc,char *argv[])
{
	unsigned char buf[1024] = {0};
	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
 
	//Obtain data packets of link layer
	int len = recvfrom(sock_raw_fd, buf, sizeof(buf), 0, NULL, NULL);
	printf("len = %d\n", len);
 
	return 0;
}

promiscuous mode

  • By default, we receive data only when the destination address is a local address. Sometimes we want to receive all data streams passing through the network card, regardless of whether the destination address is it or not. At this time, we need to set the network card to hybrid mode.

  • The hybrid mode of network card is generally used by network administrators when analyzing network data as a means of network fault diagnosis. At the same time, this mode is also used by network hackers as an entrance for network data eavesdropping. Administrator privileges are required when setting the network card hybrid mode in the Linux operating system. In both Windows operating system and Linux operating system, there are packet capture tools using hybrid mode, such as the famous open source software Wireshark.

Set the mixed mode for the Linux network card through the command (administrator permission is required)

  • Set hybrid mode: ifconfig eth0 promisc

  • Cancel hybrid mode: ifconfig eth0 - promise

  • Set hybrid mode for Linux network card through code

  • The code is as follows:
//
// Set network card hybrid mode
int CGpIpTools::rawSoketInit()
{
    int sockfd;         			// Original socket
    struct ifreq ifr;  				// Network interface address
    struct sockaddr_ll local_addr;
    /* Create original socket */
    if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
    {
        LOGERROR("socket fail\n");
        return -1;
    }

    /* Set to hybrid mode */
    strcpy(ifr.ifr_name, "eth0");  				// Specify the network card name
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)  // Get network interface
    {
        LOGERROR("ioctl fail\n");
        close(sockfd);
        return -1;
    }
    ifr.ifr_flags |= IFF_PROMISC;				// Network card setting hybrid mode
    if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
    {
        LOGERROR("ioctl fail\n");
        close(sockfd);
        return -1;
    }
    /* Get physical network card interface index */
    strcpy(ifr.ifr_name, "eth0");
    if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0)
    {
        LOGERROR("ioctl fail\n");
        close(sockfd);
        return -1;
    }
    /* Bind physical network card */
    local_addr.sll_family = PF_PACKET;
    local_addr.sll_ifindex = ifr.ifr_ifindex;
    local_addr.sll_protocol = htons(ETH_P_ALL);
    if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
    {
        LOGERROR("bind fail\n");
        close(sockfd);
        return -1;
    }

    return sockfd;
}
  • Send custom packets:
ssize_t sendto(   int sockfd, const void *buf, size_t nbytes,int flags, const struct sockaddr *to, socklen_t addrlen);
  • Parameters:
sockfd: Raw Socket 

buf: Send data buffer

nbytes:Size of send data buffer

flags: Generally 0

to: The local network interface refers to the network card of the local machine from which the data should be sent, rather than the previous destination address

addrlen: to Length of content pointed to
  • Return value:
Success: number of characters to send data

Failed: -1
  • Definition of local network interface

  • Send the complete code as follows:

#include <net/if.h>// struct ifreq
#include <sys/ioctl.h> // ioctl,SIOCGIFADDR
#include <sys/socket.h> // socket
#include <netinet/ether.h> // ETH_P_ALL
#include <netpacket/packet.h> // struct sockaddr_ll

struct sockaddr_ll sll;								// Raw socket address structure
struct ifreq req;									// Network interface address
 
strncpy(req.ifr_name, "eth0", IFNAMSIZ);			// Specify the network card name
if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))	// Get network interface
{
	perror("ioctl");
	close(sock_raw_fd);
	exit(-1);
}
 
/*Assign the network interface to the original socket address structure*/
bzero(&sll, sizeof(sll));
sll.sll_ifindex = req.ifr_ifindex;
 
// send data
// send_msg, msg_len is not defined here. Let's simulate it
int len = sendto(sock_raw_fd, send_msg, msg_len, 0 , (struct sockaddr *)&sll, sizeof(sll));
if(len == -1)
{
	perror("sendto");
}

/*udp can also be used for sending and receiving*/
//
// Initialize udp socket
int CGpIpTools::localSocketInit()
{
    int sockFd = 0;
    struct sockaddr_in sockAddr;
    if ((sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    {
        LOGERROR("socket error.\n");
        return -1;
    }

    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    sockAddr.sin_port = htons(5566);

    if (bind(sockFd, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) < 0)
    {
        LOGERROR("bind error.\n");
        return -1;
    }

    int val = 1;
    setsockopt(sockFd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
    setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

    return sockFd;
}

//
// Main function entry
int main(int argc, char **argv)
{
    int recvLen;
    int dwCrc;

    int rawSockFd = 0, localSockFd = 0;
    struct sockaddr_ll remoteAddr;
    struct sockaddr_in clientAddr;

    int addrLen = sizeof(remoteAddr);
    char recvBuf[1024] = { 0 };
    char sendBuf[1024] = { 0 };
    back_up_net_interface();
    rawSockFd = rawSoketInit();
    localSockFd = localSocketInit();

    if ((rawSockFd < 0) || (localSockFd < 0))
    {
        printf("create socket error.\n");
        return -1;
    }

    clientAddr.sin_family = AF_INET;
    clientAddr.sin_addr.s_addr = INADDR_BROADCAST;
    clientAddr.sin_port = htons(8000);

    pid_t pid = getpid();
    struct sched_param param;
    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
    sched_setscheduler(pid, SCHED_FIFO, &param);
    while (1)
    {
        bzero(recvBuf, sizeof(recvBuf));
        recvLen = recvfrom(localSockFd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&remoteAddr, &addrLen);
        DataHead *pHead = (DataHead *)recvBuf;
        //printf("magic:0x%x\n",ntohl(pHead->mMgic));

        if (MAGIC == pHead->mMgic)
        {
            printf("data len%d\n", pHead->mLen);
            bzero(sendBuf, sizeof(sendBuf));
            parseCmd(pHead->mPackType, recvBuf, sendBuf);

            if (((DataHead *)sendBuf)->mPackType == ACK_INVALID_CMD)
                continue;

            int ret = sendto(localSockFd, (char *)sendBuf, ((DataHead *)sendBuf)->mLen, 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));
            //perror("send error.\n");
            printf("data len:%d,send len:%d\n", ((DataHead *)sendBuf)->mLen, ret);
        }

        usleep(1000 * 20);
    }

    close(rawSockFd);
    close(localSockFd);

    return 0;
}

Posted on Thu, 04 Nov 2021 02:22:45 -0400 by ktsirig