Skip to content

Commit ea150ed

Browse files
author
root
committed
srcport rewrite, bump to 0.0.6
Symmetric to dstport_rewrite: map the UDP source port through an equal-size range on the wire. Sender treats srcport as the app range and srcport_rewrite as the wire range; receiver is mirrored (srcport matches the wire, srcport_rewrite is the app range to restore). Validated at config load: srcport must be set and the two ranges must have identical size.
1 parent 3148282 commit ea150ed

8 files changed

Lines changed: 56 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "transparent-udp-obfus"
3-
version = "0.0.5"
3+
version = "0.0.6"
44
edition = "2021"
55

66
[[bin]]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Each rule:
139139
| `srcport` | u16 or `[lo, hi]` | Optional. |
140140
| `dstport` | u16 | Optional. |
141141
| `dstport_rewrite` | u16 | Optional. If set, overwrite dst port after encoding / before decoding. |
142+
| `srcport_rewrite` | u16 or `[lo, hi]` | Optional. Requires `srcport`. Range size must match `srcport`. Sender maps app→wire (`srcport``srcport_rewrite`); receiver maps back (`srcport` matches wire, `srcport_rewrite` is the app range to restore). |
142143

143144
### Cipher mode
144145

src/config.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub struct TrafficRule {
4747
pub dstport: Option<u16>,
4848
#[serde(default)]
4949
pub dstport_rewrite: Option<u16>,
50+
#[serde(default)]
51+
pub srcport_rewrite: Option<PortRange>,
5052
}
5153

5254
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
@@ -112,6 +114,33 @@ impl Config {
112114
if self.tun.len() >= 16 {
113115
anyhow::bail!("tun name too long");
114116
}
117+
for (name, rules) in [
118+
("local_traffic", &self.local_traffic),
119+
("remote_traffic", &self.remote_traffic),
120+
] {
121+
for r in rules {
122+
let Some(rw) = &r.srcport_rewrite else { continue };
123+
let Some(orig) = &r.srcport else {
124+
anyhow::bail!(
125+
"{}: srcport_rewrite requires srcport to be set",
126+
name
127+
);
128+
};
129+
let (a, b) = orig.as_pair();
130+
let (c, d) = rw.as_pair();
131+
if a > b || c > d {
132+
anyhow::bail!("{}: port range must have lo <= hi", name);
133+
}
134+
if (b - a) != (d - c) {
135+
anyhow::bail!(
136+
"{}: srcport_rewrite size ({}) must equal srcport size ({})",
137+
name,
138+
d - c + 1,
139+
b - a + 1,
140+
);
141+
}
142+
}
143+
}
115144
if self.mark_emit & self.mark_reinject == 0 {
116145
tracing::warn!(
117146
"mark_emit (0x{:x}) shares no bits with mark_reinject mask (0x{:x}); \

src/matcher.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct CompiledRule {
1414
pub dstip: Option<IpNet>,
1515
pub dstport: Option<u16>,
1616
pub dstport_rewrite: Option<u16>,
17+
pub srcport_rewrite: Option<(u16, u16)>,
1718
}
1819

1920
impl CompiledRule {
@@ -25,6 +26,7 @@ impl CompiledRule {
2526
dstip: r.dstip.as_deref().map(parse_net).transpose()?,
2627
dstport: r.dstport,
2728
dstport_rewrite: r.dstport_rewrite,
29+
srcport_rewrite: r.srcport_rewrite.as_ref().map(PortRange::as_pair),
2830
})
2931
}
3032

src/packet.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ pub fn write_dport(buf: &mut [u8], p: &ParsedUdp, new_dport: u16) {
125125
buf[p.l4_offset + 2..p.l4_offset + 4].copy_from_slice(&new_dport.to_be_bytes());
126126
}
127127

128+
/// Write `new_sport` into the UDP header (does NOT recompute checksum).
129+
pub fn write_sport(buf: &mut [u8], p: &ParsedUdp, new_sport: u16) {
130+
buf[p.l4_offset..p.l4_offset + 2].copy_from_slice(&new_sport.to_be_bytes());
131+
}
132+
128133
fn ones_complement_sum(mut sum: u32, data: &[u8]) -> u32 {
129134
let mut i = 0;
130135
while i + 1 < data.len() {

src/receiver.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,16 @@ fn handle(
117117
}
118118

119119
let new_dport = rule.dstport_rewrite.unwrap_or(orig.port());
120-
packet::build(peer.ip(), orig.ip(), peer.port(), new_dport, data, out);
120+
// Symmetric to sender's srcport rewrite. On this side `srcport` matches
121+
// the wire (rewritten) range and `srcport_rewrite` is the original app
122+
// range to restore. Validated equal-size at config load.
123+
let new_sport = if let Some((orig_lo, _)) = rule.srcport_rewrite {
124+
let (wire_lo, _) = rule.srcport.unwrap();
125+
orig_lo + (peer.port() - wire_lo)
126+
} else {
127+
peer.port()
128+
};
129+
packet::build(peer.ip(), orig.ip(), new_sport, new_dport, data, out);
121130

122131
let n = unsafe {
123132
libc::write(

src/sender.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ fn handle(buf: &mut [u8], ctx: &SenderCtx) {
8484
if let Some(new_dp) = rule.dstport_rewrite {
8585
packet::write_dport(buf, &parsed, new_dp);
8686
}
87+
if let Some((rw_lo, _)) = rule.srcport_rewrite {
88+
// Validated at config load: srcport is always set when srcport_rewrite is,
89+
// and the ranges have identical size.
90+
let (app_lo, _) = rule.srcport.unwrap();
91+
let new_sp = rw_lo + (parsed.src_port - app_lo);
92+
packet::write_sport(buf, &parsed, new_sp);
93+
}
8794
packet::recompute_udp_checksum(buf, &parsed);
8895

8996
let total = parsed.total_len;

0 commit comments

Comments
 (0)