modified become_master() to a state-based system. becoming a master
[samba.git] / source / nameelect.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    NBT netbios routines and daemon - version 2
5    Copyright (C) Andrew Tridgell 1994-1995
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21    Revision History:
22
23    14 jan 96: lkcl@pires.co.uk
24    added multiple workgroup domain master support
25
26 */
27
28 #include "includes.h"
29
30 extern int ClientNMB;
31 extern int ClientDGRAM;
32
33 extern int DEBUGLEVEL;
34 extern pstring scope;
35
36 extern pstring myname;
37 extern struct in_addr ipzero;
38
39 /* machine comment for host announcements */
40 extern  pstring ServerComment;
41
42 /* here are my election parameters */
43
44 extern time_t StartupTime;
45
46 extern struct subnet_record *subnetlist;
47
48
49 /*******************************************************************
50   occasionally check to see if the master browser is around
51   ******************************************************************/
52 void check_master_browser(void)
53 {
54   static time_t lastrun=0;
55   time_t t = time(NULL);
56   struct subnet_record *d;
57
58   if (!lastrun) lastrun = t;
59   if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
60     return;
61   lastrun = t;
62
63   dump_workgroups();
64
65   for (d = subnetlist; d; d = d->next)
66     {
67       struct work_record *work;
68
69       for (work = d->workgrouplist; work; work = work->next)
70         {
71           /* if we are not the browse master of a workgroup, and we can't
72              find a browser on the subnet, do something about it. */
73
74           if (!AM_MASTER(work))
75             {
76               queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
77                                    work->work_group,0x1d,0,0,
78                                    True,False,d->bcast_ip);
79             }
80         }
81     }
82 }
83
84
85 /*******************************************************************
86   what to do if a master browser DOESN't exist
87   ******************************************************************/
88 void browser_gone(char *work_name, struct in_addr ip)
89 {
90   struct subnet_record *d = find_subnet(ip);
91   struct work_record *work = find_workgroupstruct(d, work_name, False);
92
93   if (!work || !d) return;
94
95   if (strequal(work->work_group, lp_workgroup()) &&
96       ismybcast(d->bcast_ip))
97     {
98
99       DEBUG(2,("Forcing election on %s %s\n",
100                work->work_group,inet_ntoa(d->bcast_ip)));
101
102       /* we can attempt to become master browser */
103       work->needelection = True;
104     }
105   else
106     {
107       /* XXXX note: this will delete entries that have been added in by
108          lmhosts as well. a flag to ensure that these are not deleted may
109          be considered */
110       
111       /* workgroup with no master browser is not the default workgroup:
112          it's also not on our subnet. therefore delete it: it can be
113          recreated dynamically */
114       
115       send_election(d, work->work_group, 0, 0, myname);
116       remove_workgroup(d, work);      
117     }
118 }
119
120
121 /****************************************************************************
122   send an election packet
123   **************************************************************************/
124 void send_election(struct subnet_record *d, char *group,uint32 criterion,
125                    int timeup,char *name)
126 {
127   pstring outbuf;
128   char *p;
129
130   if (!d) return;
131   
132   DEBUG(2,("Sending election to %s for workgroup %s\n",
133            inet_ntoa(d->bcast_ip),group));         
134
135   bzero(outbuf,sizeof(outbuf));
136   p = outbuf;
137   CVAL(p,0) = 8; /* election */
138   p++;
139
140   CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
141   SIVAL(p,1,criterion);
142   SIVAL(p,5,timeup*1000); /* ms - despite the spec */
143   p += 13;
144   strcpy(p,name);
145   strupper(p);
146   p = skip_string(p,1);
147   
148   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
149                       name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
150 }
151
152
153 /****************************************************************************
154   un-register a SELF name that got rejected.
155
156   if this name happens to be rejected when samba is in the process
157   of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
158   or WORKGROUP(1b)) then we must stop being a master browser. sad.
159
160   **************************************************************************/
161 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
162 {
163     struct work_record *work;
164
165     remove_netbios_name(d,name,name_type,SELF,ipzero);
166
167     if (!(work = find_workgroupstruct(d, name, False))) return;
168
169     if (special_browser_name(name, name_type) ||
170         (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
171          (name_type == 0x1d || name_type == 0x1b)))
172     {
173       int remove_type = 0;
174
175       if (special_browser_name(name, name_type))
176         remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
177       if (name_type == 0x1d)
178         remove_type = SV_TYPE_MASTER_BROWSER;
179       if (name_type == 0x1b)
180         remove_type = SV_TYPE_DOMAIN_MASTER;
181                         
182       become_nonmaster(d, work, remove_type);
183     }
184 }
185
186
187 /****************************************************************************
188   registers a name.
189
190   if the name being added is a SELF name, we must additionally check
191   whether to proceed to the next stage in samba becoming a master browser.
192
193   **************************************************************************/
194 void name_register_work(struct subnet_record *d, char *name, int name_type,
195                                 int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast)
196 {
197   enum name_source source = ismyip(ip) ? SELF : REGISTER;
198
199   if (source == SELF)
200   {
201     struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
202
203     if (work && work->state != MST_NONE)
204     {
205       /* samba is in the process of working towards master browser-ness.
206          initiate the next stage.
207        */
208       become_master(d, work);
209     }
210   }
211   add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
212 }
213
214
215 /*******************************************************************
216   become the master browser.
217
218   this is done in stages. note that this could take a while, 
219   particularly on a broadcast subnet, as we have to wait for
220   the implicit registration of each name to be accepted.
221
222   as each name is successfully registered, become_master() is
223   called again, in order to initiate the next stage. see
224   dead_netbios_entry() - deals with implicit name registration
225   and response_name_reg() - deals with explicit registration
226   with a WINS server.
227
228   stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
229   stage 2: was MST_WON  - go to MST_MSB  and register WORKGROUP(0x1d)
230   stage 3: was MST_MSB  - go to MST_BROWSER and register WORKGROUP(0x1b)
231   stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
232
233   XXXX note: this code still does not cope with the distinction
234   between different types of nodes, particularly between M and P
235   nodes. that comes later.
236
237   ******************************************************************/
238 void become_master(struct subnet_record *d, struct work_record *work)
239 {
240   uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX|0x00400000;
241
242   if (!work) return;
243   
244   DEBUG(2,("Becoming master for %s (stage %d)",work->work_group,work->state));
245   
246   switch (work->state)
247   {
248     case MST_NONE: /* while we were nothing but a server... */
249     {
250       work->state = MST_WON; /* election win was successful */
251
252       work->ElectionCriterion |= 0x5;
253
254       /* update our server status */
255       work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
256       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
257
258       DEBUG(2,("first stage: register ^1^2__MSBROWSE__^2^1\n"));
259
260       /* add special browser name */
261       add_my_name_entry(d,MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
262
263       break;
264     }
265     case MST_WON: /* while nothing had happened except we won an election... */
266     {
267       work->state = MST_MSB; /* registering MSBROWSE was successful */
268
269       /* add server entry on successful registration of MSBROWSE */
270       add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
271
272       DEBUG(2,("second stage: register as master browser\n"));
273
274       /* add master name */
275       add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE         );
276   
277       break;
278     }
279     case MST_MSB: /* while we were still only registered MSBROWSE state */
280     {
281       work->state = MST_BROWSER; /* registering WORKGROUP(1d) was successful */
282
283       /* update our server status */
284       work->ServerType |= SV_TYPE_MASTER_BROWSER;
285       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
286
287       if (d->my_interface && work->serverlist == NULL) /* no servers! */
288       {
289         /* ask all servers on our local net to announce to us */
290         announce_request(work, d->bcast_ip);
291       }
292
293       if (lp_domain_master())
294       {
295         DEBUG(2,("third stage: register as domain master\n"));
296         /* add domain master name */
297         add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE         );
298       }
299   
300       break;
301     }
302     case MST_BROWSER: /* while we were still a master browser... */
303     {
304       work->state = MST_DOMAIN; /* registering WORKGROUP(1b) was successful */
305
306       /* update our server status */
307       if (lp_domain_master())
308       {
309         work->ServerType |= SV_TYPE_DOMAIN_MASTER;
310       
311         if (lp_domain_logons())
312             {
313               work->ServerType |= SV_TYPE_DOMAIN_CTRL;
314               work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
315             }
316         DEBUG(2,("fourth stage: samba is now a domain master.\n"));
317         add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
318       }
319   
320       break;
321     }
322     case MST_DOMAIN:
323     {
324       /* nothing else to become, at the moment: we are top-dog. */
325       DEBUG(2,("fifth stage: there isn't one yet!\n"));
326       break;
327     }
328   }
329 }
330
331
332 /*******************************************************************
333   unbecome the master browser. initates removal of necessary netbios 
334   names, and tells the world that we are no longer a master browser.
335   ******************************************************************/
336 void become_nonmaster(struct subnet_record *d, struct work_record *work,
337                                 int remove_type)
338 {
339   int new_server_type = work->ServerType;
340
341   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
342   
343   /* can only remove master or domain types with this function */
344   remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
345
346   /* unbecome a master browser; unbecome a domain master, too :-( */
347   if (remove_type & SV_TYPE_MASTER_BROWSER)
348     remove_type |= SV_TYPE_DOMAIN_MASTER;
349
350   new_server_type &= ~remove_type;
351
352   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
353   {
354     /* no longer a master browser of any sort */
355
356         work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
357     work->ElectionCriterion &= ~0x4;
358     work->state = MST_NONE;
359
360         /* announce ourselves as no longer active as a master browser. */
361     announce_server(d, work, work->work_group, myname, 0, 0);
362     remove_name_entry(d,MSBROWSE        ,0x01);
363   }
364   
365   work->ServerType = new_server_type;
366
367   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
368   {
369     if (work->state == MST_DOMAIN)
370       work->state = MST_BROWSER;
371     remove_name_entry(d,work->work_group,0x1b);
372     
373   }
374
375   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
376   {
377     if (work->state >= MST_BROWSER)
378       work->state = MST_NONE;
379     remove_name_entry(d,work->work_group,0x1d);
380   }
381 }
382
383
384 /*******************************************************************
385   run the election
386   ******************************************************************/
387 void run_elections(void)
388 {
389   time_t t = time(NULL);
390   static time_t lastime = 0;
391   
392   struct subnet_record *d;
393   
394   /* send election packets once a second */
395   if (lastime && t-lastime <= 0) return;
396   
397   lastime = t;
398   
399   for (d = subnetlist; d; d = d->next)
400     {
401       struct work_record *work;
402       for (work = d->workgrouplist; work; work = work->next)
403         {
404           if (work->RunningElection)
405             {
406               send_election(d,work->work_group, work->ElectionCriterion,
407                             t-StartupTime,myname);
408               
409               if (work->ElectionCount++ >= 4)
410                 {
411                   /* I won! now what :-) */
412                   DEBUG(2,(">>> Won election on %s %s <<<\n",
413                            work->work_group,inet_ntoa(d->bcast_ip)));
414                   
415                   work->RunningElection = False;
416           work->state = MST_NONE;
417
418                   become_master(d, work);
419                 }
420             }
421         }
422     }
423 }
424
425
426 /*******************************************************************
427   work out if I win an election
428   ******************************************************************/
429 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
430                          int timeup,char *name)
431 {  
432   time_t t = time(NULL);
433   uint32 mycriterion;
434   if (version > ELECTION_VERSION) return(False);
435   if (version < ELECTION_VERSION) return(True);
436   
437   mycriterion = work->ElectionCriterion;
438
439   if (criterion > mycriterion) return(False);
440   if (criterion < mycriterion) return(True);
441
442   if (timeup > (t - StartupTime)) return(False);
443   if (timeup < (t - StartupTime)) return(True);
444
445   if (strcasecmp(myname,name) > 0) return(False);
446   
447   return(True);
448 }
449
450
451 /*******************************************************************
452   process a election packet
453
454   An election dynamically decides who will be the master. 
455   ******************************************************************/
456 void process_election(struct packet_struct *p,char *buf)
457 {
458   struct dgram_packet *dgram = &p->packet.dgram;
459   struct in_addr ip = dgram->header.source_ip;
460   struct subnet_record *d = find_subnet(ip);
461   int version = CVAL(buf,0);
462   uint32 criterion = IVAL(buf,1);
463   int timeup = IVAL(buf,5)/1000;
464   char *name = buf+13;
465   struct work_record *work;
466
467   if (!d) return;
468   
469   name[15] = 0;  
470
471   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
472            name,version,criterion,timeup));
473   
474   if (same_context(dgram)) return;
475   
476   for (work = d->workgrouplist; work; work = work->next)
477     {
478       if (listening_name(work, &dgram->dest_name) && 
479           strequal(work->work_group, lp_workgroup()) &&
480           d->my_interface)
481         {
482           if (win_election(work, version,criterion,timeup,name))
483             {
484               if (!work->RunningElection)
485                 {
486                   work->needelection = True;
487                   work->ElectionCount=0;
488           work->state = MST_NONE;
489                 }
490             }
491           else
492             {
493               work->needelection = False;
494               
495               if (work->RunningElection)
496                 {
497                   work->RunningElection = False;
498                   DEBUG(3,(">>> Lost election on %s %s <<<\n",
499                            work->work_group,inet_ntoa(d->bcast_ip)));
500                   
501                   /* if we are the master then remove our masterly names */
502                   if (AM_MASTER(work))
503                   {
504                       become_nonmaster(d, work,
505                                         SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
506                   }
507                 }
508             }
509         }
510     }
511 }
512
513
514 /****************************************************************************
515   checks whether a browser election is to be run on any workgroup
516   ***************************************************************************/
517 BOOL check_elections(void)
518 {
519   struct subnet_record *d;
520   BOOL run_any_election = False;
521
522   for (d = subnetlist; d; d = d->next)
523     {
524       struct work_record *work;
525       for (work = d->workgrouplist; work; work = work->next)
526         {
527           run_any_election |= work->RunningElection;
528           
529           if (work->needelection && !work->RunningElection)
530             {
531               DEBUG(3,(">>> Starting election on %s %s <<<\n",
532                        work->work_group,inet_ntoa(d->bcast_ip)));
533               work->ElectionCount = 0;
534               work->RunningElection = True;
535               work->needelection = False;
536             }
537         }
538     }
539   return run_any_election;
540 }
541