linux raw ethernet socket bind to specific protocol - linux

I'm writing code to send raw Ethernet frames between two Linux boxes. To test this I just want to get a simple client-send and server-receive.
I have the client correctly making packets (I can see them using a packet sniffer).
On the server side I initialize the socket like so:
fd = socket(PF_PACKET, SOCK_RAW, htons(MY_ETH_PROTOCOL));
where MY_ETH_PROTOCOL is a 2 byte constant I use as an ethertype so I don't hear extraneous network traffic.
when I bind this socket to my interface I must pass it a protocol again in the socket_addr struct:
socket_address.sll_protocol = htons(MY_ETH_PROTOCOL);
If I compile and run the code like this then it fails. My server does not see the packet. However if I change the code like so:
socket_address.sll_protocol = htons(ETH_P_ALL);
The server then can see the packet sent from the client (as well as many other packets) so I have to do some checking of the packet to see that it matches MY_ETH_PROTOCOL.
But I don't want my server to hear traffic that isn't being sent on the specified protocol so this isn't a solution. How do I do this?

I have resolved the issue.
According to http://linuxreviews.org/dictionary/Ethernet/ referring to the 2 byte field following the MAC addresses:
"values of that field between 64 and 1522 indicated the use of the new 802.3 Ethernet format with a length field, while values of 1536 decimal (0600 hexadecimal) and greater indicated the use of the original DIX or Ethernet II frame format with an EtherType sub-protocol identifier."
so I have to make sure my ethertype is >= 0x0600.
According to http://standards.ieee.org/regauth/ethertype/eth.txt use of 0x88b5 and 0x88b6 is "available for public use for prototype and vendor-specific protocol development." So this is what I am going to use as an ethertype. I shouldn't need any further filtering as the kernel should make sure to only pick up ethernet frames with the right destination MAC address and using that protocol.

I've worked around this problem in the past by using a packet filter.
Hand Waving (untested pseudocode)
struct bpf_insn my_filter[] = {
...
}
s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
struct sock_fprog pf;
pf.filter = my_filter;
pf.len = my_filter_len;
setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));
sll.sll_family = PF_PACKET;
sll.sll_protocol = htons(protocol);
sll.sll_ifindex = if_nametoindex("eth0");
bind(s, &sll, sizeof(sll));
Error checking and getting the packet filter right is left as an exercise for the reader...
Depending on your application, an alternative that may be easier to get working is libpcap.

Related

SSDP M-search does not work for unicast - single IP

SSDP M-search request for discovery works great for multicast address 239.255.255.250. However, I have a requirement to send this request to a specific IP. It does not work. As per the UPnP architecture document unicast discover message is supported, with a small difference from the multicast message
The multi-cast message is
M-SEARCH * HTTP/1.1
HOST:239.255.255.250:1900
MAN:ssdp:discover
ST:ssdp:all
MX:1
Unicast message is similar - except that the multicast IP is changed to specific IP and MX:1 is removed (as per the specification - did not find any code sample for unicast discover m-search)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
msg = 'M-SEARCH * HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:1\r\n\r\n'
s.sendto(msg.encode('utf-8'), ('239.255.255.250', 1900))
resp = s.recv(1024)
print (resp)
This works great. But the following doesn't - the recv() call times out
sock_1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
msg = 'M-SEARCH * HTTP/1.1\r\nHOST:<specific ip>:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\n\r\n'
sock_1.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock_1.settimeout(30)
sock_1.sendto(msg.encode('utf-8'), ('<specific ip>', 1900))
resp = sock_1.recv(1024)
I have tried a few more variations of 'setsockopt' which did not work.
Please share if there are any clues for possible reasons - could it be from the device side that it responds to the multicast on 1900 but not to the unicast message?
Thanks in advance!
msg = 'M-SEARCH * HTTP/1.1\r\nHOST:<specific ip>:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\n'
You're missing a final \r\n in the end (it's for the empty line signaling end of headers: without it the receiver will just keep waiting for more).
The only difference with a unicast m-search is the HOST, like you've got.
Are you binding the socket? It should be bound to port 0. Here's an example of setting up a socket for ssdp: https://github.com/lbryio/aioupnp/blob/master/aioupnp/protocols/multicast.py#L64
In addition to the final \r\n pointed out by Jussi, you may find you need to try different permutations of the M-SEARCH format, some gateways are very finicky. For some the value for MAN must be in quotes, for others it must not. Additional quirks are the order of MAN, MX, ST and the capitalization of HOST/Host and MAN/Man . You may also need to try a variety of search targets.

Generate 100% custom layer2 payload with python / scapy

I want to create a python3 script which uses a wireless active connection (the bssid which the interface is attached at the moment) to send a 100% custom layer 2 payload (or in other words, a 100% custom layer 3 or 2.5 protocol).
I have tried a lot of ways with scapy, with scripts like the following:
from scapy.all import *
import os
interfaces = os.listdir('/sys/class/net')
interface = interfaces[1] # which is the wireless interface
send(Dot11(addr1 = 'aa:aa:aa:aa:aa:aa'), iface = str(interface)) # I pretend here to send an empty frame to addr1 destination, using the wireless card and the existing bssid on which i'm currently connected
Always I get the destination MAC address as broadcast. And of course I don't understand the proper way to do what I want.
Any suggestion or clarification is welcome.
Thanks in advance.
The answer, the key is use sendp instead send, for example a IEEE1905 Ethertype:
import os
from scapy.all import *
dest_MAC = aa:aa:aa:aa:aa:aa # the mac address you want to send it
interfaces = os.listdir('/sys/class/net') interface = interfaces[3] #select one interface from the array, it could be Ethernet or an existing Wireless STA connection
p = Ether(type=0x893a, dst = dest_MAC)/b"\x00\x00\x00" #we send just three "zero" bytes
sendp(p, iface=interface) #using sendp instead send, which is layer2 abstracted

Sending MPLS tagged packets from a Linux application

I have written some VPN software which now needs to be able to be able to tag de-tunnelled traffic with MPLS tags.
I've looked at the source of Mausezahn (which can send MPLS packets) and it seems to construct the whole ethernet frame, using various helper functions, and then gives that ethernet frame to the kernel with libnet_write.
staging/send_eth.c:
...
tmpls = libnet_build_mpls(tx.mpls_label,
tx.mpls_exp,
tx.mpls_bos,
tx.mpls_ttl,
NULL,
0,
l,
0);
...
t = libnet_build_ethernet (tx.eth_dst,
tx.eth_src,
tx.eth_type,
tx.eth_payload,
tx.eth_payload_s,
L,
t);
if (t == -1)
{
fprintf(stderr, " mz/create_eth_frame: Can't build Ethernet header: %s\n",
libnet_geterror(l));
exit(EXIT_FAILURE);
}
if (verbose) (void) print_frame_details();
libnet_write(L);
...
My code currently uses an IP-level tunnel interface (e.g. tun0) to deliver packets into the kernel.
If I wish to use MPLS will I need to start using a tap interface instead?
Or is there another way, such as ioctls on tun0's fd?
Or do I need to use libnet_write?

Arduino read get values after ? in url with ethernet shield

Sorry if it has been asked before but I cannot find anything on the internet. I just want to host a page in arduino ethernet shield and when I visit it from a browser with Get parameters (e.g http://xxx.xxx.xx.xx/led.html?red=255,green=0,blue=255) to change to change the led's color. I cannot find how to send data from browser to arduino.
The answer involves the use of The Ethernet Library (Ethernet.h) - which provides an interface to Client and Server, a degree of conceptualizing low-level I/O and buffering the input, and context.
In the following code snippet ( of the sample code at http://arduino.cc/en/Tutorial/WebServer )
...
char c = client.read();
Serial.write(c);
...
the line char c = client.read() is taking a byte from the request stream and assigning it to a char type, it is then serialing that charbyte to a string where it perform conditional logic on it.
The conditional logic of that sample only cares about reading whether a return character \n on a blank line, but the bytes that are being read (on each iteration) byte to byte, actually compose the RAW Request.
At minimal, a RAW HTTP GET Request looks like this:
GET /?first=John&Last=Doe HTTP/1.1
Host: localhost
So, for you to read the query string, you'll need to buffer the bytes being read from the stream.
Then, you'll likely serialize the whole buffer into a string, and perform string operations on them, as well as your conditional logic....

wireless sniffing using pcap, MAC address filter

I am writing a wireless packet sniffer program in C. I have set my wireless interface in monitor mode using airmon-ng, and now i am sniffing on the interface "mon0". I am using linux(ubuntu 10.10).
I want to set MAC address as the filter for the packets.
I have done it as shown below, but it says
"mon0 no IPV4 address assigned"
pcap_lookupnet(dev,&net,&mask,errbuf);
printf("%s\n",errbuf);
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
printf("Couldn't open device %s: %s\n", dev, errbuf);
return 2;
}
if(pcap_compile(handle,&fp,argv[0],0,net)==-1){
fprintf(stderr,"Error calling pcap_compile\n");exit(1);}
if(pcap_setfilter(handle,&fp) == -1){
fprintf(stderr,"Error setting filter\n");exit(1);}
/* The call pcap_loop() and pass our callback function */
pcap_loop(handle, 10, my_callback, NULL);
Please help me, how i can set the filter for MAC address??
"no IPV4 address assigned" is an error from pcap_lookupnet(). All it means is that the network interface on which you're trying to capture does not have an IPv4 address assigned to it. What airmon-ng did was to create a "monitor" interface for the Wi-Fi adapter; the regular network interface for the adapter might have an IP address assigned to it, but the monitor interface won't have one.
The only place where the IP address matters is for ip broadcast filter expressions; if you're not filtering for IPv4 broadcast addresses, which you probably won't be, there's no need to get the IPv4 address. To quote the pcap_compile() man page:
If the netmask of the network on which packets are being captured isn't known to the program, or if packets are being captured on the Linux "any" pseudo-interface that can capture on more than one network, a value of 0 can be supplied; tests for IPv4 broadcast addreses won't be done correctly, but all other tests in the filter program will be OK.
so just pass 0 as the "net" argument to pcap_compile().
If you want to search for packets being sent to a particular MAC address, you can just use wlan dst XX:XX:XX:XX:XX:XX; if you want to search for packets being sent from a particular MAC address, you can just use wlan src XX:XX:XX:XX:XX:XX; if you want to search for packets being sent to or from a particular MAC address, you can just use wlan host XX:XX:XX:XX:XX:XX. If you care about the access point address, rather than the station address, you'll need to use filters such as wlan ra XX:XX:XX:XX:XX:XX or wlan ta XX:XX:XX:XX:XX:XX, at least with newer versions of libpcap. (See the pcap-filter man page or, if you don't have a pcap-filter man page, the tcpdump man page for details.)

Resources