├── datagram_ex.png
├── datagram_fmt.png
├── README.md
├── sipsas.py
├── sipsac.py
└── test-tool
├── run_test.sh
└── sipsa_test.c
/datagram_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0ki/SIPSA/HEAD/datagram_ex.png
--------------------------------------------------------------------------------
/datagram_fmt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0ki/SIPSA/HEAD/datagram_fmt.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SIPSA
2 | ======
3 | Source IP spoofing for anonymization over UDP
4 |
5 | Source IP spoofing anonymization over UDP (SIPSA) is a proposal for a protocol that in many network environments would allow two hosts on the network to hide both their source and destination addresses, while still being able to communicate information.
6 |
7 |
8 | This is a proof of concept tool that implements the SIPSA protocol. Use in production environments is highly discouraged. Do not depend on it for confidentiality, anonimity or deniablity. Do not expect the tool to preserve the integrity of your data or provide any reasonable level of availability for that matter.
9 |
10 | You are welcome to fork and experiment!
11 |
12 | * sipsas.py - IPv4 receiver
13 | * sipsac.py - IPv4 sender
14 | * test-tool - A tool used to test BCP38 limitations
15 |
--------------------------------------------------------------------------------
/sipsas.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # SIPSAs4 - Source IP spoofing for anonymization over UDP (IPv4 receiver)
3 | # (C) Kirils Solovjovs, 2016; Modified BSD licence
4 |
5 | # WARNING WARNING WARNING
6 | #
7 | # This is a proof of concept tool. Do not depend on it for confidentiality,
8 | # anonimity or deniablity. Do not expect the tool to preserve the integrity
9 | # of your data or provide any reasonable level of availability for that matter.
10 | # This is a proof of concept tool.
11 | #
12 | # WARNING WARNING WARNING
13 |
14 | # This demo supports IPv4 only, but there is no reason why this shouldn't work for IPv6
15 |
16 | # Please make sure that all other UDP ports on your systems are FILTERED (not REJECTED)
17 |
18 |
19 | # Shared key used to perform AES encryption on SIPSA metadata *only*
20 | key="YOUR SHARED KEY HERE"
21 |
22 | import socket,re,sys
23 | from Crypto.Cipher import AES
24 | from hashlib import md5
25 | from socket import inet_ntoa
26 |
27 | key=md5(key).digest() #always exactly 32 bytes
28 |
29 | UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
30 | listen_addr = ("",51654) #listen on all interfaces, port 51654
31 | UDPSock.bind(listen_addr)
32 | while True:
33 | payload,[ip,port] = UDPSock.recvfrom(65535) #larger than actually
34 | print "Got a datagram from "+ip+".",
35 | if port!=51654:
36 | sys.stderr.write("Wrong source port ("+str(port)+").")
37 | continue
38 | if len(payload)<5+3+16:
39 | sys.stderr.write("Datagram too short to be SIPSA.")
40 | continue
41 | if not re.match("SIPSA",payload): #does the payload begin with "SIPSA"?
42 | sys.stderr.write("Not a SIPSA packet.")
43 | continue
44 | if payload[5:7]!="\x00\x04":
45 | sys.stderr.write("SIPSA version mismatch.")
46 | continue
47 |
48 | try:
49 | lenIndicator=ord(payload[7]) #how long is the metadata (in blocks of 16)
50 | rawdata=payload[8:8+lenIndicator*16]
51 | data=payload[8+lenIndicator*16:]
52 | iv=rawdata[:16]
53 | crypto=rawdata[16:]
54 | cipher = AES.new(key,AES.MODE_CBC,iv)
55 | metadata = cipher.decrypt(crypto)
56 | realSrcIP=inet_ntoa(metadata[0:4]) # real src
57 | realDstIP=metadata[4:8] # real dst
58 | srcIPlst=[]
59 | dstIPlst=[]
60 | i=8
61 | while metadata[i]!="\xff": #marker that indicates end of source list
62 | srcIPlst.append(metadata[i:i+4])
63 | i = i + 4
64 | i = i + 1
65 | while metadata[i]!="\xff": #marker that indicates end of dest list
66 | dstIPlst.append(metadata[i:i+4])
67 | i = i + 4
68 |
69 | except:
70 | sys.stderr.write("Packet damaged. (maybe the key is mismatched?)\n")
71 | continue
72 | if ip==realSrcIP:
73 | realSrcIP=realSrcIP+"(ip match)"
74 | # I suggest adding a randomized ~100ms delay before sending the reply
75 | # reply only once per each packet, not each copy.
76 | # if realSrcIP is anonymized, do this for each unique "iv" and/or "crypto"
77 | # send_sipsa(realDstIP,IP()/TCP(),key,srcIPlst,dstIPlst) # send the reply
78 | print "IP", realSrcIP, "sent us this:", data
79 | else:
80 | print
81 |
82 |
--------------------------------------------------------------------------------
/sipsac.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # SIPSAc4 - Source IP spoofing for anonymization over UDP (IPv4 sender)
3 | # (C) Kirils Solovjovs, 2016; Modified BSD licence
4 |
5 | # WARNING WARNING WARNING
6 | #
7 | # This is a proof of concept tool. Do not depend on it for confidentiality,
8 | # anonimity or deniablity. Do not expect the tool to preserve the integrity
9 | # of your data or provide any reasonable level of availability for that matter.
10 | # This is a proof of concept tool.
11 | #
12 | # WARNING WARNING WARNING
13 |
14 | # This demo supports IPv4 only, but there is no reason why this shouldn't work for IPv6
15 |
16 |
17 | # How many sources and destinations to create? Total pairs = srcR x dstR
18 | srcR=5
19 | dstR=3
20 |
21 | # Shared key used to perform AES encryption on SIPSA metadata *only*
22 | key="YOUR SHARED KEY HERE"
23 |
24 |
25 | import os,sys
26 |
27 | if not os.geteuid() == 0:
28 | sys.exit("ERR: Peril awaits you, seeing you need to be root to craft packets. Quitting.")
29 |
30 | from scapy.all import *
31 | import random
32 | from struct import unpack
33 | from socket import AF_INET, inet_pton, inet_aton
34 | from Crypto.Cipher import AES
35 | from Crypto import Random
36 | from hashlib import md5
37 |
38 |
39 | key=md5(key).digest() #always exactly 32 bytes
40 |
41 |
42 | # privateIP(): is IPv4 address private or loopback?
43 | # function from http://stackoverflow.com/questions/691045 , author: jackdoe
44 | def privateIP(ip):
45 | f = unpack('!I',inet_pton(AF_INET,ip))[0]
46 | private = (
47 | [ 2130706432, 4278190080 ], # 127.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc3330
48 | [ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 http://tools.ietf.org/html/rfc1918
49 | [ 2886729728, 4293918720 ], # 172.16.0.0, 255.240.0.0 http://tools.ietf.org/html/rfc1918
50 | [ 167772160, 4278190080 ], # 10.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc1918
51 | )
52 | for net in private:
53 | if (f & net[1]) == net[0]:
54 | return True
55 | return False
56 |
57 |
58 | # genIPs(): generate a list pseudo-random IPs
59 | def genIPs(base,count):
60 | baseOct=base.split('.')
61 | avoid3=baseOct[3]
62 | if (count<2):
63 | sys.exit("ERR: You should always generate at least two IPs.")
64 | lst=[]
65 | lst.append(base) # inclose original IP
66 | while avoid3==baseOct[3]: #include
67 | baseOct[3]=str(random.randrange(1,255)) # 1-254 just to be safe
68 | lst.append(".".join(baseOct)) #same subnet
69 |
70 |
71 | # WARNING! Please DO this if your base IP is in private space *and* NATted:
72 | # Swap comments on the next two lines to disable/enable full hiding of original source IP
73 | # lst=[]
74 | count=count-2
75 |
76 |
77 | while count>0:
78 | baseOct[0]=str(random.randrange(1,240)) # 1 - 239 , no multicast
79 | baseOct[1]=str(random.randrange(0,256)) # 0 - 255
80 | baseOct[2]=str(random.randrange(0,256)) # 0 - 255
81 | baseOct[3]=str(random.randrange(1,255)) # 1 - 254 , yes, it's crude
82 | if privateIP(".".join(baseOct)): # ignore private IPs
83 | continue
84 | count=count-1
85 | lst.append(".".join(baseOct))
86 | avoid3=baseOct[3]
87 | while avoid3==baseOct[3] and count>0: #include
88 | baseOct[3]=str(random.randrange(1,255)) # 1-254 just to be safe
89 | if count>0:
90 | lst.append(".".join(baseOct)) #same subnet
91 | count=count-1
92 | return lst
93 |
94 |
95 | # send_sipsa(): send a SIPSA packet
96 | def send_sipsa(dstA,data,key="",pSrcIPlst=None,pDstIPlst=None):
97 | random.seed(dstA+realSrcIP+key+"."+str(srcR)+"."+str(dstR)) # always the same set of IP addresses
98 | if pSrcIPlst:
99 | srcIPlst=pSrcIPlst
100 | else:
101 | srcIPlst=genIPs(realSrcIP,srcR)
102 | random.shuffle(srcIPlst)
103 | if pDstIPlst:
104 | dstIPlst=pDstIPlst
105 | else:
106 | dstIPlst=genIPs(dstA,dstR)
107 | random.shuffle(dstIPlst)
108 |
109 | # WARNING! This crypto is likely not secure. Do not use in production!
110 | iv=Random.new().read(16)
111 |
112 | cipher=AES.new(key,AES.MODE_CBC,iv)
113 | metadata=inet_aton(realSrcIP)+inet_aton(dstA)+"".join([inet_aton(e) for e in srcIPlst])+"\xff"+"".join([inet_aton(e) for e in dstIPlst])+"\xff"
114 |
115 | # we can also hide the real IP from the server. it will still work:
116 | # metadata="\x00"*8+"".join([inet_aton(e) for e in srcIPlst])+"\xff"+"".join([inet_aton(e) for e in dstIPlst])+"\xff"
117 |
118 | crypto=iv+cipher.encrypt(metadata.ljust(len(metadata) + 16 - len(metadata) % 16,"\x00"))
119 | lenIndicator=len(crypto)/16
120 | if lenIndicator>255:
121 | sys.exit("ERR: Too many sources and/or dests.") # size field is currently limited to 1 byte
122 |
123 | payload="SIPSA\x00\x04"+chr(lenIndicator)+crypto+str(data) #Layer5 payload
124 |
125 | # send from and to all the addresses
126 | for srcIP in srcIPlst:
127 | for dstIP in dstIPlst:
128 | packet = Ether()/IP(src=srcIP,dst=dstIP)/UDP(sport=51654,dport=51654)/payload
129 | sendp(packet,iface=routingIface, verbose=0)
130 |
131 | print "Sent",len(srcIPlst)*len(dstIPlst),"packets."
132 |
133 |
134 | routes=[]
135 | for routeentry in conf.route.routes:
136 | if routeentry[0]==0:
137 | routes.append([routeentry[4],routeentry[3]])
138 |
139 | if len(routes)<1:
140 | sys.exit("ERR: No IPv4 routes found.")
141 |
142 | realSrcIP=routes[0][0]
143 | routingIface=routes[0][1]
144 |
145 | if len(routes)>1:
146 | sys.stderr.write("WARN: Multiple IPv4 routes found.\n")
147 |
148 | print "Choosing "+routingIface+". "+realSrcIP+" is our real source IP."
149 |
150 | if privateIP(realSrcIP):
151 | sys.exit("ERR: This PoC will not work behind NAT. Remove this warning (in code), if you are testing the tool on internal network.")
152 |
153 | # Test:
154 | send_sipsa("203.0.113.3",IP(src="8.8.8.8",dst="8.8.4.4")/TCP(sport=1234,dport=2000)/"Tunneled Layer 5 data",key)
155 |
--------------------------------------------------------------------------------
/test-tool/run_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #(C) Kirils Solovjovs, 2018. SIPSA project
3 | #
4 | # If you have any trouble running this or any concerns,
5 | # or if you would like your name listed in the research paper,
6 | # please send the any comments to research@kirils.org
7 | # Please include full output of this script where appropriate.
8 |
9 | set -e
10 |
11 | [ ! -r sipsa_test.c ] && echo source code not found in currect directory && exit 1
12 |
13 | echo This is SIPSA research tool version 20180106.
14 | echo
15 | echo This tool will send 150-200 small packets to sipsa.kirils.org.
16 | echo Various IP address data will be collected.
17 | echo Expected runtime is less than 10 seconds, usually 1 second.
18 | echo Root access will be required to craft some of the packets.
19 | echo
20 | echo Press RETURN if you agree, or ^C to abort.
21 | read
22 |
23 |
24 | echo -n Verifying that all required tools are installed...
25 |
26 | for tool in gcc cat grep cut head tail sed ip nc; do #sudo is excluded on purpose. the user can always run this as root.
27 | [ -z "$(which $tool | cat)" ] && echo && echo $tool is missing && exit 13
28 | echo -n $tool...
29 | done
30 | echo ALL GOOD.
31 |
32 | echo -n Compiling test code...
33 | gcc sipsa_test.c -o ./sipsa_test
34 | echo done
35 |
36 | echo -n Checking if test code runs on this machine...
37 | [ -z "$(./sipsa_test |grep ^Usage)" ] && echo no && exit 10
38 | echo yes
39 |
40 | echo -n Detecting main network interface...
41 | iface="$(cat /proc/net/route |grep -E 00000000.....+00000000|cut -d $'\t' -f 1|head -1)"
42 | [ -z "$iface" ] && echo failed && exit 2
43 | echo $iface
44 |
45 | echo -n Getting real IP address...
46 | rip="$(( sed -E 's/$/\r/' | nc 85.254.196.147 80 | tail -1 | grep '^[0-9]' | cat) << EOF
47 | GET /detect_ip/ HTTP/1.0
48 | Host: sipsa.kirils.org
49 |
50 | EOF)"
51 |
52 | [ -z "$rip" ] && echo failed && exit 4
53 | echo $rip
54 |
55 | echo -n Getting IP address of the machine...
56 | lip="$(ip addr show dev $iface | sed -E 's/^ +//'|grep ^inet\ | cut -d \ -f 2|grep ^$rip/ | cat)"
57 | [ -z "$lip" ] && lip="$(ip addr show dev $iface | sed -E 's/^ +//'|grep ^inet\ | cut -d \ -f 2|head -1)"
58 | [ -z "$lip" ] && echo failed && exit 3
59 | echo $lip
60 |
61 | netmask="$(echo $lip | cut -d / -f 2)"
62 | lip="$(echo $lip | cut -d / -f 1)"
63 |
64 | echo -n Calculating network address...
65 | IFS=. read -r i1 i2 i3 i4 <<< "$lip"
66 | IFS=. read -r m0 m1 m2 m3 m4 <<< $(for a in $(seq 1 32); do if [ $(((a - 1) % 8)) -eq 0 ]; then echo -n .; fi; if [ $a -le $netmask ]; then echo -n 1; else echo -n 0; fi; done)
67 | na=$(printf "%d.%d.%d.%d" "$((i1 & (2#$m1)))" "$((i2 & (2#$m2)))" "$((i3 & (2#$m3)))" "$((i4 & (2#$m4)))")
68 | echo $na
69 |
70 | echo -n Getting broadcast address...
71 | bc="$(ip addr show dev $iface | sed -E 's/^ +//'|grep ^inet\ | cut -d \ -f 4|head -1)"
72 | [ -z "$bc" ] && echo failed && exit 3
73 | echo $bc
74 |
75 | echo -n Getting gateway address...
76 | gw="$(ip route get 85.254.196.147 | sed -E 's/ +/ /g;s/ $//'|grep "$iface" | head -1 |cut -d \ -f 3| grep '^[0-9]' | cat)"
77 | [ -z "$gw" ] && echo failed && exit 5
78 | echo $gw
79 |
80 | failmode=""
81 | [ "$rip" != "$lip" ] && echo Real IP does not match the IP set on the interface. \
82 | && echo This test is useless behind NAT, but proceeding anyway. In the name of SCIENCE\! \
83 | && failmode="-nat"
84 |
85 |
86 |
87 | [ "$UID" != "0" ] && sudo="sudo " || sudo=""
88 | $sudo echo
89 | echo -n Running the tests as root...
90 |
91 |
92 | iplst=""
93 |
94 | echo -n 1...
95 | $sudo ./sipsa_test "$iface" "$lip" "$rip-$lip-native$failmode"
96 | $sudo ./sipsa_test "$iface" "$lip" "$rip-$lip-native$failmode"
97 | iplst="$iplst;$lip"
98 |
99 | echo -n 2...
100 | $sudo ./sipsa_test "$iface" "$gw" "$rip-$gw-gw$failmode"
101 | $sudo ./sipsa_test "$iface" "$gw" "$rip-$gw-gw$failmode"
102 | iplst="$iplst;$gw"
103 |
104 | echo -n 3...
105 | $sudo ./sipsa_test "$iface" "$rip" "$rip-$rip-real$failmode"
106 | $sudo ./sipsa_test "$iface" "$rip" "$rip-$rip-real$failmode"
107 | iplst="$iplst;$rip"
108 |
109 | echo -n 4...
110 | $sudo ./sipsa_test "$iface" "$na" "$rip-$na-lan0$failmode"
111 | $sudo ./sipsa_test "$iface" "$na" "$rip-$na-lan0$failmode"
112 | iplst="$iplst;$na"
113 |
114 | echo -n 5...
115 | $sudo ./sipsa_test "$iface" "$bc" "$rip-$bc-lanFF$failmode"
116 | $sudo ./sipsa_test "$iface" "$bc" "$rip-$bc-lanFF$failmode"
117 | iplst="$iplst;$bc"
118 |
119 |
120 | for ((i=1;i<4;i++)); do
121 | echo -n 6/$i...
122 | tmpip="$(echo "$lip" |cut -d \. -f 1-3).$(($RANDOM % 256))"
123 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND1$failmode"
124 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND1$failmode"
125 | iplst="$iplst;$tmpip"
126 | done
127 |
128 |
129 | for ((i=4;i<8;i++)); do
130 | echo -n 6/$i...
131 | tmpip="$(echo "$lip" |cut -d \. -f 1-2).$(($RANDOM % 256)).$(($RANDOM % 256))"
132 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND2$failmode"
133 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND2$failmode"
134 | iplst="$iplst;$tmpip"
135 | done
136 |
137 |
138 | for ((i=8;i<=10;i++)); do
139 | echo -n 6/$i...
140 | tmpip="$(echo "$lip" |cut -d \. -f 1).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
141 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND3$failmode"
142 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-lanRND3$failmode"
143 | iplst="$iplst;$tmpip"
144 | done
145 |
146 |
147 | for ((i=1;i<=10;i++)); do #class A
148 | echo -n 7/$i...
149 | tmpip="$(($RANDOM % 126+1)).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
150 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-A$i$failmode"
151 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-A$i$failmode"
152 | iplst="$iplst;$tmpip"
153 | done
154 |
155 | for ((i=1;i<=10;i++)); do #class B
156 | echo -n 8/$i...
157 | tmpip="$(($RANDOM % 40+128)).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
158 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-B$i$failmode"
159 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-B$i$failmode"
160 | iplst="$iplst;$tmpip"
161 | done
162 |
163 | for ((i=1;i<=10;i++)); do #class C
164 | echo -n 9/$i...
165 | tmpip="$(($RANDOM % 30+193)).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
166 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-C$i$failmode"
167 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-C$i$failmode"
168 | iplst="$iplst;$tmpip"
169 | done
170 |
171 | for ((i=1;i<=10;i++)); do #class D
172 | echo -n 10/$i...
173 | tmpip="$(($RANDOM % 16+224)).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
174 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-D$i$failmode"
175 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-D$i$failmode"
176 | iplst="$iplst;$tmpip"
177 | done
178 |
179 | for ((i=1;i<=10;i++)); do #class E
180 | echo -n 11/$i...
181 | tmpip="$(($RANDOM % 14+240)).$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
182 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-E$i$failmode"
183 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-E$i$failmode"
184 | iplst="$iplst;$tmpip"
185 | done
186 |
187 | #private A
188 | echo -n 12/1...
189 | tmpip="10.$(($RANDOM % 256)).$(($RANDOM % 256)).$(($RANDOM % 256))"
190 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Ap$failmode"
191 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Ap$failmode"
192 | iplst="$iplst;$tmpip"
193 |
194 | #private B
195 | echo -n 12/2...
196 | tmpip="172.20.$(($RANDOM % 256)).$(($RANDOM % 256))"
197 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Bp$failmode"
198 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Bp$failmode"
199 | iplst="$iplst;$tmpip"
200 |
201 | #private C
202 | echo -n 12/3...
203 | tmpip="192.168.$(($RANDOM % 256)).$(($RANDOM % 256))"
204 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Cp$failmode"
205 | $sudo ./sipsa_test "$iface" "$tmpip" "$rip-$tmpip-Cp$failmode"
206 | iplst="$iplst;$tmpip"
207 |
208 | echo done.
209 | $sudo ./sipsa_test "$iface" "$lip" "REPORT:20180106:$rip-$iface$failmode$iplst"
210 |
211 | echo
212 | echo Thank you for contributing to SIPSA research.
213 | echo Go to http://sipsa.kirils.org/ to view your results.
214 | echo
215 |
216 |
217 |
--------------------------------------------------------------------------------
/test-tool/sipsa_test.c:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2011-2015 P.D. Buchan (pdbuchan@yahoo.com)
2 | Copyright (C) 2018 Kirils Solovjovs (research@kirils.org)
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 | */
17 |
18 | #include
19 | #include
20 | #include // close()
21 | #include // strcpy, memset(), and memcpy()
22 |
23 | #include // struct addrinfo
24 | #include // needed for socket(), uint8_t, uint16_t, uint32_t
25 | #include // needed for socket()
26 | #include // IPPROTO_RAW, IPPROTO_IP, IPPROTO_UDP, INET_ADDRSTRLEN
27 | #include // struct ip and IP_MAXPACKET (which is 65535)
28 | #include // struct udphdr
29 | #include // inet_pton() and inet_ntop()
30 | #include // macro ioctl is defined
31 | #include // defines values for argument "request" of ioctl.
32 | #include // struct ifreq
33 |
34 | #include // errno, perror()
35 |
36 | // Define some constants.
37 | #define IP4_HDRLEN 20 // IPv4 header length
38 | #define UDP_HDRLEN 8 // UDP header length, excludes data
39 |
40 | // Function prototypes
41 | uint16_t checksum (uint16_t *, int);
42 | uint16_t udp4_checksum (struct ip, struct udphdr, uint8_t *, int);
43 | char *allocate_strmem (int);
44 | uint8_t *allocate_ustrmem (int);
45 | int *allocate_intmem (int);
46 |
47 | int
48 | main (int argc, char **argv)
49 | {
50 | int status, datalen, sd, *ip_flags;
51 | const int on = 1;
52 | char *interface, *target, *src_ip, *dst_ip;
53 | struct ip iphdr;
54 | struct udphdr udphdr;
55 | uint8_t *data, *packet;
56 | struct addrinfo hints, *res;
57 | struct sockaddr_in *ipv4, sin;
58 | struct ifreq ifr;
59 | void *tmp;
60 |
61 | if( argc != 4 ) {
62 | printf("Usage %s \n\n",argv[0]);
63 | exit(1);
64 | }
65 |
66 | // Allocate memory for various arrays.
67 | data = allocate_ustrmem (IP_MAXPACKET);
68 | packet = allocate_ustrmem (IP_MAXPACKET);
69 | interface = allocate_strmem (strlen(argv[1]));
70 | target = allocate_strmem (40);
71 | src_ip = allocate_strmem (INET_ADDRSTRLEN);
72 | dst_ip = allocate_strmem (INET_ADDRSTRLEN);
73 | ip_flags = allocate_intmem (4);
74 |
75 | // Interface to send packet through.
76 | strcpy (interface, argv[1]);
77 |
78 | // Submit request for a socket descriptor to look up interface.
79 | if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
80 | perror ("socket() failed to get socket descriptor for using ioctl() ");
81 | exit (EXIT_FAILURE);
82 | }
83 |
84 | // Use ioctl() to look up interface index which we will use to
85 | // bind socket descriptor sd to specified interface with setsockopt() since
86 | // none of the other arguments of sendto() specify which interface to use.
87 | memset (&ifr, 0, sizeof (ifr));
88 | snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
89 | if (ioctl (sd, SIOCGIFINDEX, &ifr) < 0) {
90 | perror ("ioctl() failed to find interface ");
91 | return (EXIT_FAILURE);
92 | }
93 | close (sd);
94 | //printf ("Index for interface %s is %i\n", interface, ifr.ifr_ifindex);
95 |
96 | // Source IPv4 address: you need to fill this out
97 | strcpy (src_ip, argv[2]);
98 |
99 | // Destination URL or IPv4 address: you need to fill this out
100 | strcpy (target, "85.254.196.147");
101 |
102 | // Fill out hints for getaddrinfo().
103 | memset (&hints, 0, sizeof (struct addrinfo));
104 | hints.ai_family = AF_INET;
105 | hints.ai_socktype = SOCK_STREAM;
106 | hints.ai_flags = hints.ai_flags | AI_CANONNAME;
107 |
108 | // Resolve target using getaddrinfo().
109 | if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
110 | fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (status));
111 | exit (EXIT_FAILURE);
112 | }
113 | ipv4 = (struct sockaddr_in *) res->ai_addr;
114 | tmp = &(ipv4->sin_addr);
115 | if (inet_ntop (AF_INET, tmp, dst_ip, INET_ADDRSTRLEN) == NULL) {
116 | status = errno;
117 | fprintf (stderr, "inet_ntop() failed.\nError message: %s", strerror (status));
118 | exit (EXIT_FAILURE);
119 | }
120 | freeaddrinfo (res);
121 |
122 | // UDP data
123 | datalen = strlen(argv[3]);
124 | strcpy (data, argv[3]);
125 |
126 | // IPv4 header
127 |
128 | // IPv4 header length (4 bits): Number of 32-bit words in header = 5
129 | iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t);
130 |
131 | // Internet Protocol version (4 bits): IPv4
132 | iphdr.ip_v = 4;
133 |
134 | // Type of service (8 bits)
135 | iphdr.ip_tos = 0;
136 |
137 | // Total length of datagram (16 bits): IP header + UDP header + datalen
138 | iphdr.ip_len = htons (IP4_HDRLEN + UDP_HDRLEN + datalen);
139 |
140 | // ID sequence number (16 bits): unused, since single datagram
141 | iphdr.ip_id = htons (0);
142 |
143 | // Flags, and Fragmentation offset (3, 13 bits): 0 since single datagram
144 |
145 | // Zero (1 bit)
146 | ip_flags[0] = 0;
147 |
148 | // Do not fragment flag (1 bit)
149 | ip_flags[1] = 0;
150 |
151 | // More fragments following flag (1 bit)
152 | ip_flags[2] = 0;
153 |
154 | // Fragmentation offset (13 bits)
155 | ip_flags[3] = 0;
156 |
157 | iphdr.ip_off = htons ((ip_flags[0] << 15)
158 | + (ip_flags[1] << 14)
159 | + (ip_flags[2] << 13)
160 | + ip_flags[3]);
161 |
162 | // Time-to-Live (8 bits): default to maximum value
163 | iphdr.ip_ttl = 255;
164 |
165 | // Transport layer protocol (8 bits): 17 for UDP
166 | iphdr.ip_p = IPPROTO_UDP;
167 |
168 | // Source IPv4 address (32 bits)
169 | if ((status = inet_pton (AF_INET, src_ip, &(iphdr.ip_src))) != 1) {
170 | fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
171 | exit (EXIT_FAILURE);
172 | }
173 |
174 | // Destination IPv4 address (32 bits)
175 | if ((status = inet_pton (AF_INET, dst_ip, &(iphdr.ip_dst))) != 1) {
176 | fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
177 | exit (EXIT_FAILURE);
178 | }
179 |
180 | // IPv4 header checksum (16 bits): set to 0 when calculating checksum
181 | iphdr.ip_sum = 0;
182 | iphdr.ip_sum = checksum ((uint16_t *) &iphdr, IP4_HDRLEN);
183 |
184 | // UDP header
185 |
186 | // Source port number (16 bits): pick a number
187 | udphdr.source = htons (6790);
188 |
189 | // Destination port number (16 bits): pick a number
190 | udphdr.dest = htons (6790);
191 |
192 | // Length of UDP datagram (16 bits): UDP header + UDP data
193 | udphdr.len = htons (UDP_HDRLEN + datalen);
194 |
195 | // UDP checksum (16 bits)
196 | udphdr.check = udp4_checksum (iphdr, udphdr, data, datalen);
197 |
198 | // Prepare packet.
199 |
200 | // First part is an IPv4 header.
201 | memcpy (packet, &iphdr, IP4_HDRLEN * sizeof (uint8_t));
202 |
203 | // Next part of packet is upper layer protocol header.
204 | memcpy ((packet + IP4_HDRLEN), &udphdr, UDP_HDRLEN * sizeof (uint8_t));
205 |
206 | // Finally, add the UDP data.
207 | memcpy (packet + IP4_HDRLEN + UDP_HDRLEN, data, datalen * sizeof (uint8_t));
208 |
209 | // The kernel is going to prepare layer 2 information (ethernet frame header) for us.
210 | // For that, we need to specify a destination for the kernel in order for it
211 | // to decide where to send the raw datagram. We fill in a struct in_addr with
212 | // the desired destination IP address, and pass this structure to the sendto() function.
213 | memset (&sin, 0, sizeof (struct sockaddr_in));
214 | sin.sin_family = AF_INET;
215 | sin.sin_addr.s_addr = iphdr.ip_dst.s_addr;
216 |
217 | // Submit request for a raw socket descriptor.
218 | if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
219 | perror ("socket() failed ");
220 | exit (EXIT_FAILURE);
221 | }
222 |
223 | // Set flag so socket expects us to provide IPv4 header.
224 | if (setsockopt (sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)) < 0) {
225 | perror ("setsockopt() failed to set IP_HDRINCL ");
226 | exit (EXIT_FAILURE);
227 | }
228 |
229 | // Bind socket to interface index.
230 | if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0) {
231 | perror ("setsockopt() failed to bind to interface ");
232 | exit (EXIT_FAILURE);
233 | }
234 |
235 | // Send packet.
236 | if (sendto (sd, packet, IP4_HDRLEN + UDP_HDRLEN + datalen, 0, (struct sockaddr *) &sin, sizeof (struct sockaddr)) < 0) {
237 | perror ("sendto() failed ");
238 | exit (EXIT_FAILURE);
239 | }
240 |
241 | // Close socket descriptor.
242 | close (sd);
243 |
244 | // Free allocated memory.
245 | free (data);
246 | free (packet);
247 | free (interface);
248 | free (target);
249 | free (src_ip);
250 | free (dst_ip);
251 | free (ip_flags);
252 |
253 | return (EXIT_SUCCESS);
254 | }
255 |
256 | // Computing the internet checksum (RFC 1071).
257 | // Note that the internet checksum does not preclude collisions.
258 | uint16_t
259 | checksum (uint16_t *addr, int len)
260 | {
261 | int count = len;
262 | register uint32_t sum = 0;
263 | uint16_t answer = 0;
264 |
265 | // Sum up 2-byte values until none or only one byte left.
266 | while (count > 1) {
267 | sum += *(addr++);
268 | count -= 2;
269 | }
270 |
271 | // Add left-over byte, if any.
272 | if (count > 0) {
273 | sum += *(uint8_t *) addr;
274 | }
275 |
276 | // Fold 32-bit sum into 16 bits; we lose information by doing this,
277 | // increasing the chances of a collision.
278 | // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits)
279 | while (sum >> 16) {
280 | sum = (sum & 0xffff) + (sum >> 16);
281 | }
282 |
283 | // Checksum is one's compliment of sum.
284 | answer = ~sum;
285 |
286 | return (answer);
287 | }
288 |
289 | // Build IPv4 UDP pseudo-header and call checksum function.
290 | uint16_t
291 | udp4_checksum (struct ip iphdr, struct udphdr udphdr, uint8_t *payload, int payloadlen)
292 | {
293 | char buf[IP_MAXPACKET];
294 | char *ptr;
295 | int chksumlen = 0;
296 | int i;
297 |
298 | ptr = &buf[0]; // ptr points to beginning of buffer buf
299 |
300 | // Copy source IP address into buf (32 bits)
301 | memcpy (ptr, &iphdr.ip_src.s_addr, sizeof (iphdr.ip_src.s_addr));
302 | ptr += sizeof (iphdr.ip_src.s_addr);
303 | chksumlen += sizeof (iphdr.ip_src.s_addr);
304 |
305 | // Copy destination IP address into buf (32 bits)
306 | memcpy (ptr, &iphdr.ip_dst.s_addr, sizeof (iphdr.ip_dst.s_addr));
307 | ptr += sizeof (iphdr.ip_dst.s_addr);
308 | chksumlen += sizeof (iphdr.ip_dst.s_addr);
309 |
310 | // Copy zero field to buf (8 bits)
311 | *ptr = 0; ptr++;
312 | chksumlen += 1;
313 |
314 | // Copy transport layer protocol to buf (8 bits)
315 | memcpy (ptr, &iphdr.ip_p, sizeof (iphdr.ip_p));
316 | ptr += sizeof (iphdr.ip_p);
317 | chksumlen += sizeof (iphdr.ip_p);
318 |
319 | // Copy UDP length to buf (16 bits)
320 | memcpy (ptr, &udphdr.len, sizeof (udphdr.len));
321 | ptr += sizeof (udphdr.len);
322 | chksumlen += sizeof (udphdr.len);
323 |
324 | // Copy UDP source port to buf (16 bits)
325 | memcpy (ptr, &udphdr.source, sizeof (udphdr.source));
326 | ptr += sizeof (udphdr.source);
327 | chksumlen += sizeof (udphdr.source);
328 |
329 | // Copy UDP destination port to buf (16 bits)
330 | memcpy (ptr, &udphdr.dest, sizeof (udphdr.dest));
331 | ptr += sizeof (udphdr.dest);
332 | chksumlen += sizeof (udphdr.dest);
333 |
334 | // Copy UDP length again to buf (16 bits)
335 | memcpy (ptr, &udphdr.len, sizeof (udphdr.len));
336 | ptr += sizeof (udphdr.len);
337 | chksumlen += sizeof (udphdr.len);
338 |
339 | // Copy UDP checksum to buf (16 bits)
340 | // Zero, since we don't know it yet
341 | *ptr = 0; ptr++;
342 | *ptr = 0; ptr++;
343 | chksumlen += 2;
344 |
345 | // Copy payload to buf
346 | memcpy (ptr, payload, payloadlen);
347 | ptr += payloadlen;
348 | chksumlen += payloadlen;
349 |
350 | // Pad to the next 16-bit boundary
351 | for (i=0; i