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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With