I have a NXP FRDM-K64F board and I want to set the ethernet example but I cannot get it working. This is how my code looks like after setting the static IP address.
#include "mbed.h"
#include "main-hw.h"
#include "EthernetInterface.h"
// Network interface
EthernetInterface net;
int main(void)
{
// Bring up the ethernet interface
printf("Ethernet socket example\r\n");
int ret;
ret = net.set_network("192.168.15.177","255.255.255.0","192.168.15.1");
printf("Set Net: %d\r\n",ret);
char macadd[6];
mbed_mac_address(macadd);
printf("%02x:%02x:%02x:%02x:%02x:%02x \r\n", macadd[0], macadd[1], macadd[2], macadd[3], macadd[4], macadd[5]);
const char *mac = net.get_mac_address();
printf("MAC address is: %s\r\n", mac ? mac : "No MAC");
const char *ip = net.get_ip_address();
printf("IP address is: %s\r\n", ip ? ip : "No IP");
ret = net.connect();
printf("Connect: %d\n",ret);
// Show the network address
// const char *ip = net.get_ip_address();
// printf("IP address is: %s\n", ip ? ip : "No IP");
// Open a socket on the network interface, and create a TCP connection to mbed.org
TCPSocket socket;
socket.open(&net);
socket.connect("developer.mbed.org", 80);
// Send a simple http request
char sbuffer[] = "GET / HTTP/1.1\r\nHost: developer.mbed.org\r\n\r\n";
int scount = socket.send(sbuffer, sizeof sbuffer);
printf("sent %d [%.*s]\n", scount, strstr(sbuffer, "\r\n")-sbuffer, sbuffer);
// Recieve a simple http response and print out the response line
char rbuffer[64];
int rcount = socket.recv(rbuffer, sizeof rbuffer);
printf("recv %d [%.*s]\n", rcount, strstr(rbuffer, "\r\n")-rbuffer, rbuffer);
// Close the socket to return its memory and bring down the network interface
socket.close();
// Bring down the ethernet interface
net.disconnect();
printf("Done\n");
return 0;
}
What I see is that I only get the macAddress with the mbed_mac_address command. With net.get_mac_address and net.get_ip_address I only get NULL values.
The process get to the net.connect and I see no more results.
What am I doing wrong?
With mbed OS 5.3.4 this works fine for me on a K64F:
#include "mbed.h"
#include "EthernetInterface.h"
// Network interface
EthernetInterface net;
// Socket demo
int main() {
// Set static IP
net.set_network("192.168.1.99", "255.255.255.0", "192.168.1.1");
// Bring up the ethernet interface
printf("Ethernet socket example\n");
net.connect();
// Show the network address
const char *ip = net.get_ip_address();
printf("IP address is: %s\n", ip ? ip : "No IP");
printf("MAC address is: %s\n", net.get_mac_address());
// Open a socket on the network interface, and create a TCP connection to mbed.org
TCPSocket socket;
socket.open(&net);
socket.connect("developer.mbed.org", 80);
// Send a simple http request
char sbuffer[] = "GET / HTTP/1.1\r\nHost: developer.mbed.org\r\n\r\n";
int scount = socket.send(sbuffer, sizeof sbuffer);
printf("sent %d [%.*s]\n", scount, strstr(sbuffer, "\r\n")-sbuffer, sbuffer);
// Recieve a simple http response and print out the response line
char rbuffer[64];
int rcount = socket.recv(rbuffer, sizeof rbuffer);
printf("recv %d [%.*s]\n", rcount, strstr(rbuffer, "\r\n")-rbuffer, rbuffer);
// Close the socket to return its memory and bring down the network interface
socket.close();
// Bring down the ethernet interface
net.disconnect();
printf("Done\n");
}
Updating mbed OS
If you still have the mbed library (not mbed-os) in the online compiler, right click on 'mbed', and click 'Remove'. Then click on 'Add library' > 'From URL' and enter https://github.com/armmbed/mbed-os.
If you have mbed-os, right click on the library and select 'Upgrade'.
From mbed CLI:
$ mbed remove mbed
$ mbed add mbed-os
Or when you already have mbed-os:
$ cd mbed-os
$ git pull
$ git checkout latest
Related
I am learning to write pcap code in c. Below i have written a simple c code to automatically detect a device for snifiing, getting ip and subnet mask, getting link layer headers and filtering traffic and then printing packet size.
Code complies successfully but gets stuck at
Network device found: wlo1
when run. Removing the filter part does print the packet size. And removing the priting packet part; the program complies and runs successfully.
I think i am lacking understanding of filtering part.
I compile using(on linux): gcc program_name -lpcap
Output of the code is:
Network device found: wlo1
wlo1 is wlan device
#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[]){
char *dev; //device automatically detected for sniffing
char errbuf[PCAP_ERRBUF_SIZE]; //error string
pcap_t *handle; //session hnadle
struct bpf_program fp; //The compiled filter expression
char filter_exp[] = "port 23"; //The filter expression
bpf_u_int32 mask; //The netmask of our sniffing device
bpf_u_int32 net; //The IP of our sniffing device
struct pcap_pkthdr header;
const unsigned char *packet;
//device detection block
dev = pcap_lookupdev(errbuf);
if (dev == NULL){
printf("Error finding device: %s\n", errbuf);
return 1;
}
printf("Network device found: %s\n", dev);
//opening device for sniffing
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if(handle == NULL){
fprintf(stderr,"Couldn't open device %s : %s\n",dev,errbuf);
return 1;
}
// //check for link-layer header of the device
if(pcap_datalink(handle) != DLT_EN10MB){ //for ethernet data link layer
if(pcap_datalink(handle) != DLT_IEEE802_11){ //for wlan data link layer
fprintf(stderr, "Device %s doesn't provide WLAN headers - not supported\n", dev);
return 1;
}
else{
fprintf(stderr, "Device %s doesn't provide Ethernet headers - not supported\n", dev);
return 1;
}
}
//block to get device ip and subnet mask
if(pcap_lookupnet(dev, &net, &mask, errbuf) == -1){
fprintf(stderr, "Can't get netmask for device %s\n", dev);
net = 0;
mask = 0;
}
//block for filtering traffic we want to sniff
if(pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 1;
}
if(pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 1;
}
/* Grab a packet */
packet = pcap_next(handle, &header);
/* Print its length */
printf("Jacked a packet with length of [%d]\n", header.len);
/* And close the session */
pcap_close(handle);
return 0;
}
If wlo1 is capturing in monitor mode on a "protected" network (a network with traffic encrypted at the link layer, using WEP or WPA/WPA2/WPA3), then any filter that works above the link layer - such as a TCP/UDP-layer filter, which "port 80" is - will not work, because the packets, as delivered to the filtering code, will have the 802.11 payload encrypted, so filters won't work on them.
Therefore, no packets will pass the filter.
I have connected 3 laptops in same LAN.
lap-1: 192.168.1.2
lap-2: 192.168.1.3
lap-3: 192.168.1.4
I made lap-1 as server and listen on 9333 port. lap-2 acts as client. Using netcat I sent data from lap2 to lap1. I'm able to capture packets using pcap in lap1. I have turned on promiscuous mode using sudo ifconfig eth0 promisc. Also in pcap_live_open method I have set promiscuous mode flag.
Then I turned off promiscuous mode and also in pcap_live_open function. Still I'm able to capture packets.
I googled about promiscuous mode and what I could infer was if device opens an interface in promiscuous mode it would able to capture all packets attached to that network.
so considering this, I made acting lap-3 as server and lap-2 remains as client. I followed the same procedure as above. I run the pcap executable in lap-1 hoping that I would able to capture packets transferred between lap-3 and lap-2 but pcap running in lap-1 is not able to do so with promiscuous mode on. All 3 laps are connected to same network.
Can anyone enlighten me the use of promiscuous mode with simple scenario?
This is my pcap code:
29988 is reverse(swap) of 9333, I'm just looking for that.
#include <pcap/pcap.h>
#include <stdint.h>
const u_char *packet;
int main()
{
char *dev = "eth0";
pcap_t *handle;
int j=0;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
bpf_u_int32 mask;
bpf_u_int32 net;
struct pcap_pkthdr header;
uint8_t *ip_header_len;
uint16_t ip_header_len_val;
uint16_t *port;
/* Find the properties for the device */
while (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
printf("Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
printf("lookedup pcap device: %s\n", dev);
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ,1,0, errbuf);
if (handle == NULL) {
printf("Couldn't open device %s: %s\n", dev, errbuf);
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
pcap_close(handle);
}
/* if (pcap_setfilter(handle, &fp) == -1) {
printf("Couldn't install filter %s: %s", filter_exp, pcap_geterr(handle));
return(-1);
}
*/
/* Grab a packet */
while ((packet = pcap_next(handle, &header)) != NULL)
{
uint16_t *data_size;
uint16_t size,total_len_val,tcp_header_len_val;
char tdata[128];
uint8_t *data,*tcp_header_len;
uint16_t *total_len;
//ip_proto = (uint8_t *)&packet[9];
ip_header_len = (uint8_t *)&packet[14];
ip_header_len_val = (*ip_header_len) & 0x0F;
ip_header_len_val = ip_header_len_val*4;
// printf("IP header len val:%d\n",ip_header_len_val);
port = (uint16_t *)&packet[14+ip_header_len_val+2];
//printf("port:%d\n",*port);
total_len = (uint16_t *)&packet[14+2];
total_len_val = ((*total_len) >> 8) & 0x00FF;
total_len_val = total_len_val + (((*total_len) << 8) & 0xFF00);
//total_len_val=*total_len;
// printf("tot len val:%d\n",total_len_val);
tcp_header_len = (uint8_t *)&packet[14+ip_header_len_val+12];
tcp_header_len_val = (*tcp_header_len) & 0xF0;
tcp_header_len_val = tcp_header_len_val>>4;
tcp_header_len_val = tcp_header_len_val * 4;
// printf("tcp header len val:%d\n",tcp_header_len_val);
size = (total_len_val- ip_header_len_val) - tcp_header_len_val;
data = (uint8_t *)&packet[14+ip_header_len_val+tcp_header_len_val];
memset(tdata,0,128);
mempcpy(tdata,data,size);
tdata[size]='\0';
if((*port)==29988)
{
printf("Data Packet:%s\n",tdata);
}
}
}
I expect that when you say that they are all on the same network, that what you mean is that they are connected to the same Ethernet switch. That switch will only send data to laptop1 that is destined for laptop1. In the old days when it was common to use an Ethernet hub, then all traffic went to all connected devices, but now a switch is very cheap and hubs are no longer common. If you can find a hub, then you can try this out, but otherwise you will only ever be able to see traffic destined for your device.
As Brad mentioned, the router knows at which port the destined device is connected, so it only send the packets there. If you want to try this out, you can use VirtualBox or VMware, and connect the machines in a virtual network.
I have this libnetfilter_queue application which receives packets from kernel based on some iptables rule. Before going straight to my problem, i'm giving a sample workable code and other tools to set up a test environment so that We problem definition and possible solutions can be more accurate and robust.
The following code describes the core functionality of the application:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
#include <errno.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#define PREROUTING 0
#define POSTROUTING 4
#define OUTPUT 3
/* returns packet id */
static u_int32_t
print_pkt (struct nfq_data *tb)
{
int id = 0;
struct nfqnl_msg_packet_hdr *ph;
struct nfqnl_msg_packet_hw *hwph;
u_int32_t mark, ifi;
int ret;
unsigned char *data;
ph = nfq_get_msg_packet_hdr (tb);
if (ph)
{
id = ntohl (ph->packet_id);
printf ("hw_protocol=0x%04x hook=%u id=%u ",
ntohs (ph->hw_protocol), ph->hook, id);
}
hwph = nfq_get_packet_hw (tb);
if (hwph)
{
int i, hlen = ntohs (hwph->hw_addrlen);
printf ("hw_src_addr=");
for (i = 0; i < hlen - 1; i++)
printf ("%02x:", hwph->hw_addr[i]);
printf ("%02x ", hwph->hw_addr[hlen - 1]);
}
mark = nfq_get_nfmark (tb);
if (mark)
printf ("mark=%u ", mark);
ifi = nfq_get_indev (tb);
if (ifi)
printf ("indev=%u ", ifi);
ifi = nfq_get_outdev (tb);
if (ifi)
printf ("outdev=%u ", ifi);
ifi = nfq_get_physindev (tb);
if (ifi)
printf ("physindev=%u ", ifi);
ifi = nfq_get_physoutdev (tb);
if (ifi)
printf ("physoutdev=%u ", ifi);
ret = nfq_get_payload (tb, &data);
if (ret >= 0)
printf ("payload_len=%d ", ret);
fputc ('\n', stdout);
return id;
}
static int
cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data)
{
uint32_t ip_src, ip_dst;
struct in_addr s_ip;
struct in_addr d_ip;
uint16_t src_port;
uint16_t dst_port;
int verdict;
int id;
int ret;
unsigned char *buffer;
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa);
if (ph)
{
id = ntohl (ph->packet_id);
printf ("received packet with id %d", id);
}
ret = nfq_get_payload (nfa, &buffer);
ip_src = *((uint32_t *) (buffer + 12));
ip_dst = *((uint32_t *) (buffer + 16));
src_port = *((uint16_t *) (buffer + 20));
dst_port = *((uint16_t *) (buffer + 22));
s_ip.s_addr = (uint32_t) ip_src;
d_ip.s_addr = (uint32_t) ip_dst;
*(buffer + 26) = 0x00;
*(buffer + 27) = 0x00;
printf ( "source IP %s", inet_ntoa (s_ip));
printf ( "destination IP %s", inet_ntoa (d_ip));
printf ( "source port %d", src_port);
printf ( "destination port %d", dst_port);
if (ret)
{
switch (ph->hook)
{
case PREROUTING:
printf ( "inbound packet");
//my_mangling_fun();
break;
case OUTPUT:
printf ( "outbound packet");
//my_mangling_fun();
break;
}
}
verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer);
if (verdict)
printf ( "verdict ok");
return verdict;
}
int
main (int argc, char **argv)
{
struct nfq_handle *h;
struct nfq_q_handle *qh;
struct nfnl_handle *nh;
int fd;
int rv;
char buf[4096] __attribute__ ((aligned));
printf ("opening library handle\n");
h = nfq_open ();
if (!h)
{
fprintf (stderr, "error during nfq_open()\n");
exit (1);
}
printf ("unbinding existing nf_queue handler for AF_INET (if any)\n");
if (nfq_unbind_pf (h, AF_INET) < 0)
{
fprintf (stderr, "error during nfq_unbind_pf()\n");
exit (1);
}
printf ("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
if (nfq_bind_pf (h, AF_INET) < 0)
{
fprintf (stderr, "error during nfq_bind_pf()\n");
exit (1);
}
printf ("binding this socket to queue '0'\n");
qh = nfq_create_queue (h, 0, &cb, NULL);
if (!qh)
{
fprintf (stderr, "error during nfq_create_queue()\n");
exit (1);
}
printf ("setting copy_packet mode\n");
if (nfq_set_mode (qh, NFQNL_COPY_PACKET, 0xffff) < 0)
{
fprintf (stderr, "can't set packet_copy mode\n");
exit (1);
}
fd = nfq_fd (h);
for (;;)
{
if ((rv = recv (fd, buf, sizeof (buf), 0)) >= 0)
{
printf ("pkt received\n");
nfq_handle_packet (h, buf, rv);
continue;
}
/* if your application is too slow to digest the packets that
* are sent from kernel-space, the socket buffer that we use
* to enqueue packets may fill up returning ENOBUFS. Depending
* on your application, this error may be ignored. Please, see
* the doxygen documentation of this library on how to improve
* this situation.
*/
if (rv < 0 && errno == ENOBUFS)
{
printf ("losing packets!\n");
continue;
}
perror ("recv failed");
break;
}
printf ("unbinding from queue 0\n");
nfq_destroy_queue (qh);
#ifdef INSANE
/* normally, applications SHOULD NOT issue this command, since
* it detaches other programs/sockets from AF_INET, too ! */
printf ("unbinding from AF_INET\n");
nfq_unbind_pf (h, AF_INET);
#endif
printf ("closing library handle\n");
nfq_close (h);
exit (0);
}
Notice in the callback function two calls to my_mangling_fun() is commented out. This is where i mangle the incoming and outgoing packet. I think this code would be sufficient to describe my case. If further clarification is need please ask, i will post further details.
Lets say accompanying iptables rules are following :
$iptables -t mangle -A PREROUTING -p udp --dport 5000 -j NFQUEUE
$iptables -t mangle -A OUTPUT -p udp --sport 5000 -j NFQUEUE
lets compile and fire udp the thing.
$gcc -g3 nfq_test.c -lnfnetlink -lnetfilter_queue
$./a.out (should be as root)
now we can feed garbage udp payload to this thing by netcat both client and server mode
$nc -ul 5000
$nc -uvv <IP> 5000
This will print the packet from my netfilter_queue app in stdout. Now that the development environment is set up, we can move to the next thing.
What we are trying to achieve is following :
Our server is listening on 5000 port. Now all incoming packet destined to udp port 5000 will be queued by kernel. And the handle to this queue will be given to user application we listed earlier. This queue mechanism works like this: When a packet is available, the callback function(cb() in our code) is called. after processing, the callback function calls nfq_set_verdict(). after a verdict is returned, next packet will pop from the queue. notice that a packet will not pop from queue if its preceding packet has not been issued a verdict. This verdict values are NF_ACCEPT for accepting packet, NF_DROP for dropping the packet.
Now what if i want to concatenate the udp payloads of the incoming and outgoing packet without touching client and server side code?
If i want to concatenate udp payloads from our app this very app, then we need to have multiple packets at hand. But we have seen that a packet does not pops from queue before a verdict is issued to its preceding one.
So how can this be done?
One possible solution is issue a NF_DROP to every packet and save those packets in an intermediate data structure. Let's say we have done it. But how can this packet can be delivered to the service listening on 5000 port?
We can't use network stack for delivering the packet, because if we do, then packets will end up in NFQUEUE again.
Another problem is, the server is totally agnostic about this app. That means it should not see any difference in the packets. It should see packets as if it came from the original client.
I have heard that a application can send data to a server in the same host without using network layer(ip,port) by writing some files. I do not know the validity of this statement. But if anyone knows anything about it , it will be wonderful.
I may get down voted for too much verbosity. But I think this can be fun session. we can find the solution together :)
I propose the following solution:
store packets in the application and return verdict NF_DROP
re-inject packets into the network stack using RAW sockets
tag concatenated UDP packets with a DSCP (see IP packet format)
in iptables, add a rule to match on this DSCP (--dscp) and ACCEPT the packet directly, without it passing through your netfilter application
If your provider already tags some packets with DSCP, you can add some iptables rules to clear them, like:
iptables -t mangle -A INPUT -j DSCP --set-dscp 0
I hope this solves your use-case.
First of all, thank you very much Aftnix! Your example kick started my automatic packet-inspecting wake-on-lan project. I want to my home server to sleep when it's idle, but wake up as soon as some requests come in. The idea is to inspect the request on a ddwrt router, decide it is a legit request and send a wol package. For SMTP the idea is to queue multiple packets, keep the other end happy with some bogus responses and kick in the real server transparantly.
I modified your example a little bit to queue up 1 packet and send it with the next packet. This is just a proof-of-concept, but it works fine.
// struct and variable needed to store 1 packet
struct packetbuffer {
struct nfq_q_handle *qh;
u_int32_t id;
u_int32_t verdict;
u_int32_t data_len;
unsigned char *buf;
};
struct packetbuffer pbuf;
int counter = 0;
static int cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data)
{
uint32_t ip_src, ip_dst;
struct in_addr s_ip;
struct in_addr d_ip;
uint16_t src_port;
uint16_t dst_port;
int verdict;
int id;
int ret;
unsigned char *buffer;
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa);
if (ph)
{
id = ntohl (ph->packet_id);
printf ("received packet with id %d", id);
}
ret = nfq_get_payload (nfa, &buffer);
ip_src = *((uint32_t *) (buffer + 12));
ip_dst = *((uint32_t *) (buffer + 16));
src_port = *((uint16_t *) (buffer + 20));
dst_port = *((uint16_t *) (buffer + 22));
s_ip.s_addr = (uint32_t) ip_src;
d_ip.s_addr = (uint32_t) ip_dst;
*(buffer + 26) = 0x00;
*(buffer + 27) = 0x00;
printf ( "source IP %s", inet_ntoa (s_ip));
printf ( "destination IP %s", inet_ntoa (d_ip));
printf ( "source port %d", src_port);
printf ( "destination port %d", dst_port);
if (ret)
{
switch (ph->hook)
{
case PREROUTING:
printf ( "inbound packet");
//my_mangling_fun();
break;
case OUTPUT:
printf ( "outbound packet");
//my_mangling_fun();
break;
}
}
// My modification starts here
if ((counter % 2) == 0)
{
pbuf.qh = qh;
pbuf.id = id;
pbuf.data_len = ret;
pbuf.buf = malloc(ret);
memcpy(pbuf.buf, buffer, ret);
printf(" queue package %d \n", id);
}
else
{
printf(" output 1st package %d, len=%d\n", pbuf.id, pbuf.data_len);
verdict = nfq_set_verdict (pbuf.qh, pbuf.id, NF_ACCEPT, pbuf.data_len, pbuf.buf);
free(pbuf.buf);
printf(" output 2nd package %d, len=%d\n", id, ret);
verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer);
}
counter++;
return 0;
}
This is not all code, but it should be pretty obvious what changed.
I know I'm a bit late to the party, but I decided to post this solution for future reference and hopefully some critics.
edit: hmm maybe I'd better write an userspace process like rinetd.
Is there a way how to get an IP address of an interface in Linux using libpcap?
I have found this,
Get IP address of an interface on Linux, but that doesn't use pcap.
Also, in the pcap examples it is said that something like this should get your IP but it gives you your network address.
Using the pcap_findalldevs function:
#include <pcap/pcap.h>
#include <arpa/inet.h>
static char errbuf[PCAP_ERRBUF_SIZE];
int main() {
pcap_if_t *alldevs;
int status = pcap_findalldevs(&alldevs, errbuf);
if(status != 0) {
printf("%s\n", errbuf);
return 1;
}
for(pcap_if_t *d=alldevs; d!=NULL; d=d->next) {
printf("%s:", d->name);
for(pcap_addr_t *a=d->addresses; a!=NULL; a=a->next) {
if(a->addr->sa_family == AF_INET)
printf(" %s", inet_ntoa(((struct sockaddr_in*)a->addr)->sin_addr));
}
printf("\n");
}
pcap_freealldevs(alldevs);
return 0;
}
Output of sudo ./pcap:
eth0: 192.168.2.1
usbmon1:
usbmon2:
usbmon3:
usbmon4:
usbmon5:
any:
lo: 127.0.0.1
I'm trying to forge an IGMPv2 Membership Request packet and send it on a RAW socket.
The RFC 3376 states:
IGMP messages are encapsulated in IPv4 datagrams, with an IP protocol number of 2. Every IGMP message described in this document is sent with an IP Time-to-Live of 1, IP Precedence of Internetwork Control (e.g., Type of Service 0xc0), and carries an IP Router Alert option [RFC-2113] in its IP header
So the IP_ROUTER_ALERT flag must be set.
I'm trying to forge the strict necessary of the packet (e.g. only the IGMP header & payload), so i'm using the setsockopt to edit the IP options.
some useful variables:
#define C_IP_MULTICAST_TTL 1
#define C_IP_ROUTER_ALERT 1
int sockfd = 0;
int ecsockopt = 0;
int bytes_num = 0;
int ip_multicast_ttl = C_IP_MULTICAST_TTL;
int ip_router_alert = C_IP_ROUTER_ALERT;
Here's how I open the RAW socket:
sock_domain = AF_INET;
sock_type = SOCK_RAW;
sock_proto = IPPROTO_IGMP;
if ((ecsockopt = socket(sock_domain,sock_type,sock_proto)) < 0) {
printf("Error %d: Can't open socket.\n", errno);
return 1;
} else {
printf("** Socket opened.\n");
}
sockfd = ecsockopt;
Then I set the TTL and Router Alert option:
// Set the sent packets TTL
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ip_multicast_ttl, sizeof(ip_multicast_ttl))) < 0) {
printf("Error %d: Can't set TTL.\n", ecsockopt);
return 1;
} else {
printf("** TTL set.\n");
}
// Set the Router Alert
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_ROUTER_ALERT, &ip_router_alert, sizeof(ip_router_alert))) < 0) {
printf("Error %d: Can't set Router Alert.\n", ecsockopt);
return 1;
} else {
printf("** Router Alert set.\n");
}
The setsockopt of IP_ROUTER_ALERT returns 0. After forging the packet, i send it with sendto in this way:
// Send the packet
if((bytes_num = sendto(sockfd, packet, packet_size, 0, (struct sockaddr*) &mgroup1_addr, sizeof(mgroup1_addr))) < 0) {
printf("Error %d: Can't send Membership report message.\n", bytes_num);
return 1;
} else {
printf("** Membership report message sent. (bytes=%d)\n",bytes_num);
}
The packet is sent, but the IP_ROUTER_ALERT option (checked with wireshark) is missing.
Am i doing something wrong? is there some other methods to set the IP_ROUTER_ALERT option?
Thanks in advance.
Finally i've found out that the IP_ROUTER_ALERT has to be set by the Linux Kernel. IGMP membership requests are sent after a IP_ADD_MEMBERSHIP is done and the Kernel takes charge of setting the IP_ROUTER_ALERT flag.
I do not know why your code is not working (it looks fine to me), but I can suggest a workaround: Drop one more layer down on your raw socket and build Ethernet frames. You may also want to take a look at Libnet, which handles building packets like this for you.
The documentation states:
Pass all to-be forwarded packets with
the IP Router Alert option set to this
socket. Only valid for raw sockets.
This is useful, for instance, for user
space RSVP daemons. The tapped packets
are not forwarded by the kernel, it is
the users responsibility to send them
out again. Socket binding is ignored,
such packets are only filtered by
protocol. Expects an integer flag.
This sounds as if the option only matters when receiving packets on the socket, not when sending them. If you're sending raw packets, can't you just set the required option in the IP header yourself?
As a reference I would recommend one of the many IGMP aware programs out there.
One example is igmpproxy:
https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L54
/*
* Open and initialize the igmp socket, and fill in the non-changing
* IP header fields in the output packet buffer.
*/
void initIgmp(void) {
struct ip *ip;
recv_buf = malloc(RECV_BUF_SIZE);
send_buf = malloc(RECV_BUF_SIZE);
k_hdr_include(true); /* include IP header when sending */
k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
k_set_ttl(1); /* restrict multicasts to one hop */
k_set_loop(false); /* disable multicast loopback */
ip = (struct ip *)send_buf;
memset(ip, 0, sizeof(struct ip));
/*
* Fields zeroed that aren't filled in later:
* - IP ID (let the kernel fill it in)
* - Offset (we don't send fragments)
* - Checksum (let the kernel fill it in)
*/
ip->ip_v = IPVERSION;
ip->ip_hl = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */
ip->ip_tos = 0xc0; /* Internet Control */
ip->ip_ttl = MAXTTL; /* applies to unicasts only */
ip->ip_p = IPPROTO_IGMP;
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
alligmp3_group = htonl(INADDR_ALLIGMPV3_GROUP);
}
and https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L271
/*
* Construct an IGMP message in the output packet buffer. The caller may
* have already placed data in that buffer, of length 'datalen'.
*/
static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) {
struct ip *ip;
struct igmp *igmp;
extern int curttl;
ip = (struct ip *)send_buf;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen);
if (IN_MULTICAST(ntohl(dst))) {
ip->ip_ttl = curttl;
} else {
ip->ip_ttl = MAXTTL;
}
/* Add Router Alert option */
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[0] = IPOPT_RA;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[1] = 0x04;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00;
igmp = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN);
igmp->igmp_type = type;
igmp->igmp_code = code;
igmp->igmp_group.s_addr = group;
igmp->igmp_cksum = 0;
igmp->igmp_cksum = inetChksum((unsigned short *)igmp,
IP_HEADER_RAOPT_LEN + datalen);
}