diff options
author | Jeremy Allison <jra@samba.org> | 2010-02-10 12:32:05 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2010-02-10 12:32:05 -0800 |
commit | c2f3ed48c5ac253bb17387800579f94d933b71e1 (patch) | |
tree | 9c540dd760b965af0fa993b60c82a5d11e37c1ce | |
parent | 84fba3c1bc962804259f201d465acfdf0cd3c6a8 (diff) | |
download | samba-c2f3ed48c5ac253bb17387800579f94d933b71e1.tar.gz samba-c2f3ed48c5ac253bb17387800579f94d933b71e1.tar.bz2 samba-c2f3ed48c5ac253bb17387800579f94d933b71e1.zip |
More of the fix for bug #7118 - nmbd problems with socket address.
Add a simple "processed packet queue" cache to stop nmbd responding to
packets received on the broadcast and non-broadcast socket (which
it has opened when "nmbd bind explicit broadcast = yes").
This is a very simple packet queue - it only keeps the packets
processed during a single call to listen_for_packets() (i.e. one
select call). This means that if the delivery notification for a
packet received on both broadcast and non-broadcast addresses
is done in two different select calls, the packet will still be
processed twice. This is a very rare occurrance and we can just
live with it when it does as the protocol is stateless. If this
is ever flagged as a repeatable problem then we can add a longer
lived cache, using timeout processing to clear etc. etc. But without
storing all packets processed we can never be *sure* we've eliminated
the race condition so I'm going to go with this simple solution until
someone proves a more complex one is needed :-).
Jeremy.
-rw-r--r-- | source3/nmbd/nmbd_packets.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c index 55fd24dc35..a753b2841d 100644 --- a/source3/nmbd/nmbd_packets.c +++ b/source3/nmbd/nmbd_packets.c @@ -1765,6 +1765,83 @@ only use %d.\n", count, FD_SETSIZE)); } /**************************************************************************** + List of packets we're processing this select. +***************************************************************************/ + +struct processed_packet { + struct processed_packet *next; + struct processed_packet *prev; + enum packet_type packet_type; + struct in_addr ip; + int packet_id; +}; + +/**************************************************************************** + Have we seen this before ? +***************************************************************************/ + +static bool is_processed_packet(struct processed_packet *processed_packet_list, + struct packet_struct *packet) +{ + struct processed_packet *p = NULL; + + for (p = processed_packet_list; p; p = p->next) { + if (ip_equal_v4(p->ip, packet->ip) && p->packet_type == packet->packet_type) { + if ((p->packet_type == NMB_PACKET) && + (p->packet_id == + packet->packet.nmb.header.name_trn_id)) { + return true; + } else if ((p->packet_type == DGRAM_PACKET) && + (p->packet_id == + packet->packet.dgram.header.dgm_id)) { + return true; + } + } + } + return false; +} + +/**************************************************************************** + Keep a list of what we've seen before. +***************************************************************************/ + +static bool store_processed_packet(struct processed_packet **pp_processed_packet_list, + struct packet_struct *packet) +{ + struct processed_packet *p = SMB_MALLOC_P(struct processed_packet); + if (!p) { + return false; + } + p->packet_type = packet->packet_type; + p->ip = packet->ip; + if (packet->packet_type == NMB_PACKET) { + p->packet_id = packet->packet.nmb.header.name_trn_id; + } else if (packet->packet_type == DGRAM_PACKET) { + p->packet_id = packet->packet.dgram.header.dgm_id; + } else { + return false; + } + + DLIST_ADD(*pp_processed_packet_list, p); + return true; +} + +/**************************************************************************** + Throw away what we've seen before. +***************************************************************************/ + +static void free_processed_packet_list(struct processed_packet **pp_processed_packet_list) +{ + struct processed_packet *p = NULL, *next = NULL; + + for (p = *pp_processed_packet_list; p; p = next) { + next = p->next; + DLIST_REMOVE(*pp_processed_packet_list, p); + SAFE_FREE(p); + } +} + +/**************************************************************************** Listens for NMB or DGRAM packets, and queues them. return True if the socket is dead ***************************************************************************/ @@ -1784,6 +1861,7 @@ bool listen_for_packets(bool run_election) #ifndef SYNC_DNS int dns_fd; #endif + struct processed_packet *processed_packet_list = NULL; if(listen_set == NULL || rescan_listen_set) { if(create_listen_fdset(&listen_set, &sock_array, &listen_number, &maxfd)) { @@ -1906,6 +1984,16 @@ bool listen_for_packets(bool run_election) } } + + if (is_processed_packet(processed_packet_list, packet)) { + DEBUG(7,("discarding duplicate packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + continue; + } + + store_processed_packet(&processed_packet_list, packet); + /* * 0,2,4,... are unicast sockets * 1,3,5,... are broadcast sockets @@ -1926,6 +2014,7 @@ bool listen_for_packets(bool run_election) queue_packet(packet); } + free_processed_packet_list(&processed_packet_list); return False; } |