1use std::collections::HashMap;
2use std::net::IpAddr;
3use std::time::{Duration, Instant};
4
5use crate::args::Args;
6use crate::output::{
7 print_alive, print_unreachable,
8 print_global_stats, print_per_host_stats, print_recv, print_timeout,
9 max_host_len, GlobalStatsSummary, RecvLineOpts, TimeoutLineOpts,
10};
11use crate::socket::{build_icmp_packet, open_raw_socket, recv_ping, send_ping_v4, send_ping_v6, SocketKind};
12use crate::types::{HostEntry, PendingPing};
13
14pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) {
15 let count = args.effective_count();
16 let loop_mode = args.r#loop;
17 let verbose_count = args.is_verbose_count();
18
19 let mut hosts: Vec<HostEntry> = hosts_in
20 .into_iter()
21 .map(|(name, addr)| {
22 let is_ipv6 = addr.is_ipv6();
23 let display = if args.addr { addr.to_string() } else { name.clone() };
24 let mut h = HostEntry::new(name, addr, is_ipv6, count.unwrap_or(0));
25 h.display = display;
26 h
27 })
28 .collect();
29
30 let has_v4 = hosts.iter().any(|h| !h.is_ipv6);
31 let has_v6 = hosts.iter().any(|h| h.is_ipv6);
32
33 let (fd4, kind4, dgram_id4) = if has_v4 {
34 let (fd, kind, kid) = open_raw_socket(false).unwrap_or_else(|e| {
35 eprintln!("fping: {}", e);
36 std::process::exit(3);
37 });
38 (Some(fd), kind, kid)
39 } else {
40 (None, SocketKind::Raw, None)
41 };
42
43 let (fd6, kind6, dgram_id6) = if has_v6 {
44 let (fd, kind, kid) = open_raw_socket(true).unwrap_or_else(|e| {
45 eprintln!("fping: {}", e);
46 std::process::exit(3);
47 });
48 (Some(fd), kind, kid)
49 } else {
50 (None, SocketKind::Raw, None)
51 };
52
53 let pid_id = (std::process::id() & 0xFFFF) as u16;
54 let my_id = dgram_id4.or(dgram_id6).unwrap_or(pid_id);
55
56 let interval = Duration::from_millis(args.interval);
57 let period = Duration::from_millis(args.period);
58 let timeout = Duration::from_millis(args.timeout);
59
60 for (i, h) in hosts.iter_mut().enumerate() {
61 h.next_send = Instant::now() + interval * i as u32;
62 h.retries_left = args.retry;
63 }
64
65 let mut seqmap: HashMap<u16, PendingPing> = HashMap::new();
67 let mut seq_counter: u16 = 0;
68 let mut recv_buf = vec![0u8; 4096];
69
70 let start = Instant::now();
71 let max_len = max_host_len(&hosts);
72
73 loop {
75 let now = Instant::now();
76
77 if hosts.iter().all(|h| h.done) && seqmap.is_empty() {
78 break;
79 }
80
81 for idx in 0..hosts.len() {
82 if hosts[idx].done || now < hosts[idx].next_send {
83 continue;
84 }
85
86 let ping_idx = hosts[idx].current_ping_index;
87 let seq = seq_counter;
88 seq_counter = seq_counter.wrapping_add(1);
89
90 let is_ipv6 = hosts[idx].is_ipv6;
91 let kind = if is_ipv6 { kind6 } else { kind4 };
92 let pkt = build_icmp_packet(my_id, seq, args.size, is_ipv6, kind);
93
94 let sent = match hosts[idx].addr {
95 IpAddr::V4(ref a) => fd4.map(|fd| send_ping_v4(fd, a, &pkt)).unwrap_or(false),
96 IpAddr::V6(ref a) => fd6.map(|fd| send_ping_v6(fd, a, &pkt)).unwrap_or(false),
97 };
98
99 if sent {
100 let sent_at = Instant::now();
101 seqmap.insert(seq, PendingPing { host_index: idx, ping_index: ping_idx, sent_at });
102 hosts[idx].num_sent += 1;
103 hosts[idx].last_send = Some(sent_at);
104
105 hosts[idx].next_send = if count.is_some() || loop_mode {
106 sent_at + period
107 } else {
108 let backoff = args.backoff.powi(hosts[idx].num_sent as i32 - 1);
109 sent_at + timeout.mul_f64(backoff)
110 };
111
112 hosts[idx].current_ping_index += 1;
113
114 if count.map(|c| hosts[idx].current_ping_index >= c).unwrap_or(false) {
115 hosts[idx].next_send = now + Duration::from_secs(86400);
116 }
117
118 let is_default_mode = count.is_none() && !loop_mode;
119 if is_default_mode && hosts[idx].current_ping_index > args.retry {
120 hosts[idx].next_send = now + Duration::from_secs(86400);
121 }
122 }
123 }
124
125 for (fd_opt, is_v6, kind) in &[(fd4, false, kind4), (fd6, true, kind6)] {
126 let fd = match fd_opt { Some(f) => *f, None => continue };
127 loop {
128 let received = match recv_ping(fd, &mut recv_buf, *is_v6, *kind) {
129 Some(r) => r,
130 None => break,
131 };
132
133 if received.id != my_id { continue; }
134
135 if let Some(pending) = seqmap.remove(&received.seq) {
136 let rtt = Instant::now().duration_since(pending.sent_at);
137 let hi = pending.host_index;
138 let first_reply = hosts[hi].num_recv == 0;
139 hosts[hi].record_reply(rtt, pending.ping_index);
140
141 let is_default_mode = count.is_none() && !loop_mode;
142 if is_default_mode && first_reply {
143 hosts[hi].done = true;
144 }
145
146 if !args.quiet && !args.unreach {
147 if is_default_mode {
148 if first_reply {
149 print_alive(&hosts[hi], args.timestamp, args.json);
150 }
151 } else {
152 print_recv(RecvLineOpts {
153 host: &hosts[hi],
154 ping_index: pending.ping_index,
155 rtt,
156 raw_len: received.raw_len,
157 max_len,
158 timestamp: args.timestamp,
159 json: args.json,
160 verbose_count,
161 });
162 }
163 }
164 }
165 }
166 }
167
168 let now2 = Instant::now();
169 let timed_out: Vec<u16> = seqmap
170 .iter()
171 .filter(|(_, p)| now2.duration_since(p.sent_at) > timeout)
172 .map(|(&seq, _)| seq)
173 .collect();
174
175 for seq in timed_out {
176 if let Some(pending) = seqmap.remove(&seq) {
177 let is_default_mode = count.is_none() && !loop_mode;
178 if !is_default_mode && !args.quiet && !args.alive {
179 print_timeout(TimeoutLineOpts {
180 host: &hosts[pending.host_index],
181 ping_index: pending.ping_index,
182 max_len,
183 timestamp: args.timestamp,
184 json: args.json,
185 });
186 }
187 }
188 }
189
190 let now3 = Instant::now();
191 for h in hosts.iter_mut() {
192 if h.done { continue; }
193
194 let last_expired = h.last_send
195 .map(|s| now3.duration_since(s) > timeout)
196 .unwrap_or(false);
197
198 if let Some(c) = count {
199 if h.current_ping_index >= c && last_expired {
200 h.done = true;
201 }
202 } else if !loop_mode {
203 if h.num_recv > 0 {
204 h.done = true;
205 } else if h.num_sent > args.retry && last_expired {
206 h.done = true;
207 }
208 }
209 }
210
211 std::thread::sleep(Duration::from_millis(1));
212 }
213
214 let max_len = max_host_len(&hosts);
215 let is_default_mode = count.is_none() && !loop_mode;
216
217 if is_default_mode {
218 if !args.quiet {
219 for h in &hosts {
220 if h.num_recv == 0 {
221 print_unreachable(h, args.timestamp, args.json);
222 }
223 }
224 }
225 } else {
226 for h in &hosts {
227 if args.alive && h.num_recv > 0 { println!("{}", h.display); }
228 if args.unreach && h.num_recv == 0 { println!("{}", h.display); }
229 }
230 }
231
232 if count.is_some() && !args.alive && !args.unreach {
233 for h in &hosts {
234 print_per_host_stats(h, max_len, args.json, verbose_count || args.report_all_rtts);
235 }
236 }
237
238 if args.stats {
239 let all_rtts: Vec<Duration> = hosts.iter()
240 .flat_map(|h| h.resp_times.iter().filter_map(|r| *r))
241 .collect();
242
243 let g_sum: Duration = all_rtts.iter().sum();
244 let g_count = all_rtts.len();
245
246 print_global_stats(&GlobalStatsSummary {
247 num_hosts: hosts.len(),
248 num_alive: hosts.iter().filter(|h| h.num_recv > 0).count(),
249 num_unreachable: hosts.iter().filter(|h| h.num_recv == 0).count(),
250 total_sent: hosts.iter().map(|h| h.num_sent).sum(),
251 total_recv: hosts.iter().map(|h| h.num_recv).sum(),
252 min_rtt: all_rtts.iter().min().copied(),
253 avg_rtt: (g_count > 0).then(|| g_sum / g_count as u32),
254 max_rtt: all_rtts.iter().max().copied(),
255 elapsed: start.elapsed(),
256 }, args.json);
257 }
258
259 let exit_ok = if let Some(min_reach) = args.reachable {
260 hosts.iter().filter(|h| h.num_recv > 0).count() as u32 >= min_reach
261 } else {
262 hosts.iter().all(|h| h.num_recv > 0)
263 };
264
265 if !exit_ok {
266 std::process::exit(1);
267 }
268}