Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send data over a raw ethernet socket using sendto without using sockaddr_ll?

Tags:

c

sockets

ose

I am working on a project where we use Enea OSE which is a real time embedded operating system. I guess many of you are not familiar with this OS but I think you could answer my question anyway. I want to send a raw ethernet frame between 2 cards using interface "eth1" which are directly connected to oneanother by an ethernet cable (no switch or anything). The "eth1" interface is not assigned any ip-address.

I can simply declare a socket using

int s = socket(AF_INET, RAW_SOCKET, ...)

I can then bind the socket to the appropriate device interface using:

setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "eth1", 4);

What my plan was to do, in order to send data from one card to the other, was to manually fill in a ethernet buffer array with the source and destination MAC adresses. Then send the data using sendto(....)

However the sendto routine also requires a sockaddr struct with information:

  sendto(int sockfd, const void *buf, size_t len, int flags,
           const struct sockaddr *dest_addr, socklen_t addrlen);

In the examples I have found on the internet uses sockaddr_in when the ip adress is known and sockaddr_ll for raw frames. However OSE does not provide the sockaddr_ll struct. However it provides the sockaddr which I dont know if I can use for raw ethernet frames?

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
};

I also tried to use NULL, for the sockaddr field when calling sendto(), the send fails. This brings me to my questions(s). Can I use the simple sockaddr struct to fill in the destination MAC-adress without using any IP? How would that look? (I have not seeen any code showing this). Why do we even have to provide a sockaddr struct? Doesn't the ethernet data buffer already contain this information? Finally, is what I am trying to accomplish even possible without assigning ip-addresses for the network devices?

I am aware of what I am trying to do seems peculiar but there is a reason for it :) I would be really glad if you would show me how to accomplish this without having to use the sockaddr_ll struct.

like image 889
Euklides Avatar asked Jan 28 '14 16:01

Euklides


2 Answers

Sorry about the previous vague answer. Here is my full answer. Hope its helpful.

To answer your question: Yes, you can send using just the MAC Address.

Declare the socket like this:

int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)

On error, sockfd which is the socket file descriptor will return -1.

The struct for the Ethernet frame can be constructed like this:

struct ethernet_msg{
        char destination_mac[6];
        char source_mac[6];
        char transport_protocol[2];
        char data1[6];
        char data2[6];
        char data3[8];
        char data4[8];
        char data5[8];
        char data6[2];
        char data7[2];
        char data8[6];
        char data9[4];
        char data10[6];
};
struct ethernet_msg my_msg = {
        {0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00},//destination_mac
        {0x08, 0x00, 0x27, 0x90, 0x5f, 0xae},//source_mac
        {0x22, 0xf0},//transport_protocol
        {0xfc, 0x06, 0x00, 0x2c, 0x00, 0x00},//data1
        {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//data2
        {0x08, 0x00, 0x27, 0xff, 0xfe, 0x90, 0x5f, 0xae},//data3
        {0xd8, 0x80, 0x39, 0xff, 0xfe, 0xd0, 0xac, 0xb5},//data4
        {0xd8, 0x80, 0x39, 0xff, 0xfe, 0xd0, 0x9b, 0xc8},//data5
        {0x00, 0x00},//data6
        {0x00, 0x00},//data7
        {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//data8
        {0x00, 0x00, 0x00, 0x5f},//data9
        {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}//data10
};

Note how I put char arrays of different lengths inside the frame. You can set this according to your needs. To get the MAC address of the interface to send on:

void get_interface_MAC(char interface_name[IFNAMSIZ], int sockfd)
{
        struct ifreq if_mac;
        memset(&if_mac, 0, sizeof(struct ifreq));
        strncpy(if_mac.ifr_name, interface_name, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
                perror("SIOCGIFHWADDR");
}

Then to get the index of the network device:

/* Get the index of the interface to send on */
int get_interface_index(char interface_name[IFNAMSIZ], int sockfd)
{
        struct ifreq if_idx;
        memset(&if_idx, 0, sizeof(struct ifreq));
        strncpy(if_idx.ifr_name, interface_name, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
                perror("SIOCGIFINDEX");

        return if_idx.ifr_ifindex;
}

socket_address.sll_ifindex = get_interface_index(interface_name, sockfd);
/* Address length*/
socket_address.sll_halen = ETH_ALEN;
/* Destination MAC */
memcpy(socket_address.sll_addr, avdecc_msg.destination_mac, ETH_ALEN);

And then to finally send this over a socket:

/* Send packet */
if (sendto(sockfd, &avdecc_msg, sizeof(avdecc_msg), 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
                printf("Send failed\n");

Hope that helps. I used this link and refined the code to get to this. It worked for me.

like image 97
Algorithmic Avatar answered Oct 14 '22 05:10

Algorithmic


As far as I am aware Enea OSE comes with these headers

#include <sys/socket.h>
#include <netpacket/packet.h>

The latter declares struct sockaddr_ll which should allow you to send ethernet packets to a socket of type

int sockfd = socket(AF_PACKET, type, protocol);

Type can be SOCK_DGRAM or SOCK_RAW (you will need to create the ethernet header yourself).

I could not find the right header for the ethernet protocols. You will need to search your headers for ETH_* defines and find the right ethernet header

like image 35
Sergey L. Avatar answered Oct 14 '22 04:10

Sergey L.