I am modifying kernel module (called map) in vyatta to convert IPv4 packet to IPv6.
http://enog.jp/~masakazu/vyatta/map/
I could do the conversation by removing the IPv4 header and and adding new IPv6 header, but after I call ip6_local_out(), it returns no error, but the packet is still in the struct sk_buff skb I am using. When I use tcpdump I can not see the new IPv6 packet. Can anyone tell me where I went wrong?
skb_dst_drop(skb);
skb_dst_set(skb, dst);
memcpy(&orig_iph, iph, sizeof(orig_iph));
skb_pull(skb, orig_iph.ihl * 4);
skb_push(skb, sizeof(struct ipv6hdr));
skb_reset_network_header(skb);
skb->protocol = htons(ETH_P_IPV6);
ipv6h = ipv6_hdr(skb);
ipv6h->version = 6;
ipv6h->priority = 0; /* XXX: */
ipv6h->flow_lbl[0] = 0;
ipv6h->flow_lbl[1] = 0;
ipv6h->flow_lbl[2] = 0;
ipv6h->payload_len = htons(ntohs(orig_iph.tot_len) - orig_iph.ihl * 4);
ipv6h->hop_limit = orig_iph.ttl;
memcpy(&ipv6h->saddr, &fl6.saddr, sizeof(struct in6_addr));
ipv6h->nexthdr = orig_iph.protocol;
pkt_len = skb->len;
skb->local_df = 1;
if (df)
err = ip6_local_out(skb);
else
err = ip6_fragment(skb, ip6_local_out);
return 0;
I use a my method called map_debug_print_skb("map_trans_forward_v4v6", skb); which prints IP header and transport header. I could see all the new IPv6 header detail from it but the packet is not showing in tcpdump.
Related
Hello i created Network driver that uses the uart port to send and recive.
My driver works with some issues. I was able to ping but always afther a few pings i get
ping: sendmsg: No buffer space available driver
I checked the kernel logs but i could not see anything.
this is how i recive data:
struct stm32_port *stm32_port = netdev_priv(my_net);
struct sk_buff *skb;
unsigned char *dma_start;
dma_start = stm32_port->rx_buf + (RX_BUF_L - stm32_port->last_res);
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, dma_start, 16, true);
skb = dev_alloc_skb(dma_size + 2);
if (!skb) {
if (printk_ratelimit( ))
printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
my_net->stats.rx_dropped++;
//goto error;
}
memcpy(skb_put(skb, dma_size), dma_start, dma_size);
/* Write metadata, and then pass to the receive level */
skb->dev = my_net;
skb->protocol = eth_type_trans(skb, my_net);
skb->ip_summed = CHECKSUM_NONE; // let the OS check the checksum
my_net->stats.rx_packets++;
my_net->stats.rx_bytes += dma_size;
netif_rx(skb);
port->icount.rx += dma_size;
stm32_port->last_res -= dma_size;
if (stm32_port->last_res == 0)
stm32_port->last_res = RX_BUF_L; //dma_count
Here is how i send my data:
struct stm32_port *lp = netdev_priv(ndev);
struct uart_port *port = &lp->port;
struct sk_buff *sk_buff;
struct dma_async_tx_descriptor *desc = NULL;
struct stm32_usart_offsets *ofs = &lp->info->ofs;
unsigned pktlen = skb->len;
dma_cookie_t cookie;
int ret = 0;
//print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, 16, true);
netif_stop_queue(ndev);
sk_buff = skb_get(skb);
if (ofs->icr == UNDEF_REG){
stm32_usart_clr_bits(port, ofs->isr, USART_SR_TC);
}else{
writel_relaxed(USART_ICR_TCCF, port->membase + ofs->icr);
}
memcpy(&lp->tx_buf[0], sk_buff->data, pktlen);
desc = dmaengine_prep_slave_single(lp->tx_ch,
lp->tx_dma_buf,
pktlen,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT);
if (!desc){
goto fallback_err;
}
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
/* dma no yet started, safe to free resources */
dmaengine_terminate_async(lp->tx_ch);
goto fallback_err;
}
/* Issue pending DMA TX requests */
dma_async_issue_pending(lp->tx_ch);
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
/* rely on TXE irq (mask or unmask) for sending remaining data */
stm32_usart_tx_interrupt_disable(port);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += pktlen;
fallback_err:
skb_tx_timestamp(skb);
dev_kfree_skb (skb);
netif_start_queue(ndev);
return NETDEV_TX_OK;
Thanks to #stark i found a solution i free the buffer now with the __kfree_skb() methode because like that the refcount will not be checked.
I am writing a Netfilter hook and want to do a stateful analysis of incoming TCP packets, whether they belong to an existing connection or a new connection is starting.
This is my first try at writing code using Netfilter and after reading https://people.netfilter.org/pablo/docs/login.pdf I understand I need to check if a packet is categorized as a NEW or ESTABLISHED state. But I cannot find any documentation of how to write code for this.
static unsigned int hfunc(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct iphdr *iph;
struct udphdr *udph;
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
/*
if packet SYN flag is enabled and state==NEW:
return NF_ACCEPT
else if SYN flag is disabled and state==NEW:
return NF_DROP
*/
}
return NF_ACCEPT
}
static int __init my_net_module_init(void) {
printk(KERN_INFO "Initializing my netfilter module\n");
// Allocating memory for hook structure.
my_nf_hook = (struct nf_hook_ops*) kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL);
// Constructing the structure
my_nf_hook->hook = (nf_hookfn*)hfunc; /* hook function */
my_nf_hook->hooknum = NF_INET_PRE_ROUTING; /* received packets */
my_nf_hook->pf = PF_INET; /* IPv4 */
my_nf_hook->priority = NF_IP_PRI_FIRST; /* max hook priority */
nf_register_net_hook(&init_net, my_nf_hook);
return 0;
}
static void __exit my_net_module_exit(void) {
nf_unregister_net_hook(&init_net, my_nf_hook);
kfree(my_nf_hook);
printk(KERN_INFO "Exiting my netfilter module\n");
}
module_init(my_net_module_init);
module_exit(my_net_module_exit);
Edit:
Added code snippet for registering hook in pre-routing.
Seems that in your hook you want to make a decision on packet based on conntrack(CT) info about the connection state - to block (drop) all the TCP packets which are in the middle of connection, i.e. packets both without SYN flag and without connection entry in CT.
So if you want to reap the benefits of CT, you have to let him work a bit.
Now your hook is in NF_INET_PRE_ROUTING with NF_IP_PRI_FIRST priority. Just look at the picture of Linux kernel packet flow. If we talk about pre-routing chain CT-handling is somewhere after RAW table (i.e. with a lower priority).
The list of priorities you can see here:
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
Thus to stick in after CT (after nf_conntrack_in()) you must register your hook with priority lower than NF_IP_PRI_CONNTRACK (i.e. with greater number, e.g. -50).
So you do:
static struct nf_hook_ops hooks[] __read_mostly = {
{
.hook = hfunc,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK + 150
},
// ...
};
// ...
int ret;
ret = nf_register_hooks(hooks, ARRAY_SIZE(hooks));
if (ret < 0)
// error
Then you should access the CT info from within your hook:
static unsigned int hfunc(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) {
struct iphdr *iph;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct tcphdr *tcph;
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return NF_ACCEPT;
tcph = tcp_hdr(skb)
if (tcph->syn) { // && !tcph->ack ???
if (ctinfo == IP_CT_NEW)
return NF_ACCEPT;
} else {
if (ctinfo == IP_CT_NEW)
return NF_DROP;
}
}
return NF_ACCEPT
}
Also remember that CT must be involved in your Linux kernel network processing. There should be CT modules inserted into kernel and an appropriate iptables rule added.
I am trying to write a kernel module that will dump all the HTTP packet data to dmesg.
I registered a nf_hook in POST ROUTING (tried also hooking OUTPUT table), and printing all the packets which sport is 80 (HTTP responses)
I read the following post - Print TCP Packet Data
and really got it to work! but I have a problem, the kernel module is printing only the first line of the response - HTTP/1.0 200 OK without printing all the HTTP headers and HTML.
this is my hook function -
struct iphdr *iph; /* IPv4 header */
struct tcphdr *tcph; /* TCP header */
u16 sport, dport; /* Source and destination ports */
u32 saddr, daddr; /* Source and destination addresses */
unsigned char *user_data; /* TCP data begin pointer */
unsigned char *tail; /* TCP data end pointer */
unsigned char *it; /* TCP data iterator */
/* Network packet is empty, seems like some problem occurred. Skip it */
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb); /* get IP header */
/* Skip if it's not TCP packet */
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
tcph = tcp_hdr(skb); /* get TCP header */
/* Convert network endianness to host endiannes */
saddr = ntohl(iph->saddr);
daddr = ntohl(iph->daddr);
sport = ntohs(tcph->source);
dport = ntohs(tcph->dest);
/* Watch only port of interest */
if (sport != PTCP_WATCH_PORT)
return NF_ACCEPT;
/* Calculate pointers for begin and end of TCP packet data */
user_data = (unsigned char *)((unsigned char *)tcph + (tcph->doff * 4));
tail = skb_tail_pointer(skb);
/* ----- Print all needed information from received TCP packet ------ */
/* Show only HTTP packets */
if (user_data[0] != 'H' || user_data[1] != 'T' || user_data[2] != 'T' ||
user_data[3] != 'P') {
return NF_ACCEPT;
}
/* Print packet route */
pr_debug("print_tcp: %pI4h:%d -> %pI4h:%d\n", &saddr, sport,
&daddr, dport);
/* Print TCP packet data (payload) */
pr_debug("print_tcp: data:\n");
for (it = user_data; it != tail; ++it) {
char c = *(char *)it;
if (c == '\0')
break;
printk("%c", c);
}
printk("\n\n");
return NF_ACCEPT;
I want to print the whole packet, not only the first row.
why it's printing only the first row ? My guess is that there is some routing caching (like when using IPTABLES) , is there a way to disable the caching ?
I have the same problem until closed my proxy client (like v2ray or shadowsocks), look likes the proxy client cached/simplify the http request data, you can try tcpdump to catch the http request data, there is only a line like: HTTP GET / 1.1.
I was trying to send a TCP SYN packet to a server on my machine on port 8000. Then, I wanted to check if the server responded with a SYN ACK. If this was the case, then I would send back a RST packet to abort the connection. However, when I sniff the SYN packet that I send out it tells me the TCP header has a bogus length of 0, which isn't the case. The sniffer I used was tshark, by the way. Here's my code:
In the main function, I run this:
FLAGS f = SYN;
tcp_scan("127.0.0.1",8000,f,0);
This function assembles the IP header:
struct iphdr* assemble_ip(char* dest,unsigned int proto) {
/* Assemble IP Layer */
struct iphdr* iph;
iph = malloc(sizeof(struct iphdr)); // allocate memory
if (iph == NULL) { // if the ip header is NULL
err();
return NULL;
}
srand((unsigned int)time(NULL)); // seed random number generator
/* Hardcoded values */
iph->version = 4; // the version
iph->tos = 0; // type of services
iph->ihl = 5; // internet header length
iph->id = htons(rand() % 65536); // random id
iph->ttl = rand() % 257; // ttl
iph->frag_off = 0; // fragment offset
if (iph->ttl < 64) iph->ttl += 64; // if TTL is not sufficient
iph->tot_len = htons(iph->ihl*4); // the internet header length
/* User defined values */
iph->saddr = inet_addr(client); // source address
iph->daddr = inet_addr(dest); // destination address
iph->protocol = proto; // protocol
iph->check = 0; // set to zero for later calculation
return iph;
}
This function assembles the TCP header:
struct tcphdr* assemble_tcp(unsigned int sport,unsigned int dport,FLAGS f) {
/* Assemble TCP layer */
struct tcphdr* tcph;
tcph = malloc(sizeof(struct tcphdr)); // allocate tcp header
if (tcph == NULL) { // if tcp is NULL
err();
return NULL;
}
bzero(tcph,sizeof(struct tcphdr));
srand((unsigned int)time(NULL)); // seed random number generator
/* Hardcoded values */
tcph->seq = htonl(rand() % 65001); // generate random sequence number
tcph->ack_seq = 0; // ack sequence should be 0
tcph->doff = 5; // set data offset
tcph->window = htons(rand() % 65536); // set window size
/* Increase values by random value above 64 */
if (ntohs(tcph->seq) < 64) tcph->seq += (rand() % 101 + 64);
if (ntohs(tcph->window) < 64) tcph->window += (rand() % 101 + 64);
/* User-defined values */
tcph->source = htons(sport); // source port
tcph->dest = htons(dport); // destination port
tcph = set_flags(tcph,f); // set the TCP flags
/* Set urgent ptr if URG flag is set*/
if (tcph->urg == 1) tcph->urg_ptr = 1;
else tcph->urg_ptr = 0;
tcph->check = 0; // set the checksum to 0 for other calculations
return tcph;
}
Also, I do compute the checksum of the headers. For my purposes, when calculating the checksum, the IP header is always 20 bytes long, since I'm not sending any data or options. That means that there are 10 16-bit words in the header. The TCP header is also going to be 20 bytes long since I didn't add any options or data. Here's the code:
unsigned short ip_checksum(struct iphdr* iph) {
/* Acquire IP checksum */
/*
Checksum for Internet Protocol:
One's complement of the one's complement sum of the 16 bit words in the header.
So we get the first 16 bits of the header then add it to the sum, and then
we get the next 16 bits, and add it, and so on.
...0100101010110101 -> "..." represents more bits
1111111111111111 -> this is 131071 in base 10
0000100101010110101 -> notice how the "..." bits are now 0's
*/
/* One's complement sum */
unsigned long long* ptr;
unsigned long long hdr;
unsigned short sum = 0;
unsigned long mask = 131071;
ptr = (unsigned long long*)iph; // cast structure
hdr = *ptr; // get hdr
for (int i = 0; i < 10; i++) { // 20 bytes -> 160 bits / 16 bits = 10 words
sum += (hdr & mask); // add to sum
hdr >>= 16; // shift the next 16 bits
}
sum = ~sum; // inverse
return sum;
}
TCP Checksum:
unsigned short tcp_checksum(struct tcphdr* tcph,struct iphdr* iph) {
/* Calculate TCP checksum */
struct pseudo_hdr* pseudo_hdr;
u_char* buffer;
u_char* segment;
u_char* pseudo_segment;
unsigned short sum = 0;
unsigned long mask = 131071;
unsigned long long* ptr;
unsigned long long hdr;
pseudo_hdr = malloc(sizeof(struct pseudo_hdr)); // allocate memory
buffer = malloc(32); // allocate for 32 bytes of information
if (pseudo_hdr == NULL || buffer == NULL) { // if memory wasn't allocated properly
err();
if (pseudo_hdr != NULL) free(pseudo_hdr);
if (buffer != NULL) free(buffer);
return 0;
}
pseudo_hdr->saddr = (unsigned long)iph->saddr; // we add the cast because the fields if of type u_int_32
pseudo_hdr->daddr = (unsigned long)iph->daddr; // same reason for adding the cast as above
memset(&pseudo_hdr->reserved,0,8); // set these 8 bits to 0
pseudo_hdr->proto = IPPROTO_TCP; // this will always be 6
pseudo_hdr->len = htons(tcph->doff*4); // length of tcp header
/* Place both headers into a buffer */
segment = (u_char*)tcph;
pseudo_segment = (u_char*)pseudo_hdr;
/* Concactenate */
strncat((char*)buffer,(char*)pseudo_segment,12); // first the pseudo header
strncat((char*)buffer,(char*)segment,20); // then the TCP segment
/* Calculate checksum just like IP checksum */
ptr = (unsigned long long*)buffer; // convert buffer
hdr = *ptr; // dereference for clarity in following clode
for (int i = 0; i < 16; i++) { // 32 bytes -> 256 bits / 16 bits = 16 words
sum += (hdr & mask); // apply mask to header and add to sum
hdr >>= 16; // shift the next 16 bits
}
sum = ~sum; // bitwise NOT operation
return sum;
};
Here's all of the functions I stated above put together:
int tcp_scan(char* ipaddr,unsigned int port,FLAGS f,unsigned int justsend) {
/* Do a TCP port scan */
u_char* buffer;
u_char recvbuf[65535];
u_char* ipbuf;
u_char* tcpbuf;
int s;
size_t bufsize;
size_t size;
struct sockaddr_in sa;
struct sockaddr_in recvstruct;
struct msghdr msg;
struct iovec iv[1];
struct iphdr* iph;
struct tcphdr* tcph;
FLAGS rst = RST;
srand((unsigned int)time(NULL)); // seed random number generator
bufsize = sizeof(struct tcphdr) + sizeof(struct iphdr); // store size in variable
buffer = malloc(bufsize); // allocate memory to buffer
iph = assemble_ip(ipaddr,IPPROTO_TCP); // set the ip address to provided and protocol as TCP
tcph = assemble_tcp(rand() % 65536,port,f); // set flag, source port as rand, and dest port as supplied port num
if (iph == NULL || tcph == NULL || buffer == NULL) { // if error occurs
err();
/* Deallocate memory to variables that still have it */
if (iph != NULL) free(iph);
if (tcph != NULL) free(tcph);
if (buffer != NULL) free(buffer);
return -1;
}
/* Now compute checksum */
iph->check = htons(ip_checksum(iph));
tcph->check = htons(tcp_checksum(tcph,iph));
/* Store headers in buffer */
ipbuf = (u_char*)iph;
tcpbuf = (u_char*)tcph;
/* Concactenate to buffer */
strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
strncat((char*)buffer,(char*)ipbuf,20); // do same thing but with ip header
/* Create a socket */
s = create_socket(); // create a raw socket
if (s == -1) return -1; // if the socket wasn't able to be created
/* Clear memory */
bzero(&sa,sizeof(struct sockaddr_in));
bzero(&recvstruct,sizeof(struct sockaddr_in));
bzero(&msg,sizeof(struct msghdr));
bzero(&iv,sizeof(struct iovec));
/* For analyze_packet() */
sa.sin_family = AF_INET; // address family
sa.sin_addr.s_addr = inet_addr(ipaddr); // convert ip address
sa.sin_port = htons(port); // port number
/* For sendmsg() */
iv[0].iov_base = buffer;
iv[0].iov_len = bufsize;
msg.msg_name = &sa; // caller allocated buffer
msg.msg_namelen = sizeof(struct sockaddr_in); // specify size of buffer
msg.msg_iov = iv; // iov structure array
msg.msg_iovlen = 1; // the length of the array
msg.msg_control = NULL; // for ancillary data
msg.msg_controllen = 0; // sizeof ancillary data
if (sendmsg(s,&msg,0) == -1) {
err();
return -1;
}
printf("Sent\n");
if (justsend) return 0; // exit cleanly
bzero(&recvstruct,sizeof(struct sockaddr_in)); // clear structure
size = sizeof(struct sockaddr_in); // acquire size of recv structure
for(int i = 0; i < 100; i++) { // loop until we've received 100 packets
printf("Receiving\n");
bzero(recvbuf,65535); // clear memory
if (recvfrom(s,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&recvstruct,(socklen_t*)&size) == -1) { // recv
err();
return -1;
}
if (analyze_packet(recvbuf,sa,recvstruct) == 0) { // if packet is what we wanted
printf("\ttcp %d is open\n",port); // print out that port is opened
tcp_scan(ipaddr,port,rst,-1); // abort connection with RST flag
break;
}
}
return 0;
}
Alright, now that you've seen those, here's the 'tshark' command I used to sniff the packets:
sudo tshark -o tcp.check_checksum:TRUE # I also wanted to check the checksum value to make sure it was OK
Now here's the command to run the program:
sudo ./netmap enp0s3 # enp0s3 is the interface I'm sending packets on
After running both of these in separate terminals, tshark provides this output:
1 0.000000 10.0.2.15 -> 127.0.0.1 TCP 74 31280->8000 [<None>] Seq=1 Win=0, bogus TCP header length (0, must be 20)
Please note that the declarations for struct iphdr and struct tcphdr are located in the system header files <netinet/ip.h> and <netinet/tcp.h>, respectively.
I'm really lost as to how to solve this issue. In fact, I'm not certain what is causing the issue, in the first place. According to my knowledge there's no way to specify the length of the TCP header. Any help would be appreciated.
I think your problem is here
strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
strncat((char*)buffer,(char*)ipbuf,20);
The headers aren't strings so you may only be copying part of each header. Try something like this;
memcpy((char*)buffer, (char*)tcpbuf, 20);
memcpy((char*)buffer+20, (char*)ipbuf, 20);
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);
}