-
Notifications
You must be signed in to change notification settings - Fork 154
Add a binding for pcap_breakloop
#390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
This code could lead to future problem, this hand solution to secure handle is not very rusty. If we need to share handle more safely maybe it would be better to change handle to |
|
I agree that it's not very idiomatic, but having RwLock on the entire handle would be bad for performance and require changes to lots of existing code. My little trick allows all existing code to keep working unchanged and the only place where you have to be careful about using the handle is outside of |
|
In that case use the weak feature of Arc, do |
|
I agree with @Stargateur that the current solution is not idiomatic and clunky. But I also understand where you are coming from and your concerns about code churn, @FeldrinH. This looks like a very useful functionality to have in the code base and since it already came up before, I would like to help make this PR get accepted. But first let's see if we can make it less clunky. Our
What do you think? Option 2 is some extra work but seems fairly scoped. |
|
Personally I would prefer some clunkyness in the internal implementation over compromising the performance or complexity of public API. Also, I think option 2 doesn't work in practice, because to read packets from the capture you need mutable access to the capture, which would mean an exclusive lock. The point of breakloop is to interrupt this operation, but that is not possoble because breakloop has to lock the capture first and thus would block until a packet is read. |
|
So I've done some reading up and I propose the following. Summary: I think that to implement this feature idiomatically the NonNull<pcap_t> should be wrapped in Arc (but without a lock) and the BreakLoop struct should acquire a Weak downgrade of Arc as suggested by @Stargateur. This will work because pcap_breakloop is thread-safe and can be called on a valid pcap_t pointer without any additional locking. From https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html:
So the only thing that needs to be ensured is that the pointer points to valid data and has not been closed. This can be done by calling upgrade on the Weak held by BreakLoop. Since no lock is introduced, the overhead is minimal, just a deref of Arc. It might require some care though to make sure the Arc becomes invalid before pcap_close is called to avoid a race condition where breakloop is called after pcap_close is called but before the Arc is dropped. What do you think? |
|
And I forgot to mention - the interior mutability of the pointer is provided by the fact that NonNull and pointers implement Copy. |
|
Thanks @Stargateur for making the changes, I pulled across @Stargateur's changes from their PR #391 fixing a small typo I left in and removing the reference of "poisoned lock" in the commit message. Tested the changes on Linux for my use case and it works nicely. LGTM |
* Added unit tests to verify handle validity and behavior after capture drop. * Introduced examples/break_loop.rs demonstrating thread‑safe capture abort using breakloop_handle().
|
I'm not sure how relevant it is, but I believe BreakLoop::breakloop as currently implemented cannot be safely called inside signal handlers. If breakloop is called immediately before the original capture goes out of scope then the breakloop Arc will be the last reference to the handle, which means that the handle is dropped and pcap_close is called inside the signal handler, which AFAIK is unsafe. Might not be a blocker, but at least the docstring should probably be updated to mention this. |
|
I'm not sure if this is correct but my understanding is that when we call EDIT: Oh sorry I misread that comment please continue |
|
Yes, it guarantees that pcap_close is not called when Capture drops the reference. Instead pcap_close will be called when breakloop drops the (strong) reference. If breakloop is called from a signal handler then this is a problem, because calling pcap_close from a signal handler is unsafe. |
|
PS: For what it's worth, my original breakloop using Arc<RwLock> should be safe to call in a signal handler under all circumstances. |
|
I think the In terms of calling I can add a disclaimer that |
|
Impossible, if a signal is receive during the closing the reference would already be invalid, Arc is atomic. It's impossible for a signal handle to be the one who get the last reference. |
Indeed, I don't think either solution is safe in a signal handler. I like your suggestion though. The only other option is to completely remove Arc, mark the breakloop method unsafe and document that it is up to the caller to ensure that the capture exists at the time of calling. Although that would slightly ruin the point of having this crate :-). @cai-william, I will let you decide which solution to go for in the end. Please add the comment using the |
It is possible. Consider the case where breakloop is called and it has just finished the upgrade call, but has not yet done the pcap_breakloop call but at the same time, in another thread the capture is dropped before pcap_breakloop is called. This will result in the breakloop's upgraded Arc being the last reference and thus as soon as it goes out of scope, it will call pcap_close. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few additional minor comments to tidy up the PR before it is ready for merging. Thanks a lot for your work!
Yeah, I think we'll stick to the changes @Stargateur proposed in addition to your suggested changes and the Safety comment. I can imagine the signal handler would work in a single threaded application, where the signal handler can interrupt the thread's execution and do breakloop() without worrying about Capture dropping. For multi-threaded applications they can follow the suggestion and have the signal handler defer the task to a thread. |
I'm reasonably sure the RwLock implementation is safe to call in a signal handler. Importantly, it uses try_read, so if the lock can't be aquired immediately then it just assumes that the handle is being closed and does nothing. Importantly it never blocks, so no blocking system calls are made during breakloop. But I agree that this type of reasoning is iffy in general, since Rust std doesn't really document what is safe to call in a signal handler. |
oh yeah in a multithreading context... well, honestly I dislike both solution anyway... just the simple Arc is way more maintainable that a if handle_valid... we could scope all use of handle into a closure that will always check if handle is valid... but again that will cost some perf. Honestly signal is so... annoying and limited. Generally people should only do the most basic thing ever in signal handling like changing a flag to true. |
Intuitively, I would agree. However, if you look at the list of methods safe to call from a signal handler (https://man7.org/linux/man-pages/man7/signal-safety.7.html) you will not find pthread_rwlock_tryrdlock on the list. While RwLock may have a different implementation that only uses signal-handler-safe methods we cannot tell without looking at the code so comparing to its pthread equivalent is a decent guess. |
And indeed - that is what |
|
@cai-william, there is only last Clippy warning stating that I would add a clippy allow directive for this warning and justify it with a comment that |
|
Great, thanks a lot for your work @cai-william. I will push a new version to crates with this update later today. |
Intent of this PR is to provide a method to stop a blocking capture loop in a multithreaded application so it can handle graceful shutdowns.
Most of the implementation is taken from @FeldrinH's comment here
I've tested this works on Linux, however this is untested on Windows.
Fixes #312