Added Lanman announce patch from Jacco de Leeuw <leeuw@wins.uva.nl>.
authorJeremy Allison <jra@samba.org>
Tue, 16 Dec 1997 09:20:34 +0000 (09:20 +0000)
committerJeremy Allison <jra@samba.org>
Tue, 16 Dec 1997 09:20:34 +0000 (09:20 +0000)
Also added code to stop old Samba servers that announce the workgroup
name as master browser name when they are a local master browser.
Jeremy.

source/include/nameserv.h
source/include/proto.h
source/nmbd/nmbd.c
source/nmbd/nmbd_become_lmb.c
source/nmbd/nmbd_incomingdgrams.c
source/nmbd/nmbd_packets.c
source/nmbd/nmbd_processlogon.c
source/nmbd/nmbd_sendannounce.c
source/param/loadparm.c

index 4b7216fef6f7c4c8db31b12e6b107883fb8a7c96..98a6cb330ac215745466faa79e9c33bbf1ed0c4d 100644 (file)
@@ -122,6 +122,7 @@ enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH,
 #define BROWSE_MAILSLOT    "\\MAILSLOT\\BROWSE"
 #define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON"
 #define NT_LOGON_MAILSLOT  "\\MAILSLOT\\NET\\NTLOGON"
+#define LANMAN_MAILSLOT    "\\MAILSLOT\\LANMAN"
 
 /* Samba definitions for find_name_on_subnet(). */
 #define FIND_ANY_NAME   0
index b249c9cb2031243691bbb0666dd28d28dd982917..a3fd1d1f0f25f95e5f13fa19fa933049f34de3dd 100644 (file)
@@ -298,6 +298,8 @@ int lp_lpqcachetime(void);
 int lp_syslog(void);
 int lp_client_code_page(void);
 int lp_announce_as(void);
+int lp_lm_announce(void);
+int lp_lm_interval(void);
 char *lp_preexec(int );
 char *lp_postexec(int );
 char *lp_rootpreexec(int );
@@ -485,6 +487,7 @@ void unbecome_local_master_fail(struct subnet_record *subrec, struct response_re
 void release_1d_name( struct subnet_record *subrec, char *workgroup_name);
 void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work);
 void become_local_master_browser(struct subnet_record *subrec, struct work_record *work);
+void set_workgroup_local_master_browser_name( struct work_record *work, char *newname);
 
 /*The following definitions come from  nmbd_browserdb.c  */
 
@@ -517,11 +520,13 @@ void process_workgroup_announce(struct subnet_record *subrec, struct packet_stru
 void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf);
 void process_master_browser_announce(struct subnet_record *subrec, 
                                      struct packet_struct *p,char *buf);
+void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf);
 void process_get_backup_list_request(struct subnet_record *subrec,
                                      struct packet_struct *p,char *buf);
 void process_reset_browser(struct subnet_record *subrec,
                                   struct packet_struct *p,char *buf);
 void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf);
+void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf);
 
 /*The following definitions come from  nmbd_incomingrequests.c  */
 
@@ -670,6 +675,7 @@ void reply_netbios_packet(struct packet_struct *orig_packet,
                           int ttl, char *data,int len);
 void queue_packet(struct packet_struct *packet);
 void process_browse_packet(struct packet_struct *p, char *buf,int len);
+void process_lanman_packet(struct packet_struct *p, char *buf,int len);
 BOOL validate_nmb_response_packet( struct nmb_packet *nmb );
 BOOL validate_nmb_packet( struct nmb_packet *nmb );
 void run_packet_queue();
@@ -706,6 +712,7 @@ struct response_record *find_response_record(struct subnet_record **ppsubrec,
 void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip);
 void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work);
 void announce_my_server_names(time_t t);
+void announce_my_lm_server_names(time_t t);
 void reset_announce_timer();
 void announce_myself_to_domain_master_browser(time_t t);
 void announce_my_servers_removed(void);
index 11cd50cd76f8732f940847ba755f0ea3263f3b37..86f01d8e795e0197b4c0d2b4080c2f5519fdf6be 100644 (file)
@@ -47,6 +47,9 @@ extern char **my_netbios_names;
 /* are we running as a daemon ? */
 static BOOL is_daemon = False;
 
+/* have we found LanMan clients yet? */
+BOOL found_lm_clients = False;
+
 /* what server type are we currently */
 
 time_t StartupTime = 0;
@@ -288,6 +291,13 @@ static void process(void)
      */
     announce_my_server_names(t);
 
+    /*
+     * Send out any LanMan broadcast announcements
+     * of our server names.
+     * (nmbd_sendannounce.c)
+     */
+    announce_my_lm_server_names(t);
+
     /*
      * If we are a local master browser, periodically
      * announce ourselves to the domain master browser.
index 7f54471a24155ad0778f8826ed06ea1aeff374a6..6496a2f9e5802f0e4f0434b3fe90b6a78993f7c8 100644 (file)
@@ -115,7 +115,7 @@ in workgroup %s on subnet %s\n",
   /* Forget who the local master browser was for
      this workgroup. */
 
-  *work->local_master_browser_name = '\0';
+  set_workgroup_local_master_browser_name( work, "");
 
   /*
    * Ensure the IP address of this subnet is not registered as one
@@ -333,8 +333,7 @@ on subnet %s\n", work->work_group, subrec->subnet_name));
   subrec->work_changed = True;
 
   /* Add this name to the workgroup as local master browser. */
-  StrnCpy(work->local_master_browser_name, myname,
-            sizeof(work->local_master_browser_name)-1);
+  set_workgroup_local_master_browser_name( work, myname);
 
   /* Count the number of servers we have on our list. If it's
      less than 10 (just a heuristic) request the servers
@@ -532,3 +531,27 @@ in workgroup %s on subnet %s\n",
                 become_local_master_fail1,
                 userdata);
 }
+
+/***************************************************************
+ Utility function to set the local master browser name. Does
+ some sanity checking as old versions of Samba seem to sometimes
+ say that the master browser name for a workgroup is the same
+ as the workgroup name.
+****************************************************************/
+
+void set_workgroup_local_master_browser_name( struct work_record *work, char *newname)
+{
+  DEBUG(5,("set_workgroup_local_master_browser_name: setting local master name to '%s' \
+for workgroup %s.\n", newname, work->work_group ));
+
+  if(strequal( work->work_group, newname))
+  {
+    DEBUG(5, ("set_workgroup_local_master_browser_name: Refusing to set \
+local_master_browser_name for workgroup %s to workgroup name.\n",
+         work->work_group ));
+    return;
+  }
+
+  StrnCpy(work->local_master_browser_name, newname,
+            sizeof(work->local_master_browser_name)-1);
+}
index d43b1369e6471b9e5d56b5dc0aef8e398833a0d9..452516b64ee1e49a9a4621e1077b4b5e0df23b04 100644 (file)
@@ -28,6 +28,7 @@ extern int DEBUGLEVEL;
 
 extern pstring myname;
 extern fstring myworkgroup;
+extern BOOL found_lm_clients;
 
 #if 0
 
@@ -223,9 +224,7 @@ void process_workgroup_announce(struct subnet_record *subrec, struct packet_stru
   if(*work->local_master_browser_name == '\0')
   {
     /* Set the master browser name. */
-    StrnCpy(work->local_master_browser_name, master_name,
-            sizeof(work->local_master_browser_name)-1);
-
+    set_workgroup_local_master_browser_name( work, master_name );
   }
 
   subrec->work_changed = True;
@@ -324,9 +323,7 @@ a local master browser for workgroup %s and we think we are master. Forcing elec
     StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
   }
 
-  /* Set the master browser name. */
-  StrnCpy(work->local_master_browser_name, server_name,
-            sizeof(work->local_master_browser_name)-1);
+  set_workgroup_local_master_browser_name( work, server_name );
 
   subrec->work_changed = True;
 }
@@ -384,6 +381,105 @@ master - ignoring master announce.\n"));
     update_browser_death_time(browrec);
 }
 
+/*******************************************************************
+  Process an incoming LanMan host announcement packet.
+*******************************************************************/
+
+void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  uint32 servertype = IVAL(buf,1);
+  int osmajor=CVAL(buf,5);           /* major version of node software */
+  int osminor=CVAL(buf,6);           /* minor version of node software */
+  int ttl = SVAL(buf,7);
+  char *announce_name = buf+9;
+  struct work_record *work;
+  struct server_record *servrec;
+  char *work_name;
+  char *source_name = dgram->source_name.name;
+  pstring comment;
+  char *s = buf+9;
+
+  s = skip_string(s,1);
+  StrnCpy(comment, s, 43);
+
+  DEBUG(3,("process_lm_host_announce: LM Announcement from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+              namestr(&dgram->dest_name),announce_name));
+
+  DEBUG(5,("process_lm_host_announce: os=(%d,%d) ttl=%d server type=%08x comment=%s\n",
+          osmajor, osminor, ttl, servertype,comment));
+
+  if ((osmajor < 36) || (osmajor > 38) || (osminor !=0))
+  {
+    DEBUG(5,("process_lm_host_announce: LM Announcement packet does not " \
+             "originate from OS/2 Warp client. Ignoring packet.\n"));
+    /* Could have been from a Windows machine (with its LM Announce enabled),
+       or a Samba server. Then don't disrupt the current browse list. */
+    return;
+  }
+
+  /* Filter servertype to remove impossible bits. */
+  servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+  /* A LanMan host announcement must be sent to the name WORKGROUP<00>. */
+  if(dgram->dest_name.name_type != 0x00)
+  {
+    DEBUG(2,("process_lm_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x00. Allowing packet anyway.\n",
+              inet_ntoa(p->ip), dgram->dest_name.name_type));
+    /* Change it so it was. */
+    dgram->dest_name.name_type = 0x00;
+  }
+
+  /* For a LanMan host announce the workgroup name is the destination name. */
+  work_name = dgram->dest_name.name;
+
+  /*
+   * Syntax servers version 5.1 send HostAnnounce packets to
+   * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+   * instead of WORKGROUP<1d> name. So to fix this we check if
+   * the workgroup name is our own name, and if so change it
+   * to be our primary workgroup name. This code is probably
+   * not needed in the LanMan announce code, but it won't hurt.
+   */
+
+  if(strequal(work_name, myname))
+    work_name = myworkgroup;
+
+  /*
+   * We are being very agressive here in adding a workgroup
+   * name on the basis of a host announcing itself as being
+   * in that workgroup. Maybe we should wait for the workgroup
+   * announce instead ? JRA.
+   */
+
+  if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL)
+  {
+    /* We have no record of this workgroup. Add it. */
+    if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+      return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, announce_name))==NULL)
+  {
+    /* If this server is not already in the workgroup, add it. */
+    create_server_on_workgroup(work, announce_name,
+                               servertype|SV_TYPE_LOCAL_LIST_ONLY,
+                               ttl, comment);
+  }
+  else
+  {
+    /* Update the record. */
+    servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+    update_server_ttl( servrec, ttl);
+    StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+  }
+
+  subrec->work_changed = True;
+  found_lm_clients = True;
+}
+
 /****************************************************************************
   Send a backup list response.
 *****************************************************************************/
@@ -600,12 +696,12 @@ request from %s IP %s state=0x%X\n",
 }
 
 /*******************************************************************
-  Process a announcement request packet.
+  Process an announcement request packet.
   We don't respond immediately, we just check it's a request for
-  out workgroup and then set the flag telling the announce code
+  our workgroup and then set the flag telling the announce code
   in nmbd_sendannounce.c:announce_my_server_names that an 
   announcement is needed soon.
-  ******************************************************************/
+******************************************************************/
 
 void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
 {
@@ -634,3 +730,40 @@ void process_announce_request(struct subnet_record *subrec, struct packet_struct
 
   work->needannounce = True;
 }
+
+/*******************************************************************
+  Process a LanMan announcement request packet.
+  We don't respond immediately, we just check it's a request for
+  our workgroup and then set the flag telling that we have found
+  a LanMan client (DOS or OS/2) and that we will have to start
+  sending LanMan announcements (unless specifically disabled
+  through the "lm_announce" parameter in smb.conf)
+******************************************************************/
+
+void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  struct work_record *work;
+  char *workgroup_name = dgram->dest_name.name;
+
+  DEBUG(3,("process_lm_announce_request: Announce request from %s IP %s to %s.\n",
+           namestr(&dgram->source_name), inet_ntoa(p->ip),
+           namestr(&dgram->dest_name)));
+
+  /* We only send announcement requests on our workgroup. */
+  if(strequal(workgroup_name, myworkgroup) == False)
+  {
+    DEBUG(7,("process_lm_announce_request: Ignoring announce request for workgroup %s.\n",
+           workgroup_name));
+    return;
+  }
+
+  if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+  {
+    DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+            workgroup_name));
+    return;
+  }
+
+  found_lm_clients = True;
+}
index 43249cc0a3387f3470c9a9bedbaab08afa394759..4fb05439673afb3bae907cb426a0c36bac230a3b 100644 (file)
@@ -1023,6 +1023,56 @@ command code %d from %s IP %s to %s\n",
   } 
 }
 
+/****************************************************************************
+ Dispatch a LanMan browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+void process_lanman_packet(struct packet_struct *p, char *buf,int len)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int command = SVAL(buf,0);
+  struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+
+  /* Drop the packet if it's a different NetBIOS scope, or
+     the source is from one of our names. */
+
+  if (!strequal(dgram->dest_name.scope,scope ))
+  {
+    DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scope));
+    return;
+  }
+
+  if (is_myname(dgram->source_name.name))
+  {
+    DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name)));
+    return;
+  }
+
+  switch (command)
+  {
+    case ANN_HostAnnouncement:
+    {
+      debug_browse_data(buf, len);
+      process_lm_host_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_AnnouncementRequest:
+    {
+      process_lm_announce_request(subrec, p, buf+1);
+      break;
+    }
+    default:
+    {
+      DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n",
+            subrec->subnet_name, command, namestr(&dgram->source_name),
+            inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+    }
+  }
+}
+
 /****************************************************************************
   Determine if a packet is for us on port 138. Note that to have any chance of
   being efficient we need to drop as many packets as possible at this
@@ -1100,6 +1150,12 @@ static void process_dgram(struct packet_struct *p)
     return;
   }
 
+  /* Datagram packet received for the LAN Manager mailslot */
+  if (strequal(smb_buf(buf),LANMAN_MAILSLOT)) {
+    process_lanman_packet(p,buf2,len);
+    return;
+  }
+
   /* Datagram packet received for the domain logon mailslot */
   if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT))
   {
index ae917564fe2ed8fe374f7cd157ea18f741b3f759..cd2fbfd0a56fd7d1caac869052b1374050ea73cf 100644 (file)
@@ -63,7 +63,7 @@ void process_logon_packet(struct packet_struct *p,char *buf,int len,
 
   if (!lp_domain_logons())
   {
-    DEBUG(3,("process_logon_packet: Logon packet received from IP %S and domain \
+    DEBUG(3,("process_logon_packet: Logon packet received from IP %s and domain \
 logons are not enabled.\n", inet_ntoa(p->ip) ));
     return;
   }
index e4b288aea59babd747c29e5b2a5c8e12bd6753c4..aac3dad3662ed4c58c7fbc9ab922ff171c2792cd 100644 (file)
@@ -32,6 +32,7 @@ extern pstring myname;
 extern fstring myworkgroup;
 extern char **my_netbios_names;
 extern int  updatecount;
+extern BOOL found_lm_clients;
 
 /****************************************************************************
  Send a browser reset packet.
@@ -126,6 +127,37 @@ static void send_announcement(struct subnet_record *subrec, int announce_type,
                 from_name, 0x0, to_name, to_type, to_ip, subrec->myip);
 }
 
+/****************************************************************************
+  Broadcast a LanMan announcement.
+**************************************************************************/
+
+static void send_lm_announcement(struct subnet_record *subrec, int announce_type,
+                              char *from_name, char *to_name, int to_type, struct in_addr to_ip,
+                              time_t announce_interval,
+                              char *server_name, int server_type, char *server_comment)
+{
+  pstring outbuf;
+  char *p=outbuf;
+
+  bzero(outbuf,sizeof(outbuf));
+
+  SSVAL(p,0,announce_type);
+  SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+  CVAL(p,6) = lp_major_announce_version(); /* Major version. */
+  CVAL(p,7) = lp_minor_announce_version(); /* Minor version. */
+  SSVAL(p,8,announce_interval);            /* In seconds - according to spec. */
+
+  p += 10;
+  StrnCpy(p,server_name,15);
+  strupper(p);
+  p = skip_string(p,1);
+  pstrcpy(p,server_comment);
+  p = skip_string(p,1);
+
+  send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+                from_name, 0x0, to_name, to_type, to_ip, subrec->myip);
+}
+
 /****************************************************************************
  We are a local master browser. Announce this to WORKGROUP<1e>.
 ****************************************************************************/
@@ -191,6 +223,29 @@ static void send_host_announcement(struct subnet_record *subrec, struct work_rec
                     servrec->serv.comment);
 }
 
+/****************************************************************************
+ Announce the given LanMan host
+****************************************************************************/
+
+static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work,
+                                   struct server_record *servrec, int lm_interval)
+{
+  /* Ensure we don't have the prohibited bits set. */
+  uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+  DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n",
+            type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval));
+
+  send_lm_announcement(subrec, ANN_HostAnnouncement,
+                    servrec->serv.name,              /* From nbt name. */
+                    work->work_group, 0x00,          /* To nbt name. */
+                    subrec->bcast_ip,                /* To ip. */
+                    lm_interval,                     /* Time until next announce. */
+                    servrec->serv.name,              /* Name to announce. */
+                    type,                            /* Type field. */
+                    servrec->serv.comment);
+}
+
 /****************************************************************************
   Announce a server record.
   ****************************************************************************/
@@ -258,6 +313,56 @@ void announce_my_server_names(time_t t)
   } /* for subrec */
 }
 
+/****************************************************************************
+  Go through all my registered names on all broadcast subnets and announce
+  them as a LanMan server if the timeout requires it.
+**************************************************************************/
+
+void announce_my_lm_server_names(time_t t)
+{
+  struct subnet_record *subrec;
+  static time_t last_lm_announce_time=0;
+  int announce_interval = lp_lm_interval();
+  int lm_announce = lp_lm_announce();
+
+  if ((announce_interval <= 0) || (lm_announce <= 0))
+  {
+    /* user absolutely does not want LM announcements to be sent. */
+    return;
+  }
+
+  if ((lm_announce >= 2) && (!found_lm_clients))
+  {
+    /* has been set to 2 (Auto) but no LM clients detected (yet). */
+    return;
+  }
+
+  /* Otherwise: must have been set to 1 (Yes), or LM clients *have*
+     been detected. */
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup);
+
+    if(work)
+    {
+      struct server_record *servrec;
+
+      if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval ))
+        continue;
+
+      last_lm_announce_time = t;
+
+      for (servrec = work->serverlist; servrec; servrec = servrec->next)
+      {
+        if (is_myname(servrec->serv.name))
+          /* skipping equivalent of announce_server() */
+          send_lm_host_announcement(subrec, work, servrec, announce_interval);
+      }
+    } /* if work */
+  } /* for subrec */
+}
+
 /* Announce timer. Moved into global static so it can be reset
    when a machine becomes a local master browser. */
 static time_t announce_timer_last=0;
@@ -342,6 +447,7 @@ void announce_my_servers_removed(void)
         if(AM_LOCAL_MASTER_BROWSER(work))
           send_local_master_announcement(subrec, work, servrec);
         send_host_announcement(subrec, work, servrec);
+        send_lm_host_announcement(subrec, work, servrec, 0);
       }
     }
   }
index 76618e9a79100bceafdaedbe2e39e07deff72373..277430ecc44cd2efa5ca731346a506ea776d779c 100644 (file)
@@ -160,6 +160,8 @@ typedef struct
   int max_wins_ttl;
   int min_wins_ttl;
   int ReadSize;
+  int lm_announce;
+  int lm_interval;
   int shmem_size;
   int client_code_page;
   int announce_as;   /* This is initialised in init_globals */
@@ -409,6 +411,8 @@ static struct enum_list enum_announce_as[] = {{ANNOUNCE_AS_NT, "NT"}, {ANNOUNCE_
 
 static struct enum_list enum_case[] = {{CASE_LOWER, "lower"}, {CASE_UPPER, "upper"}, {-1, NULL}};
 
+static struct enum_list enum_lm_announce[] = {{0, "False"}, {1, "True"}, {2, "Auto"}};
+
 static struct parm_struct
 {
        char *label;
@@ -507,6 +511,8 @@ static struct parm_struct
   {"max ttl",          P_INTEGER, P_GLOBAL, &Globals.max_ttl,           NULL,   NULL},
   {"max wins ttl",     P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl,      NULL,   NULL},
   {"min wins ttl",     P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl,      NULL,   NULL},
+  {"lm announce",      P_ENUM,    P_GLOBAL, &Globals.lm_announce,       NULL,   enum_lm_announce},
+  {"lm interval",      P_INTEGER, P_GLOBAL, &Globals.lm_interval,       NULL,   NULL},
   {"dns proxy",        P_BOOL,    P_GLOBAL, &Globals.bDNSproxy,         NULL,   NULL},
   {"wins support",     P_BOOL,    P_GLOBAL, &Globals.bWINSsupport,      NULL,   NULL},
   {"wins proxy",       P_BOOL,    P_GLOBAL, &Globals.bWINSproxy,        NULL,   NULL},
@@ -699,6 +705,8 @@ static void init_globals(void)
   Globals.max_wins_ttl = 60*60*24*3; /* 3 days default */
   Globals.min_wins_ttl = 60*60*6; /* 6 hours default */
   Globals.ReadSize = 16*1024;
+  Globals.lm_announce = 2;   /* = Auto: send only if LM clients found */
+  Globals.lm_interval = 60;
   Globals.shmem_size = SHMEM_SIZE;
   Globals.announce_as = ANNOUNCE_AS_NT;
   Globals.bUnixRealname = False;
@@ -947,6 +955,8 @@ FN_GLOBAL_INTEGER(lp_lpqcachetime,&Globals.lpqcachetime)
 FN_GLOBAL_INTEGER(lp_syslog,&Globals.syslog)
 FN_GLOBAL_INTEGER(lp_client_code_page,&Globals.client_code_page)
 FN_GLOBAL_INTEGER(lp_announce_as,&Globals.announce_as)
+FN_GLOBAL_INTEGER(lp_lm_announce,&Globals.lm_announce)
+FN_GLOBAL_INTEGER(lp_lm_interval,&Globals.lm_interval)
 
 FN_LOCAL_STRING(lp_preexec,szPreExec)
 FN_LOCAL_STRING(lp_postexec,szPostExec)