6
votes

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.

3

3 Answers

3
votes

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.

1
votes

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

-1
votes

I don't have a good answer but in Linux after you bind the address as show you can use write. so why do you want to use sendto() ?

first off to send raw packets in Linux (I don't know your RTOS but you should look for the missing defines in the include files provided by that OS) you'll need to be root on your system or use the setcap command to add raw capabilities for example:

sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /path/to/your/executable

then in you program:

#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>

int main()
{
    char* ifname = "eth0";
    unsigned char srcMac[6];
    unsigned char dstMac[6] = {0, 0xDE, 0xAD, 0xBE, 0xEF, 0};
    int fd = socket( PF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );

     /* bind to interface */
    struct ifreq req;
    memset( &req, 0, sizeof( req ) );
    strcpy( (char*)req.ifr_name, (char*)ifname );

    if ( ioctl( fd, SIOCGIFINDEX, &req ) < 0 )
    {
        perror( "init: ioctl" );
        close( fd );
        return -1;
    }

    struct sockaddr_ll addr;
    memset( &addr, 0, sizeof( addr ) );
    addr.sll_family   = PF_PACKET;
    addr.sll_protocol = 0;
    addr.sll_ifindex  = req.ifr_ifindex;

    if ( bind( fd, (const struct sockaddr*)&addr, sizeof( addr ) ) < 0 )
    {
        perror( "init: bind fails" );
        close( fd );
        return -1;
    }

    if ( ioctl( fd, SIOCGIFHWADDR, &req ) < 0 )
    {
        perror( "init: ioctl SIOCGIFHWADDR" );
        close( fd );
        return -1;
    }

    /* store your mac address somewhere you'll need it! in your packet */
    memcpy( srcMac, (unsigned char*)req.ifr_hwaddr.sa_data, ETH_ALEN );

    int length = 60;
    unsigned char txPkt[ length ];
    unsigned char* pkt = txPkt;
    memset( txPkt, 0, sizeof( txPkt ) );

    //destination mac address
    memcpy( pkt, dstMac, 6 );
    pkt += 6;

    // source mac address
    memcpy( pkt, srcMac, 6 );
    pkt += 6;

    // add more data, like a type field!  etc

    int bytes = write( fd, txPkt, length );
}

As you write a raw packet you'll have to put in the mac addresses etc. yourself. The FCS can be left out(should be handled by hw) so the minimal packet size is 60 bytes, you must send 60 bytes I believe. If you capture on the outgoing card you won't see the FCS.

I suppose you should be able to use the sendto() function in stead of write but note that the struct sockaddr_ll does not contain any destination information, so why bother?