Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : NBT netbios routines and daemon - version 2
4 : Copyright (C) Andrew Tridgell 1994-1998
5 : Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6 : Copyright (C) Jeremy Allison 1994-2003
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 :
21 : */
22 :
23 : #include "includes.h"
24 : #include "nmbd/nmbd.h"
25 : #include "lib/util/string_wrappers.h"
26 :
27 : /* Election parameters. */
28 : extern time_t StartupTime;
29 :
30 : /****************************************************************************
31 : Send an election datagram packet.
32 : **************************************************************************/
33 :
34 164 : static void send_election_dgram(struct subnet_record *subrec, const char *workgroup_name,
35 : uint32_t criterion, int timeup,const char *server_name)
36 : {
37 : char outbuf[1024];
38 : unstring srv_name;
39 : char *p;
40 :
41 164 : DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
42 : workgroup_name, subrec->subnet_name ));
43 :
44 164 : memset(outbuf,'\0',sizeof(outbuf));
45 164 : p = outbuf;
46 164 : SCVAL(p,0,ANN_Election); /* Election opcode. */
47 164 : p++;
48 :
49 164 : SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
50 164 : SIVAL(p,1,criterion);
51 164 : SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
52 164 : p += 13;
53 164 : unstrcpy(srv_name, server_name);
54 164 : if (!strupper_m(srv_name)) {
55 0 : DEBUG(2,("strupper_m failed for %s\n", srv_name));
56 0 : return;
57 : }
58 : /* The following call does UNIX -> DOS charset conversion. */
59 164 : push_ascii(p, srv_name, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
60 164 : p = skip_string(outbuf,sizeof(outbuf),p);
61 :
62 164 : send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
63 : lp_netbios_name(), 0,
64 : workgroup_name, 0x1e,
65 : subrec->bcast_ip, subrec->myip, DGRAM_PORT);
66 : }
67 :
68 : /*******************************************************************
69 : We found a current master browser on one of our broadcast interfaces.
70 : ******************************************************************/
71 :
72 34 : static void check_for_master_browser_success(struct subnet_record *subrec,
73 : struct userdata_struct *userdata,
74 : struct nmb_name *answer_name,
75 : struct in_addr answer_ip, struct res_rec *rrec)
76 : {
77 : unstring aname;
78 34 : pull_ascii_nstring(aname, sizeof(aname), answer_name->name);
79 34 : DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
80 : IP %s (just checking).\n", aname, inet_ntoa(answer_ip) ));
81 34 : }
82 :
83 : /*******************************************************************
84 : We failed to find a current master browser on one of our broadcast interfaces.
85 : ******************************************************************/
86 :
87 34 : static void check_for_master_browser_fail( struct subnet_record *subrec,
88 : struct response_record *rrec,
89 : struct nmb_name *question_name,
90 : int fail_code)
91 : {
92 : unstring workgroup_name;
93 : struct work_record *work;
94 :
95 34 : pull_ascii_nstring(workgroup_name,sizeof(workgroup_name),question_name->name);
96 :
97 34 : work = find_workgroup_on_subnet(subrec, workgroup_name);
98 34 : if(work == NULL) {
99 0 : DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
100 : workgroup_name, subrec->subnet_name ));
101 0 : return;
102 : }
103 :
104 34 : if (strequal(work->work_group, lp_workgroup())) {
105 :
106 34 : if (lp_local_master()) {
107 : /* We have discovered that there is no local master
108 : browser, and we are configured to initiate
109 : an election that we will participate in.
110 : */
111 34 : DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
112 : work->work_group, subrec->subnet_name ));
113 :
114 : /* Setting this means we will participate when the
115 : election is run in run_elections(). */
116 34 : work->needelection = True;
117 : } else {
118 : /* We need to force an election, because we are configured
119 : not to become the local master, but we still need one,
120 : having detected that one doesn't exist.
121 : */
122 0 : send_election_dgram(subrec, work->work_group, 0, 0, "");
123 : }
124 : }
125 : }
126 :
127 : /*******************************************************************
128 : Ensure there is a local master browser for a workgroup on our
129 : broadcast interfaces.
130 : ******************************************************************/
131 :
132 17145 : void check_master_browser_exists(time_t t)
133 : {
134 : static time_t lastrun=0;
135 : struct subnet_record *subrec;
136 17145 : const char *workgroup_name = lp_workgroup();
137 :
138 17145 : if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
139 17013 : return;
140 :
141 132 : lastrun = t;
142 :
143 132 : dump_workgroups(False);
144 :
145 264 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
146 : struct work_record *work;
147 :
148 414 : for (work = subrec->workgrouplist; work; work = work->next) {
149 282 : if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) {
150 : /* Do a name query for the local master browser on this net. */
151 68 : query_name( subrec, work->work_group, 0x1d,
152 : check_for_master_browser_success,
153 : check_for_master_browser_fail,
154 : NULL);
155 : }
156 : }
157 : }
158 : }
159 :
160 : /*******************************************************************
161 : Run an election.
162 : ******************************************************************/
163 :
164 17145 : void run_elections(time_t t)
165 : {
166 : static time_t lastime = 0;
167 :
168 : struct subnet_record *subrec;
169 :
170 : /* Send election packets once every 2 seconds - note */
171 17145 : if (lastime && (t - lastime < 2)) {
172 12848 : return;
173 : }
174 :
175 4297 : lastime = t;
176 :
177 8594 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
178 : struct work_record *work;
179 :
180 14030 : for (work = subrec->workgrouplist; work; work = work->next) {
181 9733 : if (work->RunningElection) {
182 : /*
183 : * We can only run an election for a workgroup if we have
184 : * registered the WORKGROUP<1e> name, as that's the name
185 : * we must listen to.
186 : */
187 : struct nmb_name nmbname;
188 :
189 164 : make_nmb_name(&nmbname, work->work_group, 0x1e);
190 164 : if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
191 0 : DEBUG(8,("run_elections: Cannot send election packet yet as name %s not \
192 : yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
193 0 : continue;
194 : }
195 :
196 164 : send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
197 164 : t - StartupTime, lp_netbios_name());
198 :
199 164 : if (work->ElectionCount++ >= 4) {
200 : /* Won election (4 packets were sent out uncontested. */
201 32 : DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
202 : work->work_group, subrec->subnet_name ));
203 :
204 32 : work->RunningElection = False;
205 :
206 32 : become_local_master_browser(subrec, work);
207 : }
208 : }
209 : }
210 : }
211 : }
212 :
213 : /*******************************************************************
214 : Determine if I win an election.
215 : ******************************************************************/
216 :
217 38 : static bool win_election(struct work_record *work, int version,
218 : uint32_t criterion, int timeup, const char *server_name)
219 : {
220 38 : int mytimeup = time(NULL) - StartupTime;
221 38 : uint32_t mycriterion = work->ElectionCriterion;
222 :
223 : /* If local master is false then never win in election broadcasts. */
224 38 : if(!lp_local_master()) {
225 0 : DEBUG(3,("win_election: Losing election as local master == False\n"));
226 0 : return False;
227 : }
228 :
229 38 : DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
230 : version, ELECTION_VERSION,
231 : criterion, mycriterion,
232 : timeup, mytimeup,
233 : server_name, lp_netbios_name()));
234 :
235 38 : if (version > ELECTION_VERSION)
236 0 : return(False);
237 38 : if (version < ELECTION_VERSION)
238 0 : return(True);
239 :
240 38 : if (criterion > mycriterion)
241 32 : return(False);
242 6 : if (criterion < mycriterion)
243 3 : return(True);
244 :
245 3 : if (timeup > mytimeup)
246 3 : return(False);
247 0 : if (timeup < mytimeup)
248 0 : return(True);
249 :
250 0 : if (strcasecmp_m(lp_netbios_name(), server_name) > 0)
251 0 : return(False);
252 :
253 0 : return(True);
254 : }
255 :
256 : /*******************************************************************
257 : Process an incoming election datagram packet.
258 : ******************************************************************/
259 :
260 38 : void process_election(struct subnet_record *subrec, struct packet_struct *p, const char *buf)
261 : {
262 38 : struct dgram_packet *dgram = &p->packet.dgram;
263 38 : int version = CVAL(buf,0);
264 38 : uint32_t criterion = IVAL(buf,1);
265 38 : int timeup = IVAL(buf,5)/1000;
266 : unstring server_name;
267 : struct work_record *work;
268 : unstring workgroup_name;
269 :
270 38 : pull_ascii_nstring(server_name, sizeof(server_name), buf+13);
271 38 : pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
272 :
273 38 : server_name[15] = 0;
274 :
275 38 : DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
276 : server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
277 :
278 38 : DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
279 :
280 38 : if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
281 0 : DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
282 : workgroup_name, subrec->subnet_name ));
283 0 : goto done;
284 : }
285 :
286 38 : if (!strequal(work->work_group, lp_workgroup())) {
287 0 : DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
288 : is not my workgroup.\n", work->work_group, subrec->subnet_name ));
289 0 : goto done;
290 : }
291 :
292 38 : if (win_election(work, version,criterion,timeup,server_name)) {
293 : /* We take precedence over the requesting server. */
294 3 : if (!work->RunningElection) {
295 : /* We weren't running an election - start running one. */
296 :
297 2 : work->needelection = True;
298 2 : work->ElectionCount=0;
299 : }
300 :
301 : /* Note that if we were running an election for this workgroup on this
302 : subnet already, we just ignore the server we take precedence over. */
303 : } else {
304 : /* We lost. Stop participating. */
305 35 : work->needelection = False;
306 :
307 35 : if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) {
308 6 : work->RunningElection = False;
309 6 : DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
310 : work->work_group, subrec->subnet_name ));
311 6 : if (AM_LOCAL_MASTER_BROWSER(work))
312 2 : unbecome_local_master_browser(subrec, work, False);
313 : }
314 : }
315 33 : done:
316 38 : return;
317 : }
318 :
319 : /****************************************************************************
320 : This function looks over all the workgroups known on all the broadcast
321 : subnets and decides if a browser election is to be run on that workgroup.
322 : It returns True if any election packets need to be sent (this will then
323 : be done by run_elections().
324 : ***************************************************************************/
325 :
326 17188 : bool check_elections(void)
327 : {
328 : struct subnet_record *subrec;
329 17188 : bool run_any_election = False;
330 :
331 34376 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
332 : struct work_record *work;
333 48076 : for (work = subrec->workgrouplist; work; work = work->next) {
334 30888 : if (work->RunningElection) {
335 2143 : run_any_election = work->RunningElection;
336 : }
337 :
338 : /*
339 : * Start an election if we have any chance of winning.
340 : * Note this is a change to the previous code, that would
341 : * only run an election if nmbd was in the potential browser
342 : * state. We need to run elections in any state if we're told
343 : * to. JRA.
344 : */
345 :
346 30888 : if (work->needelection && !work->RunningElection && lp_local_master()) {
347 : /*
348 : * We can only run an election for a workgroup if we have
349 : * registered the WORKGROUP<1e> name, as that's the name
350 : * we must listen to.
351 : */
352 : struct nmb_name nmbname;
353 :
354 623 : make_nmb_name(&nmbname, work->work_group, 0x1e);
355 623 : if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
356 586 : DEBUG(8,("check_elections: Cannot send election packet yet as name %s not \
357 : yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
358 586 : continue;
359 : }
360 :
361 37 : DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
362 : work->work_group, subrec->subnet_name ));
363 :
364 37 : work->ElectionCount = 0;
365 37 : work->RunningElection = True;
366 37 : work->needelection = False;
367 : }
368 : }
369 : }
370 17188 : return run_any_election;
371 : }
372 :
373 : /****************************************************************************
374 : Process a internal Samba message forcing an election.
375 : ***************************************************************************/
376 :
377 0 : void nmbd_message_election(struct messaging_context *msg,
378 : void *private_data,
379 : uint32_t msg_type,
380 : struct server_id server_id,
381 : DATA_BLOB *data)
382 : {
383 : struct subnet_record *subrec;
384 :
385 0 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
386 : struct work_record *work;
387 0 : for (work = subrec->workgrouplist; work; work = work->next) {
388 0 : if (strequal(work->work_group, lp_workgroup())) {
389 0 : work->needelection = True;
390 0 : work->ElectionCount=0;
391 0 : work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
392 : }
393 : }
394 : }
395 0 : }
|