{"id":15659118,"url":"https://github.com/bol-van/ipobfs","last_synced_at":"2025-05-04T08:38:27.913Z","repository":{"id":108734601,"uuid":"216591077","full_name":"bol-van/ipobfs","owner":"bol-van","description":"IP obfuscation NFQUEUE/kmod filter","archived":false,"fork":false,"pushed_at":"2025-04-22T07:45:53.000Z","size":3,"stargazers_count":46,"open_issues_count":0,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-04T01:03:05.709Z","etag":null,"topics":["censorship-circumvention","dpi","hide-vpn","linux","openwrt"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bol-van.png","metadata":{"files":{"readme":"readme.eng.txt","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-10-21T14:38:06.000Z","updated_at":"2025-04-22T07:45:57.000Z","dependencies_parsed_at":"2025-05-03T03:30:20.994Z","dependency_job_id":"b1078fbe-22bf-4dd1-81b4-3917c03a2a53","html_url":"https://github.com/bol-van/ipobfs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bol-van%2Fipobfs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bol-van%2Fipobfs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bol-van%2Fipobfs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bol-van%2Fipobfs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bol-van","download_url":"https://codeload.github.com/bol-van/ipobfs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252310921,"owners_count":21727512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["censorship-circumvention","dpi","hide-vpn","linux","openwrt"],"created_at":"2024-10-03T13:15:06.089Z","updated_at":"2025-05-04T08:38:27.895Z","avatar_url":"https://github.com/bol-van.png","language":"C","readme":"This project is intended to fight DPI protocol analysis and bypass protocol blocking.\r\n\r\nOne of the possible ways to overcome DPI signature analysis is to modify the protocol.\r\nThe fastest but not the easiest way is to modify the software itself.\r\nFor TCP, obfsproxy exists. However, in the case of VPN - only not very fast solutions (openvpn) work over TCP.\r\n\r\nWhat to do in case of udp?\r\nIf both endpoints are on a external IP, then its possible to modify packets on IP level.\r\nFor example, if you have a VPS, and you have an openwrt router at home and external IP from ISP,\r\nthen you can use this technique. If one endpoint is behind NAT, then abilities are limited,\r\nbut its still possible to tamper with udp/tcp headers and data payload.\r\n\r\nThe scheme is as follows:\r\n peer 1 \u003c=\u003e IP obfuscator/deobfuscator \u003c=\u003e network \u003c=\u003e IP obfuscator/deobfuscator \u003c=\u003e peer 2\r\n\r\nIn order for a packet to be delivered from peer 1 to peer 2, both having external IPs,\r\nit is enough to have correct IP headers. You can set any protocol number, obfuscate or encrypt IP payload,\r\nincluding tcp / udp headers. DPI will not understand what it is dealing with.\r\nIt will see non-standard IP protocols with unknown content.\r\n\r\nipobfs\r\n------\r\n\r\nNFQUEUE queue handler, IP obfuscator/deobfuscator.\r\n\r\n --qnum=\u003cnfqueue_number\u003e\r\n --daemon                       ; daemonize\r\n --pidfile=\u003cfilename\u003e           ; write pid to file\r\n --user=\u003cusername\u003e              ; drop root privs\r\n --debug                        ; print debug info\r\n --uid=uid[:gid]                ; drop root privs\r\n --ipproto-xor=0..255|0x00-0xFF ; xor protocol ID with given value\r\n --data-xor=0xDEADBEAF          ; xor IP payload (after IP header) with 32-bit HEX value\r\n --data-xor-offset=\u003cposition\u003e   ; start xoring at specified position after IP header end\r\n --data-xor-len=\u003cbytes\u003e         ; xor block max length. xor entire packet after offset if not specified\r\n --csum=none|fix|valid          ; transport header checksum : none = dont touch, fix = ignore checksum on incoming packets, valid = always make checksum valid\r\n\r\nThe XOR operation is symmetric, therefore the same parameters are set for the obfuscator and deobfuscator.\r\nOn each side, one instance of the program is launched.\r\n\r\nFiltering outgoing packets is easy because they go open, however, some u32 is required for incoming.\r\nThe protocol number (\"-p\") in the filter is the result of the xor of the original protocol with ipproto-xor.\r\n\r\nserver ipv4 udp:16 :\r\niptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 \"0\u003e\u003e22\u00260x3C@0\u00260xFFFF=16\" -j NFQUEUE --queue-num 300 --queue-bypass\r\niptables -t mangle -I POSTROUTING -o eth0 -p udp --sport 16  -j NFQUEUE --queue-num 300 --queue-bypass\r\n\r\nclient ipv4 udp:16 :\r\niptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 \"0\u003e\u003e22\u00260x3C@0\u003e\u003e16\u00260xFFFF=16\" -j NFQUEUE --queue-num 300 --queue-bypass\r\niptables -t mangle -I POSTROUTING -o eth0 -p udp --dport 16  -j NFQUEUE --queue-num 300 --queue-bypass\r\n\r\nipobfs --qnum=300 --ipproto-xor=128 --data-xor=0x458A2ECD --data-xor-offset=4 --data-xor-len=44\r\n\r\nWhy data-xor-offset = 4: tcp and udp protocol headers start with source and destination port numbers, 2 bytes each.\r\nTo make it easier to write u32 do not touch the port numbers. You can touch, but then you have to figure out into what\r\nnumbers original ports will be transformed and write those values to u32.\r\nWhy data-xor-len = 44: an example is given for wireguard. 44 bytes is enough to XOR the udp header and all wireguard headers.\r\nNext come the encrypted wireguard data, it makes no sense to XOR it.\r\n\r\nYou can even turn udp into \"tcp trash\" with ipproto-xor = 23. According to the ip header, this is tcp, but in place of the tcp header is garbage.\r\nOn the one hand, such packets can go through middle-boxes, and conntrack can go crazy.\r\nOn the other hand, it may even be good.\r\n\r\nThere are nuances with ipv6. In ipv6 there is no concept of a protocol number. But there is the concept of \"next header\".\r\nAs in ipv4, you can write anything there. But in practice, this can cause ICMPv6 Type 4 - Parameter Problem messages.\r\nTo avoid this, you can cast the protocol to the value 59. It means \"no Next Header\".\r\nTo get \"ipproto-xor\" parameter, XOR original protocol number with 59.\r\n\r\nudp : ipproto-xor=17^59=42\r\ntcp : ipproto-xor=6^59=61\r\n\r\nserver ipv6 tcp:12345 :\r\nip6tables -t mangle -I PREROUTING -i eth0 -p 59 -m u32 --u32 \"40\u00260xFFFF=12345\" -j NFQUEUE --queue-num 300 --queue-bypass\r\nip6tables -t mangle -I POSTROUTING -o eth0 -p tcp --sport 12345 -j NFQUEUE --queue-num 300 --queue-bypass\r\n\r\nclient ipv6 tcp:12345 :\r\nip6tables -t mangle -I PREROUTING -i eth0 -p 59 -m u32 --u32 \"38\u00260xFFFF=12345\" -j NFQUEUE --queue-num 300 --queue-bypass\r\nip6tables -t mangle -I POSTROUTING -o eth0 -p tcp --dport 12345 -j NFQUEUE --queue-num 300 --queue-bypass\r\n\r\nipobfs --qnum=300 --ipproto-xor=61 --data-xor=0x458A2ECD --data-xor-offset=4\r\n\r\nIP FRAGMENTATION\r\nIf the sending host sends too long packet, it is fragmented at the IP level.\r\nThe receiving host only reassembles packets addressed to the host itself.\r\nIn the PREROUTING chain packets are still fragmented.\r\nWhen applying deobfuscation only to a part of the packet, the cheksum inevitably becomes invalid.\r\ncsum = fix does not help.\r\nFor ipv4, adding a rule to the INPUT chain instead of PREROUTING helps.\r\nOf course, only packets addressed to the host itself are caught, but they come\r\nin NFQEUEUE in already assembled state and correctly deobfuscated.\r\nIP fragmentation is an undesirable, it should be combated by setting the correct MTU\r\ninside the tunnel. There are some protocols that rely on ip fragmentation. These include IKE (without rfc7383).\r\n\r\nIPV6 FRAGMENTATION\r\nFragmentation is also possible in ipv6, however, it is performed only by the sending host, usually only for\r\nudp and icmp when the frame does not fit into mtu. The header \"44\" is added to all fragments immediately after the ipv6 header.\r\nUnfortunately, all attempts to catch the reconstructed full frame in various tables failed.\r\nOnly the first fragment is caught. It was not possible to find out the reason. Is this a bug or feature is known only to Torvalds.\r\n\r\nCHECKSUMS :\r\nWork with checksums begins when a tcp or udp packet passes through the obfuscator.\r\nFor incoming packets, the ipproto-xor operation performed first, and after that it is analyzed whether it is tcp or udp.\r\nFor outgoing, the opposite is true.\r\n--csum=none - do not touch checksums at all. if after deobfuscation checksum is invalid, the system will discard the packet.\r\n--csum=fix - checksum ignore mode. its not possible to disable checksum verification inside NFQUEUE.\r\nInstead, on incoming packets checksum is recomputed and replaced, so the system will accept the packet.\r\n--csum=valid - bring the checksum to a valid state for all packets - incoming and outgoing.\r\nThis mode is useful when working through NAT which blocks invalid packets.\r\n\r\nRecomputing checksum increases cpu usage.\r\nSee also section \"NAT break\".\r\n\r\n\r\nDISADVANTAGE :\r\nEach packet will be thrown into nfqueue, therefore the speed will decrease significantly. 2-3 times.\r\nIf you compare wireguard + ipobfs with openvpn on a soho router, then openvpn will still be slower.\r\n\r\n\r\nipobfs_mod\r\n-----------\r\n\r\nThe same as ipobfs, but implemented as a linux kernel module. It gives a performance drop of only 20%.\r\nIt duplicates ipobfs logic and is compatible with it.\r\n\r\nIts possible to use ipobfs on peer1 and ipobfs_mod on peer2, they will work together.\r\nHowever, by default ipobfs_mod will produce tcp and udp packets with invalid cheksums, the system\r\nwith ipobfs will discarded them. Use csum=fix on ipobfs_mod side.\r\n\r\nThe iptables commands are the same, but instead of \"-j NFQEUEUE\" use \"-j MARK --set-xmark\".\r\nipobfs_mod performs packet processing based on fwmark.\r\n\r\nSettings are passed through the kernel module parameters specified in the insmod command.\r\n\r\nserver ipv4 udp:16 :\r\niptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 \"0\u003e\u003e22\u00260x3C@0\u00260xFFFF=16\" -j MARK --set-xmark 0x100/0x100\r\niptables -t mangle -I POSTROUTING -o eth0 -p udp --sport 16 -j MARK --set-xmark 0x100/0x100\r\n\r\nclient ipv4 udp:16 :\r\niptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 \"0\u003e\u003e22\u00260x3C@0\u003e\u003e16\u00260xFFFF=16\" -j MARK --set-xmark 0x100/0x100\r\niptables -t mangle -I POSTROUTING -o eth0 -p udp --dport 16 -j MARK --set-xmark 0x100/0x100\r\n\r\nrmmod ipobfs\r\ninsmod /lib/modules/`uname -r`/extra/ipobfs.ko  mark=0x100 ipp_xor=128 data_xor=0x458A2ECD data_xor_offset=4 data_xor_len=44\r\n\r\nThe module supports up to 32 profiles. Parameter settings for each profile are separated by commas.\r\nFor example, the following command combines the functions of 2 NFQUEUE handlers from the previous examples:\r\ninsmod /lib/modules/`uname -r`/extra/ipobfs.ko  mark=0x100,0x200 ipp_xor=128,61 data_xor=0x458A2ECD,0x458A2ECD data_xor_offset=4,4 data_xor_len=44,0\r\nIt is possible to use different profiles for outgoing and incoming packets.\r\nThis will confuse DPI even more by reducing the correlation of in/out streams.\r\nIf parameter 'markmask' is set, profile with mask/markmask wins, otherwise mask/mask is searched.\r\nmarkmask parameter is single for all profiles, no need for commas.\r\nUse markmask if profiles are numerous to not waste single bit for each one.\r\nFor example : 0x10/0xf0, 0x20/0xf0, ..., 0xf0/0xf0\r\n\r\nBy default, the module sets a hook on incoming packets with priority mangle+1, so that the table mangle was already processed\r\nby the time of the call. If non-standard IP protocols arrive at the input, everything is OK. But if there are packets with \r\nthe transport protocol that support checksumming, such as tcp or udp, then modified packets with invalid checksum\r\nwill not reach the mangle+1 hook. The module will not receive them.\r\nTo solve this problem, specify the pre=raw parameter and do : iptables -t raw -I PREROUTING ...\r\nOutgoing packets can be processed in the usual manner through mangle.\r\n\r\nIf you need to work with fragmented ipv4 protocols, replace iptables PREROUTING with INPUT (see the remark in the ipobfs section),\r\nspecify the module parameter \"prehook=input\".\r\n\r\nParameters pre,prehook,post,posthook are set individually for each profile and must be comma separated.\r\n\r\nThe module disables OS-level checksum checking and computing for all processed packets, in some cases\r\nrecomputing tcp and udp checksums independently.\r\nIf the parameter csum=none, module does not compute checksum at all, allowing sending packets with invalid checksum\r\nbefore obfuscation. Deobfuscated packets can contain invalid checksum.\r\nIf csum=fix, the module takes over the recalculation of the checksum on outgoing packets before the payload is modified,\r\nthereby repeating the functions of the OS or hardware offload. Otherwise OS or hw offload would spoil 2 bytes of data\r\nand after deobfuscation packet would contain incorrect checksum.\r\nIf csum=valid, the recalculation of the checksum is done after modifying the payload for both outgoing and incoming packets.\r\nThis ensures the visibility of the transmission of packets with a valid checksum.\r\nChecksum correction on the incoming packet is necessary if the device with ipobfs is not the receiver,\r\nbut performs the function of a router (forward). So that there is a valid packet on the output interface.\r\nThe regular recipient will not accept packets with incorrect checksum.\r\n\r\nThe debug = 1 parameter enables debugging output. You will see what is done with each processed packet in dmesg.\r\nIt should be used only for debugging. With a large number of packets, the system will slow down significantly\r\ndue to excessive output in dmesg.\r\n\r\nYou can view and change some ipobfs parameters without reloading the module : /sys/module/ipobfs/parameters\r\n\r\nCOMPILING MODULE on traditional linux system :\r\nAt first install kernel headers. for debian :\r\nsudo apt-get install linux-headers.....\r\ncd ipobfs_mod\r\nmake\r\nsudo make install\r\n\r\nSPEED NOTICE\r\nIf only ipproto-xor is specified, slowdown is very close to zero.\r\nWith data-xor its preferred not to xor offsets after 100-140 bytes.\r\nThis way you can avoid linearizing skb's and save lots of cpu time.\r\ndebug=1 option can show whether linearizing happens.\r\n\r\nopenwrt\r\n-------\r\n\r\nOn a x64 linux system, download and unzip the SDK from your firmware version for your device.\r\nThe SDK version must exactly match the firmware version, otherwise you will not build a suitable kernel module.\r\nIf you built the firmware yourself, instead of the SDK, you can and should use that buildroot.\r\nscripts/feeds update -a\r\nscripts/feeds install -a\r\nCopy openwrt/* to SDK folder, preserving directory structure.\r\nCopy ipobfs и ipobfs_mod (source code) to packages/ipobfs (the one there openwrt Makefile is).\r\nFrom SDK root run : make package/ipobfs/compile V=99\r\nLook for 2 ipk : bin/packages/..../ipobfs..ipk и bin/targets/..../kmod-ipobfs..ipk\r\nCopy selected version to the device, install via \"opkg install ...ipk\".\r\nIf reinstalling, first \"opkg remove ipobfs\" / \"opkg remove kmod-ipobfs\".\r\n\r\nNAT break\r\n------------\r\n\r\nIn the general case, its safe to assume that NAT can only pass tcp, udp, icmp traffic.\r\nSome NATs also contain helpers for special protocols (GRE). But not all NATs and not on all devices.\r\nNAT can pass non-standard IP protocols, but it does not have the means to track the source IP that initiated\r\ncommunication. If non-standard protocols work through NAT, then only work for only one device behind NAT.\r\nUsing one IP protocol with more than one device behind NAT is not possible. There will be a conflict.\r\nTherefore, ipproto-xor can be used, but carefully.\r\n\r\nConsider linux-based NAT (almost all home routers) without helpers.\r\nAs the study shows, transport header fields containing payload length and flags are important.\r\nTherefore, the minimum xor-data-offset for tcp is 14, for udp it is 6. Otherwise, the packet will not pass NAT at all.\r\n\r\nAny NAT will definitely follow the tcp flags, because conntrack determines the start of the connection.\r\nConntrack is vital part of any NAT. Flags field offset in tcp header is 13.\r\n\r\nLinux conntrack by default verifies transport protocol checksums and does not track packets with invalid checksum.\r\nSuch packets do not cause the appearance or change of entries in the conntrack table, the status of packets is INVALID,\r\nSNAT operation will not be applied to them, nevertheless, the forwarding of such packets will still happen unchanged,\r\nmaintaining the source address from the internal network. To avoid this behavior, properly configured routers apply\r\nrules like \"-m state --state INVALID -j DROP\" or \"-m conntrack --ctstate INVALID -j DROP\", thereby prohibiting forwarding\r\npackets that conntrack refused to account.\r\nThis behavior can be changed with the command \"sysctl -w net.netfilter.nf_conntrack_checksum=0\".\r\nIn this case, the checksums will not be considered, conntrack will accept packets even with invalid cheksums, NAT will work.\r\nIn openwrt, by default net.netfilter.nf_conntrack_checksum=0, so NAT works with invalid packets.\r\nBut other routers usually do not change the default value, which is 1.\r\n\r\nWithout exception, all NATs will correct the 2-byte checksum in tcp (offset 18) and udp (offset 6) header,\r\nsince it is computed using ip source and destination. NAT changes the source ip when sending, source port\r\ncan also change. To save resources, a full checksum recalculation is usually not performed.\r\nThe initial checksum is taken as a basis, the difference between the initial and changed values​is added to it.\r\nThe recipient receives a packet with an invalid checksum, then packet is deobfuscated by ipobfs and checksum becomes\r\nvalid again, but only if the initial checksum was not changed during obfuscation, that is,\r\ndata-xor-offset\u003e = 20 for tcp and data-xor-offset\u003e = 8 for udp.\r\nThe obfuscator XORs, checksum is additive, so they are incompatible.\r\nipobfs by default does not recalculate the checksums of transport headers, so if it is used at the receiving end, then\r\ndata-xor-offset must not cover checksum field, otherwise the packet will be discarded by the system after deobfuscation\r\nAs an alternative use --csum=fix option.\r\nipobfs_mod disables checksums verification, so there is no such problem when using it. default behavior is similar to --csum=fix\r\nIf ipproto_xor is used, router will not recalculate the checksum, packet will arrive with invalid checksum after deobfuscation.\r\n\r\nMany routers perform mss fix (-j TCPMSS --clamp-mss-to-pmtu or -j TCPMSS --set-mss).\r\nmss is in the tcp header options. Windows and linux send mss as the first option. The option itself takes 4 bytes.\r\nIt turns out that the minimum xor-data-offset for tcp rises to 24, because bytes 22-23 can be changed by router.\r\n\r\nSUMMARY :\r\n tcp : data-xor-offset\u003e=24\r\n udp : data-xor-offset\u003e=8\r\n\r\nIf NAT doesn’t pass packets with invalid checksums, use --csum=valid option.\r\nIn terms of cpu load, it would be preferable not to use the --csum=valid mode if possible.\r\n\r\nThere is information that some mobile operators terminate tcp on their servers for later proxying to the original\r\ndestination. In this case, any tcp modification not at the data flow level is doomed to failure.\r\nA terminating middlebox will reject packets with a corrupted header or invalid checksum.\r\nAn outgoing connection from middlebox will not repeat the same packetization as the original connection.\r\nUse obfsproxy.\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbol-van%2Fipobfs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbol-van%2Fipobfs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbol-van%2Fipobfs/lists"}