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 131 : 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 131 : DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
42 : workgroup_name, subrec->subnet_name ));
43 :
44 131 : memset(outbuf,'\0',sizeof(outbuf));
45 131 : p = outbuf;
46 131 : SCVAL(p,0,ANN_Election); /* Election opcode. */
47 131 : p++;
48 :
49 131 : SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
50 131 : SIVAL(p,1,criterion);
51 131 : SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
52 131 : p += 13;
53 131 : unstrcpy(srv_name, server_name);
54 131 : 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 131 : push_ascii(p, srv_name, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
60 131 : p = skip_string(outbuf,sizeof(outbuf),p);
61 :
62 131 : 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 43 : 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 43 : pull_ascii_nstring(aname, sizeof(aname), answer_name->name);
79 43 : 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 43 : }
82 :
83 : /*******************************************************************
84 : We failed to find a current master browser on one of our broadcast interfaces.
85 : ******************************************************************/
86 :
87 31 : 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 31 : pull_ascii_nstring(workgroup_name,sizeof(workgroup_name),question_name->name);
96 :
97 31 : work = find_workgroup_on_subnet(subrec, workgroup_name);
98 31 : 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 31 : if (strequal(work->work_group, lp_workgroup())) {
105 :
106 31 : 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 31 : 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 31 : 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 15165 : void check_master_browser_exists(time_t t)
133 : {
134 : static time_t lastrun=0;
135 : struct subnet_record *subrec;
136 15165 : const char *workgroup_name = lp_workgroup();
137 :
138 15165 : if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
139 15048 : return;
140 :
141 117 : lastrun = t;
142 :
143 117 : dump_workgroups(False);
144 :
145 234 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
146 : struct work_record *work;
147 :
148 308 : for (work = subrec->workgrouplist; work; work = work->next) {
149 191 : 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 74 : 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 15165 : 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 15165 : if (lastime && (t - lastime < 2)) {
172 11357 : return;
173 : }
174 :
175 3808 : lastime = t;
176 :
177 7616 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
178 : struct work_record *work;
179 :
180 10319 : for (work = subrec->workgrouplist; work; work = work->next) {
181 6511 : 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 131 : make_nmb_name(&nmbname, work->work_group, 0x1e);
190 131 : 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 131 : send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
197 131 : t - StartupTime, lp_netbios_name());
198 :
199 131 : if (work->ElectionCount++ >= 4) {
200 : /* Won election (4 packets were sent out uncontested. */
201 26 : DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
202 : work->work_group, subrec->subnet_name ));
203 :
204 26 : work->RunningElection = False;
205 :
206 26 : become_local_master_browser(subrec, work);
207 : }
208 : }
209 : }
210 : }
211 : }
212 :
213 : /*******************************************************************
214 : Determine if I win an election.
215 : ******************************************************************/
216 :
217 33 : static bool win_election(struct work_record *work, int version,
218 : uint32_t criterion, int timeup, const char *server_name)
219 : {
220 33 : int mytimeup = time(NULL) - StartupTime;
221 33 : uint32_t mycriterion = work->ElectionCriterion;
222 :
223 : /* If local master is false then never win in election broadcasts. */
224 33 : if(!lp_local_master()) {
225 0 : DEBUG(3,("win_election: Losing election as local master == False\n"));
226 0 : return False;
227 : }
228 :
229 33 : 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 33 : if (version > ELECTION_VERSION)
236 0 : return(False);
237 33 : if (version < ELECTION_VERSION)
238 0 : return(True);
239 :
240 33 : if (criterion > mycriterion)
241 21 : return(False);
242 12 : if (criterion < mycriterion)
243 0 : return(True);
244 :
245 12 : if (timeup > mytimeup)
246 12 : 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 33 : void process_election(struct subnet_record *subrec, struct packet_struct *p, const char *buf)
261 : {
262 33 : struct dgram_packet *dgram = &p->packet.dgram;
263 33 : int version = CVAL(buf,0);
264 33 : uint32_t criterion = IVAL(buf,1);
265 33 : int timeup = IVAL(buf,5)/1000;
266 : unstring server_name;
267 : struct work_record *work;
268 : unstring workgroup_name;
269 :
270 33 : pull_ascii_nstring(server_name, sizeof(server_name), buf+13);
271 33 : pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
272 :
273 33 : server_name[15] = 0;
274 :
275 33 : 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 33 : DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
279 :
280 33 : 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 33 : 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 33 : if (win_election(work, version,criterion,timeup,server_name)) {
293 : /* We take precedence over the requesting server. */
294 0 : if (!work->RunningElection) {
295 : /* We weren't running an election - start running one. */
296 :
297 0 : work->needelection = True;
298 0 : 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 33 : work->needelection = False;
306 :
307 33 : if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) {
308 8 : work->RunningElection = False;
309 8 : DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
310 : work->work_group, subrec->subnet_name ));
311 8 : if (AM_LOCAL_MASTER_BROWSER(work))
312 2 : unbecome_local_master_browser(subrec, work, False);
313 : }
314 : }
315 31 : done:
316 33 : 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 15204 : bool check_elections(void)
327 : {
328 : struct subnet_record *subrec;
329 15204 : bool run_any_election = False;
330 :
331 30408 : for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
332 : struct work_record *work;
333 36731 : for (work = subrec->workgrouplist; work; work = work->next) {
334 21527 : if (work->RunningElection) {
335 1974 : 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 21527 : 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 475 : make_nmb_name(&nmbname, work->work_group, 0x1e);
355 475 : if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
356 442 : 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 442 : continue;
359 : }
360 :
361 33 : DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
362 : work->work_group, subrec->subnet_name ));
363 :
364 33 : work->ElectionCount = 0;
365 33 : work->RunningElection = True;
366 33 : work->needelection = False;
367 : }
368 : }
369 : }
370 15204 : 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 : }
|