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