├── .cargo └── config.toml ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── main.rs └── route_nl.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_env = "musl")'] 2 | rustflags = ["-C", "target-feature=+crt-static"] -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | # Sequence of patterns matched against refs/tags 5 | tags: 6 | - "v*" 7 | - "test-release" 8 | env: 9 | GITHUB_TOKEN: ${{ github.token }} 10 | RUST_BACKTRACE: 1 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | test: 17 | name: ${{ matrix.platform.os_name }} with rust ${{ matrix.toolchain }} 18 | runs-on: ${{ matrix.platform.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | platform: 23 | - os_name: Linux-x86_64 24 | os: ubuntu-20.04 25 | target: x86_64-unknown-linux-musl 26 | bin: ns_dup 27 | - os_name: Linux-aarch64 28 | os: ubuntu-20.04 29 | target: aarch64-unknown-linux-musl 30 | bin: ns_dup 31 | - os_name: Linux-arm 32 | os: ubuntu-20.04 33 | target: arm-unknown-linux-musleabi 34 | bin: ns_dup 35 | - os_name: Linux-i686 36 | os: ubuntu-20.04 37 | target: i686-unknown-linux-musl 38 | bin: ns_dup 39 | - os_name: Linux-mips 40 | os: ubuntu-20.04 41 | target: mips-unknown-linux-musl 42 | bin: ns_dup 43 | - os_name: Linux-mipsel 44 | os: ubuntu-20.04 45 | target: mipsel-unknown-linux-musl 46 | bin: ns_dup 47 | - os_name: Linux-mips64 48 | os: ubuntu-20.04 49 | target: mips64-unknown-linux-muslabi64 50 | bin: ns_dup 51 | - os_name: Linux-mips64el 52 | os: ubuntu-20.04 53 | target: mips64el-unknown-linux-muslabi64 54 | bin: ns_dup 55 | - os_name: Linux-riscv64 56 | os: ubuntu-20.04 57 | target: riscv64gc-unknown-linux-gnu 58 | bin: ns_dup 59 | toolchain: 60 | - stable 61 | # - beta 62 | # - nightly 63 | steps: 64 | - uses: actions/checkout@v3 65 | - name: Cache cargo & target directories 66 | uses: Swatinem/rust-cache@v2 67 | with: 68 | key: "v2" 69 | - name: Build binary 70 | uses: houseabsolute/actions-rust-cross@v0 71 | with: 72 | command: "build" 73 | target: ${{ matrix.platform.target }} 74 | toolchain: ${{ matrix.toolchain }} 75 | args: "--locked --release" 76 | strip: true 77 | - name: Upload artifacts 78 | uses: actions/upload-artifact@v3 79 | with: 80 | name: ns_dup-${{ matrix.platform.os_name }} 81 | path: target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }} 82 | if: matrix.toolchain == 'stable' && github.ref == 'refs/tags/test-release' 83 | - name: Publish GitHub release 84 | uses: svenstaro/upload-release-action@v2 85 | with: 86 | repo_token: ${{ secrets.GITHUB_TOKEN }} 87 | file: target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }} 88 | asset_name: ns_dup-${{ matrix.platform.os_name }} 89 | tag: ${{ github.ref }} 90 | if: matrix.toolchain == 'stable' && startsWith( github.ref, 'refs/tags/v' ) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 19 | 20 | [[package]] 21 | name = "bitflags" 22 | version = "2.6.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 25 | 26 | [[package]] 27 | name = "byteorder" 28 | version = "1.5.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 37 | 38 | [[package]] 39 | name = "cfg_aliases" 40 | version = "0.1.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 43 | 44 | [[package]] 45 | name = "either" 46 | version = "1.15.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 49 | 50 | [[package]] 51 | name = "glob" 52 | version = "0.3.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 55 | 56 | [[package]] 57 | name = "ipnetwork" 58 | version = "0.20.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" 61 | dependencies = [ 62 | "serde", 63 | ] 64 | 65 | [[package]] 66 | name = "libc" 67 | version = "0.2.169" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 70 | 71 | [[package]] 72 | name = "log" 73 | version = "0.4.27" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 76 | 77 | [[package]] 78 | name = "memchr" 79 | version = "2.7.4" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 82 | 83 | [[package]] 84 | name = "memoffset" 85 | version = "0.9.1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 88 | dependencies = [ 89 | "autocfg", 90 | ] 91 | 92 | [[package]] 93 | name = "neli" 94 | version = "0.6.5" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" 97 | dependencies = [ 98 | "byteorder", 99 | "libc", 100 | "log", 101 | "neli-proc-macros", 102 | ] 103 | 104 | [[package]] 105 | name = "neli-proc-macros" 106 | version = "0.1.4" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" 109 | dependencies = [ 110 | "either", 111 | "proc-macro2", 112 | "quote", 113 | "serde", 114 | "syn 1.0.109", 115 | ] 116 | 117 | [[package]] 118 | name = "nix" 119 | version = "0.28.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" 122 | dependencies = [ 123 | "bitflags", 124 | "cfg-if", 125 | "cfg_aliases", 126 | "libc", 127 | "memoffset", 128 | ] 129 | 130 | [[package]] 131 | name = "no-std-net" 132 | version = "0.6.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" 135 | 136 | [[package]] 137 | name = "ns_dup" 138 | version = "0.1.0" 139 | dependencies = [ 140 | "neli", 141 | "nix", 142 | "pnet", 143 | ] 144 | 145 | [[package]] 146 | name = "pnet" 147 | version = "0.35.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d" 150 | dependencies = [ 151 | "ipnetwork", 152 | "pnet_base", 153 | "pnet_datalink", 154 | "pnet_packet", 155 | "pnet_sys", 156 | "pnet_transport", 157 | ] 158 | 159 | [[package]] 160 | name = "pnet_base" 161 | version = "0.35.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" 164 | dependencies = [ 165 | "no-std-net", 166 | ] 167 | 168 | [[package]] 169 | name = "pnet_datalink" 170 | version = "0.35.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" 173 | dependencies = [ 174 | "ipnetwork", 175 | "libc", 176 | "pnet_base", 177 | "pnet_sys", 178 | "winapi", 179 | ] 180 | 181 | [[package]] 182 | name = "pnet_macros" 183 | version = "0.35.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863" 186 | dependencies = [ 187 | "proc-macro2", 188 | "quote", 189 | "regex", 190 | "syn 2.0.93", 191 | ] 192 | 193 | [[package]] 194 | name = "pnet_macros_support" 195 | version = "0.35.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab" 198 | dependencies = [ 199 | "pnet_base", 200 | ] 201 | 202 | [[package]] 203 | name = "pnet_packet" 204 | version = "0.35.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f" 207 | dependencies = [ 208 | "glob", 209 | "pnet_base", 210 | "pnet_macros", 211 | "pnet_macros_support", 212 | ] 213 | 214 | [[package]] 215 | name = "pnet_sys" 216 | version = "0.35.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" 219 | dependencies = [ 220 | "libc", 221 | "winapi", 222 | ] 223 | 224 | [[package]] 225 | name = "pnet_transport" 226 | version = "0.35.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf" 229 | dependencies = [ 230 | "libc", 231 | "pnet_base", 232 | "pnet_packet", 233 | "pnet_sys", 234 | ] 235 | 236 | [[package]] 237 | name = "proc-macro2" 238 | version = "1.0.92" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 241 | dependencies = [ 242 | "unicode-ident", 243 | ] 244 | 245 | [[package]] 246 | name = "quote" 247 | version = "1.0.38" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 250 | dependencies = [ 251 | "proc-macro2", 252 | ] 253 | 254 | [[package]] 255 | name = "regex" 256 | version = "1.11.1" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 259 | dependencies = [ 260 | "aho-corasick", 261 | "memchr", 262 | "regex-automata", 263 | "regex-syntax", 264 | ] 265 | 266 | [[package]] 267 | name = "regex-automata" 268 | version = "0.4.9" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 271 | dependencies = [ 272 | "aho-corasick", 273 | "memchr", 274 | "regex-syntax", 275 | ] 276 | 277 | [[package]] 278 | name = "regex-syntax" 279 | version = "0.8.5" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 282 | 283 | [[package]] 284 | name = "serde" 285 | version = "1.0.217" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 288 | dependencies = [ 289 | "serde_derive", 290 | ] 291 | 292 | [[package]] 293 | name = "serde_derive" 294 | version = "1.0.217" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 297 | dependencies = [ 298 | "proc-macro2", 299 | "quote", 300 | "syn 2.0.93", 301 | ] 302 | 303 | [[package]] 304 | name = "syn" 305 | version = "1.0.109" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 308 | dependencies = [ 309 | "proc-macro2", 310 | "quote", 311 | "unicode-ident", 312 | ] 313 | 314 | [[package]] 315 | name = "syn" 316 | version = "2.0.93" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" 319 | dependencies = [ 320 | "proc-macro2", 321 | "quote", 322 | "unicode-ident", 323 | ] 324 | 325 | [[package]] 326 | name = "unicode-ident" 327 | version = "1.0.14" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 330 | 331 | [[package]] 332 | name = "winapi" 333 | version = "0.3.9" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 336 | dependencies = [ 337 | "winapi-i686-pc-windows-gnu", 338 | "winapi-x86_64-pc-windows-gnu", 339 | ] 340 | 341 | [[package]] 342 | name = "winapi-i686-pc-windows-gnu" 343 | version = "0.4.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 346 | 347 | [[package]] 348 | name = "winapi-x86_64-pc-windows-gnu" 349 | version = "0.4.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 352 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ns_dup" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | nix = {version = "0.28.0", features = ["socket","net","process"]} 10 | pnet = {version = "*"} 11 | neli = {version= "0.6.5"} 12 | 13 | [profile.release] 14 | lto = "fat" 15 | opt-level = 'z' 16 | panic = 'abort' 17 | strip = true 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ns_dup 2 | 3 | (应该能)解决奇葩网络环境(校园网)下的 IPv6 ndp relay问题。 4 | 5 | 6 | ## Usage 7 | 1. 在OpenWrt路由器配置好ndp relay 8 | 2. 运行程序 9 | ```sh 10 | ns_dup 11 | ``` 12 | 13 | ## HOW IT WORKS 14 | 15 | 抓住发向WAN中的IPv6 NS请求,向LAN区域中转发一遍。 16 | 17 | 具体问题成因与解决可以参考下面链接中的文章。 18 | 19 | ### 20 | 21 | ## Readings 22 | 1. https://wjk.moe/2022/%E9%94%90%E6%8D%B7%E6%A0%A1%E5%9B%AD%E7%BD%91IPv6%E7%9A%84%EF%BC%8F64%E5%86%85%E7%BD%91%E4%B8%AD%E7%BB%A7%E9%85%8D%E7%BD%AE%EF%BC%9Andp-proxy%E3%80%81relay/ -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddrV6; 2 | use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; 3 | use nix::{errno::Errno, libc, libc::*, sys::socket}; 4 | use nix::sys::socket::{LinkAddr, MsgFlags, SockaddrIn6}; 5 | use pnet::packet::{icmpv6, Packet}; 6 | use pnet::packet::icmpv6::ndp::NdpOption; 7 | use std::ffi::OsString; 8 | use std::env; 9 | mod route_nl; 10 | 11 | fn main() { 12 | let args: Vec = env::args().collect(); 13 | if args.len() != 3 { 14 | println!("Usage: {} ", args[0]); 15 | return; 16 | } 17 | let wan_name = &args[1]; 18 | let lan_name = &args[2]; 19 | 20 | let mut val = 2; 21 | let socket_fd = unsafe { 22 | let raw_socket = libc::socket( 23 | libc::AF_PACKET, 24 | libc::SOCK_RAW | libc::SOCK_CLOEXEC, 25 | (libc::ETH_P_ALL as u16).to_be() as i32, 26 | ); 27 | match raw_socket { 28 | -1 => Err(Errno::last()), 29 | fd => { 30 | // Safe because libc::socket returned success 31 | Ok(OwnedFd::from_raw_fd(fd)) 32 | } 33 | } 34 | }.expect("Failed to create socket"); 35 | 36 | let mut bpf = unsafe { 37 | [ 38 | BPF_STMT((BPF_LD | BPF_H | BPF_ABS) as u16, 12), 39 | BPF_JUMP((BPF_JMP | BPF_JEQ | BPF_K) as u16, 0x86dd as u32, 0, 5), 40 | BPF_STMT((BPF_LD | BPF_B | BPF_ABS) as u16, 14+6), 41 | BPF_JUMP((BPF_JMP | BPF_JEQ | BPF_K) as u16, IPPROTO_ICMPV6 as u32, 0, 3), 42 | BPF_STMT((BPF_LD | BPF_B | BPF_ABS) as u16, 14+40), 43 | BPF_JUMP((BPF_JMP | BPF_JEQ | BPF_K) as u16, 135, 0, 1), 44 | BPF_STMT((BPF_RET | BPF_K) as u16, 0xffffffff), 45 | BPF_STMT((BPF_RET | BPF_K) as u16, 0), 46 | ] 47 | }; 48 | let bpf_prog = sock_fprog { 49 | len: bpf.len() as u16, 50 | filter: bpf.as_mut_ptr(), 51 | }; 52 | let res = unsafe { 53 | let res = libc::setsockopt( 54 | socket_fd.as_raw_fd(), 55 | libc::SOL_SOCKET, 56 | libc::SO_ATTACH_FILTER, 57 | &bpf_prog as *const _ as *const libc::c_void, 58 | std::mem::size_of_val(&bpf_prog) as u32, 59 | ); 60 | Errno::result(res).map(drop) 61 | }; 62 | res.expect("Failed to set filter"); 63 | let ping_fd = unsafe { 64 | let raw_socket = libc::socket( 65 | libc::AF_INET6, 66 | libc::SOCK_RAW | libc::SOCK_CLOEXEC, 67 | libc::IPPROTO_ICMPV6, 68 | ); 69 | match raw_socket { 70 | -1 => Err(Errno::last()), 71 | fd => { 72 | // Safe because libc::socket returned success 73 | Ok(OwnedFd::from_raw_fd(fd)) 74 | } 75 | } 76 | }.expect("Failed to create socket"); 77 | 78 | 79 | let mut buffer = [0u8; 1500]; 80 | 81 | let wan_iface = pnet::datalink::interfaces() 82 | .into_iter() 83 | .find(|iface| iface.name == *wan_name) 84 | .expect("Failed to find interface"); 85 | println!("{:?}", wan_iface); 86 | let lan_iface = pnet::datalink::interfaces() 87 | .into_iter() 88 | .find(|iface| iface.name == *lan_name) 89 | .expect("Failed to find interface"); 90 | 91 | socket::setsockopt(&ping_fd, socket::sockopt::BindToDevice, &OsString::from(lan_iface.name)).unwrap(); 92 | 93 | unsafe { 94 | let res = libc::setsockopt( 95 | ping_fd.as_raw_fd(), 96 | libc::IPPROTO_RAW, 97 | libc::IPV6_CHECKSUM, 98 | &val as *const c_int as *const c_void, 99 | std::mem::size_of_val(&val) as u32, 100 | ); 101 | Errno::result(res).map(drop) 102 | }.unwrap(); 103 | val = 255; 104 | socket::setsockopt(&ping_fd, socket::sockopt::Ipv6MulticastHops, &val).unwrap(); 105 | let filter = [0xff; 8]; 106 | unsafe { 107 | let res = libc::setsockopt( 108 | ping_fd.as_raw_fd(), 109 | libc::IPPROTO_ICMPV6, 110 | 1, // ICMPV6_FILTER 111 | filter.as_ptr() as *const c_void, 112 | std::mem::size_of_val(&filter) as u32, 113 | ); 114 | Errno::result(res).map(drop) 115 | }.unwrap(); 116 | loop { 117 | let (read_len, _) = socket::recvfrom::(socket_fd.as_raw_fd(), &mut buffer).unwrap(); 118 | 119 | let eth_packet = pnet::packet::ethernet::EthernetPacket::new(&buffer[..read_len]).unwrap(); 120 | if eth_packet.get_source() == wan_iface.mac.unwrap(){ 121 | 122 | let mut ns = pnet::packet::icmpv6::ndp::MutableNeighborSolicitPacket::new(&mut buffer[14+40..read_len]).unwrap(); 123 | // println!("{:?}",ns); 124 | // ns.set_options(&[]); 125 | ns.set_options(&[NdpOption{option_type:icmpv6::ndp::NdpOptionTypes::SourceLLAddr, length:1, data: lan_iface.mac.unwrap().octets().to_vec()}]); 126 | // println!("{:?}",ns); 127 | let dest = SockaddrIn6::from( 128 | SocketAddrV6::new(ns.get_target_addr(), 0, 0, 0) 129 | ); 130 | ns.set_checksum(0xffff); 131 | 132 | route_nl::mod_route(ns.get_target_addr(), lan_iface.index as i32, true).unwrap(); 133 | socket::sendto(ping_fd.as_raw_fd(), ns.packet(),&dest, MsgFlags::empty()).unwrap(); 134 | route_nl::mod_route(ns.get_target_addr(), lan_iface.index as i32, false).unwrap(); 135 | // println!("{:?}",ns) 136 | } 137 | 138 | } 139 | 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/route_nl.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv6Addr; 2 | 3 | use neli::{ 4 | consts::{ 5 | nl::{ *}, 6 | rtnl::{RtAddrFamily, RtScope, RtTable, Rta, Rtm, RtmFFlags, Rtn, Rtprot}, 7 | socket::*, 8 | }, 9 | err::NlError, 10 | nl::{NlPayload, Nlmsghdr}, 11 | rtnl::{Rtattr, Rtmsg}, 12 | socket::NlSocketHandle, 13 | types::RtBuffer, 14 | }; 15 | // To be implemented 16 | // Add route to the routing table 17 | 18 | 19 | pub fn mod_route(dst: Ipv6Addr, index: i32, is_add: bool) -> Result<(), NlError> { 20 | let mut socket = NlSocketHandle::connect(NlFamily::Route, Some(nix::unistd::getpid().as_raw() as u32), &[])?; 21 | socket.block()?; 22 | let mut route_rtbuff = RtBuffer::new(); 23 | 24 | route_rtbuff.push(Rtattr::new(None, Rta::Dst, dst.octets().to_vec())?); 25 | route_rtbuff.push(Rtattr::new(None, Rta::Priority, 1023)?); 26 | route_rtbuff.push(Rtattr::new(None, Rta::Oif, index)?); 27 | 28 | let rtmsg = Rtmsg { 29 | rtm_family: RtAddrFamily::Inet6, 30 | rtm_dst_len: 128, 31 | rtm_src_len: 0, 32 | rtm_tos: 0, 33 | rtm_table: RtTable::Main, 34 | rtm_protocol: if is_add { 35 | Rtprot::Static 36 | } else { 37 | Rtprot::Unspec 38 | }, 39 | rtm_scope: if is_add { 40 | RtScope::Link 41 | } else { 42 | RtScope::Nowhere 43 | }, 44 | rtm_type: if is_add { Rtn::Unicast } else { Rtn::Unspec }, 45 | rtm_flags: RtmFFlags::empty(), 46 | rtattrs: route_rtbuff, 47 | }; 48 | let nlmsg = Nlmsghdr::new( 49 | None, 50 | if is_add { Rtm::Newroute } else { Rtm::Delroute }, 51 | NlmFFlags::new(&[ 52 | NlmF::Request, 53 | NlmF::Ack, 54 | if is_add { NlmF::Create } else { NlmF::Replace }, 55 | ]), 56 | None, 57 | None, 58 | NlPayload::Payload(rtmsg), 59 | ); 60 | socket.send(nlmsg)?; 61 | // Wait for the ack 62 | socket.recv()?; 63 | Ok(()) 64 | } 65 | 66 | --------------------------------------------------------------------------------