2 Unix SMB/Netbios implementation.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1996
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.
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.
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.
21 Module name: nameelect.c
25 14 jan 96: lkcl@pires.co.uk
26 added multiple workgroup domain master support
28 04 jul 96: lkcl@pires.co.uk
29 added system to become a master browser by stages.
37 extern int ClientDGRAM
;
39 extern int DEBUGLEVEL
;
42 extern pstring myname
;
43 extern struct in_addr ipzero
;
44 extern struct in_addr ipgrp
;
46 /* here are my election parameters */
48 extern time_t StartupTime
;
50 extern struct subnet_record
*subnetlist
;
52 extern uint16 nb_type
; /* samba's NetBIOS name type */
54 /*******************************************************************
55 occasionally check to see if the master browser is around
56 ******************************************************************/
57 void check_master_browser(time_t t
)
59 static time_t lastrun
=0;
60 struct subnet_record
*d
;
62 if (!lastrun
) lastrun
= t
;
63 if (t
< lastrun
+ CHECK_TIME_MST_BROWSE
* 60)
69 for (d
= subnetlist
; d
; d
= d
->next
)
71 struct work_record
*work
;
73 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
75 /* if we are not the browse master of a workgroup, and we can't
76 find a browser on the subnet, do something about it. */
80 queue_netbios_packet(d
,ClientNMB
,NMB_QUERY
,NAME_QUERY_MST_CHK
,
81 work
->work_group
,0x1d,0,0,0,NULL
,NULL
,
82 True
,False
,d
->bcast_ip
,d
->bcast_ip
);
89 /*******************************************************************
90 what to do if a master browser DOESN't exist
91 ******************************************************************/
92 void browser_gone(char *work_name
, struct in_addr ip
)
94 struct subnet_record
*d
= find_subnet(ip
);
95 struct work_record
*work
= find_workgroupstruct(d
, work_name
, False
);
97 /* i don't know about this workgroup, therefore i don't care */
98 if (!work
|| !d
) return;
100 /* don't do election stuff on the WINS subnet */
101 if (ip_equal(d
->bcast_ip
,ipgrp
))
104 if (strequal(work
->work_group
, lp_workgroup()))
107 DEBUG(2,("Forcing election on %s %s\n",
108 work
->work_group
,inet_ntoa(d
->bcast_ip
)));
110 /* we can attempt to become master browser */
111 work
->needelection
= True
;
115 /* local interfaces: force an election */
116 send_election(d
, work
->work_group
, 0, 0, myname
);
118 /* only removes workgroup completely on a local interface
119 persistent lmhosts entries on a local interface _will_ be removed).
121 remove_workgroup(d
, work
,True
);
126 /****************************************************************************
127 send an election packet
128 **************************************************************************/
129 void send_election(struct subnet_record
*d
, char *group
,uint32 criterion
,
130 int timeup
,char *name
)
137 DEBUG(2,("Sending election to %s for workgroup %s\n",
138 inet_ntoa(d
->bcast_ip
),group
));
140 bzero(outbuf
,sizeof(outbuf
));
142 CVAL(p
,0) = ANN_Election
; /* election */
145 CVAL(p
,0) = (criterion
== 0 && timeup
== 0) ? 0 : ELECTION_VERSION
;
146 SIVAL(p
,1,criterion
);
147 SIVAL(p
,5,timeup
*1000); /* ms - despite the spec */
151 p
= skip_string(p
,1);
153 send_mailslot_reply(BROWSE_MAILSLOT
,ClientDGRAM
,outbuf
,PTR_DIFF(p
,outbuf
),
154 name
,group
,0,0x1e,d
->bcast_ip
,*iface_ip(d
->bcast_ip
));
158 /****************************************************************************
159 un-register a SELF name that got rejected.
161 if this name happens to be rejected when samba is in the process
162 of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
163 or WORKGROUP(1b)) then we must stop being a master browser. sad.
165 **************************************************************************/
166 void name_unregister_work(struct subnet_record
*d
, char *name
, int name_type
)
168 struct work_record
*work
;
170 remove_netbios_name(d
,name
,name_type
,SELF
,ipzero
);
172 if (!(work
= find_workgroupstruct(d
, name
, False
))) return;
174 if (ms_browser_name(name
, name_type
) ||
175 (AM_MASTER(work
) && strequal(name
, lp_workgroup()) == 0 &&
176 (name_type
== 0x1d || name_type
== 0x1b)))
180 if (ms_browser_name(name
, name_type
))
181 remove_type
= SV_TYPE_MASTER_BROWSER
|SV_TYPE_DOMAIN_MASTER
;
182 if (name_type
== 0x1d)
183 remove_type
= SV_TYPE_MASTER_BROWSER
;
184 if (name_type
== 0x1b)
185 remove_type
= SV_TYPE_DOMAIN_MASTER
;
187 become_nonmaster(d
, work
, remove_type
);
192 /****************************************************************************
195 if the name being added is a SELF name, we must additionally check
196 whether to proceed to the next stage in samba becoming a master browser.
198 **************************************************************************/
199 void name_register_work(struct subnet_record
*d
, char *name
, int name_type
,
200 int nb_flags
, time_t ttl
, struct in_addr ip
, BOOL bcast
)
202 enum name_source source
= (ismyip(ip
) || ip_equal(ip
, ipzero
)) ?
207 struct work_record
*work
= find_workgroupstruct(d
, lp_workgroup(), False
);
209 add_netbios_entry(d
,name
,name_type
,nb_flags
,ttl
,source
,ip
,True
,!bcast
);
213 if (work
->state
!= MST_NONE
)
215 /* samba is in the process of working towards master browser-ness.
216 initiate the next stage.
218 become_master(d
, work
);
226 /*******************************************************************
227 become the master browser.
229 this is done in stages. note that this could take a while,
230 particularly on a broadcast subnet, as we have to wait for
231 the implicit registration of each name to be accepted.
233 as each name is successfully registered, become_master() is
234 called again, in order to initiate the next stage. see
235 dead_netbios_entry() - deals with implicit name registration
236 and response_name_reg() - deals with explicit registration
239 stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
240 stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d)
241 stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b)
242 stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
244 XXXX note: this code still does not cope with the distinction
245 between different types of nodes, particularly between M and P
246 nodes. that comes later.
248 ******************************************************************/
249 void become_master(struct subnet_record
*d
, struct work_record
*work
)
251 /* domain type must be limited to domain enum + server type. it must
252 not have SV_TYPE_SERVER or anything else with SERVER in it, else
253 clients get confused and start thinking this entry is a server
256 uint32 domain_type
= SV_TYPE_DOMAIN_ENUM
|SV_TYPE_NT
;
260 DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
261 work
->work_group
,inet_ntoa(d
->bcast_ip
),work
->state
));
265 case MST_NONE
: /* while we were nothing but a server... */
267 DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
268 work
->state
= MST_WON
; /* ... an election win was successful */
270 work
->ElectionCriterion
|= 0x5;
272 /* update our server status */
273 work
->ServerType
&= ~SV_TYPE_POTENTIAL_BROWSER
;
274 add_server_entry(d
,work
,myname
,work
->ServerType
,0,lp_serverstring(),True
);
276 /* add special browser name */
277 add_my_name_entry(d
,MSBROWSE
,0x01,nb_type
|NB_ACTIVE
|NB_GROUP
);
279 /* DON'T do anything else after calling add_my_name_entry() */
282 case MST_WON
: /* while nothing had happened except we won an election... */
284 DEBUG(3,("go to second stage: register as master browser\n"));
285 work
->state
= MST_MSB
; /* ... registering MSBROWSE was successful */
287 /* add server entry on successful registration of MSBROWSE */
288 add_server_entry(d
,work
,work
->work_group
,domain_type
,0,myname
,True
);
290 /* add master name */
291 add_my_name_entry(d
,work
->work_group
,0x1d,nb_type
|NB_ACTIVE
);
293 /* DON'T do anything else after calling add_my_name_entry() */
296 case MST_MSB
: /* while we were still only registered MSBROWSE state... */
298 DEBUG(3,("2nd stage complete: registered as master browser\n"));
299 work
->state
= MST_BROWSER
; /* ... registering WORKGROUP(1d) succeeded */
301 /* update our server status */
302 work
->ServerType
|= SV_TYPE_MASTER_BROWSER
;
303 add_server_entry(d
,work
,myname
,work
->ServerType
,0,lp_serverstring(),True
);
305 if (work
->serverlist
== NULL
) /* no servers! */
307 /* ask all servers on our local net to announce to us */
308 announce_request(work
, d
->bcast_ip
);
315 /* don't have to do anything: just report success */
316 DEBUG(3,("3rd stage: become master browser!\n"));
321 case MST_DOMAIN_NONE
:
323 if (lp_domain_master())
325 work
->state
= MST_DOMAIN_MEM
; /* ... become domain member */
326 DEBUG(3,("domain first stage: register as domain member\n"));
328 /* add domain member name */
329 add_my_name_entry(d
,work
->work_group
,0x1e,nb_type
|NB_ACTIVE
|NB_GROUP
);
331 /* DON'T do anything else after calling add_my_name_entry() */
336 DEBUG(4,("samba not configured as a domain master.\n"));
344 if (lp_domain_master())
346 work
->state
= MST_DOMAIN_TST
; /* ... possibly become domain master */
347 DEBUG(3,("domain second stage: register as domain master\n"));
349 if (lp_domain_logons())
351 work
->ServerType
|= SV_TYPE_DOMAIN_MEMBER
;
352 add_server_entry(d
,work
,myname
,work
->ServerType
,0,lp_serverstring(),True
);
355 /* add domain master name */
356 add_my_name_entry(d
,work
->work_group
,0x1b,nb_type
|NB_ACTIVE
);
358 /* DON'T do anything else after calling add_my_name_entry() */
363 DEBUG(4,("samba not configured as a domain master.\n"));
369 case MST_DOMAIN_TST
: /* while we were still a master browser... */
371 /* update our server status */
372 if (lp_domain_master())
374 struct subnet_record
*d1
;
375 uint32 update_type
= 0;
377 DEBUG(3,("domain third stage: samba is now a domain master.\n"));
378 work
->state
= MST_DOMAIN
; /* ... registering WORKGROUP(1b) succeeded */
380 update_type
|= DFLT_SERVER_TYPE
| SV_TYPE_DOMAIN_MASTER
|
381 SV_TYPE_POTENTIAL_BROWSER
;
383 work
->ServerType
|= update_type
;
384 add_server_entry(d
,work
,myname
,work
->ServerType
,0,lp_serverstring(),True
);
386 for (d1
= subnetlist
; d1
; d1
= d1
->next
)
388 struct work_record
*w
;
389 if (ip_equal(d1
->bcast_ip
, d
->bcast_ip
)) continue;
391 for (w
= d1
->workgrouplist
; w
; w
= w
->next
)
393 struct server_record
*s
= find_server(w
, myname
);
394 if (strequal(w
->work_group
, work
->work_group
))
396 w
->ServerType
|= update_type
;
400 s
->serv
.type
|= update_type
;
401 DEBUG(4,("found server %s on %s: update to %8x\n",
402 s
->serv
.name
, inet_ntoa(d1
->bcast_ip
),
414 /* don't have to do anything: just report success */
415 DEBUG(3,("fifth stage: there isn't one yet!\n"));
422 /*******************************************************************
423 unbecome the master browser. initates removal of necessary netbios
424 names, and tells the world that we are no longer a master browser.
425 ******************************************************************/
426 void become_nonmaster(struct subnet_record
*d
, struct work_record
*work
,
429 int new_server_type
= work
->ServerType
;
431 DEBUG(2,("Becoming non-master for %s\n",work
->work_group
));
433 /* can only remove master or domain types with this function */
434 remove_type
&= SV_TYPE_MASTER_BROWSER
|SV_TYPE_DOMAIN_MASTER
;
436 /* unbecome a master browser; unbecome a domain master, too :-( */
437 if (remove_type
& SV_TYPE_MASTER_BROWSER
)
438 remove_type
|= SV_TYPE_DOMAIN_MASTER
;
440 new_server_type
&= ~remove_type
;
442 if (!(new_server_type
& (SV_TYPE_MASTER_BROWSER
|SV_TYPE_DOMAIN_MASTER
)))
444 /* no longer a master browser of any sort */
446 work
->ServerType
|= SV_TYPE_POTENTIAL_BROWSER
;
447 work
->ElectionCriterion
&= ~0x4;
448 work
->state
= MST_NONE
;
450 /* announce ourselves as no longer active as a master browser. */
451 announce_server(d
, work
, work
->work_group
, myname
, 0, 0);
452 remove_name_entry(d
,MSBROWSE
,0x01);
455 work
->ServerType
= new_server_type
;
457 if (!(work
->ServerType
& SV_TYPE_DOMAIN_MASTER
))
459 if (work
->state
== MST_DOMAIN
)
460 work
->state
= MST_BROWSER
;
461 remove_name_entry(d
,work
->work_group
,0x1b);
464 if (!(work
->ServerType
& SV_TYPE_MASTER_BROWSER
))
466 if (work
->state
>= MST_BROWSER
)
467 work
->state
= MST_NONE
;
468 remove_name_entry(d
,work
->work_group
,0x1d);
473 /*******************************************************************
475 ******************************************************************/
476 void run_elections(time_t t
)
478 static time_t lastime
= 0;
480 struct subnet_record
*d
;
482 /* send election packets once a second */
483 if (lastime
&& t
-lastime
<= 0) return;
487 for (d
= subnetlist
; d
; d
= d
->next
)
489 struct work_record
*work
;
490 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
492 if (work
->RunningElection
)
494 send_election(d
,work
->work_group
, work
->ElectionCriterion
,
495 t
-StartupTime
,myname
);
497 if (work
->ElectionCount
++ >= 4)
499 /* I won! now what :-) */
500 DEBUG(2,(">>> Won election on %s %s <<<\n",
501 work
->work_group
,inet_ntoa(d
->bcast_ip
)));
503 work
->RunningElection
= False
;
504 work
->state
= MST_NONE
;
506 become_master(d
, work
);
514 /*******************************************************************
515 work out if I win an election
516 ******************************************************************/
517 static BOOL
win_election(struct work_record
*work
,int version
,uint32 criterion
,
518 int timeup
,char *name
)
520 int mytimeup
= time(NULL
) - StartupTime
;
521 uint32 mycriterion
= work
->ElectionCriterion
;
523 DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
524 version
,ELECTION_VERSION
,
525 criterion
,mycriterion
,
529 if (version
> ELECTION_VERSION
) return(False
);
530 if (version
< ELECTION_VERSION
) return(True
);
532 if (criterion
> mycriterion
) return(False
);
533 if (criterion
< mycriterion
) return(True
);
535 if (timeup
> mytimeup
) return(False
);
536 if (timeup
< mytimeup
) return(True
);
538 if (strcasecmp(myname
,name
) > 0) return(False
);
544 /*******************************************************************
545 process a election packet
547 An election dynamically decides who will be the master.
548 ******************************************************************/
549 void process_election(struct packet_struct
*p
,char *buf
)
551 struct dgram_packet
*dgram
= &p
->packet
.dgram
;
552 struct in_addr ip
= dgram
->header
.source_ip
;
553 struct subnet_record
*d
= find_subnet(ip
);
554 int version
= CVAL(buf
,0);
555 uint32 criterion
= IVAL(buf
,1);
556 int timeup
= IVAL(buf
,5)/1000;
558 struct work_record
*work
;
562 if (ip_equal(d
->bcast_ip
,ipgrp
)) {
563 DEBUG(3,("Unexpected election request from %s %s on WINS net\n",
564 name
, inet_ntoa(p
->ip
)));
570 DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
571 name
,inet_ntoa(p
->ip
),version
,criterion
,timeup
));
573 if (same_context(dgram
)) return;
575 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
577 if (!strequal(work
->work_group
, lp_workgroup()))
580 if (win_election(work
, version
,criterion
,timeup
,name
)) {
581 if (!work
->RunningElection
) {
582 work
->needelection
= True
;
583 work
->ElectionCount
=0;
584 work
->state
= MST_NONE
;
587 work
->needelection
= False
;
589 if (work
->RunningElection
|| AM_MASTER(work
)) {
590 work
->RunningElection
= False
;
591 DEBUG(3,(">>> Lost election on %s %s <<<\n",
592 work
->work_group
,inet_ntoa(d
->bcast_ip
)));
594 become_nonmaster(d
, work
,
595 SV_TYPE_MASTER_BROWSER
|
596 SV_TYPE_DOMAIN_MASTER
);
603 /****************************************************************************
604 checks whether a browser election is to be run on any workgroup
606 this function really ought to return the time between election
607 packets (which depends on whether samba intends to be a domain
608 master or a master browser) in milliseconds.
610 ***************************************************************************/
611 BOOL
check_elections(void)
613 struct subnet_record
*d
;
614 BOOL run_any_election
= False
;
616 for (d
= subnetlist
; d
; d
= d
->next
)
618 struct work_record
*work
;
619 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
621 run_any_election
|= work
->RunningElection
;
623 if (work
->needelection
&& !work
->RunningElection
)
625 DEBUG(3,(">>> Starting election on %s %s <<<\n",
626 work
->work_group
,inet_ntoa(d
->bcast_ip
)));
627 work
->ElectionCount
= 0;
628 work
->RunningElection
= True
;
629 work
->needelection
= False
;
633 return run_any_election
;