Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[metze/samba/wip.git] / source3 / nmbd / nmbd_browsesync.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-1998
6    Copyright (C) Luke Kenneth Casson Leighton 1994-1998
7    Copyright (C) Jeremy Allison 1994-1998
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22    
23 */
24
25 #include "includes.h"
26 #include "smb.h"
27
28 extern struct in_addr ipzero;
29 extern pstring global_myname;
30 extern fstring global_myworkgroup;
31
32 /* This is our local master browser list database. */
33 extern ubi_dlList lmb_browserlist[];
34
35 /****************************************************************************
36 As a domain master browser, do a sync with a local master browser.
37 **************************************************************************/
38 static void sync_with_lmb(struct browse_cache_record *browc)
39 {                     
40   struct work_record *work;
41
42   if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) )
43   {
44     if( DEBUGLVL( 0 ) )
45     {
46       dbgtext( "sync_with_lmb:\n" );
47       dbgtext( "Failed to get a workgroup for a local master browser " );
48       dbgtext( "cache entry workgroup " );
49       dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
50     }
51     return;
52   }
53
54   /* We should only be doing this if we are a domain master browser for
55      the given workgroup. Ensure this is so. */
56
57   if(!AM_DOMAIN_MASTER_BROWSER(work))
58   {
59     if( DEBUGLVL( 0 ) )
60     {
61       dbgtext( "sync_with_lmb:\n" );
62       dbgtext( "We are trying to sync with a local master browser " );
63       dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
64       dbgtext( "and we are not a domain master browser on this workgroup.\n" );
65       dbgtext( "Error!\n" );
66     }
67     return;
68   }
69
70   if( DEBUGLVL( 2 ) )
71   {
72     dbgtext( "sync_with_lmb:\n" );
73     dbgtext( "Initiating sync with local master browser " );
74     dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
75     dbgtext( "for workgroup %s\n", browc->work_group );
76   }
77
78   sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
79
80   browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
81 }
82
83 /****************************************************************************
84 Sync or expire any local master browsers.
85 **************************************************************************/
86 void dmb_expire_and_sync_browser_lists(time_t t)
87 {
88   static time_t last_run = 0;
89   struct browse_cache_record *browc;
90
91   /* Only do this every 20 seconds. */  
92   if (t - last_run < 20) 
93    return;
94
95   last_run = t;
96
97   expire_lmb_browsers(t);
98
99   for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
100        browc;
101        browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
102   {
103     if (browc->sync_time < t)
104       sync_with_lmb(browc);
105   }
106 }
107
108 /****************************************************************************
109 As a local master browser, send an announce packet to the domain master browser.
110 **************************************************************************/
111
112 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
113 {
114   pstring outbuf;
115   char *p;
116
117   if(ismyip(work->dmb_addr))
118   {
119     if( DEBUGLVL( 2 ) )
120     {
121       dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
122       dbgtext( "We are both a domain and a local master browser for " );
123       dbgtext( "workgroup %s.  ", work->work_group );
124       dbgtext( "Do not announce to ourselves.\n" );
125     }
126     return;
127   }
128
129   memset(outbuf,'\0',sizeof(outbuf));
130   p = outbuf;
131   CVAL(p,0) = ANN_MasterAnnouncement;
132   p++;
133
134   StrnCpy(p,global_myname,15);
135   strupper(p);
136   p = skip_string(p,1);
137
138   if( DEBUGLVL( 4 ) )
139   {
140     dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
141     dbgtext( "Sending local master announce to " );
142     dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
143                                        work->work_group );
144   }
145
146   send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
147                 global_myname, 0x0, work->dmb_name.name, 0x0, 
148                 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
149
150 }
151
152 /****************************************************************************
153 As a local master browser, do a sync with a domain master browser.
154 **************************************************************************/
155
156 static void sync_with_dmb(struct work_record *work)
157 {
158   if( DEBUGLVL( 2 ) )
159   {
160     dbgtext( "sync_with_dmb:\n" );
161     dbgtext( "Initiating sync with domain master browser " );
162     dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
163     dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
164     dbgtext( "for workgroup %s\n", work->work_group );
165   }
166
167   sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type, 
168                     work->dmb_addr, False, True);
169 }
170
171 /****************************************************************************
172   Function called when a node status query to a domain master browser IP succeeds.
173 ****************************************************************************/
174
175 static void domain_master_node_status_success(struct subnet_record *subrec,
176                                               struct userdata_struct *userdata,
177                                               struct res_rec *answers,
178                                               struct in_addr from_ip)
179 {
180   struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
181
182   if( work == NULL )
183   {
184     if( DEBUGLVL( 0 ) )
185     {
186       dbgtext( "domain_master_node_status_success:\n" );
187       dbgtext( "Unable to find workgroup " );
188       dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
189     }
190     return;
191   }
192
193   if( DEBUGLVL( 3 ) )
194   {
195     dbgtext( "domain_master_node_status_success:\n" );
196     dbgtext( "Success in node status for workgroup " );
197     dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
198   }
199
200   /* Go through the list of names found at answers->rdata and look for
201      the first SERVER<0x20> name. */
202
203   if(answers->rdata != NULL)
204   {
205     char *p = answers->rdata;
206     int numnames = CVAL(p, 0);
207
208     p += 1;
209
210     while (numnames--)
211     {
212       char qname[17];
213       uint16 nb_flags;
214       int name_type;
215
216       StrnCpy(qname,p,15);
217       name_type = CVAL(p,15);
218       nb_flags = get_nb_flags(&p[16]);
219       trim_string(qname,NULL," ");
220
221       p += 18;
222
223       if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
224       {
225         struct nmb_name nmbname;
226
227         make_nmb_name(&nmbname, qname, name_type);
228
229         /* Copy the dmb name and IP address
230            into the workgroup struct. */
231
232         work->dmb_name = nmbname;
233         putip((char *)&work->dmb_addr, &from_ip);
234
235         /* Do the local master browser announcement to the domain
236            master browser name and IP. */
237         announce_local_master_browser_to_domain_master_browser( work );
238
239         /* Now synchronise lists with the domain master browser. */
240         sync_with_dmb(work);
241         break;
242       }
243     }
244   }
245   else
246     if( DEBUGLVL( 0 ) )
247     {
248       dbgtext( "domain_master_node_status_success:\n" );
249       dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
250       dbgtext( "%s.\n", inet_ntoa(from_ip) );
251     }
252 }
253
254 /****************************************************************************
255   Function called when a node status query to a domain master browser IP fails.
256 ****************************************************************************/
257
258 static void domain_master_node_status_fail(struct subnet_record *subrec,
259                        struct response_record *rrec)
260 {
261   struct userdata_struct *userdata = rrec->userdata;
262
263   if( DEBUGLVL( 0 ) )
264   {
265     dbgtext( "domain_master_node_status_fail:\n" );
266     dbgtext( "Doing a node status request to the domain master browser\n" );
267     dbgtext( "for workgroup %s ", userdata->data );
268     dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
269     dbgtext( "Cannot sync browser lists.\n" );
270   }
271 }
272
273 /****************************************************************************
274   Function called when a query for a WORKGROUP<1b> name succeeds.
275 ****************************************************************************/
276
277 static void find_domain_master_name_query_success(struct subnet_record *subrec,
278                         struct userdata_struct *userdata_in,
279                         struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
280 {
281   /* 
282    * Unfortunately, finding the IP address of the Domain Master Browser,
283    * as we have here, is not enough. We need to now do a sync to the
284    * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
285    * respond to the SMBSERVER name. To get this name from IP
286    * address we do a Node status request, and look for the first
287    * NAME<0x20> in the response, and take that as the server name.
288    * We also keep a cache of the Domain Master Browser name for this
289    * workgroup in the Workgroup struct, so that if the same IP addess
290    * is returned every time, we don't need to do the node status
291    * request.
292    */
293
294   struct work_record *work;
295   struct nmb_name nmbname;
296   struct userdata_struct *userdata;
297   int size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
298
299   if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) )
300   {
301     if( DEBUGLVL( 0 ) )
302       {
303       dbgtext( "find_domain_master_name_query_success:\n" );
304       dbgtext( "Failed to find workgroup %s\n", q_name->name );
305       }
306     return;
307   }
308
309   /* First check if we already have a dmb for this workgroup. */
310
311   if(!ip_equal(work->dmb_addr, ipzero) && ip_equal(work->dmb_addr, answer_ip))
312   {
313     /* Do the local master browser announcement to the domain
314        master browser name and IP. */
315     announce_local_master_browser_to_domain_master_browser( work );
316
317     /* Now synchronise lists with the domain master browser. */
318     sync_with_dmb(work);
319     return;
320   }
321   else
322     putip((char *)&work->dmb_addr, &ipzero);
323
324   /* Now initiate the node status request. */
325   make_nmb_name(&nmbname,"*",0x0);
326
327   /* Put the workgroup name into the userdata so we know
328      what workgroup we're talking to when the reply comes
329      back. */
330
331   /* Setup the userdata_struct - this is copied so we can use
332      a stack variable for this. */
333   if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
334   {
335     DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
336     return;
337   }
338
339   userdata->copy_fn = NULL;
340   userdata->free_fn = NULL;
341   userdata->userdata_len = strlen(work->work_group)+1;
342   pstrcpy(userdata->data, work->work_group);
343
344   node_status( subrec, &nmbname, answer_ip, 
345                domain_master_node_status_success,
346                domain_master_node_status_fail,
347                userdata);
348
349   zero_free(userdata, size);
350 }
351
352 /****************************************************************************
353   Function called when a query for a WORKGROUP<1b> name fails.
354   ****************************************************************************/
355 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
356                                     struct response_record *rrec,
357                                     struct nmb_name *question_name, int fail_code)
358 {
359   if( DEBUGLVL( 0 ) )
360   {
361     dbgtext( "find_domain_master_name_query_fail:\n" );
362     dbgtext( "Unable to find the Domain Master Browser name " );
363     dbgtext( "%s for the workgroup %s.\n",
364              nmb_namestr(question_name), question_name->name );
365     dbgtext( "Unable to sync browse lists in this workgroup.\n" );
366   }
367 }
368
369 /****************************************************************************
370 As a local master browser for a workgroup find the domain master browser
371 name, announce ourselves as local master browser to it and then pull the
372 full domain browse lists from it onto the given subnet.
373 **************************************************************************/
374
375 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
376                                                    struct work_record *work)
377 {
378   struct nmb_name nmbname;
379
380   /* Only do this if we are using a WINS server. */
381   if(we_are_a_wins_client() == False)
382   {
383     if( DEBUGLVL( 10 ) )
384     {
385       dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
386       dbgtext( "Ignoring, as we are not a WINS client.\n" );
387     }
388     return;
389   }
390
391   make_nmb_name(&nmbname,work->work_group,0x1b);
392
393   /* First, query for the WORKGROUP<1b> name from the WINS server. */
394   query_name(unicast_subnet, nmbname.name, nmbname.name_type,
395              find_domain_master_name_query_success,
396              find_domain_master_name_query_fail,
397              NULL);
398
399 }
400
401 /****************************************************************************
402   Function called when a node status query to a domain master browser IP succeeds.
403   This function is only called on query to a Samba 1.9.18 or above WINS server.
404
405   Note that adding the workgroup name is enough for this workgroup to be
406   browsable by clients, as clients query the WINS server or broadcast 
407   nets for the WORKGROUP<1b> name when they want to browse a workgroup
408   they are not in. We do not need to do a sync with this Domain Master
409   Browser in order for our browse clients to see machines in this workgroup.
410   JRA.
411 ****************************************************************************/
412
413 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
414                                               struct userdata_struct *userdata,
415                                               struct res_rec *answers,
416                                               struct in_addr from_ip)
417 {
418   struct work_record *work;
419   fstring server_name;
420
421   server_name[0] = 0;
422
423   if( DEBUGLVL( 3 ) )
424   {
425     dbgtext( "get_domain_master_name_node_status_success:\n" );
426     dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
427   }
428
429   /* 
430    * Go through the list of names found at answers->rdata and look for
431    * the first WORKGROUP<0x1b> name.
432    */
433
434   if(answers->rdata != NULL)
435   {
436     char *p = answers->rdata;
437     int numnames = CVAL(p, 0);
438
439     p += 1;
440
441     while (numnames--)
442     {
443       char qname[17];
444       uint16 nb_flags;
445       int name_type;
446
447       StrnCpy(qname,p,15);
448       name_type = CVAL(p,15);
449       nb_flags = get_nb_flags(&p[16]);
450       trim_string(qname,NULL," ");
451
452       p += 18;
453
454       if(!(nb_flags & NB_GROUP) && (name_type == 0x00) && 
455          server_name[0] == 0) {
456               /* this is almost certainly the server netbios name */
457               fstrcpy(server_name, qname);
458               continue;
459       }
460
461       if(!(nb_flags & NB_GROUP) && (name_type == 0x1b))
462       {
463         if( DEBUGLVL( 5 ) )
464         {
465           dbgtext( "get_domain_master_name_node_status_success:\n" );
466           dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
467           dbgtext( "is a domain master browser for workgroup " );
468           dbgtext( "%s. Adding this name.\n", qname );
469         }
470
471         /* 
472          * If we don't already know about this workgroup, add it
473          * to the workgroup list on the unicast_subnet.
474          */
475         if((work = find_workgroup_on_subnet( subrec, qname)) == NULL)
476         {
477                 struct nmb_name nmbname;
478                 /* 
479                  * Add it - with an hour in the cache.
480                  */
481                 if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
482                         return;
483
484                 /* remember who the master is */
485                 fstrcpy(work->local_master_browser_name, server_name);
486                 make_nmb_name(&nmbname, server_name, 0x20);
487                 work->dmb_name = nmbname;
488                 work->dmb_addr = from_ip;
489         }
490         break;
491       }
492     }
493   }
494   else
495     if( DEBUGLVL( 0 ) )
496     {
497       dbgtext( "get_domain_master_name_node_status_success:\n" );
498       dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
499       dbgtext( "%s.\n", inet_ntoa(from_ip) );
500     }
501 }
502
503 /****************************************************************************
504   Function called when a node status query to a domain master browser IP fails.
505 ****************************************************************************/
506
507 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
508                        struct response_record *rrec)
509 {
510   if( DEBUGLVL( 0 ) )
511   {
512     dbgtext( "get_domain_master_name_node_status_fail:\n" );
513     dbgtext( "Doing a node status request to the domain master browser " );
514     dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
515     dbgtext( "Cannot get workgroup name.\n" );
516   }
517 }
518
519 /****************************************************************************
520   Function called when a query for *<1b> name succeeds.
521 ****************************************************************************/
522
523 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
524                         struct userdata_struct *userdata_in,
525                         struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
526 {
527   /* 
528    * We now have a list of all the domain master browsers for all workgroups
529    * that have registered with the WINS server. Now do a node status request
530    * to each one and look for the first 1b name in the reply. This will be
531    * the workgroup name that we will add to the unicast subnet as a 'non-local'
532    * workgroup.
533    */
534
535   struct nmb_name nmbname;
536   struct in_addr send_ip;
537   int i;
538
539   if( DEBUGLVL( 5 ) )
540   {
541     dbgtext( "find_all_domain_master_names_query_succes:\n" );
542     dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
543     dbgtext( "IP addresses for Domain Master Browsers.\n" );
544   }
545
546   for(i = 0; i < rrec->rdlength / 6; i++)
547   {
548     /* Initiate the node status requests. */
549     make_nmb_name(&nmbname, "*", 0);
550
551     putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
552
553     /* 
554      * Don't send node status requests to ourself.
555      */
556
557     if(ismyip( send_ip ))
558     {
559       if( DEBUGLVL( 5 ) )
560       {
561         dbgtext( "find_all_domain_master_names_query_succes:\n" );
562         dbgtext( "Not sending node status to our own IP " );
563         dbgtext( "%s.\n", inet_ntoa(send_ip) );
564       }
565       continue;
566     }
567
568     if( DEBUGLVL( 5 ) )
569     {
570       dbgtext( "find_all_domain_master_names_query_success:\n" );
571       dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
572     }
573
574     node_status( subrec, &nmbname, send_ip, 
575                  get_domain_master_name_node_status_success,
576                  get_domain_master_name_node_status_fail,
577                  NULL);
578   }
579 }
580
581 /****************************************************************************
582   Function called when a query for *<1b> name fails.
583   ****************************************************************************/
584 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
585                                     struct response_record *rrec,
586                                     struct nmb_name *question_name, int fail_code)
587 {
588   if( DEBUGLVL( 10 ) )
589   {
590     dbgtext( "find_domain_master_name_query_fail:\n" );
591     dbgtext( "WINS server did not reply to a query for name " );
592     dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
593     dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
594   }
595 }
596
597 /****************************************************************************
598  If we are a domain master browser on the unicast subnet, do a query to the
599  WINS server for the *<1b> name. This will only work to a Samba WINS server,
600  so ignore it if we fail. If we succeed, contact each of the IP addresses in
601  turn and do a node status request to them. If this succeeds then look for a
602  <1b> name in the reply - this is the workgroup name. Add this to the unicast
603  subnet. This is expensive, so we only do this every 15 minutes.
604 **************************************************************************/
605 void collect_all_workgroup_names_from_wins_server(time_t t)
606 {
607   static time_t lastrun = 0;
608   struct work_record *work;
609   struct nmb_name nmbname;
610
611   /* Only do this if we are using a WINS server. */
612   if(we_are_a_wins_client() == False)
613     return;
614
615   /* Check to see if we are a domain master browser on the unicast subnet. */
616   if((work = find_workgroup_on_subnet( unicast_subnet, global_myworkgroup)) == NULL)
617   {
618     if( DEBUGLVL( 0 ) )
619     {
620       dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
621       dbgtext( "Cannot find my workgroup %s ", global_myworkgroup );
622       dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
623     }
624     return;
625   }
626
627   if(!AM_DOMAIN_MASTER_BROWSER(work))
628     return;
629
630   if ((lastrun != 0) && (t < lastrun + (15 * 60)))
631     return;
632      
633   lastrun = t;
634
635   make_nmb_name(&nmbname,"*",0x1b);
636
637   /* First, query for the *<1b> name from the WINS server. */
638   query_name(unicast_subnet, nmbname.name, nmbname.name_type,
639              find_all_domain_master_names_query_success,
640              find_all_domain_master_names_query_fail,
641              NULL);
642
643
644
645 /****************************************************************************
646  If we are a domain master browser on the unicast subnet, do a regular sync
647  with all other DMBs that we know of on that subnet.
648
649 To prevent exponential network traffic with large numbers of workgroups
650 we use a randomised system where sync probability is inversely proportional
651 to the number of known workgroups
652 **************************************************************************/
653 void sync_all_dmbs(time_t t)
654 {
655         static time_t lastrun = 0;
656         struct work_record *work;
657         int count=0;
658
659         /* Only do this if we are using a WINS server. */
660         if(we_are_a_wins_client() == False)
661                 return;
662
663         /* Check to see if we are a domain master browser on the
664            unicast subnet. */
665         work = find_workgroup_on_subnet(unicast_subnet, global_myworkgroup);
666         if (!work) return;
667
668         if (!AM_DOMAIN_MASTER_BROWSER(work))
669                 return;
670
671         if ((lastrun != 0) && (t < lastrun + (5 * 60)))
672                 return;
673      
674         /* count how many syncs we might need to do */
675         for (work=unicast_subnet->workgrouplist; work; work = work->next) {
676                 if (strcmp(global_myworkgroup, work->work_group)) {
677                         count++;
678                 }
679         }
680
681         /* sync with a probability of 1/count */
682         for (work=unicast_subnet->workgrouplist; work; work = work->next) {
683                 if (strcmp(global_myworkgroup, work->work_group)) {
684                         if (((unsigned)sys_random()) % count != 0) continue;
685
686                         lastrun = t;
687
688                         if (!work->dmb_name.name[0]) {
689                                 /* we don't know the DMB - assume it is
690                                    the same as the unicast local master */
691                                 make_nmb_name(&work->dmb_name, 
692                                               work->local_master_browser_name,
693                                               0x20);
694                         }
695
696                         DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
697                                  work->dmb_name.name, 
698                                  inet_ntoa(work->dmb_addr)));
699                         sync_browse_lists(work, 
700                                           work->dmb_name.name,
701                                           work->dmb_name.name_type, 
702                                           work->dmb_addr, False, False);
703                 }
704         }
705