Send Raw Ethernet Frame

Hello,

I would like to send an Ethernet frame consisting of the preamble, the start of frame delimiter, and then some custom data (in other words, NOT the MAC destination / source, nor the rest of the typical format for an Ethernet frame).

I found a python script that looks like it works with Linux (AF_PACKET): https://gist.github.com/cslarsen/11339448

From further reading and trial and error, it seems that this needs modified to use the Berkley Packet Filter.

What changes need to be made? Note: this is for a personal project.

I'm pretty sure AF_INET is what I need.

Python:
"""Demonstrates how to construct and send raw Ethernet packets on the
network.
You probably need root privs to be able to bind to the network interface,
e.g.:
    $ sudo python sendeth.py
"""

from socket import *

def sendeth(src, dst, eth_type, payload, interface = "eth0"):
  """Send raw Ethernet packet on interface."""

  assert(len(src) == len(dst) == 6) # 48-bit ethernet addresses
  assert(len(eth_type) == 2) # 16-bit ethernet type

  s = socket(AF_PACKET, SOCK_RAW)

  # From the docs: "For raw packet
  # sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"
  s.bind((interface, 0))
  return s.send(src + dst + eth_type + payload)

if __name__ == "__main__":
  print("Sent %d-byte Ethernet packet on eth0" %
    sendeth("\xFE\xED\xFA\xCE\xBE\xEF",
            "\xFE\xED\xFA\xCE\xBE\xEF",
            "\x7A\x05",
"hello"))
 
Here is something I played with:
C:
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_vlan_var.h>
#include <netinet/igmp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sysexits.h>
#include <unistd.h>

static uint16_t
in_cksum(const void *ptr, size_t len)
{
        size_t words = len / 2;
        const uint16_t *p = ptr, *endp = p + words;
        uint32_t res = 0;
        for (; p != endp; ++p) {
                res += *p;
        }
        if (len % 2) {
                res += *(uint8_t*)p;
        }
        res = (res >> 16) + (res & 0xffff);
        return ~((res >> 16) + res) & 0xffff;
}

int
main(/*int argc, char *argv[]*/void)
{
        /* http://bastian.rieck.ru/howtos/bpf/ */
        const char *bpf_path = "/dev/bpf";
        int fd = open(bpf_path, O_WRONLY, 0);
        if (fd == -1)
                err(EX_OSERR, "open '%s'", bpf_path);
        warnx("opened '%s' as fd %d", bpf_path, fd);

        const char *ifname = "hn0";
        struct ifreq bind_if;
        memset(&bind_if, 0, sizeof bind_if);
        strcpy(bind_if.ifr_name, ifname);
        if (ioctl(fd, BIOCSETIF, &bind_if) == -1)
                err(EX_OSERR, "ioctl BIOCSETIF");
        warnx("fd %d bound to %s", fd, ifname);

        uint8_t pkt[1500] = {};
        uint8_t *p = &pkt[0];
        struct ether_header *ether = (struct ether_header*)p;
        // or struct ether_vlan_header!
        p += sizeof *ether;
        struct ip *ip = (struct ip*)p;
        p += sizeof *ip;
        struct igmp *igmp = (struct igmp*)p;
        p += sizeof *igmp;
        const size_t pkt_len = 64; // p - &pkt[0];
        const size_t payload_len = sizeof *igmp;

        struct in_addr mcast_group;
        inet_aton("239.1.1.1", &mcast_group);

        ether->ether_dhost[0] = 0x01;
        ether->ether_dhost[1] = 0x00;
        ether->ether_dhost[2] = 0x5e;
        ether->ether_dhost[3] = 0x01; // 239.1.1.1
        ether->ether_dhost[4] = 0x01;
        ether->ether_dhost[5] = 0x01;
        ether->ether_shost[0] = 0x00;
        ether->ether_shost[1] = 0x15;
        ether->ether_shost[2] = 0x5d;
        ether->ether_shost[3] = 0x01;
        ether->ether_shost[4] = 0x98;
        ether->ether_shost[5] = 0x38;
        ether->ether_type = ntohs (0x0800);

        ip->ip_hl = 5; // 5 times 4 bytes = 20 bytes
        ip->ip_v = 4;
        ip->ip_tos = 0x10; // from igmp-joiner
        ip->ip_len = ntohs (20 + payload_len);
        ip->ip_id = ntohs (0);
        ip->ip_off = ntohs (IP_DF);
        ip->ip_ttl = 1;
        ip->ip_p = IPPROTO_IGMP;
        ip->ip_sum = 0;
        inet_aton("192.168.1.21", &ip->ip_src);
        ip->ip_dst = mcast_group;
        ip->ip_sum = in_cksum(ip, sizeof *ip);

        igmp->igmp_type = IGMP_v2_HOST_MEMBERSHIP_REPORT;
        igmp->igmp_code = 0;
        igmp->igmp_cksum = 0;
        igmp->igmp_group = mcast_group;
        igmp->igmp_cksum = in_cksum(igmp, sizeof *igmp);

        if (write(fd, pkt, pkt_len) == -1)
                err(EX_OSERR, "write");

        close(fd);
        return 0;
}
 
Is there a way to do what I want using ng_ether?

From the ng_ether man page:

"Writing to this [the lower] hook results in a raw Ethernet frame being transmitted by the device. ... In all cases, frames are raw Ethernet frames with the standard 14 byte Ethernet header (but no checksum)."

An example given:

Code:
cat sample.pkt | nghook fxp0: orphans

So would the frame sent out contain MAC addresses, assuming sample.pkt did NOT have them?
 
I've no idea. But I believe the benefit of using BPF is, that at least on theory, your program will work everywhere, where BPF is available.
 
Back
Top