Browse Source

Add compile-time configuration options for counts and buffer sizes.

Dario Nieuwenhuis 1 year ago
parent
commit
b047cbeade
12 changed files with 466 additions and 85 deletions
  1. 135 0
      Cargo.toml
  2. 70 1
      README.md
  3. 101 0
      build.rs
  4. 83 0
      gen_config.py
  5. 11 20
      src/iface/fragmentation.rs
  6. 11 13
      src/iface/interface/mod.rs
  7. 1 1
      src/iface/interface/tests.rs
  8. 2 6
      src/iface/neighbor.rs
  9. 4 5
      src/iface/route.rs
  10. 23 0
      src/lib.rs
  11. 6 9
      src/socket/dns.rs
  12. 19 30
      src/storage/assembler.rs

+ 135 - 0
Cargo.toml

@@ -80,6 +80,141 @@ default = [
 
 "_proto-fragmentation" = []
 
+# BEGIN AUTOGENERATED CONFIG FEATURES
+# Generated by gen_config.py. DO NOT EDIT.
+iface-max-addr-count-1 = []
+iface-max-addr-count-2 = [] # Default
+iface-max-addr-count-3 = []
+iface-max-addr-count-4 = []
+iface-max-addr-count-5 = []
+iface-max-addr-count-6 = []
+iface-max-addr-count-7 = []
+iface-max-addr-count-8 = []
+
+iface-max-multicast-group-count-1 = []
+iface-max-multicast-group-count-2 = []
+iface-max-multicast-group-count-3 = []
+iface-max-multicast-group-count-4 = [] # Default
+iface-max-multicast-group-count-5 = []
+iface-max-multicast-group-count-6 = []
+iface-max-multicast-group-count-7 = []
+iface-max-multicast-group-count-8 = []
+iface-max-multicast-group-count-16 = []
+iface-max-multicast-group-count-32 = []
+iface-max-multicast-group-count-64 = []
+iface-max-multicast-group-count-128 = []
+iface-max-multicast-group-count-256 = []
+iface-max-multicast-group-count-512 = []
+iface-max-multicast-group-count-1024 = []
+
+iface-max-sixlowpan-address-context-count-1 = []
+iface-max-sixlowpan-address-context-count-2 = []
+iface-max-sixlowpan-address-context-count-3 = []
+iface-max-sixlowpan-address-context-count-4 = [] # Default
+iface-max-sixlowpan-address-context-count-5 = []
+iface-max-sixlowpan-address-context-count-6 = []
+iface-max-sixlowpan-address-context-count-7 = []
+iface-max-sixlowpan-address-context-count-8 = []
+iface-max-sixlowpan-address-context-count-16 = []
+iface-max-sixlowpan-address-context-count-32 = []
+iface-max-sixlowpan-address-context-count-64 = []
+iface-max-sixlowpan-address-context-count-128 = []
+iface-max-sixlowpan-address-context-count-256 = []
+iface-max-sixlowpan-address-context-count-512 = []
+iface-max-sixlowpan-address-context-count-1024 = []
+
+iface-neighbor-cache-count-1 = []
+iface-neighbor-cache-count-2 = []
+iface-neighbor-cache-count-3 = []
+iface-neighbor-cache-count-4 = [] # Default
+iface-neighbor-cache-count-5 = []
+iface-neighbor-cache-count-6 = []
+iface-neighbor-cache-count-7 = []
+iface-neighbor-cache-count-8 = []
+iface-neighbor-cache-count-16 = []
+iface-neighbor-cache-count-32 = []
+iface-neighbor-cache-count-64 = []
+iface-neighbor-cache-count-128 = []
+iface-neighbor-cache-count-256 = []
+iface-neighbor-cache-count-512 = []
+iface-neighbor-cache-count-1024 = []
+
+iface-max-route-count-1 = []
+iface-max-route-count-2 = [] # Default
+iface-max-route-count-3 = []
+iface-max-route-count-4 = []
+iface-max-route-count-5 = []
+iface-max-route-count-6 = []
+iface-max-route-count-7 = []
+iface-max-route-count-8 = []
+iface-max-route-count-16 = []
+iface-max-route-count-32 = []
+iface-max-route-count-64 = []
+iface-max-route-count-128 = []
+iface-max-route-count-256 = []
+iface-max-route-count-512 = []
+iface-max-route-count-1024 = []
+
+fragmentation-buffer-size-256 = []
+fragmentation-buffer-size-512 = []
+fragmentation-buffer-size-1024 = []
+fragmentation-buffer-size-1500 = [] # Default
+fragmentation-buffer-size-2048 = []
+fragmentation-buffer-size-4096 = []
+fragmentation-buffer-size-8192 = []
+fragmentation-buffer-size-16384 = []
+fragmentation-buffer-size-32768 = []
+fragmentation-buffer-size-65536 = []
+
+assembler-max-segment-count-1 = []
+assembler-max-segment-count-2 = []
+assembler-max-segment-count-3 = []
+assembler-max-segment-count-4 = [] # Default
+assembler-max-segment-count-8 = []
+assembler-max-segment-count-16 = []
+assembler-max-segment-count-32 = []
+
+reassembly-buffer-size-256 = []
+reassembly-buffer-size-512 = []
+reassembly-buffer-size-1024 = []
+reassembly-buffer-size-1500 = [] # Default
+reassembly-buffer-size-2048 = []
+reassembly-buffer-size-4096 = []
+reassembly-buffer-size-8192 = []
+reassembly-buffer-size-16384 = []
+reassembly-buffer-size-32768 = []
+reassembly-buffer-size-65536 = []
+
+reassembly-buffer-count-1 = [] # Default
+reassembly-buffer-count-2 = []
+reassembly-buffer-count-3 = []
+reassembly-buffer-count-4 = []
+reassembly-buffer-count-8 = []
+reassembly-buffer-count-16 = []
+reassembly-buffer-count-32 = []
+
+dns-max-result-count-1 = [] # Default
+dns-max-result-count-2 = []
+dns-max-result-count-3 = []
+dns-max-result-count-4 = []
+dns-max-result-count-8 = []
+dns-max-result-count-16 = []
+dns-max-result-count-32 = []
+
+dns-max-server-count-1 = [] # Default
+dns-max-server-count-2 = []
+dns-max-server-count-3 = []
+dns-max-server-count-4 = []
+dns-max-server-count-8 = []
+dns-max-server-count-16 = []
+dns-max-server-count-32 = []
+
+dns-max-name-size-64 = []
+dns-max-name-size-128 = []
+dns-max-name-size-255 = [] # Default
+
+# END AUTOGENERATED CONFIG FEATURES
+
 [[example]]
 name = "packet2pcap"
 path = "utils/packet2pcap.rs"

+ 70 - 1
README.md

@@ -147,6 +147,8 @@ You probably want to disable default features and configure them one by one:
 smoltcp = { version = "0.8.0", default-features = false, features = ["log"] }
 ```
 
+## Feature flags
+
 ### Feature `std`
 
 The `std` feature enables use of objects and slices owned by the networking stack through a
@@ -195,7 +197,7 @@ Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respective
 
 These features are enabled by default.
 
-### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4`
+### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4`, `socket-dns`
 
 Enable the corresponding socket type.
 
@@ -208,6 +210,73 @@ Enable [IPv4] and [IPv6] respectively.
 [IPv4]: https://tools.ietf.org/rfc/rfc791.txt
 [IPv6]: https://tools.ietf.org/rfc/rfc8200.txt
 
+## Configuration
+
+_smoltcp_ has some configuration settings that are set at compile time, affecting sizes
+and counts of buffers.
+
+They can be set in two ways:
+
+- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and
+use dashes instead of underscores. For example. `iface-max-addr-count-3`. Only a selection of values
+is available, check `Cargo.toml` for the list.
+- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example 
+`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. 
+Any value can be set, unlike with Cargo features.
+
+Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
+with different values, compilation fails.
+
+### IFACE_MAX_ADDR_COUNT
+
+Max amount of IP addresses that can be assigned to one interface (counting both IPv4 and IPv6 addresses). Default: 2.
+
+### IFACE_MAX_MULTICAST_GROUP_COUNT
+
+Max amount of multicast groups that can be joined by one interface. Default: 4.
+
+### IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT
+
+Max amount of 6LoWPAN address contexts that can be assigned to one interface. Default: 4.
+
+### IFACE_NEIGHBOR_CACHE_COUNT
+
+Amount of "IP address -> hardware address" entries the neighbor cache (also known as the "ARP cache" or the "ARP table") holds. Default: 4.
+
+### IFACE_MAX_ROUTE_COUNT
+
+Max amount of routes that can be added to one interface. Includes the default route. Includes both IPv4 and IPv6. Default: 2.
+
+### FRAGMENTATION_BUFFER_SIZE
+
+Size of the buffer used for fragmenting outgoing packets larger than the MTU. Packets larger than this setting will be dropped instead of fragmented. Default: 1500.
+
+### ASSEMBLER_MAX_SEGMENT_COUNT
+
+Maximum number of non-contiguous segments the assembler can hold. Used for both packet reassembly and TCP stream reassembly. Default: 4.
+
+### REASSEMBLY_BUFFER_SIZE
+
+Size of the buffer used for reassembling (de-fragmenting) incoming packets. If the reassembled packet is larger than this setting, it will be dropped instead of reassembled. Default: 1500.
+
+### REASSEMBLY_BUFFER_COUNT
+
+Number of reassembly buffers, i.e how many different incoming packets can be reassembled at the same time. Default: 1.
+
+### DNS_MAX_RESULT_COUNT
+
+Maximum amount of address results for a given DNS query that will be kept. For example, if this is set to 2 and the queried name has 4 `A` records, only the first 2 will be returned. Default: 1.
+
+### DNS_MAX_SERVER_COUNT
+
+Maximum amount of DNS servers that can be configured in one DNS socket. Default: 1.
+
+### DNS_MAX_NAME_SIZE
+
+Maximum length of DNS names that can be queried. Default: 255.
+
+
+
 ## Hosted usage examples
 
 _smoltcp_, being a freestanding networking stack, needs to be able to transmit and receive

+ 101 - 0
build.rs

@@ -0,0 +1,101 @@
+use std::collections::HashMap;
+use std::fmt::Write;
+use std::path::PathBuf;
+use std::{env, fs};
+
+static CONFIGS: &[(&str, usize)] = &[
+    // BEGIN AUTOGENERATED CONFIG FEATURES
+    // Generated by gen_config.py. DO NOT EDIT.
+    ("IFACE_MAX_ADDR_COUNT", 2),
+    ("IFACE_MAX_MULTICAST_GROUP_COUNT", 4),
+    ("IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT", 4),
+    ("IFACE_NEIGHBOR_CACHE_COUNT", 4),
+    ("IFACE_MAX_ROUTE_COUNT", 2),
+    ("FRAGMENTATION_BUFFER_SIZE", 1500),
+    ("ASSEMBLER_MAX_SEGMENT_COUNT", 4),
+    ("REASSEMBLY_BUFFER_SIZE", 1500),
+    ("REASSEMBLY_BUFFER_COUNT", 1),
+    ("DNS_MAX_RESULT_COUNT", 1),
+    ("DNS_MAX_SERVER_COUNT", 1),
+    ("DNS_MAX_NAME_SIZE", 255),
+    // END AUTOGENERATED CONFIG FEATURES
+];
+
+struct ConfigState {
+    value: usize,
+    seen_feature: bool,
+    seen_env: bool,
+}
+
+fn main() {
+    // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
+    // other file changed.
+    println!("cargo:rerun-if-changed=build.rs");
+
+    // Rebuild if config envvar changed.
+    for (name, _) in CONFIGS {
+        println!("cargo:rerun-if-env-changed=SMOLTCP_{name}");
+    }
+
+    let mut configs = HashMap::new();
+    for (name, default) in CONFIGS {
+        configs.insert(
+            *name,
+            ConfigState {
+                value: *default,
+                seen_env: false,
+                seen_feature: false,
+            },
+        );
+    }
+
+    for (var, value) in env::vars() {
+        if let Some(name) = var.strip_prefix("SMOLTCP_") {
+            let Some(cfg) = configs.get_mut(name) else {
+                panic!("Unknown env var {name}")
+            };
+
+            let Ok(value) = value.parse::<usize>() else {
+                panic!("Invalid value for env var {name}: {value}")
+            };
+
+            cfg.value = value;
+            cfg.seen_env = true;
+        }
+
+        if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
+            if let Some(i) = feature.rfind('_') {
+                let name = &feature[..i];
+                let value = &feature[i + 1..];
+                if let Some(cfg) = configs.get_mut(name) {
+                    let Ok(value) = value.parse::<usize>() else {
+                        panic!("Invalid value for feature {name}: {value}")
+                    };
+
+                    // envvars take priority.
+                    if !cfg.seen_env {
+                        if cfg.seen_feature {
+                            panic!(
+                                "multiple values set for feature {}: {} and {}",
+                                name, cfg.value, value
+                            );
+                        }
+
+                        cfg.value = value;
+                        cfg.seen_feature = true;
+                    }
+                }
+            }
+        }
+    }
+
+    let mut data = String::new();
+
+    for (name, cfg) in &configs {
+        writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
+    }
+
+    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+    let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
+    fs::write(out_file, data).unwrap();
+}

+ 83 - 0
gen_config.py

@@ -0,0 +1,83 @@
+import os
+
+abspath = os.path.abspath(__file__)
+dname = os.path.dirname(abspath)
+os.chdir(dname)
+
+features = []
+
+
+def feature(name, default, min, max, pow2=None):
+    vals = set()
+    val = min
+    while val <= max:
+        vals.add(val)
+        if pow2 == True or (isinstance(pow2, int) and val >= pow2):
+            val *= 2
+        else:
+            val += 1
+    vals.add(default)
+
+    features.append(
+        {
+            "name": name,
+            "default": default,
+            "vals": sorted(list(vals)),
+        }
+    )
+
+
+feature("iface_max_addr_count", default=2, min=1, max=8)
+feature("iface_max_multicast_group_count", default=4, min=1, max=1024, pow2=8)
+feature("iface_max_sixlowpan_address_context_count", default=4, min=1, max=1024, pow2=8)
+feature("iface_neighbor_cache_count", default=4, min=1, max=1024, pow2=8)
+feature("iface_max_route_count", default=2, min=1, max=1024, pow2=8)
+feature("fragmentation_buffer_size", default=1500, min=256, max=65536, pow2=True)
+feature("assembler_max_segment_count", default=4, min=1, max=32, pow2=4)
+feature("reassembly_buffer_size", default=1500, min=256, max=65536, pow2=True)
+feature("reassembly_buffer_count", default=1, min=1, max=32, pow2=4)
+feature("dns_max_result_count", default=1, min=1, max=32, pow2=4)
+feature("dns_max_server_count", default=1, min=1, max=32, pow2=4)
+feature("dns_max_name_size", default=255, min=64, max=255, pow2=True)
+
+# ========= Update Cargo.toml
+
+things = ""
+for f in features:
+    name = f["name"].replace("_", "-")
+    for val in f["vals"]:
+        things += f"{name}-{val} = []"
+        if val == f["default"]:
+            things += " # Default"
+        things += "\n"
+    things += "\n"
+
+SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
+SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
+HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
+with open("Cargo.toml", "r") as f:
+    data = f.read()
+before, data = data.split(SEPARATOR_START, maxsplit=1)
+_, after = data.split(SEPARATOR_END, maxsplit=1)
+data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
+with open("Cargo.toml", "w") as f:
+    f.write(data)
+
+
+# ========= Update build.rs
+
+things = ""
+for f in features:
+    name = f["name"].upper()
+    things += f'    ("{name}", {f["default"]}),\n'
+
+SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
+SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
+HELP = "    // Generated by gen_config.py. DO NOT EDIT.\n"
+with open("build.rs", "r") as f:
+    data = f.read()
+before, data = data.split(SEPARATOR_START, maxsplit=1)
+_, after = data.split(SEPARATOR_END, maxsplit=1)
+data = before + SEPARATOR_START + HELP + things + "    " + SEPARATOR_END + after
+with open("build.rs", "w") as f:
+    f.write(data)

+ 11 - 20
src/iface/fragmentation.rs

@@ -4,18 +4,14 @@ use core::fmt;
 
 use managed::{ManagedMap, ManagedSlice};
 
+use crate::config::{REASSEMBLY_BUFFER_COUNT, REASSEMBLY_BUFFER_SIZE};
 use crate::storage::Assembler;
 use crate::time::{Duration, Instant};
 
-// TODO: make configurable.
-const BUFFER_SIZE: usize = 1500;
-
 #[cfg(feature = "alloc")]
 type Buffer = alloc::vec::Vec<u8>;
 #[cfg(not(feature = "alloc"))]
-type Buffer = [u8; BUFFER_SIZE];
-
-const PACKET_ASSEMBLER_COUNT: usize = 4;
+type Buffer = [u8; REASSEMBLY_BUFFER_SIZE];
 
 /// Problem when assembling: something was out of bounds.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -44,14 +40,14 @@ pub struct PacketAssembler<K> {
 
 impl<K> PacketAssembler<K> {
     /// Create a new empty buffer for fragments.
-    pub fn new() -> Self {
+    pub const fn new() -> Self {
         Self {
             key: None,
 
             #[cfg(feature = "alloc")]
             buffer: Buffer::new(),
             #[cfg(not(feature = "alloc"))]
-            buffer: [0u8; BUFFER_SIZE],
+            buffer: [0u8; REASSEMBLY_BUFFER_SIZE],
 
             assembler: Assembler::new(),
             total_size: None,
@@ -172,20 +168,16 @@ impl<K> PacketAssembler<K> {
 /// Set holding multiple [`PacketAssembler`].
 #[derive(Debug)]
 pub struct PacketAssemblerSet<K: Eq + Copy> {
-    assemblers: [PacketAssembler<K>; PACKET_ASSEMBLER_COUNT],
+    assemblers: [PacketAssembler<K>; REASSEMBLY_BUFFER_COUNT],
 }
 
 impl<K: Eq + Copy> PacketAssemblerSet<K> {
+    const NEW_PA: PacketAssembler<K> = PacketAssembler::new();
+
     /// Create a new set of packet assemblers.
     pub fn new() -> Self {
         Self {
-            // TODO: support any PACKET_ASSEMBLER_COUNT
-            assemblers: [
-                PacketAssembler::new(),
-                PacketAssembler::new(),
-                PacketAssembler::new(),
-                PacketAssembler::new(),
-            ],
+            assemblers: [Self::NEW_PA; REASSEMBLY_BUFFER_COUNT],
         }
     }
 
@@ -291,10 +283,9 @@ mod tests {
     #[test]
     fn packet_assembler_set_full() {
         let mut set = PacketAssemblerSet::new();
-        set.get(&Key { id: 0 }, Instant::ZERO).unwrap();
-        set.get(&Key { id: 1 }, Instant::ZERO).unwrap();
-        set.get(&Key { id: 2 }, Instant::ZERO).unwrap();
-        set.get(&Key { id: 3 }, Instant::ZERO).unwrap();
+        for i in 0..REASSEMBLY_BUFFER_COUNT {
+            set.get(&Key { id: i }, Instant::ZERO).unwrap();
+        }
         assert!(set.get(&Key { id: 4 }, Instant::ZERO).is_err());
     }
 

+ 11 - 13
src/iface/interface/mod.rs

@@ -27,6 +27,10 @@ use super::fragmentation::PacketAssemblerSet;
 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
 use super::socket_set::SocketSet;
+use crate::config::{
+    FRAGMENTATION_BUFFER_SIZE, IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT,
+    IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
+};
 use crate::iface::Routes;
 use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
 use crate::rand::Rand;
@@ -36,13 +40,6 @@ use crate::socket::*;
 use crate::time::{Duration, Instant};
 use crate::wire::*;
 
-const MAX_IP_ADDR_COUNT: usize = 5;
-#[cfg(feature = "proto-igmp")]
-const MAX_IPV4_MULTICAST_GROUPS: usize = 4;
-const FRAGMENTATION_BUFFER_SIZE: usize = 1500;
-#[cfg(feature = "proto-sixlowpan")]
-const SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4;
-
 #[cfg(feature = "_proto-fragmentation")]
 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -248,15 +245,16 @@ pub struct InterfaceInner {
     #[cfg(feature = "proto-ipv4-fragmentation")]
     ipv4_id: u16,
     #[cfg(feature = "proto-sixlowpan")]
-    sixlowpan_address_context: Vec<SixlowpanAddressContext, SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
+    sixlowpan_address_context:
+        Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
     tag: u16,
-    ip_addrs: Vec<IpCidr, MAX_IP_ADDR_COUNT>,
+    ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
     #[cfg(feature = "proto-ipv4")]
     any_ip: bool,
     routes: Routes,
     #[cfg(feature = "proto-igmp")]
-    ipv4_multicast_groups: LinearMap<Ipv4Address, (), MAX_IPV4_MULTICAST_GROUPS>,
+    ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
     /// When to report for (all or) the next multicast group membership via IGMP
     #[cfg(feature = "proto-igmp")]
     igmp_report_state: IgmpReportState,
@@ -642,7 +640,7 @@ impl Interface {
     ///
     /// # Panics
     /// This function panics if any of the addresses are not unicast.
-    pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, MAX_IP_ADDR_COUNT>)>(&mut self, f: F) {
+    pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
         f(&mut self.inner.ip_addrs);
         InterfaceInner::flush_cache(&mut self.inner);
         InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
@@ -690,7 +688,7 @@ impl Interface {
     #[cfg(feature = "proto-sixlowpan")]
     pub fn sixlowpan_address_context(
         &self,
-    ) -> &Vec<SixlowpanAddressContext, SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
+    ) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
         &self.inner.sixlowpan_address_context
     }
 
@@ -698,7 +696,7 @@ impl Interface {
     #[cfg(feature = "proto-sixlowpan")]
     pub fn sixlowpan_address_context_mut(
         &mut self,
-    ) -> &mut Vec<SixlowpanAddressContext, SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
+    ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
         &mut self.inner.sixlowpan_address_context
     }
 

+ 1 - 1
src/iface/interface/tests.rs

@@ -1012,7 +1012,7 @@ fn test_icmpv4_socket() {
 #[cfg(feature = "proto-ipv6")]
 fn test_solicited_node_addrs() {
     let (mut iface, _, _device) = create(MEDIUM);
-    let mut new_addrs = heapless::Vec::<IpCidr, MAX_IP_ADDR_COUNT>::new();
+    let mut new_addrs = heapless::Vec::<IpCidr, IFACE_MAX_ADDR_COUNT>::new();
     new_addrs
         .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64))
         .unwrap();

+ 2 - 6
src/iface/neighbor.rs

@@ -3,14 +3,10 @@
 
 use heapless::LinearMap;
 
+use crate::config::IFACE_NEIGHBOR_CACHE_COUNT;
 use crate::time::{Duration, Instant};
 use crate::wire::{HardwareAddress, IpAddress};
 
-#[cfg(not(test))]
-pub const NEIGHBOR_CACHE_SIZE: usize = 16;
-#[cfg(test)]
-pub const NEIGHBOR_CACHE_SIZE: usize = 3;
-
 /// A cached neighbor.
 ///
 /// A neighbor mapping translates from a protocol address to a hardware address,
@@ -48,7 +44,7 @@ impl Answer {
 /// A neighbor cache backed by a map.
 #[derive(Debug)]
 pub struct Cache {
-    storage: LinearMap<IpAddress, Neighbor, NEIGHBOR_CACHE_SIZE>,
+    storage: LinearMap<IpAddress, Neighbor, IFACE_NEIGHBOR_CACHE_COUNT>,
     silent_until: Instant,
 }
 

+ 4 - 5
src/iface/route.rs

@@ -1,14 +1,13 @@
-use crate::time::Instant;
 use heapless::Vec;
 
+use crate::config::IFACE_MAX_ROUTE_COUNT;
+use crate::time::Instant;
 use crate::wire::{IpAddress, IpCidr};
 #[cfg(feature = "proto-ipv4")]
 use crate::wire::{Ipv4Address, Ipv4Cidr};
 #[cfg(feature = "proto-ipv6")]
 use crate::wire::{Ipv6Address, Ipv6Cidr};
 
-pub const MAX_ROUTE_COUNT: usize = 4;
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct RouteTableFull;
@@ -58,7 +57,7 @@ impl Route {
 /// A routing table.
 #[derive(Debug)]
 pub struct Routes {
-    storage: Vec<Route, MAX_ROUTE_COUNT>,
+    storage: Vec<Route, IFACE_MAX_ROUTE_COUNT>,
 }
 
 impl Routes {
@@ -70,7 +69,7 @@ impl Routes {
     }
 
     /// Update the routes of this node.
-    pub fn update<F: FnOnce(&mut Vec<Route, MAX_ROUTE_COUNT>)>(&mut self, f: F) {
+    pub fn update<F: FnOnce(&mut Vec<Route, IFACE_MAX_ROUTE_COUNT>)>(&mut self, f: F) {
         f(&mut self.storage);
     }
 

+ 23 - 0
src/lib.rs

@@ -129,6 +129,29 @@ mod macros;
 mod parsers;
 mod rand;
 
+#[cfg(test)]
+mod config {
+    #![allow(unused)]
+    pub const ASSEMBLER_MAX_SEGMENT_COUNT: usize = 4;
+    pub const DNS_MAX_NAME_SIZE: usize = 255;
+    pub const DNS_MAX_RESULT_COUNT: usize = 1;
+    pub const DNS_MAX_SERVER_COUNT: usize = 1;
+    pub const FRAGMENTATION_BUFFER_SIZE: usize = 1500;
+    pub const IFACE_MAX_ADDR_COUNT: usize = 8;
+    pub const IFACE_MAX_MULTICAST_GROUP_COUNT: usize = 4;
+    pub const IFACE_MAX_ROUTE_COUNT: usize = 4;
+    pub const IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4;
+    pub const IFACE_NEIGHBOR_CACHE_COUNT: usize = 3;
+    pub const REASSEMBLY_BUFFER_COUNT: usize = 4;
+    pub const REASSEMBLY_BUFFER_SIZE: usize = 1500;
+}
+
+#[cfg(not(test))]
+mod config {
+    #![allow(unused)]
+    include!(concat!(env!("OUT_DIR"), "/config.rs"));
+}
+
 #[cfg(any(
     feature = "medium-ethernet",
     feature = "medium-ip",

+ 6 - 9
src/socket/dns.rs

@@ -4,6 +4,7 @@ use core::task::Waker;
 use heapless::Vec;
 use managed::ManagedSlice;
 
+use crate::config::{DNS_MAX_NAME_SIZE, DNS_MAX_RESULT_COUNT, DNS_MAX_SERVER_COUNT};
 use crate::socket::{Context, PollAt};
 use crate::time::{Duration, Instant};
 use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type};
@@ -12,12 +13,8 @@ use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr};
 #[cfg(feature = "async")]
 use super::WakerRegistration;
 
-pub const MAX_ADDRESS_COUNT: usize = 4;
-pub const MAX_SERVER_COUNT: usize = 4;
-
 const DNS_PORT: u16 = 53;
 const MDNS_DNS_PORT: u16 = 5353;
-const MAX_NAME_LEN: usize = 255;
 const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000);
 const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000);
 const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs
@@ -79,7 +76,7 @@ enum State {
 
 #[derive(Debug)]
 struct PendingQuery {
-    name: Vec<u8, MAX_NAME_LEN>,
+    name: Vec<u8, DNS_MAX_NAME_SIZE>,
     type_: Type,
 
     port: u16, // UDP port (src for request, dst for response)
@@ -102,7 +99,7 @@ pub enum MulticastDns {
 
 #[derive(Debug)]
 struct CompletedQuery {
-    addresses: Vec<IpAddress, MAX_ADDRESS_COUNT>,
+    addresses: Vec<IpAddress, DNS_MAX_RESULT_COUNT>,
 }
 
 /// A handle to an in-progress DNS query.
@@ -115,7 +112,7 @@ pub struct QueryHandle(usize);
 /// packet buffers.
 #[derive(Debug)]
 pub struct Socket<'a> {
-    servers: Vec<IpAddress, MAX_SERVER_COUNT>,
+    servers: Vec<IpAddress, DNS_MAX_SERVER_COUNT>,
     queries: ManagedSlice<'a, Option<DnsQuery>>,
 
     /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
@@ -216,7 +213,7 @@ impl<'a> Socket<'a> {
             name = &name[..name.len() - 1];
         }
 
-        let mut raw_name: Vec<u8, MAX_NAME_LEN> = Vec::new();
+        let mut raw_name: Vec<u8, DNS_MAX_NAME_SIZE> = Vec::new();
 
         let mut mdns = MulticastDns::Disabled;
         #[cfg(feature = "socket-mdns")]
@@ -292,7 +289,7 @@ impl<'a> Socket<'a> {
     pub fn get_query_result(
         &mut self,
         handle: QueryHandle,
-    ) -> Result<Vec<IpAddress, MAX_ADDRESS_COUNT>, GetQueryResultError> {
+    ) -> Result<Vec<IpAddress, DNS_MAX_RESULT_COUNT>, GetQueryResultError> {
         let slot = &mut self.queries[handle.0];
         let q = slot.as_mut().unwrap();
         match &mut q.state {

+ 19 - 30
src/storage/assembler.rs

@@ -1,5 +1,7 @@
 use core::fmt;
 
+use crate::config::ASSEMBLER_MAX_SEGMENT_COUNT;
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct TooManyHolesError;
 
@@ -27,7 +29,7 @@ impl fmt::Display for Contig {
 }
 
 impl Contig {
-    fn empty() -> Contig {
+    const fn empty() -> Contig {
         Contig {
             hole_size: 0,
             data_size: 0,
@@ -66,24 +68,13 @@ impl Contig {
     }
 }
 
-#[cfg(feature = "alloc")]
-use alloc::boxed::Box;
-#[cfg(feature = "alloc")]
-const CONTIG_COUNT: usize = 32;
-
-#[cfg(not(feature = "alloc"))]
-const CONTIG_COUNT: usize = 4;
-
 /// A buffer (re)assembler.
 ///
 /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer.
 #[derive(Debug, PartialEq, Eq, Clone)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Assembler {
-    #[cfg(not(feature = "alloc"))]
-    contigs: [Contig; CONTIG_COUNT],
-    #[cfg(feature = "alloc")]
-    contigs: Box<[Contig; CONTIG_COUNT]>,
+    contigs: [Contig; ASSEMBLER_MAX_SEGMENT_COUNT],
 }
 
 impl fmt::Display for Assembler {
@@ -106,12 +97,11 @@ impl fmt::Display for Assembler {
 
 impl Assembler {
     /// Create a new buffer assembler.
-    pub fn new() -> Assembler {
-        #[cfg(not(feature = "alloc"))]
-        let contigs = [Contig::empty(); CONTIG_COUNT];
-        #[cfg(feature = "alloc")]
-        let contigs = Box::new([Contig::empty(); CONTIG_COUNT]);
-        Assembler { contigs }
+    pub const fn new() -> Assembler {
+        const EMPTY: Contig = Contig::empty();
+        Assembler {
+            contigs: [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT],
+        }
     }
 
     pub fn clear(&mut self) {
@@ -355,10 +345,9 @@ mod test {
 
     impl From<Vec<(usize, usize)>> for Assembler {
         fn from(vec: Vec<(usize, usize)>) -> Assembler {
-            #[cfg(not(feature = "alloc"))]
-            let mut contigs = [Contig::empty(); CONTIG_COUNT];
-            #[cfg(feature = "alloc")]
-            let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]);
+            const EMPTY: Contig = Contig::empty();
+
+            let mut contigs = [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT];
             for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
                 contigs[i] = Contig {
                     hole_size,
@@ -468,7 +457,7 @@ mod test {
     #[test]
     fn test_rejected_add_keeps_state() {
         let mut assr = Assembler::new();
-        for c in 1..=CONTIG_COUNT {
+        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
             assert_eq!(assr.add(c * 10, 3), Ok(()));
         }
         // Maximum of allowed holes is reached
@@ -499,12 +488,12 @@ mod test {
 
     #[test]
     fn test_boundary_case_remove_front() {
-        let mut vec = vec![(1, 1); CONTIG_COUNT];
+        let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
         vec[0] = (0, 2);
         let mut assr = Assembler::from(vec);
         assert_eq!(assr.remove_front(), 2);
-        let mut vec = vec![(1, 1); CONTIG_COUNT];
-        vec[CONTIG_COUNT - 1] = (0, 0);
+        let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
+        vec[ASSEMBLER_MAX_SEGMENT_COUNT - 1] = (0, 0);
         let exp_assr = Assembler::from(vec);
         assert_eq!(assr, exp_assr);
     }
@@ -648,7 +637,7 @@ mod test {
     #[test]
     fn test_add_then_remove_front_at_front_full() {
         let mut assr = Assembler::new();
-        for c in 1..=CONTIG_COUNT {
+        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
             assert_eq!(assr.add(c * 10, 3), Ok(()));
         }
         // Maximum of allowed holes is reached
@@ -660,7 +649,7 @@ mod test {
     #[test]
     fn test_add_then_remove_front_at_front_full_offset_0() {
         let mut assr = Assembler::new();
-        for c in 1..=CONTIG_COUNT {
+        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
             assert_eq!(assr.add(c * 10, 3), Ok(()));
         }
         assert_eq!(assr.add_then_remove_front(0, 3), Ok(3));
@@ -708,7 +697,7 @@ mod test {
                     }
 
                     // Compare.
-                    let wanted_res = if contigs.len() > CONTIG_COUNT {
+                    let wanted_res = if contigs.len() > ASSEMBLER_MAX_SEGMENT_COUNT {
                         Err(TooManyHolesError)
                     } else {
                         Ok(())