1
votes

I am using Enea OSE and want to send a raw ethernet frame without any IP by opening a raw socket. Note that OSE uses the NetBSD stack which seems to be a little different from the Linux-stack.

The problem is that I havent found any good examples for doing this in OSE, however the same methods exists basically for opening a socket and sending (sendto) etc. However the *sockaddr_ll* struct which is used in Linux for sending raw ethernet frames does not exist in OSE. But I found another struct, *sockaddr_dl* which basically seems to have the same fields:

    struct sockaddr_dl
    {
       uint8_t sdl_len;
       sa_family_t sdl_family;     //AF_LINK
       uint16_t sdl_index;
       uint8_t sdl_type;
       uint8_t sdl_nlen;
       uint8_t sdl_alen;
       uint8_t sdl_slen;
       char sdl_data[12];
     }

I have found one article which discusses the differences between the Linux and the NetBSD stack, http://sock-raw.org/papers/sock_raw

I can open the socket without any error but the sendto() function fails with error value 22, Invalid Argument. What might the problem be? All threads I have read with this error ocurring people have not been careful when using sizeof at a pointer, but that is not what this is about I think. Ofcourse I am not certain that the sockaddr_dl struct is suited for raw sockets and perhaps it might be some problem there? Also perhaps the ethernet buffer is not filled in correctly? Also note that the socket used in Linux is opened the following way for this purpose:

int sock = socket(AF_PACKET, SOCK_RAW, ...) 

but this is not possible since the AF_PACKET is not included in OSE. From the article above it seems that a raw socket should be opened as shown in my code below.

int sockfd;
char sendbuf[FRAME_SIZE];
int tx_len = FRAME_SIZE;

/* Open RAW socket to send on */
if ((sockfd = socket(AF_INET, SOCK_RAW, 0)) == -1) {
    printf("Error when opening socket: %s\n", strerror(errno));
}

/*Fill sockaddr_dl struct (Link - Layer)*/
unsigned char src_mac[6] = {0x00, 0x80, 0x81, 0x82, 0x83, 0x84};
unsigned char dst_mac[6] = {0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e};
struct sockaddr_dl sock_test;
memset(&sock_test, 0, sizeof (struct sockaddr_dl));
sock_test.sdl_family = AF_LINK;
sock_test.sdl_len = sizeof (struct sockaddr_dl);
sock_test.sdl_index = 0;
sock_test.sdl_type = IFT_ETHER;
sock_test.sdl_alen = ETH_LEN;      //ETHER_ADDR_LEN = 6;
sock_test.sdl_data[0] = dst_mac[0];
sock_test.sdl_data[1] = dst_mac[1];
sock_test.sdl_data[2] = dst_mac[2];
sock_test.sdl_data[3] = dst_mac[3];
sock_test.sdl_data[4] = dst_mac[4];
sock_test.sdl_data[5] = dst_mac[5];

/*Fill in ethernet frame*/
unsigned char ether_type[2] = {0x08, 0x00};
memset((void*)sendbuf, 0, FRAME_SIZE); //Clear buffer
memcpy((void*)sendbuf, (void*)dst_mac, ETH_LEN);
memcpy((void*) (sendbuf+ETH_LEN), (void*)src_mac, ETH_LEN);
memcpy((void*) (sendbuf+2*ETH_LEN), (void*)ether_type, 2);

int status = sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr*)&sock_test,  (socklen_t)sizeof(sock_test));
if(status  < 0){
    printf("Error: %s      ::::  with value %d\n", strerror(errno), errno);
}
else
    printf("SENT DONE!!! \n");

Any suggestions are mostly welcome!

1
memcpy((void*)sendbuf, 0, FRAME_SIZE); //Clear buffer, should be a memset as memcpy will treat the 0 as a const void* for its source which, in this case, you are copying the memory of a temp stack variable (when your intention is to simply 0 a buffer).txtechhelp
memcpy((void*) (sendbuf+2*ETH_LEN), (void*)ether_type, 2); the (sendbuf+2*ETH_LEN) part ... keep operator precedence in mind on this too as it's actually being evaluated to (sendbuf+(2*ETH_LEN)) since multiplication has higher precedence than additiontxtechhelp
Thats true, it should be memset, however that does not seem to solve the problem. However the (sendbuf+2*ETH_LEN) is really what I want. I want to set the 2 bytes following the MAC-adress fields. That is byte 12 and 13.Euklides
Are you trying to construct your packets because AF_PACKET is not available? Have you tried the PF_PACKET variant?txtechhelp
These, among many, are the examples I have looked at. As was stated in the question, this is a NetBSD stack which differs from the Linux-stack. That was the whole point of this question. For example, the sockaddr_ll struct which is used in the examples are not available.Euklides

1 Answers

0
votes

Linux AF_PACKET code is ported to BSD bpf like:

Replace

int sock = socket(AF_PACKET, SOCK_RAW, protocol);

with

int sock = open(filename, O_RDWR);

with filename starting with "/dev/bpf0"

Replace ioctls with

ioctl(sock, BIOCSETIF, ...) # bind interface
ioctl(sock, BIOCIMMEDIATE, ...) # enable "immediate mode"
ioctl(sock, BIOCSETF, ...) # set bpf program

with bpf program like:

struct bpf_insn bf_insn[] = {
  /* Make sure this is a protocol packet... */
  BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, protocol, 0, 1),
  /* If we passed all the tests, ask for the whole packet. */
  BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
  /* Otherwise, drop it. */
  BPF_STMT(BPF_RET+BPF_K, 0),
};
struct bpf_program bf_program = {
  sizeof (bf_insn) / (sizeof bf_insn[0]),
  bf_insn 
};

Note the protocol parameter in the bpf program, which is the one used in

int sock = socket(AF_PACKET, SOCK_RAW, protocol);

Replace recv() and send() with read() and write()

Note that read() and write() would be blocking unless O_NONBLOCK is set for the socket with fcntl()