1use crate::bpf::fs::resolve_pinned_bpf_object;
6use crate::bpf::program::{Program, ProgramHandle};
7use crate::security;
8use crate::task::CurrentTask;
9use crate::vfs::socket::iptables_utils::{
10 self, Ip, IpTableParseError, IptReplaceContext, TableId, string_to_ascii_buffer,
11};
12use crate::vfs::socket::{SockOptValue, SocketDomain, SocketHandle, SocketType};
13use bstr::BString;
14use fidl_fuchsia_net_filter_ext::sync::Controller;
15use fidl_fuchsia_net_filter_ext::{
16 Change, CommitError, ControllerId, PushChangesError, RegisterEbpfProgramError,
17};
18use fuchsia_component::client::connect_to_protocol_sync;
19use itertools::Itertools;
20use starnix_logging::{log_error, log_warn, track_stub};
21use starnix_sync::{KernelIpTables, Locked, OrderedRwLock, Unlocked};
22use starnix_uapi::auth::CAP_NET_ADMIN;
23use starnix_uapi::errors::Errno;
24use starnix_uapi::iptables_flags::NfIpHooks;
25use starnix_uapi::open_flags::OpenFlags;
26use starnix_uapi::{
27 IP6T_SO_GET_ENTRIES, IP6T_SO_GET_INFO, IP6T_SO_GET_REVISION_MATCH, IP6T_SO_GET_REVISION_TARGET,
28 IPT_SO_GET_ENTRIES, IPT_SO_GET_INFO, IPT_SO_GET_REVISION_MATCH, IPT_SO_GET_REVISION_TARGET,
29 IPT_SO_SET_ADD_COUNTERS, IPT_SO_SET_REPLACE, SOL_IP, SOL_IPV6, errno, error, ip6t_entry,
30 ip6t_get_entries, ip6t_getinfo, ipt_entry, ipt_get_entries, ipt_getinfo,
31 nf_inet_hooks_NF_INET_NUMHOOKS, xt_counters, xt_counters_info,
32 xt_entry_target__bindgen_ty_1__bindgen_ty_1 as xt_entry_target, xt_error_target,
33 xt_get_revision, xt_standard_target,
34};
35use static_assertions::const_assert_eq;
36use std::collections::HashMap;
37use std::mem::size_of;
38use std::ops::{Deref as _, Index, IndexMut};
39use zerocopy::{FromBytes, IntoBytes};
40use {fidl_fuchsia_ebpf as febpf, fidl_fuchsia_net_filter as fnet_filter};
41
42const NAMESPACE_ID_PREFIX: &str = "starnix";
43
44const IPT_ENTRY_SIZE: u16 = size_of::<ipt_entry>() as u16;
45const IP6T_ENTRY_SIZE: u16 = size_of::<ip6t_entry>() as u16;
46const STANDARD_TARGET_SIZE: u16 = size_of::<xt_standard_target>() as u16;
47const ERROR_TARGET_SIZE: u16 = size_of::<xt_error_target>() as u16;
48
49const FILTER_HOOKS: [u32; 5] = [0, 0, 1, 2, 0];
57const NAT_HOOKS: [u32; 5] = [0, 1, 0, 2, 3];
58const MANGLE_HOOKS: [u32; 5] = [0, 1, 2, 3, 4];
59const RAW_HOOKS: [u32; 5] = [0, 0, 0, 1, 0];
60
61#[derive(Debug, Default)]
64struct IpTable {
65 pub valid_hooks: u32,
66 pub hook_entry: [u32; nf_inet_hooks_NF_INET_NUMHOOKS as usize],
67 pub underflow: [u32; nf_inet_hooks_NF_INET_NUMHOOKS as usize],
68 pub num_entries: u32,
69 pub size: u32,
70 pub entries: Vec<u8>,
71 pub num_counters: u32,
72 pub counters: Vec<xt_counters>,
73}
74
75impl IpTable {
76 fn accept_policy_v4() -> Vec<u8> {
77 [
78 ipt_entry {
79 target_offset: IPT_ENTRY_SIZE,
80 next_offset: IPT_ENTRY_SIZE + STANDARD_TARGET_SIZE,
81 ..Default::default()
82 }
83 .as_bytes(),
84 xt_entry_target { target_size: STANDARD_TARGET_SIZE, ..Default::default() }.as_bytes(),
85 iptables_utils::VerdictWithPadding {
86 verdict: iptables_utils::VERDICT_ACCEPT,
87 ..Default::default()
88 }
89 .as_bytes(),
90 ]
91 .concat()
92 }
93
94 fn accept_policy_v6() -> Vec<u8> {
95 [
96 ip6t_entry {
97 target_offset: IP6T_ENTRY_SIZE,
98 next_offset: IP6T_ENTRY_SIZE + STANDARD_TARGET_SIZE,
99 ..Default::default()
100 }
101 .as_bytes(),
102 xt_entry_target { target_size: STANDARD_TARGET_SIZE, ..Default::default() }.as_bytes(),
103 iptables_utils::VerdictWithPadding {
104 verdict: iptables_utils::VERDICT_ACCEPT,
105 ..Default::default()
106 }
107 .as_bytes(),
108 ]
109 .concat()
110 }
111
112 fn end_of_input_v4() -> Vec<u8> {
113 [
114 ipt_entry {
115 target_offset: IPT_ENTRY_SIZE,
116 next_offset: IPT_ENTRY_SIZE + ERROR_TARGET_SIZE,
117 ..Default::default()
118 }
119 .as_bytes(),
120 xt_entry_target {
121 target_size: ERROR_TARGET_SIZE,
122 name: string_to_ascii_buffer("ERROR").expect("convert \"ERROR\" to ASCII"),
123 revision: 0,
124 }
125 .as_bytes(),
126 iptables_utils::ErrorNameWithPadding {
127 errorname: string_to_ascii_buffer("ERROR").expect("convert \"ERROR\" to ASCII"),
128 ..Default::default()
129 }
130 .as_bytes(),
131 ]
132 .concat()
133 }
134
135 fn end_of_input_v6() -> Vec<u8> {
136 [
137 ip6t_entry {
138 target_offset: IP6T_ENTRY_SIZE,
139 next_offset: IP6T_ENTRY_SIZE + ERROR_TARGET_SIZE,
140 ..Default::default()
141 }
142 .as_bytes(),
143 xt_entry_target {
144 target_size: ERROR_TARGET_SIZE,
145 name: string_to_ascii_buffer("ERROR").expect("convert \"ERROR\" to ASCII"),
146 revision: 0,
147 }
148 .as_bytes(),
149 iptables_utils::ErrorNameWithPadding {
150 errorname: string_to_ascii_buffer("ERROR").expect("convert \"ERROR\" to ASCII"),
151 ..Default::default()
152 }
153 .as_bytes(),
154 ]
155 .concat()
156 }
157
158 fn default_ipv4_nat_table() -> Self {
159 let hook_entry = NAT_HOOKS.map(|n| n * u32::from(IPT_ENTRY_SIZE + STANDARD_TARGET_SIZE));
160 let accept_policy = Self::accept_policy_v4();
161 let entries = [
162 accept_policy.as_slice(),
163 accept_policy.as_slice(),
164 accept_policy.as_slice(),
165 accept_policy.as_slice(),
166 Self::end_of_input_v4().as_slice(),
167 ]
168 .concat();
169 Self {
170 valid_hooks: NfIpHooks::NAT.bits(),
171 hook_entry,
172 underflow: hook_entry,
173 num_entries: 5,
174 size: entries.len() as u32,
175 entries,
176 ..Default::default()
177 }
178 }
179
180 fn default_ipv6_nat_table() -> Self {
181 let hook_entry = NAT_HOOKS.map(|n| n * u32::from(IP6T_ENTRY_SIZE + STANDARD_TARGET_SIZE));
182 let accept_policy = Self::accept_policy_v6();
183 let entries = [
184 accept_policy.as_slice(),
185 accept_policy.as_slice(),
186 accept_policy.as_slice(),
187 accept_policy.as_slice(),
188 Self::end_of_input_v6().as_slice(),
189 ]
190 .concat();
191
192 Self {
193 valid_hooks: NfIpHooks::NAT.bits(),
194 hook_entry,
195 underflow: hook_entry,
196 num_entries: 5,
197 size: entries.len() as u32,
198 entries,
199 ..Default::default()
200 }
201 }
202
203 fn default_ipv4_filter_table() -> Self {
204 let hook_entry = FILTER_HOOKS.map(|n| n * u32::from(IPT_ENTRY_SIZE + STANDARD_TARGET_SIZE));
205 let accept_policy = Self::accept_policy_v4();
206 let entries = [
207 accept_policy.as_slice(),
208 accept_policy.as_slice(),
209 accept_policy.as_slice(),
210 Self::end_of_input_v4().as_slice(),
211 ]
212 .concat();
213
214 Self {
215 valid_hooks: NfIpHooks::FILTER.bits(),
216 hook_entry,
217 underflow: hook_entry,
218 num_entries: 4,
219 size: entries.len() as u32,
220 entries,
221 ..Default::default()
222 }
223 }
224
225 fn default_ipv6_filter_table() -> Self {
226 let hook_entry =
227 FILTER_HOOKS.map(|n| n * u32::from(IP6T_ENTRY_SIZE + STANDARD_TARGET_SIZE));
228 let accept_policy = Self::accept_policy_v6();
229 let entries = [
230 accept_policy.as_slice(),
231 accept_policy.as_slice(),
232 accept_policy.as_slice(),
233 Self::end_of_input_v6().as_slice(),
234 ]
235 .concat();
236
237 Self {
238 valid_hooks: NfIpHooks::FILTER.bits(),
239 hook_entry,
240 underflow: hook_entry,
241 num_entries: 4,
242 size: entries.len() as u32,
243 entries,
244 ..Default::default()
245 }
246 }
247
248 fn default_ipv4_mangle_table() -> Self {
249 let hook_entry = MANGLE_HOOKS.map(|n| n * u32::from(IPT_ENTRY_SIZE + STANDARD_TARGET_SIZE));
250 let accept_policy = Self::accept_policy_v4();
251 let entries = [
252 accept_policy.as_slice(),
253 accept_policy.as_slice(),
254 accept_policy.as_slice(),
255 accept_policy.as_slice(),
256 accept_policy.as_slice(),
257 Self::end_of_input_v4().as_slice(),
258 ]
259 .concat();
260
261 Self {
262 valid_hooks: NfIpHooks::MANGLE.bits(),
263 hook_entry,
264 underflow: hook_entry,
265 num_entries: 6,
266 size: entries.len() as u32,
267 entries,
268 ..Default::default()
269 }
270 }
271
272 fn default_ipv6_mangle_table() -> Self {
273 let hook_entry =
274 MANGLE_HOOKS.map(|n| n * u32::from(IP6T_ENTRY_SIZE + STANDARD_TARGET_SIZE));
275 let accept_policy = Self::accept_policy_v6();
276 let entries = [
277 accept_policy.as_slice(),
278 accept_policy.as_slice(),
279 accept_policy.as_slice(),
280 accept_policy.as_slice(),
281 accept_policy.as_slice(),
282 Self::end_of_input_v6().as_slice(),
283 ]
284 .concat();
285
286 Self {
287 valid_hooks: NfIpHooks::MANGLE.bits(),
288 hook_entry,
289 underflow: hook_entry,
290 num_entries: 6,
291 size: entries.len() as u32,
292 entries,
293 ..Default::default()
294 }
295 }
296
297 fn default_ipv4_raw_table() -> Self {
298 let hook_entry = RAW_HOOKS.map(|n| n * u32::from(IPT_ENTRY_SIZE + STANDARD_TARGET_SIZE));
299 let accept_policy = Self::accept_policy_v4();
300 let entries = [
301 accept_policy.as_slice(),
302 accept_policy.as_slice(),
303 Self::end_of_input_v4().as_slice(),
304 ]
305 .concat();
306
307 Self {
308 valid_hooks: NfIpHooks::RAW.bits(),
309 hook_entry,
310 underflow: hook_entry,
311 num_entries: 3,
312 size: entries.len() as u32,
313 entries,
314 ..Default::default()
315 }
316 }
317
318 fn default_ipv6_raw_table() -> Self {
319 let hook_entry = RAW_HOOKS.map(|n| n * u32::from(IP6T_ENTRY_SIZE + STANDARD_TARGET_SIZE));
320 let accept_policy = Self::accept_policy_v6();
321 let entries = [
322 accept_policy.as_slice(),
323 accept_policy.as_slice(),
324 Self::end_of_input_v6().as_slice(),
325 ]
326 .concat();
327
328 Self {
329 valid_hooks: NfIpHooks::RAW.bits(),
330 hook_entry,
331 underflow: hook_entry,
332 num_entries: 3,
333 size: entries.len() as u32,
334 entries,
335 ..Default::default()
336 }
337 }
338}
339
340type IpTablesArray = [IpTable; iptables_utils::NUM_TABLES];
341
342impl Index<TableId> for IpTablesArray {
343 type Output = IpTable;
344
345 fn index(&self, index: TableId) -> &Self::Output {
346 &self[index as usize]
347 }
348}
349
350impl IndexMut<TableId> for IpTablesArray {
351 fn index_mut(&mut self, index: TableId) -> &mut Self::Output {
352 &mut self[index as usize]
353 }
354}
355
356fn default_ipv4_tables() -> IpTablesArray {
357 const_assert_eq!(TableId::Filter as usize, 0);
358 const_assert_eq!(TableId::Mangle as usize, 1);
359 const_assert_eq!(TableId::Nat as usize, 2);
360 const_assert_eq!(TableId::Raw as usize, 3);
361 [
362 IpTable::default_ipv4_filter_table(),
363 IpTable::default_ipv4_mangle_table(),
364 IpTable::default_ipv4_nat_table(),
365 IpTable::default_ipv4_raw_table(),
366 ]
367}
368
369fn default_ipv6_tables() -> IpTablesArray {
370 [
371 IpTable::default_ipv6_filter_table(),
372 IpTable::default_ipv6_mangle_table(),
373 IpTable::default_ipv6_nat_table(),
374 IpTable::default_ipv6_raw_table(),
375 ]
376}
377
378#[derive(Default, PartialEq, Eq, Copy, Clone)]
380struct IpTableSet(u64);
381
382impl IpTableSet {
383 fn element_index(ip: Ip, table: TableId) -> usize {
384 (ip as usize) * iptables_utils::NUM_TABLES + table as usize
385 }
386
387 fn add(&mut self, ip: Ip, table: TableId) {
388 self.0 |= 1 << Self::element_index(ip, table);
389 }
390
391 fn remove(&mut self, ip: Ip, table: TableId) {
392 self.0 &= !(1 << Self::element_index(ip, table));
393 }
394
395 fn is_empty(&self) -> bool {
396 *self == Self::default()
397 }
398}
399
400struct EbpfProgramState {
404 #[allow(dead_code)]
405 program: ProgramHandle,
406 tables: IpTableSet,
407}
408
409#[derive(Default)]
410struct LazyController {
411 controller: Option<Controller>,
412}
413
414impl LazyController {
415 fn get(&mut self) -> Result<&mut Controller, Errno> {
416 if self.controller.is_none() {
417 let control_proxy =
418 connect_to_protocol_sync::<fnet_filter::ControlMarker>().map_err(|e| {
419 log_error!("failed to connect to fuchsia.net.filter.Control: {e}");
420 errno!(EIO)
421 })?;
422 self.controller = Some(
423 Controller::new(
424 &control_proxy,
425 &ControllerId(NAMESPACE_ID_PREFIX.to_string()),
426 zx::MonotonicInstant::INFINITE,
427 )
428 .map_err(|e| {
429 log_error!("failed to create filter controller: {e}");
430 errno!(EIO)
431 })?,
432 );
433 }
434 Ok(self.controller.as_mut().expect("just ensured this is Some"))
435 }
436}
437
438struct IpTablesState {
439 ipv4: IpTablesArray,
440 ipv6: IpTablesArray,
441
442 controller: LazyController,
444
445 ebpf_programs: HashMap<febpf::ProgramId, EbpfProgramState>,
446}
447
448impl IpTablesState {
449 fn send_changes_to_net_filter(
450 &mut self,
451 changes: impl Iterator<Item = Change>,
452 ) -> Result<(), Errno> {
453 let controller = self.controller.get()?;
454 for chunk in &changes.chunks(fnet_filter::MAX_BATCH_SIZE as usize) {
455 match controller.push_changes(chunk.collect(), zx::MonotonicInstant::INFINITE) {
456 Ok(()) => {}
457 Err(
458 e @ (PushChangesError::CallMethod(_)
459 | PushChangesError::TooManyChanges
460 | PushChangesError::FidlConversion(_)),
461 ) => {
462 log_warn!(
463 "IpTables: failed to call \
464 fuchsia.net.filter.NamespaceController/PushChanges: {e}"
465 );
466 return error!(ECOMM);
467 }
468 Err(e @ PushChangesError::ErrorOnChange(_)) => {
469 log_warn!(
470 "IpTables: fuchsia.net.filter.NamespaceController/PushChanges \
471 returned error: {e}"
472 );
473 return error!(EINVAL);
474 }
475 }
476 }
477
478 match controller.commit_idempotent(zx::MonotonicInstant::INFINITE) {
479 Ok(()) => Ok(()),
480 Err(e @ (CommitError::CallMethod(_) | CommitError::FidlConversion(_))) => {
481 log_warn!(
482 "IpTables: failed to call \
483 fuchsia.net.filter.NamespaceController/Commit: {e}"
484 );
485 error!(ECOMM)
486 }
487 Err(
488 e @ (CommitError::RuleWithInvalidMatcher(_)
489 | CommitError::RuleWithInvalidAction(_)
490 | CommitError::TransparentProxyWithInvalidMatcher(_)
491 | CommitError::CyclicalRoutineGraph(_)
492 | CommitError::RedirectWithInvalidMatcher(_)
493 | CommitError::MasqueradeWithInvalidMatcher(_)
494 | CommitError::ErrorOnChange(_)),
495 ) => {
496 log_warn!(
497 "IpTables: fuchsia.net.filter.NamespaceController/Commit \
498 returned error: {e}"
499 );
500 error!(EINVAL)
501 }
502 }
503 }
504
505 fn register_ebpf_programs(
507 &mut self,
508 ip: Ip,
509 table_id: TableId,
510 ebpf_programs: HashMap<febpf::ProgramId, ProgramHandle>,
511 ) -> Result<(), Errno> {
512 let controller = self.controller.get()?;
514 for (id, program_handle) in &ebpf_programs {
515 if !self.ebpf_programs.contains_key(id) {
516 let program: &Program = program_handle.deref();
517 match controller.register_ebpf_program(
518 program.fidl_handle(),
519 program.try_into()?,
520 zx::MonotonicInstant::INFINITE,
521 ) {
522 Ok(_) => {}
523 Err(RegisterEbpfProgramError::AlreadyRegistered) => {}
524 Err(e) => {
525 log_warn!("IpTables: failed to register eBPF program: {e}");
526 return error!(EINVAL);
527 }
528 }
529 }
530 }
531
532 self.ebpf_programs.retain(|_id, program_state| {
534 program_state.tables.remove(ip, table_id);
535
536 !program_state.tables.is_empty()
537 });
538
539 for (id, program_handle) in &ebpf_programs {
541 let entry = self.ebpf_programs.entry(*id).or_insert_with(|| EbpfProgramState {
542 program: program_handle.clone(),
543 tables: IpTableSet::default(),
544 });
545 entry.tables.add(ip, table_id);
546 }
547
548 Ok(())
549 }
550
551 fn replace_table(
552 &mut self,
553 ip: Ip,
554 ebpf_programs: HashMap<febpf::ProgramId, ProgramHandle>,
555 table: iptables_utils::IpTable,
556 ) -> Result<(), Errno> {
557 let entries = table.parser.entries_bytes().to_vec();
558 let replace_info = table.parser.replace_info.clone();
559 let iptable_entry = IpTable {
560 num_entries: replace_info.num_entries as u32,
561 size: replace_info.size as u32,
562 entries,
563 num_counters: replace_info.num_counters,
564 valid_hooks: replace_info.valid_hooks.bits(),
565 hook_entry: replace_info.hook_entry,
566 underflow: replace_info.underflow,
567 counters: vec![],
568 };
569
570 let table_id = replace_info.table_id;
571 self.register_ebpf_programs(ip, table_id, ebpf_programs)?;
572
573 self.send_changes_to_net_filter(table.into_changes())?;
574
575 match ip {
576 Ip::V4 => self.ipv4[table_id] = iptable_entry,
577 Ip::V6 => self.ipv6[table_id] = iptable_entry,
578 }
579
580 Ok(())
581 }
582}
583
584pub struct IpTables {
586 state: OrderedRwLock<IpTablesState, KernelIpTables>,
587}
588
589impl IpTables {
590 pub fn new() -> Self {
591 Self {
595 state: OrderedRwLock::new(IpTablesState {
596 ipv4: default_ipv4_tables(),
597 ipv6: default_ipv6_tables(),
598 controller: LazyController::default(),
599 ebpf_programs: HashMap::new(),
600 }),
601 }
602 }
603
604 pub fn can_handle_getsockopt(level: u32, optname: u32) -> bool {
606 matches!(
607 (level, optname),
608 (
609 SOL_IP,
610 IPT_SO_GET_INFO
611 | IPT_SO_GET_ENTRIES
612 | IPT_SO_GET_REVISION_MATCH
613 | IPT_SO_GET_REVISION_TARGET,
614 ) | (
615 SOL_IPV6,
616 IP6T_SO_GET_INFO
617 | IP6T_SO_GET_ENTRIES
618 | IP6T_SO_GET_REVISION_MATCH
619 | IP6T_SO_GET_REVISION_TARGET,
620 )
621 )
622 }
623
624 pub fn can_handle_setsockopt(level: u32, optname: u32) -> bool {
626 matches!(
627 (level, optname),
628 (SOL_IP | SOL_IPV6, IPT_SO_SET_REPLACE | IPT_SO_SET_ADD_COUNTERS)
629 )
630 }
631
632 pub fn getsockopt(
633 &self,
634 locked: &mut Locked<Unlocked>,
635 current_task: &CurrentTask,
636 socket: &SocketHandle,
637 optname: u32,
638 mut optval: Vec<u8>,
639 ) -> Result<Vec<u8>, Errno> {
640 security::check_task_capable(current_task, CAP_NET_ADMIN)?;
641
642 if optval.is_empty() {
643 return error!(EINVAL);
644 }
645 if socket.socket_type != SocketType::Raw {
646 return error!(ENOPROTOOPT);
647 }
648
649 match optname {
650 IPT_SO_GET_INFO => {
652 if socket.domain == SocketDomain::Inet {
653 let (mut info, _) =
654 ipt_getinfo::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
655 let Ok(table_id) = TableId::try_from(&info.name) else {
656 return error!(EINVAL);
657 };
658 let state = self.state.read(locked);
659 let table = &state.ipv4[table_id];
660 info.valid_hooks = table.valid_hooks;
661 info.hook_entry = table.hook_entry;
662 info.underflow = table.underflow;
663 info.num_entries = table.num_entries;
664 info.size = table.size;
665 Ok(info.as_bytes().to_vec())
666 } else {
667 let (mut info, _) =
668 ip6t_getinfo::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
669 let Ok(table_id) = TableId::try_from(&info.name) else {
670 return error!(EINVAL);
671 };
672 let state = self.state.read(locked);
673 let table = &state.ipv6[table_id];
674 info.valid_hooks = table.valid_hooks;
675 info.hook_entry = table.hook_entry;
676 info.underflow = table.underflow;
677 info.num_entries = table.num_entries;
678 info.size = table.size;
679 Ok(info.as_bytes().to_vec())
680 }
681 }
682
683 IPT_SO_GET_ENTRIES => {
685 if socket.domain == SocketDomain::Inet {
686 let (get_entries, _) =
687 ipt_get_entries::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
688 let Ok(table_id) = TableId::try_from(&get_entries.name) else {
689 return error!(EINVAL);
690 };
691 let mut entry_bytes = self.state.read(locked).ipv4[table_id].entries.clone();
692
693 if entry_bytes.len() > get_entries.size as usize {
694 log_warn!("Entries are longer than expected so truncating.");
695 entry_bytes.truncate(get_entries.size as usize);
696 }
697
698 optval.truncate(std::mem::size_of::<ipt_get_entries>());
699 optval.append(&mut entry_bytes);
700 } else {
701 let (get_entries, _) =
702 ip6t_get_entries::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
703 let Ok(table_id) = TableId::try_from(&get_entries.name) else {
704 return error!(EINVAL);
705 };
706 let mut entry_bytes = self.state.read(locked).ipv6[table_id].entries.clone();
707
708 if entry_bytes.len() > get_entries.size as usize {
709 log_warn!("Entries are longer than expected so truncating.");
710 entry_bytes.truncate(get_entries.size as usize);
711 }
712
713 optval.truncate(std::mem::size_of::<ip6t_get_entries>());
714 optval.append(&mut entry_bytes);
715 }
716 Ok(optval)
717 }
718
719 IPT_SO_GET_REVISION_MATCH | IP6T_SO_GET_REVISION_MATCH => {
721 let (mut revision, _) =
722 xt_get_revision::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
723 revision.revision = u8::MAX;
724 Ok(revision.as_bytes().to_vec())
725 }
726
727 IPT_SO_GET_REVISION_TARGET | IP6T_SO_GET_REVISION_TARGET => {
729 let (mut revision, _) =
730 xt_get_revision::read_from_prefix(&*optval).map_err(|_| errno!(EINVAL))?;
731 revision.revision = u8::MAX;
732 Ok(revision.as_bytes().to_vec())
733 }
734
735 _ => {
736 track_stub!(TODO("https://fxbug.dev/322875228"), "optname for network sockets");
737 Ok(vec![])
738 }
739 }
740 }
741
742 pub fn setsockopt(
743 &self,
744 locked: &mut Locked<Unlocked>,
745 current_task: &CurrentTask,
746 socket: &SocketHandle,
747 optname: u32,
748 optval: SockOptValue,
749 ) -> Result<(), Errno> {
750 security::check_task_capable(current_task, CAP_NET_ADMIN)?;
751
752 let mut bytes = optval.to_vec(current_task)?;
753 match optname {
754 IPT_SO_SET_REPLACE => {
756 if socket.domain == SocketDomain::Inet {
758 self.replace_ipv4_table(locked, current_task, bytes)
759 } else {
760 self.replace_ipv6_table(locked, current_task, bytes)
761 }
762 }
763
764 IPT_SO_SET_ADD_COUNTERS => {
766 let (counters_info, _) =
767 xt_counters_info::read_from_prefix(&*bytes).map_err(|_| errno!(EINVAL))?;
768
769 let Ok(table_id) = TableId::try_from(&counters_info.name) else {
770 return error!(EINVAL);
771 };
772
773 let mut state = self.state.write(locked);
774 let entry: &mut IpTable = match socket.domain {
775 SocketDomain::Inet => &mut state.ipv4[table_id],
776 _ => &mut state.ipv6[table_id],
777 };
778
779 entry.num_counters = counters_info.num_counters;
780 let mut counters = vec![];
781 bytes = bytes.split_off(std::mem::size_of::<xt_counters_info>());
782 for chunk in bytes.chunks(std::mem::size_of::<xt_counters>()) {
783 counters
784 .push(xt_counters::read_from_prefix(chunk).map_err(|_| errno!(EINVAL))?.0);
785 }
786 entry.counters = counters;
787 Ok(())
788 }
789
790 _ => Ok(()),
791 }
792 }
793
794 fn replace_ipv4_table(
795 &self,
796 locked: &mut Locked<Unlocked>,
797 current_task: &CurrentTask,
798 bytes: Vec<u8>,
799 ) -> Result<(), Errno> {
800 let mut ebpf_state = IpTablesEbpfState::new(locked, current_task);
801 let table =
802 iptables_utils::IpTable::from_ipt_replace(&mut ebpf_state, bytes).map_err(|e| {
803 log_warn!("Iptables: encountered error while parsing rules: {e}");
804 errno!(EINVAL)
805 })?;
806 let ebpf_programs = ebpf_state.take_programs();
807 self.state.write(locked).replace_table(Ip::V4, ebpf_programs, table)?;
808
809 Ok(())
810 }
811
812 fn replace_ipv6_table(
813 &self,
814 locked: &mut Locked<Unlocked>,
815 current_task: &CurrentTask,
816 bytes: Vec<u8>,
817 ) -> Result<(), Errno> {
818 let mut ebpf_state = IpTablesEbpfState::new(locked, current_task);
819 let table =
820 iptables_utils::IpTable::from_ip6t_replace(&mut ebpf_state, bytes).map_err(|e| {
821 log_warn!("Iptables: encountered error while parsing rules: {e}");
822 errno!(EINVAL)
823 })?;
824 let ebpf_programs = ebpf_state.take_programs();
825 self.state.write(locked).replace_table(Ip::V6, ebpf_programs, table)?;
826
827 Ok(())
828 }
829}
830
831struct IpTablesEbpfState<'a> {
832 locked: &'a mut Locked<Unlocked>,
833 current_task: &'a CurrentTask,
834 ebpf_programs: HashMap<febpf::ProgramId, ProgramHandle>,
835}
836
837impl<'a> IpTablesEbpfState<'a> {
838 fn new(locked: &'a mut Locked<Unlocked>, current_task: &'a CurrentTask) -> Self {
839 Self { locked, current_task, ebpf_programs: HashMap::default() }
840 }
841
842 fn take_programs(self) -> HashMap<febpf::ProgramId, ProgramHandle> {
843 self.ebpf_programs
844 }
845}
846
847impl<'a> IptReplaceContext for IpTablesEbpfState<'a> {
848 fn resolve_ebpf_socket_filter(
849 &mut self,
850 path: &BString,
851 ) -> Result<febpf::ProgramId, IpTableParseError> {
852 let program = resolve_pinned_bpf_object(
853 self.locked,
854 self.current_task,
855 path.as_ref(),
856 OpenFlags::RDONLY,
857 )
858 .and_then(|(handle, _node)| handle.into_program())
859 .map_err(|e| {
860 log_warn!("Failed to resolve eBPF program path {} for iptable matcher: {:?}", path, e);
861 IpTableParseError::InvalidEbpfProgramPath { path: path.clone() }
862 })?;
863
864 let id = program.fidl_id();
865 self.ebpf_programs.insert(id, program);
866 Ok(id)
867 }
868}