[Samba] Netbios name service forwarding.

James james at madingley.org
Thu Feb 20 22:26:08 GMT 2003


I wrote this little program to deal with the situation
where there are a number of workgroups on a number of
subnets with no WINS server [actually I couldn't get this
configuration to run with a WINS server - but that's
another story]

You run this program on machines bridging your subnets and
it listens for netbios nameserver packets and forwards
them. [Broadcast packets are sent on to other nets as
broadcast packets, the unicast replies are returned as
unicast replies] It also sends a copy of any netbios
traffic it sees to the local nmbd which will be running
on a different port.

So I have a machine running samba bridging 
eth0 192.168.42.0/24
eth1 192.168.48.0/24

I start nmbd with nmbd -D -p 138
and this program
nbnsfw 138 eth0 eth1

then my windows machines on each side can resolve names
without a WINS server and across multiple workgroups.

take care,

J.

-------------- next part --------------
/*
 * nbnsfw.c:
 *
 * Copyright (c) 2003 James McKenzie <james at fishsoup.dhs.org>,
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

static char rcsid[] =
  "$Id: nbnsfw.c,v 1.2 2003/02/20 22:00:46 root Exp root $";

/*
 * $Log: nbnsfw.c,v $
 * Revision 1.2  2003/02/20 22:00:46  root
 * #
 *
 * Revision 1.1  2003/02/20 21:54:22  root
 * Initial revision
 *
 */


#include <syslog.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <malloc.h>
#include <net/if.h>
#include <arpa/inet.h>


#define NETBIOSPORT 137

typedef struct iface_struct
{
  struct iface_struct *next;
  char *name;
  int fd;
  struct sockaddr_in me;
  struct sockaddr_in nmbd;
  struct sockaddr_in bcast;
  struct sockaddr_in addr_cache[0x10000];
}
 *iface;

iface ifs;


void
open_if (char *name, int nb_port, int dm_port)
{
  struct sockaddr_in me = { 0 };
  iface i = (iface) malloc (sizeof (struct iface_struct));

  int fd;
  int one = 1;
  int j;

  struct ifreq ifr = { 0 };

  bzero (i, sizeof (struct iface_struct));

  i->name = strdup (name);
  i->fd = socket (AF_INET, SOCK_DGRAM, 0);
  if (i->fd < 0)
    {
      syslog (LOG_ERR, "Can't open socket: %m");
      exit (1);
    }

  strcpy (&ifr.ifr_name[0], name);
  if (setsockopt (i->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)))
    {
      syslog (LOG_ERR, "Can't bind to interface %s: %m", name);
      exit (1);
    }


  bzero (&ifr, sizeof (ifr));
  strcpy (&ifr.ifr_name[0], name);
  if (ioctl (i->fd, SIOCGIFADDR, &ifr))
    {
      syslog (LOG_ERR, "Can't get ip address for interface %s: %m", name);
      exit (1);
    }
  bcopy (&ifr.ifr_addr, &i->me, sizeof (struct sockaddr_in));
  i->me.sin_port = htons (nb_port);

  bzero (&ifr, sizeof (ifr));
  strcpy (&ifr.ifr_name[0], name);
  if (ioctl (i->fd, SIOCGIFBRDADDR, &ifr))
    {
      syslog (LOG_ERR, "Can't get broadcast address for interface %s: %m",
              name);
      exit (1);
    }
  bcopy (&ifr.ifr_addr, &i->bcast, sizeof (struct sockaddr_in));
  i->bcast.sin_port = htons (nb_port);


  me.sin_addr.s_addr = INADDR_ANY;
  me.sin_port = htons (nb_port);

  if (bind (i->fd, (struct sockaddr *) &me, sizeof (struct sockaddr_in)) < 00)
    {
      syslog (LOG_ERR, "Can't bind to port %d on interface %s: %m",
              nb_port, name);
      exit (1);
    }

  if (setsockopt (i->fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof (one)))
    {
      syslog (LOG_ERR, "Can't enable broadcasts on interface %s: %m", name);
      exit (1);
    }

  i->nmbd = i->me;
  i->nmbd.sin_port = htons (dm_port);

  for (j = 0; j < 0x10000; ++j)
    {
      i->addr_cache[j].sin_addr.s_addr = INADDR_BROADCAST;
      i->addr_cache[j].sin_port = htons (nb_port);
    }

  {
    char buf[1024], *ptr = buf;

    ptr +=
      sprintf (ptr, "Initialized %s ip %s", name, inet_ntoa (i->me.sin_addr));
    ptr += sprintf (ptr, " bcast %s", inet_ntoa (i->bcast.sin_addr));

    syslog (LOG_ERR, ptr);
  }


  i->next = ifs;
  ifs = i;
}


int
main (int argc, char **argv)
{
  unsigned char buf[8192];
  int w = getdtablesize ();
  int len;
  int fromlen;
  iface i, j;
  struct sockaddr_in from;
  fd_set rfds;
  int tid;
  int bcast;
  int dmport;

  if (argc < 3)
    {
      fprintf (stderr, "Usage:\n");
      fprintf (stderr, "%s nmbdport interface [interface] [interface] ...\n");
      exit (1);
    }

  openlog ("nbnsfw", LOG_CONS, LOG_DAEMON);

  daemon (0, 0);
  setsid ();

  argc--;
  argv++;

  dmport = atoi (*(argv++));
  argc--;

  while (argc--)
    {
      open_if (*(argv++), NETBIOSPORT, dmport);
    }

  syslog (LOG_ERR, "Local nmbd port is %d", dmport);


  for (;;)
    {
      FD_ZERO (&rfds);

      for (i = ifs; i; i = i->next)
        FD_SET (i->fd, &rfds);

      select (w, &rfds, 0, 0, NULL);

      for (i = ifs; i; i = i->next)
        {
          if (FD_ISSET (i->fd, &rfds))
            {
              int me = 0;

              bzero (&from, fromlen = sizeof (from));
              len =
                recvfrom (i->fd, buf, sizeof (buf), 0,
                          (struct sockaddr *) &from, &fromlen);
              if (!len)
                continue;
              if (fromlen != sizeof (struct sockaddr_in))
                continue;


              for (j = ifs; j; j = j->next)
                if (from.sin_addr.s_addr == j->me.sin_addr.s_addr)
                  me++;

              if (me)
                continue;


              tid = (buf[0] << 8) + buf[1];
              bcast = buf[3] & 0x10;
              i->addr_cache[tid] = from;

#if 0
              printf ("%s %s.%d T%04x %s ->\n",
                      i->name, inet_ntoa (from.sin_addr),
                      ntohs (from.sin_port), tid,
                      bcast ? "(BROAD)" : "       ");
#endif



              for (j = ifs; j; j = j->next)
                {
                  struct sockaddr_in to;


                  if (j == i)
                    continue;


                  if (bcast)
                    {
                      /*It's a broadcast packet send it out */
                      /*on the bcast address */
                      to = j->bcast;
                    }
                  else
                    {
                      /*It's unicast, look up the tid and send */
                      /*it to a responsible citizen */
                      to = j->addr_cache[tid];
                    }

#if 0
                  printf ("\t\t\t\t\t\t%s %s.%d\n", j->name,
                          inet_ntoa (to.sin_addr), ntohs (to.sin_port));
#endif

                  sendto (j->fd, buf, len, 0, (struct sockaddr *) &to,
                          sizeof (struct sockaddr_in));
                }

              /*Copy any packets we se to our poor belegured NMB */
              /*Deamon running on port whatever on the interface */
              /*the packet entered on */

              sendto (i->fd, buf, len, 0, (struct sockaddr *) &i->nmbd,
                      sizeof (struct sockaddr_in));


            }
        }
    }
}


More information about the samba mailing list