Lasaro Camargos camargol at
Tue Jul 19 09:56:16 GMT 2005

Hi all,

I've been playing with interception and re-injection of diverted  
packets but I am having some problems. The biggest is that I am not  
really using freebsd, but OSX. But I guess the problems are general  
enough to be answered here. If not, please ignore this message.

The situation is the following: as I didn't find any multicast router  
for OSX I was obliged to implement one. So I am diverting multicast  
packets from one interface to the other. Specifically, I divert  
incoming to a program that reinsert it back in the stack to be  
received by the router, and inserting a copy of it as outgoing on the  
other interface (I don't know why, but tee seems not to work). I am  
aware of possible security implications of this "solution", but it is  
going to be used in a very restricted environment.

By using to instances of my code I am able to forward multicast  
packets between two networks, and that is good.

BUT, when the router/forwarder machine (RFM) also send/receive  
multicast packets, I find some problems:
a multicast packet sent by RFM is diverted but, after reinserted,  
both original incoming and forced outgoing packets they simply vanish.
if the packet is not diverted, RFM receive it, but (of course) not  
the machines in the network to where the packets should be forwarded.

My code and more details of my environment are attached in the  
bottom. Any comment/pointer is really welcome.

Thank you all


/* **********

    This is a program that I use to forward multicast packets on OSX  
(should work on other BSDs). It will forward any packet diverted  
(teed) to the raw sockets it listens, though.

    To use it, create a rule in ipfw to divert packets like this:

    >> ipfw add divert 5000 from to recv en0

    this rule will divert any multicast packet
    comming from the network
    entering through the iface en0 (see 1)
    for multicast addresses in the range -
    to port 5000.

    If the router should also receive the packet, use tee instead of  
divert (see 2). Remember that a teed packet will be immediatelly  
accepted by ipfw. If you don't want this, you'll have to work on your  
rules (see 3).

    diverter 5000

#running like this, diverter will forward diverted packets to the  
network connected in the interface with address

Someday I will add the code to add the rule into ipfw from inside  
this program.

Open issues (and possible solutions) :
i) loops in the routing.
by now, specify both network and interface from where the packet is  
comming. The last example get rid of loops. (1)

ii) loopback delivery
have to setsockopt( , MULTICAST_LOOP , 0 ,); or something like that.

iii) sending multicast on two interfaces from the router.
ipfw add tee 5000 from IP_MULTICAST_INTERFACE to out

iv) tee not working
see next

v) security problem with tee
use divert in the rule and define __TEE__ to duplicate the packet  
inside this code. (3)

running example:

on the router

sudo ipfw add 9 skipto 11 udp from me to recv en0
sudo ipfw add 10 divert 10000 udp from to  
recv en0
sudo ipfw add 11 skipto 13 udp from me to recv en1
sudo ipfw add 12 divert 10001 udp from to  
recv en1

ipfw list

00009 skipto 11 udp from me to recv en0
00010 divert 10000 udp from to recv en0
00011 skipto 13 udp from me to recv en1
00012 divert 10001 udp from to recv en1
65535 allow ip from any to any

everything running except that packets from itself are not forwarded  
to net This is, of course,
because of rule 9.

But if I remove this rule, diverted packets are received by the  
router nor in the net The packets
are being reinserted but not delivered by the application.

I can't identify where the packet is being lost. Any idea of which  
log file should I look at?


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <arpa/inet.h>

#include <ifaddrs.h>

#define __DEBUG__
#define __TEE__
#define __IP_MULTICAST__

#define BUFSIZE 65535

int main(int argc, char ** argv) {
   int   sock_fd,

   struct sockaddr_in    bindPort,
   int sinlen = sizeof(struct sockaddr_in);

   unsigned char packet[BUFSIZE];
   struct ip *hdr;
   struct in_addr        out_iface;

   if (argc != 3) {
     fprintf(stderr, "This is a packet forwarder.\nAdd an \"in\"  
divert (or tee) rule to ipfw to port <port number>.\nPackets diverted  
will be outbounded on interface <out iface>. CAUTION!! Remember that  
by now \"tee\"d packets will be imediatelly accepted and that  
reinjected packets will be processed by the next ipfw rule, not the  
first.\n Usage: %s <port number> <out iface IP>\n error: %i\n", argv 
     exit (1);

   if(inet_aton(argv[2], &out_iface) != 1) {
     fprintf(stderr, "%s: wrong IP address param.\n", argv[0]);
     exit (1);

   //Create socket
   fprintf(stderr, "%s:creating a raw socket\n",argv[0]);
   sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
   if(sock_fd == -1) {
     fprintf(stderr, "%s: Unable to open divert socket (%i) \n", argv 
     switch(errno) {
       case EACCES: fprintf(stderr, "EACCESS\n"); break;
       case EMFILE: fprintf(stderr, "EMFILE\n"); break;
       case ENFILE: fprintf(stderr, "ENFILE\n"); break;
       case ENOBUFS: fprintf(stderr, "ENOBUFFS\n"); break;
       case EPROTONOSUPPORT: fprintf(stderr, "PROTONOSUPPORT\n"); break;

   //Bind on port
   fprintf(stderr, "%s: Binding a socket\n",argv[0]);
   bindPort.sin_family = AF_INET;
   bindPort.sin_port = atol(argv[1]);

   if( (ret = bind(sock_fd, (struct sockaddr *)&bindPort, sizeof 
(struct sockaddr_in))) != 0) {
     fprintf(stderr, "%s:Error executing bind():%s\n", argv 

#ifdef __IP_MULTICAST__
   fprintf(stderr, "%s: Setting IP MULTICAST interface to %s\n",argv 
   ret = setsockopt(sock_fd, IPPROTO_IP, IP_MULTICAST_IF, &out_iface,  

#ifdef __DEBUG__
   if(ret < 0)
     fprintf(stderr,"%s: set_interface returns %i with errno %i: %s 
\n", argv[0], ret, errno, strerror(errno));
#endif //__DEBUG__
#endif //__IP_MULTICAST__

   /* Wait for packets. */
   fprintf(stderr,"\%s:Waiting for data.", argv[0]);
   while(1) {
     /* Capture */
     data_len = recvfrom(sock_fd, packet, BUFSIZE, 0,  (struct  
sockaddr *)&sin, (socklen_t *)&sinlen);
     hdr = (struct ip*) packet;

#ifdef __DEBUG__
     fprintf(stderr,"%s:The packet looks like this:\n\t", argv[0]);
     for( i = 0; i < data_len; i++) {
       fprintf(stderr,"%02x ", (int)*(packet+i));
       if(!((i+1) %16)) fprintf(stderr,"\n\t");

     for( i = 28; i < data_len; i++) {
       fprintf(stderr,"%c", (char)*(packet+i));
       if(!((i-28+1) %16)) fprintf(stderr,"\n\t");


#ifdef __DEBUG__
     fprintf(stderr,"%s: Source address %s\n",      argv[0], inet_ntoa 
     fprintf(stderr,"%s: Destination address %s\n", argv[0], inet_ntoa 
     fprintf(stderr,"%s: Receiving IF address %s\n",argv[0], inet_ntoa 
     fprintf(stderr,"%s: Protocol Number %i\n",     argv[0], hdr->ip_p);

     /* Reinjection */
#ifdef __DEBUG__
     if(IN_MULTICAST((ntohl(hdr->ip_dst.s_addr)))) {
       fprintf(stderr,"\%s: Multicast address!\n", argv[0]);

#ifdef __TEE__ //In the case the ipfw do not tee the packet properly.
#ifdef __DEBUG__
     fprintf(stderr,"%s: Reinjecting diverted packet %i bytes\n", argv 
[0], data_len);

     data_len = sendto(sock_fd, packet, data_len, 0, (struct sockaddr  
*)&sin, sinlen);

#ifdef __DEBUG__
     fprintf(stderr,"%s: %i bytes reinjected.\n", argv[0], data_len);
#endif //__TEE__

     //Turn it into an outgoing package. If it already was, it's your  
     sin.sin_addr.s_addr = INADDR_ANY;

#ifdef __DEBUG__
     fprintf(stderr,"%s: Reinjecting diverted packet %i bytes\n", argv 
[0], data_len);

     data_len = sendto(sock_fd, packet, data_len, 0, (struct sockaddr  
*)&sin, sinlen);

#ifdef __DEBUG__
     fprintf(stderr,"%s: %i bytes reinjected.\n", argv[0], data_len);

#ifdef __DEBUG__
     if (data_len <= 0) fprintf(stderr,"%s errno = %i\n", argv[0],  
     switch(errno) {
       //case EBADRQC:     printf("errno = EBADRQC"); break;
       case ENETUNREACH: fprintf(stderr,"errno = ENETUNREACH"); break;
       default : break;

More information about the freebsd-ipfw mailing list